枢轴大data.table
我有一个大的数据表中R:枢轴大data.table
library(data.table)
set.seed(1234)
n <- 1e+07*2
DT <- data.table(
ID=sample(1:200000, n, replace=TRUE),
Month=sample(1:12, n, replace=TRUE),
Category=sample(1:1000, n, replace=TRUE),
Qty=runif(n)*500,
key=c('ID', 'Month')
)
dim(DT)
我想枢此data.table,使得分类成为一列。不幸的是,由于类别的数量在组内不固定,我不能使用this answer。
任何想法我可能会这样做?
/编辑:基于joran的意见和flodel的答案,我们真正重塑以下data.table
:
agg <- DT[, list(Qty = sum(Qty)), by = c("ID", "Month", "Category")]
这种重塑可以实现多种方式(我已经得到了一些很好的答案为止),但我真正想要的是能够很好地适应data.table
的行为,其中包含数百万行和数百到数千个类别。
data.table
实现的melt/dcast
data.table具体方法(在C)更快的版本。它还增加了熔化和铸造多列的附加功能。请参阅Efficient reshaping using data.tables小插图。
请注意,我们不需要加载reshape2
包。
library(data.table)
set.seed(1234)
n <- 1e+07*2
DT <- data.table(
ID=sample(1:200000, n, replace=TRUE),
Month=sample(1:12, n, replace=TRUE),
Category=sample(1:800, n, replace=TRUE), ## to get to <= 2 billion limit
Qty=runif(n),
key=c('ID', 'Month')
)
dim(DT)
> system.time(ans <- dcast(DT, ID + Month ~ Category, fun=sum))
# user system elapsed
# 65.924 20.577 86.987
> dim(ans)
# [1] 2399401 802
那样?
agg <- DT[, list(Qty = sum(Qty)), by = c("ID", "Month", "Category")]
reshape(agg, v.names = "Qty", idvar = c("ID", "Month"),
timevar = "Category", direction = "wide")
没有data.table
特定的宽度调整方法。
这是一种可行的方法,但它相当宽松。
有一个功能要求#2619 Scoping for LHS in :=
,以帮助使这个更简单。
下面是一个简单的例子
# a data.table
DD <- data.table(a= letters[4:6], b= rep(letters[1:2],c(4,2)), cc = as.double(1:6))
# with not all categories represented
DDD <- DD[1:5]
# trying to make `a` columns containing `cc`. retaining `b` as a column
# the unique values of `a` (you may want to sort this...)
nn <- unique(DDD[,a])
# create the correct wide data.table
# with NA of the correct class in each created column
rows <- max(DDD[, .N, by = list(a,b)][,N])
DDw <- DDD[, setattr(replicate(length(nn), {
# safe version of correct NA
z <- cc[1]
is.na(z) <-1
# using rows value calculated previously
# to ensure correct size
rep(z,rows)},
simplify = FALSE), 'names', nn),
keyby = list(b)]
# set key for binary search
setkey(DDD, b, a)
# The possible values of the b column
ub <- unique(DDw[,b])
# nested loop doing things by reference, so should be
# quick (the feature request would make this possible to
# speed up using binary search joins.
for(ii in ub){
for(jj in nn){
DDw[list(ii), {jj} := DDD[list(ii,jj)][['cc']]]
}
}
DDw
# b d e f
# 1: a 1 2 3
# 2: a 4 2 3
# 3: b NA 5 NA
# 4: b NA 5 NA
我会在我的示例data.table上试试这个,并让你知道发生了什么。 – Zach 2013-04-05 14:27:18
编辑
我发现这个SO post,其中包括更好的方式向 缺少的行插入到data.table。功能
fun_DT
相应地调整 。现在代码更清洁了;虽然我没有看到速度改善 。
看到其他职位我更新。 Arun的解决方案也适用,但您必须手动插入缺失的组合。由于你在这里有更多的标识符列(ID,Month),所以我在这里只提出了一个肮脏的解决方案(首先创建一个ID2,然后创建所有的ID2-Category组合,然后填充data.table,然后进行重塑)。
我敢肯定这是不是最好的解决办法,但如果this FR是内置的,这些步骤可以自动完成。
的解决方案是大致相同的速度明智的,尽管它会看到如何扩展(我的机器太慢很有趣,所以我不希望任何进一步增加n个...电脑死机往往已经;-)
library(data.table)
library(rbenchmark)
fun_reshape <- function(n) {
DT <- data.table(
ID=sample(1:100, n, replace=TRUE),
Month=sample(1:12, n, replace=TRUE),
Category=sample(1:10, n, replace=TRUE),
Qty=runif(n)*500,
key=c('ID', 'Month')
)
agg <- DT[, list(Qty = sum(Qty)), by = c("ID", "Month", "Category")]
reshape(agg, v.names = "Qty", idvar = c("ID", "Month"),
timevar = "Category", direction = "wide")
}
#UPDATED!
fun_DT <- function(n) {
DT <- data.table(
ID=sample(1:100, n, replace=TRUE),
Month=sample(1:12, n, replace=TRUE),
Category=sample(1:10, n, replace=TRUE),
Qty=runif(n)*500,
key=c('ID', 'Month')
)
agg <- DT[, list(Qty = sum(Qty)), by = c("ID", "Month", "Category")]
agg[, ID2 := paste(ID, Month, sep="_")]
setkey(agg, ID2, Category)
agg <- agg[CJ(unique(ID2), unique(Category))]
agg[, as.list(setattr(Qty, 'names', Category)), by=list(ID2)]
}
library(rbenchmark)
n <- 1e+07
benchmark(replications=10,
fun_reshape(n),
fun_DT(n))
test replications elapsed relative user.self sys.self user.child sys.child
2 fun_DT(n) 10 45.868 1 43.154 2.524 0 0
1 fun_reshape(n) 10 45.874 1 42.783 2.896 0 0
我会用200,000个ID和1,000个类别来尝试这些,并让你知道它是怎么回事。我怀疑'fun_DT'会爆炸,但'fun_reshape'可能会起作用。 – Zach 2013-04-05 14:26:36
@Zach让我知道,这将是有趣的。你为什么认为'run_DT'会爆炸?无论如何,我认为这些附加领域必须以某种方式创建,所以我不会期望这一点。希望我说得对。另请参阅我的更新。现在代码更清洁。 – 2013-04-06 01:22:02
200,000个ID * 12个月* 1,000个类别= 2,400,000,000行的完整数据帧,大于R(2,147,483,648)中的最大数据量。 – Zach 2013-04-06 01:33:48
你的意思是用'Qty'填充表格的正文吗?总结任何重复的组合? – joran 2013-04-04 22:19:48
@joran:在我的例子中有重复的组合,但为了争论,让我们假设没有。我想要的是对于类别字段的每个值都有一个不同的列,其中NA或0表示缺失的组合。 – Zach 2013-04-04 22:23:33
@joran我认为你的问题的正确答案是肯定的:我希望类别成为一列,每列中包含数量,缺失类别为NAs或0,重复数据应该总结(但在我们之前进行总结是公平的)重塑)。 – Zach 2013-04-05 14:30:02