我需要为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()
示例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
要包含在给定顺序中的列是
selected_cols <- c("V3", "V4", "V1")
ifelse
硬编码版
dt[, best_value := ifelse(!is.na(V3), V3, ifelse(!is.na(V4), V4, V1))]
将给出最佳值的预期结果
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
但它仍然没有显示哪个列的最佳值。
在第2行中,V3
列已经有一个非NA值。对于第5,6和7行,将获取列V4
中的值。最后,列V1
给出了第3行和第4行的值,其中V3
和V4
都是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]
}
我们得到了完整的预期结果
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
此外,可以轻松更改要包含的列的向量。
然而,使用带有两个语句的for
循环的方法对我来说看起来很笨拙而不是data.table
- 就像。
使用data.table
或dplyr
甚至基数R是否有更好的方法来实现这些结果?
答案 0 :(得分: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
答案 1 :(得分:5)
我们在.SDcols
中指定'selected_cols',按行序列分组,我们unlist
Data.table的子集(unlist(.SD)
),获取第一个非索引NA值('j1'),用它来获取对应于索引和列名的'v1',赋值(:=
)以创建两个新列。
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
如果我们使用的是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"