根据其他列满足的条件计算列的行平均值

时间:2018-02-13 07:12:52

标签: r data.table mean

我想基于其他列中的值来获取某些列的行平均值。如果我们采用以下数据集:

library(data.table)
test <- data.table(s1=c(0,4,29,9,1,2,10),
                   s2=c(20,17,11,15,32,15,10),
                   s3=c(1,0,2,1,4,7,0),
                   m1=c(0,4,29,NA,1,22,8),
                   m2=c(20,17,NA,15,32,15,12),
                   m3=c(1,0,1,1,1,NA,0),
                   z=c(1,5,25,5,30,20,10)
)

我想取s1, s2, s3的平均值并根据m1, m2, m3z的值创建新列;具体使用以下条件。

ifelse( !is.na(m) & m<z, s, NA)

也就是说,如果m不是NAm < z,那么只有s才能考虑行平均值。

到目前为止,我已经有了这个工作,但似乎太长了

test[,t1:=ifelse(!is.na(m1) & m1<z,s1,NA),]
test[,t2:=ifelse(!is.na(m2) & m2<z,s2,NA),]
test[,t3:=ifelse(!is.na(m3) & m3<z,s3,NA),]

test[,s_avg:=rowMeans(.SD,na.rm = TRUE),.SDcols=c('t1','t2','t3')]

另一个data.frame解决方案也将受到赞赏。

编辑:不需要t列。

3 个答案:

答案 0 :(得分:3)

一个选项是使用Map对's'和'm'的相应列进行比较

nm1 <- grep("s\\d+", names(test), value = TRUE)
nm2 <- grep("m\\d+", names(test), value = TRUE)
test[, paste0("t", 1:3) := Map(function(x, y) 
         ifelse(y < z & !is.na(y), x, NA), .SD[, ..nm1], .SD[, ..nm2]) ]

然后做,OP的帖子中的最后一步。目前尚不清楚OP是否需要't'列。

test[,s_avg:=rowMeans(.SD,na.rm = TRUE),.SDcols=c('t1','t2','t3')]
test
#    s1 s2 s3 m1 m2 m3  z t1 t2 t3 s_avg
#1:  0 20  1  0 20  1  1  0 NA NA   0.0
#2:  4 17  0  4 17  0  5  4 NA  0   2.0
#3: 29 11  2 29 NA  1 25 NA NA  2   2.0
#4:  9 15  1 NA 15  1  5 NA NA  1   1.0
#5:  1 32  4  1 32  1 30  1 NA  4   2.5
#6:  2 15  7 22 15 NA 20 NA 15 NA  15.0
#7: 10 10  0  8 12  0 10 10 NA  0   5.0

如果我们不需要't'列,那么可以在上面的步骤中创建s_avg

test[,  s_avg := rowMeans(mapply(function(x, y) x *(NA^!(y < z & !is.na(y))),
                     .SD[, ..nm1], .SD[, ..nm2]), na.rm = TRUE) ]
test
#   s1 s2 s3 m1 m2 m3  z s_avg
#1:  0 20  1  0 20  1  1   0.0
#2:  4 17  0  4 17  0  5   2.0
#3: 29 11  2 29 NA  1 25   2.0
#4:  9 15  1 NA 15  1  5   1.0
#5:  1 32  4  1 32  1 30   2.5
#6:  2 15  7 22 15 NA 20  15.0
#7: 10 10  0  8 12  0 10   5.0

即使是grep步也可以在上面的代码行中完成。

另一种选择是在创建行索引后melt将其设置为'long'格式,然后对索引进行连接以创建's_avg'

test[, ind := seq_len(.N)]
test[melt(test, measure = patterns("^s\\d+", "^m\\d+"),
     value.name = c("s", "m"))[!is.na(m) & m < z][, 
     .(s_avg = mean(s, na.rm = TRUE)), ind], 
             on = .(ind)][order(ind)][, ind := NULL][]
#    s1 s2 s3 m1 m2 m3  z s_avg
#1:  0 20  1  0 20  1  1   0.0
#2:  4 17  0  4 17  0  5   2.0
#3: 29 11  2 29 NA  1 25   2.0
#4:  9 15  1 NA 15  1  5   1.0
#5:  1 32  4  1 32  1 30   2.5
#6:  2 15  7 22 15 NA 20  15.0
#7: 10 10  0  8 12  0 10   5.0

答案 1 :(得分:1)

似乎你不需要ifelse。只需使用i表达式。

iter <- 1:3
t <- paste0("t", iter)
s <- paste0("s", iter)
m <- paste0("m", iter)
for (i in iter) test[!is.na(get(m[i])) & get(m[i]) < z, (t[i]) := get(s[i])]
test[, s_avg := rowMeans(.SD, na.rm = TRUE), .SDcols = t]
print(test)
#>    s1 s2 s3 m1 m2 m3  z t1 t2 t3 s_avg
#> 1:  0 20  1  0 20  1  1  0 NA NA   0.0
#> 2:  4 17  0  4 17  0  5  4 NA  0   2.0
#> 3: 29 11  2 29 NA  1 25 NA NA  2   2.0
#> 4:  9 15  1 NA 15  1  5 NA NA  1   1.0
#> 5:  1 32  4  1 32  1 30  1 NA  4   2.5
#> 6:  2 15  7 22 15 NA 20 NA 15 NA  15.0
#> 7: 10 10  0  8 12  0 10 10 NA  0   5.0

答案 2 :(得分:1)

方法1:

test[ , avg := rowMeans( test[, .(ifelse( m1 < z, s1, NA),
                                  ifelse( m2 < z, s2, NA),
                                  ifelse( m3 < z, s3, NA)) ],
                         na.rm = TRUE ) ]

方法2:使用表达式

expr <- paste0("ifelse( m", 1:3, " < z, s", 1:3, ", NA )")
test[ , avg := rowMeans( test[, lapply( expr, function(x) eval(parse(text = x)))],
                         na.rm = TRUE ) ]

<强>输出:

test
#    Time Zone quadrat s1 s2 s3 m1 m2 m3  z  avg
# 1:    0    1       1  0 20  1  0 20  1  1  0.0
# 2:    0    1       2  4 17  0  4 17  0  5  2.0
# 3:    0    0       3 29 11  2 29 NA  1 25  2.0
# 4:    7    1       1  9 15  1 NA 15  1  5  1.0
# 5:    7    0       2  1 32  4  1 32  1 30  2.5
# 6:    7    0       3  2 15  7 22 15 NA 20 15.0
# 7:   12    1       1 10 10  0  8 12  0 10  5.0