我希望将数据框(编辑:以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
的所有行> 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
指定的一维。
答案 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)
您可以使用interaction
和duplicated
:
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))
排除了版本,因为它不是一个非常好的选择,而且耗时太长。