最近学徒群在讨论一个需求,就是用数据框的每一列的平均数替换每一列的NA值。但是问题的提出者自己的代码是错的,如下:
他认为替换不干净,应该是循环有问题。希望我们帮忙检查,我通常是懒得看其他人写的代码,所以让群里的小伙伴们有空的都尝试写一下。
答案一:双重for循环
我同样是没有细看这个代码,但是写出双重for循环肯定是没有理解R语言的便利性。
代码语言:javascript复制#我好像试着写出来了,上面的这个将每一列的NA替换成每一列的平均值。
#代码如下,请各位老师瞅瞅有没有毛病。
tmp <- data.frame(a=c(1:5,NA,6:9),
b=c(NA,sample(1:100,9)),
c=c(sample(1:50,3),NA,sample(1:50,4),NA,NA),
d=c(sample(1:40,9),NA),
e=c(sample(1:50,5),NA,NA,NA,NA,NA))
out <- list()
for (i in 1:ncol(tmp)) {
out[[i]] <- which(is.na(tmp[,i]))
for (y in 1:length(out[[i]])) {
tmp[out[[i]][y],i] <- mean(tmp[[i]],na.rm = T)
}
}
答案的提出者自己还点评了一句:我是这么想的,也不知道对不对,希望各位老师能指正一下:因为tmp数据框中,NA个数不唯一,我还想获取他们的横坐标的话,输出的结果就为一个list而不是一个数据框了。所以我在全局环境里面设置了一个空的list,然后每一列占据了list的一个元素的位置。list的每个元素里面包括了NA的横坐标。
答案二:使用Hmisc的impute函数
我给出的点评是:这样的偷懒大法好!使用Hmisc的impute函数可以输入指定值来替代NA值做简单插补,平均数、中位数、众数。
代码语言:javascript复制rm(list = ls())
tmp <- data.frame(a=c(1:5,NA,6:9),
b=c(NA,sample(1:100,9)),
c=c(sample(1:50,3),NA,sample(1:50,4),NA,NA),
d=c(sample(1:40,9),NA),
e=c(sample(1:50,5),NA,NA,NA,NA,NA))
library(Hmisc)
me1=as.data.frame(apply(tmp, 2, function(x){
impute(x,mean)}))
最后一个也是很差的答案
代码语言:javascript复制tmp <- data.frame(a=c(1:5,NA,6:9),
b=c(NA,sample(1:100,9)),
c=c(sample(1:50,3),NA,sample(1:50,4),NA,NA),
d=c(sample(1:40,9),NA),
e=c(sample(1:50,5),NA,NA,NA,NA,NA))
#ifelse(is.na(tmp),apply(tmp,2,mean,na.rm=T))
tmp <- rbind(tmp,apply(tmp, 2, mean,na.rm=T))
for (i in 1:dim(tmp)[2]) {
tmp[,i][is.na(tmp[,i])] <- tmp[nrow(tmp),i]
}
tmp
我的答案
不知道为啥,大家好像一直无法get到R语言编程的便利性!
代码语言:javascript复制a=1:1000
a[sample(a,100)]=NA
dim(a)=c(20,50)
a
# 按照列,替换每一列的NA值为该列的平均值
b=apply(a,2,function(x){
x[is.na(x)]=mean(x,na.rm = T)
return(x)
})
大家可以对比一下,看看自己的R语言水平停留在哪一个答案的水平
学徒作业
把 melt 和dcast函数,自己写一遍自定义函数实现同样的功能,就数据框的长-宽转换!