Объединение двух таблиц с миллионами строк в Python

Я использую Python для некоторого анализа данных. У меня есть две таблицы, первая (назовем ее «А») имеет 10 миллионов строк и 10 столбцов, а вторая («В») имеет 73 миллиона строк и 2 столбца. У них есть 1 столбец с общими идентификаторами, и я хочу пересечь две таблицы на основе этого столбца. В частности, мне нужно внутреннее соединение таблиц.

Я не мог загрузить таблицу B в память как фрейм данных pandas, чтобы использовать обычную функцию слияния для pandas. Я попытался прочитать файл таблицы B по фрагментам, пересекая каждый фрагмент с A и объединяя эти пересечения (выход из внутренних соединений). Это нормально на скорости, но время от времени это доставляет мне проблемы и выдает ошибку сегментации ... не так уж и здорово. Эту ошибку трудно воспроизвести, но она возникает на двух разных машинах (Mac OS X v10.6 (Snow Leopard) и UNIX, Red Hat Linux).

Наконец, я попытался использовать комбинацию Pandas и PyTables, записав таблицу B на диск, а затем перебрав таблицу A и выбрав из таблицы B соответствующие строки. Последний вариант работает, но медленно. Таблица B в pytables уже проиндексирована по умолчанию.

Как решить эту проблему?


person user2027051    schedule 30.01.2013    source источник
comment
почему вы не можете просто вычислить это в базе данных?   -  person eLRuLL    schedule 31.01.2013
comment
Я просто не очень хорошо знаком с базами данных и SQL. PyTables кажется довольно быстрым по сравнению с SQLite и другими. Я думал, что это будет хороший путь вперед. Я попробую с SQLlite и посмотрю, как пойдет.   -  person user2027051    schedule 01.02.2013


Ответы (1)


Это немного псевдокодово, но я думаю, должно быть довольно быстро.

Простое слияние на диске со всеми таблицами на диске. Суть в том, что вы не делаете выборку как таковую, а просто индексируете таблицу через start/stop, что довольно быстро.

Выбор строк, соответствующих критерию в B (с использованием идентификаторов A), не будет очень быстрым, потому что я думаю, что это может быть перенос данных в пространство Python, а не поиск в ядре (я не уверен, но вы можете захотеть чтобы узнать больше на pytables.org в разделе оптимизации в ядре.Есть способ определить, будет ли это в ядре или нет).

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

См. это answer для комментария о том, как выполнение операции соединения на самом деле будет «внутренним» соединением.

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

Еще один вариант (в зависимости от того, насколько «большой» A) может состоять в том, чтобы разделить A на 2 части (которые индексируются одинаково), используя меньший (возможно, использовать один столбец) в первой таблице; вместо сохранения результатов слияния как таковых сохраните индекс строки; позже можно вытащить нужные данные (вроде как с помощью индексатора и взять). См. http://pandas.pydata.org/pandas-docs/stable/io.html#multiple-table-queries

A = HDFStore('A.h5')
B = HDFStore('B.h5')

nrows_a = A.get_storer('df').nrows
nrows_b = B.get_storer('df').nrows
a_chunk_size = 1000000
b_chunk_size = 1000000

def merge_a_b(a,b):
    # Function that returns an operation on passed
    # frames, a and b.
    # It could be a merge, join, concat, or other operation that
    # results in a single frame.


for a in xrange(int(nrows_a / a_chunk_size) + 1):

    a_start_i = a * a_chunk_size
    a_stop_i  = min((a + 1) * a_chunk_size, nrows_a)

    a = A.select('df', start = a_start_i, stop = a_stop_i)

    for b in xrange(int(nrows_b / b_chunk_size) + 1):

        b_start_i = b * b_chunk_size
        b_stop_i = min((b + 1) * b_chunk_size, nrows_b)

        b = B.select('df', start = b_start_i, stop = b_stop_i)

        # This is your result store
        m = merge_a_b(a, b)

        if len(m):
            store.append('df_result', m)
person Jeff    schedule 31.01.2013
comment
Спасибо за помощь, Джефф. Я не могу использовать параметры запроса нескольких таблиц для pandas, потому что для таблицы A и B требуется одинаковое количество строк. - person user2027051; 01.02.2013
comment
Я избегаю использовать опцию join/concat/merge для pandas, так как все они время от времени выдают ошибку сегментации: 11. Я изо всех сил пытаюсь воспроизвести эту ошибку: а) ее нет в определенной строке любой таблицы и б) это происходит на другом компьютере/ОС. Это происходит не постоянно, но все же означает, что я не могу использовать этот маршрут. До сих пор я использую индексы для заполнения столбцов в A значениями из B. Что, вероятно, похоже на неэффективное соединение. Любые другие идеи приветствуются. - person user2027051; 01.02.2013
comment
запрос с несколькими таблицами предназначен только для вашей таблицы A и только в том случае, если вам нужно сказать, что у вас есть много столбцов, которые нужны только в конце вычисления. Вы пробовали с вышеуказанным алгоритмом? интересно посмотреть, какую производительность вы получите; также насколько велики файлы на диске? - person Jeff; 01.02.2013
comment
привет, в таблице A не так много столбцов, всего 8. Мне все еще нужно получить информацию из таблицы B, которая является узким местом. Файлы на диске составляют около 500 МБ для A и 1,5 ГБ для B. Я попробую ваш подход и посмотрю, насколько он быстр. - person user2027051; 01.02.2013
comment
Наконец-то я использовал решение, очень похожее на ваше. Таблицу А можно хранить в памяти. Таблицы A и B индексируются по интересующему столбцу. Я извлекаю из таблицы HDF5 B 1 миллион строк за раз и пересекаюсь. На самом деле это довольно быстро. Если бы таблицу A нельзя было безопасно загрузить в память, тогда ваш код был бы решением. Спасибо! - person user2027051; 14.02.2013
comment
@ user2027051 .. Я ищу это решение. Можете ли вы помочь мне поделиться любым примером кода. - person avinash; 15.08.2019