如何用组/子集的均值替换 NA?

问题描述 投票:0回答:6

我有一个数据框,其中包含来自蝾螈内脏的各种节肢动物的长度和宽度。因为有些肠道有数千种特定的猎物,所以我只测量了每种猎物类型的一个子集。我现在想用该猎物的平均长度和宽度替换每个未测量的个体。我想保留数据框并仅添加估算列(长度2,宽度2)。主要原因是每一行还有包含收集蝾螈的日期和位置数据的列。我可以用随机选择的测量个体来填充 NA,但为了论证起见,我们假设我只想用平均值替换每个 NA。

例如,假设我有一个看起来像这样的数据框:

id    taxa        length  width
101   collembola  2.1     0.9
102   mite        0.9     0.7
103   mite        1.1     0.8
104   collembola  NA      NA
105   collembola  1.5     0.5
106   mite        NA      NA

实际上,我有更多的列和大约 25 个不同的分类群,总共约有 30,000 个猎物。看起来 plyr 包可能是理想的选择,但我只是不知道如何做到这一点。我不太懂 R 或编程,但我正在努力学习。

并不是说我知道我在做什么,但如果有帮助的话,我会尝试创建一个小数据集来使用。

exampleDF <- data.frame(id = seq(1:100), taxa = c(rep("collembola", 50), rep("mite", 25), 
rep("ant", 25)), length = c(rnorm(40, 1, 0.5), rep("NA", 10), rnorm(20, 0.8, 0.1), rep("NA", 
5), rnorm(20, 2.5, 0.5), rep("NA", 5)), width = c(rnorm(40, 0.5, 0.25), rep("NA", 10), 
rnorm(20, 0.3, 0.01), rep("NA", 5), rnorm(20, 1, 0.1), rep("NA", 5)))

以下是我尝试过的一些方法(但没有成功):

# mean imputation to recode NA in length and width with means 
  (could do random imputation but unnecessary here)
mean.imp <- function(x) { 
  missing <- is.na(x) 
  n.missing <-sum(missing) 
  x.obs <-a[!missing] 
  imputed <- x 
  imputed[missing] <- mean(x.obs) 
  return (imputed) 
  } 

mean.imp(exampleDF[exampleDF$taxa == "collembola", "length"])

n.taxa <- length(unique(exampleDF$taxa))
for(i in 1:n.taxa) {
  mean.imp(exampleDF[exampleDF$taxa == unique(exampleDF$taxa[i]), "length"])
} # no way to get back into dataframe in proper places, try plyr? 

另一次尝试:

imp.mean <- function(x) {
  a <- mean(x, na.rm = TRUE)
  return (ifelse (is.na(x) == TRUE , a, x)) 
 } # tried but not sure how to use this in ddply

Diet2 <- ddply(exampleDF, .(taxa), transform, length2 = function(x) {
  a <- mean(exampleDF$length, na.rm = TRUE)
  return (ifelse (is.na(exampleDF$length) == TRUE , a, exampleDF$length)) 
  })

有什么建议吗?

r subset na
6个回答
49
投票

不是我自己的技术,我不久前在板上看到它:

dat <- read.table(text = "id    taxa        length  width
101   collembola  2.1     0.9
102   mite        0.9     0.7
103   mite        1.1     0.8
104   collembola  NA      NA
105   collembola  1.5     0.5
106   mite        NA      NA", header=TRUE)


library(plyr)
impute.mean <- function(x) replace(x, is.na(x), mean(x, na.rm = TRUE))
dat2 <- ddply(dat, ~ taxa, transform, length = impute.mean(length),
     width = impute.mean(width))

dat2[order(dat2$id), ] #plyr orders by group so we have to reorder

编辑带有

for
循环的非plyr方法:

for (i in which(sapply(dat, is.numeric))) {
    for (j in which(is.na(dat[, i]))) {
        dat[j, i] <- mean(dat[dat[, "taxa"] == dat[j, "taxa"], i],  na.rm = TRUE)
    }
}

编辑很多个月后,这是一个data.tabledplyr方法:

数据表

library(data.table)
setDT(dat)

dat[, length := impute.mean(length), by = taxa][,
    width := impute.mean(width), by = taxa]

dplyr

library(dplyr)

dat %>%
    group_by(taxa) %>%
    mutate(
        length = impute.mean(length),
        width = impute.mean(width)  
    )

3
投票

其他几个选项:

1) 与 的新

