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.

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')

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()

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)

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()

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()
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.