首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >创建一个函数,将一个data.frame中的NAs替换为另一个from中的值

创建一个函数,将一个data.frame中的NAs替换为另一个from中的值
EN

Stack Overflow用户
提问于 2011-12-02 07:35:41
回答 3查看 3.3K关注 0票数 17

我经常遇到这样的情况,我需要用其他处于不同聚合级别的data.frame中的值来替换某个data.frame中缺少的值。因此,例如,如果我有一个充满县数据的data.frame,我可能会用存储在另一个data.frame中的州值替换NA值。在编写了相同的merge……ifelse(is.na()) yada几十次之后,我决定分解并编写一个函数来做这件事。

这是我想出来的,还有一个我如何使用它的例子:

fillNaDf <- function(naDf, fillDf, mergeCols, fillCols){
 mergedDf <- merge(naDf, fillDf, by=mergeCols)
 for (col in fillCols){
   colWithNas <- mergedDf[[paste(col, "x", sep=".")]]
   colWithOutNas <- mergedDf[[paste(col, "y", sep=".")]]
   k <- which( is.na( colWithNas ) )
   colWithNas[k] <- colWithOutNas[k]
   mergedDf[col] <- colWithNas
   mergedDf[[paste(col, "x", sep=".")]] <- NULL
   mergedDf[[paste(col, "y", sep=".")]] <- NULL
 }
 return(mergedDf)
}

## test case
fillDf <- data.frame(a = c(1,2,1,2), b = c(3,3,4,4) ,f = c(100,200, 300, 400), g = c(11, 12, 13, 14))
naDf <- data.frame( a = sample(c(1,2), 100, rep=TRUE), b = sample(c(3,4), 100, rep=TRUE), f = sample(c(0,NA), 100, rep=TRUE), g = sample(c(0,NA), 200, rep=TRUE) )
fillNaDf(naDf, fillDf, mergeCols=c("a","b"), fillCols=c("f","g") )

因此,在我运行这个程序之后,我有一种奇怪的感觉,可能有人已经在我之前以一种更优雅的方式解决了这个问题。这个问题有没有更好/更容易/更快的解决方案?还有,有没有办法消除函数中间的循环呢?之所以有这个循环,是因为我经常在多个列中替换NAs。是的,该函数假设我们从填充的列名相同,我们填充的到的列名相同,合并也是如此。

任何指导或重构都会很有帮助。

编辑12月2日,我意识到我的示例中有逻辑缺陷,我修复了这些缺陷。

EN

回答 3

Stack Overflow用户

发布于 2011-12-02 08:47:16

多好的问题啊。

这是一个data.table解决方案:

# Convert data.frames to data.tables (i.e. data.frames with extra powers;)
library(data.table)
fillDT <- data.table(fillDf, key=c("a", "b"))
naDT <- data.table(naDf, key=c("a", "b"))


# Merge data.tables, based on their keys (columns a & b)
outDT <- naDT[fillDT]    
#      a b  f  g f.1 g.1
# [1,] 1 3 NA  0 100  11
# [2,] 1 3 NA NA 100  11
# [3,] 1 3 NA  0 100  11
# [4,] 1 3  0  0 100  11
# [5,] 1 3  0 NA 100  11
# First 5 rows of 200 printed.

# In outDT[i, j], on the following two lines 
#   -- i is a Boolean vector indicating which rows will be operated on
#   -- j is an expression saying "(sub)assign from right column (e.g. f.1) to 
#        left column (e.g. f)
outDT[is.na(f), f:=f.1]
outDT[is.na(g), g:=g.1]

# Just keep the four columns ultimately needed   
outDT <- outDT[,list(a,b,g,f)]
#       a b  g   f
#  [1,] 1 3  0   0
#  [2,] 1 3 11   0
#  [3,] 1 3  0   0
#  [4,] 1 3 11   0
#  [5,] 1 3 11   0
# First 5 rows of 200 printed.
票数 14
EN

Stack Overflow用户

发布于 2011-12-02 09:54:06

以下是您的方法的一个稍微更简洁/更健壮的版本。您可以将for循环替换为对lapply的调用,但我发现该循环更易于阅读。

此函数假定任何不在mergeCols中的列都可以填充它们的NAs。我不太确定这是否有帮助,但我会在选民中碰碰运气。

fillNaDf.ju <- function(naDf, fillDf, mergeCols) {
  mergedDf <- merge(fillDf, naDf, by=mergeCols, suffixes=c(".fill",""))
  dataCols <- setdiff(names(naDf),mergeCols)
  # loop over all columns we didn't merge by
  for(col in dataCols) {
    rows <- is.na(mergedDf[,col])
    # skip this column if it doesn't contain any NAs
    if(!any(rows)) next
    rows <- which(rows)
    # replace NAs with values from fillDf
    mergedDf[rows,col] <- mergedDf[rows,paste(col,"fill",sep=".")]
  }
  # don't return ".fill" columns
  mergedDf[,names(naDf)]
}
票数 6
EN

Stack Overflow用户

发布于 2011-12-03 02:00:54

我更喜欢从merge中取出执行匹配的代码,然后自己来做,这样我就可以保持原始数据帧的顺序不变,包括行和列。我还使用矩阵索引来避免任何循环,不过为了做到这一点,我用修改后的fillCols创建了一个新的数据框,并用它替换了原始数据框中的列;我认为可以直接填充它,但显然您不能使用矩阵排序来替换data.frame的某些部分,所以如果在某些情况下循环遍历这些名称会更快,我也不会感到惊讶。

使用矩阵索引:

fillNaDf <- function(naDf, fillDf, mergeCols, fillCols) {
  fillB <- do.call(paste, c(fillDf[, mergeCols, drop = FALSE], sep="\r"))
  naB <- do.call(paste, c(naDf[, mergeCols, drop = FALSE], sep="\r"))
  na.ind <- is.na(naDf[,fillCols])
  fill.ind <- cbind(match(naB, fillB)[row(na.ind)[na.ind]], col(na.ind)[na.ind])
  naX <- naDf[,fillCols]
  fillX <- fillDf[,fillCols]
  naX[na.ind] <- fillX[fill.ind]
  naDf[,colnames(naX)] <- naX
  naDf
}

使用循环:

fillNaDf2 <- function(naDf, fillDf, mergeCols, fillCols) {
  fillB <- do.call(paste, c(fillDf[, mergeCols, drop = FALSE], sep="\r"))
  naB <- do.call(paste, c(naDf[, mergeCols, drop = FALSE], sep="\r"))
  m <- match(naB, fillB)
  for(col in fillCols) {
    fix <- which(is.na(naDf[,col]))
    naDf[fix, col] <- fillDf[m[fix],col]
  }
  naDf
}
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/8349909

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档