R - 旋转一个困难的数据框
R - rotating a difficult data frame
假设我有销售各种产品的三个销售代表的销售数据。困难在于每个销售代表销售不同的产品组合,而且数量不一定相同:
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
我想在 Product 上旋转它,结果如下所示:
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 上将它们重新组合在一起。我已经用 spread
和 dcast
尝试了所有我能想到的方法。感谢您的帮助!
示例数据帧的代码:
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
)
结合使用 row_number
、gather
、spread
和 unite
,我们可以重塑数据。如果您愿意,您可以自行决定对列进行重新排序。在最后一行,我们在 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>
这个问题被标记为 dcast
,所以我觉得有必要 post 使用 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
编辑:改进版本,列按请求的顺序排列
,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
假设我有销售各种产品的三个销售代表的销售数据。困难在于每个销售代表销售不同的产品组合,而且数量不一定相同:
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
我想在 Product 上旋转它,结果如下所示:
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 上将它们重新组合在一起。我已经用 spread
和 dcast
尝试了所有我能想到的方法。感谢您的帮助!
示例数据帧的代码:
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
)
结合使用 row_number
、gather
、spread
和 unite
,我们可以重塑数据。如果您愿意,您可以自行决定对列进行重新排序。在最后一行,我们在 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>
这个问题被标记为 dcast
,所以我觉得有必要 post 使用 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
编辑:改进版本,列按请求的顺序排列
因此,下面的变体对列进行了重新排序,以便将属于一个产品的所有列组合在一起:
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