【问题标题】:R - rotating a difficult data frameR - 旋转一个困难的数据框
【发布时间】:2023-08-21 04:22:01
【问题描述】:

假设我有销售各种产品的三位销售代表的销售数据。困难在于每个销售代表销售的产品组合不同,而且数量也不一定相同:

Bob 销售产品 A、B 和 C

Mike 销售产品 A、B、C 和 D

Sara 销售产品 A、B 和 E

   RepName Product SalesDollarAmt SalesQty
 1     Bob       A             43        3
 2    Mike       A             14        5
 3    Sara       A             53        1
 4     Bob       B            100       35
 5    Mike       B            215       80
 6    Sara       B            310      105
 7     Bob       C              5        8
 8    Mike       C             10        3
 9    Mike       D            105       50
10    Sara       E             25       18

我想在产品上旋转这个,这样结果看起来像这样:

  RepName Product.1 SalesDollarAmt.1 SalesQty.1 Product.2 SalesDollarAmt.2 SalesQty.2 Product.3 SalesDollarAmt.3 SalesQty.3 Product.4 SalesDollarAmt.4 SalesQty.4
1     Bob         A               43          3         B              100         35         C                5          8      <NA>                0          0
2    Mike         A               14          5         B              215         80         C               10          3         D              105         50
3    Sara         A               53          1         B              310        105         E               25         18      <NA>                0          0

如果他们都有相同的产品,我会按产品将它们过滤到单独的数据框中,然后在 RepName 上将它们重新组合在一起。我已经用spreaddcast 尝试了所有我能想到的东西。感谢您的帮助!

示例数据帧的代码:

library(tidyverse)

# initial sales data
df <- tribble(
  ~RepName, ~Product, ~SalesDollarAmt, ~SalesQty,
               #-------------------------------
               "Bob", "A", 43, 3,
               "Mike", "A", 14, 5,
               "Sara", "A", 53, 1,
               "Bob", "B", 100, 35,
               "Mike", "B", 215, 80,
               "Sara", "B", 310, 105,
               "Bob", "C", 5, 8,
               "Mike", "C", 10, 3,
               "Mike", "D", 105, 50,
               "Sara", "E", 25, 18
                )

# ideally rotated data
df2 <- tribble(
  ~RepName, ~Product.1, ~SalesDollarAmt.1, ~SalesQty.1, ~Product.2, ~SalesDollarAmt.2, ~SalesQty.2, ~Product.3, ~SalesDollarAmt.3, ~SalesQty.3, ~Product.4, ~SalesDollarAmt.4, ~SalesQty.4,
  #--------------------------------------------------------------
  "Bob", "A", 43, 3, "B", 100, 35, "C", 5, 8, NA, 0, 0, 
  "Mike", "A", 14, 5, "B", 215, 80, "C", 10, 3, "D", 105, 50,
  "Sara", "A", 53, 1, "B", 310, 105, "E", 25, 18, NA, 0, 0 
)

【问题讨论】:

  • 对于每个RepNameProduct 是否真的只有一行? (例如,Bob 产品 A 是否只有一行?)
  • 是的,这是正确的;这只是一个时期的销售数据,所以不会有这样的重复。
  • 似乎您正在将数据从整洁的格式转换为不整洁的格式。能问下是什么原因吗?有时对于根本问题有更好的解决方案。
  • 我需要将输出传递给使用陈旧的 Excel 宏生成报告的其他人,并且以这种方式展开对他来说效果更好;宏循环遍历行并使用 VLOOKUP 一次创建每个代表的报告。

标签: r dataframe tidyverse spread dcast


【解决方案1】:

使用row_numbergatherspreadunite 的组合,我们可以重塑数据。如果您愿意,您可以对列重新排序。在最后一行,我们在对spread 的调用中指定convert = TRUE。这是因为当我们将数据转换为长格式(使用gather)时,列值会转换为字符。在对spread 的调用中指定convert = TRUE(应该)将值恢复为有用的形式。

df %>%
  group_by(RepName) %>%
  mutate(product_count = row_number()) %>% # product "id" within RepName
  gather(variable, value, -RepName, -product_count) %>% # reshape to long
  unite(var_prod, variable, product_count) %>%
  spread(var_prod, value, convert = TRUE) # reshape to wide

  RepName Product_1 Product_2 Product_3 Product_4 SalesDollarAmt_1 SalesDollarAmt_2 SalesDollarAmt_3 SalesDollarAmt_4 SalesQty_1 SalesQty_2 SalesQty_3 SalesQty_4
1     Bob         A         B         C      <NA>               43              100                5             <NA>          3         35          8       <NA>
2    Mike         A         B         C         D               14              215               10              105          5         80          3         50
3    Sara         A         B         E      <NA>               53              310               25             <NA>          1        105         18       <NA>

【讨论】:

  • 感谢一百万!这太棒了。
【解决方案2】:

这个问题被标记为dcast,所以我觉得有义务发布一个使用dcast()的解决方案。

dcast()data.table 版本可以同时重塑多个值列,这正是我们需要的。此外,rowid() 函数用于为每个RepName 单独填充列:

library(data.table)
cast(setDT(df), RepName ~ rowid(RepName), value.var = c("Product", "SalesDollarAmt", "SalesQty"))
   RepName Product_1 Product_2 Product_3 Product_4 SalesDollarAmt_1 SalesDollarAmt_2 SalesDollarAmt_3 SalesDollarAmt_4 SalesQty_1 SalesQty_2 SalesQty_3 SalesQty_4
1:     Bob         A         B         C        NA               43              100                5               NA          3         35          8         NA
2:    Mike         A         B         C         D               14              215               10              105          5         80          3         50
3:    Sara         A         B         E        NA               53              310               25               NA          1        105         18         NA

编辑:改进的版本,列按请求的顺序

In a comment,OP 透露需要进行整形,因为数据将由 Excel 宏进一步处理。通常,列的位置对于 Excel 公式至关重要。

因此,下面的变体对列进行重新排序,以便将属于一个产品的所有列组合在一起:

library(data.table)
# value columns
val <- c("Product", "SalesDollarAmt", "SalesQty")
# create vector of column names in the expected order
col_order <- setDT(df)[, .N, by = RepName][, CJ(seq_len(max(N)), val)][, paste(V2, V1, sep = "_")]
dcast(df, RepName ~ rowid(RepName), value.var = val)[
  #re-order columns in place, i.e., without copying
  , setcolorder(.SD, c("RepName", col_order))]
   RepName Product_1 SalesDollarAmt_1 SalesQty_1 Product_2 SalesDollarAmt_2 SalesQty_2 Product_3 SalesDollarAmt_3 SalesQty_3 Product_4 SalesDollarAmt_4 SalesQty_4
1:     Bob         A               43          3         B              100         35         C                5          8        NA               NA         NA
2:    Mike         A               14          5         B              215         80         C               10          3         D              105         50
3:    Sara         A               53          1         B              310        105         E               25         18        NA               NA         NA

【讨论】:

    最近更新 更多