web-dev-qa-db-ja.com

Python

一部のデータ分析にPythonを使用しています。2つのテーブルがあります。最初のテーブル(「A」と呼びましょう)には1,000万行と10列があり、2番目のテーブル(「B」)には73があります。百万行と2列。共通のIDを持つ1列があり、その列に基づいて2つのテーブルを交差させたい。特にテーブルの内部結合が必要。

パンダで通常のマージ関数を使用するために、テーブルBをa pandasデータフレームとしてメモリにロードできませんでした。チャンクでテーブルBのファイルを読み取り、各チャンクをAと交差させてみました。これらの交差(内部結合からの出力)を連結します。これは速度では問題ありませんが、時々問題が発生し、セグメンテーションエラーが発生します...それほど大きくありません。このエラーは再現が困難ですが、2つの異なる場合に発生します。マシン(Mac OS X v10.6(Snow Leopard)およびUNIX、Red Hat Linux)。

最後に、PandasとPyTablesの組み合わせで、テーブルBをディスクに書き込んでから、テーブルAを繰り返し処理し、テーブルBから一致する行を選択してみました。この最後のオプションは機能しますが、時間がかかります。 pytablesのBは、デフォルトですでにインデックス付けされています。

この問題にどのように取り組むのですか?

13
user2027051

これは少し疑似コディッシュですが、かなり速いはずだと思います。

ディスク上のすべてのテーブルを使用した、単純なディスクベースのマージ。重要なのは、選択自体を行うのではなく、開始/停止を介してテーブルにインデックスを付けるだけであるということです。これは非常に高速です。

(AのIDを使用して)Bの条件を満たす行を選択するのはそれほど速くありません。これは、データをカーネル内検索ではなくPythonスペースに取り込む可能性があるためです(よくわかりませんが、pytables.orgのカーネル内最適化セクションでさらに調査することをお勧めします。カーネル内になるかどうかを判断する方法があります)。

また、あなたがそれに取り組んでいる場合、これは非常に並行した問題です(複数のプロセスから同じファイルに結果を書き込まないでください。pytablesはそのために書き込み安全ではありません)。

結合操作の実行が実際に「内部」結合になる方法についてのコメントについては、 この回答 を参照してください。

Merge_a_b操作には、非常に効率的な標準のpandas結合を使用できると思います(メモリ内の場合)。

もう1つのオプション(「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)
16
Jeff