【问题标题】:Best way to map values using some ID using dplyr?使用 dplyr 使用某些 ID 映射值的最佳方法?
【发布时间】:2021-11-24 18:13:18
【问题描述】:

我需要做的是:我有一个带有q1_monetary_unit 和value 列的数据框,我们称之为df:

ID     q1_monetary_unit   value
1          -999           1000
2          -999           2000
3        10 US dollars    -888
4        21 euros         -888

每当我所在国家/地区的货币未回答价值时,都会有一个描述价值和外币的字符串。我还有一个数据框,其中包含我所在国家/地区货币中每种货币的价值,如下所示:

US dollar    Euro
   780        850

所以我的最终目标是将“10 美元”变成 10 * 780,将 21 欧元变成 21 * 850,并将这些值放入 value 列,如下所示:

ID     value
1        1000
2        2000
3        7800
4        17850

所以,处理我的原始数据,经过一些转换后,我设法获得了以下数据框,我们称之为df2:

ID    value
3     7800
4    17850

所以,我想知道当ID 与这个新数据框中的 ID 相对应时,在我的原始数据框中替换 value 的最简单方法是什么。尝试做类似的事情

df %>% mutate(value = case_when(id %in% df2$id~ df2$value,
                                                   T ~ value))

但它不起作用。我可以删除df 中的行并绑定df2 中的行,但这听起来效率不高。在 Python 中,我会使用 map() 函数,但我不确定 R 的 map 是否以同样的方式工作。任何帮助将不胜感激。

【问题讨论】:

    标签: r dataframe dplyr


    【解决方案1】:

    我跳过了你的 df2 阶段:

    df <- dplyr::tibble(
        ID = 1:4,
        q1_monetary_unit = c(-999, -999, "10 US dollars", "21 euros"), 
        value = c(1000, 2000, -888, -888)
    )
    
    conversions <- dplyr::tibble(
        `US dollars` = 780,
        euros = 850 # NB not exact match in you example!
    )
    
    # make long for join
    conversions_long <- 
    conversions %>% tidyr::pivot_longer(
        everything(), names_to = "currency", values_to = "conversion_factor"
    )
    
    df %>%
        tidyr::extract(
            q1_monetary_unit,
            into = c("val", "currency"),
            regex = "(\\d+) (.*)", # split the value from the currency name
            remove = TRUE, # drop the q1_monetary_unit column
            convert = TRUE # convert numeric type automatically
        ) %>%
        dplyr::full_join(
            conversions_long, by = "currency" # join by currency
        ) %>%
        mutate(
            val = as.double(val), # make val a double as defaulted to int and if_else enforces same type
            value = if_else(is.na(val), value, val), # merge val and value to fill missing data
            conversion_factor = if_else(is.na(conversion_factor), 1, conversion_factor),
            # multiple by 1 if currency it not specified
            value = value * conversion_factor
        ) %>%
        dplyr::select(-val) # drop the val column as no longer needed
    

    虽然看起来很有用,但我不知道重新编码功能!

    【讨论】:

      【解决方案2】:

      设法做到了:

      将df2 变成一个列表,然后使用它重新编码:

      value_list = as.list(df2$value)
      names(value_list ) = df2$id
      df = df%>% mutate(value= recode(df$id, !!!value_list , .default=value))
      

      也欢迎任何更好的选择

      【讨论】:

        【解决方案3】:

        单个正则表达式完成两个难点:(1) 使用rematch2 将货币提取到一个新列(称为currency)和(2) 剥离货币标签,以便value_1 可以转换为编号为sub()。

        在pattern_currencies 的括号内,货币的肉被识别和提取。括号外的垃圾被去掉,例如“s”和“欧元”或“美元”的结尾(ie,s?)和任何周围的空格(ie ,\\s*)。

        如果只涉及少数几种货币,并且它们始终如一地呈现,那么随着时间的推移,这应该不难维持。如果它们不一致,您可能需要第二层转换(eg,“USD”和“US Dollar”都转换为“US Dollar”)。请记住连接区分大小写

        # Incoming dataset & exchange rates
        ds <- 
          tibble::tribble(
            ~ID, ~q1               , ~value_1,
            1,   "-999"            ,  1000,
            2,   "-999"            ,  2000,
            3,   "10 US dollars"   ,  -888,
            4,   "21 Euros"        ,  -888
          )
        ds_exchange <- 
          tibble::tribble(
            ~currency  , ~rate,
            "US dollar",  780,
            "Euro"     ,  850
          )
        
        # Extract & Convert
        pattern_currencies <- "\\s*(?<currency>US dollar|Euro)s?\\s*"
        ds |> 
          rematch2::bind_re_match(from = q1, pattern_currencies) |> 
          dplyr::left_join(ds_exchange, by = "currency") |> 
          dplyr::mutate(
            q1      = dplyr::na_if(q1, "-999"),                   # Set junk to NA
            # value_1 = dplyr::na_if(value_1, "-888"),            # Consider setting to NA
            rate    = dplyr::coalesce(rate, 1),                   # Fill in missing/unnecessary rates
            
            value_2 = sub(pattern_currencies, "", q1, perl = T),  # Strip currency label
            value_2 = as.numeric(value_2),                        # Cast to a number
            value_2 = dplyr::coalesce(value_2, value_1),
            value_3 = value_2 * rate                              # Multiple by exchange rate
          )
        

        输出:

          ID            q1 value_1  currency rate value_2 value_3
        1  1          <NA>    1000      <NA>    1    1000    1000
        2  2          <NA>    2000      <NA>    1    2000    2000
        3  3 10 US dollars    -888 US dollar  780      10    7800
        4  4      21 Euros    -888      Euro  850      21   17850
        

        【讨论】:

        • 我喜欢@Richard J. Acton 的代码处理了这两种情况,所以我为未转换的行添加了几行。
        猜你喜欢
        • 2011-12-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-10-22
        • 1970-01-01
        • 2010-11-19
        相关资源
        最近更新 更多