如何使用使用两列的自定义函数在 R 中进行聚合
How to aggregate in R with a custom function that uses two columns
是否可以使用使用两列的自定义函数聚合到 return 一列?
假设我有一个数据框:
x <- c(2,4,3,1,5,7)
y <- c(3,2,6,3,4,6)
group <- c("A","A","A","A","B","B")
data <- data.frame(group, x, y)
data
# group x y
# 1 A 2 3
# 2 A 4 2
# 3 A 3 6
# 4 A 1 3
# 5 B 5 4
# 6 B 7 6
我有我想在两列(x 和 y)上使用的函数:
pathlength <- function(xy) {
out <- as.matrix(dist(xy))
sum(out[row(out) - col(out) == 1])
}
我尝试了以下聚合:
out <- aggregate(cbind(x, y) ~ group, data, FUN = pathlength)
out <- aggregate(cbind(x, y) ~ group, data, function(x) pathlength(x))
但是,这会分别调用 x 和 y 上的路径长度,而不是一起调用,给我:
# group x y
#1 A 5 8
#2 B 2 2
我想要的是将 x 和 y 上的路径长度一起调用并以这种方式聚合。这是我希望聚合执行的操作:
realA <- matrix(c(2,4,3,1,3,2,6,3), nrow=4, ncol=2)
pathlength(realA)
# [1] 9.964725
realB <- matrix(c(5,7,4,6), nrow=2, ncol=2)
pathlength(realB)
# [1] 2.828427
group <- c("A", "B")
pathlength <- c(9.964725,2.828427)
real_out <- data.frame(group, pathlength)
real_out
# group pathlength
# 1 A 9.964725
# 2 B 2.828427
有人有什么建议吗?或者是否有一些我在 google 上找不到的其他功能可以让我这样做?我宁愿不使用 for 循环来解决这个问题,因为我假设它对于大数据集来说会很慢。
如您所知,基础 aggregate()
函数一次只能作用于一列。相反,您可以使用 by()
函数
by(data[,c("x","y")], data$group, pathlength)
data$group: A
[1] 9.964725
-----------------------------------------------------------------------
data$group: B
[1] 2.828427
或split()/lapply()
lapply(split(data[,c("x","y")], data$group), pathlength)
$A
[1] 9.964725
$B
[1] 2.828427
新答案
正如@BrodieG 所指出的,这很容易用 "data.table":
完成
> as.data.table(data)[, pathlength(.SD), by = group]
group V1
1: A 9.964725
2: B 2.828427
原创过度杀答案
可以考虑在"data.table"中输入matrix
"on-the-fly":
library(data.table)
as.data.table(data)[, pathlength(matrix(unlist(.SD), ncol = length(.SD))), by = group]
# group V1
# 1: A 9.964725
# 2: B 2.828427
因此,您还可以考虑制作一个辅助函数,如下所示,它会为您创建矩阵:
sdmat <- function(sd) matrix(unlist(sd), ncol = length(sd))
那么,你可以这样做:
as.data.table(data)[, pathlength(sdmat(.SD)), by = group]
# group V1
# 1: A 9.964725
# 2: B 2.828427
甚至:
as.data.table(data)[, pathlength(sdmat(list(x, y))), by = group]
# group V1
# 1: A 9.964725
# 2: B 2.828427
或者,您可以尝试 "dplyr":
library(dplyr)
data %>%
group_by(group) %>%
summarise(pathlength = pathlength(matrix(c(x, y), ncol = 2)))
# Source: local data frame [2 x 2]
#
# group pathlength
# 1 A 9.964725
# 2 B 2.828427
或者,您可以将数据转换为 "long" 格式,然后使用您喜欢的聚合函数。
这是 "dplyr" 的延续:
library(dplyr)
library(tidyr)
data %>%
gather(var, val, -group) %>%
group_by(group) %>%
summarise(pathlength = pathlength(matrix(val, ncol = length(unique(var)))))
# Source: local data frame [2 x 2]
#
# group pathlength
# 1 A 9.964725
# 2 B 2.828427
如果有人想要另一个简单的解决方案,我最终使用了 ddply。事实证明,与聚合不同,您可以使用 ddply 在多个列上使用一个函数。
代码如下:
out <- ddply(data, "group", summarise,
pathlength = pathlength(cbind(x,y)))
是否可以使用使用两列的自定义函数聚合到 return 一列?
假设我有一个数据框:
x <- c(2,4,3,1,5,7)
y <- c(3,2,6,3,4,6)
group <- c("A","A","A","A","B","B")
data <- data.frame(group, x, y)
data
# group x y
# 1 A 2 3
# 2 A 4 2
# 3 A 3 6
# 4 A 1 3
# 5 B 5 4
# 6 B 7 6
我有我想在两列(x 和 y)上使用的函数:
pathlength <- function(xy) {
out <- as.matrix(dist(xy))
sum(out[row(out) - col(out) == 1])
}
我尝试了以下聚合:
out <- aggregate(cbind(x, y) ~ group, data, FUN = pathlength)
out <- aggregate(cbind(x, y) ~ group, data, function(x) pathlength(x))
但是,这会分别调用 x 和 y 上的路径长度,而不是一起调用,给我:
# group x y
#1 A 5 8
#2 B 2 2
我想要的是将 x 和 y 上的路径长度一起调用并以这种方式聚合。这是我希望聚合执行的操作:
realA <- matrix(c(2,4,3,1,3,2,6,3), nrow=4, ncol=2)
pathlength(realA)
# [1] 9.964725
realB <- matrix(c(5,7,4,6), nrow=2, ncol=2)
pathlength(realB)
# [1] 2.828427
group <- c("A", "B")
pathlength <- c(9.964725,2.828427)
real_out <- data.frame(group, pathlength)
real_out
# group pathlength
# 1 A 9.964725
# 2 B 2.828427
有人有什么建议吗?或者是否有一些我在 google 上找不到的其他功能可以让我这样做?我宁愿不使用 for 循环来解决这个问题,因为我假设它对于大数据集来说会很慢。
如您所知,基础 aggregate()
函数一次只能作用于一列。相反,您可以使用 by()
函数
by(data[,c("x","y")], data$group, pathlength)
data$group: A
[1] 9.964725
-----------------------------------------------------------------------
data$group: B
[1] 2.828427
或split()/lapply()
lapply(split(data[,c("x","y")], data$group), pathlength)
$A
[1] 9.964725
$B
[1] 2.828427
新答案
正如@BrodieG 所指出的,这很容易用 "data.table":
完成> as.data.table(data)[, pathlength(.SD), by = group]
group V1
1: A 9.964725
2: B 2.828427
原创过度杀答案
可以考虑在"data.table"中输入matrix
"on-the-fly":
library(data.table)
as.data.table(data)[, pathlength(matrix(unlist(.SD), ncol = length(.SD))), by = group]
# group V1
# 1: A 9.964725
# 2: B 2.828427
因此,您还可以考虑制作一个辅助函数,如下所示,它会为您创建矩阵:
sdmat <- function(sd) matrix(unlist(sd), ncol = length(sd))
那么,你可以这样做:
as.data.table(data)[, pathlength(sdmat(.SD)), by = group]
# group V1
# 1: A 9.964725
# 2: B 2.828427
甚至:
as.data.table(data)[, pathlength(sdmat(list(x, y))), by = group]
# group V1
# 1: A 9.964725
# 2: B 2.828427
或者,您可以尝试 "dplyr":
library(dplyr)
data %>%
group_by(group) %>%
summarise(pathlength = pathlength(matrix(c(x, y), ncol = 2)))
# Source: local data frame [2 x 2]
#
# group pathlength
# 1 A 9.964725
# 2 B 2.828427
或者,您可以将数据转换为 "long" 格式,然后使用您喜欢的聚合函数。
这是 "dplyr" 的延续:
library(dplyr)
library(tidyr)
data %>%
gather(var, val, -group) %>%
group_by(group) %>%
summarise(pathlength = pathlength(matrix(val, ncol = length(unique(var)))))
# Source: local data frame [2 x 2]
#
# group pathlength
# 1 A 9.964725
# 2 B 2.828427
如果有人想要另一个简单的解决方案,我最终使用了 ddply。事实证明,与聚合不同,您可以使用 ddply 在多个列上使用一个函数。
代码如下:
out <- ddply(data, "group", summarise,
pathlength = pathlength(cbind(x,y)))