【问题标题】:Replace NA with mean based on row subset matching another column?根据与另一列匹配的行子集将 NA 替换为均值?
【发布时间】:2016-12-05 20:59:05
【问题描述】:

我有每行包含一个人的性别和体重(以磅为单位)的数据:

genders <- c("FEMALE", "FEMALE", "FEMALE", "FEMALE", "FEMALE", "MALE", "MALE", "MALE", "MALE")
weights <- c(110.0, 120.0, 112.0, NA, NA, 190.0, 202.0, 195.0, NA)

df <- data.frame(gender=genders, weight=weights)
df
#   gender weight
# 1 FEMALE    110
# 2 FEMALE    120
# 3 FEMALE    112
# 4 FEMALE     NA
# 5 FEMALE     NA
# 6   MALE    190
# 7   MALE    202
# 8   MALE    195
# 9   MALE     NA

对于在 weight 列中具有 NA 的每一行,我想用 weight 平均值替换/估算 NA,但应仅使用匹配相同 gender 的行来计算平均值值作为带有 NA 的行。

具体来说,第 4 行和第 5 行具有 FEMALE 的 gender 和 NA 的 weight。我想用与 FEMALE 的 gender 匹配的行子集计算的平均值 weight 替换 NA。在这种情况下,其他第 1、2 和 3 行的平均值为 (110+120+112)/3=114.0。

同样,我想将第 9 行中的 NA 替换为 MALE gender 的权重平均值。

我尝试了以下命令,但它用所有两性用户的平均体重代替了 NA,这不是我想要的。

df$weight[is.na(df$weight)] <- mean(subset(df, gender=df$gender)$weight, na.rm=T)
df
#   gender   weight
# 1 FEMALE 110.0000
# 2 FEMALE 120.0000
# 3 FEMALE 112.0000
# 4 FEMALE 154.8333
# 5 FEMALE 154.8333
# 6   MALE 190.0000
# 7   MALE 202.0000
# 8   MALE 195.0000
# 9   MALE 154.8333

我搜索了其他问题,但它们与我的问题不太一样:

Replace NA with mean matching the same ID

How to replace NA with mean by subset in R (impute with plyr?)

How to replace NA values in a table for selected columns? data.frame, data.table

【问题讨论】:

    标签: r dataframe


    【解决方案1】:

    这可以使用zoo 中的na.aggregate 轻松完成。将“data.frame”转换为“data.table”(setDT(df)),按“性别”分组,我们将na.aggregate 应用于“权重”以将NA 元素替换为mean 值。默认情况下,na.aggregate 返回mean,但我们也可以更改FUN 参数以获取mediansum 等。

    library(data.table)
    library(zoo)
    setDT(df)[, weight := na.aggregate(weight) , by = gender]
    

    或与ave 来自base R

    with(df, ave(weight, gender, FUN = na.aggregate))
    #[1] 110.0000 120.0000 112.0000 114.0000 114.0000 190.0000 202.0000 195.0000 195.6667
    

    【讨论】:

      【解决方案2】:

      您可以将ave()replace() 一起使用(或标准手动替换)。

      df$weight <- with(df, ave(weight, gender,
          FUN = function(x) replace(x, is.na(x), mean(x, na.rm = TRUE))))
      

      给了

        gender   weight
      1 FEMALE 110.0000
      2 FEMALE 120.0000
      3 FEMALE 112.0000
      4 FEMALE 114.0000
      5 FEMALE 114.0000
      6   MALE 190.0000
      7   MALE 202.0000
      8   MALE 195.0000
      9   MALE 195.6667
      

      【讨论】:

      • 谢谢。没有额外软件包的简单答案。正是我想要的。 ave() 函数看起来很强大。
      【解决方案3】:

      使用 base R 这似乎是您正在寻找的:

      df$weight[df$gender=="FEMALE" & is.na(df$weight)] <- mean(df$weight[df$gender=="FEMALE"], na.rm=TRUE)
      df$weight[df$gender=="MALE" & is.na(df$weight)] <- mean(df$weight[df$gender=="MALE"], na.rm=TRUE)
      
      > df
        gender   weight
      1 FEMALE 110.0000
      2 FEMALE 120.0000
      3 FEMALE 112.0000
      4 FEMALE 114.0000
      5 FEMALE 114.0000
      6   MALE 190.0000
      7   MALE 202.0000
      8   MALE 195.0000
      9   MALE 195.6667
      

      【讨论】:

      • 这是非常手动的方法。他们将如何使用超过两个组?在 cmets 中查看更通用的方法
      • 有没有办法在不硬编码“FEMALE”和“MALE”的情况下做到这一点?一列中的数据可能有几十个唯一值。
      • @DavidArenburg 好点。看起来 Richard Scriven 的方法更好,并且适用于存在多个唯一值的情况。
      【解决方案4】:

      您可以按gender 对数据框进行分组,然后计算权重的平均值并将NA 替换为ifelse 语句,在dplyr 中,它可能是:

      library(dplyr)
      df %>% 
            group_by(gender) %>% 
            mutate(weight = ifelse(is.na(weight), mean(weight, na.rm = T), weight))
      
      # Source: local data frame [9 x 2]
      # Groups: gender [2]
      
      #  gender   weight
      #  <fctr>    <dbl>
      # 1 FEMALE 110.0000
      # 2 FEMALE 120.0000
      # 3 FEMALE 112.0000
      # 4 FEMALE 114.0000
      # 5 FEMALE 114.0000
      # 6   MALE 190.0000
      # 7   MALE 202.0000
      # 8   MALE 195.0000
      # 9   MALE 195.6667
      

      【讨论】:

      • 新的coalesce 函数非常适合这里。
      • @alistaire 看起来非常有用和方便。
      猜你喜欢
      • 2017-03-04
      • 1970-01-01
      • 1970-01-01
      • 2015-12-14
      • 1970-01-01
      • 2016-11-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多