【问题标题】:R - Create multiple new columns using conditional statementsR - 使用条件语句创建多个新列
【发布时间】:2018-06-29 18:45:19
【问题描述】:

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'))

【问题讨论】:

    标签: r


    【解决方案1】:

    考虑构建一个 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
    

    【讨论】:

      【解决方案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)。

      【讨论】:

        【解决方案3】:

        如果您想执行多项操作,则必须使用 for-loop。 @R Yoda 的解决方案可能更好。就像他说的那样,我会使用 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
          }
        }
        

        【讨论】:

        • 您刚刚将矢量化过程转换为逐行迭代!
        • 异端,我知道!用户要求提供一个 if 声明,但要小心你想要的。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-08-27
        • 1970-01-01
        • 1970-01-01
        • 2021-08-27
        • 2017-12-02
        • 2021-02-02
        • 2014-09-06
        相关资源
        最近更新 更多