【问题标题】:How to run lapply on a list of lists to generate a report table for each list如何在列表列表上运行 lapply 以为每个列表生成报告表
【发布时间】:2020-08-27 09:57:06
【问题描述】:

我有一个 listlists 包含总频率,我想运行 function 以使用 lapply 为每个 list 生成结果表。

鉴于这些数据:

# Exemplary list of lists
list1 <- list (1, 2, 3) 
list2 <- list (4, 5, 6)
list3 <- list (7, 8, 9)
names(list1) <- c("arg1", "arg2", "arg3")
names(list2) <- c("arg1", "arg2", "arg3")
names(list3) <- c("arg1", "arg2", "arg3")
lol <- list(list1, list2, list3)

# Function for returning absolute numbers (counts) and percentage
fun_freq <- lapply(lol, function(x) {
  c(n=x, 
    Percent=(x/(Reduce('+',x)) * 100)) 
})

# Transposing output from fun_freq, set rounding options
stratified_dat <- data.frame(t(sapply(fun_freq,c))) %>%
  mutate_at(2, round, 1)

# Setting colnames and rownames
colnames(stratified_dat) <- c ("n", "%")
rownames(stratified_dat) <- c ("arg1", "arg2", "arg3")
stratified_dat

如果我选择一个 list,我目前的方法似乎可以正常工作,例如lol[[1]]。但是,在listslist 中的多个lists 上运行function 以获取每个list 的频率表似乎是一个问题。我需要如何修改我的代码?

