R-使用条件语句创建多个新列

时间:2018-06-29 18:45:20

标签: r

R-使用条件语句创建多个新列

我想知道是否有一种方法可以根据条件创建多个列。

例如,在下面的示例中,我有一个包含数据的数据框,我想基于基于ccy的列创建两列。一栏是ccy的gbp转换率,另一栏是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 :(得分:3)

考虑建立一个 rates 数据集并与原始数据合并,避免嵌套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

答案 1 :(得分:2)

如果您有许多“等于”条件,则可以使用类似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"标记)这会混淆列的数据类型-double vs character)。

答案 2 :(得分:0)

如果要执行多个操作,则必须使用for-loop。 @R Yoda的解决方案可能更好。就像他说的那样,我将使用NA而不是字符串,这样您就不会在向量中混合数据类型,否则它将默认为character。

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
  }
}