Wie schreibe ich DataFrame in die Postgres-Tabelle?

103

Es gibt die DataFrame.to_sql- Methode, die jedoch nur für MySQL-, SQLite- und Oracle-Datenbanken funktioniert. Ich kann nicht auf diese Methode Postgres Verbindung oder SQLalchemy Engine übergeben.

m9_psy
quelle

Antworten:

125

Ab pandas 0.14 (veröffentlicht Ende Mai 2014) wird postgresql unterstützt. Das sqlModul unterstützt jetzt sqlalchemyverschiedene Datenbankvarianten. Sie können eine SQLalchemy-Engine für eine PostgresQL-Datenbank übergeben (siehe Dokumente ). Z.B:

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

Sie haben Recht, dass in Pandas bis Version 0.13.1 postgresql nicht unterstützt wurde. Wenn Sie eine ältere Version von Pandas verwenden müssen, finden Sie hier eine gepatchte Version von pandas.io.sql: https://gist.github.com/jorisvandenbossche/10841234 .
Ich habe dies vor einiger Zeit geschrieben, kann also nicht vollständig garantieren, dass es immer funktioniert, aber die Basis sollte da sein. Wenn Sie diese Datei in Ihr Arbeitsverzeichnis coneinfügen und importieren, sollten Sie in der Lage sein (wo ist eine Postgresql-Verbindung):

import sql  # the patched version (file is named sql.py)
sql.write_frame(df, 'table_name', con, flavor='postgresql')
Joris
quelle
1
Hat es das auf 0,14 geschafft?
Quant
Ja, und auch 0.15 ist bereits freigegeben (Freigabekandidat). Ich werde die Antwort aktualisieren, danke für die Nachfrage.
Joris
1
Dieser Beitrag löste das Problem für mich: stackoverflow.com/questions/24189150/…
srodriguex
Hinweis: to_sql exportiert keine Array-Typen in Postgres.
Saurabh Saha
1
Sqlalchemy engineKann ich eine neue PostgresVerbindung verwenden, die mit erstellt wurde, anstatt eine neue zu erstellen psycopg2.connect()?
Jarvis
83

Schnellere Option:

Mit dem folgenden Code wird Ihr Pandas DF viel schneller als mit der Methode df.to_sql in die Postgres-Datenbank kopiert, und Sie benötigen keine Zwischen-CSV-Datei, um die df zu speichern.

Erstellen Sie eine Engine basierend auf Ihren DB-Spezifikationen.

Erstellen Sie in Ihrer Postgres-Datenbank eine Tabelle mit der gleichen Anzahl von Spalten wie der Datenrahmen (df).

Daten in DF werden in Ihre Postgres-Tabelle eingefügt .

from sqlalchemy import create_engine
import psycopg2 
import io

Wenn Sie die Tabelle ersetzen möchten, können wir sie durch die normale to_sql-Methode ersetzen, indem wir Header aus unserem df verwenden und dann den gesamten zeitaufwändigen df in die DB laden.

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
quelle
Was macht die Variable contents? Sollte dies derjenige sein, in dem geschrieben steht copy_from()?
n1000
@ n1000 Ja, ignoriere einfach die contentsVariable, alles andere sollte gut funktionieren
Bobby
2
warum tun Sie output.seek(0)?
Moshevi
7
Das ist so schnell, dass es lustig ist: D
Shadi
1
Das Laden der Tabelle schlägt für mich aufgrund neuer Zeilenzeichen in einigen Feldern fehl. Wie gehe ich damit um? df.to_csv (Ausgabe, sep = '\ t', Header = False, Index = False, Kodierung = 'utf-8') cur.copy_from (Ausgabe, 'messages', null = "") # Nullwerte werden ''
Conetfun
22

Pandas 0.24.0+ Lösung

In Pandas 0.24.0 wurde eine neue Funktion eingeführt, die speziell für schnelles Schreiben in Postgres entwickelt wurde. Weitere Informationen finden Sie hier: 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
quelle
3
Für die meiste Zeit ist die method='multi'Option zum Hinzufügen schnell genug. Aber ja, diese COPYMethode ist momentan der schnellste.
Schwert
Ist das nur für CSVs? Kann es auch mit .xlsx verwendet werden? Einige Hinweise dazu, was jeder Teil davon tut, wären hilfreich. Der erste Teil nach dem withschreibt in einen In-Memory-Puffer. Der letzte Teil von withbesteht darin, eine SQL-Anweisung zu verwenden und die Geschwindigkeit von copy_expert zu nutzen, um die Daten in großen Mengen zu laden. Was ist der mittlere Teil, der damit beginnt columns =?
DudeWah
Das hat bei mir sehr gut funktioniert. Und könnten Sie bitte die keysArgumente in der psql_insert_copyFunktion erklären ? Wie bekommt es irgendwelche Schlüssel und sind die Schlüssel nur die Spaltennamen?
Bowen Liu
Ich habe versucht, diese Methode zu verwenden, aber es gibt mir einen Fehler : Table 'XYZ' already exists. Soweit ich weiß, sollte es keine Tabelle erstellen, oder?
E. Epstein
@ E.Epstein - Sie können die letzte Zeile ändern in df.to_sql('table_name', engine, if_exists='replace', method=psql_insert_copy)- dies erstellt eine Tabelle in Ihrer Datenbank.
mgoldwasser
21

So habe ich es gemacht.

Es kann schneller sein, weil es verwendet 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
quelle
1
Ich erhalte AttributeError: Modul 'psycopg2' hat kein Attribut 'Extras'. Ah, das muss explizit importiert werden. import psycopg2.extras
GeorgeLPerkins
Diese Funktion ist viel schneller als die SQLalchemie-Lösung
Saurabh Saha
0

Für Python 2.7 und Pandas 0.24.2 und mit Psycopg2

Psycopg2-Verbindungsmodul

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

Stellen Sie eine Verbindung zur Datenbank her

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

Angenommen, der Datenrahmen ist bereits als df vorhanden

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
quelle