Comment écrire DataFrame dans la table postgres?

103

Il existe une méthode DataFrame.to_sql , mais elle ne fonctionne que pour les bases de données mysql, sqlite et oracle. Je ne peux pas passer à cette méthode postgres connection ou sqlalchemy engine.

m9_psy
la source

Réponses:

125

À partir de pandas 0.14 (publié fin mai 2014), postgresql est pris en charge. Le sqlmodule utilise maintenant sqlalchemypour prendre en charge différentes saveurs de base de données. Vous pouvez transmettre un moteur sqlalchemy pour une base de données postgresql (voir la documentation ). Par exemple:

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

Vous avez raison de dire que dans les pandas jusqu'à la version 0.13.1, postgresql n'était pas pris en charge. Si vous devez utiliser une version plus ancienne de pandas, voici une version corrigée de pandas.io.sql: https://gist.github.com/jorisvandenbossche/10841234 .
Je l'ai écrit il y a un certain temps, je ne peux donc pas garantir pleinement que cela fonctionne toujours, mais la base devrait être là). Si vous placez ce fichier dans votre répertoire de travail et que vous l'importez, vous devriez pouvoir faire (où se controuve une connexion postgresql):

import sql  # the patched version (file is named sql.py)
sql.write_frame(df, 'table_name', con, flavor='postgresql')
joris
la source
1
Cela a-t-il atteint 0,14?
Quant
Oui, et aussi 0.15 est déjà publié (release candidate). Je mettrai à jour la réponse, merci de demander.
joris
1
Ce message a résolu le problème pour moi: stackoverflow.com/questions/24189150/…
srodriguex
Remarque: to_sql n'exporte pas les types de tableaux dans postgres.
Saurabh Saha
1
Au lieu de créer une nouvelle Sqlalchemy engine, puis-je utiliser une Postgresconnexion existante créée à l'aide de psycopg2.connect()?
Jarvis
84

Option plus rapide:

Le code suivant copiera votre Pandas DF vers postgres DB beaucoup plus rapidement que la méthode df.to_sql et vous n'aurez pas besoin d'un fichier csv intermédiaire pour stocker le df.

Créez un moteur basé sur vos spécifications DB.

Créez une table dans votre base de données postgres qui a le même nombre de colonnes que le Dataframe (df).

Les données dans DF seront insérées dans votre table postgres.

from sqlalchemy import create_engine
import psycopg2 
import io

si vous souhaitez remplacer la table, nous pouvons la remplacer par la méthode to_sql normale en utilisant les en-têtes de notre df, puis charger tout le df qui prend beaucoup de temps dans 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
la source
Que fait la variable contents? Cela devrait-il être celui qui est écrit copy_from()?
n1000
@ n1000 Ouais, ignorez simplement la contentsvariable, tout le reste devrait bien fonctionner
Bobby
2
pourquoi tu fais output.seek(0)?
moshevi
7
C'est tellement rapide que c'est drôle: D
shadi
1
Le chargement de la table échoue pour moi à cause de nouveaux caractères de ligne dans certains champs. Comment gérer cela? df.to_csv (sortie, sep = '\ t', header = False, index = False, encoding = 'utf-8') cur.copy_from (sortie, 'messages', null = "") # valeurs nulles deviennent ''
conetfun
23

Solution Pandas 0.24.0+

Dans Pandas 0.24.0, une nouvelle fonctionnalité a été introduite spécialement conçue pour les écritures rapides sur Postgres. Vous pouvez en savoir plus ici: 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
la source
3
Pour la plupart du temps, l' method='multi'option d' ajout est assez rapide. Mais oui, cette COPYméthode est actuellement la méthode la plus rapide.
ssword
Est-ce uniquement pour csv? Peut-il également être utilisé avec .xlsx? Quelques notes sur ce que fait chaque partie de ceci seraient utiles. La première partie après l' withécriture dans une mémoire tampon. La dernière partie de l ' withutilise une instruction SQL et profite de la vitesse de copy_expert pour charger les données en masse. Quelle est la partie médiane qui commence par columns =faire?
DudeWah
Cela a très bien fonctionné pour moi. Et pourriez-vous expliquer les keysarguments de la psql_insert_copyfonction s'il vous plaît? Comment obtient-il des clés et les clés sont-elles uniquement les noms de colonnes?
Bowen Liu
Je l' ai essayé d' utiliser cette méthode, mais il me renvoie une erreur: Table 'XYZ' already exists. Pour autant que je sache, cela ne devrait pas créer une table, n'est-ce pas?
E. Epstein
@ E.Epstein - vous pouvez modifier la dernière ligne en df.to_sql('table_name', engine, if_exists='replace', method=psql_insert_copy)- cela crée une table dans votre base de données.
mgoldwasser
21

Voilà comment je l'ai fait.

Cela peut être plus rapide car il utilise 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
la source
1
J'obtiens AttributeError: le module 'psycopg2' n'a pas d'attribut 'extras'. Ah, cela doit être explicitement importé. import psycopg2.extras
GeorgeLPerkins
cette fonction est beaucoup plus rapide que la solution sqlalchemy
Saurabh Saha
-1

Pour Python 2.7 et Pandas 0.24.2 et utilisant Psycopg2

Module de connexion 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

Connectez-vous à la base de données

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

En supposant que le dataframe soit déjà présent en tant que 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
la source