У меня есть тиббл, содержащий события и их вероятности. Количество исходов может быть два (Да/Нет) или больше (A/B/C/...). В последнем случае у меня есть исчерпывающий список событий, поэтому я не хочу ничего делать:
ok <- tibble(
event = c("A", "B", "C"),
prob = c(0.1, 0.5, 0.4)
)
Если есть только два события, у меня есть только одна строка для вероятности возникновения события:
not_ok <- tibble(
event = "Yes",
prob = 0.4
)
Эти тибблы изменяются в цепочке каналов. В какой-то момент я хочу добавить строку «Нет» к тибблам второго типа.
В настоящее время я прерываю трубу, чтобы сделать:
if (nrow(not_ok) == 1) {
not_ok %<>%
add_row(event = "No", prob = 1-not_ok$prob)
}
И тогда я возобновляю трубку. Однако делать это медленно, некрасиво и требует большего количества заданий.
Можно ли включить этот условный оператор в цепочку каналов, не создавая отдельно оператор if? Код в конечном итоге должен выглядеть так:
data %>%
do something %>%
add row with "No" if necessary %>%
do something else %>%
plot
Если возможно, я бы хотел избежать использования глобальных присваиваний или функций.
Для ясности: данные поступают из запроса к серверу, поэтому я не знаю, вытягивает ли конкретный запрос тиббл типа ok или not_ok. Мне нужна одна операция, которая работает в обоих случаях: в первом случае ничего не делается, а во втором — добавление строки. Например, то, что я делаю сейчас, работает, потому что я использую функцию if для редактирования not_ok только в том случае, если это результат запроса. Например:
fread("...") %>%
mutate(...) %>%
add row if nrow == one
Проблема в том, как сделать это обусловленным количеством строк. Если оно больше единицы, то уже все в порядке. Если есть одна строка, это «Да». поэтому я хочу добавить строку «Нет».
Я что-то упускаю: разве это не именно то, что я сделал? или вы говорите, что порядок важен, поэтому «добавить» означает «добавить в конец»?
Извините, я не увидел вашего ответа. Я отвечу на это.





Вот решение с использованием complete:
not_ok %>%
complete(
event = c("Yes", "No"),
fill = list(prob = (1 - sum(not_ok$prob)))
)
# A tibble: 2 × 2
event prob
<chr> <dbl>
1 No 0.6
2 Yes 0.4
тогда как ok остается неизменным:
ok %>%
complete(
event = c("A", "B", "C"),
fill = list(prob = (1 - sum(not_ok$prob)))
)
# A tibble: 3 × 2
event prob
<chr> <dbl>
1 A 0.1
2 B 0.5
3 C 0.4
Если ваш тиббл сгруппирован или вам нужно более сложное вменение, небольшого изменения должно быть достаточно.
Редактировать
В ответ на комментарий ОП, вот как инкапсулировать эту функциональность в функцию, удобную для канала.
make_complete <- function(df, event_list) {
df %>%
complete(
event = event_list,
fill = list(prob = (1 - sum(df$prob)))
)
}
После которого
not_ok %>% make_complete(c("Yes", "No"))
и
ok %>% make_complete(c("A", "B", "C"))
Оба дают ожидаемый результат, указанный выше. Функция явно может быть встроена в более длинный канал.
Что касается вашего комментария: «Я бы хотел избежать использования... функций». Я не понимаю, как можно сделать это обобщенным без использования функции...
Это работает, но, возможно, мне не совсем понятны данные. Оно происходит от какой-то функции, поэтому назовите его в общем виде df. Мне нужно сделать операцию, которая работает как для ok, так и для not_ok, потому что я не знаю, какая именно. Внесу правку в исходный пост.
Как я показываю, это работает в обоих случаях. если вы хотите интегрировать его в свой канал, инкапсулируйте его в функцию, первым параметром которой является фрейм данных, а вторым — полный список значений event.
Спасибо за написание функции. Проблема заключалась в том, что, поскольку я не знаю, являются ли мои данные ok или not_ok, у меня нет event_list, который можно было бы предоставить функции. Но работает следующее: df %>% make_complete(event_list = if (nrow(df) == 1) {c("Yes", "No")} else { df$event }). Это работает как df <- ok, добавляя строку, так и df <- not_ok, ничего не делая.
Создайте тиббл events всех событий, eventsdf, присоедините его к not_ok, а затем используйте coalesce, чтобы заполнить NA, если таковой имеется.
library(dplyr)
f <- function(df, all_events) {
eventsdf <- tibble(event = all_events)
df %>%
right_join(eventsdf, by = "event") %>%
mutate(prob = coalesce(prob, 1 - sum(prob, na.rm = TRUE)))
}
f(not_ok, c("Yes", "No"))
## # A tibble: 2 × 2
## event prob
## <chr> <dbl>
## 1 Yes 0.4
## 2 No 0.6
f(ok, c("A", "B", "C"))
## # A tibble: 3 × 2
## event prob
## <chr> <dbl>
## 1 A 0.1
## 2 B 0.5
## 3 C 0.4
Спасибо, пожалуйста, посмотрите редактирование исходного сообщения. Это работает для изменения тиббла not_ok, но данные поступают из запроса и могут быть либо ok, либо not_ok, поэтому мне нужен условный оператор, который работает с последним.
Пересмотрели. Теперь должно работать в обоих случаях.
tidyr::expand()илиtidyr::completeдолжны делать то, что вы хотите, в зависимости от вашего конкретного варианта использования. Все, что вам понадобится, это вектор всех возможныхevent. Добавьтеmutate, чтобы получить оставшуюся вероятность. Если есть вероятность или несколько пропущенных типовevent, вам придется определить, как распределить вероятности.