Измените фактор на новую переменную в операторе case_when()

#r #case #r-factor

Вопрос:

Настройка данных

У меня есть набор данных, который выглядит примерно так, как этот простой фрейм данных ниже:

 CAD_EXCHANGE lt;- 1.34 EUR_EXCHANGE lt;- 0.88   df lt;- tibble(  shipment = c("A", "B", "C", "D", "E"),  invoice = c(rep(500, 5)),  currency = factor(c("USD", "EUR", "CAD", NA, "SDD")) )   df # A tibble: 5 x 3  shipment invoice currency  lt;chrgt; lt;dblgt; lt;fctgt;  1 A 500 USD  2 B 500 EUR  3 C 500 CAD  4 D 500 NA  5 E 500 SDD   levels(df$currency) [1] "CAD" "EUR" "SDD" "USD"  

Конечная Цель

Я пытаюсь конвертировать счета-фактуры в доллары США для некоторых распространенных других валют (EUR и CAD), но не для всех из них или если данные отсутствуют (т. Е. SDD и NA ). Мой конечный фрейм данных должен выглядеть следующим образом:

 # A tibble: 5 x 5  shipment invoice currency invoice_converted currency_converted  lt;chrgt; lt;dblgt; lt;fctgt; lt;dblgt; lt;fctgt;  1 A 500 USD 500 USD  2 B 500 EUR 568 USD  3 C 500 CAD 373 USD  4 D 500 NA 500 NA  5 E 500 SDD 500 SDD   

Пробная Версия 1 — Не Работает

В будущем у меня может быть больше, чем просто эти несколько валют для конвертации, поэтому я подал case_when() заявление. Это была моя первая попытка:

 df_USD1 lt;- df %gt;%  mutate(  invoice_converted = case_when(  currency == "EUR" ~ round(invoice / EUR_EXCHANGE),  currency == "CAD" ~ round(invoice / CAD_EXCHANGE),  TRUE ~ invoice  ),  currency_converted = case_when(currency == "EUR" ~ "USD",  currency == "CAD" ~ "USD",  TRUE ~ currency)  )  Error: Problem with `mutate()` column `currency_converted`. i `currency_converted = case_when(...)`. x must be a character vector, not a `factor` object.  

Учитывая вышесказанное, я понимаю, что смешиваю характер и фактор в назначении currency_converted , потому что у меня есть значение по умолчанию TRUE ~ currency currency это фактор). Поэтому я попытался использовать для задания только факторы…

Пробная версия 2 — Работает, но ненадежно

 df_USD2 lt;- df %gt;%  mutate(  invoice_converted = case_when(  currency == "EUR" ~ round(invoice / EUR_EXCHANGE),  currency == "CAD" ~ round(invoice / CAD_EXCHANGE),  TRUE ~ invoice  ),  currency_converted = case_when(  currency == "EUR" ~ currency[1],  currency == "CAD" ~ currency[1],  TRUE ~ currency)  )  

It works, but only because in my setup for this question, USD is in the first position, and I cannot rely on that.

 gt; df$currency [1] USD EUR CAD lt;NAgt; SDD  Levels: CAD EUR SDD USD  

Trial 3 — Doesn’t work

I thought I could try some other way of getting at the factor with subsetting, but this doesn’t work:

 df_USD3 lt;- df %gt;%  mutate(  invoice_converted = case_when(  currency == "EUR" ~ round(invoice / EUR_EXCHANGE),  currency == "CAD" ~ round(invoice / CAD_EXCHANGE),  TRUE ~ invoice  ),  currency_converted = case_when(  currency == "EUR" ~ df$currency[df$currency == "USD"],  currency == "CAD" ~ df$currency[df$currency == "USD"],  TRUE ~ currency  )  )  Error: Problem with `mutate()` column `currency_converted`. i `currency_converted = factor(...)`. x `currency == "EUR" ~ df$currency[df$currency == "USD"]`, `currency == "CAD" ~ df$currency[df$currency == "USD"]` must be length 5 or one, not 2. Run `rlang::last_error()` to see where the error occurred.  

И, похоже, это из NA -за того, что возвращается…

 gt; df$currency[df$currency == "USD"] [1] USD lt;NAgt; Levels: CAD EUR SDD USD  

…потому что, если я вернусь к своему оригиналу df и NA заменю его какой-нибудь другой валютой, это сработает, но, очевидно, мне нужно быть в состоянии сохранить NA то, чему оно принадлежит.

Я чувствую, что есть какой-то очень хороший способ сделать это, но мне его не хватает, несмотря на то, что я читаю о факторах и пробую разные вещи. Помочь?

Ответ №1:

case_when не выполняет преобразование типов автоматически — т. Е. currency является factor , в то время как возврат из других условий case_when является справедливым character . Таким образом, мы можем принудительно преобразовать currency в character , чтобы все возвращаемые значения были одного класса, и это должно сработать

 library(dplyr) df %gt;%  mutate(  invoice_converted = case_when(  currency == "EUR" ~ round(invoice / EUR_EXCHANGE),  currency == "CAD" ~ round(invoice / CAD_EXCHANGE),  TRUE ~ invoice  ), currency_converted = case_when(currency == "EUR" ~ "USD",  currency == "CAD" ~ "USD",  TRUE ~ as.character(currency)))  

-выход

 # A tibble: 5 × 5  shipment invoice currency invoice_converted currency_converted  lt;chrgt; lt;dblgt; lt;fctgt; lt;dblgt; lt;chrgt;  1 A 500 USD 500 USD  2 B 500 EUR 568 USD  3 C 500 CAD 373 USD  4 D 500 lt;NAgt; 500 lt;NAgt;  5 E 500 SDD 500 SDD   

Если мы хотим сохранить его как factor , либо оберните factor после case_when , либо непосредственно используйте fct_recode вместо case_when

 library(forcats) df %gt;%  mutate(  invoice_converted = case_when(  currency == "EUR" ~ round(invoice / EUR_EXCHANGE),  currency == "CAD" ~ round(invoice / CAD_EXCHANGE),  TRUE ~ invoice  ), currency_converted = fct_recode(currency, USD = "EUR", USD = "CAD"))  

-выход

 # A tibble: 5 × 5  shipment invoice currency invoice_converted currency_converted  lt;chrgt; lt;dblgt; lt;fctgt; lt;dblgt; lt;fctgt;  1 A 500 USD 500 USD  2 B 500 EUR 568 USD  3 C 500 CAD 373 USD  4 D 500 lt;NAgt; 500 lt;NAgt;  5 E 500 SDD 500 SDD