将列乘以 R 中的子字符串

Multiply Columns by Substrings in R

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

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”来乘以列。

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

创建数据

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

在 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

我们创建一个函数来查找特定名称,然后计算行乘积。这个函数的重头戏是 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

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

# 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

建议:如果能以长格式存储数据,以后的维护会更简洁。

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,同时您也可以灵活地使用可变列号。

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

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