【问题讨论】:

    标签: r functional-programming


    【解决方案1】:

    你可以像这样遍历列表

    for(lmao in lol){
    
    
    fun_freq <- lapply(lmao, function(x) {
      c(n=x, 
        Percent=(x/(Reduce('+',x)) * 100)) 
    })
    
    # Transposing output from fun_freq, set rounding options
    stratified_dat <- data.frame(t(sapply(fun_freq,c))) %>%
      mutate_at(2, round, 1)
    
    # Setting colnames and rownames
    colnames(stratified_dat) <- c ("n", "%")
    rownames(stratified_dat) <- c ("arg1", "arg2", "arg3")
    print(stratified_dat)
    
    }
    

    输出

         n   %
    arg1 1 100
    arg2 2 100
    arg3 3 100
         n   %
    arg1 4 100
    arg2 5 100
    arg3 6 100
         n   %
    arg1 7 100
    arg2 8 100
    arg3 9 100
    

    编辑:

    或者,根据您的数据和预期输出,您可以尝试

    lol <- unlist(lol,recursive=F) 
    

    你会得到你的原始代码

      n   %
    1 1 100
    2 2 100
    3 3 100
    4 4 100
    5 5 100
    6 6 100
    7 7 100
    8 8 100
    9 9 100
    

    【讨论】:

    • @Daniel O 太好了 :) 你还知道是否有某种方法可以在lol 上使用applylmao 的嵌套结构?
    • 你当然可以,只是很难理解。例如:lapply( lol, function(x) sapply(x, max)) 它很快变得不可读。除非每个操作都被矢量化,否则在 for 循环上使用 lapply 将没有任何好处
    • 是的,包含多个lapply 实例的代码看起来并不方便。我想对于这个分析,我将不得不选择这里提供的解决方案之一。所以谢谢大家的想法和想法!
    【解决方案2】:

    我注意到虽然@daniel-o 使用您的方法和功能生成了一个很好的答案,但我认为数学不正确。正如我认为你不希望每个百分比都达到 100%。我不太喜欢lapply,更喜欢purrrtidyverse 的其余部分。所以这是一个替代答案,我相信它可以满足您的要求。

    library(dplyr)
    library(purrr)
    library(tidyr)
    
    map_dfr(lol, `[`, c("arg1", "arg2", "arg3"), .id = "Which_list") %>%
      pivot_longer(cols = starts_with("arg"), values_to = "n") %>%
      group_by(Which_list) %>%
      mutate(pct = n / sum(n) * 100)
    
    #> # A tibble: 9 x 4
    #> # Groups:   Which_list [3]
    #>   Which_list name      n   pct
    #>   <chr>      <chr> <dbl> <dbl>
    #> 1 1          arg1      1  16.7
    #> 2 1          arg2      2  33.3
    #> 3 1          arg3      3  50  
    #> 4 2          arg1      4  26.7
    #> 5 2          arg2      5  33.3
    #> 6 2          arg3      6  40  
    #> 7 3          arg1      7  29.2
    #> 8 3          arg2      8  33.3
    #> 9 3          arg3      9  37.5
    

    您的数据:

    # Exemplary list of lists
    list1 <- list (1, 2, 3) 
    list2 <- list (4, 5, 6)
    list3 <- list (7, 8, 9)
    names(list1) <- c("arg1", "arg2", "arg3")
    names(list2) <- c("arg1", "arg2", "arg3")
    names(list3) <- c("arg1", "arg2", "arg3")
    lol <- list(list1, list2, list3)
    

    reprex package (v0.3.0) 于 2020-05-11 创建

    【讨论】:

    • 感谢您提供除了lapply 方法之外的建议。我也喜欢与tidyverse 合作。是的,关于百分比列的数学,你是对的。但是,在如下所示的单个列表上运行 fun_freq 似乎可行。 fun_freq &lt;- lapply(lol[[1]], function(x) { c(n=x, Percent=(x/(Reduce('+',lol[[1]])) * 100)) })
    • 这里有很多可行的答案,尽情享受吧。
    • 绝对!有趣的是,您的方法和@user12728748 中常见的一件事是,你们俩似乎都使用长数据格式来解决这个问题。所以这是我再次面对这类数据时应该记住的一件事。
    【解决方案3】:

    我可能会误解您在这里要计算的内容,但是如果您想要每个列表的 arg1、arg2、arg3 的分数,或者所有列表中 arg、arg2、arg3 的分数,您可以执行以下操作来获得比例:

    lol <- setNames(list(
      setNames(as.list(1:3), c("arg1", "arg2", "arg3")),
      setNames(as.list(4:6), c("arg1", "arg2", "arg3")),
      setNames(as.list(7:9), c("arg1", "arg2", "arg3"))
    ),  paste0("list", 1:3))
    loldf <- do.call(rbind, lapply(lol, unlist))
    loldf
    #>       arg1 arg2 arg3
    #> list1    1    2    3
    #> list2    4    5    6
    #> list3    7    8    9
    
    # proportion of each argument per list
    round(100*prop.table(loldf, 1), 2)
    #>        arg1  arg2 arg3
    #> list1 16.67 33.33 50.0
    #> list2 26.67 33.33 40.0
    #> list3 29.17 33.33 37.5
    
    # proportion of single arguments over lists
    round(100*prop.table(loldf, 2), 2)
    #>        arg1  arg2  arg3
    #> list1  8.33 13.33 16.67
    #> list2 33.33 33.33 33.33
    #> list3 58.33 53.33 50.00
    

    如果您喜欢data.table,您可以像这样获得相同的结果(在此处拆分为列表):

    library(data.table)
    lol <- setNames(list(
      setNames(as.list(1:3), c("arg1", "arg2", "arg3")),
      setNames(as.list(4:6), c("arg1", "arg2", "arg3")),
      setNames(as.list(7:9), c("arg1", "arg2", "arg3"))
    ),  paste0("list", 1:3))
    lmao <- melt(rbindlist(lol, idcol = "name"), id.vars="name")
    
    # proportion of each argument per list
    split(lmao[, .(arg=unique(variable), n=value, Percent=round(100*value/sum(value), 2)), 
        by=.(name)], by="name", keep.by = FALSE)
    #> $list1
    #>     arg n Percent
    #> 1: arg1 1   16.67
    #> 2: arg2 2   33.33
    #> 3: arg3 3   50.00
    #> 
    #> $list2
    #>     arg n Percent
    #> 1: arg1 4   26.67
    #> 2: arg2 5   33.33
    #> 3: arg3 6   40.00
    #> 
    #> $list3
    #>     arg n Percent
    #> 1: arg1 7   29.17
    #> 2: arg2 8   33.33
    #> 3: arg3 9   37.50
    
    # proportion of single arguments over lists
    split(lmao[, .(list=unique(name), n=value, Percent=round(100*value/sum(value), 2)), 
        by=.(variable)], by="variable", keep.by = FALSE)
    #> $arg1
    #>     list n Percent
    #> 1: list1 1    8.33
    #> 2: list2 4   33.33
    #> 3: list3 7   58.33
    #> 
    #> $arg2
    #>     list n Percent
    #> 1: list1 2   13.33
    #> 2: list2 5   33.33
    #> 3: list3 8   53.33
    #> 
    #> $arg3
    #>     list n Percent
    #> 1: list1 3   16.67
    #> 2: list2 6   33.33
    #> 3: list3 9   50.00
    

    reprex package (v0.3.0) 于 2020-05-11 创建

    编辑: tidyverse 版本

    如果您愿意,下面是使用tidyverse 函数而不是data.table 的版本。

    library(tidyverse)
    lol <- setNames(list(
      setNames(as.list (1:3), c("arg1", "arg2", "arg3")),
      setNames(as.list (4:6), c("arg1", "arg2", "arg3")),
      setNames(as.list (7:9), c("arg1", "arg2", "arg3"))
    ),  paste0("list", 1:3))
    
    
    lol %>% bind_rows(,.id="list") %>% 
      pivot_longer(-list) %>% 
      group_by(list) %>% 
      mutate(Percent=round(100*value/sum(value), 2)) %>% 
      split(., .$list)
    #> $list1
    #> # A tibble: 3 x 4
    #> # Groups:   list [1]
    #>   list  name  value Percent
    #>   <chr> <chr> <int>   <dbl>
    #> 1 list1 arg1      1    16.7
    #> 2 list1 arg2      2    33.3
    #> 3 list1 arg3      3    50  
    #> 
    #> $list2
    #> # A tibble: 3 x 4
    #> # Groups:   list [1]
    #>   list  name  value Percent
    #>   <chr> <chr> <int>   <dbl>
    #> 1 list2 arg1      4    26.7
    #> 2 list2 arg2      5    33.3
    #> 3 list2 arg3      6    40  
    #> 
    #> $list3
    #> # A tibble: 3 x 4
    #> # Groups:   list [1]
    #>   list  name  value Percent
    #>   <chr> <chr> <int>   <dbl>
    #> 1 list3 arg1      7    29.2
    #> 2 list3 arg2      8    33.3
    #> 3 list3 arg3      9    37.5
    

    reprex package (v0.3.0) 于 2020-05-12 创建

    【讨论】:

    • 我的目标是展示每个列表中每个参数的比例。虽然我之前没有使用过data.table,但这是一个简洁的解决方案。如果我对您的代码的理解正确,您正在使用带有 melt 的长数据格式,然后在计算百分比的附加列的步骤中将这个更长的 list 拆分为 name 先前在 setNames 中定义。
    • 正确。我已经添加了一个使用 tidyverse 函数的版本,如果你喜欢那些。
    • 哇,谢谢!我非常感谢您花时间在您的答案中添加tidyverse 建议。如果我可以再投票一次,我肯定会这样做。
    猜你喜欢
    • 1970-01-01
    • 2021-08-16
    • 1970-01-01
    • 2019-10-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-06
    • 2023-03-30
    相关资源
    最近更新 更多