我在聚合数据框时遇到一些麻烦,同时保持组的原始顺序(基于数据框中的第一次出现的顺序)。我已经设法做到了,但我希望有一个更简单的方法来解决它。
以下是要处理的示例数据集:
set.seed(7)
sel.1 <- sample(1:5, 20, replace = TRUE) # selection vector 1
sel.2 <- sample(1:5, 20, replace = TRUE)
add.1 <- sample(81:100) # additional vector 1
add.2 <- sample(81:100)
orig.df <- data.frame(sel.1, sel.2, add.1, add.2)
需要注意的一些要点:有两个选择列可确定数据如何组合在一起。他们将是相同的,他们的名字是已知的。我在这些数据中只添加了两列,但可能还有更多。我给出了以'sel'和'add'开头的列名,以便更容易理解,但实际数据有不同的名称(因此虽然grep
技巧很酷,但它们在这里没用)。
我要做的是根据“sel”列将数据框聚合成组,并将所有“添加”列相加。这很简单,使用aggregate
如下:
# Get the names of all the additional columns
all.add <- names(orig.df)[!(names(orig.df)) %in% c("sel.1", "sel.2")]
aggr.df <- aggregate(orig.df[,all.add],
by=list(sel.1 = orig.df$sel.1, sel.2 = orig.df$sel.2), sum)
问题是结果是由'sel'列排序的;我希望它根据每个组首次出现在原始数据中进行排序。
以下是我做这项工作的最佳尝试:
## Attempt 1
# create indices for each row (x) and find the minimum index for each range
index.df <- aggregate(x = 1:nrow(orig.df),
by=list(sel.1 = orig.df$sel.1, sel.2 = orig.df$sel.2), min)
# Make sure the x vector (indices) are in the right range for aggr.df
index.order <- (1:nrow(index.df))[order(index.df$x)]
aggr.df[index.order,]
## Attempt 2
# get the unique groups. These are in the right order.
unique.sel <- unique(orig.df[,c("sel.1", "sel.2")])
# use sapply to effectively loop over data and sum additional columns.
sums <- t(sapply(1:nrow(unique.sel), function (x) {
sapply(all.add, function (y) {
sum(aggr.df[which(aggr.df$sel.1 == unique.sel$sel.1[x] &
aggr.df$sel.2 == unique.sel$sel.2[x]), y])
})
}))
data.frame(unique.sel, sums)
虽然这些给了我正确的结果,但我希望有人可以指出一个更简单的解决方案。如果解决方案适用于标准R安装附带的软件包,那将是更好的选择。
我查看了aggregate
和match
的文档,但我找不到答案(我想我希望有类似“keep.original.order”参数的内容aggregate
)。
非常感谢任何帮助!
更新:(万一有人偶然发现)
这是我尝试再过几天后能找到的最干净的方式:
unique(data.frame(sapply(names(orig.df), function(x){
if(x %in% c("sel.1", "sel.2")) orig.df[,x] else
ave(orig.df[,x], orig.df$sel.1, orig.df$sel.2, FUN=sum)},
simplify=FALSE)))
答案 0 :(得分:5)
在data.table中简短而简单。它默认以第一个出现顺序返回组。
require(data.table)
DT = as.data.table(orig.df)
DT[, list(sum(add.1),sum(add.2)), by=list(sel.1,sel.2)]
sel.1 sel.2 V1 V2
1: 5 4 96 84
2: 2 2 175 176
3: 1 5 384 366
4: 2 5 95 89
5: 4 1 174 192
6: 2 4 82 87
7: 5 3 91 98
8: 3 2 189 178
9: 1 4 170 183
10: 1 1 100 91
11: 3 3 81 82
12: 5 5 83 88
13: 2 3 90 96
这对于大数据来说速度很快,因此如果您确实发现速度问题,则无需在以后更改代码。以下替代语法是传递哪些列分组的最简单方法。
DT[, lapply(.SD,sum), by=c("sel.1","sel.2")]
sel.1 sel.2 add.1 add.2
1: 5 4 96 84
2: 2 2 175 176
3: 1 5 384 366
4: 2 5 95 89
5: 4 1 174 192
6: 2 4 82 87
7: 5 3 91 98
8: 3 2 189 178
9: 1 4 170 183
10: 1 1 100 91
11: 3 3 81 82
12: 5 5 83 88
13: 2 3 90 96
或,by
也可以是单个逗号分隔的列名字符串:
DT[, lapply(.SD,sum), by="sel.1,sel.2"]
答案 1 :(得分:1)
有点难以阅读,但它给了你想要的东西,我添加了一些评论来澄清。
# Define the columns you want to combine into the grouping variable
sel.col <- grepl("^sel", names(orig.df))
# Create the grouping variable
lev <- apply(orig.df[sel.col], 1, paste, collapse=" ")
# Split and sum up
data.frame(unique(orig.df[sel.col]),
t(sapply(split(orig.df[!sel.col], factor(lev, levels=unique(lev))),
apply, 2, sum)))
输出看起来像这样
sel.1 sel.2 add.1 add.2
1 5 4 96 84
2 2 2 175 176
3 1 5 384 366
5 2 5 95 89
6 4 1 174 192
7 2 4 82 87
8 5 3 91 98
10 3 2 189 178
11 1 4 170 183
14 1 1 100 91
17 3 3 81 82
19 5 5 83 88
20 2 3 90 96
答案 2 :(得分:1)
寻找同一问题的解决方案,我发现了一个使用aggregate()的新解决方案,但首先将select变量转换为具有所需顺序的因子。
all.add <- names(orig.df)[!(names(orig.df)) %in% c("sel.1", "sel.2")]
# Selection variables as factor with leves in the order you want
orig.df$sel.1 <- factor(orig.df$sel.1, levels = unique(orig.df$sel.1))
orig.df$sel.2 <- factor(orig.df$sel.2, levels = unique(orig.df$sel.2))
# This is ordered first by sel.1, then by sel.2
aggr.df.ordered <- aggregate(orig.df[,all.add],
by=list(sel.1 = orig.df$sel.1, sel.2 = orig.df$sel.2), sum)
输出结果为:
newvar add.1 add.2
1 1 1 100 91
2 1 4 170 183
3 1 5 384 366
4 2 2 175 176
5 2 3 90 96
6 2 4 82 87
7 2 5 95 89
8 3 2 189 178
9 3 3 81 82
10 4 1 174 192
11 5 3 91 98
12 5 4 96 84
13 5 5 83 88
要让它为两个变量的每个组合首次出现而订购,您需要一个新变量:
# ordered by first appearance of the two variables (needs a new variable)
orig.df$newvar <- paste(orig.df$sel.1, orig.df$sel.2)
orig.df$newvar <- factor(orig.df$newvar, levels = unique(orig.df$newvar))
aggr.df.ordered2 <- aggregate(orig.df[,all.add],
by=list(newvar = orig.df$newvar,
sel.1 = orig.df$sel.1,
sel.2 = orig.df$sel.2), sum)
给出输出:
newvar sel.2 sel.1 add.1 add.2
1 5 4 4 5 96 84
2 5 5 5 5 83 88
3 5 3 3 5 91 98
4 2 4 4 2 82 87
5 2 2 2 2 175 176
6 2 5 5 2 95 89
7 2 3 3 2 90 96
8 1 4 4 1 170 183
9 1 5 5 1 384 366
10 1 1 1 1 100 91
11 4 1 1 4 174 192
12 3 2 2 3 189 178
13 3 3 3 3 81 82
使用此解决方案,您无需安装任何新软件包。
答案 3 :(得分:0)
不确定这个解决方案对于大型数据集的速度和存储容量等,但我认为这是解决此问题的一种非常简单的方法。
# Create dataframe
x <- c("C", "C", "A", "A", "A","B", "B")
y <- c(5, 6, 3, 2, 7, 8, 9)
df <- data.frame(x, y)
df
原始数据框:
x y
1 C 5
2 C 6
3 A 3
4 A 2
5 A 7
6 B 8
7 B 9
解决方案:
# Add column with the original order
order <- seq(1:length(df$x))
df$order <- order
# Aggregate
# use sum for column Y (the variable you want to aggregate according to X)
df1 <- aggregate(y~x,data=df,FUN=sum)
# use mean for column 'order'
df2 <- aggregate(order~x, data=df,FUN=mean)
# Add the mean of order values to the dataframe
df <- df1
df$order <- df2$order
# Order the dataframe according the the mean of order values
df <- df[order(df$order),]
df
具有相同顺序的聚合数据框:
x y order
3 C 11 1.5
1 A 12 4.0
2 B 17 6.5