【问题标题】:Subsetting a data frame based on key spanning several columns in another (summary) data frame基于跨越另一个(摘要)数据帧中的几列的键对数据帧进行子集
【发布时间】:2015-02-08 12:17:26
【问题描述】:

我有一个数据框 a,其中包含 4 个标识列:A, B, C, D。使用ddply() 创建的第二个数据框b 包含每组A,B,C 的不同Ds 的所有值的摘要。第三个数据框c 包含b 的子集,其中包含我想从a 中删除的错误值。

因此,我想要来自a 的子集,省略由A,B,C 组合标识的所有行,这些行也存在于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

也请随时解决我的问题中的不明确之处。如果您能指出正确的方向,我很乐意进行编辑。

【问题讨论】:

  • 请提供一些示例数据集和预期结果。另外,您的描述并不具体。摘要可以是均值、总和、中位数等。
  • 感谢@akrun 的评论。我将尝试包含原始数据集的信息部分。
  • 我现在添加了一个循环,它以迂回的方式创建结果,试图满足@David 对预期结果数据的 [已删除] 请求。

标签: r dataframe subset


【解决方案1】:

如果我们已经创建了 3 个数据集并希望根据“c/c1”的元素对第一个“a”进行子集化,则一个选项是 anti_join from dplyr

library(dplyr)
anti_join(a, c1, by=c('A', 'B', 'C'))

更新

或者我们可以使用base R 选项和interaction 将感兴趣的列粘贴到两个数据集中,并使用%in% 检查第二个('c')的元素是否在第一个('a')中.逻辑索引可用于子集“a”。

 a1 <- a[!(as.character(interaction(a[1:3], sep=".")) %in% 
          as.character(interaction(c[LETTERS[1:3]], sep="."))),]

或者正如@David Arenburg 提到的,我们可能不需要创建bc 数据集来获得预期的输出。使用plyr,在“a”中创建一个新的均值列(“mean_Val”),其中mutatesubset均值大于0的行(mean_Val &gt;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)

或者,如果我们不需要“平均”值作为“a”中的一列,也可以使用来自base Rave

  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),]

【讨论】:

  • 虽然anti_join() 似乎可以满足我的要求,但我现在必须使用的 R 版本对于 dplyr 来说太旧了。我只安装了 plyr。
  • 您可以将Mean_Val 保留为带有ave 的变量,如subset(transform(a, Mean_Val = ave(Val, B, C, A, FUN = mean)), Mean_Val &gt; 0) 中一样
  • @DavidArenburg 是的,我试图复制 OP 的结果。谢谢。
  • 谢谢,@akrun; interaction() 做得非常出色!我非常感谢您花时间提供和修改您的答案。 :D
  • @BigSmoke 没问题。很高兴为您提供帮助。
【解决方案2】:

这是一个可能的data.table 解决方案,不需要创建bc

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]

【讨论】:

  • 我喜欢 data.table 的想法,David。来自数据库开发人员。背景,我非常欣赏它明显的简单性和强大的功能。太糟糕了,我在旧 Linux 上使用旧 R,无法安装软件包,所以我接受了 @akrun 的基本 R 解决方案。
猜你喜欢
  • 2020-07-07
  • 1970-01-01
  • 1970-01-01
  • 2021-10-16
  • 1970-01-01
  • 1970-01-01
  • 2018-06-07
  • 1970-01-01
  • 2018-11-05
相关资源
最近更新 更多