Подтвердить что ты не робот

Эффективные способы перепрофилирования огромных данных из длинного в широкий формат - похожие на dcast

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

Начиная с простого примера,

> z <- data.table(col1=c(1,1,2,3,4), col2=c(10,10,20,20,30), 
                  col3=c(5,2,2.3,2.4,100), col4=c("a","a","b","c","a"))

> z
     col1 col2  col3 col4
1:    1   10   5.0    a      # col1 = 1, col2 = 10
2:    1   10   2.0    a      # col1 = 1, col2 = 10
3:    2   20   2.3    b
4:    3   20   2.4    c
5:    4   30 100.0    a

Нам нужно создать "широкую" таблицу, которая будет иметь значения столбца col4 как имена столбцов и значение суммы (col3) для каждой комбинации col1 и col2.

> ulist = unique(z$col4) # These will be the additional column names

# Create long table with sum
> z2 <- z[,list(sumcol=sum(col3)), by='col1,col2,col4']

# Pivot the long table
> z2 <- z2[,as.list((sumcol[match(ulist,col4)])), by=c("col1","col2")]

# Add column names
> setnames(z2[],c("col1","col2",ulist))

> z2
   col1 col2   a   b   c
1:    1   10   7  NA  NA  # a = 5.0 + 2.0 = 7 corresponding to col1=1, col2=10
2:    2   20  NA 2.3  NA
3:    3   20  NA  NA 2.4
4:    4   30 100  NA  NA

Проблема заключается в том, что, несмотря на то, что вышеописанный метод подходит для небольших таблиц, практически невозможно их запустить (если только вы не можете нормально ждать в течение часа) на очень больших таблицах.

Это, скорее всего, связано с тем фактом, что таблица с поворотным/большим размером намного больше, чем исходные таблицы, поскольку каждая строка в широкой таблице имеет n столбцов, соответствующих уникальным значениям столбца поворота, независимо от того, есть ли какое-либо значение, соответствующее этой ячейке (это значения NA выше). Поэтому размер новой таблицы часто равен 2x + по сравнению с исходной "длинной" таблицей.

Моя исходная таблица имеет ~ 500 миллионов строк, около 20 уникальных значений. Я попытался запустить вышеупомянутое, используя только 5 миллионов строк, и он занимает навсегда в R (слишком долго ждать его завершения).

Для целей бенчмаркинга пример (с использованием 5 миллионов строк) завершается примерно через 1 минуту, используя производственные системы rdbms, выполняющие многопоточность. Он заканчивается примерно через 8 секунд с использованием одноядерного ядра с использованием KDB +/Q (http://www.kx.com). Это может быть не справедливое сравнение, но дает представление о том, что эти операции можно выполнять намного быстрее, используя альтернативные способы. KDB + не имеет разреженных строк, поэтому он выделяет память для всех ячеек и все еще намного быстрее, чем все, что я пробовал.

Что мне нужно, это R-решение:), и до сих пор я не нашел эффективного способа выполнения подобных операций.

Если у вас был опыт и я мог подумать о любом альтернативном/более оптимальном решении, мне было бы интересно узнать то же самое. Ниже приведен пример кода. Вы можете изменить значение n для имитации результатов. Уникальные значения для сводной колонки (столбец c3) были исправлены на уровне 25.

n = 100 # Increase this to benchmark

z <- data.table(c1=sample(1:10000,n,replace=T),
    c2=sample(1:100000,n,replace=T),
    c3=sample(1:25,n,replace=T),
    price=runif(n)*10)

c3.unique <- 1:25

z <- z[,list(sumprice=sum(price)), by='c1,c2,c3'][,as.list((sumprice[match(c3.unique,c3)])), by='c1,c2']
setnames(z[], c("c1","c2",c3.unique))

Спасибо,

  • Радж.
4b9b3361

Ответ 1

Для n=1e6 следующее занимает около 10 секунд с простым dcast и около 4 секунд с dcast.data.table:

library(reshape2)

dcast(z[, sum(price), by = list(c1, c2, c3)], c1 + c2 ~ c3)

# or with 1.8.11
dcast.data.table(z, c1 + c2 ~ c3, fun = sum)