【问题标题】:R - How to convert this nested for loop into an lapply function that can mutate a listR - 如何将此嵌套的 for 循环转换为可以改变列表的 lapply 函数
【发布时间】:2020-07-02 02:21:29
【问题描述】:

我的数据看起来像这样

aList <- list(a1 = c("apple", "banana", "orange", "strawberry", "cherry"),
              a2 = c("banana", "cherry", "apple"),
              a3 = c("apple", "strawberry", "pineapple"),
              a4 = c("raspberry", "strawberry", "apple"),
              a5 = c("pineapple", "lemon", "orange", "banana", "apple"),
              a6 = c("lemon", "apple", "blueberry"),
              a7 = c("watermelon", "apple", "banana", "mango"),
              a8 = c("mango", "cherry", "apple", "lemon"),
              a9 = c("orange", "banana", "strawberry"),
              a10 = c("mango", "strawberry"))

我想把它变成垂直格式,就像你运行这段代码时发生的那样:

vertical_data <- list()
for (x in names(aList)) {
  for (y in aList[[x]]) {
    if (is.null(vertical_data[[y]])) {
      vertical_data[[y]] <- x
    } else {
      vertical_data[[y]] <- c(x, vertical_data[[y]])
    }
  }
}
vertical_data

我希望每个条目都告诉我特定水果的产地。

使用双 for 循环很容易做到这一点。但是当我用嵌套的 lapply 函数做同样的事情时,它看起来根本没有修改列表(即vertical_data)。这是为什么?我想使用 apply 函数执行此操作的原因是它更快。我的实际数据集将包含数千个项目和“水果”。使用 for 循环会花费很长时间。

非常感谢您的帮助。

谢谢

【问题讨论】:

    标签: r loops lapply


    【解决方案1】:

    我们可以在unlisted 数据上使用split

    split(rep(names(aList), lengths(aList)), unlist(aList))
    

    或者另一种选择是将stack 转换为两列“data.frame”,然后执行split

    with(stack(aList), split(as.character(ind), values))
    #$apple
    #[1] "a1" "a2" "a3" "a4" "a5" "a6" "a7" "a8"
    
    #$banana
    #[1] "a1" "a2" "a5" "a7" "a9"
    
    #$blueberry
    #[1] "a6"
    
    #$cherry
    #[1] "a1" "a2" "a8"
    
    #$lemon
    #[1] "a5" "a6" "a8"
    
    #$mango
    #[1] "a7"  "a8"  "a10"
    
    #$orange
    #[1] "a1" "a5" "a9"
    
    #$pineapple
    #[1] "a3" "a5"
    
    #$raspberry
    #[1] "a4"
    
    #$strawberry
    #[1] "a1"  "a3"  "a4"  "a9"  "a10"
    
    #$watermelon
    #[1] "a7"
    

    或者正如@rawr 提到的那样

    unstack(stack(aList)[2:1])
    

    关于lapplyfor 循环内的分配,它是基于环境的。在for循环中,赋值修改了全局env中的对象,但是在lapply中,它是一个自包含的env或者必须做&lt;&lt;-(不建议)或者指定env为全局env

    vertical_data <- list()
    lapply(names(aList), function(x) lapply(aList[[x]], 
          function(y) if (is.null(vertical_data[[y]])) {
             vertical_data[[y]] <<- x
             } else {vertical_data[[y]] <<- c(x, vertical_data[[y]])
             }))
    

    【讨论】:

    • unstack(stack(aList)[2:1])
    • 谢谢!为什么不建议使用
    【解决方案2】:

    我们可以使用enframe将名称列表转换为数据框,然后根据value拆分name

    tibble::enframe(aList) %>% tidyr::unnest(value) %>% {split(.$name, .$value)}
    
    #$apple
    #[1] "a1" "a2" "a3" "a4" "a5" "a6" "a7" "a8"
    
    #$banana
    #[1] "a1" "a2" "a5" "a7" "a9"
    
    #$blueberry
    #[1] "a6"
    
    #$cherry
    #[1] "a1" "a2" "a8"
    
    #$lemon
    #[1] "a5" "a6" "a8"
    
    #$mango
    #[1] "a7"  "a8"  "a10"
    
    #$orange
    #[1] "a1" "a5" "a9"
    
    #$pineapple
    #[1] "a3" "a5"
    
    #$raspberry
    #[1] "a4"
    
    #$strawberry
    #[1] "a1"  "a3"  "a4"  "a9"  "a10"
    
    #$watermelon
    #[1] "a7"
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-09
      • 2022-01-24
      • 2021-09-16
      • 2017-04-20
      • 2020-06-03
      相关资源
      最近更新 更多