Как записать DataFrame в таблицу postgres?

103

Есть метод DataFrame.to_sql , но он работает только для баз данных mysql, sqlite и oracle. Я не могу перейти к этому методу postgres connection или sqlalchemy engine.

m9_psy
источник

Ответы:

127

Начиная с pandas 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 не поддерживался. Если вам нужно использовать старую версию pandas, вот исправленная версия 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 08
1
Этот пост решил для меня проблему: stackoverflow.com/questions/24189150/…
srodriguex 04
Примечание: to_sql не экспортирует типы массивов в postgres.
Саураб Саха
1
Sqlalchemy engineМогу ли я использовать существующее Postgresсоединение, созданное с помощью, вместо создания нового psycopg2.connect()?
Джарвис
84

Более быстрый вариант:

Следующий код скопирует ваш Pandas DF в базу данных postgres намного быстрее, чем метод df.to_sql, и вам не понадобится какой-либо промежуточный файл csv для хранения df.

Создайте движок на основе спецификаций вашей БД.

Создайте таблицу в своей базе данных postgres, которая имеет такое же количество столбцов, что и Dataframe (df).

Данные из DF будут вставлены в вашу таблицу postgres.

from sqlalchemy import create_engine
import psycopg2 
import io

если вы хотите заменить таблицу, мы можем заменить ее обычным методом to_sql, используя заголовки из нашего df, а затем загрузить весь большой трудоемкий df в БД.

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
источник
Что делает переменная contents? Это должно быть то, что написано copy_from()?
n1000
@ n1000 Да, просто игнорируйте contentsпеременную, все остальное должно работать нормально,
Бобби,
2
зачем ты это делаешь output.seek(0)?
Moshevi
7
Это настолько быстро, что это забавно: D
шади
1
Загрузка таблицы у меня не работает из-за символов новой строки в некоторых полях. Как мне с этим справиться? df.to_csv (output, sep = '\ t', header = False, index = False, encoding = 'utf-8') cur.copy_from (output, 'messages', null = "") # значения null становятся ''
conetfun
24

Решение 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 09
Это только для csv? Можно ли его использовать и с .xlsx? Некоторые заметки о том, что делает каждая часть этого, были бы полезны. Первая часть после - withэто запись в буфер в памяти. В последней части withиспользуется оператор SQL и скорость copy_expert для массовой загрузки данных. С чего начинается средняя часть columns =?
DudeWah
У меня это сработало очень хорошо. Не могли бы вы объяснить keysаргументы в psql_insert_copyфункции? Как он получает какие-либо ключи и являются ли ключи просто именами столбцов?
Боуэн Лю,
Я попытался с помощью этого метода, однако он выдает мне ошибку: Table 'XYZ' already exists. Насколько я понимаю, он не должен создавать таблицу?
Э. Эпштейн,
@ E.Epstein - вы можете изменить последнюю строку на df.to_sql('table_name', engine, if_exists='replace', method=psql_insert_copy)- это создает таблицу в вашей базе данных.
mgoldwasser
23

Вот как я это сделал.

Это может быть быстрее, потому что он использует 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: модуль psycopg2 не имеет атрибута extras. Ах, это нужно явно импортировать. import psycopg2.extras
Джордж Перкинс
эта функция значительно быстрее , чем решение SQLAlchemy
Саурабй Саа
-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)

Предполагая, что фрейм данных уже присутствует как 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()
Маюх Гош
источник