我有一个数据集,我想为其遗漏缺失值。我不想使用列中位数,而是使用类别中位数。我可以创建一个聚合,但我想知道将这两个部分集成的最佳方法是什么。这是一个玩具数据集。
df1 <- iris
set.seed(456)
df1[sample(nrow(df1), 30, replace = F), 'Sepal.Length'] <- NA
set.seed(456)
df1[sample(nrow(df1), 30, replace = F), 'Sepal.Width'] <- NA
set.seed(456)
df1[sample(nrow(df1), 30, replace = F), 'Petal.Length'] <- NA
set.seed(456)
df1[sample(nrow(df1), 30, replace = F), 'Petal.Width'] <- NA
agg1 <- aggregate(. ~ Species, data = df1, FUN = median)
我知道我可以使用一堆ifelse()'s
和循环来做到这一点,但我认为这是一种更优雅的方式。任何建议将不胜感激。
编辑: 这就是我自己想出的:
for(i in names(df1)[sapply(df1, is.numeric)]){ # i = "Sepal.Length"
for(k in agg1$Species){
df1[,i] <- ifelse(is.na(df1[,i]), agg1[which(agg1$Species == k),i], df1[,i])
}
}
答案 0 :(得分:1)
有几种方法可以对此操作进行矢量化。
如果行的顺序不重要(即,您很乐意最后追加所有插补的行),那么以下是一个选项:
df2 <- rbind(na.omit(df1),
agg1[match(df1[!complete.cases(df1), 'Species'], agg1$Species), ])
或者,merge
可用于保留行顺序(这可能更可取):
df1[!complete.cases(df1), -5] <-
merge(agg1, df1[!complete.cases(df1), 'Species', drop=FALSE],
by='Species')[, -c(1, 5)]
答案 1 :(得分:0)
您也可以使用dplyr
library(dplyr)
library(tidyr)
获取中值
dfMed <- df1%>%
gather(Var,Val, Sepal.Length:Petal.Width)%>%
group_by(Species, Var) %>%
summarize(Val=median(Val, na.rm=T))%>%
spread(Var,Val)
dfMed
# Source: local data frame [3 x 5]
# Species Sepal.Length Sepal.Width Petal.Length Petal.Width
# 1 setosa 5.0 3.4 1.45 0.2
# 2 versicolor 5.9 2.9 4.40 1.3
# 3 virginica 6.4 3.0 5.50 2.0
inner_join
NA行df1
dfJoin <- inner_join(dfMed, df1%>%
do(filter(., !complete.cases(.))), by="Species")[,c(2:5,1)]
用dfJoin
df1[!df1%>% complete.cases(),] <- dfJoin
答案 2 :(得分:0)
使用data.table
:
首先我们将您的数据发送到data.table
:
setDT(df1)
然后我们得到agg1
:
agg1 = df1[, lapply(.SD, median, na.rm=TRUE), by=Species]
setcolorder(agg1, chmatch(names(df1), names(agg1)))
现在,我们用基于二进制搜索的子集(比矢量扫描更快的速度)用引用(不会复制)的值替换NA
s agg1
,一次,仅适用于所有NA
s的行:
cols = names(df1)
setkey(agg1, Species)
df1[is.na(Sepal.Length) & is.na(Sepal.Width) & is.na(Petal.Length) &
is.na(Petal.Width), (cols) := agg1[J(Species)]]
i
中的条件完全拼写出来,因为使用complete.cases
可能会导致数据集中只有一列或多列中有NA
的其他行,如我明白不应该被替换。
答案 3 :(得分:0)
这是我最终使用的内容:
imputeMed <- function(x){
medX <- median(x, na.rm = T)
x <- ifelse(is.na(x), medX, x)
return(x)
}
vtu1 <- names(df1)[sapply(df1, is.numeric)]
specLev <- unique(as.character(df1$Species))
for(i in specLev){ # i = specLev[1]
df1[df1$Species == i,vtu1] <- as.data.frame(lapply(df1[df1$Species == i,vtu1], imputeMed))
}