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

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

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

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

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

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

4b9b3361

Ответ 1

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

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

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

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

См. этот ответ для комментария о том, как выполнение операции объединения фактически будет "внутренним" соединением.

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

Еще один вариант (в зависимости от того, как "большой" 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)