什么是SQL的等效SQL“SELECT * FROM table GROUP BY c1,c2”?

时间:2014-11-28 17:16:14

标签: sql r data.table aggregate

我希望将数据框(编辑:以cpu效率方式)减少到具有c3,c4对唯一值的行,同时保留所有列。换句话说,我想转换我的数据框

> df <- data.frame(c1=seq(7), c2=seq(4, 10), c3=c("A", "B", "B", "C", "B", "A", "A"), c4=c(1, 2, 3, 3, 2, 2, 1))
  c1 c2 c3 c4
1  1  4  A  1
2  2  5  B  2
3  3  6  B  3
4  4  7  C  3
5  5  8  B  2
6  6  9  A  2
7  7 10  A  1

到数据框

  c1 c2 c3 c4
1  1  4  A  1
2  2  5  B  2
3  3  6  B  3
4  4  7  C  3
6  6  9  A  2

其中c1和c2的值可以是对于唯一的c3,c4对出现的任何值。此外,结果数据框的顺序并不重要。

编辑:我的数据框有大约250 000行和12列,应该按2列分组 - 因此我需要一个CPU高效的解决方案

工作但不满意的替代方案

我用

解决了这个问题
> library(sqldf)
> sqldf("Select * from df Group By c3, c4")

但为了加快和并行化我的程序,我必须消除对sqldf的调用。

编辑:目前,sqldf解决方案的时间为3.5秒。我认为这是一个体面的时间。问题是我无法并行启动各种查询,因此我正在寻找另一种方法。

无法正常工作

重复()

> df[duplicated(df, by=c("c3", "c4")),]
[1] c1 c2 c3 c4
<0 rows> (or 0-length row.names)

选择重复的行,而不选择只有列c3和c4重复的行。

集合体()

> aggregate(df, by=list(df$c3, df$c4))
Error in match.fun(FUN) : argument "FUN" is missing, with no default

聚合需要一个函数应用于具有相同值c3和c4

的所有行

data.table&#39;

> library(data.table)
> dt <- data.table(df)
> dt[,list(c1, c2) ,by=list(c3, c4)]
    c3 c4 c1 c2
1:  A  1  1  4
2:  A  1  7 10
3:  B  2  2  5
4:  B  2  5  8
5:  B  3  3  6
6:  C  3  4  7
7:  A  2  6  9

不会踢出具有非唯一值c3和c4的行,而

> dt[ ,length(c1), by=list(c3, c4)]
   c3 c4 V1
1:  A  1  2
2:  B  2  2
3:  B  3  1
4:  C  3  1
5:  A  2  1

会丢弃c1和c2的值,并将它们缩小为传递函数length指定的一维。

4 个答案:

答案 0 :(得分:4)

这是一个data.table解决方案。

library(data.table)
setkey(setDT(df),c3,c4)   # convert df to a data.table and set the keys.
df[,.SD[1],by=list(c3,c4)]
#    c3 c4 c1 c2
# 1:  A  1  1  4
# 2:  A  2  6  9
# 3:  B  2  2  5
# 4:  B  3  3  6
# 5:  C  3  4  7

你建议的SQL似乎提取了第一行有(c3,c4)的给定组合 - 我假设你想要的。


编辑:回应OP的评论。

你引用的结果似乎很奇怪。下面的基准测试,在12列和2.5e5行的数据集上,显示data.table解决方案在没有设置密钥的情况下运行大约25 <毫秒,在设置密钥时大约7毫秒。

set.seed(1)  # for reproducible example
df <- data.frame(c3=sample(LETTERS[1:10],2.5e5,replace=TRUE),
                 c4=sample(1:10,2.5e5,replace=TRUE),
                 matrix(sample(1:10,2.5e6,replace=TRUE),nc=10))
library(data.table)
DT.1 <- as.data.table(df)
DT.2 <- as.data.table(df)
setkey(DT.2,c3,c4)
f.nokeys <- function() DT.1[,.SD[1],by=list(c3,c4)]
f.keys   <- function() DT.2[,.SD[1],by=list(c3,c4)]
library(microbenchmark)
microbenchmark(f.nokeys(),f.keys(),times=10)
# Unit: milliseconds
#        expr      min        lq    median        uq       max neval
#  f.nokeys() 23.73651 24.193129 24.609179 25.747767 26.181288    10
#    f.keys()  5.93546  6.207299  6.395041  6.733803  6.900224    10

您的数据集在哪些方面与此不同?

答案 1 :(得分:2)

缺点(可能):所有解决方案都按组变量对结果进行排序。

使用aggregate

马丁提到的解决方案:aggregate(. ~ c3 + c4, df, head, 1)

我的旧解决方案:

> aggregate(df,by=list(df$c3,df$c4),FUN=head,1)
  Group.1 Group.2 c1 c2 c3 c4
1       A       1  1  4  A  1
2       A       2  6  9  A  2
3       B       2  2  5  B  2
4       B       3  3  6  B  3
5       C       3  4  7  C  3
> aggregate(df,by=list(df$c3,df$c4),FUN=head,1)[,-(1:2)]
  c1 c2 c3 c4
1  1  4  A  1
2  6  9  A  2
3  2  5  B  2
4  3  6  B  3
5  4  7  C  3

使用ddply

> require(plyr)
Loading required package: plyr
> ddply(df, ~ c3 + c4, head, 1)
  c1 c2 c3 c4
1  1  4  A  1
2  6  9  A  2
3  2  5  B  2
4  3  6  B  3
5  4  7  C  3

答案 2 :(得分:1)

您可以使用interactionduplicated

subset(df, !duplicated(interaction(c3, c4)))
#   c1 c2 c3 c4
# 1  1  4  A  1
# 2  2  5  B  2
# 3  3  6  B  3
# 4  4  7  C  3
# 6  6  9  A  2

答案 3 :(得分:1)

一些dplyr选项:

library(dplyr)
group_by(df, c3, c4) %>% filter(row_number() == 1)
group_by(df, c3, c4) %>% slice(1)
group_by(df, c3, c4) %>% do(head(.,1))
group_by(df, c3, c4) %>% summarise_each(funs(first))
group_by(df, c3, c4) %>% summarise_each(funs(.[1]))
group_by(df, c3, c4) %>% summarise_each(funs(head(.,1)))
group_by(df, c3, c4) %>% distinct()

这是一个仅限dplyr的基准:

library(microbenchmark)
set.seed(99)
df <- data.frame(matrix(sample(500, 25e4*12, replace = TRUE), ncol = 12))
dim(df)

microbenchmark(
  f1 = {group_by(df, X1, X2) %>% filter(row_number() == 1)},
  f2 = {group_by(df, X1, X2) %>% summarise_each(funs(first))},
  f3 = {group_by(df, X1, X2) %>% summarise_each(funs(.[1]))},
  f4 = {group_by(df, X1, X2) %>% summarise_each(funs(head(., 1)))},
  f5 = {group_by(df, X1, X2) %>% distinct()},
  times = 10
)

Unit: milliseconds
 expr   min    lq median    uq   max neval
   f1   498   505    509   527   615    10
   f2   726   766    794   815   823    10
   f3  1485  1504   1545  1571  1639    10
   f4 25170 25668  26027 26188 26406    10
   f5   618   622    631   653   675    10

我用do(head(.,1))排除了版本,因为它不是一个非常好的选择,而且耗时太长。