在data.table中具有不同列的嵌套ifelse

Uwe*_*Uwe 5 r dataframe dplyr data.table

我需要为a的某些列的每一行计算"最佳值" data.table.每行的最佳值是所选列的给定顺序中的第一个非NA列的值.

作为要求,要包括的列可能因顺序或编号而异.此外,应为每行存储给出最佳值的列的名称.

示例数据

library(data.table)
library(magrittr)
n <- 7
set.seed(1234)
dt <- sample.int(100, n*5, replace = TRUE) %>% 
  ifelse(. < 35, NA, .) %>% 
  matrix(, nrow = n) %>% 
  as.data.table()
Run Code Online (Sandbox Code Playgroud)

样本data.table

   V1 V2 V3 V4 V5
1: NA NA NA NA 84
2: 63 67 84 NA NA
3: 61 52 NA NA 46
4: 63 70 NA NA NA
5: 87 55 NA 82 NA
6: 65 NA NA 53 51
7: NA 93 NA 92 NA
Run Code Online (Sandbox Code Playgroud)

要包含在给定顺序中的列是

selected_cols <- c("V3", "V4", "V1")
Run Code Online (Sandbox Code Playgroud)

硬编码嵌套的预期结果 ifelse

硬编码版本

dt[, best_value := ifelse(!is.na(V3), V3, ifelse(!is.na(V4), V4, V1))]
Run Code Online (Sandbox Code Playgroud)

将给出最佳价值的预期结果

   V1 V2 V3 V4 V5 best_value
1: NA NA NA NA 84         NA
2: 63 67 84 NA NA         84
3: 61 52 NA NA 46         61
4: 63 70 NA NA NA         63
5: 87 55 NA 82 NA         82
6: 65 NA NA 53 51         53
7: NA 93 NA 92 NA         92
Run Code Online (Sandbox Code Playgroud)

但它仍然没有显示哪个列具有最佳值.

在第2行中,列V3已经具有非NA值.对于行5,6和7,采用列V4中的值.最后,列V1给出的行3和4,其中这两个值V3V4 是NA.第1行包含NA,因为所考虑的所有列都是NA.

灵活的for循环方法

for在所选列和一些data.table功能上使用循环

dt[, best_value := NA_integer_]
dt[, best_col := NA_character_]
for (x in selected_cols) {
  dt[is.na(best_value), best_col := ifelse(!is.na(.SD), names(.SD), NA), .SDcols = x]
  dt[is.na(best_value), best_value:= .SD, .SDcols = x]
}
Run Code Online (Sandbox Code Playgroud)

我们得到了完整的预期结果

   V1 V2 V3 V4 V5 best_value best_col
1: NA NA NA NA 84         NA       NA
2: 63 67 84 NA NA         84       V3
3: 61 52 NA NA 46         61       V1
4: 63 70 NA NA NA         63       V1
5: 87 55 NA 82 NA         82       V4
6: 65 NA NA 53 51         53       V4
7: NA 93 NA 92 NA         92       V4
Run Code Online (Sandbox Code Playgroud)

另外,可以容易地改变要包括的列的矢量.

但是,for带有两个语句的循环的方法对我来说看起来很笨拙而且data.table不太像.

有没有更好的方式来实现这些结果与data.table或者dplyr甚至在基础R?

ale*_*laz 7

使用'for'循环并利用list- data.table结构:

ans_col = rep_len(NA_character_, nrow(dt))
ans_val = rep_len(NA_real_, nrow(dt))
for(col in selected_cols) {
    i = is.na(ans_col) & (!is.na(dt[[col]]))
    ans_col[i] = col
    ans_val[i] = dt[[col]][i]   
}
data.frame(ans_val, ans_col)
#  ans_val ans_col
#1      NA    <NA>
#2      84      V3
#3      61      V1
#4      63      V1
#5      82      V4
#6      53      V4
#7      92      V4
Run Code Online (Sandbox Code Playgroud)


akr*_*run 5

我们指定'selected_cols' .SDcols,按行序列分组,我们unlist是Data.table(unlist(.SD))的子集,得到第一个非NA值('j1')的索引,用它来得到对应的'v1'索引和列名,assign(:=)创建两个新列.

dt[, c("best_val", "best_col") := {v1 <- unlist(.SD)
     j1 <- which(!is.na(v1))[1]
     list(v1[j1], names(.SD)[j1]) },
        .SDcols = selected_cols, by = 1:nrow(dt)]
dt
#   V1 V2 V3 V4 V5 best_val best_col
#1: NA NA NA NA 84       NA       NA
#2: 63 67 84 NA NA       84       V3
#3: 61 52 NA NA 46       61       V1
#4: 63 70 NA NA NA       63       V1
#5: 87 55 NA 82 NA       82       V4
#6: 65 NA NA 53 51       53       V4
#7: NA 93 NA 92 NA       92       V4
Run Code Online (Sandbox Code Playgroud)

如果我们使用base R,可以使用行/列索引max.col

setDF(dt)
j1 <-  max.col(!is.na(dt[selected_cols]), "first")
best_value <- dt[selected_cols][cbind(1:nrow(dt),j1)]
best_value
#[1] NA 84 61 63 82 53 92
j2 <- j1*NA^(!rowSums(!is.na(dt[selected_cols])))

best_col <- selected_cols[j2]
best_col
#[1] NA   "V3" "V1" "V1" "V4" "V4" "V4"
Run Code Online (Sandbox Code Playgroud)

  • 谢谢,令人难以置信的代码和平,仍然试图掌握它是如何工作的.刚修改了你的第一个解决方案,用`dt [,best_col:= {v1 < - unlist(.SD)selected_cols [which(!is.na(v1))[1]]},.SDcols = selected_cols来计算`best_col`, by = 1:nrow(dt)]`但你现在已经把它放在一个声明中了! (2认同)