【问题标题】:Use "i" in a for loop to apply function to certain columns在 for 循环中使用“i”将函数应用于某些列
【发布时间】:2019-10-18 07:04:09
【问题描述】:

我正在使用 for (i in cols) 循环遍历列表 cols = c("x", "y", "z"),但是:

  1. 使用“:=”创建新列时,无法将“i”用作列名
  2. 我创建了 mode_func 来获取向量中出现频率最高的字符串,但是当我使用 lapply 时,“i”似乎没有用作列。

有人可以帮我理解 for 循环中“i”的问题和动态吗?非常感谢!

set.seed(10)
dummy = data.table(id = c("11", "11", "11", "22", "22", "22", "33", "33", "33", "33"),
                   x = sample(c("a", "b", "c"), 10, replace = T),
                   y = sample(c("a", "b", "c"), 10, replace = T),
                   z = sample(c("a", "b", "c"), 10, replace = T),
                   i = sample(3, 10, replace = T),
                   j = sample(3, 10, replace = T),
                   k = sample(3, 10, replace = T))
mode_func <- function(x) {
  uniqx <- unique(na.omit(x))
  uniqx[which.max(tabulate(match(x, uniqx)))]
}

(1) 最频繁

cols = c("x", "y", "z")
for (i in cols){
  dummy[, as.character(i) := mode_func(i), by = "id"]
}

# The following works but it's too much coding!
dummy[, x := mode_func(x), by = "id"]
dummy[, y := mode_func(y), by = "id"]
dummy[, z := mode_func(z), by = "id"]

预期的结果如下所示:

    id x y z
 1: 11 b b c
 2: 11 b b c
 3: 11 b b c
 4: 22 a b b
 5: 22 a b b
 6: 22 a b b
 7: 33 a a c
 8: 33 a a c
 9: 33 a a c
10: 33 a a c

(2) 我也尝试了平均值,但这对我不起作用:

cols = c("i", "j", "k")
dummy[, (cols) := lapply(.SD, function(x) round(mean(x, na.rm = T))), .SDcols = cols, by = "id"]

【问题讨论】:

  • 为了运行你的循环,你可以做for (i in cols) dummy[, (i) := mode_func(.SD), .SDcols = i, by = id]。关于您第二次尝试中的错误,它只是类型不匹配。您的代码很好,但您试图用双精度组覆盖整数列。所以有一个阶段,当列的一部分仍然是整数(1、2、3)并且它的一部分变成双倍(例如,2.5)并且 R 不能有一个包含两个类的列。您可以通过创建新列来说明这一点,例如,dummy[, paste0(cols, "_mean") := lapply(.SD, mean, na.rm = TRUE), .SDcols = cols, by = id]
  • 在使用列名作为索引时也应该小心,它会使环境变得混乱。你们都有一个名为i 的列,并且您还在i 上运行循环。这会弄乱mget/get 等函数。因此,如果您将i 替换为a,您也可以使用for (a in cols) dummy[, (a) := mode_func(get(a)), by = id]。您不能只运行mode_func(i) 的原因是因为 data.table 在 j 中进行非标准评估,因此它希望列名不被引用 - 例如i 而不是 "i"
  • 非常感谢您的详细解释和解决方案!祝你有美好的一天,来自巴塞罗那的问候:)

标签: r for-loop data.table


【解决方案1】:

您可以使用lapply直接在cols上调用mode_func

library(data.table)
dummy[, (cols) := lapply(.SD, mode_func), by = "id"]

dummy
#    id x y z
# 1: 11 b b c
# 2: 11 b b c
# 3: 11 b b c
# 4: 22 a b b
# 5: 22 a b b
# 6: 22 a b b
# 7: 33 a a c
# 8: 33 a a c
# 9: 33 a a c
#10: 33 a a c

就运行for 循环而言,当您为每一列分别调用mode_func 函数时,您需要使用.SDcols 对该特定列进行子集化,并将.SD 值作为输入传递给函数每次迭代。 (感谢@David Arenburg 的评论)

for (i in cols){
   dummy[, (i) := mode_func(.SD), by = "id", .SDcols = i]
}

【讨论】:

  • 感谢它运行良好,但是,我很想了解“i”如何在 for 循环中进行交互。顺便说一句,我也尝试使用您提供的类似方式对一些列进行平均,您能帮我解决我遇到的错误吗?我在虚拟表中加入了新的 i、j、k 列。非常感谢!!
  • @ThomasSun David 已经很好地解释了(在 cmets 中)您尝试 for 循环的问题是什么。他还解释了您的第二次尝试。假设您有数字列,您可以创建新列来存储它们。
【解决方案2】:

我们可以从dplyr使用mutate_at

library(dplyr)
dummy %>% 
    group_by(id) %>% 
    mutate_at(vars(cols), mode_func)

【讨论】:

    猜你喜欢
    • 2022-01-03
    • 1970-01-01
    • 1970-01-01
    • 2023-02-02
    • 1970-01-01
    • 1970-01-01
    • 2015-02-06
    • 2017-07-21
    • 2018-11-10
    相关资源
    最近更新 更多