1. ホーム
  2. python

[解決済み] pandasを使った "大量データ "ワークフロー【終了しました

2022-03-18 01:57:57

質問

私は、pandasを学びながら、この質問に対する答えを何ヶ月も考え続けてきました。 私は日々の仕事にSASを使っていますが、SASはアウトオブコアのサポートで素晴らしいものです。 しかし、SASは他の多くの理由からソフトウェアとしてはひどいものです。

いつかSASの使用をpythonとpandasに置き換えたいと考えていますが、現在は大規模なデータセットのためのアウトオブコアのワークフローが不足しています。 分散型ネットワークを必要とするようなビッグデータではなく、メモリに収まらないほど大きなファイルでも、ハードディスクに収まるほど小さなファイルでもいいんです。

私が最初に考えたのは HDFStore を使い、大きなデータセットをディスクに保存し、必要な部分のみをデータフレームに取り込んで分析することができます。 他の人はMongoDBをより使いやすい選択肢として挙げています。 私の質問はこれです。

以下のことを実現するためのベストプラクティスのワークフローは何か。

  1. フラットファイルを恒久的なディスク上のデータベース構造にロードする。
  2. データベースを照会して、pandasのデータ構造に投入するデータを取得する。
  3. pandasでピースを操作した後、データベースを更新する

特に、quot;large data"でpandasを使用している方からの実例をお待ちしています。

Edit -- どのように動作させたいかの例です。

  1. 大きなフラットファイルを繰り返しインポートし、永久的にディスク上のデータベース構造に格納する。 これらのファイルは、通常、メモリに収まらないほど大きい。
  2. Pandasを使うには、メモリに収まるこのデータのサブセット(通常は一度に数列だけ)を読みたいと思います。
  3. 選択した列に対して様々な操作を行い、新しい列を作りたいのですが。
  4. そして、これらの新しいカラムをデータベース構造に追加する必要があります。

これらのステップを実行するためのベストプラクティスの方法を見つけようとしています。pandasとpytablesについてのリンクを読むと、新しいカラムを追加することが問題になる可能性があるようです。

Edit -- Jeffの質問に具体的に回答しています。

  1. 私は消費者信用リスクモデルを構築しています。データの種類は、電話番号、SSN、住所の特徴、資産価値、犯罪歴や破産歴などの不利な情報などです。毎日使うデータセットには、平均して1,000~2,000のフィールドがあり、連続変数、名義変数、順序変数、数値データ、文字データなど、さまざまな種類のデータが混在しています。 行を追加することはほとんどありませんが、新しい列を作成するような操作はたくさん行っています。
  2. 典型的な操作としては、条件付きロジックを使って複数の列を組み合わせ、新しい複合列を作ることである。例えば if var1 > 2 then newvar = 'A' elif var2 = 4 then newvar = 'B' . これらの操作の結果、私のデータセットのすべてのレコードに対して新しいカラムが作成されます。
  3. 最後に、これらの新しいカラムをディスク上のデータ構造に追加したいと思います。 ステップ2を繰り返し、クロスタブや記述統計を使ってデータを調査し、興味深い直感的な関係を見つけ、モデル化したいと思います。
  4. 一般的なプロジェクトファイルは、通常1GB程度です。 ファイルは、1行が消費者データのレコードで構成されるような形で整理されています。 各行には、すべてのレコードについて同じ数の列があります。 これは常に同じである。
  5. 新しいカラムを作成する際に、行でサブセットすることはかなり稀です。 しかし、レポートを作成したり、記述統計を生成したりする際には、行でサブセットすることがよくあります。 たとえば、特定のビジネス分野(小売業のクレジットカードなど)について、簡単な頻度を作成することがあります。 この場合、レポート対象の列に加え、業種が小売業であるレコードのみを選択します。 ただし、新しい列を作成する場合は、すべての行のデータを取り出し、業務に必要な列のみを作成します。
  6. モデリングプロセスでは、すべての列を分析し、何らかの結果変数との興味深い関係を探し、それらの関係を記述する新しい複合列を作成する必要があります。 私が探索する列は、通常、小さなセットで行われます。 例えば、資産価値を扱う20の列のセットに焦点を当て、それらがローンの不履行とどのように関連しているかを観察することになります。 これらの列を調査し、新しい列を作成したら、別の列のグループ、例えば大学の学歴に移動し、このプロセスを繰り返します。 私が行っているのは、データと結果の関係を説明する変数の候補を作成することです。 このプロセスの最後に、学習技術を適用して、これらの複合列から方程式を作成するのです。

データセットに行を追加することはめったにありません。 ほぼ常に新しい列(統計・機械学習用語でいうところの変数や特徴)を作成することになります。

どのように解決するのか?

私は日常的に数十ギガバイトのデータをこのような方法で使用しています。 例えば、ディスク上にテーブルがあり、クエリで読み込み、データを作成し、再び追加します。

一読の価値あり ドキュメント このスレッドの後半に には、データを保存する方法に関するいくつかの提案があります。

