使用“FUN=first”跳过NA值

4

可能有一个很简单的解释来说明我做错了什么,但我今天已经在这上面工作了相当长时间,但还是无法让它正常工作。我以为这会像在公园里散步一样简单,然而我的代码并没有像预期的那样正常工作。

所以举个例子,假设我有一个数据框如下。

df
Row#   user      columnB    
1        1          NA        
2        1          NA        
3        1          NA        
4        1          31        
5        2          NA        
6        2          NA        
7        2          15        
8        3          18        
9        3          16       
10       3          NA

基本上,我想创建一个新的列,使用 TTR 库包中的第一个(以及最后一个)函数,获取每个用户的第一个非 NA 值。因此,我的期望数据框应该是这样的。

df
Row#   user      columnB    firstValue
1        1          NA        31
2        1          NA        31 
3        1          NA        31
4        1          31        31
5        2          NA        15
6        2          NA        15 
7        2          15        15
8        3          18        18
9        3          16        18
10       3          NA        18

我主要使用谷歌搜索了一下,但是没有找到确切的答案。

这是我尝试过的一些代码,但是我没有得到我想要的结果(请注意,我是从记忆中带来的,所以还有很多其他变化形式,但这些是我一直在尝试的基本形式)。

    df$firstValue<-ave(df$columnB,df$user,FUN=first,na.rm=True)
    df$firstValue<-ave(df$columnB,df$user,FUN=function(x){x,first,na.rm=True})
    df$firstValue<-ave(df$columnB,df$user,FUN=function(x){first(x,na.rm=True)})
    df$firstValue<-by(df,df$user,FUN=function(x){x,first,na.rm=True})

失败了,这些只给出每个组的第一个值,这将是NA。

再次说明,这些只是我脑海中的一些示例,我玩过na.rm,使用na.exclude,na.omit,na.action(na.omit)等...

任何帮助都将不胜感激。谢谢。


此外,以下是我参考的一些网站。http://tolstoy.newcastle.edu.au/R/e2/help/06/09/1294.html http://www.statmethods.net/input/missingdata.html https://stat.ethz.ch/pipermail/r-help/2006-September/113478.html http://www.ats.ucla.edu/stat/r/faq/missing.htm - rj2700
啊,抱歉,实际上是那些额外的HTML链接出了点问题。无论如何,我已经添加了文本。谢谢。 - rj2700
2
请注意,您应该将所有的 na.rm=True 替换为 na.rm=TRUE - juba
2
尚未测试,但是(以下是 @Arun 现已删除的回答):ddply(df, .(user), transform, firstValue=ifelse(is.na(columnB),NA,na.omit(columnB)[1])) - Ben Bolker
主要问题是 first/last 没有内置的 na.rm 参数,因此您必须使用类似于 na.omit() 的东西。 - Ben Bolker
显示剩余4条评论
4个回答

4
一个 data.table 的解决方案
require(data.table)
DT <- data.table(df, key="user")
DT[, firstValue := na.omit(columnB)[1], by=user]

3

使用 plyr 工具包可以解决问题:

ddply(df, .(user), transform, firstValue=na.omit(columnB)[1])

这将会给出:

  Row user columnB firstValue
1   1    1      NA         31
2   2    1      NA         31
3   3    1      NA         31
4   4    1      31         31
5   5    2      NA         15
6   6    2      NA         15
7   7    2      15         15
8   8    3      18         18
9   9    3      16         18

如果您想捕获最后一个值,可以执行以下操作:
ddply(df, .(user), transform, firstValue=tail(na.omit(columnB),1))

2
我认为你可以使用 na.omit(columnB)[1] - Ben Bolker
1
еҸҜиғҪдҪ еә”иҜҘдҪҝз”ЁTTRеҢ…дёӯзҡ„first(.)еҮҪж•°жқҘжӣҝжҚў[1]пјҢеӣ дёәOPжӯЈеңЁеҜ»жұӮдҪҝз”ЁжӯӨеҮҪж•°гҖӮ - Arun
谢谢大家,我明天会尝试一下。我会使用 first(.) 和 last(.) 来尝试它。 - rj2700
1
@Arun:说得好,但我怀疑TTR包中的firstlast早于base-R中的headtail;此外,如果使用向量,我可以理解tail(x)x[length(x)]的简化形式,但head(x)并不比x[1]更好(first/head的主要优点在于它们的通用性)。 - Ben Bolker
@BenBolker 我同意。我只是因为OP特别要求才写的。 - Arun
显示剩余2条评论

3
使用 data.table
library (data.table)
DT <- data.table(df, key="user")
DT <- setnames(DT[unique(DT[!is.na(columnB), list(columnB), by="user"])], "columnB.1", "first")

不好意思,我在使用我的iPhone。出了什么问题吗? :) - Ricardo Saporta
感谢您的帮助,不幸的是,我没有机会尝试这个,因为第二次尝试使用了Arun的解决方案。但它看起来与他的解决方案相似。再次感谢。 - rj2700

2
使用一个非常小的辅助函数
finite <- function(x) x[is.finite(x)]

这里有一个只使用标准R函数的一行代码:

df <- cbind(df, firstValue = unlist(sapply(unique(df[,1]), function(user) rep(finite(df[df[,1] == user,2])[1], sum(df[,1] == user))))

为了更好地概述,这里将一行代码展开为多行代码:
# for each user, find the first finite (in this case non-NA) value of the second column and replicate it as many times as the user has rows
# then, the results of all users are joined into one vector (unlist) and appended to the data frame as column
df <- cbind(
  df,
  firstValue = unlist(
    sapply(
       unique(df[,1]),
       function(user) {
         rep(
           finite(df[df[,1] == user,2])[1],
           sum(df[,1] == user)
         )
       }
    )
  )
)

1
为什么要检查 is.finite 而不是 is.na?这将排除 NaNInf 等,包括 NA,这可能不是我们想要的。 - Arun
始终保留基础的 R 例子,以便进行比较是很好的,但相对复杂的解决方案确实显示了 plyrdata.table 的优点。 - Ben Bolker
你也可以使用 do.call(rbind,lapply(split(...),FUN)) 来复制 plyr 的方法。 - Ben Bolker
@Arun 是的,这是真的。我默认假设 OP 正在寻找一种方法来查找每个用户的第一个实际数字。如果不需要,请将 is.finite 更改为 !is.na - QkuCeHBH

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接