R:基于若干标准选择数据帧行

时间:2012-08-31 21:44:30

标签: r

我创建了一个数据框my.df,并希望根据几个条件选择行(或删除行)。在这个示例数据框架中,我想保留行1,2,4,7和8.具体来说,我想:

  1. 在第3,4或5列
  2. 中保留包含数字的任何行
  3. 如果列1和列2,则在第3-5列中保留包含所有缺失观察值的任何行 不是空白且不包含垃圾
  4. 我可以做到这一点,但我的解决方案似乎过于复杂,我希望有人可能会提出更有效的方法。

    my.df <- data.frame(C1 = c("group1", "group1",     "",     "", "junk", "junk", "group2",       ""),
                        C2 = c(     "A",      "B",     "",     "",     "", "junk",      "B",      "C"),
                        C3 = c(     100,       NA,     NA,     10,     NA,     NA,       NA,       NA),
                        C4 = c(     200,       NA,     NA,     20,     NA,     NA,      100,       NA),
                        C5 = c(     100,       NA,     NA,     30,     NA,     NA,       NA,        5))
    
    my.df
    
    # the number of missing observations in columns 3-5 is < 3 or
    # when the number of missing observations in columns 3-5 is 3 neither column 1 nor 2 is either blank or 'junk'
    
    df.2 <- my.df[ (rowSums(is.na(my.df[,3:5]))  < (ncol(my.df)-2)) | 
                   (rowSums(is.na(my.df[,3:5])) == (ncol(my.df)-2) & my.df[,1] != 'junk' & my.df[,2] != 'junk'  & my.df[,1] != '' & my.df[,2] != '') , ]
    df.2
    

    根据我的实际数据,什么是垃圾可能很复杂。所以,在这里我将junk概括为junk1junk2,我仍然希望保留第1,2,4,7和8行。下面的代码可以使用。

    my.df <- data.frame(C1 = c("group1", "group1",     "",     "", "junk2", "junk1", "group2",       ""),
                        C2 = c(     "A",      "B",     "",     "",      "", "junk1",      "B",      "C"),
                        C3 = c(     100,       NA,     NA,     10,      NA,      NA,       NA,       NA),
                        C4 = c(     200,       NA,     NA,     20,      NA,      NA,      100,       NA),
                        C5 = c(     100,       NA,     NA,     30,      NA,      NA,       NA,        5))
    
    my.df
    
    df.3 <- my.df[ (rowSums(is.na(my.df[,3:5]))  < (ncol(my.df)-2)) | 
                   (rowSums(is.na(my.df[,3:5])) == (ncol(my.df)-2)  & 
                    my.df[,1] != 'junk1' & my.df[,2] != 'junk1'     & 
                    my.df[,1] != 'junk2' & my.df[,2] != 'junk2'     &
                    my.df[,1] != '' & my.df[,2] != '') 
    
            , ]
    df.3
    

    因为符合垃圾条件的字符串在这里变得非常复杂和复杂,我尝试使用%in%来简化代码以组合垃圾,但是我得到了一个错误。

    all.junk <- c("", "junk1", "junk2")
    
    my.df.1 <- my.df[,1]
    my.df.2 <- my.df[,2]
    
    my.df.1 <- as.character(my.df.1)
    my.df.2 <- as.character(my.df.2)
    
    df.4 <- my.df[ (rowSums(is.na(my.df[,3:5]))  < (ncol(my.df)-2)) | 
                   (rowSums(is.na(my.df[,3:5])) == (ncol(my.df)-2) & 
                    my.df.1[!(my.df.1%in%all.junk)] & my.df.2[!(my.df.2%in%all.junk)]) , ]
    df.4
    

    我可以继续使用我所拥有的功能代码,为每个符合垃圾级别的字符串添加一行新行df.3,但我怀疑有一个更有效的解决方案。

    我在Stackoverflow上发现了类似的问题,但是我发现的任何问题都没有像本例中那样处理那么多或复杂的选择标准。

    感谢您提出任何建议,尤其是关于df.4中的错误。

2 个答案:

答案 0 :(得分:3)

这非常紧凑:保持每一行都不是垃圾/鼻子:

all.junk=c("junk","")
subset(my.df,!(C1%in%all.junk &
               C2%in%all.junk & 
               is.na(C3) & is.na(C4) & is.na(C5)))

输出

      C1 C2  C3  C4  C5
1 group1  A 100 200 100
2 group1  B  NA  NA  NA
4            10  20  30
7 group2  B  NA 100  NA
8         C  NA  NA   5

答案 1 :(得分:1)

如果您的担忧是可读性,则可以重构此代码:

df.3 <- my.df[ (rowSums(is.na(my.df[,3:5]))  < (ncol(my.df)-2)) | 
           (rowSums(is.na(my.df[,3:5])) == (ncol(my.df)-2)  & 
            my.df[,1] != 'junk1' & my.df[,2] != 'junk1'     & 
            my.df[,1] != 'junk2' & my.df[,2] != 'junk2'     &
            my.df[,1] != '' & my.df[,2] != '') 
    , ]

分为:

# Rows I want
good.rows = (rowSums(is.na(my.df[,3:5]))  < (ncol(my.df)-2)) | 
           (rowSums(is.na(my.df[,3:5])) == (ncol(my.df)-2)  & 
            my.df[,1] != 'junk1' & my.df[,2] != 'junk1'     & 
            my.df[,1] != 'junk2' & my.df[,2] != 'junk2'     &
            my.df[,1] != '' & my.df[,2] != '') 

df.3 <- my.df[good.rows,]

进一步说:

sums.are.fine = (rowSums(is.na(my.df[,3:5]))  < (ncol(my.df)-2)) | 
           (rowSums(is.na(my.df[,3:5])) == (ncol(my.df)-2)

no.junk = my.df[,1] != 'junk1' & my.df[,2] != 'junk1'     & 
            my.df[,1] != 'junk2' & my.df[,2] != 'junk2'     &
            my.df[,1] != '' & my.df[,2] != '') 

good.rows = sums.are.fine & no.junk

df.3 <- my.df[good.rows,]

您还可以编写单独的函数,例如返回布尔值的check.if.sums.are.fine(table.to.check)和调用所有这些函数的check.everything(table.to.check)函数,并给出最终结果。

如果输入部分困扰您,我注意到的一件事是,对于“不能junk1junk2或null”之类的内容,您可以制作bad.values = character()并填充此内容使用您不想要的每个值,然后只需检查bad.values中没有包含哪些值(除非您使用for循环,否则这可能需要一些工作才能添加到您的解决方案中。)