など、データの保存方法に影響を与えるような内容。
できる限り詳しく教えてください。

  1. データの大きさ、行数、列数、列の種類。 行、または列だけですか?
  2. 典型的な操作はどのようなものでしょうか。例えば、行の束と特定の列を選択するために列のクエリを実行し、次に操作(インメモリ)を実行し、新しい列を作成し、これらを保存します。
    (おもちゃのような例を挙げていただければ、より具体的なご提案ができるかと思います)
  3. その処理の後、次に何をするのですか?ステップ2はアドホックなのか、それとも繰り返し可能なのか?
  4. 入力フラットファイル:数、大まかな合計サイズ(Gb)。これらはレコードごとにどのように整理されていますか?各ファイルは異なるフィールドを含むのか、それとも各ファイルの全フィールドを含むいくつかのレコードを持つのか?
  5. 条件に基づいて行(レコード)のサブセットを選択することはありますか(例:フィールドA > 5を持つ行を選択する)? そして何かをするのか、それともすべてのレコードでフィールドA、B、Cを選択するだけ(そして何かをする)のか?
  6. すべての列を(グループで)「作業」していますか?それとも、レポート用にのみ使用する列がかなりありますか(例:データは残しておきたいが、最終結果までその列を明確に引き出す必要はない)?

解決方法

があることを確認してください。 パンダは少なくとも 0.10.1 をインストールした。

読む ファイルのチャンク単位での繰り返し 複数テーブルクエリ .

pytablesは行単位で操作するように最適化されているので(これはクエリを実行するものです)、フィールドのグループごとにテーブルを作成することにします。こうすることで、小さなフィールドのグループを選択することが簡単になります(大きなテーブルでも動作しますが、この方法の方が効率的です...)。この制限は将来的に修正できるかもしれません...とにかくこの方が直感的です)。
(以下は疑似コードです)

import numpy as np
import pandas as pd

# create a store
store = pd.HDFStore('mystore.h5')

# this is the key to your storage:
#    this maps your fields to a specific group, and defines 
#    what you want to have as data_columns.
#    you might want to create a nice class wrapping this
#    (as you will want to have this map and its inversion)  
group_map = dict(
    A = dict(fields = ['field_1','field_2',.....], dc = ['field_1',....,'field_5']),
    B = dict(fields = ['field_10',......        ], dc = ['field_10']),
    .....
    REPORTING_ONLY = dict(fields = ['field_1000','field_1001',...], dc = []),

)

group_map_inverted = dict()
for g, v in group_map.items():
    group_map_inverted.update(dict([ (f,g) for f in v['fields'] ]))

ファイルを読み込んでストレージを作成する(基本的に append_to_multiple が行う)。

for f in files:
   # read in the file, additional options may be necessary here
   # the chunksize is not strictly necessary, you may be able to slurp each 
   # file into memory in which case just eliminate this part of the loop 
   # (you can also change chunksize if necessary)
   for chunk in pd.read_table(f, chunksize=50000):
       # we are going to append to each table by group
       # we are not going to create indexes at this time
       # but we *ARE* going to create (some) data_columns

       # figure out the field groupings
       for g, v in group_map.items():
             # create the frame for this group
             frame = chunk.reindex(columns = v['fields'], copy = False)    

             # append it
             store.append(g, frame, index=False, data_columns = v['dc'])

これで、すべてのテーブルがファイルに格納されました (実際には、必要なら別々のファイルに格納することもできます。group_map にファイル名を追加する必要がありますが、おそらくこれは必要ないでしょう)。

このようにして、カラムを取得したり、新しいカラムを作成したりします。

frame = store.select(group_that_I_want)
# you can optionally specify:
# columns = a list of the columns IN THAT GROUP (if you wanted to
#     select only say 3 out of the 20 columns in this sub-table)
# and a where clause if you want a subset of the rows

# do calculations on this frame
new_frame = cool_function_on_frame(frame)

# to 'add columns', create a new group (you probably want to
# limit the columns in this new_group to be only NEW ones
# (e.g. so you don't overlap from the other tables)
# add this info to the group_map
store.append(new_group, new_frame.reindex(columns = new_columns_created, copy = False), data_columns = new_columns_created)

post_processingの準備ができたら。

# This may be a bit tricky; and depends what you are actually doing.
# I may need to modify this function to be a bit more general:
report_data = store.select_as_multiple([groups_1,groups_2,.....], where =['field_1>0', 'field_1000=foo'], selector = group_1)

data_columnsについてですが、実際には ANY data_columns は、カラムに基づいた行のサブ選択を可能にします。例えば、次のようなものです。

store.select(group, where = ['field_1000=foo', 'field_1001>0'])

最終的なレポート作成段階で最も興味を引くかもしれません(基本的にデータ列は他の列から分離されるので、たくさん定義する場合は多少効率に影響があるかもしれません)。

もいいかもしれません。

  • フィールドのリストを受け取り、groups_map でグループを検索し、それらを選択して結果を連結し、結果のフレームを取得する関数を作成します(これは本質的に select_as_multiple が行うことです)。 こうすれば、かなり透過的な構造になりますね。
  • 特定のデータ列のインデックスを作成します(行のサブセットをより高速にします)。
  • 圧縮を有効にする。

質問があったら教えてください