179  group_byでの集計

library(tidyverse)

id <- 1:15

age <- c(30,40,65,34,86,
         43,64,26,87,45,
         76,24,97,45,34)

gender <- c("f","m","f","f","f",
            "m","m","f","f","m",
            "f","f","m","m","m")

isx <- c(F,T,F,F,T,
         T,T,F,T,F,
         T,F,F,F,T)

dat <- tibble(id     = id    , age = age, 
              gender = gender, isx = isx)

ひとつ前の動画での課題、やってみましたか?次のようなスクリプトができているば、こちらが意図していた通りとなります。

dat %>% 
  group_by(isx) %>% 
  summarise(
    age_mean = mean(age),
    age_min  = min(age),
    age_max  = max(age),
    gender_m_n  = sum(gender=="m"),
    gender_m_p  = 100*gender_m_n/n()
  )
# A tibble: 2 × 6
  isx   age_mean age_min age_max gender_m_n gender_m_p
  <lgl>    <dbl>   <dbl>   <dbl>      <int>      <dbl>
1 FALSE     45.8      24      97          3       37.5
2 TRUE      61.4      34      87          4       57.1

ところで、集団で「ない」集計のときに集計した結果を表示するスクリプトを再度記載してみます

res <- tibble(id     = id, 
              age    = age, 
              gender = gender, 
              isx    = isx   ) %>%
  
  summarise(age_mean = mean(age),
            age_min = min(age),
            age_max = max(age),
            gender_male_n  = sum(gender=="m"),
            gender_male_p  = 100*gender_male_n/n() ) %>% 
  
  mutate(across(everything(), ~{format(.,nsmall=1, digits=1)})) %>% 
  
  mutate(
    `年齢(最小-最大)` = str_c(age_mean,"(",age_min,"-",age_max,")"),
    `性別:男性(%)`  = str_c(gender_male_n,"(",gender_male_p,")")
  ) %>%
  
  select(matches("^年齢|^性別")) %>% 
  
  pivot_longer(everything(),
               names_to = " ",
               values_to = "全体")
  
res
# A tibble: 2 × 2
  ` `             全体           
  <chr>           <chr>          
1 年齢(最小-最大) 53.1(24.0-97.0)
2 性別:男性(%)    7(46.7)        

これの、summariseの手前にgroup_by(isx)をいれて、select()以降を除去して実行してみましょう。また、across関数の部分では、isxはロジカル型のままで残しておきたい(文字列に変更する必要がない)ので、.colsで省いておきます

res2 <- 
tibble(id     = id, 
       age    = age, 
       gender = gender, 
       isx    = isx   ) %>%
  group_by(isx) %>%  #これを足した
  summarise(age_mean = mean(age),
            age_min = min(age),
            age_max = max(age),
            gender_male_n  = sum(gender=="m"),
            gender_male_p  = 100*gender_male_n/n() ) %>% 
  mutate(
    across(
      .cols=!isx, #isxは変換しない。
      ~{format(.,nsmall=1, digits=1)}
    )
  ) %>% 
  mutate(
    `年齢(最小-最大)` = str_c(age_mean,"(",age_min,"-",age_max,")"),
    `性別:男性(%)`  = str_c(gender_male_n,"(",gender_male_p,")")
  ) 

View(res2)

いかがでしょうか?isx毎に、最初に作った集計がうまくできていることが確認できますね?

あとは、この結果をうまく最終的に表示したい

              | 購入あり | 購入なし|

|:=============:|:========:|:=======:| |年齢(最小-最大)| ~~~~~~ | ~~~~~~~ | |性別:男性(%) | ~~~~~~ | ~~~~~~~ |

こんな形に持っていくことができたら最初の目標である、グループ集計の完成です。

できますか?今表示されているこの結果から、この表の形になるように動画をとめて実行してみてください。pivot_longerとpivot_widerを1回ずつ使うとその形にできます

やってみましょう。

まず必要な列のみに絞り込んでおきます。

res2 %>% 
  select(isx, matches("^年齢|^性別"))
# A tibble: 2 × 3
  isx   `年齢(最小-最大)` `性別:男性(%)`
  <lgl> <chr>             <chr>         
1 FALSE 45.8(24.0-97.0)   3(37.5)       
2 TRUE  61.4(34.0-87.0)   4(57.1)       

こんな感じですね。

それで、pivot_longerで一度縦持ちデータに変換しましょう

res2 %>% 
  select(isx, matches("^年齢|^性別")) %>% 
  pivot_longer(cols=!isx,names_to="name",values_to="value")
# A tibble: 4 × 3
  isx   name            value          
  <lgl> <chr>           <chr>          
1 FALSE 年齢(最小-最大) 45.8(24.0-97.0)
2 FALSE 性別:男性(%)    3(37.5)        
3 TRUE  年齢(最小-最大) 61.4(34.0-87.0)
4 TRUE  性別:男性(%)    4(57.1)        

後は、このデータをisxを横方向に広げてあげるとよいので

res2 %>% 
  select(isx, matches("^年齢|^性別")) %>% 
  pivot_longer(cols=!isx,names_to="name",values_to="value") %>% 
  pivot_wider(id_cols=name, names_from=isx, values_from=value)
# A tibble: 2 × 3
  name            `FALSE`         `TRUE`         
  <chr>           <chr>           <chr>          
1 年齢(最小-最大) 45.8(24.0-97.0) 61.4(34.0-87.0)
2 性別:男性(%)    3(37.5)         4(57.1)        

良い感じですね?

後は、名前を変更しつつ並び替えてみましょう

res2 %>% 
  select(isx, matches("^年齢|^性別")) %>% 
  pivot_longer(cols=!isx,names_to="name",values_to="value") %>% 
  pivot_wider(id_cols=name, names_from=isx, values_from=value) %>% 
  select(` ` = name, `購買あり`=`TRUE`, `購買なし`=`FALSE`)
# A tibble: 2 × 3
  ` `             購買あり        購買なし       
  <chr>           <chr>           <chr>          
1 年齢(最小-最大) 61.4(34.0-87.0) 45.8(24.0-97.0)
2 性別:男性(%)    4(57.1)         3(37.5)        

できあがりました!

お疲れさまでした。

ここまでで、dplyrのみを使って集計して、表を作成することにとりくんできました。

group_by関数やacross関数の使い方を解説するため、「めんどうな方法」をあえてここではお示ししています。

次の動画からは、group_by関数の使い方を実例をご提示しながら解説していきます。