根据跨越另一个(摘要)数据框中的多列的键对数据框进行子集化
Subsetting a data frame based on key spanning several columns in another (summary) data frame
我有一个数据框 a
,其中包含 4 个标识列:A, B, C, D
。使用 ddply()
创建的第二个数据框 b
包含每组 A,B,C
的不同 D
的所有值的摘要。第三个数据框 c
包含 b
的一个子集,其中包含我想从 a
.
中删除的错误值
因此,我想要 a
中的一个子集,省略 c
中也存在的由 A,B,C
的组合标识的所有行。我可以想出在循环中执行此操作(丑陋且低效)的方法,但是,我的 DBA 背景鼓励我寻求一种更……直接的解决方案。
在代码中:
a <- data.frame(
A=rep(c('2013-10-30', '2014-11-6'), each=16*20),
B=rep(1:8, each=2*20),
C=rep(1:4, each=20),
D=1:20
)
a$Val=rnorm(nrow(a))
library(plyr)
b <- ddply(a, ~B+C+A, summarise,
mean_Val=mean(Val))
# Some subset criteria based on AOI group values
c <- subset(b, mean_Val <= 0)
# EDIT: Delete all the rows from a for which the
# key-triplets A,B,C are present in c
for (i in 1:nrow(c)) {
c_row = c[i,]
a <- a[ which( !(a$A==c_row$A & a$B==c_row$B & a$C==c_row$C) ), ]
}
# This is the loopy type of 'solution' I didn't want to use
也请随时解决我问题中的不明确之处。如果您能指出正确的方向,我很乐意进行编辑。
如果我们已经创建了 3 个数据集并希望根据 "c/c1" 的元素对第一个 "a" 进行子集化,一个选项是 anti_join
来自 dplyr
library(dplyr)
anti_join(a, c1, by=c('A', 'B', 'C'))
更新
或者我们可以使用带有 interaction
的 base R
选项将两个数据集中感兴趣的列粘贴在一起,并检查第二个 ('c') 的元素是否在第一个 ( 'a') 使用 %in%
。逻辑索引可用于子集 "a".
a1 <- a[!(as.character(interaction(a[1:3], sep=".")) %in%
as.character(interaction(c[LETTERS[1:3]], sep="."))),]
或者正如@David Arenburg 提到的,我们可能不需要创建 b
或 c
数据集来获得预期的输出。使用 plyr
,在 "a" 中创建一个新的均值列 ("mean_Val"),其中 mutate
和 subset
均值大于 0 的行 (mean_Val >0
)
library(plyr)
subset(ddply(a, ~B+C+A, mutate, mean_Val=mean(Val)), mean_Val>0)
或使用 dplyr
的类似方法
library(dplyr)
a %>%
group_by(B, C, A) %>%
mutate(mean_Val=mean(Val)) %>%
filter(mean_Val>0)
或者如果我们不需要 "mean" 值作为 "a" 中的一列,也可以使用 base R
中的 ave
。
a[!!with(a, ave(Val, B, C, A, FUN=function(x) mean(x)>0)),]
如果我们需要保留 mean_Val
列(@David Arenburg 提出的变体)
subset(transform(a, Mean_Val = ave(Val, B, C, A, FUN = mean)),
Mean_Val > 0)
数据
set.seed(24)
a <- data.frame(A= sample(LETTERS[1:3], 20, replace=TRUE),
B=sample(LETTERS[1:3], 20, replace=TRUE), C=sample(LETTERS[1:3],
20, replace=TRUE), D=rnorm(20))
b <- a %>%
group_by(A, B, C) %>%
summarise(D=sum(D))
set.seed(39)
c1 <- b[sample(1:nrow(b), 6, replace=FALSE),]
这是一个可能的 data.table
解决方案,不需要创建 b
或 c
library(data.table)
as.data.table(a)[, if(mean(Val) > 0) .SD, by = list(B, C, A)]
或者类似的(如果你也想要均值本身)
as.data.table(a)[, Mean_Val := mean(Val), list(B, C, A)][Mean_Val > 0]
我有一个数据框 a
,其中包含 4 个标识列:A, B, C, D
。使用 ddply()
创建的第二个数据框 b
包含每组 A,B,C
的不同 D
的所有值的摘要。第三个数据框 c
包含 b
的一个子集,其中包含我想从 a
.
因此,我想要 a
中的一个子集,省略 c
中也存在的由 A,B,C
的组合标识的所有行。我可以想出在循环中执行此操作(丑陋且低效)的方法,但是,我的 DBA 背景鼓励我寻求一种更……直接的解决方案。
在代码中:
a <- data.frame(
A=rep(c('2013-10-30', '2014-11-6'), each=16*20),
B=rep(1:8, each=2*20),
C=rep(1:4, each=20),
D=1:20
)
a$Val=rnorm(nrow(a))
library(plyr)
b <- ddply(a, ~B+C+A, summarise,
mean_Val=mean(Val))
# Some subset criteria based on AOI group values
c <- subset(b, mean_Val <= 0)
# EDIT: Delete all the rows from a for which the
# key-triplets A,B,C are present in c
for (i in 1:nrow(c)) {
c_row = c[i,]
a <- a[ which( !(a$A==c_row$A & a$B==c_row$B & a$C==c_row$C) ), ]
}
# This is the loopy type of 'solution' I didn't want to use
也请随时解决我问题中的不明确之处。如果您能指出正确的方向,我很乐意进行编辑。
如果我们已经创建了 3 个数据集并希望根据 "c/c1" 的元素对第一个 "a" 进行子集化,一个选项是 anti_join
来自 dplyr
library(dplyr)
anti_join(a, c1, by=c('A', 'B', 'C'))
更新
或者我们可以使用带有 interaction
的 base R
选项将两个数据集中感兴趣的列粘贴在一起,并检查第二个 ('c') 的元素是否在第一个 ( 'a') 使用 %in%
。逻辑索引可用于子集 "a".
a1 <- a[!(as.character(interaction(a[1:3], sep=".")) %in%
as.character(interaction(c[LETTERS[1:3]], sep="."))),]
或者正如@David Arenburg 提到的,我们可能不需要创建 b
或 c
数据集来获得预期的输出。使用 plyr
,在 "a" 中创建一个新的均值列 ("mean_Val"),其中 mutate
和 subset
均值大于 0 的行 (mean_Val >0
)
library(plyr)
subset(ddply(a, ~B+C+A, mutate, mean_Val=mean(Val)), mean_Val>0)
或使用 dplyr
library(dplyr)
a %>%
group_by(B, C, A) %>%
mutate(mean_Val=mean(Val)) %>%
filter(mean_Val>0)
或者如果我们不需要 "mean" 值作为 "a" 中的一列,也可以使用 base R
中的 ave
。
a[!!with(a, ave(Val, B, C, A, FUN=function(x) mean(x)>0)),]
如果我们需要保留 mean_Val
列(@David Arenburg 提出的变体)
subset(transform(a, Mean_Val = ave(Val, B, C, A, FUN = mean)),
Mean_Val > 0)
数据
set.seed(24)
a <- data.frame(A= sample(LETTERS[1:3], 20, replace=TRUE),
B=sample(LETTERS[1:3], 20, replace=TRUE), C=sample(LETTERS[1:3],
20, replace=TRUE), D=rnorm(20))
b <- a %>%
group_by(A, B, C) %>%
summarise(D=sum(D))
set.seed(39)
c1 <- b[sample(1:nrow(b), 6, replace=FALSE),]
这是一个可能的 data.table
解决方案,不需要创建 b
或 c
library(data.table)
as.data.table(a)[, if(mean(Val) > 0) .SD, by = list(B, C, A)]
或者类似的(如果你也想要均值本身)
as.data.table(a)[, Mean_Val := mean(Val), list(B, C, A)][Mean_Val > 0]