首页 > 解决方案 > 宽到长格式:如何在特定列之间进行平均(2 组列)

问题描述

我有一个宽格式的数据集,我想转换为长格式。宽格式的列表示一个长格式变量的两个级别。我想对宽格式的每组列进行逐行平均,并在汇总的长格式表中表示每组列的该值(平均值)。

虚拟数据

假设我有一项研究的数据,该研究测试了兽医如何给动物称重。有 10 名兽医(这些是“受试者”),每行一名。至于被称重的动物,有狗或猫(其中6种)。每个兽医都在诊所环境或家庭环境中测量所有动物。我想为每位兽医计算(1)他/她测量的所有狗的平均体重,以及(2)他/她测量的所有猫的平均体重。

library(tidyverse)

set.seed(123)
df <- data.frame(replicate(12,sample(10:25,10,rep=TRUE)))
id <- 1:10
clinic_vs_house <- sample(c(0,1), 10, replace = TRUE)
df <- cbind(id, clinic_vs_house, df)
names(df) <- c("id",
               "location",
               "cat_a",
               "cat_b",
               "cat_c",
               "cat_d",
               "cat_e",
               "cat_f",
               "dog_a",
               "dog_b",
               "dog_c",
               "dog_d",
               "dog_e",
               "dog_f")

> df
##    id location cat_a cat_b cat_c cat_d cat_e cat_f dog_a dog_b dog_c dog_d dog_e dog_f
## 1   1        0    24    13    18    18    24    21    14    15    23    15    18    25
## 2   2        1    24    23    21    23    25    14    17    14    15    11    13    23
## 3   3        0    12    15    18    12    19    16    21    24    20    10    15    21
## 4   4        0    23    18    18    13    22    14    24    18    17    11    18    24
## 5   5        0    12    19    22    23    16    20    22    24    15    13    25    18
## 6   6        1    19    20    12    10    18    25    11    19    15    22    23    23
## 7   7        1    11    25    17    20    18    15    10    21    16    14    18    23
## 8   8        0    15    14    19    16    19    18    18    24    24    15    16    25
## 9   9        0    20    12    16    14    16    11    20    25    25    24    12    12
## 10 10        0    14    20    19    21    20    22    18    13    10    12    17    23

我目前笨拙的解决方案

我创建了两个单独的数据框,每个数据框用于一组不同的动物,我在其中计算相关列的平均值。然后我合并了表格。

cat_means <- 
  df %>%
  mutate(mean_wt = rowMeans(select(., matches("cat")))) %>%
  select(id, location, mean_wt) %>%
  mutate(animal = "cat")


dog_means <- 
  df %>%
  mutate(mean_wt = rowMeans(select(., matches("dog")))) %>%
  select(id, location, mean_wt) %>%
  mutate(animal = "dog")


means_table <-
  bind_rows(cat_means, dog_means) %>%
  select(id, location, animal, mean_wt) %>%
  arrange(id)

> means_table
##    id location animal  mean_wt
## 1   1        0    cat 16.85714
## 2   1        0    dog 18.33333
## 3   2        1    cat 18.71429
## 4   2        1    dog 15.50000
## 5   3        0    cat 13.14286
## 6   3        0    dog 18.50000
## 7   4        0    cat 15.42857
## 8   4        0    dog 18.66667
## 9   5        0    cat 16.00000
## 10  5        0    dog 19.50000
## 11  6        1    cat 15.00000
## 12  6        1    dog 18.83333
## 13  7        1    cat 15.28571
## 14  7        1    dog 17.00000
## 15  8        0    cat 14.42857
## 16  8        0    dog 20.33333
## 17  9        0    cat 12.71429
## 18  9        0    dog 19.66667
## 19 10        0    cat 16.57143
## 20 10        0    dog 15.50000

所以是的,我能够得到我想要的结果,但我对这个解决方案不满意,因为它是重复的代码,如果我有不止 2 组要转换的列,那将是一个痛苦。有没有更简洁的方法来获得相同的结果?我知道我总是可以编写一个运行 x 次的函数,但我想知道是否有一个我不知道的更优雅的解决方案,尤其是使用tidyverse.

标签: rtidyverse

解决方案


df %>%
  pivot_longer(cols=cat_a:dog_f, names_pattern = "(.*)_(.*)", names_to=c("animal","letter")) %>%
  group_by(id, location, animal) %>%
  summarise(mean_wt=mean(value))

# A tibble: 20 x 4
# Groups:   id, location [10]
      id location animal mean_wt
   <int>    <dbl> <chr>    <dbl>
 1     1        0 cat       19.7
 2     1        0 dog       18.3
 3     2        1 cat       21.7
 4     2        1 dog       15.5
 5     3        0 cat       15.3
 6     3        0 dog       18.5
 7     4        0 cat       18  
 8     4        0 dog       18.7
 9     5        0 cat       18.7
10     5        0 dog       19.5
11     6        1 cat       17.3
12     6        1 dog       18.8
13     7        1 cat       17.7
14     7        1 dog       17  
15     8        0 cat       16.8
16     8        0 dog       20.3
17     9        0 cat       14.8
18     9        0 dog       19.7
19    10        0 cat       19.3
20    10        0 dog       15.5

推荐阅读