Оптимальное извлечение дня недели / недели / месяца из дат для компактности памяти? - PullRequest
1 голос
/ 07 августа 2011

Краткое содержание: * Повышение эффективности памяти функций извлечения на основе даты, используемых в совокупных вызовах fn ниже; чтобы не выбить предел памяти в 1 Гб. *.

У меня есть большой набор данных tr , хранящийся в кадре данных (3 столбца, 12 миллионов строк; ~ 200 МБ)
Это следующие столбцы: customer_id (целое число), visit_date и visit_spend (числовое значение) Для набора данных требуется регистрация , поэтому это настолько воспроизводимо, насколько это возможно:
Набор данных выглядит так (полный файл здесь, требуется регистрация ):

customer_id,visit_date,visit_spend 
2,2010-04-01,5.97 
2,2010-04-06,12.71 
2,2010-04-07,34.52
#... 12146637 rows in total

Диапазон дат ограничен между 2010-04-01 ... 2011-06-30 (14700..15155 в целых числах)

Здесь я спрашиваю, какое оптимальное представление выбрать для поля visit_date . Я делаю aggregate звонки (пример кода внизу), которые взрывают память. Я также использую утилиту даты fns, что-то вроде того, что здесь прикреплено внизу (для компактности им понадобится перекодирование, но это типичные операции, которые я хочу сделать много). Поэтому мне нужно представление для даты, которая избегает этого.

