Я хочу понять, почему lapply исчерпывает память, но цикл for не - PullRequest
0 голосов
/ 29 марта 2019

Я работаю в R и пытаюсь понять, как лучше объединить кадры данных, когда один из них очень большой.

У меня есть фрейм данных, который не является мучительно большим, но и не маленьким (~ 80K наблюдений 8 переменных, 144 МБ). Мне нужно сопоставить наблюдения из этого фрейма данных с наблюдениями из другого меньшего фрейма данных на основе диапазона дат. Конкретно у меня есть:

events.df <- data.frame(individual=c('A','B','C','A','B','C'),
                     event=c(1,1,1,2,2,2),
                     time=as.POSIXct(c('2014-01-01 08:00:00','2014-01-05 13:00:00','2014-01-10 07:00:00','2014-05-01 01:00:00','2014-06-01 12:00:00','2014-08-01 10:00:00'),format="%Y-%m-%d %H:%M:%S"))

trips.df <- data.frame(individual=c('A','B','C'),trip=c('x1A','CA1B','XX78'),
                    trip_start = as.POSIXct(c('2014-01-01 06:00:00','2014-01-04 03:00:00','2014-01-08 12:00:00'),format="%Y-%m-%d %H:%M:%S"),
                    trip_end=as.POSIXct(c('2014-01-03 06:00:00','2014-01-06 03:00:00','2014-01-11 12:00:00'),format="%Y-%m-%d %H:%M:%S"))   

В моем случае events.df содержит около 80 000 уникальных событий, и я хочу сопоставить их с событиями из фрейма данных trips.df , который содержит около 200 уникальных поездок. Каждая поездка имеет уникальный идентификатор поездки («поездка»). Я хотел бы провести сопоставление в зависимости от того, имело ли место событие в диапазоне дат, определяющем поездку.

Сначала я попробовал fuzzy_inner_join из библиотеки fuzzyjoin . Это прекрасно работает в принципе:

fuzzy_inner_join(events.df,trips.df,by=c('individual'='individual','time'='trip_start','time'='trip_end'),match_fun=list(`==`,`>=`,`<=`))
   individual.x event                time individual.y trip          trip_start            trip_end
1            A     1 2014-01-01 08:00:00            A  x1A 2014-01-01 06:00:00 2014-01-03 06:00:00
2            B     1 2014-01-05 13:00:00            B CA1B 2014-01-04 03:00:00 2014-01-06 03:00:00
3            C     1 2014-01-10 07:00:00            C XX78 2014-01-08 12:00:00 2014-01-11 12:00:00
> 

но не хватает памяти, когда я пытаюсь применить его к большим кадрам данных.

Вот второе решение, которое я собрал вместе:

trip.match <- function(tripid){
   individual <- trips.df$individual[trips$trip==tripid]
   start <- trips.df$trip_start[trips$trip==tripid]
   end <- trips.df$trip_end[trips$trip==tripid]

tmp <- events.df[events.df$individual==individual &
                 events.df$time>= start &
                 events.df$time<= end,]
tmp$trip <- tripid
return(tmp) 
}

result <- data.frame(rbindlist(lapply(unique(trips.df$trip),trip.match)

Это решение также выходит из строя, поскольку объект списка, возвращаемый lapply, равен 25 ГБ, а попытка привести этот список к фрейму данных также исчерпывает доступную память.

Я смог сделать то, что мне нужно, используя цикл for. По сути, я добавляю столбец к events.df , перебираю уникальные идентификаторы поездок и заполняю новый столбец в events.df соответственно:

events.df$trip <- NA
for(i in unique(trips.df$trip)){
  individual <- trips.df$individual[trips.df$trip==i]
  start <- min(trips.df$trip_start[trips.df$trip==i])
  end <- max(trips.df$trip_end[trips.df$trip==i])  

  events.df$trip[events.df$individual==individual & events.df$time >= start & events.df$time <= end] <- i
}

> events.df
  individual event                time trip
1          A     1 2014-01-01 08:00:00  x1A
2          B     1 2014-01-05 13:00:00 CA1B
3          C     1 2014-01-10 07:00:00 XX78
4          A     2 2014-05-01 01:00:00 <NA>
5          B     2 2014-06-01 12:00:00 <NA>
6          C     2 2014-08-01 10:00:00 <NA>

У меня такой вопрос: я не очень продвинутый программист на R, поэтому я ожидаю, что есть более эффективный способ памяти для выполнения того, что я пытаюсь сделать. Есть ли?

Ответы [ 2 ]

2 голосов
/ 29 марта 2019

Попробуйте создать таблицу, которая расширяет диапазоны поездок на час, а затем объединяется с событием.Вот пример (использование функции data.table, потому что data.table превосходит data.frame для больших наборов данных):

library('data.table')
tripsV <- unique(trips.df$trip)
tripExpand <- function(t){
  dateV <- seq(trips.df$trip_start[trips.df$trip == t], 
               trips.df$trip_end[trips.df$trip == t], 
               by = 'hour')
  data.table(trip = t, time = dateV)
}

trips.dt <- rbindlist(
  lapply(tripsV, function(t) tripExpand(t))
  )

merge(events.df,
      trips.dt,
      by = 'time')

Вывод:

                 time individual event trip
1 2014-01-01 08:00:00          A     1  x1A
2 2014-01-05 13:00:00          B     1 CA1B
3 2014-01-10 07:00:00          C     1 XX78

Таким образом, вы в основном переводитетаблица поездок к набору длинных панелей данных за час.Это облегчает объединение с набором данных событий.Я не сравнивал его с вашим текущим методом, но я догадываюсь, что он будет более эффективным с точки зрения памяти и процессора.

1 голос
/ 29 марта 2019

Рассмотрите возможность разделения ваших данных на split data.table и запустите каждое подмножество на fuzzy_inner_join, затем вызовите rbindlist, чтобы связать все элементы фрейма данных вместе для одного вывода.

df_list <- data.table::split(events.df, by="individual")

fuzzy_list <- lapply(df_list, function(sub.df) {
      fuzzy_inner_join(sub.df, trips.df, 
                       by = c('individual'='individual', 'time'='trip_start', 'time'='trip_end'), 
                       match_fun = list(`==`,`>=`,`<=`)
      )
})

# REMOVE TEMP OBJECT AND CALL GARBAGE COLLECTOR
rm(df_list); gc()

final_df <- rbindlist(fuzzy_list)

# REMOVE TEMP OBJECT AND CALL GARBAGE COLLECTOR
rm(fuzzy_list); gc()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...