【问题标题】:Replace values in multiple columns, on condition from another variable, with R data.table用 R data.table 根据另一个变量的条件替换多列中的值
【发布时间】:2020-01-08 23:24:10
【问题描述】:

一个大的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))

我想通过结果变量中的条件更改/分组变量 var1、var2 (和任何其他变量)的“标签”。一张表格直观地解释了我想要更改的内容

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

当未出现结果==F1 时,应将 var1、var2 和任何其他变量的标签分组。从第 1 个表开始:更改 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 语句更改变量

第一步

#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])}

这工作正常......到目前为止。现在让我们看看 temps 和 tabs:

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

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

就是这样。我被困住了,我已经在这两天了,几乎所有的 Stackoverflow 链接都是紫色的。现在我不知道该怎么做。

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

谢谢, 阿尔多

【问题讨论】:

  • 酷计数器。但是,您能粘贴预期的输出应该是什么样子吗?
  • @NelsonGon 编辑以显示预期结果
  • 为什么不先融化再进行操作?
  • @chinsoon12,是的!发布后,我与融化非常接近。我想睡个好觉会创造奇迹;但它仍然不能 100% 工作。亚历克西斯的答案有效,但我仍在学习为什么会这样......

标签: r data.table reshape melt


【解决方案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。 这就像计算 outcome 为 F1 时从未出现的值的 0 计数。

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

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

【讨论】:

  • 这个有效。我看到你先改成as.character; rm(long) 对于我的原始数据集将是必要的。睡了一夜好觉后,我想我应该试试melt。我非常接近我的方法,但你的答案有效。此外,二级索引符号;直到
猜你喜欢
  • 2022-11-30
  • 1970-01-01
  • 2021-12-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-01
  • 1970-01-01
相关资源
最近更新 更多