基于多个条件选择数据框的行

3
我已经创建了一个数据框my.df,希望根据多个条件选择行(或删除行)。对于这个示例数据框,我想保留第1、2、4、7和8行。具体来说,我想要:
  1. 保留任何包含列3、4或5中数字的行
  2. 如果列1和列2不为空且不含垃圾信息,则保留包含列3-5所有缺失观测值的行
我可以做到这一点,但我的解决方案似乎过于复杂,我希望有人能提出更有效的方法。
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个回答

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
如果您关心的是可读性,您可以重构这段代码:
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循环,否则可能需要一些工作将其添加到解决方案中)。


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