计算每一列中各组内总计的百分比并进行转置

时间:2018-10-03 20:14:23

标签: r dplyr

是否可以创建以下输出(假设有很多ID和更多属性)? 在按ID中的ATT1然后按ATT2等计算总计的百分比后,我陷入了困境。不知道如何将行变成列标题并进行汇总。

输入文件(R中的df):

ID  ATT1    ATT2    ATT3    ATT4    Value
1      a       x       d       i    10
1      a       y       d       j    10
1      a       y       d       k    10
1      b       y       c       k    10
1      b       y       c       l    10
2      a       x       c       k    20
…               

我希望输出文件看起来像(ATT4_l被切断):

 ID ATT1_a  ATT1_b  ATT2_x  ATT2_y  ATT3_d  ATT3_c  ATT4_i  ATT4_j  ATT4_k   
  1  0.6     0.4     0.2     0.8     0.6     0.4     0.2     0.2     0.4    
 ...

我尝试使用dplyr

df %>% group_by(ID, ATT1) %>% mutate(proc = (Value/sum(Value) * 100))

但是我不确定一旦计算出所有的ATT就可以将它们分成几列并进行汇总,以便每个ID仅包含1行数据,该怎么办。

2 个答案:

答案 0 :(得分:1)

您可以使用tidyverse的两个主要功能来完成此操作:dplyr用于计算,tidyr用于重塑数据。重塑有些复杂,因此我将其分为几步。

library(dplyr)
library(tidyr)
...

如果您将数据从其原始的宽格式gather转换为长格式,则将有一列ID,一列ATTx值,一列字母(不知道这些,因此我实际上将其称为letters)和一列值。通过这种格式,您可以按ID,ATT和字母的组合对观察进行分组,然后可以按布局方式将ATT和字母粘贴在一起。

df %>%
  gather(key = att, value = letter, -ID, -Value) %>%
  head()
#> # A tibble: 6 x 4
#>      ID Value att   letter
#>   <int> <int> <chr> <chr> 
#> 1     1    10 ATT1  a     
#> 2     1    10 ATT1  a     
#> 3     1    10 ATT1  a     
#> 4     1    10 ATT1  b     
#> 5     1    10 ATT1  b     
#> 6     2    20 ATT1  a

分组后,计算每个ID / ATT /字母组合的总值:

df %>%
  gather(key = att, value = letter, -ID, -Value) %>%
  group_by(ID, att, letter) %>%
  summarise(group_val = sum(Value)) %>%
  head()
#> # A tibble: 6 x 4
#> # Groups:   ID, att [3]
#>      ID att   letter group_val
#>   <int> <chr> <chr>      <int>
#> 1     1 ATT1  a             30
#> 2     1 ATT1  b             20
#> 3     1 ATT2  x             10
#> 4     1 ATT2  y             40
#> 5     1 ATT3  c             20
#> 6     1 ATT3  d             30

使用mutate,您可以计算每个观察值在其较大组中的份额。 mutate删除了分组层次结构的一层,因此这是给定ID和ATT中每个字母的值份额。由于您不再需要总价值,只需共享它们的份额,然后删除该列,然后将ATT和字母与unite放在一起即可。

df %>%
  gather(key = att, value = letter, -ID, -Value) %>%
  group_by(ID, att, letter) %>%
  summarise(group_val = sum(Value)) %>%
  mutate(share = group_val / sum(group_val)) %>%
  select(-group_val) %>%
  unite(group, att, letter, sep = "_") %>%
  head()
#> # A tibble: 6 x 3
#> # Groups:   ID [1]
#>      ID group  share
#>   <int> <chr>  <dbl>
#> 1     1 ATT1_a   0.6
#> 2     1 ATT1_b   0.4
#> 3     1 ATT2_x   0.2
#> 4     1 ATT2_y   0.8
#> 5     1 ATT3_c   0.4
#> 6     1 ATT3_d   0.6

现在,您拥有了所需的所有信息,只需将其转换为宽格式,即可将group列中的值转换为单独的列。您可以使用spread进行此操作:

df %>%
  gather(key = att, value = letter, -ID, -Value) %>%
  group_by(ID, att, letter) %>%
  summarise(group_val = sum(Value)) %>%
  mutate(share = group_val / sum(group_val)) %>%
  select(-group_val) %>%
  unite(group, att, letter, sep = "_") %>%
  spread(key = group, value = share)
#> # A tibble: 2 x 11
#> # Groups:   ID [2]
#>      ID ATT1_a ATT1_b ATT2_x ATT2_y ATT3_c ATT3_d ATT4_i ATT4_j ATT4_k
#>   <int>  <dbl>  <dbl>  <dbl>  <dbl>  <dbl>  <dbl>  <dbl>  <dbl>  <dbl>
#> 1     1    0.6    0.4    0.2    0.8    0.4    0.6    0.2    0.2    0.4
#> 2     2    1     NA      1     NA      1     NA     NA     NA      1  
#> # ... with 1 more variable: ATT4_l <dbl>

请注意,这里没有填写NA,其中没有观察到ID / ATT /字母的组合。我假设您将拥有比发布的样本中更完整的数据。

reprex package(v0.2.1)于2018-10-03创建

答案 1 :(得分:0)

我相信您正在寻找reshape2包裹

library(reshape2)

df.new <- dcast(df,
                formula = ID~ATT1,
                value.var = "proc",
                fun.aggregate = mean)

尽管这不能完全解决您的问题-我建议您先这样做以使数据整洁

df.tidy <- melt(df, 
                id.vars = c("ID","Value"),
                variable.name = "ATT1_4",
                value.name = "att.factor")
df.tidy <- df.tidy %>% group_by(ID, att.factor) %>% mutate(proc = (Value/sum(Value)*100))
df.new <- dcast(df.tidy,
                formula = ID~att.factor,
                value.var = "proc",
                fun.aggregate = mean)

对于df.tidy中未表示的任何组合,将返回NaN。您可以使用fill参数为它们分配一个值。