Работа с данными большого размера ^ 1 может быть решена несколькими способами.
- Я был бы упущен, если бы не предложил ("правильную") базу данных SQL.
- Загрузка / обработка только небольшого фрагмента за раз, агрегирование только в конце.То есть, сделайте некоторые из того, что я упоминаю ниже, пытаясь сократить ваши «используемые в настоящее время данные» до одного дня или нескольких дней за один раз.
- Если вы можете загрузить все данные одновременноно просто не можете загрузить инкрементные фрагменты и работать с ними, не превышая объем памяти, а затем попробуйте следующую методику.
Образец данных
(Это создает файлы в вашей работекаталог.)
x1 <- read.csv(header=TRUE, stringsAsFactors=FALSE, text='
X1,X2,X3
10,07:00,30
11,07:01,31
12,07:02,32
13,07:03,33
15,07:05,35
16,07:06,36
18,07:08,38
19,07:09,39')
write.csv(x1, file="20120204.dat", row.names=FALSE)
x2 <- read.csv(header=TRUE, stringsAsFactors=FALSE, text='
X1,X2,X3
20,07:00,40
22,07:02,42
23,07:03,43
24,07:04,44
25,07:05,45
26,07:06,46')
write.csv(x1, file="20120205.dat", row.names=FALSE)
Если вы можете сделать это с двумя файлами, вы можете сделать это с двумя тысячами.В этом случае второй столбец - «время», исходя из вашего вопроса, что единственный способ определить дату / время - это имя файла и это поле.(Обратите внимание, что в процессе, приведенном ниже, merge
установит ключ присоединения в качестве первого столбца, поэтому порядок изменится . Вы можете, конечно, усилить это.)
Работа
fnames <- list.files(pattern=".*\\.dat", full.names=TRUE)
first <- TRUE
times <- c("00:00", "23:59")
for (fn in fnames) {
thisdate <- gsub(".*(20[0-9]{6}).dat", "\\1", fn)
twotimes <- as.POSIXct(paste(thisdate, c("00:00", "23:59")), format = "%Y%m%d %H:%M")
allminutes <- data.frame(X2 = seq(twotimes[1], twotimes[2], by="min"))
dat <- read.csv(fn, stringsAsFactors=FALSE)
dat$X2 <- as.POSIXct(paste(thisdate, dat$X2), format = "%Y%m%d %H:%M")
dat <- merge(dat, allminutes, by="X2", all=TRUE)
write.table(dat, "alldata.csv", append=!first,
col.names=first, row.names=FALSE, sep=",", na="")
first <- FALSE
rm(dat)
gc() # optional, if you really want/need to control memory management *now*
}
Шаг:
times
, twotimes
и allminutes
используются для заполнения data.frame такчто у вас всегда есть как минимум 1440 рядов.Я говорю «по крайней мере», потому что это гарантирует, что у вас будет одно вхождение в «00» секунд каждой минуты.Если у вас есть что-то еще, вам нужно временно создать столбец времени «0 секунд» в каждом кадре или какой-то другой метод, чтобы включить слияние.Если вы посмотрите на allminutes
, вы увидите, что это 1440 строк, 1 столбец, и это все, но его нужно создавать для каждого дня. thisdate
: я извлекаю датукомпонент из имени файла;шаблон может отличаться для вас, но должно быть достаточно ресурсов для его формирования на основе ваших имен файлов. as.POSIXct(...)
: объединение thisdate
и поля времени (X2
), создание истинной даты /объект времени (для замены строк времени). merge()
эффективно добавит строку для пропущенных времен со значениями NA
для всех остальных столбцов. write.table(..., append=!first, col.names=first, ...)
добавляет данныедля CSV, включая только строку заголовка при первом проходе через цикл. rm(dat)
и gc()
вызывает сборку мусора, которая обычно удаляет dat
из памяти.Как правило, это неэффективно, необязательно и иногда нежелательно, но в вашем случае это принудительная гарантия.Вполне возможно, что это будет работать (автоматически собирая неиспользуемые данные) без этого, не стесняйтесь тестировать и сообщать.
Допущения
- Вы можете справиться со всемиданных в памяти за один раз (возможно, с использованием
data.table
), только не с загрузкой и изменением всего этого несколько раз. - Кстати: если у вас будет 9 ГБ данных без заполнения отсутствующихстрок, то вы только увеличите размер файла, и, следовательно, вы, скорее всего, никогда не сможете загрузить этот один файл в свой экземпляр компьютера объемом 8 ГБ. Этот ответ не бесполезен: теперь у вас есть один файл лучшего формата дляданные, которые затем могут быть импортированы в базу данных или аналогичную.
База данных?
Как я упоминал ранее, эти большие объемы данных для локального использования действительно требуют увеличения объема ОЗУ (много) или, возможно, более элегантно, использование базы данных.Можно использовать SQLite (через RSQLite
) для локальной базы данных без сервера, поскольку они могут обрабатывать такие большие данные.Однако, в зависимости от вашего мастерства, нетрудно раскрутить один докер-контейнер с postgres
, mariadb
или даже mssql-server-linux
.
^ 1: «большие данные» могут быть классифицированы на основе того, сколько вы можете сделать на своем компьютере;если вы не можете загрузить его с учетом вашей оперативной памяти и языка / инструмента, то он «большой» для вас.