【问题标题】:'seq_along' named columns and replace NAs with appropriate values'seq_along' 命名列并用适当的值替换 NA
【发布时间】:2016-07-21 14:21:36
【问题描述】:

尝试在包含大量列的非常大的数据框中重新编码 NA。我已将列名存储在字符向量 (num_var) 中,并将不同列的替换值存储在命名向量 (median.to.replace) 中。 在每一列中,NA 应替换为来自 median.to.replace 的正确值。

使用 seq_along 循环内的代码手动运行代码并手动指定每个列名没有问题

但是,当我尝试这个简单的代码时,所有 NA 都不会被重新编码,并且一些 NA 会被错误的值替换??

for (name_col in seq_along(num_var))
{
  na_rows <- is.na(allProspect.tst[,name_col]) 
  allProspect.tst[na_rows,name_col] <- median.to.replace[name_col]

}

任何人都知道出了什么问题吗?尝试对此使用快速且内存高效的方法

【问题讨论】:

  • 欢迎来到 StackOverflow。请提供minimal reproducible example
  • seq_along 将产生一个从 1 到 length(num_var) 的整数向量。如果您跳过 data.frame 中的任何列(例如 id 变量),那么您的索引不正确。
  • 是 median.to.replace 一个向量吗?
  • 顺便说一句,如果您的兴趣是估算,您应该看看mice 包。
  • length(num_var)==length(median.to.replace)?

标签: r


【解决方案1】:

根据您的评论,向量num_var 不是从数据框的第一列开始并且不连续,那么您需要这个

# simple example with just four columns
allProspect.tst <- data.frame(one=c(1:3,8), two=c(NA,4:6), three=1:4, four= c(5,NA,7, 8))
# want to replace NAs in columns "two" and "four" with values 5 and 7, respectively
num_var <- c("two","four")
median.to.replace <- c(5, 7)
# let's see the data before replacement
print(allProspect.tst)
##  one two three four
##1   1  NA     1    5
##2   2   4     2   NA
##3   3   5     3    7
##4   8   6     4    8

# just loop over the collection of column names (not indices)
for (name_col in num_var) {
  na_rows <- is.na(allProspect.tst[,name_col])
  # key is to get the corresponding element in median.to.replace 
  # using which() index in num_var has value equal name_col
  allProspect.tst[na_rows,name_col] <- median.to.replace[which(num_var==name_col)]
}
# now let's see the replaced data
print(allProspect.tst)
##  one two three four
##1   1   5     1    5
##2   2   4     2    7
##3   3   5     3    7
##4   8   6     4    8

更新:提高效率

有很多方法可以使大量列的替换操作更有效,但最基本的使用 *apply 系列函数 look here for an excellent overview,来自 R base 包。更新后的代码如下:

replace.with.median <- function(col, median.val, df) {
  na_rows <- is.na(df[, col])
  df[na_rows, col] <- median.val  
  return(df[, col])
}
allProspect.tst[, num_var] <- mapply(replace.with.median, num_var, median.to.replace, 
                                     MoreArgs=list(df=allProspect.tst))
print(allProspect.tst)
##  one two three four
##1   1   5     1    5
##2   2   4     2    7
##3   3   5     3    7
##4   8   6     4    8

注意事项:

  1. 原来的for循环体被封装在函数replace.with.median中。输入参数是:

    • col:要查找NAs 替换的列名
    • median.val:来自median.to.replace的对应替换值
    • df: 包含数据的数据框

    此函数从df 返回col 列,其NAs 被替换为median.val。

  2. 使用mapply,根据上面的链接:

    当您有多个数据结构(例如向量、列表)并且您希望将函数应用于每个数据结构的第一个元素,然后每个元素的第二个元素等时,

    在这里,我们希望将函数replace.with.median 应用于两个向量num_varmedian.to.replace 以“锁步”方式相互关联。另外,我们通过mapplyMoreArgs参数提供allProspect.tstreplace.with.median的数据框。

  3. mapply 返回的是已替换NAs 的列向量的集合。然后我们用这些替换allProspect.tst 的相应列。

希望这会有所帮助。

【讨论】:

  • 非常有帮助!我修改了我的代码并按照您的建议使用了正确的循环结构。运行效率很高,只需几秒钟即可重新编码约 900 万。行和 40 列。自己找到了“映射”,但不知道如何将它应用到我的问题上。您的示例确实帮助我理解了如何应用它。
【解决方案2】:

如果您使用 data.table 而不是 data.frame,这会更快。在这里,我创建了一个随机数据集,其中包含 mtcars 数据集中的缺失值,然后使用查找表来替换这些缺失值。

library(data.table)
set.seed(44)
f_dowle<-function(DT,value=-1,col) { #copied and edited this function from elsewhere
  set(DT,which(is.na(DT[[col]])),col,value)
}

data(mtcars)

setDT(mtcars)

for(i in colnames(mtcars)){
  rand_na<-sample(1:nrow(mtcars),3)
  mtcars[rand_na,eval(as.name(i)):=NA]

}
head(mtcars) #showing random missing values

        mpg cyl disp  hp drat    wt  qsec vs am gear carb
1: 21.0  NA  160  NA 3.90 2.620 16.46  0  1    4    4
2: 21.0   6   NA 110 3.90    NA 17.02  0  1    4    4
3: 22.8   4  108  NA 3.85 2.320 18.61  1  1    4    1
4: 21.4   6   NA 110 3.08 3.215 19.44  1  0    3    1
5: 18.7  NA  360 175   NA 3.440 17.02  0  0    3    2
6: 18.1   6  225 105 2.76    NA 20.22  1  0    3    1

lkp_dt<-data.table(column=colnames(mtcars),value=1:11)
for(i in colnames(mtcars)){
  value=lkp_dt[column==i,value]
  f_dowle(mtcars,value=value,col=i)

}

head(mtcars) #missing values replaced

    mpg cyl disp  hp drat    wt  qsec vs am gear carb
1: 21.0   2  160   4 3.90 2.620 16.46  0  1    4    4
2: 21.0   6    3 110 3.90 6.000 17.02  0  1    4    4
3: 22.8   4  108   4 3.85 2.320 18.61  1  1    4    1
4: 21.4   6    3 110 3.08 3.215 19.44  1  0    3    1
5: 18.7   2  360 175 5.00 3.440 17.02  0  0    3    2
6: 18.1   6  225 105 2.76 6.000 20.22  1  0    3    1

【讨论】:

  • 很好的例子!但是,我要重新编码的第一列不是从“1”开始,并且列号不是连续的。不太了解如何使您的示例适应这种情况。例如,我想用 median.to.replace[1,2,3,4] 重新编码列“3”、“7”、“22”、“97”实际上更多的列
  • @wabe 回答要包含的列 - 您可以手动编写列表或检查具有缺失值的列。至于查找表,这可以是您想要的任何东西。如果你想用这些列的中值重新编码,你可以把它放在一个查找表中,或者使用函数直接从表本身计算。
猜你喜欢
  • 2022-06-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-10-28
  • 2018-02-18
相关资源
最近更新 更多