Я возьму этот старый пост в качестве ссылки. Итак, измененный набор данных выглядит следующим образом:
df <- data.frame(dive = factor(sample(c("dive1","dive2","dive3","dive4"), 14, replace=TRUE)),
speed = runif (14)
)
> df
dive speed
1 dive1 0.627296799
2 dive1 0.288594538
3 dive4 0.598177856
4 dive2 0.371158436
5 dive2 0.827468739
6 dive3 0.485977449
7 dive2 0.151295215
8 dive4 0.773988372
9 dive2 0.567155356
10 dive1 0.008585884
11 dive4 0.433648371
12 dive2 0.759196515
13 dive2 0.641193241
14 dive3 0.089451537
Я хотел бы изменить столбец speed
так, чтобы он содержал среднее значение для группы (одинаковая запись для каждого .group
) для dive1
и dive2
и ничего не делать (оставить df
как есть) для двух других групп).
Я пробовал с if
(и, конечно же, group_by
и summarise
), но это не то, что я хочу, я получаю предупреждение и только 4 результата...
df2 <- if (!(df$dive %in% c("dive3", "dive4"))){
summarise(group_by(df, dive), speed = mean(speed))
}
Warning message:
In if (!(df$dive %in% c("dive3", "dive4"))) { :
the condition has length > 1 and only the first element will be used
> df2
# A tibble: 4 x 2
dive speed
<fct> <dbl>
1 dive1 0.860
2 dive2 0.460
3 dive3 0.277
4 dive4 0.330
Да, извините, мой плохой, я отредактировал «предупреждающее сообщение».
часто (и легко) неправильно интерпретировать одно за другое, хотя некоторые предупреждения должны быть ошибками (и наоборот, хотя и реже)
df %>%
group_by(dive) %>%
mutate(speed = if (first(dive) %in% c("dive1", "dive2")) mean(speed) else speed) %>%
ungroup()
# # A tibble: 14 × 2
# dive speed
# <fct> <dbl>
# 1 dive4 0.548
# 2 dive3 0.156
# 3 dive4 0.207
# 4 dive3 0.148
# 5 dive4 0.886
# 6 dive1 0.498
# 7 dive3 0.690
# 8 dive1 0.498
# 9 dive4 0.0968
# 10 dive3 0.596
# 11 dive2 0.447
# 12 dive2 0.447
# 13 dive3 0.859
# 14 dive3 0.663
или, возможно, немного короче, используя
df %>%
mutate(speed = if (first(dive) %in% c("dive1", "dive2")) mean(speed) else speed,
.by = dive)
Если я неправильно понял, и вместо этого вы хотите сократить две группы до одной строки, сохраняя при этом другие группы как есть (не сокращенные), то, возможно:
df %>%
filter(dive %in% c("dive1", "dive2")) %>%
summarize(speed = mean(speed), .by = dive) %>%
bind_rows(filter(df, !dive %in% c("dive1", "dive2")))
# dive speed
# 1 dive1 0.4983562
# 2 dive2 0.4470575
# 3 dive4 0.5477776
# 4 dive3 0.1558491
# 5 dive4 0.2068528
# 6 dive3 0.1479428
# 7 dive4 0.8858552
# 8 dive3 0.6896862
# 9 dive4 0.0967569
# 10 dive3 0.5961494
# 11 dive3 0.8593978
# 12 dive3 0.6634452
Именно это! Я искал способ использовать if
и mutate
действительно, но я не мог понять, как это сделать!
Зачем мне first()
?
Понятно, потому что if
принимает условия только длины 1. Однако это довольно нелогично.
Конечно, вы можете изменить if (first(dive) %in% ...) ... else ...
на ifelse(dive %in% c(..), mean(speed), speed)
. Если вы предпочитаете это, идите с ним. Здесь эффективнее и быстрее сделать это с помощью if
, так как мы точно знаем, что dive
инвариантна внутри группы, поэтому гораздо проще проверить только одну, а не все.
О, это отличное объяснение, я запомню это. Очень очень полезно!
(1) Это предупреждение, а не ошибка (хотя, я думаю, это станет ошибкой в R-4.3). (2)
if
всегда требует ровно 1 длины (см. stackoverflow.com/q/14170778/3358272 , stackoverflow.com/q/10374932/3358272), ноdf$dive %in% ...
будет возвращать вектор длиныnrow(df)
. Если вы намереваетесь выполнить это определение вgroup_by
иsummarize
, см. последний блок кода в моем ответе. Если это не сработает, добавьте ожидаемый результат (в виде буквального объекта фрейма) в свой вопрос. Спасибо!