Jak napisać DataFrame do tabeli postgres?

103

Istnieje metoda DataFrame.to_sql , ale działa ona tylko dla baz danych mysql, sqlite i oracle. Nie mogę przejść do tej metody połączenia postgres lub silnika sqlalchemy.

m9_psy
źródło

Odpowiedzi:

127

Począwszy od pandy 0.14 (wydanej pod koniec maja 2014), obsługiwany jest postgresql. sqlModuł używa teraz sqlalchemywspierać różne smaki bazy danych. Możesz przekazać silnik sqlalchemy dla bazy danych postgresql (zobacz dokumentację ). Na przykład:

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

Masz rację, że w pandach do wersji 0.13.1 postgresql nie był obsługiwany. Jeśli musisz użyć starszej wersji pand, oto poprawiona wersja pandas.io.sql: https://gist.github.com/jorisvandenbossche/10841234 .
Napisałem to jakiś czas temu, więc nie mogę w pełni zagwarantować, że zawsze działa, ale podstawa powinna tam być). Jeśli umieścisz ten plik w katalogu roboczym i zaimportujesz go, powinieneś być w stanie to zrobić (gdzie conjest połączenie postgresql):

import sql  # the patched version (file is named sql.py)
sql.write_frame(df, 'table_name', con, flavor='postgresql')
joris
źródło
1
Czy to dotarło do 0.14?
Quant
Tak, a także wersja 0.15 jest już wydana (kandydat do wydania). Zaktualizuję odpowiedź, dzięki za pytanie.
joris
1
Ten post rozwiązał problem za mnie: stackoverflow.com/questions/24189150/…
srodriguex
Uwaga: to_sql nie eksportuje typów tablic w postgres.
Saurabh Saha
1
Zamiast tworzyć nowe Sqlalchemy engine, czy mogę użyć istniejącego Postgrespołączenia utworzonego za pomocą psycopg2.connect()?
Jarvis
84

Szybsza opcja:

Poniższy kod skopiuje twój Pandas DF do postgres DB znacznie szybciej niż metoda df.to_sql i nie będziesz potrzebować żadnego pośredniego pliku csv do przechowywania df.

Utwórz silnik na podstawie specyfikacji bazy danych.

Utwórz tabelę w swojej bazie danych postgres, która ma taką samą liczbę kolumn jak Dataframe (df).

Dane w DF zostaną wstawione do Twojej tabeli postgres.

from sqlalchemy import create_engine
import psycopg2 
import io

jeśli chcesz zamienić tabelę, możemy zastąpić ją normalną metodą to_sql, używając nagłówków z naszego df, a następnie załadować cały czasochłonny plik df do 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
źródło
Co robi zmienna contents? Czy to powinien być ten, w którym jest napisane copy_from()?
n1000
@ n1000 Tak, po prostu zignoruj contentszmienną, wszystko inne powinno działać dobrze
Bobby
2
dlaczego to robisz output.seek(0)?
moshevi
7
To jest tak szybkie, że aż zabawne: D
shadi
1
Ładowanie tabeli kończy się niepowodzeniem z powodu znaków nowego wiersza w niektórych polach. Jak sobie z tym radzę? df.to_csv (wyjście, sep = '\ t', nagłówek = fałsz, indeks = fałsz, kodowanie = 'utf-8') cur.copy_from (wyjście, 'wiadomości', null = "") # wartości null stają się ''
conetfun
23

Rozwiązanie Pandas 0.24.0+

W Pandas 0.24.0 wprowadzono nową funkcję zaprojektowaną specjalnie do szybkiego zapisu do Postgres. Możesz dowiedzieć się więcej na ten temat tutaj: 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
źródło
3
W większości przypadków method='multi'opcja dodawania jest wystarczająco szybka. Ale tak, ta COPYmetoda jest teraz najszybsza.
słowo
Czy to tylko dla CSV? Czy można go również używać z .xlsx? Przydałyby się pewne notatki na temat tego, co robi każda z tych części. Pierwsza część po withznaku to zapis do bufora pamięci. Ostatnią częścią withjest użycie instrukcji SQL i wykorzystanie szybkości copy_expert do masowego ładowania danych. Jaka jest środkowa część, od której zaczyna się columns =robienie?
DudeWah
To zadziałało dla mnie bardzo dobrze. Czy mógłbyś wyjaśnić keysargumenty w psql_insert_copyfunkcji? W jaki sposób uzyskuje klucze i czy klucze to tylko nazwy kolumn?
Bowen Liu
Próbowałem za pomocą tej metody, jednak wyrzuca mi błąd: Table 'XYZ' already exists. O ile rozumiem, nie powinien tworzyć tabeli, prawda?
E. Epstein
@ E.Epstein - możesz zmodyfikować ostatnią linię na df.to_sql('table_name', engine, if_exists='replace', method=psql_insert_copy)- to tworzy tabelę w twojej bazie danych.
mgoldwasser
23

Tak to zrobiłem.

Może być szybszy, ponieważ używa 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
źródło
1
Otrzymuję AttributeError: moduł „psycopg2” nie ma atrybutu „dodatki”. Ach, to musi być wyraźnie importowane. import psycopg2.extras
GeorgeLPerkins
ta funkcja jest znacznie szybsza niż rozwiązanie sqlalchemy
Saurabh Saha
-1

Dla Pythona 2.7 i Pandas 0.24.2 oraz przy użyciu Psycopg2

Moduł połączeniowy 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

Połącz się z bazą danych

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

Zakładając, że dataframe jest już obecna jako 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
źródło