根据另一个变量的条件,用 R data.table 替换多列中的值

Replace values in multiple columns, on condition from another variable, with R data.table

一个大的ish data.table 有 750k 行和近 200 列,但是这样就可以了:

dt <- data.table(id = 1:15,
             outcome = factor(c(0, 0, 0, 1, 1, 3, 3, 3, 3, 3, 0, 3, 1, 1, 3),
                              labels = c("F0","F1","F3")),
             var1 = c(0, 0, 0, 1, 1, 2, 2, 2, NA, NA, 0, 0, 0, 0, 0),
             var2 = c(0, 0, NA, NA, NA, 0, 0, 0, 0, 0, 6, 6, 4, 4, 4))

我想通过结果变量中的条件 change/group 变量 var1、var2(和任何其他变量)的 "labels"。 table 直观地解释了我想要更改的内容

xtabs(~var1+outcome, dt, addNA = TRUE)
xtabs(~var2+outcome, dt, addNA = TRUE)

当 outcome==F1 没有出现时,var1、var2 和任何其他变量的标签应该分组。从1号开始table:改2;从第 2 个 table,第 0 组和第 6 组开始。

如果水平和变量的数量很少,我可以一个衬垫:

手工完成
dt$var1[dt$var1==2] <- "nF"                                  #data frame way
dt[, var1 := as.character(var1)][var1 == "2", var1 := "nF"]  #data.table way
xtabs(~var1+outcome, dt, addNA = TRUE)                       #check

      outcome
var1   F0 F1 F3
  0     4  2  2
  1     0  2  0
  nF    0  0  3
  <NA>  0  0  2

这两个 one liner 都可以工作,但是你可以想象,有 200 列和一些超过一千级的变量,这是不可能的。

所以我想出了一个主意:

  1. 为每个与结果相关的变量建立一个table
  2. 获取 outcome==F1 出现 0 次的标签
  3. 用 ifelse 语句更改变量

第 1 步

#rebuild dt to try this
(temp1 <- dcast(data = dt,
           formula = var2 ~ outcome,
           value.var = "outcome",
           fun.aggregate = length))

第 2 步

tempvar <- temp1[F1==0 & var2!="NA", var2]

第 3 步

dt[, var2 := ifelse(var2 %in% tempvar, "nF", var2)]
xtabs(~var2+outcome, dt, addNA = TRUE)                       #check


      outcome
var2   F0 F1 F3
  4     0  2  1
  nF    3  0  6
  <NA>  1  2  0

这也有效,而且我避免了必须检查所有这些标签。所以我把它变成了一个 for 循环......有一个非常酷的计数器 =p

# Initialize
tabs <- c()
temp <- c()
counter <- 0

for (i in colnames(dt[, c("var1", "var2")])) {

# counter & progress
counter <- counter + 1
cat("Variable: ", counter, "of", ncol(dt), " ", i, "\n")

# build tables for each variable with dcast
tabs[[i]] <- dcast(data = dt,
                 formula = dt[[i]] ~ outcome,
                 value.var = "outcome",
                 fun.aggregate = length)

# temp: labels to group & set name
temp[[i]] <- data.table(tabs[[i]][F1==0 & dt!="NA", dt],
                      "nF")
colnames(temp[[i]])[1] <- i
}

# Names of the 1st column for each tabs (for some reason, I couldn't do it inside the loop)
for(i in 1:length(tabs)) {colnames(tabs[[i]])[1] <- names(tabs[i])}

这工作正常...到目前为止。现在让我们看看温度和标签:

#temp has the labels to be changed for each variable
temp

#tabs has the tables for each variable with respect to the outcome
tabs

就是这样。我被卡住了,我已经做了 2 天了,几乎所有的 Whosebug 链接都是紫色的。现在我不知道怎么办了。

谢谢, 奥尔多

如果我没理解错的话, 这就是你想要的:

library(data.table)

dt <- data.table(id = 1:15,
                 outcome = factor(c(0, 0, 0, 1, 1, 3, 3, 3, 3, 3, 0, 3, 1, 1, 3),
                                  labels = c("F0","F1","F3")),
                 var1 = as.character(c(0, 0, 0, 1, 1, 2, 2, 2, NA, NA, 0, 0, 0, 0, 0)),
                 var2 = as.character(c(0, 0, NA, NA, NA, 0, 0, 0, 0, 0, 6, 6, 4, 4, 4)))

long <- melt(dt, "outcome", setdiff(names(dt), c("id", "outcome")))

to_group <- long[, .(dummy = .N), by = .(outcome, variable, value)
                 ][, .(value = setdiff(value, c(NA, value[outcome == "F1"]))), by = "variable"]

for (var in unique(to_group$variable)) {
  dt[list(to_group[variable == var, value]), (var) := "nF", on = var]
}

dt[]
    id outcome var1 var2
 1:  1      F0    0   nF
 2:  2      F0    0   nF
 3:  3      F0    0 <NA>
 4:  4      F1    1 <NA>
 5:  5      F1    1 <NA>
 6:  6      F3   nF   nF
 7:  7      F3   nF   nF
 8:  8      F3   nF   nF
 9:  9      F3 <NA>   nF
10: 10      F3 <NA>   nF
11: 11      F0    0   nF
12: 12      F3    0   nF
13: 13      F1    0    4
14: 14      F1    0    4
15: 15      F3    0    4

使用 melt 更改为长格式可以更轻松地为每个 var* 列应用后续逻辑。 对于您的演示数据, long 看起来像这样:

> head(long)
   outcome variable value
1:      F0     var1     0
2:      F0     var1     0
3:      F0     var1     0
4:      F1     var1     1
5:      F1     var1     1
6:      F3     var1     2

因此,您可以将[, .(dummy = .N), by = .(outcome, variable, value)]帧视为"distinct"操作。 它将创建类似于 xtabs 的内容,但不会向不存在的组合添加 0。

下一帧简单地获取每个 var* 的所有值的集合,并删除与 outcome == "F1" 同时出现的值, 以及 NA。 这就像计算当 outcomeF1.

时从未出现的值的 0 计数

for 循环中的代码使用 secondary indices notation。 对于每个 var* 列, 它搜索值与 to_group$value 中存在的值匹配的行, 并用 "nF".

替换所述值

我不确定这是否最有效, 但既然你说你想修改原来的 dt (可能要保留 id), 这就是我想出的。 最后你可能想要rm(long)