R:根据前一个非 NA 行中的值分配前一个非 NA 值 'n' 次

R: assign previous non NA value 'n' times based on value in previous non NA row

我有一个数据框 test_case。我在一列 (income) 中缺少数据。

test_case <- data.frame(
person=c(1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 3),
year=c(2010, 2011, 2012, 2010, 2011, 2012, 2010, 2011, 2013, 2014, 2014, 2014),
income=c(4, 10, 13, NA, NA, NA, 13, NA, NA, NA, NA, NA),
cutoff=c(0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0)
)

变量 cutoff 指定我想将 income 中的值 'carry forward' 到后续行的次数(使用包 zoo 中的 na.locf() 方法) .例如,在上面的数据框中,cutoff 的值为 2 表示收入应结转两次。

我在 SO 上看到了关于指定如何使用 na.locf 结转 n 次 当 n 为常量时的示例。 但就我而言,我遇到了麻烦当 n 变化时泛化 (R -- Carry last observation forward n times)。

这是我的原始数据框:

   person year income cutoff
1       1 2010      4      0
2       1 2011     10      0
3       1 2012     13      2
4       2 2010     NA      0
5       2 2011     NA      0
6       2 2012     NA      0
7       3 2010     13      3
8       3 2011     NA      0
9       3 2013     NA      0
10      3 2014     NA      0
11      3 2014     NA      0
12      3 2014     NA      0

这是所需的输出:

   person year income cutoff
1       1 2010      4      0
2       1 2011     10      0
3       1 2012     13      2
4       2 2010     13      0
5       2 2011     13      0
6       2 2012     NA      0
7       3 2010     13      3
8       3 2011     13      0
9       3 2013     13      0
10      3 2014     13      0
11      3 2014     NA      0
12      3 2014     NA      0

这是使用 dplyr 的答案。

它通过按不同截止值的累积总和进行分组来工作。

如果截止值为 0,则它会生成一个包含一个 FALSE 的列表,以及截止数量为 TRUE 的列表,该列表未列出并根据组的大小进行切片。

然后使用ifelse,收入要么不变,要么成为第一个收入(即截止收入)。

library(dplyr)

test_case %>% group_by(z = cumsum(cutoff != 0)) %>%
              mutate(income = ifelse(unlist(lapply(cutoff, function(x) rep(as.logical(x), max(1,x + 1))))[1:n()], income[1], income))

Source: local data frame [12 x 5]
Groups: z [3]

       z person  year income cutoff
   (int)  (dbl) (dbl)  (dbl)  (dbl)
1      0      1  2010      4      0
2      0      1  2011     10      0
3      1      1  2012     13      2
4      1      2  2010     13      0
5      1      2  2011     13      0
6      1      2  2012     NA      0
7      2      3  2010     13      3
8      2      3  2011     13      0
9      2      3  2013     13      0
10     2      3  2014     13      0
11     2      3  2014     NA      0
12     2      3  2014     NA      0

使用 na.locf 的解决方案可以以与@jeremycg 的解决方案类似的方式工作。我们只需要按 cumsum(cutoff != 0) 和另一个变量分组,即移位后的 row_number

我的解决方案不如 jeremycg 的解决方案优雅,但我是这样处理的:

library(dplyr)
library(zoo)
test_case %>%
  mutate(
    rownum = row_number(),
    cutoff2 = ifelse(cutoff == 0, NA, cutoff + rownum),
    cutoff2 = na.locf(cutoff2, na.rm = FALSE),
    cutoff2 = ifelse(rownum > cutoff2, NA, cutoff2)
  ) %>%
  group_by(z = cumsum(cutoff != 0), cutoff2) %>%
  mutate(income = na.locf(income, na.rm = FALSE))
# Source: local data frame [12 x 7]
# Groups: z, cutoff2 [5]
# 
#    person  year income cutoff rownum cutoff2     z
#     (dbl) (dbl)  (dbl)  (dbl)  (int)   (dbl) (int)
# 1       1  2010      4      0      1      NA     0
# 2       1  2011     10      0      2      NA     0
# 3       1  2012     13      2      3       5     1
# 4       2  2010     13      0      4       5     1
# 5       2  2011     13      0      5       5     1
# 6       2  2012     NA      0      6      NA     1
# 7       3  2010     13      3      7      10     2
# 8       3  2011     13      0      8      10     2
# 9       3  2013     13      0      9      10     2
# 10      3  2014     13      0     10      10     2
# 11      3  2014     NA      0     11      NA     2
# 12      3  2014     NA      0     12      NA     2

这是使用 data.table 的尝试。分组方法在@jeremys 的回答中,虽然我在这里避免使用 ifelselapply,而是将根据第一个 income 值复制的第一个 income 值与 NAs 值重复 .N - (cutoff[1L] + 1L) 次。我也只对第一次以来的值进行操作 cutoff > 0L)

library(data.table)
setDT(test_case)[which.max(cutoff > 0L):.N, # Or `cutoff > 0L | is.na(income)`
                 income := c(rep(income[1L], cutoff[1L] + 1L), rep(NA, .N - (cutoff[1L] + 1L))), 
                 by = cumsum(cutoff != 0L)]
test_case
#     person year income cutoff
#  1:      1 2010      4      0
#  2:      1 2011     10      0
#  3:      1 2012     13      2
#  4:      2 2010     13      0
#  5:      2 2011     13      0
#  6:      2 2012     NA      0
#  7:      3 2010     13      3
#  8:      3 2011     13      0
#  9:      3 2013     13      0
# 10:      3 2014     13      0
# 11:      3 2014     NA      0
# 12:      3 2014     NA      0