【问题标题】:How to Divide DataFrame into groups in R如何在 R 中将 DataFrame 分组
【发布时间】:2019-12-06 00:38:27
【问题描述】:

我有一个像这样的大数据框:

df:

col_1   col_2  col_3
  1       2       1

我想很快地把它分成这个子组:

df_1:

 col_1   col_3
  1       1

df_2:

  col_2
    2

我知道有一种方法是这样的:

df_1 <- df[df == 1]
df_2 <- df[df == 2]

但这并不快。 我该怎么办?

谢谢

【问题讨论】:

  • 您的数据中是否只有一行?如果不能,您能否再包含几行并基于此显示预期输出?
  • 我只有一排 @RonakShah

标签: r large-data


【解决方案1】:

dplyrtidyr 的选项:

 df %>% 
  tidyr::gather(key,val) %>% 
  group_split(val)   #attributed to @agila for pointing out the unnenecessary call to group_by that I missed initially
[[1]]
# A tibble: 2 x 2
  key     val
  <chr> <int>
1 col_1     1
2 col_3     1

[[2]]
# A tibble: 1 x 2
  key     val
  <chr> <int>
1 col_2     2

attr(,"ptype")

【讨论】:

  • 我认为应该是 df %&gt;% gather(key, val) %&gt;% group_split(val)
  • 至少在我的机器上仍然没有达到 OP 的预期输出。
  • @agila 修复了它(或者我认为)。似乎 OP 对按 val 分组而不是 key 更感兴趣,第二次看你在那里写的,我错过了。已更正,谢谢。
【解决方案2】:

欢迎来到 SO。

我建议查看dplyrdata.table 包,它们专注于快速和内存高效的实现。特别是我会建议this question 的惊人答案,这将很好地理解这两个包的功能。

data.table 确实倾向于优于 dplyr 随着组和重复子集数量的增长,因为它使用索引和键控子集,但对于大多数情况来说,它归结为偏好。专注于子集,我将提供一个可重复的示例和一些速度比较。

可重现的例子

set.seed(1)
df <- data.frame(group = sample(LETTERS, 1e7, TRUE), 
                 random_numbers = rnorm(1e7), 
                 random_binaries = rbinom(1e7, 1, 0.3))
# size = 152.6 MiB
format(object.size(df), units = "MiB")

方法:

Base-R 方法

现在在 base-R 中,子集可以通过多种方式执行,一种是您自己展示的方式。 df[df == ..]。另一种方法是使用subset 函数,但这是一个实用函数,并且专注于可读性而不是速度,并且通常会执行得更差。下面给出了它们的使用示例。但是,可以使用which 函数将逻辑向量转换为索引,这样做可能会提高性能。

df[df$group == "C",]
#Equivalent
df[which(df$group == "C"),]
#Equivalent
subset(df, group == "C")

dplyr 方法

另一种选择是 dplyr 包。 dplyr 是语法糖,提供管道选项与其他一些包(例如magrittr 包)不同,但不同的基准测试(显示在first link 中)表明该包可用于提高各个方面的性能。但是我不是这个包的专家,因为我倾向于使用data.table 包。该软件包提供了%&gt;% 管道函数和一些实用函数,例如filter,可用于子集数据

library(dplyr)
df %>% filter(group == "C")
# subsetting two columns
df %>% filter(group == "C", random_binaries == TRUE) #Equivalent to group == "C" & random_binaries == TRUE

Data.table 方法:

最后一个流行的包是data.table 包。该软件包专为提高性能和内存效率而设计,例如dplyr。该语法被设计为类似于 SQL 语句(select、from、where、group by),但从语法开始可能会有点混乱。该包提供了一个新的 data.table 类,而不是 data.frame 类,后者的子集化速度非常慢。

但是,几乎可以完全忽略包的语法,因为data.table 在大多数情况下使用data.frame 语法,并且在任何情况下都可以用作data.frame

library(data.table)
#Convert the data.frame to data.table
setDT(df) 

data.table 有两种标准方法:使用 indices 和使用 keys。如果使用与data.frame 方法类似的方法,则使用索引

df1 <- df[random_binaries == TRUE]
df2 <- df[group == "C"]

索引在第一次使用时的速度大致相同,但在每次后续使用时会提高性能。

Keys 用于对data.table 进行预排序,从而实现智能子集。设置密钥确实需要一些时间,并且语法略有不同,但优于其他方法(尽管索引速度相似)

#Set the key using either setkey, or setkeyv (multiple columns)
setkeyv(df, c("group", "random_binaries"))
#Subset on group
df[.("C")]
#subset on random_binaries
df[CJ(group, TRUE, unique = TRUE)]
df[.(unique(group), TRUE)]
# Subset on multiple conditions
df[.(c("C", "H"), c(TRUE, TRUE))]

语法可能令人困惑,但可以查看它们有用的 wiki page 或许多 stackoverflow posts(截至今天为 8968 个),它们提供了大多数问题的答案。

性能对比

我检查了所提供的子集方法的性能,如下所示。可视化显示了使用所示方法的group == "C"group == "H" &amp; random_binaries == TRUE" 子集的各种方法。 x 轴以毫秒为单位表示运行时间,y 轴显示方法。 斑点的宽度表示范围,而斑点的大小表示范围内的次数密度。

从可视化中可以看出,对于 2 列的数据集,在 1 列和 2 列上进行子集化,使用键的 data.table 方法要快得多(标记为 data.table_.._keyed),而使用索引则略胜一筹其余方法。使用subset 比标准方法慢,令人惊讶的是,对于这个插图dplyr 比base-R 慢,但这可能是由于我对包没有经验。

【讨论】:

    【解决方案3】:

    这是使用来自 base R 的 lapply 的一种方法,它为您提供所需数据帧的列表 -

    df <- data.frame(col_1 = 1, col_2 = 2, col_3 = 1)
    
    lapply(unique(unlist(df)), function(x) {
      df[, df == x, drop = F]
    })
    
    # output
    
    [[1]]
      col_1 col_3
    1     1     1
    
    [[2]]
      col_2
    1     2
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-09-27
      • 2016-07-25
      • 2022-11-17
      • 2021-06-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多