Come scrivere DataFrame nella tabella Postgres?


103

Esiste il metodo DataFrame.to_sql , ma funziona solo per i database mysql, sqlite e Oracle. Non posso passare a questo metodo connessione postgres o motore sqlalchemy.

Risposte:


125

A partire da pandas 0.14 (rilasciato a fine maggio 2014), è supportato postgresql. Il sqlmodulo ora utilizza sqlalchemyper supportare diversi tipi di database. È possibile passare un motore sqlalchemy per un database postgresql (vedere la documentazione ). Per esempio:

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

Hai ragione che nei panda fino alla versione 0.13.1 postgresql non era supportato. Se è necessario utilizzare una versione precedente di panda, ecco una versione con patch di pandas.io.sql: https://gist.github.com/jorisvandenbossche/10841234 .
L'ho scritto tempo fa, quindi non posso garantire completamente che funzioni sempre, ma la base dovrebbe essere lì). Se metti quel file nella tua directory di lavoro e lo importi, dovresti essere in grado di fare (dov'è conuna connessione postgresql):

import sql  # the patched version (file is named sql.py)
sql.write_frame(df, 'table_name', con, flavor='postgresql')

1
Questo è arrivato a 0,14?
Quant

Sì, e anche la 0.15 è già stata rilasciata (release candidate). Aggiornerò la risposta, grazie per avermelo chiesto.
joris

1
Questo post ha risolto il problema per me: stackoverflow.com/questions/24189150/...
srodriguex

Nota: to_sql non esporta i tipi di array in postgres.
Saurabh Saha

1
Invece di crearne una nuova Sqlalchemy engine, posso usare una Postgresconnessione esistente creata usando psycopg2.connect()?
Jarvis

84

Opzione più veloce:

Il codice seguente copierà il tuo Pandas DF nel DB postgres molto più velocemente del metodo df.to_sql e non avrai bisogno di alcun file csv intermedio per memorizzare il df.

Crea un motore basato sulle specifiche del tuo DB.

Crea una tabella nel tuo DB postgres che abbia lo stesso numero di colonne del Dataframe (df).

I dati in DF verranno inseriti nella tua tabella postgres.

from sqlalchemy import create_engine
import psycopg2 
import io

se si desidera sostituire la tabella, possiamo sostituirla con il normale metodo to_sql utilizzando le intestazioni dal nostro df e quindi caricare l'intero df che richiede molto tempo nel 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()

Cosa fa la variabile contents? Dovrebbe essere questo quello in cui è scritto copy_from()?
n1000

@ n1000 Sì, ignora la contentsvariabile, tutto il resto dovrebbe funzionare bene
Bobby

2
perchè lo fai output.seek(0)?
moshevi

7
È così veloce che è divertente: D
shadi

1
Il caricamento della tabella non riesce per me a causa di nuovi caratteri di riga in alcuni campi. Come lo gestisco? df.to_csv (output, sep = '\ t', header = False, index = False, encoding = 'utf-8') cur.copy_from (output, 'messages', null = "") # i valori nulli diventano ''
conetfun

23

Panda 0.24.0+ soluzione

In Pandas 0.24.0 è stata introdotta una nuova funzionalità specificamente progettata per scritture veloci su Postgres. Puoi saperne di più qui: 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
Per la maggior parte del tempo, l'aggiunta di method='multi'un'opzione è abbastanza veloce. Ma sì, questo COPYmetodo è il modo più veloce in questo momento.
ssword

È solo per CSV? Può essere utilizzato anche con .xlsx? Sarebbero utili alcune note su ciò che ogni parte di questo sta facendo. La prima parte dopo il withsta scrivendo in un buffer di memoria. L'ultima parte di withutilizza un'istruzione SQL e sfrutta la velocità di copy_expert per caricare in blocco i dati. Qual è la parte centrale che inizia con il columns =fare?
DudeWah

Questo ha funzionato molto bene per me. E potresti spiegare gli keysargomenti nella psql_insert_copyfunzione per favore? Come ottiene le chiavi e le chiavi sono solo i nomi delle colonne?
Bowen Liu,

Ho provato con questo metodo, tuttavia mi genera un errore: Table 'XYZ' already exists. Per quanto ho capito, non dovrebbe creare una tabella, vero?
E. Epstein

@ E.Epstein - puoi modificare l'ultima riga in df.to_sql('table_name', engine, if_exists='replace', method=psql_insert_copy)- questo crea una tabella nel tuo database.
mgoldwasser

21

Ecco come l'ho fatto.

Potrebbe essere più veloce perché utilizza 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
Ottengo AttributeError: il modulo "psycopg2" non ha attributi "extra". Ah, questo deve essere importato esplicitamente. import psycopg2.extras
GeorgeLPerkins

questa funzione è molto più veloce della soluzione sqlalchemy
Saurabh Saha

-1

Per Python 2.7 e Panda 0.24.2 e utilizzando Psycopg2

Modulo di connessione 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

Connettiti al database

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

Supponendo che dataframe sia già presente come 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()
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.