如何在R中使用自定义函数对两列进行聚合

4

能否使用一个自定义函数聚合两列数据并返回一列数据?

假设我有一个数据框:

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上调用pathlength,给出了如下结果:
#  group x y
#1     A 5 8
#2     B 2 2

我希望你能将x和y的pathlength一起调用并按照以下方式进行聚合。这就是我的聚合要做的事情:
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

有人有什么建议吗?或者有没有其他的函数可以让我做到这一点,但我在谷歌上找不到。我不想使用for循环来解决这个问题,因为我认为对于大数据集来说速度会很慢。

3个回答

6

正如您所发现的,基本的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

3
或者 stack(lapply(split(data[-1], data[1]), pathlength)) - BrodieG

3

新答案

正如 @BrodieG 指出的那样,使用 "data.table" 轻松完成此操作:

> as.data.table(data)[, pathlength(.SD), by = group]
   group       V1
1:     A 9.964725
2:     B 2.828427

简洁翻译

你可以考虑在"data.table"中实时生成"matrix"输入:
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

或者,您可以将数据转换为“长”格式,然后使用您喜欢的聚合函数。

这里是使用“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

0
如果有人想要另一个简单的解决方案,我最终使用了ddply。结果发现你可以在ddply中对多个列使用函数,而不像聚合函数那样。以下是代码:
out <- ddply(data, "group", summarise,
                       pathlength = pathlength(cbind(x,y)))

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