【问题标题】:How to access an element of a vector while using lapply over a list在列表上使用 lapply 时如何访问向量的元素
【发布时间】:2019-11-01 17:21:52
【问题描述】:

我想向数据框添加行,使第一列包含 1 到 10 之间的所有数字,并且根据数字,第二列应该有一定的输出。

我可以使用如下的 for 循环来做到这一点:

Dt <- data.frame(ID = c(1,2,5,7,8), Value = "x", stringsAsFactors = FALSE)
Special_cases <- c(3,4)

for (i in 1:10){

  if( i %in% Dt$ID){

    Dt <- Dt

  } else if (i %in% Special_cases){

    Dt <- rbind(Dt, c(i,"y"))

  } else {

    Dt <- rbind(Dt, c(i,"z"))

  }

}

 ID Value
1   1     x
2   2     x
3   5     x
4   7     x
5   8     x
6   3     y
7   4     y
8   6     z
9   9     z
10 10     z

虽然这可行,但我想摆脱使用 for 循环的坏习惯,但我一直在努力使用 lapply 重新编写它。我不确定如何处理我调用i 的第一个 if 语句。

那么我怎样才能将这个 for 循环转换为 lapply 呢?我也不确定 lapply 的第一个参数是什么。

【问题讨论】:

  • 这里的逻辑是什么?所有x 值是否总是来自第一个data.frame?你只是硬编码y 3 和4 的值?其他都是z?我不清楚输入是什么或输出在不同条件下会是什么样子。
  • 没错。我最初问题的数据框需要 ID = 1:100 作为第一列,但由于我的问题的性质,并非所有 100 个值都在数据框中。所以我需要一种方法来检查,例如 ID = 15 是否从数据框中丢失,将创建一个 ID = 15 的行,并且 value = z 因为 15 不是特殊情况。我已经调整了问题以强调这一点。

标签: r for-loop apply


【解决方案1】:

我确信有一个更优雅的解决方案,但这个可行。首先,在所需 ID 的向量上运行 lapply,根据您的规范创建单行数据框列表。

results <- lapply(seq(10), function(i) {

    if (i %in% Dt$ID) {

        Dt[which(Dt$ID==i),]

    } else if (i %in% c(3,4)) {

        data.frame(ID = i, Value = "y")        

    } else {

        data.frame(ID = i, Value = "z") 

    }

})

然后,将该列表折叠成一个数据框。您还可以通过将调用嵌套到lapply 将其滚动到上一步,其中results 出现在此处。

Dt2 <- do.call(rbind.data.frame, results)

如果您不介意特别引入对 tidyverse' 或 purrr 的依赖关系,您还可以在上面的代码块中将 map_dfr 替换为 lapply,它会继续并折叠结果在同一步骤中列出一个数据框。但请注意,它还会抛出有关将因子转换为字符以执行此操作的警告,即使所有这些 ID 一开始都是字符。

这是结果。请注意,如果您关心按该功能对事物进行分组,您仍然需要根据 Value 进行排序。

   ID Value
1   1     x
2   2     x
11  3     y
12  4     y
3   5     x
13  6     z
4   7     x
5   8     x
14  9     z
15 10     z

【讨论】:

  • 非常感谢您的回答。你会说这是比 for 循环更好的方法吗?我的 for 循环似乎更容易阅读,但整个 R 社区都坚持用应用函数替换 for 循环
  • 应该更快。对于小型数据集,这种差异可能无关紧要,但随着数据集的增长,它可能会很显着。
【解决方案2】:

这是使用来自dplyr 的合并的另一种方式

library(dplyr)
data.frame(ID=1:10) %>% left_join(Dt) %>% 
  left_join(tibble(ID=Special_cases, Value2="y")) %>% 
  mutate(Value=coalesce(Value, Value2, "z"), Value2=NULL)

或者这是另一种不涉及循环或 lapply 的方式。只需查看缺少的内容并一次性添加所有内容。

if (any(!Special_cases %in% Dt$ID)) {
  Dt <- rbind(Dt, data.frame(ID = setdiff(Special_cases, Dt$ID), Value = "y", stringsAsFactors = FALSE))
}
if (any(!1:10 %in% Dt$ID)) {
  Dt <- rbind(Dt, data.frame(ID = setdiff(1:10, Dt$ID), Value = "z", stringsAsFactors = FALSE))
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-10-31
    • 2019-08-23
    • 2021-12-28
    • 1970-01-01
    • 2015-05-30
    • 2021-05-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多