Эффективное преобразование отдельных журналов времени прихода/ухода в общую занятость здания с течением времени

Итак, у меня есть данные в формате .csv, показывающие время, в течение которого конкретные пользователи входят в здание и выходят из него за несколько месяцев. Я пытаюсь использовать R для составления таблицы заполняемости здания каждые 15/30 минут для анализа.

Данные были очищены и представлены в виде большого фрейма данных ffdf (~11 миллионов записей). Фрейм данных имеет типы numeric и factor (он включает строки символов и числа, которые потенциально могут пригодиться позже), а время прихода/ухода находится в столбцах, отформатированных как POSIXct.

Если бы это был гораздо меньший набор данных, я бы сделал следующее:

  • Создайте пустой файл .csv (т.е. occupancy) для хранения уровня занятости, со временем в качестве заголовков столбцов и датой в виде строк.
  • Use a for loop to iterate the following across all rows:
    • Calculate total time in building (clock out - clock in time)
    • Извлеките дату из записи часов, сохраните как inDate
    • Используйте round_any из пакета plyr, чтобы найти ближайший 15-минутный потолок для времени начала, сохраните как nearest15
    • Найдите количество полных 15-минутных блоков, которые были заняты пользователем, через floor(as.numeric((clockouttime - clockintime)/15))
    • Добавьте 1 к соответствующему количеству блоков времени, в течение которых пользователь находился в здании, начиная с occupancy[inDate, nearest15].

Однако цикл for, который перебирает 11 миллионов строк, вообще не будет эффективным.

Кто-нибудь знает, как это сделать эффективно? Я в недоумении - насколько я знаю, семейство функций apply приведёт все данные к одному типу. Нет необходимости в конкретных командах, если вы с ними не знакомы, я просто хотел бы, чтобы кто-нибудь указал мне правильный пакет и общую идею для реализации.

В настоящее время я использую ff для доступа к данным, но если есть лучшие пакеты, которые могут это сделать, я открыт для предложений.

Спасибо.

РЕДАКТИРОВАТЬ: вот отредактированный фрагмент кода, на который я смотрю:

user_hash, section_hash, dept_id, col_a, col_b, clockin_datetime, clockout_datetime EEDD1DA7F38CA42A35CF3C003B,85C7,TS,1,,2013-08-08 12:52:00,2013-08-08 23:00:00 2BCB6AA1603BB4357BC0D390C9,BFA3,VS,1,,2013-08-08 12:48:00,2013-08-08 22:58:00 46D859B55C4802DF51445025C5,943B,TS,1,,2013-08-08 11:58:00,2013-08-08 16:04:00 FE4EEA83AF6EA50CA5738B5610,00B3,VT,1,,2013-08-08 19:56:00,2013-08-08 23:04:00 8DB43D322F0AEF6D2B877862C3,DB1F,TS,1,,2013-08-08 12:49:00,2013-08-08 13:03:00 4E636571D425A74CA6B5FA7909,1860,VS,1,,2013-08-08 12:21:00,2013-08-08 14:01:00 26B41FA581408BDFD747234640,FDA4,VS,1,,2013-08-08 20:38:00,2013-08-08 23:03:00 A6C3C190BFFDCB4194774C1026,45C0,VT,1,,2013-08-08 12:58:00,2013-08-08 20:03:00 938506D977353EA65DC6BB5260,1819,VT,1,,2013-08-08 12:54:00,2013-08-08 16:01:00 E82F9350DA9FFC73EE6A66A286,04C1,VT,1,,2013-08-08 08:42:00,2013-08-08 12:45:00 6B92F1AB6B3EE193430B6B2793,6C2E,TS,1,,2013-08-08 09:58:00,2013-08-08 13:03:00 2B88836D8A4CA5183AAE5D3D9A,497C,TS,2,,2013-08-08 10:35:00,2013-08-08 16:06:00

Желаемый результат, который я имею в виду, выглядит примерно так, хотя любая форма, которая показывает мне занятость в любой заданный период времени/дату, прекрасна.

date 12.00 12.15 12.30 12.45 ....... 2013-08-01 1344 1632 3742 1024 2013-08-02 342 435 435 435 2013-08-03 2013-08-04 ...


