2015-06-12 12 views
3

У меня есть список из 40 фреймов данных размером около 250 тыс. Строк, и я хочу добавить новую переменную в каждый файл данных. Эта новая переменная period вычисляется из другой переменной, содержащей объекты Date, преобразование очень просто, если годовая часть даты меньше, чем в период 2015 года, устанавливается на «новое» еще «старое».Почему R (в моем примере) очень медленный для обработки дат/времени?

Я думал, что вычисление будет очень быстрым с использованием векторизации, но для завершения потребуется около 41 секунды! (Используйте цикл for или lapply, дающий те же самые показатели).

Возпроизводимо пример:

datas.d <- function(nDf, nRow) { 
    lapply(seq_len(nDf), function(x) { 
    data.frame(
     id1 = sample(7e8:9e8, nRow), 
     id2 = sample(1e9, nRow), 
     id3 = sample(1e9, nRow), 
     date = sample(seq(as.Date("2012-01-01"), Sys.Date(), by = 1), nRow, rep = TRUE), 
     code1 = sample(10, nRow, rep = TRUE), 
     code2 = sample(10, nRow, rep = TRUE), 
     code3 = sample(10, nRow, rep = TRUE) 
    ) 
    }) 
} 

datasDate <- datas.d(40, 25e4) 

forLoopDate <- function(datas) { 
    for (i in seq_along(datas)) { 
    datas[[i]]$period <- rep("old", nrow(datas[[i]])) 
    datas[[i]]$period[format(datas[[i]]$date, "%Y") == "2015"] <- "new" 
    } 
    return(datas) 
} 

> system.time(forLoopDate(datasDate)) 
utilisateur  système  écoulé 
     41.46  0.31  41.84 

я уже испытал медленные выступления, когда я принужден строк в даты в 800K строк dataframe поэтому я подозревал дату манипуляцию виновности для плохих выступлений. R Profiler подтвердил:

Rprof(tmp <- tempfile()) 
datas <- forLoopDate(datasDate) 
Rprof(NULL) 
summaryRprof(tmp) 
$by.self 
        self.time self.pct total.time total.pct 
"format.POSIXlt"  39.34 94.16  39.34  94.16 
"as.POSIXlt.Date"  1.80  4.31  1.80  4.31 
"=="     0.36  0.86  0.36  0.86 
"forLoopDate"   0.22  0.53  41.78 100.00 
"format.Date"   0.06  0.14  41.20  98.61 

Так что я попробовал то же преобразование skiping с датой форматирования, то есть непосредственно использовать строку в течение года. Усиление производительности недвусмысленно:

Я также тестирую его с помощью другой функции форматирования, year из пакета lubridate. Форматирование происходит очень быстро, я думаю, потому что он работает на уровне C?

datas.s <- function(nDf, nRow) { 
    lapply(seq_len(nDf), function(x) { 
    data.frame(
     id1 = sample(7e8:9e8, nRow), 
     id2 = sample(1e9, nRow), 
     id3 = sample(1e9, nRow), 
     date = sample(2012:2015, nRow, rep = TRUE), 
     code1 = sample(10, nRow, rep = TRUE), 
     code2 = sample(10, nRow, rep = TRUE), 
     code3 = sample(10, nRow, rep = TRUE) 
    ) 
    }) 
} 

datasString <- datas.s(40, 25e4) 

forLoopString <- function(datas) { 
    for (i in seq_along(datas)) { 
    datas[[i]]$period <- rep("old", nrow(datas[[i]])) 
    datas[[i]]$period[datas[[i]]$date == "2015"] <- "new" 
    } 
    return(datas) 
} 

library(lubridate) 
forLoopDate2 <- function(datas) { 
    for (i in seq_along(datas)) { 
    datas[[i]]$period <- rep("old", nrow(datas[[i]])) 
    datas[[i]]$period[year(datas[[i]]$date) == 2015] <- "new" 
    } 
    return(datas) 
} 

library(microbenchmark) 
mbm <- microbenchmark(
    date = datas <- forLoopDate(datasDate), 
    string = datas <- forLoopString(datasString), 
    lubridate = datas <- forLoopDate2(datasDate), 
    times = 10L) 

> mbm 
Unit: seconds 
expr  min  lq  mean median  uq  max neval 
date 41.502728 41.561497 41.649533 41.652306 41.69218 41.875110 10 
string 4.119266 4.131186 4.167809 4.166946 4.17993 4.239481 10 
lubridate 2.088281 2.105413 2.133042 2.111710 2.15794 2.250739 10 

И здесь возникает много вопросов!

_Почему форматирование/конвертирование Даты - это медленное с R?

_Can Я улучшаю производительность своего кода, используя Base R? Каковы наилучшие методы, с точки зрения производительности, при работе с датами/датами?

Спасибо!

ответ

5

A format Функция, которая может возвращать много разных форматов, можно ожидать, что она будет довольно медленной. Если вы счастливы с year функции lubridate, вы могли бы просто использовать его (очень простой) код:

as.POSIXlt(x, tz = tz(x))$year + 1900 

В общем, следует избегать переходов между любыми типами/классами и символами, когда производительность имеет значение. Это часто будет медленным. Лучше делать числовые вычисления (например, вы можете использовать целые числа, которые являются основой переменных Date, но это приводит к проблемам с високосными годами, поэтому лучше использовать POSIXlt, который позаботится об этом для вас).