多行删除:根据其他行删除行

时间:2012-11-26 11:15:00

标签: r

我遇到了一个非常复杂的问题。我有一个包含三行的数据框:id,info和rownum。数据如下所示:

id   info   row
 1      a     1
 1      b     2
 1      c     3
 2      a     4
 3      b     5
 3      a     6
 4      b     7
 4      c     8

我现在想要做的是删除一个ID的所有其他行,如果其中一行包含信息 a 。这意味着,例如,行 2 3 应该被删除,因为行 1 的颜色信息包含值 a 。请注意,信息值未订购(标识3 /第5行和第6行),由于其他数据限制,无法订购。

我使用 for loop 解决了这个问题:

# select all id containing an "a"-value 
a_val <- data$id[grep("a", data$info)]

# check for every id containing an "a"-value
for(i in a_val) {

   temp_data <- data[which(data$id == i),]

   # only go on if the given id contains more than one row
   if (nrow(temp_data) > 1) {

      for (ii in nrow(temp_data)) {

         if (temp_data$info[ii] != "a") {
            temp <- temp_data$row[ii]

            if (!exists("delete_rows")) {
               delete_rows <- temp
            } else {
               delete_rows <- c(delete_rows, temp)
            }
         }
      }
   }
}

我的解决方案效果很好。然而,由于原始数据包含超过700k行且超过150k行具有“a”值,因此非常非常非常慢。

我可以使用带有4个内核的 foreach 循环来加速它,但也许有人可以给我一个更好的解决方案的提示。

最好的问候,
·阿尔

[UPDATE]

结果应该是:

id   info   row
 1      a     1
 2      a     4
 3      a     6
 4      b     7
 4      c     8

5 个答案:

答案 0 :(得分:2)

这是一种可能的解决方案。

首先找到id包含info的{​​{1}}:

"a"

子集数据:

ids <- with(data, unique(id[info == "a"]))

输出:

subset(data, (id %in% ids & info == "a") | !id %in% ids)

另一种解决方案(可能更难破译):

  id info row
1  1    a   1
4  2    a   4
6  3    a   6
7  4    b   7
8  4    c   8

注意的。 @BenBarnes发现,只有在根据subset(data, info == "a" | !rep.int(tapply(info, id, function(x) any(x == "a")), table(id))) 对数据框进行排序时,此解决方案才有效。

答案 1 :(得分:2)

您可能需要调查data.table包:

编辑:如果row变量不是数据中每一行的顺序编号(正如我所假设的那样),您可以创建这样的变量来获取原始行顺序:

library(data.table)
# Create data.table of your data
dt <- as.data.table(data)
# Create index to maintain row order
dt[, idx := seq_len(nrow(dt))]
# Set a key on id and info
setkeyv(dt, c("id", "info"))
# Determine unique ids
uid <- dt[, unique(id)]
# subset your data to select rows with "a"
dt2 <- dt[J(uid, "a"), nomatch = 0]
# identify rows of dataset where the id doesn't have an "a"
dt3 <- dt[J(dt2[, setdiff(uid, id)])]
# rbind those two data.tables together
(dt4 <- rbind(dt2, dt3))

#    id info row idx
# 1:  1    a   1   1
# 2:  2    a   4   4
# 3:  3    a   6   6
# 4:  4    b   7   7
# 5:  4    c   8   8

# And if you need the original ordering of rows,
dt5 <- dt4[order(idx)]

请注意,为data.table设置密钥会根据键列对行进行排序。最后一步(创建dt5)将行顺序设置回原始行。

答案 2 :(得分:1)

以下是使用ddply的方法:

df <- read.table(text="id   info   row
 1      a     1
 1      b     2
 1      c     3
 2      a     4
 3      b     5
 3      a     6
 4      b     7
 4      c     8",header=TRUE)


library("plyr")
ddply(df,.(id),subset,rep(!'a'%in%info,length(info))|info=='a')

返回:

  id info row
1  1    a   1
2  2    a   4
3  3    a   6
4  4    b   7
5  4    c   8

答案 3 :(得分:0)

如果df是这个(上面的RE Sacha)使用匹配,它只找到第一次出现的索引:

df <- read.table(text="id   info   row
 1      a     1
 1      b     2
 1      c     3
 2      a     4
 3      b     5
 3      a     6
 4      b     7
 4      c     8",header=TRUE)


# the first info row matching 'a' and all other rows that are not 'a'
with(df, df[c(match('a',info), which(info != 'a')),])

  id info row
1  1    a   1
2  1    b   2
3  1    c   3
5  3    b   5
7  4    b   7
8  4    c   8

答案 4 :(得分:-1)

尝试查看子集,它非常易于使用,它将解决您的问题。

您只需要指定要基于的子列的值,或者您可以选择更多列。

http://stat.ethz.ch/R-manual/R-devel/library/base/html/subset.html

http://www.statmethods.net/management/subset.html