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

3
我被一个相当复杂的问题困住了。我有一个数据框,有三行: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

现在我想做的是,如果一行包含信息a,则删除一个id的所有其他行。例如,这意味着应删除第2行和第3行,因为第1行的列信息包含值a。请注意,信息值未排序(id 3/行5&6),并且由于其他数据限制,无法排序。

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

# 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循环来加速它,但也许有人能给我一个更好的解决方案的提示。
此致
阿恩
[更新]
结果应该是:
id   info   row
 1      a     1
 2      a     4
 3      a     6
 4      b     7
 4      c     8

我想你是指列而不是行? - Sacha Epskamp
所以如果我理解正确,在你的例子中,剩下的行只有7和8吗?像这样data[!(data$id %in% unique(data$id[data$info=="a"])),]是否解决了你的问题? - sebastian-c
你应该提供一个可重现的例子 https://dev59.com/eG025IYBdhLWcg3whGSx - Roman Luštrik
@SachaEpskamp 不,我真的是指行。 - ahs85
@sebastian-c 结果应该是只剩下1、4、6、7、8行。 - ahs85
啊哈,所以除了只包含“a”的那些id之外,所有带有“a”的id都要。 - sebastian-c
5个回答

2
这里有一个可能的解决方案。
首先找到info包含"a"id
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

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

subset(data, info == "a" | !rep.int(tapply(info, id, function(x) any(x == "a")),
                                    table(id)))

注意。@BenBarnes发现此解决方案仅适用于按照id排序的数据帧。


1
我不这么认为。OP 表示他想要第 1、4、6、7、8 行。 - sebastian-c
@sebastian-c,没错,请往上看更新后的原始帖子。 - ahs85
@ahs85 我修改了我的回答。 - Sven Hohenstein
运行起来非常完美,而且速度也非常快! - ahs85
1
@SvenHohenstein,看起来你的替代方案需要按照iddata进行排序。 - BenBarnes
@BenBarnes + 1 非常感谢你的提示。我已经在另一种解决方案中添加了注释。 - Sven Hohenstein

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)将行顺序恢复到原始状态。


+1 感觉应该使用新的非连接语法 (DT[!"a"]),但是我没有完全理解问题在快速浏览后的两列方面。 - Matt Dowle
@MatthewDowle,不使用连接的建议很好。我通常会落后于最新的软件包版本一段时间,所以这个选项对我来说仍然是非常新的领域。不过我会去看看的。 - BenBarnes

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

0

如果 df 是这样的(参见上面的RE Sacha),则使用match,它只会找到第一次出现的索引:

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

-1

我尝试了子集,但如何让它识别上面和/或下面的行? - ahs85
我没有做这件事,但可能是因为这更像是一条评论(类似于RTFM),而不是一个答案。 - A5C1D2H2I1M1N2O1R2T1

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