Bagaimana cara menulis DataFrame ke tabel postgres?

103

Ada metode DataFrame.to_sql , tetapi hanya berfungsi untuk database mysql, sqlite dan oracle. Saya tidak bisa meneruskan ke metode ini koneksi postgres atau mesin sqlalchemy.

m9_psy
sumber

Jawaban:

127

Mulai dari pandas 0.14 (dirilis akhir Mei 2014), postgresql didukung. The sqlmodul sekarang menggunakan sqlalchemyuntuk mendukung rasa database yang berbeda. Anda dapat mengirimkan mesin sqlalchemy untuk database postgresql (lihat dokumen ). Misalnya:

from sqlalchemy import create_engine
engine = create_engine('postgresql://scott:tiger@localhost:5432/mydatabase')
df.to_sql('table_name', engine)

Anda benar bahwa di panda hingga versi 0.13.1 postgresql tidak didukung. Jika Anda perlu menggunakan panda versi lama, berikut adalah versi patch dari pandas.io.sql: https://gist.github.com/jorisvandenbossche/10841234 .
Saya menulis ini beberapa waktu lalu, jadi tidak dapat sepenuhnya menjamin bahwa itu selalu berhasil, tetapi dasarnya harus ada). Jika Anda meletakkan file itu di direktori kerja Anda dan mengimpornya, maka Anda harus dapat melakukannya (di mana conada koneksi postgresql):

import sql  # the patched version (file is named sql.py)
sql.write_frame(df, 'table_name', con, flavor='postgresql')
joris
sumber
1
Apakah ini membuatnya menjadi 0,14?
Kuantitas
Ya, dan juga 0,15 sudah dirilis (kandidat rilis). Saya akan memperbarui jawabannya, terima kasih telah bertanya.
joris
1
Posting ini memecahkan masalah untuk saya: stackoverflow.com/questions/24189150/…
srodriguex
Catatan: to_sql tidak mengekspor tipe array di postgres.
Saurabh Saha
1
Daripada membuat yang baru Sqlalchemy engine, dapatkah saya menggunakan Postgreskoneksi yang sudah ada yang dibuat menggunakan psycopg2.connect()?
Jarvis
84

Opsi lebih cepat:

Kode berikut akan menyalin Pandas DF Anda ke postgres DB jauh lebih cepat daripada metode df.to_sql dan Anda tidak memerlukan file csv perantara untuk menyimpan df.

Buat mesin berdasarkan spesifikasi DB Anda.

Buat tabel di postgres DB Anda yang memiliki jumlah kolom yang sama dengan Dataframe (df).

Data di DF akan dimasukkan ke dalam tabel postgres Anda.

from sqlalchemy import create_engine
import psycopg2 
import io

jika Anda ingin mengganti tabel, kita dapat menggantinya dengan metode to_sql normal menggunakan header dari df kita dan kemudian memuat seluruh df yang memakan waktu besar ke dalam DB.

engine = create_engine('postgresql+psycopg2://username:password@host:port/database')

df.head(0).to_sql('table_name', engine, if_exists='replace',index=False) #truncates the table

conn = engine.raw_connection()
cur = conn.cursor()
output = io.StringIO()
df.to_csv(output, sep='\t', header=False, index=False)
output.seek(0)
contents = output.getvalue()
cur.copy_from(output, 'table_name', null="") # null values become ''
conn.commit()
Aseem
sumber
Apa yang dilakukan variabel contents? Haruskah ini yang tertulis copy_from()?
n1000
@ n1000 Ya abaikan saja contentsvariabelnya, semua yang lain akan bekerja dengan baik
Bobby
2
kenapa kau lakukan output.seek(0)?
moshevi
7
Ini sangat cepat sehingga lucu: D
shadi
1
Beban tabel gagal untuk saya karena karakter baris baru di beberapa bidang. Bagaimana cara saya menangani ini? df.to_csv (output, sep = '\ t', header = False, index = False, encoding = 'utf-8') cur.copy_from (output, 'messages', null = "") # nilai null menjadi ''
conetfun
24

Solusi panda 0.24.0+

Di Pandas 0.24.0, fitur baru diperkenalkan yang dirancang khusus untuk penulisan cepat ke Postgres. Anda dapat mempelajarinya lebih lanjut di sini: https://pandas.pydata.org/pandas-docs/stable/user_guide/io.html#io-sql-method

import csv
from io import StringIO

from sqlalchemy import create_engine