На мой взгляд, есть три возможных представления, которые я мог бы использовать для поля visit_date , вот плюсы и минусы, относительно того, что я пытаюсь сделать. Моя цель состоит в том, чтобы получить формат, который не взрывает память и дает наименьшее горе в ходе этих операций обработки данных, агрегирования и т. Д.

  • целое число или коэффициент
    Минусы:
    1) не допускает операции сравнения или сортировки, поэтому агрегация болезненна.
    2) Мне нужно жестко закодировать все функции, связанные с датами (например, 14700..14729 на апрель 2010 г.) - выполнимо, но больно.
    3) Требуется ручная обработка графиков.
  • цифровая
    Минусы: взрывает память из-за необходимости везде вызывать asDate ().
  • Дата
    Плюсы: наиболее читаемые для print (), графиков и гистограмм; не требует обработки вручную перед графиком.
    Минусы: взрывается (сбой нехватки памяти), если я применяю какие-либо строковые операции (формат) или агрегирование. Я думаю, что это chron :: Date, это то, что вы получаете, когда устанавливаете class(tr$visit_date)<-'Date') или используете read.csv(colClasses=c(...,"Date",...)

Это утилита date fns Я хочу запустить много (но в настоящее время они взрываются во время агрегации):

# Utility fns related to date    
library(chron)    
# Get dayoftheweek as integer 0(Sun)..6(Sat)
dayofweek <- function(ddd) {
    with( month.day.year(ddd), day.of.week(month,day,year) )
    #do.call( "day.of.week", month.day.year(x) )
    # as.numeric(x-3)%%7
}    
weekofyear <- function(dt) {
    as.numeric(format(as.Date(dt), "%W"))
}
monthofyear <- function(dt) {
    as.numeric(format(as.Date(dt), "%m"))
}    
# Append a dayoftheweek-as-string field
append_dotwa_column <- function(x,from_date_fld) {
    x$dotwa <- format(x$from_date_fld,"%a")
}

и вот только один вызов aggregate (), который завершается с ошибкой:

agg_dly_spend  <- aggregate(tr$visit_spend, 
        list('visit_date'=tr$visit_date), FUN="sum")
agg_wkly_spend <- aggregate(tr$visit_spend, 
        list('weekofyear'=weekofyear(tr$visit_date)), FUN="sum")

(Сколько памяти должны занимать эти вызовы aggregate ()? Поправьте меня, если я ошибаюсь, но смешанные типы затрудняют использование bigmemory. Поэтому мне, возможно, придется перейти на SQL, но это большая потеря - я теряю действительно хорошую подгруппу R по дате: tr[tr$visit_date > "2010-09-01",])

(Платформа R 2.13.1, Windows Vista 32b, поэтому общий лимит процесса составляет 2 ГБ, что означает, что любой фрейм данных не должен превышать ~ 600-900 МБ)

1 Ответ

6 голосов
/ 07 августа 2011

EDIT: Код, который я скопировал, не был конечной функцией, поэтому в нем были ошибки.Исправлены ошибки.

Я не совсем согласен с голосами, чтобы закрыть, но ваш вопрос нуждается в чтении.Как я понял, проблема в представлении даты.Числовые это просто сумасшедшая идея, в этом случае используйте целое число.Так же, как обзор различных форматов и их относительного пространства (используя функцию lsos из , этот вопрос :)

Dates <- rep(seq.Date(as.Date("2010-04-01"),
             as.Date("2011-04-01"),by='day'),each=100)

chardates <- as.character(Dates)
POSIXctDates <- as.POSIXct(Dates)
POSIXltDates <- as.POSIXlt(Dates)
integerDates <- as.integer(Dates)
chronDates <- as.chron(Dates)
numericDates <- as.numeric(Dates)

 > lsos()
                  Type    Size PrettySize  Rows Columns
POSIXltDates   POSIXlt 1464976     1.4 Mb 36600      NA
chronDates       dates  293400   286.5 Kb 36600      NA
POSIXctDates   POSIXct  292976   286.1 Kb 36600      NA
Dates             Date  292944   286.1 Kb 36600      NA
numericDates   numeric  292824     286 Kb 36600      NA
charDates    character  161064   157.3 Kb 36600      NA
integerDates   integer  146424     143 Kb 36600      NA

Внутренний Date может довольно хорошо конкурировать с числовым представлением,character вызывает проблемы со всей остальной функциональностью, поэтому забудьте об этом.Я просто использую Date, это будет делать и сохраняет функциональность в порядке.Обратите внимание на размер POSIXlt: все функции для извлечения месяцев, недель, дня года и т. Д. Работают в этом формате.Это верно для format() и для функций weekdays(), months(), ... в пакете base или chron.

Некоторые замечания:

  • В Vista теоретический предел 3Gb на машине с 4Gb.Вы пробовали memory.limit(3000): См. ?memory.limit.
  • Пожалуйста, обновите как можно скорее до Windows 7, если вам что-нибудь нужно для достойного управления памятью.Виста просто отстой.Долгое время.Если возможно, получите 64-битный компьютер, если вы работаете с этими данными.
  • Периодически очищайте рабочее пространство .Я могу без проблем запустить ваш код на 32-битной R с ограничением памяти 2 Гб на приведенном ниже кадре данных (14+ миллионов строк).

На ваш код.Я работаю с форматом Date, размер которого примерно равен числовому формату.Давайте попробуем это со следующими данными (которые вы могли бы предоставить ...) с 14,6 миллионами строк.Я использую Windows7 (64-битную) с общей памятью 4 ГБ.

n <- 40000
tr <- data.frame(
  customer_id = rep(1:366,n),
  visit_date = rep(seq.Date(as.Date("2010-04-01"),
                     as.Date("2011-04-01"),by='day'),each=n),
  visit_spend =runif(366*n,3,12)
 )

Сначала ваша недельная функция.Как уже говорилось, функция format использует базовый формат POSIXlt, который, как показано, требует большого объема памяти.Тем не менее, вы можете вырезать около половины памяти, просто обращаясь к ней напрямую (см. ?POSIXlt).Он возвращает целые числа, которые занимают примерно половину памяти возвращаемых вами чисел:

dayofweek2 <- function(dt) {
  as.POSIXlt(dt)$wday
}

monthofyear2 <- function(dt){
  as.POSIXlt(dt)$mon +1L
}

weekofyear2 <- function(dt) {
  n <- length(dt)
  lt <- as.POSIXlt(dt)
  yearstart <- c(0,which(diff(lt$year)!=0))
  fday <- rep(lt[yearstart+1L]$wday,
              diff(c(yearstart,n)+1L)
          )
  as.integer((lt$yday+fday-1L)%/%7)
}

> lsos()
                   Type    Size PrettySize  Rows Columns
tr           data.frame  733128   715.9 Kb 36600       3
Dates              Date  293048   286.2 Kb 36600      NA
x1              numeric  292840     286 Kb 36600      NA # from your function
x2              integer  146440     143 Kb 36600      NA # from my function

Если вам нужно еще меньше, вам придется делать математику самостоятельно.Но я советую вам не испытывать это и определенно не основываться на представлении персонажа.Строковые операции, такие как strsplit() и substr(), наверняка разрушат вашу память.Как и функция month.day.year() пакета chron. Держитесь подальше от chron с большими данными.Фактически, независимо от огромного пространства, необходимого объектам POSIXlt, использование POSIXlt по-прежнему является лучшим вариантом для извлечения памяти.

На aggregate.Это предназначено для фреймов данных, и, следовательно, aggregate вызов снова делает много копий данных.Делая вызов более вручную, можно снова сохранить копии.Предложение для функции:

my.agg <- function(x,d,AGGFUN=NULL,FUN="sum"){
  FUN <- match.fun(FUN)
  if(!is.null(AGGFUN)) {
    AGGFUN <- match.fun(AGGFUN)
    d <- AGGFUN(d)
  }
  ud <- sort(unique(d))

  xout <- sapply(ud,function(i){
    id <- which(d==i)  # find which values respond to a certain value of d
    FUN(x[id])         # apply the function to only those values
  },USE.NAMES=FALSE)

  data.frame(d=ud,x=xout)    
}

Теперь, если мы применим это и посмотрим использование памяти:

gc(TRUE,reset=TRUE)
x1 <-  aggregate(tr$visit_spend, 
           list('weekofyear'=weekofyear(tr$visit_date)), FUN="sum")
rm(x1)
gc(TRUE,reset=TRUE)
Sys.sleep(5)
x2 <- my.agg(tr$visit_spend,tr$visit_date,weekofyear2,sum)    
gc(TRUE,reset=TRUE)

, я получу следующий результат:

enter image description here

Красный квадрат - ваш совокупный вызов, желтый квадрат - мое предложение.Первым ударом в использовании памяти вашего совокупного вызова является функция weekofyear, которую вы используете.Мое предложение экономит как на использовании памяти weekofyear, так и на вызове aggregate, и работает также немного быстрее.Я никогда не получал более 2,6 ГБ общей памяти, используя мое предложение.

Надеюсь, это поможет.

...