Умножить столбцы на подстроки в R

Предположим, у меня есть фрейм данных, который имеет несколько компонентов и их свойства перечислены в нескольких столбцах, и я хочу запустить несколько функций для столбцов. Мой подход состоял в том, чтобы попытаться основать его на подстроке в заголовке каждого столбца, но я не смог понять, как это сделать. Ниже приведен пример фрейма данных.

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

По сути, я хочу привязать два новых столбца к концу этого кадра данных, который умножает Qty и P, чтобы вы получили два новых столбца в конце, как показано ниже.

F_Total_1   F_Total_2
25          18
30          100

Ввод динамический, поэтому иногда в некоторых корзинах может быть 2 или 10 фруктов. Но я могу понять эту часть, более того, он пытается выяснить, как умножить столбцы на основе подстрок "1" или "2".

Я ценю вашу помощь и любые другие подходы, которые вы можете иметь!

6 ответов

Решение

Мы создаем функцию, которая находит конкретные имена, а затем вычисляет линейные произведения. Тяжелоатлет этой функции является 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
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

В базе 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

Вот метод, использующий 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

Создать данные

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

Предложение: Если бы вы могли хранить ваши данные в длинном формате, это сделает будущее обслуживание намного чище.

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, давая вам возможность иметь переменные номера столбцов тоже.

Имя столбца должно быть относительно легко исправить, если оно важно.

Другие вопросы по тегам