根据多列值将数据框划分为多个数据框

25

我正在尝试对数据框进行子集操作,以便根据多个列值获取多个数据框。这是我的例子:

>df
  v1   v2   v3   v4   v5
   A    Z    1    10   12
   D    Y    10   12    8
   E    X    2    12   15
   A    Z    1    10   12
   E    X    2    14   16

期望的输出结果大致如下,我将根据列v1v2将此数据框拆分为多个数据框

>df1
 v3   v4   v5
  1   10   12
  1   10   12
>df2
 v3   v4   v5
 10   12    8
>df3
 v3   v4   v5
 2    12   15
 2    14   16

我已经编写了一段代码,目前它可以正常工作,但我认为这不是最好的方法。肯定有更好的方法来处理它。假设tab是包含初始数据的数据框。以下是我的代码:

v1Factors<-levels(factor(tab$v1))
v2Factors<-levels(factor(tab$v2))

for(i in 1:length(v1Factors)){
  for(j in 1:length(v2Factors)){
    subsetTab<-subset(tab, v1==v1Factors[i] & v2==v2Factors[j], select=c("v3", "v4", "v5"))
    print(subsetTab)
  }
}

请有人提出更好的方法来完成上述操作?


你想要重复使用这些数据框,还是只是按这些列分组打印它们? - Thilo
我想要重复使用它们...想在这些数据框上绘制图形。 - Rachit Agrawal
2个回答

40
您正在寻找 split
split(df, with(df, interaction(v1,v2)), drop = TRUE)
$E.X
  v1 v2 v3 v4 v5
3  E  X  2 12 15
5  E  X  2 14 16

$D.Y
  v1 v2 v3 v4 v5
2  D  Y 10 12  8

$A.Z
  v1 v2 v3 v4 v5
1  A  Z  1 10 12

正如评论中所指出的

以下任何一种方式都可以

library(microbenchmark)
microbenchmark(
                split(df, list(df$v1,df$v2), drop = TRUE), 
               split(df, interaction(df$v1,df$v2), drop = TRUE),
               split(df, with(df, interaction(v1,v2)), drop = TRUE))


Unit: microseconds
                                                  expr      min        lq    median       uq      max neval
            split(df, list(df$v1, df$v2), drop = TRUE) 1119.845 1129.3750 1145.8815 1182.119 3910.249   100
     split(df, interaction(df$v1, df$v2), drop = TRUE)  893.749  900.5720  909.8035  936.414 3617.038   100
 split(df, with(df, interaction(v1, v2)), drop = TRUE)  895.150  902.5705  909.8505  927.128 1399.284   100

看起来interaction稍微快一些(可能是因为f = list(...)仅在函数内部被转换为交互)


编辑

如果你只想使用子集数据框,那么我建议使用data.table来更轻松地编写代码。

library(data.table)

dt <- data.table(df)
dt[, plot(v4, v5), by = list(v1, v2)]

2
split 可以接受一个列表作为 f,而不必使用 interaction。不过我不确定哪种方法更有效率。 - A5C1D2H2I1M1N2O1R2T1
感谢提供基准测试数据。在这种情况下,@Arun的技巧(with(df, split(df, f = do.call(paste, df[1:2]))))可能会更快!而且,这不会创建不必要的需要删除的级别。 - A5C1D2H2I1M1N2O1R2T1
@mnel 感谢您的及时回复。您的建议肯定是有效的。但是我并没有完全理解您所建议的多种方法之间的区别。 - Rachit Agrawal
@mnel 看起来交互会创建所有可能的级别组合。但是当我运行上述示例时,它并没有创建所有级别。为什么?在实际数据集中,它正在创建...这让我感到困惑。 - Rachit Agrawal

8

现在还有来自tidyrnest(),这个很不错。

library(tidyr)
nestdf <- df %>% nest(v3:v5)
nestdf$data

> nestdf$data
[[1]]
# A tibble: 2 × 3
     v3    v4    v5
  <int> <int> <int>
1     1    10    12
2     1    10    12

[[2]]
# A tibble: 1 × 3
     v3    v4    v5
  <int> <int> <int>
1    10    12     8

[[3]]
# A tibble: 2 × 3
     v3    v4    v5
  <int> <int> <int>
1     2    12    15
2     2    14    16

使用nestdf$data[1]等方式访问单个tibble。


1
dplyr::group_split()现在是另一个选项,请参见类似的问题和答案:https://dev59.com/xp_ha4cB1Zd3GeqP59wP#57239963 - Stan Rhodes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接