約 6,000 万行のデータセットから、約 3,000 万~ 5,000 万行の 2,000 列のピボット テーブルを作成する必要があります。100,000 行のチャンクでピボット処理を試みたところ、うまくいきましたが、.append() に続いて .groupby('someKey').sum() を実行して DataFrames を再結合しようとすると、すべてのメモリが消費され、最終的に Python がクラッシュします。
RAM の量が限られている場合、これほど大きなデータのピボットを実行するにはどうすればよいでしょうか?
編集: サンプルコードを追加
次のコードには、途中でさまざまなテスト出力が含まれていますが、最後の出力こそが本当に興味深いものです。segMax を 4 ではなく 3 に変更すると、コードは正しい出力に対して誤検知を生成することに注意してください。主な問題は、sum(wawa) が調べるすべてのチャンクに shippingid エントリが含まれていない場合、出力に表示されないことです。
import pandas as pd
import numpy as np
import random
from pandas.io.pytables import *
import os
pd.set_option('io.hdf.default_format','table')
# create a small dataframe to simulate the real data.
def loadFrame():
frame = pd.DataFrame()
frame['shipmentid']=[1,2,3,1,2,3,1,2,3] #evenly distributing shipmentid values for testing purposes
frame['qty']= np.random.randint(1,5,9) #random quantity is ok for this test
frame['catid'] = np.random.randint(1,5,9) #random category is ok for this test
return frame
def pivotSegment(segmentNumber,passedFrame):
segmentSize = 3 #take 3 rows at a time
frame = passedFrame[(segmentNumber*segmentSize):(segmentNumber*segmentSize + segmentSize)] #slice the input DF
# ensure that all chunks are identically formatted after the pivot by appending a dummy DF with all possible category values
span = pd.DataFrame()
span['catid'] = range(1,5+1)
span['shipmentid']=1
span['qty']=0
frame = frame.append(span)
return frame.pivot_table(['qty'],index=['shipmentid'],columns='catid', \
aggfunc='sum',fill_value=0).reset_index()
def createStore():
store = pd.HDFStore('testdata.h5')
return store
segMin = 0
segMax = 4
store = createStore()
frame = loadFrame()
print('Printing Frame')
print(frame)
print(frame.info())
for i in range(segMin,segMax):
segment = pivotSegment(i,frame)
store.append('data',frame[(i*3):(i*3 + 3)])
store.append('pivotedData',segment)
print('\nPrinting Store')
print(store)
print('\nPrinting Store: data')
print(store['data'])
print('\nPrinting Store: pivotedData')
print(store['pivotedData'])
print('**************')
print(store['pivotedData'].set_index('shipmentid').groupby('shipmentid',level=0).sum())
print('**************')
print('$$$')
for df in store.select('pivotedData',chunksize=3):
print(df.set_index('shipmentid').groupby('shipmentid',level=0).sum())
print('$$$')
store['pivotedAndSummed'] = sum((df.set_index('shipmentid').groupby('shipmentid',level=0).sum() for df in store.select('pivotedData',chunksize=3)))
print('\nPrinting Store: pivotedAndSummed')
print(store['pivotedAndSummed'])
store.close()
os.remove('testdata.h5')
print('closed')
ベストアンサー1
HDF5/pytables を使用して追加を行うことができます。これにより、RAM を節約できます。
使用表形式:
store = pd.HDFStore('store.h5')
for ...:
...
chunk # the chunk of the DataFrame (which you want to append)
store.append('df', chunk)
これで、これを DataFrame として一度に読み込むことができます (この DataFrame がメモリに収まると仮定した場合)。
df = store['df']
クエリを実行して、DataFrame のサブセクションのみを取得することもできます。
余談ですが、RAM も追加で購入したほうがいいですよ。安いですから。
編集: ストアからグループ化/合計することができます繰り返しこれはチャンクに対して「マップ削減」を行うため、
# note: this doesn't work, see below
sum(df.groupby().sum() for df in store.select('df', chunksize=50000))
# equivalent to (but doesn't read in the entire frame)
store['df'].groupby().sum()
編集2: 上記のようにsumを使用すると、実際にはpandas 0.16では機能しません(0.15.2では機能すると思っていました)。代わりに、reduce
とadd
:
reduce(lambda x, y: x.add(y, fill_value=0),
(df.groupby().sum() for df in store.select('df', chunksize=50000)))
Python 3ではfunctools からreduceをインポートする。
おそらく、次のように書くほうが Python らしくて読みやすいでしょう。
chunks = (df.groupby().sum() for df in store.select('df', chunksize=50000))
res = next(chunks) # will raise if there are no chunks!
for c in chunks:
res = res.add(c, fill_value=0)
パフォーマンスが悪い場合、または新しいグループの数が多い場合は、正しいサイズのゼロとしてリソースを開始し (チャンクをループするなどして一意のグループ キーを取得することによって)、その場で追加することが望ましい場合があります。