从宽到长塑造一个巨大的data.table(1,000,000 × 4,000 别名8GB)
Shape a huge data.table (1,000,000 × 4,000 alias 8GB) from wide to long
我的磁盘上有这个 8GB 的 CSV 文件。
每行有一个 "match"。
一个 "match" 由一些数据组成,例如 id
、date
和 winner
。但它也有 10 名球员及其所有数据。这些存储在 participants.0.stats.visionScore
、participants.1.stats.visionScore
、...、participants.0.stats.assists
、...、participants.9.stats.assists
、...我想你明白了。只是participants.{number}.stats.{variable_name}
。每个参与者实际上都有数百个统计数据;这就是为什么我总共有大约 4,000 列。
我是这样读取数据的:
> d <-恐惧("Matches.csv")
> 头(d)
参与者。1.stats.totalDamageDealt
1:118504
2:20934
3:76639
4:123932
5:160561
6:237046
参与者。8.stats.totalDamageTaken 参与者。9.stats.totalPlayerScore
1: 18218 0
2: 12378 0
3: 46182 0
4: 19340 0
5: 30808 0
6: 36194 0
... [我在这里省略了数千行] ...
当然,我现在想要一个数据表示,其中一行对应一个参与者。我想象这样的结果:
> [魔法]
> 头(d)
参与者 stats.totalDamageDealt
1:1 118504
2:2 190143
3:3 46700
4:4 60787
5:5 78108
6:6 124761
stats.totalDamageTaken stats.totalPlayerScore
1: 18218 0
2: 15794 0
3: 34578 0
4: 78771 0
5: 16749 0
6: 11540 0
...
但是所有可用的方法,如 meld
、cast
和 reshape
都需要我手动命名所有列。即使 patterns
for meld
我最终也不得不为每个参与者命名我所有的数百个专栏。有没有办法在 R 中使这个东西变长?
我不是 100% 确定我了解数据的布局方式,但我想我了解了。从示例数据来看,参与者 1 似乎有来自原始数据的 totalDamageDealt 的多行数据,并且结果不需要聚合。如果不是这样,则可能需要不同的步骤。我不得不制作自己的样本数据来尝试 运行 这个。如果您想 post 涵盖所有可能性的最小数据集,这将很有帮助。
否则,这里有一些方法可以使数据完全变长以提取参与者信息,然后再次变宽以使其成为您想要的格式。如果在 dcast
步骤中可能发生的数据变宽时需要任何聚合。
library(data.table)
library(stringr)
# Create example data
dt <- data.table(participant.1.stats.visionScore = c(1,1.1,1.2,1.3,1.4,1.5),
participant.1.stats.totalDamageDealt = c(7.1,8.1,9.1,10.1,11.1,12.1),
participant.2.stats.visionScore = c(2,2.1,2.2,2.3,2.4,2.5),
participant.2.stats.totalDamageDealt = c(7.2,8.2,9.2,10.2,11.2,12.2))
# Make data totally long (not wide at all)
dt <- melt(dt,measure.vars = names(dt))
# Separate participant and stat details into columns
dt[,participant := variable %>% str_extract("(?<=^participant\.)\d+")]
dt[,stat := variable %>% str_extract("(?<=.stats.).+")]
# Remove variable for cleanup
dt[,variable := NULL]
# Create an index to create a unique key in order to be able to dcast without aggregating
dt[,index := 1:.N, by = list(participant,stat)]
# dcast to make the data wide again
dt <- dcast(dt,index + participant ~ stat, value.var = "value")
# Sort to make it easier for a human to view the table
dt <- dt[order(participant)]
# index participant totalDamageDealt visionScore
# 1: 1 1 7.1 1.0
# 2: 2 1 8.1 1.1
# 3: 3 1 9.1 1.2
# 4: 4 1 10.1 1.3
# 5: 5 1 11.1 1.4
# 6: 6 1 12.1 1.5
# 7: 1 2 7.2 2.0
# 8: 2 2 8.2 2.1
# 9: 3 2 9.2 2.2
# 10: 4 2 10.2 2.3
# 11: 5 2 11.2 2.4
# 12: 6 2 12.2 2.5
好的,使用您提供的数据样本:
library(data.table)
setDT(d)
d <- melt(d, measure = patterns("^participants"), value.name = "value")
d <- d[, `:=` (ID = gsub(".*?\.(\d+)\..*","\1", variable),
stats = gsub(".*?(stats\..*)$","\1", variable))
][, .(variable, value, ID, stats)]
d <- dcast(d, ID ~ stats, value.var= "value", fun.aggregate = sum)
编辑: 将此重写为 data.table
速度的唯一解决方案
请注意,您的源数据中还有一些其他列(例如 participantIdentities.6.player.accountId
)您没有提及,因此我只是将它们排除在外。如果需要包含它们,您可以将它们添加到 patterns
或 id.vars
in melt
.
请注意:您输入的所有值都必须是数字,否则dcast
将失败。我相信这将是您的完整数据集的问题。这意味着您需要将 participants.1.highestAchievedSeasonTier
之类的列正确标识为 melt
中的 id.vars
,或者将它们从 dcast
.
中排除
导致(我只是粘贴许多列的前 4 列)
ID participants.4.timeline.xpPerMinDeltas.20-30 stats.goldEarned stats.perk3Var1
1 1 0 0 0
2 4 NA 0 3475
3 7 0 0 0
4 8 0 0 0
5 9 0 105872 0
我找到了一个答案,即使在处理如此大量的数据时也能非常有效地工作。事实上,我猜它在这种情况下与在 R:
中一样有效
cn <- names(d)
pc <- cn[which(grepl("participants.", cn))]
ppcn <- substring(pc[0:(length(pc)/10)], 16)
d_long <- reshape(d, direction='long', varying=pc, timevar='participant', times=c('participants.0', 'participants.1', 'participants.2', 'participants.3', 'participants.4', 'participants.5', 'participants.6', 'participants.7', 'participants.8', 'participants.9'), v.names=ppcn)
它背后的想法是用一些额外的代码行来制作 reshape
函数的参数,这样 R 就可以知道我真正在说什么列。
使用此解决方案,我的长 d
(没有双关语意)是 (1) 在一个步骤中创建的,不需要临时的潜在大表,并且 (2) 没有类型转换,包括所有的列类型。
我的磁盘上有这个 8GB 的 CSV 文件。 每行有一个 "match"。
一个 "match" 由一些数据组成,例如 id
、date
和 winner
。但它也有 10 名球员及其所有数据。这些存储在 participants.0.stats.visionScore
、participants.1.stats.visionScore
、...、participants.0.stats.assists
、...、participants.9.stats.assists
、...我想你明白了。只是participants.{number}.stats.{variable_name}
。每个参与者实际上都有数百个统计数据;这就是为什么我总共有大约 4,000 列。
我是这样读取数据的:
> d <-恐惧("Matches.csv") > 头(d) 参与者。1.stats.totalDamageDealt 1:118504 2:20934 3:76639 4:123932 5:160561 6:237046 参与者。8.stats.totalDamageTaken 参与者。9.stats.totalPlayerScore 1: 18218 0 2: 12378 0 3: 46182 0 4: 19340 0 5: 30808 0 6: 36194 0 ... [我在这里省略了数千行] ...
当然,我现在想要一个数据表示,其中一行对应一个参与者。我想象这样的结果:
> [魔法] > 头(d) 参与者 stats.totalDamageDealt 1:1 118504 2:2 190143 3:3 46700 4:4 60787 5:5 78108 6:6 124761 stats.totalDamageTaken stats.totalPlayerScore 1: 18218 0 2: 15794 0 3: 34578 0 4: 78771 0 5: 16749 0 6: 11540 0 ...
但是所有可用的方法,如 meld
、cast
和 reshape
都需要我手动命名所有列。即使 patterns
for meld
我最终也不得不为每个参与者命名我所有的数百个专栏。有没有办法在 R 中使这个东西变长?
我不是 100% 确定我了解数据的布局方式,但我想我了解了。从示例数据来看,参与者 1 似乎有来自原始数据的 totalDamageDealt 的多行数据,并且结果不需要聚合。如果不是这样,则可能需要不同的步骤。我不得不制作自己的样本数据来尝试 运行 这个。如果您想 post 涵盖所有可能性的最小数据集,这将很有帮助。
否则,这里有一些方法可以使数据完全变长以提取参与者信息,然后再次变宽以使其成为您想要的格式。如果在 dcast
步骤中可能发生的数据变宽时需要任何聚合。
library(data.table)
library(stringr)
# Create example data
dt <- data.table(participant.1.stats.visionScore = c(1,1.1,1.2,1.3,1.4,1.5),
participant.1.stats.totalDamageDealt = c(7.1,8.1,9.1,10.1,11.1,12.1),
participant.2.stats.visionScore = c(2,2.1,2.2,2.3,2.4,2.5),
participant.2.stats.totalDamageDealt = c(7.2,8.2,9.2,10.2,11.2,12.2))
# Make data totally long (not wide at all)
dt <- melt(dt,measure.vars = names(dt))
# Separate participant and stat details into columns
dt[,participant := variable %>% str_extract("(?<=^participant\.)\d+")]
dt[,stat := variable %>% str_extract("(?<=.stats.).+")]
# Remove variable for cleanup
dt[,variable := NULL]
# Create an index to create a unique key in order to be able to dcast without aggregating
dt[,index := 1:.N, by = list(participant,stat)]
# dcast to make the data wide again
dt <- dcast(dt,index + participant ~ stat, value.var = "value")
# Sort to make it easier for a human to view the table
dt <- dt[order(participant)]
# index participant totalDamageDealt visionScore
# 1: 1 1 7.1 1.0
# 2: 2 1 8.1 1.1
# 3: 3 1 9.1 1.2
# 4: 4 1 10.1 1.3
# 5: 5 1 11.1 1.4
# 6: 6 1 12.1 1.5
# 7: 1 2 7.2 2.0
# 8: 2 2 8.2 2.1
# 9: 3 2 9.2 2.2
# 10: 4 2 10.2 2.3
# 11: 5 2 11.2 2.4
# 12: 6 2 12.2 2.5
好的,使用您提供的数据样本:
library(data.table)
setDT(d)
d <- melt(d, measure = patterns("^participants"), value.name = "value")
d <- d[, `:=` (ID = gsub(".*?\.(\d+)\..*","\1", variable),
stats = gsub(".*?(stats\..*)$","\1", variable))
][, .(variable, value, ID, stats)]
d <- dcast(d, ID ~ stats, value.var= "value", fun.aggregate = sum)
编辑: 将此重写为 data.table
速度的唯一解决方案
请注意,您的源数据中还有一些其他列(例如 participantIdentities.6.player.accountId
)您没有提及,因此我只是将它们排除在外。如果需要包含它们,您可以将它们添加到 patterns
或 id.vars
in melt
.
请注意:您输入的所有值都必须是数字,否则dcast
将失败。我相信这将是您的完整数据集的问题。这意味着您需要将 participants.1.highestAchievedSeasonTier
之类的列正确标识为 melt
中的 id.vars
,或者将它们从 dcast
.
导致(我只是粘贴许多列的前 4 列)
ID participants.4.timeline.xpPerMinDeltas.20-30 stats.goldEarned stats.perk3Var1
1 1 0 0 0
2 4 NA 0 3475
3 7 0 0 0
4 8 0 0 0
5 9 0 105872 0
我找到了一个答案,即使在处理如此大量的数据时也能非常有效地工作。事实上,我猜它在这种情况下与在 R:
中一样有效cn <- names(d)
pc <- cn[which(grepl("participants.", cn))]
ppcn <- substring(pc[0:(length(pc)/10)], 16)
d_long <- reshape(d, direction='long', varying=pc, timevar='participant', times=c('participants.0', 'participants.1', 'participants.2', 'participants.3', 'participants.4', 'participants.5', 'participants.6', 'participants.7', 'participants.8', 'participants.9'), v.names=ppcn)
它背后的想法是用一些额外的代码行来制作 reshape
函数的参数,这样 R 就可以知道我真正在说什么列。
使用此解决方案,我的长 d
(没有双关语意)是 (1) 在一个步骤中创建的,不需要临时的潜在大表,并且 (2) 没有类型转换,包括所有的列类型。