私は毎日約2〜250万レコードをPostgresデータベースにロードしています。
次に、pd.read_sqlを使用してこのデータを読み取り、データフレームに変換してから、列の操作と小さなマージを行います。他の人が使用できるように、この変更されたデータを別のテーブルとして保存しています。
私がpd.to_sqlを実行すると、時間がかかります。 csvファイルを保存してPostgresでCOPY FROMを使用すると、全体が数分しかかかりませんが、サーバーが別のマシン上にあり、そこにファイルを転送するのは面倒です。
Psycopg2を使用すると、copy_expertを使用して一括コピーのメリットを享受できるように見えますが、それでもpythonを使用しています。できれば、実際のcsvファイルの作成は避けたいです。 pandas dataframeを使用してメモリでこれを行うことはできますか?
これが私のpandasコードの例です。copy_expertなどを追加して、可能であればこのデータをより速く保存できるようにしたいと考えています。
for date in required_date_range:
df = pd.read_sql(sql=query, con=pg_engine, params={'x' : date})
...
do stuff to the columns
...
df.to_sql('table_name', pg_engine, index=False, if_exists='append', dtype=final_table_dtypes)
誰かがサンプルコードで私を助けてくれますか?私はpandasを引き続き使用することを好み、メモリ内でそれを実行するのは良いことです。そうでない場合は、csv一時ファイルを作成し、その方法で実行します。
編集-ここに私の最終的なコードがあります。数時間ではなく、日付あたり数百秒(数百万行)しかかかりません。
to_sql = "" "CSVヘッダーのあるSTDINから%sをコピー" ""
def process_file(conn, table_name, file_object):
fake_conn = cms_dtypes.pg_engine.raw_connection()
fake_cur = fake_conn.cursor()
fake_cur.copy_expert(sql=to_sql % table_name, file=file_object)
fake_conn.commit()
fake_cur.close()
#after doing stuff to the dataframe
s_buf = io.StringIO()
df.to_csv(s_buf)
process_file(cms_dtypes.pg_engine, 'fact_cms_employee', s_buf)
Pythonモジュールio
( docs )には、ファイルのようなオブジェクトに必要なツールがあります。
import io
# text buffer
s_buf = io.StringIO()
# saving a data frame to a buffer (same as with a regular file):
df.to_csv(s_buf)
編集します。(忘れました)後でバッファから読み取るには、その位置を先頭に設定する必要があります。
s_buf.seek(0)
psycopg2
だが docs によるとcopy_expert
およびcopy_from
を使用できます。例:
cur.copy_from(s_buf, table)
(Python 2については StringIO を参照してください。)
Ptrjからのソリューションの実装に問題がありました。
問題は、pandasバッファの位置を最後に設定することから生じていると思います。
以下を参照してください。
from StringIO import StringIO
df = pd.DataFrame({"name":['foo','bar'],"id":[1,2]})
s_buf = StringIO()
df.to_csv(s_buf)
s_buf.__dict__
# Output
# {'softspace': 0, 'buflist': ['foo,1\n', 'bar,2\n'], 'pos': 12, 'len': 12, 'closed': False, 'buf': ''}
Posが12であることに注意してください。後続のcopy_fromコマンドが機能するためには、posを0に設定する必要がありました。
s_buf.pos = 0
cur = conn.cursor()
cur.copy_from(s_buf, tablename, sep=',')
conn.commit()