จะเขียน DataFrame ลงในตาราง Postgres ได้อย่างไร?


109

มีเมธอดDataFrame.to_sqlแต่ใช้ได้กับฐานข้อมูล mysql, sqlite และ oracle เท่านั้น ฉันไม่สามารถส่งต่อไปยังวิธีนี้ postgres connection หรือ sqlalchemy engine

คำตอบ:


132

เริ่มจากแพนด้า 0.14 (วางจำหน่ายปลายเดือนพฤษภาคม 2014) รองรับ postgresql sqlโมดูลตอนนี้ใช้sqlalchemyเพื่อสนับสนุนรสชาติที่แตกต่างกันของฐานข้อมูล คุณสามารถส่งผ่านโปรแกรม sqlalchemy สำหรับฐานข้อมูล postgresql (ดูเอกสาร ) เช่น:

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

คุณถูกต้องที่ไม่รองรับในแพนด้าจนถึงเวอร์ชัน 0.13.1 postgresql หากคุณจำเป็นต้องใช้รุ่นเก่าของหมีแพนด้าที่นี่เป็นรุ่น patched ของpandas.io.sql: https://gist.github.com/jorisvandenbossche/10841234
ฉันเขียนสิ่งนี้เมื่อครั้งก่อนดังนั้นจึงไม่สามารถรับประกันได้ว่าจะใช้งานได้ตลอดเวลา แต่ควรมีพื้นฐานอยู่ที่นั่น) หากคุณใส่ไฟล์นั้นในไดเร็กทอรีการทำงานของคุณและนำเข้าคุณควรจะทำได้ ( conการเชื่อมต่อ postgresql อยู่ที่ไหน):

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

1
สิ่งนี้ทำให้เป็น 0.14 หรือไม่?
Quant

ใช่และมีการเปิดตัว 0.15 แล้วด้วย ฉันจะอัปเดตคำตอบขอบคุณที่ถาม
joris

1
โพสต์นี้แก้ปัญหาให้ฉัน: stackoverflow.com/questions/24189150/…
srodriguex

หมายเหตุ: to_sql ไม่ส่งออกประเภทอาร์เรย์ใน postgres
Saurabh Saha

1
แทนที่จะสร้างใหม่Sqlalchemy engineฉันสามารถใช้การPostgresเชื่อมต่อที่มีอยู่ที่สร้างขึ้นโดยใช้psycopg2.connect()?
Underoos

93

ตัวเลือกที่เร็วกว่า:

โค้ดต่อไปนี้จะคัดลอก Pandas DF ของคุณไปยัง postgres DB เร็วกว่าเมธอด df.to_sql มากและคุณไม่จำเป็นต้องใช้ไฟล์ csv กลางเพื่อจัดเก็บ df

สร้างเอ็นจิ้นตามข้อกำหนด DB ของคุณ

สร้างตารางใน Postgres DB ที่มีจำนวนคอลัมน์เท่ากับ Dataframe (df)

ข้อมูลใน DF จะถูกแทรกลงในตาราง postgres ของคุณ

from sqlalchemy import create_engine
import psycopg2 
import io

หากคุณต้องการแทนที่ตารางเราสามารถแทนที่ด้วยเมธอด to_sql ปกติโดยใช้ส่วนหัวจาก df ของเราจากนั้นโหลด df ที่ใช้เวลานานทั้งหมดลงใน 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()

ตัวแปรcontentsทำอะไร? นี่ควรเป็นแบบที่เขียนไว้copy_from()ไหม
n1000

3
ทำไมคุณทำoutput.seek(0)?
moshevi

10
นี้เป็นอย่างรวดเร็วว่ามันตลก: D
Shadi

1
การโหลดเป็นตารางล้มเหลวสำหรับฉันเนื่องจากมีอักขระขึ้นบรรทัดใหม่ในบางฟิลด์ ฉันจะจัดการสิ่งนี้ได้อย่างไร? df.to_csv (ผลลัพธ์, sep = '\ t', header = False, index = False, encoding = 'utf-8') cur.copy_from (ผลลัพธ์, 'ข้อความ', null = "") # ค่า null กลายเป็น ''
conetfun

1
@moshevi - วิธีการค้นหาจะตั้งค่าตำแหน่งปัจจุบันของไฟล์ดังนั้นที่นี่จึงเป็นการย้ายตำแหน่งกลับไปที่จุดเริ่มต้นของไฟล์อย่างชัดเจนโดยไบต์ 0 kite.com/python/docs/StringIO.StringIO.seek
foakesm

27

โซลูชัน Pandas 0.24.0+

ใน Pandas 0.24.0 มีการเปิดตัวคุณลักษณะใหม่ที่ออกแบบมาโดยเฉพาะสำหรับการเขียนอย่างรวดเร็วไปยัง Postgres คุณสามารถเรียนรู้เพิ่มเติมได้ที่นี่: 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
ส่วนใหญ่แล้วการเพิ่มmethod='multi'ตัวเลือกนั้นเร็วพอ แต่ใช่COPYวิธีนี้เป็นวิธีที่เร็วที่สุดในตอนนี้
ssword

ใช้สำหรับ csv เท่านั้นหรือไม่? ใช้กับ. xlsx ได้ด้วยหรือไม่? ข้อสังเกตบางประการเกี่ยวกับสิ่งที่แต่ละส่วนทำจะเป็นประโยชน์ ส่วนแรกหลังการwithเขียนลงในบัฟเฟอร์หน่วยความจำ ส่วนสุดท้ายของการwithใช้คำสั่ง SQL และใช้ประโยชน์จากความเร็วของ copy_expert เพื่อโหลดข้อมูลจำนวนมาก ส่วนตรงกลางเริ่มต้นด้วยcolumns =การทำอะไร?
DudeWah

สิ่งนี้ได้ผลดีสำหรับฉัน และคุณช่วยอธิบายkeysอาร์กิวเมนต์ในpsql_insert_copyฟังก์ชันได้ไหม มันรับคีย์ได้อย่างไรและคีย์เป็นเพียงชื่อคอลัมน์หรือไม่?
Bowen Liu

ฉันได้ลองใช้วิธีนี้แล้ว แต่มันทำให้ฉันเกิดข้อผิดพลาด: Table 'XYZ' already exists. เท่าที่ฉันเข้าใจมันไม่ควรสร้างตารางควรหรือไม่?
E. Epstein

@ E. Epstein - คุณสามารถแก้ไขบรรทัดสุดท้ายเป็นdf.to_sql('table_name', engine, if_exists='replace', method=psql_insert_copy)- สิ่งนี้จะสร้างตารางในฐานข้อมูลของคุณ
mgoldwasser

24

นี่คือวิธีที่ฉันทำ

อาจเร็วกว่าเนื่องจากใช้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
ฉันได้รับ AttributeError: module 'psycopg2' ไม่มีแอตทริบิวต์ 'extras' อาจะต้องนำเข้าอย่างชัดเจน import psycopg2.extras
GeorgeLPerkins

ฟังก์ชันนี้เร็วกว่าโซลูชัน sqlalchemy มาก
Saurabh Saha

-1

สำหรับ Python 2.7 และ Pandas 0.24.2 และใช้ Psycopg2

โมดูลการเชื่อมต่อ 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

เชื่อมต่อกับฐานข้อมูล

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

สมมติว่า dataframe มีอยู่แล้วเป็น 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()
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.