提问人:BillPetti 提问时间:7/15/2015 最后编辑:zx8754BillPetti 更新时间:11/8/2023 访问量:26924
按组对数据帧运行自定义函数
Run a custom function on a dataframe by group
问:
用于循环访问数据帧中的组的自定义函数。
下面是一些示例数据:
set.seed(42)
tm <- as.numeric(c("1", "2", "3", "3", "2", "1", "2", "3", "1", "1"))
d <- as.numeric(sample(0:2, size = 10, replace = TRUE))
t <- as.numeric(sample(0:2, size = 10, replace = TRUE))
h <- as.numeric(sample(0:2, size = 10, replace = TRUE))
df <- as.data.frame(cbind(tm, d, t, h))
df$p <- rowSums(df[2:4])
我创建了一个自定义函数来计算值 w:
calc <- function(x) {
data <- x
w <- (1.27*sum(data$d) + 1.62*sum(data$t) + 2.10*sum(data$h)) / sum(data$p)
w
}
当我对整个数据集运行该函数时,我得到以下答案:
calc(df)
[1]1.664474
理想情况下,我想返回按 tm 分组的结果,例如:
tm w
1 result of calc
2 result of calc
3 result of calc
到目前为止,我已经尝试使用我的函数,但我收到以下错误:aggregate
aggregate(df, by = list(tm), FUN = calc)
Error in data$d : $ operator is invalid for atomic vectors
我觉得我盯着这个看得太久了,有一个显而易见的答案。
答:
20赞
Colonel Beauvel
7/15/2015
#1
您可以尝试:split
sapply(split(df, tm), calc)
# 1 2 3
#1.665882 1.504545 1.838000
如果你想要一个列表.lapply(split(df, tm), calc)
或者用:data.table
library(data.table)
setDT(df)[,calc(.SD),tm]
# tm V1
#1: 1 1.665882
#2: 2 1.504545
#3: 3 1.838000
4赞
MrGumble
7/15/2015
#2
library(plyr)
ddply(df, .(tm), calc)
评论
0赞
BillPetti
7/15/2015
这正是我最初正在寻找的,但试图在 .你知道等价物是什么吗?dplyr
0赞
MrGumble
7/15/2015
优秀的后续问题。我没有想过 dplyr 取代 ddply(和相关功能)。我现在正在寻求这个问题的答案......
0赞
MrGumble
7/16/2015
我能得出的最接近的是以下内容:,但添加的并不漂亮。group_by(df, tm) %>% do(as.data.frame(calc(.)))
as.data.frame
0赞
MrGumble
7/16/2015
跟进;函数是返回 Data.Frame 而不是标量所必需的。只要返回 data.frame,您就是安全的。do
calc
14赞
akrun
7/15/2015
#3
用dplyr
library(dplyr)
df %>%
group_by(tm) %>%
do(data.frame(val=calc(.)))
# tm val
#1 1 1.665882
#2 2 1.504545
#3 3 1.838000
如果我们稍微更改函数以包含多个参数,这也适用于summarise
calc1 <- function(d1, t1, h1, p1){
(1.27*sum(d1) + 1.62*sum(t1) + 2.10*sum(h1) )/sum(p1) }
df %>%
group_by(tm) %>%
summarise(val=calc1(d, t, h, p))
# tm val
#1 1 1.665882
#2 2 1.504545
#3 3 1.838000
0赞
Richard
8/2/2017
#4
...以及地图功能解决方案...
library(purrr)
df %>%
split(.$tm) %>%
map_dbl(calc)
# 1 2 3
# 1.665882 1.504545 1.838000
5赞
moodymudskipper
2/19/2019
#5
从 dplyr 0.8 开始,您可以使用:group_map
library(dplyr)
df %>% group_by(tm) %>% group_map(~tibble(w=calc(.)))
#> # A tibble: 3 x 2
#> # Groups: tm [3]
#> tm w
#> <dbl> <dbl>
#> 1 1 1.67
#> 2 2 1.50
#> 3 3 1.84
0赞
Simen Løkken
11/8/2023
#6
这是一个简洁的解决方案,也与整洁的格式完全兼容,这里用一个使用 palmerpenguins 数据集和线性回归模型的示例进行说明:
palmerpenguins::penguins |>
drop_na() |>
group_by(species) |>
nest() |>
mutate(
test_results = map(
.x = data,
.f = ~ lm(body_mass_g ~ flipper_length_mm, data = .x
)
|> broom::tidy(conf.int = TRUE)
)
) |>
unnest(test_results) |>
select(species, term, estimate, p.value, conf.low, conf.high) |>
filter(term != "(Intercept)") |>
ungroup()
评论