nafill
-函数

library(data.table)
setDT(dat)

cols <- c("length", "width")

dat[, (cols) := lapply(.SD, function(x) nafill(x, type = "const", fill = mean(x, na.rm = TRUE)))
    , by = taxa
    , .SDcols = cols][]

2) 与

na.aggregate
-函数

library(zoo)
library(data.table)
setDT(dat)

cols <- c("length", "width")

dat[, (cols) := lapply(.SD, na.aggregate)
    , by = taxa
    , .SDcols = cols][]

na.aggregate
的默认函数是
mean
;如果您想使用另一个函数,您应该使用
FUN
参数指定(例如:
FUN = median
)。另请参阅带有
?na.aggregate
的帮助文件。

当然你也可以在tidyverse中使用这个:

library(dplyr)
library(zoo)

dat %>% 
  group_by(taxa) %>% 
  mutate_at(cols, na.aggregate)

2
投票

在回答这个问题之前,我想说我是 R 初学者。因此,如果您觉得我的答案是错误的,请告诉我。

代码:

DF[is.na(DF$length), "length"] <- mean(na.omit(telecom_original_1$length))

并对宽度应用相同的方法。

DF 代表 data.frame 的名称。

谢谢, 帕蒂


0
投票

扩展@Tyler Rinker 的解决方案,假设

features
是要插补的列。在这种情况下
features <- c('length', 'width')
。然后使用
data.table
解决方案变为:

library(data.table)
setDT(dat)

dat[, (features) := lapply(.SD, impute.mean), by = taxa, .SDcols = features]

0
投票

R-base

这是另一种基于 R 的方法,依赖于

vapply()
+
ave()

  1. 由于应进行均值插补的列属于
    character
    类,
> vapply(X = exampleDF, FUN = class, FUN.VALUE = "integer")
         id        taxa      length       width 
  "integer" "character" "character" "character" 

将它们更改为

numeric
:

exampleDF[, c("length", "width")] <- 
  apply(X = exampleDF[, c("length", "width")], MARGIN = 2L, FUN = as.numeric)

注意,如果事先进行了列强制转换,则可以使用

as.numeric()

# exampleDF[, c("length", "width")] <- 
vapply(X = exampleDF[, c("length", "width")], 
       FUN = \(x) {
         ave(x = x, # alternative: as.numeric()
             exampleDF[, "taxa"], # grouping 
             FUN = \(y) {
               y[is.na(y)] <- mean(y, na.rm = TRUE) 
               y 
               }
             )
       },
       FUN.VALUE = numeric(length = nrow(exampleDF))
       )

一种不太硬编码的方法,其中还可以指定插补机制/统计量(平均值、中位数、最大值……),可能值得将其包装到自编写的函数中。

OP的数据:

exampleDF <- 
  data.frame(id = seq(1:100), 
             taxa = c(rep("collembola", 50), rep("mite", 25), rep("ant", 25)), 
             length = c(rnorm(40, 1, 0.5), rep("NA", 10), 
                        rnorm(20, 0.8, 0.1), rep("NA", 5), 
                        rnorm(20, 2.5, 0.5), rep("NA", 5)), 
             width = c(rnorm(40, 0.5, 0.25), rep("NA", 10), 
                       rnorm(20, 0.3, 0.01), rep("NA", 5), 
                       rnorm(20, 1, 0.1), rep("NA", 5)))

-1
投票

我遇到了类似的事件,我可以提供一个非常简单的步骤来改变列的分组平均值。

library(tidyr)

dataset <- dataset %>% group_by(taxa) %>% mutate(length1= ifelse(is.na(length),mean(length,na.rm = T),length))

View(dataset)

如果我可以提供任何进一步的帮助,请告诉我。

© www.soinside.com 2019 - 2024. All rights reserved.