【问题标题】:Relative frequencies/proportions with dplyr create new columns instead of rowsdplyr 的相对频率/比例创建新列而不是行
【发布时间】:2024-01-22 20:06:01
【问题描述】:

这个问题的灵感来自thisthis 问题。

我正在尝试计算每个组中不同值的比例,但我不想为组创建“新”行,而是创建新列。

以上面第二个问题为例。如果我有以下数据:

data <- structure(list(value = c(1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 
2L, 2L, 2L, 3L, 3L, 3L, 3L), class = structure(c(1L, 1L, 1L, 
2L, 2L, 2L, 1L, 1L, 1L, 1L, 2L, 2L, 1L, 1L, 1L, 1L), .Label = c("A", 
"B"), class = "factor")), .Names = c("value", "class"), class = "data.frame", row.names = c(NA, 
-16L))

我可以计算每个值(1,2,3)在每个类(A,B)中的比例:

data %>%
    group_by(value, class) %>%
    summarise(n = n()) %>%
    complete(class, fill = list(n = 0)) %>%
    group_by(class) %>%
    mutate(freq = n / sum(n))
# A tibble: 6 x 4
  value  class     n      freq
  <int> <fctr> <dbl>     <dbl>
1     1      A     3 0.2727273
2     1      B     3 0.6000000
3     2      A     4 0.3636364
4     2      B     2 0.4000000
5     3      A     4 0.3636364
6     3      B     0 0.0000000

但是我最终会为每个值/类对添加一行,而不是我想要这样的东西:

# some code
# A tibble: 6 x 4
   class     n      1        2         3
  <fctr> <dbl>     <dbl>    <dbl>     <dbl>
1    A     11 0.2727273  0.3636364  0.3636364
2    B     5  0.6000000  0.4000000  0.0000000

每个组都有一个列。我可以编写 for 循环来从旧的数据框构造一个新的数据框,但我确信有更好的方法。有什么建议吗?

谢谢

【问题讨论】:

    标签: r group-by dplyr frequency


    【解决方案1】:

    我们可以在最后使用pivot_wider

    library(dplyr)
    library(tidyr)
    data %>%
        group_by(value, class) %>%
        summarise(n = n()) %>%
        complete(class, fill = list(n = 0)) %>%
        group_by(class) %>%
        mutate(freq = n / sum(n), n = sum(n)) %>% 
        pivot_wider(names_from = value, values_from = freq)
    # A tibble: 2 x 5
    # Groups:   class [2]
    #  class     n   `1`   `2`   `3`
    #  <fct> <dbl> <dbl> <dbl> <dbl>
    #1 A        11 0.273 0.364 0.364
    #2 B         5 0.6   0.4   0    
    

    或者正如@IcecreamToucan 提到的,complete 不是必需的,因为pivot_wider 可以选择填充自定义值(默认为 NA)

    data %>% 
        group_by(value, class) %>% 
        summarise(n = n()) %>%  
        group_by(class) %>%
        mutate(freq = n / sum(n), n = sum(n)) %>% 
        pivot_wider(names_from = value, values_from = freq, values_fill = list(freq = 0))
    

    如果我们使用的是以前版本的tidyr,那么使用spread

    data %>%
        group_by(value, class) %>%
        summarise(n = n()) %>%
        complete(class, fill = list(n = 0)) %>%
        group_by(class) %>%
        mutate(freq = n / sum(n), n = sum(n)) %>% 
        spread(value, freq)
    

    【讨论】:

    • 我会调查 pivot_wider。 (我对 R 相当陌生)。
    • 所以这看起来正是我想要的,但它告诉我could not find function "pivot_wider"。我的 tidyr 版本是 0.8.3,所以也许我需要更新它。下次会议后我会在这方面做更多工作。
    • @goryh 来自tidyr。我用packageVersion('tidyr')# [1] ‘1.0.0’
    【解决方案2】:

    使用data.table::dcast 代替pivot_wider 的方法。

    第 1 行:获取每个(值、类)组的计数 (.N),并将其命名为 n

    第 2 行:在每个 class 组中创建新变量:

    • N,之前计数的总和
    • pctN每个n的百分比组成

    第 3 行:以 classN 作为行,value 作为列名,pct 作为列元素,将空元素设置为 0。

    library(magrittr) # For %>%. Not necessary if dplyr is loaded already
    library(data.table)
    setDT(data)
    
    data[, .(n = .N), by = .(value, class)] %>% 
        .[, `:=`(N = sum(n), pct = n/sum(n)), by = class] %>% 
      dcast(class + N ~ value, value.var = 'pct', fill = 0)
    
    #    class  N         1         2         3
    # 1:     A 11 0.2727273 0.3636364 0.3636364
    # 2:     B  5 0.6000000 0.4000000 0.0000000
    

    【讨论】:

    • 谢谢。另一个优先事项出现了,所以我还没有时间尝试你的答案。
    【解决方案3】:

    我们可以使用count来统计valueclassgroup_byclass的出现次数,计算频率,得到宽格式数据。

    library(dplyr)
    library(tidyr)
    
    data %>%
      count(value, class) %>%
      group_by(class) %>%
      mutate(freq = n/sum(n), n = sum(n)) %>%
      pivot_wider(names_from = value, values_from = freq, values_fill = list(freq = 0))
    
    # class     n   `1`   `2`   `3`
    #  <fct> <int> <dbl> <dbl> <dbl>
    #1 A        11 0.273 0.364 0.364
    #2 B         5 0.6   0.4   0    
    

    【讨论】:

    • 这看起来和akrun的asnwer一样。
    • @goryh 我没用过group_by + summarise 而是用count代替了它。
    • 啊我第一次看的不够近。