根据另一个变量的条件,用 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 列和一些超过一千级的变量,这是不可能的。
所以我想出了一个主意:
- 为每个与结果相关的变量建立一个table
- 获取 outcome==F1 出现 0 次的标签
- 用 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 链接都是紫色的。现在我不知道怎么办了。
- 我是不是做得太过了? - 有没有更好的方法?
- 你有什么帮助可以发给我吗?由于速度,我更喜欢 data.table,但在这一点上,我不会抱怨。
谢谢,
奥尔多
如果我没理解错的话,
这就是你想要的:
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
。
这就像计算当 outcome
为 F1
.
时从未出现的值的 0 计数
for 循环中的代码使用 secondary indices notation。
对于每个 var*
列,
它搜索值与 to_group$value
中存在的值匹配的行,
并用 "nF"
.
替换所述值
我不确定这是否最有效,
但既然你说你想修改原来的 dt
(可能要保留 id
),
这就是我想出的。
最后你可能想要rm(long)
。
一个大的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 列和一些超过一千级的变量,这是不可能的。
所以我想出了一个主意:
- 为每个与结果相关的变量建立一个table
- 获取 outcome==F1 出现 0 次的标签
- 用 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 链接都是紫色的。现在我不知道怎么办了。
- 我是不是做得太过了? - 有没有更好的方法?
- 你有什么帮助可以发给我吗?由于速度,我更喜欢 data.table,但在这一点上,我不会抱怨。
谢谢, 奥尔多
如果我没理解错的话, 这就是你想要的:
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
。
这就像计算当 outcome
为 F1
.
for 循环中的代码使用 secondary indices notation。
对于每个 var*
列,
它搜索值与 to_group$value
中存在的值匹配的行,
并用 "nF"
.
我不确定这是否最有效,
但既然你说你想修改原来的 dt
(可能要保留 id
),
这就是我想出的。
最后你可能想要rm(long)
。