person ethane    schedule 13.03.2015    source источник
comment
Рассмотрите возможность использования cut для создания коэффициента, который делит часы прихода и ухода на 15-минутные фрагменты. Затем используйте table, чтобы свести в таблицу количество входов и выходов часов в каждом интервале. Разница между cumsum(clockins) и cumsum(clockouts) заключается в вашей занятости. См. второй фрагмент кода в этой статье, чтобы узнать, как использовать lubridate для создания вектора. с регулярной серией раз, которую вы можете использовать для cut() ваших данных через равные промежутки времени.   -  person Jthorpe    schedule 14.03.2015
comment
Было бы полезнее, если бы вы могли предоставить воспроизводимый пример с примерными входными данными и желаемым результатом.   -  person MrFlick    schedule 14.03.2015
comment
@MrFlick, я только что добавил некоторые образцы данных.   -  person ethane    schedule 14.03.2015
comment
@Jthorpe, спасибо за предложение - я рассмотрю его!   -  person ethane    schedule 14.03.2015


Ответы (1)


Я работал с подобными данными в прошлом и обнаружил, что переупорядочивание данных может помочь. Во-первых, я предполагаю, что ваши даты правильно закодированы как значения дат и что ваши образцы данных находятся в data.frame с именем dd. Например

dd <- structure(list(user_hash = structure(c(11L, 3L, 4L, 12L, 7L, 
5L, 1L, 9L, 8L, 10L, 6L, 2L), .Label = c("26B41FA581408BDFD747234640", 
"2B88836D8A4CA5183AAE5D3D9A", "2BCB6AA1603BB4357BC0D390C9", "46D859B55C4802DF51445025C5", 
"4E636571D425A74CA6B5FA7909", "6B92F1AB6B3EE193430B6B2793", "8DB43D322F0AEF6D2B877862C3", 
"938506D977353EA65DC6BB5260", "A6C3C190BFFDCB4194774C1026", "E82F9350DA9FFC73EE6A66A286", 
"EEDD1DA7F38CA42A35CF3C003B", "FE4EEA83AF6EA50CA5738B5610"), class = "factor"), 
    section_hash = structure(c(8L, 10L, 9L, 1L, 11L, 4L, 12L, 
    5L, 3L, 2L, 7L, 6L), .Label = c("00B3", "04C1", "1819", "1860", 
    "45C0", "497C", "6C2E", "85C7", "943B", "BFA3", "DB1F", "FDA4"
    ), class = "factor"), dept_id = structure(c(1L, 2L, 1L, 3L, 
    1L, 2L, 2L, 3L, 3L, 3L, 1L, 1L), .Label = c("TS", "VS", "VT"
    ), class = "factor"), col_a = c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 
    1L, 1L, 1L, 1L, 2L), col_b = c(NA, NA, NA, NA, NA, NA, NA, 
    NA, NA, NA, NA, NA), clockin_datetime = structure(c(1375980720, 
    1375980480, 1375977480, 1376006160, 1375980540, 1375978860, 
    1376008680, 1375981080, 1375980840, 1375965720, 1375970280, 
    1375972500), class = c("POSIXct", "POSIXt"), tzone = ""), 
    clockout_datetime = structure(c(1376017200, 1376017080, 1375992240, 
    1376017440, 1375981380, 1375984860, 1376017380, 1376006580, 
    1375992060, 1375980300, 1375981380, 1375992360), class = c("POSIXct", 
    "POSIXt"), tzone = "")), .Names = c("user_hash", "section_hash", 
"dept_id", "col_a", "col_b", "clockin_datetime", "clockout_datetime"
), row.names = c(NA, -12L), class = "data.frame")

Теперь, если вы упорядочиваете данные, чтобы иметь поток времени прихода/ухода и присваиваете значение +1 для людей, входящих в здание, и -1, когда они выходят из здания, у вас будет что-то вроде

dx <- rbind(
   data.frame(val=1, time=dd$clockin_datetime), 
   data.frame(val=-1, time=dd$clockout_datetime)
)
dx <- dx[order(dx$time), ]

Затем, чтобы найти количество людей в любой момент времени, вам просто нужно выполнить кумулятивную сумму в столбце val.

transform(dx, pop=cumsum(val))

Затем вы можете разделить это на интервалы.

Работа с data.tables, а не с data.frames, вероятно, будет лучше с точки зрения производительности для данных вашего масштаба, но настройка вещей, чтобы выяснить, что лучше всего сработает для ваших данных, потребует более крупного тестового примера. Но я думаю, что эта общая стратегия может быть весьма полезной.

person MrFlick    schedule 14.03.2015
comment
MrFlick, спасибо! Я рассмотрю этот метод. Я просмотрел пакет data.table, но не уверен, что он разрешает файловый доступ к файлам данных. Мой файл данных в настоящее время составляет около 1 ГБ, который определенно может быть загружен в память, но я хотел использовать это время, чтобы также попрактиковаться в работе с большими данными за пределами объема ОЗУ, и, насколько я знаю, data.table загружает все данные в память. Хотя могу ошибаться - буду разбираться дальше. - person ethane; 14.03.2015