web-dev-qa-db-ja.com

Pandas DataFramesでの行追加パフォーマンスの向上

ネストされた辞書をループし、各レコードからデータを取得し、Pandas DataFrameに追加します。データは次のようになります。

data = {"SomeCity": {"Date1": {record1, record2, record3, ...}, "Date2": {}, ...}, ...}

合計で数百万のレコードがあります。スクリプト自体は次のようになります。

city = ["SomeCity"]
df = DataFrame({}, columns=['Date', 'HouseID', 'Price'])
for city in cities:
    for dateRun in data[city]:
        for record in data[city][dateRun]:
            recSeries = Series([record['Timestamp'], 
                                record['Id'], 
                                record['Price']],
                                index = ['Date', 'HouseID', 'Price'])
            FredDF = FredDF.append(recSeries, ignore_index=True)

ただし、これはひどく遅く実行されます。それを並列化する方法を探す前に、Pandasはまだかなり新しいので、これをそのまま高速に実行できる明白なものを見逃していないことを確認したいだけです。

23
Brideau

また、データフレームのappend関数をループ内で使用し、その実行速度に困惑しました。

このページの正しい答えに基づいて、苦しんでいる人のための有用な例。

Pythonバージョン:3

パンダのバージョン:0.20.3

# the dictionary to pass to panda's dataframe
dict = {}

# a counter to use to add entries to "dict"
i = 0 

# Example data to loop and append to a dataframe
data = [{"foo": "foo_val_1", "bar": "bar_val_1"}, 
       {"foo": "foo_val_2", "bar": "bar_val_2"}]

# the loop
for entry in data:

    # add a dictionary entry to the final dictionary
    dict[i] = {"col_1_title": entry['foo'], "col_2_title": entry['bar']}

    # increment the counter
    i = i + 1

# create the dataframe using 'from_dict'
# important to set the 'orient' parameter to "index" to make the keys as rows
df = DataFrame.from_dict(dict, "index")

「from_dict」関数: https://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.from_dict.html

20
P-S

BrenBarnの提案を使用して、元の辞書を、from_dictの予想される構造を利用するために適切にフォーマットされた新しい辞書に再編成しました。辞書の再編成は非常に迅速に行われ、それからこの新しい辞書でfrom_dictを呼び出すだけでした。

最初の1時間ほどではなく、データの読み込みから書き込みまで、約12秒ですべてが実行されました。ずっといい!

13
Brideau

DataFrameに何度も追加しなければならないという同様の問題に遭遇しましたが、追加の前に値がわかりませんでした。私は、内部でblists()であるデータ構造のような軽量のDataFrameを作成しました。私はそれを使用してすべてのデータを蓄積し、それが完了したら出力をPandas DataFrameに変換します。これが私のプロジェクトへのリンクです。すべてのオープンソースなので、他の人に役立つことを願っています。

https://pypi.python.org/pypi/raccoon

3
Ryan Sheftel

そのための最良の方法は、受信するデータがわかっている場合は、事前に割り当てることだと思います。

import numpy as np
import pandas as pd

random_matrix = np.random.randn(100, 100)
insert_df = pd.DataFrame(random_matrix)

df = pd.DataFrame(columns=range(100), index=range(200))
df.loc[range(100), df.columns] = random_matrix
df.loc[range(100, 200), df.columns] = random_matrix

これは私が最も理解できるパターンだと思います。 appendは、データフレームが非常に小さい場合は高速になりますが、スケーリングされません。

In [1]: import numpy as np; import pandas as pd

In [2]: random_matrix = np.random.randn(100, 100)
   ...: insert_df = pd.DataFrame(random_matrix)
   ...: df = pd.DataFrame(np.random.randn(100, 100))

In [2]: %timeit df.append(insert_df)
272 µs ± 2.36 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

In [3]: %timeit df.loc[range(100), df.columns] = random_matrix
493 µs ± 4.25 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

In [4]: %timeit df.loc[range(100), df.columns] = insert_df
821 µs ± 8.68 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

これを100,000行のデータフレームで実行すると、より劇的な結果が得られます。

In [1]: df = pd.DataFrame(np.random.randn(100_000, 100))

In [2]: %timeit df.append(insert_df)
17.9 ms ± 253 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [3]: %timeit df.loc[range(100), df.columns] = random_matrix
465 µs ± 13.7 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

In [4]: %timeit df.loc[range(99_900, 100_000), df.columns] = random_matrix
465 µs ± 5.75 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

In [5]: %timeit df.loc[range(99_900, 100_000), df.columns] = insert_df
1.02 ms ± 3.42 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

したがって、追加はデータフレームを使用した挿入よりも約17倍遅く、numpy配列を使用した挿入よりも35倍遅いことがわかります。

3
Rob

リストへの行の追加は、DataFrameよりもはるかに効率的です。したがって、あなたはしたいでしょう

  1. 行をリストに追加します。
  2. 次に、それをDataFrameに変換し、
  3. 必要に応じてインデックスを設定します。
1