【问题标题】:Multiply Columns by Substrings in R将列乘以 R 中的子字符串
【发布时间】:2018-06-18 18:57:58
【问题描述】:

假设我有一个数据框,其中包含多个组件,并且它们的属性列在多列中,并且我想针对这些列运行多个函数。我的方法是尝试将它基于每个列标题中的子字符串,但我无法弄清楚如何做到这一点。以下是数据框的示例。

Basket   F_Type_1   F_Qty_1   F_P_1   F_Type_2   F_Qty_2   F_P_2 
AAA       Apple     10        2.5     Banana     9         2
BBB       Peach     5         6       Melon      20        5

我本质上想将两个新列 cbind 到这个数据框的末尾,将 Qty 和 P 相乘,这样你就可以在末尾得到两个新列,如下所示。

F_Total_1   F_Total_2
25          18
30          100

输入是动态的,因此有时它可能是某些篮子中的 2 个水果或 10 个水果。但我可以弄清楚那部分,它更想弄清楚如何根据子字符串“1”或“2”来乘以列。

感谢您的所有帮助以及您可能拥有的任何其他方法!

【问题讨论】:

    标签: r dataframe substr


    【解决方案1】:

    我们创建一个函数来查找特定名称,然后计算行乘积。这个函数的重头戏是mapply 函数。我们添加最后一步来重命名结果data.frame

    fun1 <- function(data){
      qty_names <- names(data)[grepl(pattern = "Qty", x = names(data))]
    
      p_names <- names(data)[grepl(pattern = "P", x = names(data))]
    
      setNames(
        data.frame(
          mapply(qty_names, p_names, 
                 FUN = function(n1, n2) apply(data[c(n1,n2)], 1, prod))),
          paste0('F_Total_', 1:length(p_names)))
    
    
    }
    
    cbind(dat, fun1(dat))
    
      Basket F_Type_1 F_Qty_1 F_P_1 F_Type_2 F_Qty_2 F_P_2 F_Total_1 F_Total_2
    1    AAA    Apple      10   2.5   Banana       9     2        25        18
    2    BBB    Peach       5   6.0    Melon      20     5        30       100
    

    【讨论】:

    • 不应该是apply(data[c(n1,n2)]吗?
    • @Nettle 大获成功!我已经编辑了我的答案。谢谢!
    【解决方案2】:

    在base R中,您可以使用[[]]表单来访问带有字符串的列,因此您可以轻松循环

    df <- read.table(text = "Basket   F_Type_1   F_Qty_1   F_P_1   F_Type_2   F_Qty_2   F_P_2 
    AAA       Apple     10        2.5     Banana     9         2
    BBB       Peach     5         6       Melon      20        5",header = T)
    
    for(i in 1:2)
    {
      df[[paste0("F_Total_",i)]] <- as.numeric(df[[paste0("F_P_",i)]])*as.numeric(df[[paste0("F_Qty_",i)]])
    }
    
      Basket F_Type_1 F_Qty_1 F_P_1 F_Type_2 F_Qty_2 F_P_2 F_Total_1 F_Total_2
    1    AAA    Apple      10   2.5   Banana       9     2        25        18
    2    BBB    Peach       5   6.0    Melon      20     5        30       100
    

    【讨论】:

      【解决方案3】:

      这是一种使用tidyverse 函数重塑数据的方法。基本上使用tidyr 动词,我们将您的数据重塑为更“整洁”的格式。

      # library(dplyr); library(tidyr)
      dd %>% select(Basket, contains("_Qty_"), contains("_P_")) %>% 
        gather("key", "value", -Basket) %>% 
        separate(key, c("F", "Val", "Grp")) %>% 
        group_by(Basket, Grp) %>% 
        spread(Val, value) %>% 
        mutate(Total=P*Qty, GrpN=paste0("Total_", Grp)) %>% 
        ungroup() %>% 
        select(Basket, GrpN, Total) %>% 
        spread(GrpN ,Total)
      
      #   Basket Total_1 Total_2
      #   <fct>    <dbl>   <dbl>
      # 1 AAA         25      18
      # 2 BBB         30     100
      

      【讨论】:

        【解决方案4】:

        创建数据

        library(data.table)
        df <- fread("
        Basket   F_Type_1   F_Qty_1   F_P_1   F_Type_2   F_Qty_2   F_P_2 
        AAA       Apple     10        2.5     Banana     9         2
        BBB       Peach     5         6       Melon      20        5
        ")
        
        df
        #    Basket F_Type_1 F_Qty_1 F_P_1 F_Type_2 F_Qty_2 F_P_2
        # 1:    AAA    Apple      10   2.5   Banana       9     2
        # 2:    BBB    Peach       5   6.0    Melon      20     5
        

        对于从 1 到 sum(grepl('F_P_', names(df))) 的数字,将 F_Total_{number} 设置为 F_Qty_{number}*F_P_{number}

        for(i in seq(sum(grepl('F_P_', names(df)))))
          df[, paste0('F_Total_', i) := Reduce(`*`, .SD)
             , .SDcols = paste0(c('F_Qty_', 'F_P_'), i)]
        
        df
        #    Basket F_Type_1 F_Qty_1 F_P_1 F_Type_2 F_Qty_2 F_P_2 F_Total_1 F_Total_2
        # 1:    AAA    Apple      10   2.5   Banana       9     2        25        18
        # 2:    BBB    Peach       5   6.0    Melon      20     5        30       100
        

        或者在基础 R 中,df 作为data.frame

        for(i in seq(sum(grepl('F_P_', names(df)))))
          df[paste0('F_Total_', i)] <- Reduce(`*`, df[paste0(c('F_Qty_', 'F_P_'), i)])
        

        【讨论】:

          【解决方案5】:
          setNames(df[grepl("F_Qty",names(df))]*df[grepl("F_P",names(df))],paste0("F_Total_",1:2))
          
            F_Total_1 F_Total_2
          1        25        18
          2        30       100
          

          【讨论】:

            【解决方案6】:

            建议:如果你能以长格式存储你的数据,这将使以后的维护更加简洁。

            Basket     Item     Type    Qty Price    Total
            AAA           1    Apple     10   2.5       25
            AAA           2    Banana     9     2       18
            BBB           1    Peach      5   6.0       30
            BBB           2    Melon     20     5      100
            BBB           3    Orange    11   2.7     29.7
            

            然后,当你确实需要上述格式的数据时,使用data.table包转置数据。

            library(data.table)
            fruits <- data.frame("Basket" = c("AAA", "AAA", "BBB", "BBB", "BBB"),
                             "Item" = c(1,2,1,2,3),
                             "Type" = c("Apple", "Banana", "Peach", "Melon", "Orange"),
                             "Qty" = c(10, 9, 5, 20, 11),
                             "Price" = c(2.5, 2, 6.0, 5, 2.7)
            )
            
            fruits$Total <- fruits$Qty * fruits$Price
            fruits.New <- data.table::dcast(setDT(fruits),
                                        formula = Basket ~ Item,
                                        value.var = c("Type", "Price", "Qty", "Total"))
            

            这样,您只需要维护一个公式,而不必担心apply,同时让您也可以灵活地使用可变列号。

            如果它很重要,列名应该相对容易修复。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2013-12-17
              • 1970-01-01
              • 2018-07-21
              • 1970-01-01
              • 1970-01-01
              • 2015-06-14
              • 2012-08-04
              • 2018-05-16
              相关资源
              最近更新 更多