我在R中有一个要汇总的数据框。我要应用于每个子集的摘要函数是一个自定义函数,该函数将几个变量(列)作为输入,并返回一个向量或可变长度的列表。作为输出,我希望有一个数据帧,其中包含分组变量的一列,而另一列包含输出矢量(长度可变)。
举一个模拟的例子,假设我有以下数据框:
df <- data.frame( particle = c(rep("X",5),rep("Y",3),rep("Z",4)),
time = c(1:5,1:3,1:4), state = c(c("A","A","B","C","A"),c("A","B","B"),
c("B","C","A","A")), energy = round(runif(12,0,10)))
> df
particle time state energy
1 X 1 A 9
2 X 2 A 8
3 X 3 B 7
4 X 4 C 5
5 X 5 A 0
6 Y 1 A 1
7 Y 2 B 7
8 Y 3 B 7
9 Z 1 B 3
10 Z 2 C 9
11 Z 3 A 5
12 Z 4 A 6
我想为每个粒子获取它们每次改变状态时拥有的能量的列表。我正在寻找的输出是这样的:
>
particle energy
1 X c(9,7,5,0)
2 Y c(1,7)
3 Z c(3,9,5)
为此,我将定义如下函数:
myfun <- function(state, energy){
tempstate <- state[1]
energyvec <- energy[1]
for(i in 2:length(state)){
if(state[i] != tempstate){
energyvec <- c(energyvec, energy[i])
tempstate <- state[i]
}
}
return(energyvec)
}
并尝试通过某种方式将其汇总
我为此尝试过的两个数据结构是data.frame和data.table。
在data.frame中,使用返回矢量的自定义函数似乎可以提供我想要的正确输出格式,即输出列实际上是一个列表,并且每一行都包含一个列表,其中包含功能。但是,以这种方式进行聚合时,似乎无法将多个列传递给该函数。
使用data.table,当考虑多个变量的函数时,聚合更容易进行。但是,我似乎无法获得所需的输出。确实,
dt <- data.table(df)
dt[,myfun(state, energy), by= Particle]
仅返回energyvec的第一个元素(而不是矢量),并且
dt <- data.table(df)
dt[,as.list(myfun(state, energy)), by= Particle]
不起作用,因为输出的长度都不相同。
有没有其他方法可以做到这一点?
非常感谢您的所有帮助!
答案 0 :(得分:1)
这是一种tidyverse
的方法:
library(tidyverse)
df <- data.frame( particle = c(rep("X",5),rep("Y",3),rep("Z",4)),
time = c(1:5,1:3,1:4), state = c(c("A","A","B","C","A"),c("A","B","B"),
c("B","C","A","A")), energy = round(runif(12,0,10)))
# Hard-code energy to make this reproducible
df$energy <- c(9, 8, 7, 5, 0, 1, 7, 7, 3, 9, 5, 6)
df %>%
group_by(particle) %>%
mutate(
changed_state = coalesce(state != lag(state, 1), TRUE)
) %>%
filter(changed_state) %>%
summarise(
string = toString(energy)
)
#> # A tibble: 3 x 2
#> particle string
#> <fct> <chr>
#> 1 X 9, 7, 5, 0
#> 2 Y 1, 7
#> 3 Z 3, 9, 5
我将分别运行管道的每一行。基本上,通过检查“ this”状态是否与最后一个状态changed_state
相匹配来创建lag(state, 1)
变量。由于我们只关心何时发生这种情况,因此我们在filter
处TRUE
(更冗长的行将是filter(changed_state == TRUE)
。toString
函数根据需要折叠能量行,我们已经被particle
“分组”了。
答案 1 :(得分:0)
data.table
方法
样本数据
#stolen from JasonAizkalns's answer
df <- data.frame( particle = c(rep("X",5),rep("Y",3),rep("Z",4)),
time = c(1:5,1:3,1:4), state = c(c("A","A","B","C","A"),c("A","B","B"),
c("B","C","A","A")), energy = round(runif(12,0,10)))
df$energy <- c(9, 8, 7, 5, 0, 1, 7, 7, 3, 9, 5, 6)
代码
library( data.table )
#create data.table
dt <- as.data.table(df)
#use `uniqlist` to get rownumbers where the value of `state` changes,
# then get these rows into a subset
result <- dt[ data.table:::uniqlist(dt[, c("particle", "state")]), ]
#split the resulting `energy`-column by the contents of the `particle`-column
l <- split( result$energy, result$particle)
# $X
# [1] 9 7 5 0
#
# $Y
# [1] 1 7
#
# $Z
# [1] 3 9 5
#craete final output
data.table( particle = names(l), energy = l )
# particle energy
# 1: X 9,7,5,0
# 2: Y 1,7
# 3: Z 3,9,5
答案 2 :(得分:0)
另一种可能的data.table
方法
library(data.table)
setDT(DF)[, .(energy=.(.SD[, first(energy), by=.(rleid(state))]$V1)), by=.(particle)]
输出:
particle energy
1: X 9,4,6,9
2: Y 2,9
3: Z 7,6,1
数据:
set.seed(0L)
DF <- data.frame( particle = c(rep("X",5),rep("Y",3),rep("Z",4)),
time = c(1:5,1:3,1:4), state = c(c("A","A","B","C","A"),c("A","B","B"),
c("B","C","A","A")), energy = round(runif(12,0,10)))
DF
# particle time state energy
# 1 X 1 A 9
# 2 X 2 A 3
# 3 X 3 B 4
# 4 X 4 C 6
# 5 X 5 A 9
# 6 Y 1 A 2
# 7 Y 2 B 9
# 8 Y 3 B 9
# 9 Z 1 B 7
# 10 Z 2 C 6
# 11 Z 3 A 1
# 12 Z 4 A 2