根据另一个变量的条件,用R data.table

时间:2019-09-06 04:59:06

标签: r data.table reshape melt

具有75万行和近200列的大型 ish 数据表,但这可以做到:

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))

我想根据结果变量中的条件来更改/分组变量var1,var2(以及任何其他变量)的“标签”。一张桌子直观地说明了我要更改的内容

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

当没有结果== F1时,应将var1,var2和其他任何变量的标签分组。从第一张表:更改2;在第二个表中,组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

这两个一个内衬都可以工作,但是正如您可以想象的那样,如果有200列和一些具有上千个水平的变量,这是不可能的。

所以我想出了一个主意:

  1. 为每个变量建立一个关于结果的表
  2. 获取结果== 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

这也有效,并且我避免必须遍历所有这些标签。所以我用一个很酷的计数器= 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天了,几乎所有Stackoverflow链接都是紫色的。现在我不知道该怎么做了。

  • 我做得太过分了吗? -有更好的方法吗?
  • 有什么可以帮助我的方式吗?由于速度原因,我更喜欢data.table,但是在这一点上,我不会抱怨。

谢谢, 奥尔多

1 个答案:

答案 0 :(得分:1)

如果我理解正确, 这就是您想要的:

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)]框架视为“不同的”操作。 它将创建类似于xtabs的内容,但不将0添加到不存在的组合中。

下一帧仅获取每个var*的所有值的集合,并删除与outcome == "F1"同时出现的那些值, 以及NA。 这就像为outcomeF1时永远不会出现的值计算0计数一样。

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

我不确定这是否是最有效的, 但由于您说过您想修改原始的dt (可能保留id), 这就是我想出的。 您可能最后想rm(long)