2016-11-02 2 views
3

Я хотел бы знать, сколько раз каждая переменная изменяется внутри каждой группы, а затем добавляет результат для всех групп.данные.table. Быстрый способ подсчета количества изменений в каждом столбце

Я нашел способ:

mi[,lapply(.SD, function(x) sum(x != shift(x), 
    na.rm=T)), by = ID][,-1][,lapply(.SD,sum, na.rm=T)] 

Он работает, она производит правильный результат, но это очень медленно в моей большой DataTable. Я хотел бы сделать обе операции внутри одного и того же lapply (или чего-то более быстрого и компактного), но первый из них выполняется группой, второй - нет.

Это может быть написано в более простом способе (возможно не всегда)

mi[,lapply(.SD, function(x) sum(x != shift(x), 
    na.rm=T))] [,-1]-mi[,length(unique(ID))]+1 

Но это по-прежнему медленно и требует много памяти.

Любая другая идея?

Я также попытался использовать diff вместо смены, но это становится сложнее.

Здесь у вас есть фиктивный пример:

mi <- data.table(ID=rep(1:3,each=4) , year=rep(1:4, times=3), 
    VREP=rep(1:3,each=4) , VDI=rep(1:4, times=3), RAN=sample(12)) 
mi <- rbind(mi, data.table(4,1,1,1,0), use.names=F) 

Большой пример для теста

mi <- as.data.table(matrix(sample(0:100,10000000, 
replace=T), nrow=100000, ncol=100)) 
mi[,ID := rep(1:1000,each=100)] 

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

+1

Если я что-то не хватает, заказав на «ID», вы могли бы посчитать, сколько раз изменения в значениях идет вместе с изменением id; 'id = mi $ ID [-1] == mi $ ID [-nrow (mi)]; sapply (mi, function (x) sum ((x [-1]! = x [-length (x)]) & id)) ' –

+0

Мне жаль, что ваш метод в два раза медленнее моего первого метода и в четыре раза медленнее второго. – skan

+0

Бенчмарк и достаточно большой пример (написанный как функция 'n'), необходимый ... – Frank

ответ

2

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

setorder(mi, ID) 
setDT(Map(`!=`, mi, shift(mi)))[, 
    lapply(lapply(.SD, `&`, !ID), sum, na.rm = T), .SDcols = -"ID"] 
# year VREP VDI RAN 
#1: 9 0 9 9 

На вашем большом образце:

setorder(mi, ID) 
microbenchmark(method1(), alexis_laz(), eddi(), times = 5) 
#Unit: milliseconds 
#   expr  min  lq  mean median  uq  max neval 
# method1() 7336.1830 7510.9543 7932.0476 8150.3197 8207.2181 8455.563  5 
# alexis_laz() 1350.0338 1492.3793 1509.0790 1492.5426 1577.3318 1633.107  5 
#  eddi() 400.3999 475.6908 494.5805 504.6163 524.2077 567.988  5 
+0

Не могли бы вы объяснить Карту, пожалуйста? – skan

+1

он в основном делает то же самое, что и 'mi! = Shift (mi)', за исключением того, что сохраняет структуру списка вместо преобразования в матрицу, которая экономит много времени. – eddi

+0

Я пробовал это, и моя база данных в 60 раз быстрее. – skan

Смежные вопросы