【问题标题】:How do I take subsets of a data frame according to a grouping in R?如何根据 R 中的分组获取数据帧的子集?
【发布时间】:2011-11-24 01:45:20
【问题描述】:

我有一个聚合问题,我无法弄清楚如何在 R 中有效地执行。

假设我有以下数据:

group1 <- c("a","b","a","a","b","c","c","c","c",
            "c","a","a","a","b","b","b","b")
group2 <- c(1,2,3,4,1,3,5,6,5,4,1,2,3,4,3,2,1)
value  <- c("apple","pear","orange","apple",
            "banana","durian","lemon","lime",
            "raspberry","durian","peach","nectarine",
            "banana","lemon","guava","blackberry","grape")
df <- data.frame(group1,group2,value)

我有兴趣从数据框df 中进行抽样,这样我会从group1group2 的每个因子组合中随机选择一行。

如你所见,table(df$group1,df$group2)的结果

  1 2 3 4 5 6
a 2 1 2 1 0 0
b 2 2 1 1 0 0
c 0 0 1 1 2 1

表明某些组合出现了不止一次,而另一些则从未见过。对于那些被多次看到的(例如,group1="a"group2=3),我只想随机选择一个相应的行并返回一个只有该行子集的新数据框。这样,分组因子的每个可能组合仅由数据框中的一行表示。

这里的一个重要方面是,我的实际数据集可以包含从 500,000 行到 >2,000,000 行的任何地方,因此请务必注意性能。

我在 R 方面相对较新,所以我一直无法弄清楚如何正确生成这个结构。一次尝试看起来像这样(使用plyr 包):

choice <- function(x,label) {
    cbind(x[sample(1:nrow(x),1),],data.frame(state=label))
}

df <- ddply(df[,c("group1","group2","value")],
            .(group1,group2),
            pick_junc,
            label="test")

请注意,在这种情况下,我还在数据框中添加了一个名为“label”的额外列,该列被指定为ddply 函数的额外参数。但是,我在大约 20 分钟后杀死了它。

在其他情况下,我尝试过使用aggregatebytapply,但我永远不知道指定的函数得到了什么,它应该返回什么,或者如何处理结果(尤其是对于by)。

我正在尝试从 python 切换到 R 以进行探索性数据分析,但这种类型的聚合对我来说至关重要。在 python 中,我可以非常快速地执行这些操作,但这很不方便,因为我必须为要执行的每种不同类型的聚合生成单独的脚本/数据结构。

我想爱 R,所以请帮忙!谢谢!

乌里

【问题讨论】:

  • 数据框这么大,看包data.table,挺快的

标签: r dataframe plyr aggregation


【解决方案1】:

这里是plyr 解决方案

set.seed(1234)
ddply(df, .(group1, group2), summarize, 
     value = value[sample(length(value), 1)])

这给了我们

   group1 group2      value
1       a      1      apple
2       a      2  nectarine
3       a      3     banana
4       a      4      apple
5       b      1      grape
6       b      2 blackberry
7       b      3      guava
8       b      4      lemon
9       c      3     durian
10      c      4     durian
11      c      5  raspberry
12      c      6       lime

编辑。对于这么大的数据框,最好使用data.table

library(data.table)
dt = data.table(df)
dt[,list(value = value[sample(length(value), 1)]),'group1, group2']

编辑 2:性能比较:数据表快 15 倍

group1 = sample(letters, 1000000, replace = T)
group2 = sample(LETTERS, 1000000, replace = T)
value  = runif(1000000, 0, 1)
df     = data.frame(group1, group2, value)
dt     = data.table(df)

f1_dtab = function() {
   dt[,list(value = value[sample(length(value), 1)]),'group1, group2']
}
f2_plyr = function() {ddply(df, .(group1, group2), summarize, value =          
   value[sample(length(value), 1)])
}

f3_by = function() {do.call(rbind,by(df,list(grp1 = df$group1,grp2 = df$group2),
  FUN = function(x){x[sample(nrow(x),1),]}))
}


library(rbenchmark)
benchmark(f1_dtab(), f2_plyr(), f3_by(), replications = 10)

      test  replications elapsed relative
  f1_dtab()           10   4.764  1.00000    
  f2_plyr()           10  68.261 14.32851    
    f3_by()           10  67.369 14.14127 

【讨论】:

  • data.table 获胜也就不足为奇了。不过,不知何故,我认为by 会比ddply 击败更多。
  • 那么为什么不使用by 返回可以强制转换为数据框的内容呢? by-type 对象是什么?
  • @Uri Laserson:有关“by”返回的内容,请参见“by”和“tapply”的帮助页面。在这种情况下,每个组应该只有一个值,它可能是一个类似矩阵的对象,但具有支持按类的附加属性。
【解决方案2】:

另一种方式:

with(df, tapply(value, list( group1,  group2), length))
   1  2 3 4  5  6
a  2  1 2 1 NA NA
b  2  2 1 1 NA NA
c NA NA 1 1  2  1
# Now use tapply to sample withing groups
# `resample` fn is from the sample help page:
# Avoids an error with sample when only one value in a group.
resample <- function(x, ...) x[sample.int(length(x), ...)]
#Create a row index
df$idx <- 1:NROW(df)
rowidxs <- with(df,  unique( c(    # the `c` function will make a matrix into a vector
              tapply(idx, list( group1,  group2),
                            function (x) resample(x, 1) ))))
rowidxs
# [1]  1  5 NA 12 16 NA  3 15  6  4 14 10 NA NA  7 NA NA  8
df[rowidxs[!is.na(rowidxs)] , ]

【讨论】:

  • 这不能回答问题。
  • 我扩展了答案以演示如何使用tapply 进行抽样。
猜你喜欢
  • 1970-01-01
  • 2023-04-07
  • 2016-09-25
  • 1970-01-01
  • 2015-04-19
  • 1970-01-01
  • 1970-01-01
  • 2014-12-02
相关资源
最近更新 更多