R - Создать несколько новых столбцов, используя условные выражения - PullRequest
0 голосов
/ 29 июня 2018

R - Создать несколько новых столбцов, используя условные операторы

Мне интересно, есть ли способ создать несколько столбцов, основанных на условии (ях).

Например, ниже, у меня есть фрейм данных с данными, и я хочу создать два столбца на основе ccy. Один столбец - это коэффициент конвертации в фунты стерлингов в ccy, а другой - конвертация в cad.

Если я передам мутации, я смогу заставить их работать, но есть повторение (и в моей реальной проблеме у меня есть сложный список ifelse, поэтому повторение кода для каждого столбца создаст много повторений).

df <- structure(list(product = c('option', 'forward', 'forward', 'option'),
                 ccy = c('usd', 'usd', 'eur', 'usd'),
                 amount = c(1000, 2000, 1000, 5000)),
            .Names = c('product', 'ccy', 'amount'),
            row.names = c(NA, 4L),
            class = "data.frame")
df
  product ccy amount
1  option usd   1000
2 forward usd   2000
3 forward eur   1000
4  option usd   5000

df %>% mutate(gbp_amount = 
                  ifelse(ccy == 'usd', round(amount / 1.8, 2),
                         ifelse(ccy == 'eur', round(amount / 1.3, 2),
                                'not_converted'))) %>% 
    mutate(cad_amount = 
               ifelse(ccy == 'usd', round(amount / 0.85, 2),
                      ifelse(ccy == 'eur', round(amount / .7, 2),
                             'not_converted')))

  product ccy amount gbp_amount cad_amount
1  option usd   1000     555.56    1176.47
2 forward usd   2000    1111.11    2352.94
3 forward eur   1000     769.23    1428.57
4  option usd   5000    2777.78    5882.35

Есть ли способ создать несколько столбцов на основе одного условия if?

Например, что-то вроде этого псевдокода ...

df %>% ifelse(df$ccy == 'usd',
        (mutate(gbp_amount = round(amount / 1.8, 2)),
        mutate(cad_amount = round(amount / 0.85, 2))),
    ifelse(df$ccy == 'eur',
        (mutate(gbp_amount = round(amount / 1.3, 2)),
        mutate(cad_amount = round(amount / 0.7, 2))),
        'not_converted'))

Ответы [ 3 ]

0 голосов
/ 30 июня 2018

Вам придется использовать for-loop, если вы хотите выполнить несколько действий. Решение @R Йоды, вероятно, лучше, хотя. Как он сказал, я бы использовал NA, а не символьную строку, чтобы вы не смешивали типы данных в векторе, в противном случае по умолчанию будет использоваться символ.

for (i in 1:nrow(df)) {
  if(df$ccy[i] == "usd") {
    df$gbp_amount[i] <- round(df$amount[i] / 1.8, 2);
    df$cad_amount[i] <- round(df$amount[i] / 0.85, 2);
  } else {
    NA
 }
  if(df$ccy[i] == "eur") {
    df$gbp_amount[i] <- round(df$amount[i] / 1.3, 2);
    df$cad_amount[i] <- round(df$amount[i] / 0.7, 2);
  } else {
    NA
  }
}
0 голосов
/ 30 июня 2018

Рассмотрите возможность создания набора данных тарифов и объединения с вашим оригиналом, избегая вложенных ifelse:

rates_df <- data.frame(ccy = c('usd', 'eur'),
                       type = c('gbp', 'gbp', 'cad', 'cad'),
                       rate = c(1.8, 1.3, 0.85, 0.7),
                       stringsAsFactors = FALSE)    
rates_df

df %>% 
  inner_join(rates_df, by="ccy") %>%
  mutate(gbp_amount = ifelse(type=="gbp", round(amount / rate, 2), 0),
         cad_amount = ifelse(type=="cad", round(amount / rate, 2), 0)) %>%
  select(product, ccy, matches("amount")) %>%
  group_by(product, ccy, amount) %>%
  summarise_all(sum)

# # A tibble: 4 x 5
# # Groups:   product, ccy [?]
#   product   ccy amount gbp_amount cad_amount
#     <chr> <chr>  <dbl>      <dbl>      <dbl>
# 1 forward   eur   1000     769.23    1428.57
# 2 forward   usd   2000    1111.11    2352.94
# 3  option   usd   1000     555.56    1176.47
# 4  option   usd   5000    2777.78    5882.35
0 голосов
/ 29 июня 2018

Вы можете использовать SQL-подобные объединения, если у вас много условий "равно".

Я использую синтаксис data.table, но вы также можете сделать это dplyr:

library(data.table)

setDT(df)

# add a row which cannot be found ("joined") to demonstrate missing rates
df <- rbind(df, data.table(product = "option", ccy = "aud", amount = 3000))
df

lookup <- data.table(ccy      = c("usd", "eur"),
                     gbp_rate = c( 1.8,   1.3),
                     cad_rate = c( 0.85,  0.7))
lookup
#    ccy gbp_rate cad_rate
# 1: usd      1.8     0.85
# 2: eur      1.3     0.70

df[lookup, `:=`(gbp_amount = round(amount / gbp_rate, 2),
                cad_amount = round(amount / cad_rate, 2)),
                on = "ccy"]
df
#    product ccy amount gbp_amount cad_amount
# 1:  option usd   1000     555.56    1176.47
# 2: forward usd   2000    1111.11    2352.94
# 3: forward eur   1000     769.23    1428.57
# 4:  option usd   5000    2777.78    5882.35
# 5:  option aud   3000         NA         NA

Вы должны отсортировать результат по своему усмотрению и пометить ошибки поиска (отсутствующие коэффициенты конверсии) другим значением, чем NA, если хотите (но не строкой "not_converted", как в вашем вопросе, поскольку это может привести к путанице). тип данных столбца - двойной против символа).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...