Сформируйте огромный data.table (1,000,000 × 4000 псевдоним 8GB) от широкого до длинного - PullRequest
0 голосов
/ 17 января 2019

На моем диске есть 8GB-файл CSV. Он имеет один «матч» на строку.

«Соответствие» состоит из некоторых данных, таких как 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}. Каждый участник имеет буквально сотни статистики; поэтому у меня всего около 4000 столбцов.

Я читаю данные так:

> d <- fread("Matches.csv")
> head(d)
   participants.1.stats.totalDamageDealt
1:                                118504
2:                                 20934
3:                                 76639
4:                                123932
5:                                160561
6:                                237046
   participants.8.stats.totalDamageTaken participants.9.stats.totalPlayerScore
1:                                 18218                                     0
2:                                 12378                                     0
3:                                 46182                                     0
4:                                 19340                                     0
5:                                 30808                                     0
6:                                 36194                                     0
... [there are thousands of lines I omit here] ...

Конечно, теперь я хочу представить данные, где одна строка соответствует одному участнику. Я представляю результат, подобный этому:

> [magic]
> head(d)
   participant             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 для meld мне приходится называть все мои сотни столбцов на каждого участника. Неужели нет выхода, чтобы сделать эту вещь длинной формы в R?

Ответы [ 3 ]

0 голосов
/ 18 января 2019

Хорошо, используя предоставленный вами образец данных:

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 в melt.

Одно примечание: все приведенные вами значения должны быть числовыми, в противном случае dcast завершится ошибкой. Я считаю, что это будет проблемой с вашим полным набором данных. Это означает, что вам нужно либо правильно определить столбцы, такие как participants.1.highestAchievedSeasonTier, как id.vars в melt, либо исключить их из dcast.

.

В результате (я просто вставляю первые четыре столбца из многих)

  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
0 голосов
/ 10 марта 2019

Я нашел ответ, который довольно эффективно работает даже с таким огромным количеством данных. На самом деле, я полагаю, что он так же эффективен для этого сценария, как и в 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) без преобразования типов, включая столбцы всех типов.

0 голосов
/ 17 января 2019

Я не уверен на 100%, что понимаю, как выкладываются данные, но, думаю, у меня есть. Из данных примера видно, что участник 1 имеет несколько строк данных для totalDamageDealt из необработанных данных и что результат не требует агрегирования. Если это не так, могут потребоваться другие шаги. Я должен был сделать свои собственные данные образца, чтобы попытаться выполнить это. Если вы хотите опубликовать минимальный набор данных, который охватывает все возможности, это будет полезно.

В противном случае, вот несколько способов сделать данные полностью длинными, чтобы извлечь информацию об участнике, а затем снова расширить их, чтобы преобразовать их в нужный вам формат. Если вам требуется какое-либо агрегирование при расширении данных, это может произойти на шаге 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
...