Рассмотрим эти данные:
df <- data.frame(group = c(1, 2, 2, 2),
start = c(2, 7, 7, 7),
stop = c(8, 7, 8, 9),
unstop = c(10, 7, 9, 10))
Теперь я хочу настроить более или менее простой case_when для каждой группы в виде «если первая строка сделает это, если вторая строка сделает то». Однако я получаю сообщение об ошибке. Я предполагаю, что это потому, что в группе 1 есть только одна строка, поэтому условие не может быть проверено:
df |>
group_by(group) |>
mutate(n_rows = n(),
split_weeks = case_when(n_rows == 1 ~ str_c(start:stop, collapse = ","),
n_rows > 1 & row_number() == 1 ~ str_c(c(start:stop, unstop:lead(stop)), collapse = ","),
TRUE ~ "fail"))
Error in `mutate()`:
! Problem while computing `split_weeks = case_when(...)`.
ℹ The error occurred in group 1: group = 1.
Caused by error in `unstop:lead(stop)`:
! NA/NaN argument
Run `rlang::last_error()` to see where the error occurred.
Есть идеи, что здесь происходит?
Я предполагаю, что это связано с функцией lead
, потому что, если я удалю эту часть, я «получу только предупреждения, но, по крайней мере, я получу результат.
Ожидаемый результат:
# A tibble: 4 × 6
# Groups: group [2]
group start stop unstop n_rows split_weeks
<dbl> <dbl> <dbl> <dbl> <int> <chr>
1 1 2 8 10 1 2,3,4,5,6,7,8
2 2 7 7 7 3 7,8
3 2 7 8 9 3 fail
4 2 7 9 10 3 fail
Я думаю, что ошибки возникают, когда вы просите R найти lead
либо а) за концом строк таблицы, либо б) за пределами группы. Вы можете передать ему значение по умолчанию, равное 0, которое никогда не используется и подавляет ошибку, но выполняется только на полпути, поскольку функция пытается объединить каждое значение start:stop
и unstop:lead(stop)
во всех строках в группе:
library(tidyverse)
df <- data.frame(group = c(1, 2, 2, 2),
start = c(2, 7, 7, 7),
stop = c(8, 7, 8, 9),
unstop = c(10, 7, 9, 10))
df |>
group_by(group) |>
mutate(
n_rows = n(),
split_weeks = case_when(
n_rows == 1 ~ str_c(start:stop, collapse = ","),
n_rows > 1 &
row_number() == 1 ~ str_c(c(start:stop, unstop:lead(stop, default = 0)), collapse = ","),
TRUE ~ "fail"
)
)
#> Warning in start:stop: numerical expression has 3 elements: only the first used
#> Warning in start:stop: numerical expression has 3 elements: only the first used
#> Warning in start:stop: numerical expression has 3 elements: only the first used
#> Warning in start:stop: numerical expression has 3 elements: only the first used
#> Warning in unstop:lead(stop, 0): numerical expression has 3 elements: only the
#> first used
#> Warning in unstop:lead(stop, 0): numerical expression has 3 elements: only the
#> first used
#> # A tibble: 4 × 6
#> # Groups: group [2]
#> group start stop unstop n_rows split_weeks
#> <dbl> <dbl> <dbl> <dbl> <int> <chr>
#> 1 1 2 8 10 1 2,3,4,5,6,7,8
#> 2 2 7 7 7 3 7,7
#> 3 2 7 8 9 3 fail
#> 4 2 7 9 10 3 fail
Способ уборки будет следующим:
Что заканчивается так, хотя не уверен, почему 7,7,8
помещается в эту ячейку (имеет смысл, поскольку он объединяет 7 с 7 и 7 с 8):
df |>
mutate(lead_stop = lead(stop, default = 0)) |>
group_by(group) |>
mutate(
n_rows = n(),
rownum = row_number()) |>
ungroup() |>
rowwise() |>
mutate(
split_weeks = case_when(
rownum > 1 ~ "fail",
n_rows == 1 ~ str_c(start:stop, collapse = ","),
n_rows > 1 & rownum == 1 ~ str_c(c(start:stop, unstop:lead_stop), collapse = ","),
TRUE ~ "fail"
)
)
#> # A tibble: 4 × 8
#> # Rowwise:
#> group start stop unstop lead_stop n_rows rownum split_weeks
#> <dbl> <dbl> <dbl> <dbl> <dbl> <int> <int> <chr>
#> 1 1 2 8 10 7 1 1 2,3,4,5,6,7,8
#> 2 2 7 7 7 8 3 1 7,7,8
#> 3 2 7 8 9 9 3 2 fail
#> 4 2 7 9 10 0 3 3 fail
Created on 2022-05-07 by the reprex package (v2.0.1)
Вот альтернатива, которая дает желаемый результат (по крайней мере, в этом случае). @Энди Бакстер дал хорошее объяснение того, почему оригинал терпит неудачу; несмотря на то, что case_when использует результат первого случая, второй случай выдает ошибку, поэтому операция завершается неудачно. Вы можете обойти это, используя lead(stop, default = 0)
или, возможно, coalesce(lead(stop), SOMETHING)
, любой из которых даст вычисляемый (если бессмысленный/ненужный) результат, когда нет «следующего» значения.
df |>
group_by(group) |>
mutate(n_rows = n()) %>%
mutate(split_weeks = case_when(
n_rows == 1 ~ str_c(start:stop, collapse = ","),
n_rows > 1 & row_number() == 1 ~ str_c(unstop:(lead(stop, default = 0)), collapse = ","),
# n_rows > 1 & row_number() == 1 ~ str_c(unstop:(coalesce(lead(stop), unstop)), collapse = ","), # Alternative
TRUE ~ "fail"))
Результат
# A tibble: 4 × 6
# Groups: group [2]
group start stop unstop n_rows split_weeks
<dbl> <dbl> <dbl> <dbl> <int> <chr>
1 1 2 8 10 1 2,3,4,5,6,7,8
2 2 7 7 7 3 7,8
3 2 7 8 9 3 fail
4 2 7 9 10 3 fail
Спасибо Джон - это более аккуратный способ выразить это! Я был заинтригован, обнаружив, что case_when
, похоже, не выполняет ленивую оценку — он вычисляет значение при каждом ИСТИННОМ условии, а затем возвращается к первому. Ошибки, возникающие там, где они не имеют значения, являются интересным побочным эффектом!
Чтобы добавить к этому выше, кажется, что даже Хэдли отказался от попыток найти способ обойти это! github.com/tidyverse/dplyr/issues/6158
Интересно узнать об этих ограничениях case_when.
У меня тоже было такое подозрение. Спасибо за решение. Я действительно удивлен, что
lead
не придерживается группировки, применяемой к моим данным.