Dplyr - 多列求均值

34

我希望使用dplyr计算多列的均值,并因此创建一个新的用于存储平均值的列,而无需使用融合+合并。

> head(growth2)
  CODE_COUNTRY CODE_PLOT IV12_ha_yr IV23_ha_yr IV34_ha_yr IV14_ha_yr IV24_ha_yr IV13_ha_yr
1            1         6       4.10       6.97         NA         NA         NA       4.58
2            1        17       9.88       8.75         NA         NA         NA       8.25
3            1        30         NA         NA         NA         NA         NA         NA
4            1        37      15.43      15.07      11.89      10.00      12.09      14.33
5            1        41      20.21      15.01      14.72      11.31      13.27      17.09
6            1        46      12.64      14.36      13.65       9.07      12.47      12.36
> 

我需要在数据集中添加一个新列,该列包含所有IV列的平均值。 我尝试了以下代码:

growth2 %>% 
  group_by(CODE_COUNTRY, CODE_PLOT) %>%
  summarise(IVmean=mean(IV12_ha_yr:IV13_ha_yr, na.rm=TRUE))

并且依据所用的示例返回了几个错误,例如:

Error in NA_real_:NA_real_ : NA/NaN argument
或者
Error in if (trim > 0 && n) { : missing value where TRUE/FALSE needed

4
你需要为这些列使用rowMeans吗? - akrun
相关帖子:https://dev59.com/RGkx5IYBdhLWcg3wA_1S - zx8754
什么是growth2?它是来自这里吗?https://search.r-project.org/CRAN/refmans/AER/html/BenderlyZwick.html - undefined
5个回答

40

你不需要进行分组,只需使用 select() 然后使用 mutate()

library(dplyr)
mutate(df, IVMean = rowMeans(select(df, starts_with("IV")), na.rm = TRUE))

2
目前这个不起作用(R 3.2.0和dplyr 0.4.1)。我得到了以下错误:错误:每个参数必须产生正整数或负整数。 - rcorty
1
它可以与 starts_with() 一起使用,但是例如 mtcars %>% mutate(IVMean = rowMeans(select(., cyl,mpg), na.rm = TRUE)) 不起作用。如果将其从 mutate 中取出,则可以正常工作。 - Ruben

13

这里是使用 dplyrc_across 解决方案,它专为逐行聚合而设计。这使得按名称、类型或位置引用列并对所选列应用任何函数变得容易。

注意rowwise() 是一种分组操作(即每行都在自己的分组中)。我们可以通过 ungroup() 来反转分组。感谢 @Matifou 指出了这个细节。

library("tidyverse")

df <-
  tibble::tribble(
    ~CODE_COUNTRY, ~CODE_PLOT, ~IV12_ha_yr, ~IV23_ha_yr, ~IV34_ha_yr, ~IV14_ha_yr, ~IV24_ha_yr, ~IV13_ha_yr,
    1L, 6L, 4.1, 6.97, NA, NA, NA, 4.58,
    1L, 17L, 9.88, 8.75, NA, NA, NA, 8.25,
    1L, 30L, NA, NA, NA, NA, NA, NA,
    1L, 37L, 15.43, 15.07, 11.89, 10, 12.09, 14.33,
    1L, 41L, 20.21, 15.01, 14.72, 11.31, 13.27, 17.09,
    1L, 46L, 12.64, 14.36, 13.65, 9.07, 12.47, 12.36
  )

df %>%
  rowwise() %>%
  mutate(
    mean = mean(c_across(starts_with("IV")), na.rm = TRUE),
    sd = sd(c_across(starts_with("IV")), na.rm = TRUE)
  ) %>%
  ungroup()
#> # A tibble: 6 × 10
#>   CODE_COUNTRY CODE_PLOT IV12_ha_yr IV23_ha_yr IV34_ha_yr IV14_ha_yr IV24_ha_yr
#>          <int>     <int>      <dbl>      <dbl>      <dbl>      <dbl>      <dbl>
#> 1            1         6       4.1        6.97       NA        NA          NA  
#> 2            1        17       9.88       8.75       NA        NA          NA  
#> 3            1        30      NA         NA          NA        NA          NA  
#> 4            1        37      15.4       15.1        11.9      10          12.1
#> 5            1        41      20.2       15.0        14.7      11.3        13.3
#> 6            1        46      12.6       14.4        13.6       9.07       12.5
#>   IV13_ha_yr   mean     sd
#>        <dbl>  <dbl>  <dbl>
#> 1       4.58   5.22  1.54 
#> 2       8.25   8.96  0.835
#> 3      NA    NaN    NA    
#> 4      14.3   13.1   2.14 
#> 5      17.1   15.3   3.09 
#> 6      12.4   12.4   1.82

2023年4月17日创建于reprex v2.0.2


你的代码可能需要取消行分组,即添加 ungroup - Matifou

9

在dplyr中使用.

library(dplyr)
mutate(df, IVMean = rowMeans(select(., starts_with("IV")), na.rm = TRUE))

4

我尝试评论Rick Scriven的答案,但没有足够的经验积分。无论如何,我想做出贡献。他的回答是这样的:

    library(dplyr)
    mutate(df, IVMean = rowMeans(select(df, starts_with("IV")), na.rm = TRUE))

这个方法是可行的,但如果所有列都不是以“IV”开头,该怎么办呢?事实证明,select不想要逻辑向量,因此不能使用AND或OR。例如,您不能说“starts_with('X') | starts_with('Y')”。您必须构建一个数字向量。以下是如何完成的。

    mutate(df, IVMean = rowMeans(select(df, c(starts_with("IV"), starts_with("IX"))), na.rm = TRUE))

你可以使用 cbind:例如,mutate(df, mean = rowMeans(cbind(my_col_1, my_col_2), na.rm=T))) - Nova

