大きなデータフレーム(数百万行)があります。
Groupby操作を実行できるようにしたいのですが、個々の行の特定のプロパティを使用して行グループを決定するのではなく、任意の連続した(好ましくは同じサイズの)行のサブセットでグループ化します。
使用例:IPythonの並列マップを介して各行に関数を適用します。関数は一度に1行に基づいて結果を計算するため、どの行がどのバックエンドエンジンに行くかは関係ありません。 (少なくとも概念的には、実際にはベクトル化されています。)
私は次のようなものを思いつきました:
# Generate a number from 0-9 for each row, indicating which tenth of the DF it belongs to
max_idx = dataframe.index.max()
tenths = ((10 * dataframe.index) / (1 + max_idx)).astype(np.uint32)
# Use this value to perform a groupby, yielding 10 consecutive chunks
groups = [g[1] for g in dataframe.groupby(tenths)]
# Process chunks in parallel
results = dview.map_sync(my_function, groups)
しかし、これは非常に時間がかかりそうで、同じサイズのチャンクを保証するものではありません。特に、インデックスがスパースまたは非整数などである場合。
より良い方法のための提案はありますか?
ありがとう!
実際には、等しいサイズのチャンクを保証することはできません。結局、行の数は素数である可能性があります。その場合、唯一のチャンクオプションはサイズ1のチャンクまたは1つの大きなチャンクです。配列をgroupby
に渡す傾向があります。から始まる:
>>> df = pd.DataFrame(np.random.Rand(15, 5), index=[0]*15)
>>> df[0] = range(15)
>>> df
0 1 2 3 4
0 0 0.746300 0.346277 0.220362 0.172680
0 1 0.657324 0.687169 0.384196 0.214118
0 2 0.016062 0.858784 0.236364 0.963389
[...]
0 13 0.510273 0.051608 0.230402 0.756921
0 14 0.950544 0.576539 0.642602 0.907850
[15 rows x 5 columns]
インデックスを0に設定して意図的に情報量を少なくした場合、サイズ(ここでは10)を決定し、それによって配列を整数分割します。
>>> df.groupby(np.arange(len(df))//10)
<pandas.core.groupby.DataFrameGroupBy object at 0xb208492c>
>>> for k,g in df.groupby(np.arange(len(df))//10):
... print(k,g)
...
0 0 1 2 3 4
0 0 0.746300 0.346277 0.220362 0.172680
0 1 0.657324 0.687169 0.384196 0.214118
0 2 0.016062 0.858784 0.236364 0.963389
[...]
0 8 0.241049 0.246149 0.241935 0.563428
0 9 0.493819 0.918858 0.193236 0.266257
[10 rows x 5 columns]
1 0 1 2 3 4
0 10 0.037693 0.370789 0.369117 0.401041
0 11 0.721843 0.862295 0.671733 0.605006
[...]
0 14 0.950544 0.576539 0.642602 0.907850
[5 rows x 5 columns]
インデックスがそれと互換性がない場合、DataFrameのスライスに基づくメソッドは失敗する可能性がありますが、常に.iloc[a:b]
を使用してインデックス値を無視し、位置によってデータにアクセスできます。
Numpyを使用して、これを組み込みます:np.array_split()
import numpy as np
import pandas as pd
data = pd.DataFrame(np.random.Rand(10, 3))
for chunk in np.array_split(data, 5):
assert len(chunk) == len(data) / 5
これがまさにあなたが望むものかどうかはわかりませんが、これらのグルーパー関数は another SO thread でマルチプロセッサプールを実行するのにかなり便利です。
そのスレッドの短い例を次に示します。これは、あなたが望むようなことをするかもしれません:
import numpy as np
import pandas as pds
df = pds.DataFrame(np.random.Rand(14,4), columns=['a', 'b', 'c', 'd'])
def chunker(seq, size):
return (seq[pos:pos + size] for pos in xrange(0, len(seq), size))
for i in chunker(df,5):
print i
これはあなたに次のようなものを与えます:
a b c d
0 0.860574 0.059326 0.339192 0.786399
1 0.029196 0.395613 0.524240 0.380265
2 0.235759 0.164282 0.350042 0.877004
3 0.545394 0.881960 0.994079 0.721279
4 0.584504 0.648308 0.655147 0.511390
a b c d
5 0.276160 0.982803 0.451825 0.845363
6 0.728453 0.246870 0.515770 0.343479
7 0.971947 0.278430 0.006910 0.888512
8 0.044888 0.875791 0.842361 0.890675
9 0.200563 0.246080 0.333202 0.574488
a b c d
10 0.971125 0.106790 0.274001 0.960579
11 0.722224 0.575325 0.465267 0.258976
12 0.574039 0.258625 0.469209 0.886768
13 0.915423 0.713076 0.073338 0.622967
それがお役に立てば幸いです。
編集
この場合、(およそ)この方法で プロセッサのプール でこの関数を使用しました。
from multiprocessing import Pool
nprocs = 4
pool = Pool(nprocs)
for chunk in chunker(df, nprocs):
data = pool.map(myfunction, chunk)
data.domorestuff()
これは、IPythonの分散型機械を使用するのと非常に似ているはずですが、試していません。
良好な環境の兆候は多くの選択肢があるため、これを Anaconda Blaze から追加します。実際には Odo を使用します
import blaze as bz
import pandas as pd
df = pd.DataFrame({'col1':[1,2,3,4,5], 'col2':[2,4,6,8,10]})
for chunk in bz.odo(df, target=bz.chunks(pd.DataFrame), chunksize=2):
# Do stuff with chunked dataframe