我遇到了一个非常复杂的问题。我有一个包含三行的数据框: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
答案 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