dcast中的自定义聚合函数

custom aggregation function in dcast

我有一个 table 需要重新格式化。 table 看起来像:

date   ItemID   NewPrice   Sale Amount
1-1     1         5            3
1-1     2         8            2
1-1     3         3            5
1-2     1         6            4
1-2     3         4            3
1-3     2         7            2
1-3     3         2            1

第一个 table 我想重新表述如下:

date   item_1    item_2    item_3
1-1      3         2         5 
1-2      4         0         3
1-3      0         2         1

item id 成为列名,value 为销售额。棘手的是,有些天,有些项目没有记录,就像1-2中的项目2没有项目记录一样。在这种情况下,销售金额应填写为0。

第二个table我想重新表述如下:

date     item_1     item_2     item_3
1-1        5          8          3
1-2        6          8          4
1-3        6          7          2

所以我想做的是使用 item_id 作为列,并为每个日期使用 NewPrice 作为值。

棘手的是,在每一天,总有一些物品没有出现,所以那天没有这个物品的新价格。在这种情况下,NewPrice 应该是最后一天的 NewPrice。

reshape2 的继任者是 tidyr,它与 dplyr 很好地结合在一起。你的第一个案例很简单:

library(dplyr)
library(tidyr)

       # get rid of excess column
df %>% select(-NewPrice) %>% 
    # fix labels so they'll make nice column names
    mutate(ItemID = paste0('item_', ItemID)) %>% 
    # spread from long to wide, filling with 0 instead of NA
    spread(ItemID, Sale.Amount, fill = 0)

#   date item_1 item_2 item_3
# 1  1-1      3      2      5
# 2  1-2      4      0      3
# 3  1-3      0      2      1

对于第二个,明确使用fill代替spread中的参数:

       # get rid of excess column
df %>% select(-Sale.Amount) %>% 
    # fix labels so they'll make nice column names
    mutate(ItemID = paste0('item_', ItemID)) %>% 
    # spread from long to wide
    spread(ItemID, NewPrice) %>% 
    # fill NA values with previous value
    fill(-date)


#     date item_1 item_2 item_3
# 1    1-1      5      8      3
# 2    1-2      6      8      4
# 3    1-3      6      7      2

这可以使用 dcast

在一行中轻松完成
library(data.table)
dcast(setDT(dfN), date~paste0("item_", ItemID), value.var="Sale.Amount", fill=0)
#   date item_1 item_2 item_3
#1:  1-1      3      2      5
#2:  1-2      4      0      3
#3:  1-3      0      2      1

对于第二种情况,我们可以使用 na.locf 将 NA 值替换为之前的非 NA 值(在使用 dcast 重塑为 'wide' 之后)。

library(zoo)
dcast(setDT(dfN), date~paste0("item_", ItemID), value.var="NewPrice")[, 
          (2:4) := lapply(.SD, na.locf), .SDcols = item_1:item_3][]
#   date item_1 item_2 item_3
#1:  1-1      5      8      3
#2:  1-2      6      8      4
#3:  1-3      6      7      2

这是第一部分的基础 R 解决方案:

xtabs(`Sale Amount` ~ date + ItemID, DF)
##      ItemID
## date  1 2 3
##   1-1 3 2 5
##   1-2 4 0 3
##   1-3 0 2 1

而对于第二部分,我们在动物园中使用 na.locftapplyna.rm = FALSE 是为了防止第一个日期有 NA。在那种情况下,我们将其保留为 NA。

library(zoo)

na.locf(tapply(DF$NewPrice, DF[c("date", "ItemID")], c), na.rm = FALSE)
##      ItemID
## date  1 2 3
##   1-1 5 8 3
##   1-2 6 8 4
##   1-3 6 7 2

注意:可重现形式的输入DF是:

Lines <- "date   ItemID   NewPrice   'Sale Amount'
1-1     1         5            3
1-1     2         8            2
1-1     3         3            5
1-2     1         6            4
1-2     3         4            3
1-3     2         7            2
1-3     3         2            1"
DF <- read.table(text = Lines, header = TRUE, check.names = FALSE)