def psql_insert_copy(table, conn, keys, data_iter):
    # gets a DBAPI connection that can provide a cursor
    dbapi_conn = conn.connection
    with dbapi_conn.cursor() as cur:
        s_buf = StringIO()
        writer = csv.writer(s_buf)
        writer.writerows(data_iter)
        s_buf.seek(0)

        columns = ', '.join('"{}"'.format(k) for k in keys)
        if table.schema:
            table_name = '{}.{}'.format(table.schema, table.name)
        else:
            table_name = table.name

        sql = 'COPY {} ({}) FROM STDIN WITH CSV'.format(
            table_name, columns)
        cur.copy_expert(sql=sql, file=s_buf)

engine = create_engine('postgresql://myusername:mypassword@myhost:5432/mydatabase')
df.to_sql('table_name', engine, method=psql_insert_copy)
mgoldwasser.dll
sumber
3
Untuk sebagian besar waktu, menambahkan method='multi'opsi cukup cepat. Tapi ya, COPYcara ini adalah cara tercepat saat ini.
ssword
Apakah ini hanya untuk csv? Bisakah itu digunakan dengan .xlsx juga? Beberapa catatan tentang apa yang dilakukan setiap bagian ini akan membantu. Bagian pertama setelah withmenulis ke dalam buffer memori. Bagian terakhir withadalah menggunakan pernyataan SQL dan memanfaatkan kecepatan copy_expert untuk memuat data secara massal. Apa bagian tengah yang dimulai dengan columns =melakukan?
DudeWah
Ini bekerja dengan sangat baik untuk saya. Dan bisakah Anda menjelaskan keysargumen dalam psql_insert_copyfungsi tersebut? Bagaimana cara mendapatkan kunci dan apakah kuncinya hanya nama kolom?
Bowen Liu
Saya sudah mencoba menggunakan metode ini, namun melempar saya sebuah kesalahan: Table 'XYZ' already exists. Sejauh yang saya mengerti, seharusnya tidak membuat tabel, bukan?
E. Epstein
@ E.Epstein - Anda dapat mengubah baris terakhir menjadi df.to_sql('table_name', engine, if_exists='replace', method=psql_insert_copy)- ini membuat tabel di database Anda.
mgoldwasser
23

Beginilah cara saya melakukannya.

Mungkin lebih cepat karena menggunakan execute_batch:

# df is the dataframe
if len(df) > 0:
    df_columns = list(df)
    # create (col1,col2,...)
    columns = ",".join(df_columns)

    # create VALUES('%s', '%s",...) one '%s' per column
    values = "VALUES({})".format(",".join(["%s" for _ in df_columns])) 

    #create INSERT INTO table (columns) VALUES('%s',...)
    insert_stmt = "INSERT INTO {} ({}) {}".format(table,columns,values)

    cur = conn.cursor()
    psycopg2.extras.execute_batch(cur, insert_stmt, df.values)
    conn.commit()
    cur.close()
Behdad Forghani
sumber
1
Saya mendapatkan AttributeError: modul 'psycopg2' tidak memiliki atribut 'ekstra'. Ah, ini perlu diimpor secara eksplisit. impor psycopg2.extras
GeorgeLPerkins
fungsi ini jauh lebih cepat daripada solusi sqlalchemy
Saurabh Saha
-1

Untuk Python 2.7 dan Pandas 0.24.2 dan menggunakan Psycopg2

Modul Koneksi Psycopg2

def dbConnect (db_parm, username_parm, host_parm, pw_parm):
    # Parse in connection information
    credentials = {'host': host_parm, 'database': db_parm, 'user': username_parm, 'password': pw_parm}
    conn = psycopg2.connect(**credentials)
    conn.autocommit = True  # auto-commit each entry to the database
    conn.cursor_factory = RealDictCursor
    cur = conn.cursor()
    print ("Connected Successfully to DB: " + str(db_parm) + "@" + str(host_parm))
    return conn, cur

Hubungkan ke database

conn, cur = dbConnect(databaseName, dbUser, dbHost, dbPwd)

Dengan asumsi dataframe sudah ada sebagai df

output = io.BytesIO() # For Python3 use StringIO
df.to_csv(output, sep='\t', header=True, index=False)
output.seek(0) # Required for rewinding the String object
copy_query = "COPY mem_info FROM STDOUT csv DELIMITER '\t' NULL ''  ESCAPE '\\' HEADER "  # Replace your table name in place of mem_info
cur.copy_expert(copy_query, output)
conn.commit()
Mayukh Ghosh
sumber