循环以跨列相乘

loop to multiply across columns

我有一个数据框,其中的列标记为 sales1sales2price1price2,我想通过乘以 sales1 * 来计算收入price1 以迭代方式遍历每个数字等等。

data <- data_frame(
  "sales1" = c(1, 2, 3),
  "sales2" = c(2, 3, 4),
  "price1" = c(3, 2, 2),
  "price2" = c(3, 3, 5))

data
# A tibble: 3 x 4
#  sales1 sales2 price1 price2
#   <dbl>  <dbl>  <dbl>  <dbl>
#1      1      2      3      3
#2      2      3      2      3
#3      3      4      2      5

为什么以下代码不起作用?

data %>%
  mutate (
    for (i in seq_along(1:2)) {
      paste0("revenue",i) = paste0("sales",i) * paste0("price",i)
    }
  )

假设您的列已经订购(sales1sales2price1price2)。我们可以将数据帧分成两部分,然后将它们相乘

data[grep("sales", names(data))] * data[grep("price", names(data))]

#  sales1 sales2
#1      3      6
#2      4      9
#3      6     20

如果列还没有根据它们的名称排序,我们可以使用order对它们进行排序,然后使用上面的命令。

data <- data[order(names(data))]

这个回答并不简短。为此,@RonakShah 的现有答案值得一看!

我的回复旨在解决一个更广泛的问题,即在 tidyverse 中尝试这样做的难度。我的理解是这很难,因为数据当前不是 "tidy" 格式。相反,您可以像这样创建一个整洁的数据框:

library(tidyverse)

tidy_df <- data %>% 
  rownames_to_column() %>%
  gather(key, value, -rowname) %>% 
  extract(key, c("variable", "id"), "([a-z]+)([0-9]+)") %>%
  spread(variable, value)

这使得最终的计算变得简单

tidy_df %>% mutate(revenue = sales * price)

#> # A tibble: 6 x 5
#>   rowname    id price sales revenue
#>     <chr> <chr> <dbl> <dbl>   <dbl>
#> 1       1     1     3     1       3
#> 2       1     2     3     2       6
#> 3       2     1     2     2       4
#> 4       2     2     3     3       9
#> 5       3     1     2     3       6
#> 6       3     2     5     4      20

如果您需要将数据恢复为原始格式,您可以这样做,尽管我觉得这很笨拙(我相信这可以通过某种方式改进)。

tidy_df %>% mutate(revenue = sales * price) %>%
  gather(key, value, -c(rowname, id)) %>%
  unite(key, key, id, sep = "") %>%
  spread(key, value) %>% 
  select(starts_with("price"), 
         starts_with("sales"),
         starts_with("revenue"))

#> # A tibble: 3 x 6
#>   price1 price2 sales1 sales2 revenue1 revenue2
#> *  <dbl>  <dbl>  <dbl>  <dbl>    <dbl>    <dbl>
#> 1      3      3      1      2        3        6
#> 2      2      3      2      3        4        9
#> 3      2      5      3      4        6       20