У меня есть тиббл, содержащий события и их вероятности. Количество исходов может быть два (Да/Нет) или больше (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
, вам придется определить, как распределить вероятности.