LoginSignup
1
0

大分類/中分類/小分類があるデータに対して小計を作りたい in R

Last updated at Posted at 2023-05-30

r-wakalangで聞いたら、Rつよつよな人たちが優しく教えてくれました。
無料版slackだと消えてしまうので備忘録も兼ねて記事にしておきます。

問題

大分類/中分類/小分類とあるようなデータに対して、それぞれのレベルに応じて小計を出す方法
想定コードとしては以下。

library(tidyverse)
df <- tibble(Catergory=sample(c("A","B"),size=100,replace = TRUE),
             Subcatergory=sample(c("i","j","k"),size=100,replace = TRUE), 
             Subsubcatergory = sample(c("p","q","r"),size=100,replace = TRUE),
             score=rnorm(100, mean=70, sd=10))

こんな感じのデータです。

Catergory Subcatergory Subsubcatergory score
B i r 68.14897
A k p 77.29207
B k q 55.99673
B j q 77.63559
B j q 61.85481
B j q 47.65746
A j q 74.75191
A i r 73.32049

とりあえず当座の解決案は下記

df %>%
  group_by(Catergory,Subcatergory,Subsubcatergory) %>% 
  summarise(n=n(),Mean=mean(score),Max=max(score),min=min(score)) %>% ungroup()%>% ungroup() ->df1
df %>%
  group_by(Catergory,Subcatergory) %>% 
  summarise(n=n(),Mean=mean(score),Max=max(score),min=min(score)) %>% ungroup() %>% 
  mutate(Subsubcatergory="ALL")->df2
df %>%
  group_by(Catergory) %>% 
  summarise(n=n(),Mean=mean(score),Max=max(score),min=min(score)) %>% 
  mutate(Subsubcatergory="ALL",
         Subcatergory="ALL")->df3
bind_rows(df1,df2,df3) %>% arrange(Catergory,Subcatergory,Subsubcatergory) 
Catergory Subcatergory Subsubcatergory n Mean Max min
A ALL ALL 47 71.54586 101.44371 50.75666
A i ALL 12 68.54373 78.54747 53.35708
A i p 3 59.31068 66.20233 53.35708
A i q 5 72.53580 78.54747 69.09217
A i r 4 70.47843 73.32049 67.08316
A j ALL 23 74.57838 101.44371 58.39143
A j p 10 77.79374 93.15439 68.86340
A j q 6 70.63459 82.79089 62.69836
A j r 7 73.36538 101.44371 58.39143
A k ALL 12 68.73566 84.06569 50.75666
A k p 4 70.71224 79.92134 56.51757
A k q 3 64.96890 84.06569 50.75666
A k r 5 69.41446 73.61545 59.87375
B ALL ALL 53 70.06481 88.23096 47.65746

解法

解法1

KDさんの解法

コードは短くならないのですが、個人的に二通りのやり方を思いつきました。

  1. まずグループ化変数が異なるだけで、出したい小計は同じなのでそこを関数にしてしまった方がスッキリすると思います。
  2. 今は分類が3段階なので、段階の数だけ my_sum 関数を呼べばよいですが、もっと階層が深い場合は、再帰的に関数を適用するというのも手かなと思いました。
# 1. 小計を出す関数を定義する ------------------------------
my_sum <- function(x, y, ...) {
  x |> 
    group_by(...) |> 
    summarize(n = n(), Mean = mean({{ y }}), Max = max({{ y }}), Min = min({{ y }}), .groups = "drop")
}

bind_rows(
  my_sum(df, score, Category),
  my_sum(df, score, Category, Subcategory),
  my_sum(df, score, Category, Subcategory, Subsubcategory)
) |> 
  mutate(across(ends_with("category"), ~ replace_na(.x, "ALL"))) |> 
  relocate(ends_with("category"), .before = 1L) |> 
  arrange(Category, Subcategory, Subsubcategory)


# 2. グループ化変数を操作しながら再帰的に関数を適用する ------------------------------
recursive_my_sum <- function(x, y, ...) {
  g <- enquos(...) # グループ化引数 (例: Category, Subcategory, Subsubcategory)
  new_g <- head(g, -1) # 次のグループ化引数 (例: Category, Subcategory)
  if (length(g) > 0) {
    bind_rows(my_sum(x, {{ y }}, !!!g), recursive_my_sum(x, {{ y }}, !!!new_g))
  }
}

recursive_my_sum(df, score, Category, Subcategory, Subsubcategory) |> 
  mutate(across(ends_with("category"), ~ replace_na(.x, "ALL"))) |> 
  arrange(Category, Subcategory, Subsubcategory)

解法2

fmsan51さんの解法

df_a <- df %>% mutate(Subsubcatergory = "ALL")
df_b <- df_a %>% mutate(Subcatergory = "ALL")
bind_rows(df, df_a, df_b) %>% 
  group_by(Catergory,Subcatergory,Subsubcatergory) %>% 
  summarise(n=n(),Mean=mean(score),Max=max(score),min=min(score))

雑感

関数を作ったり、別テーブルを用意したりする方法がありますね。どちらも集約をひとまとめに出来ていて、変更しやすさが高いですね。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0