0
您可以按如下方式使用:

您的数据

data<- structure(list(CODE_COUNTRY = c(1L, 1L, 1L, 1L, 1L, 1L), CODE_PLOT = c(6L, 
17L, 30L, 37L, 41L, 46L), IV12_ha_yr = c(4.1, 9.88, NA, 15.43, 
20.21, 12.64), IV23_ha_yr = c(6.97, 8.75, NA, 15.07, 15.01, 14.36
), IV34_ha_yr = c(NA, NA, NA, 11.89, 14.72, 13.65), IV14_ha_yr = c(NA, 
NA, NA, 10, 11.31, 9.07), IV24_ha_yr = c(NA, NA, NA, 12.09, 13.27, 
12.47), IV13_ha_yr = c(4.58, 8.25, NA, 14.33, 17.09, 12.36)), .Names = c("CODE_COUNTRY", 
"CODE_PLOT", "IV12_ha_yr", "IV23_ha_yr", "IV34_ha_yr", "IV14_ha_yr", 
"IV24_ha_yr", "IV13_ha_yr"), class = "data.frame", row.names = c("1", 
"2", "3", "4", "5", "6"))

mydata <- cbind(data,IVMean=apply(data[,3:8],1,mean, na.rm=TRUE))

你也可以这样做

 mydata <- cbind(data,IVMean=rowMeans(data[3:8], na.rm=TRUE))

我猜你需要在apply中排除前两列,因为OP只对IV12_ha_yr:....列感兴趣。 - akrun
@akrun 你好,你的意思是这样的 mydata <- cbind(data[,3:8],apply(data[,3:8],1,mean))。 - user1267127
1
我指的是 cbind(data,IVMean=apply(data[,3:8],1,mean, na.rm=TRUE))。根据原帖中的代码,预期输出有点令人困惑。 - akrun
@akrun 是的,考虑到缺失值,这是肯定的! - user1267127
1
此外,您可以将 apply 步骤替换为(或作为替代方法添加) rowMeans(data[3:8], na.rm=TRUE) - akrun
@akrun 当然,我们甚至不需要使用apply。我已经更新了它。 - user1267127

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接