Преобразование ORM SQLAlchemy в pandas DataFrame

109

Эта тема давно не обсуждалась ни здесь, ни где-либо еще. Есть ли решение, преобразовывающее SQLAlchemy <Query object>в pandas DataFrame?

Pandas имеет возможность использовать, pandas.read_sqlно для этого требуется необработанный SQL. У меня есть две причины, по которым я хочу этого избежать: 1) у меня уже есть все, использующее ORM (хорошая причина сама по себе) и 2) я использую списки python как часть запроса (например: .db.session.query(Item).filter(Item.symbol.in_(add_symbols)где Itemмой класс модели и add_symbolsэто список). Это эквивалент SQL SELECT ... from ... WHERE ... IN.

Возможно ли что-нибудь?

Джаред
источник

Ответы:

194

Ниже должно работать в большинстве случаев:

df = pd.read_sql(query.statement, query.session.bind)

См. pandas.read_sqlДокументацию для получения дополнительной информации о параметрах.

фургон
источник
@van +1, но можно было бы немного подробнее. например, я сделал, df = pd.read_sql(query, query.bind)когда queryэто sqlalchemy.sql.selectable.Select. В противном случае я получил 'Select' object has no attribute 'session'.
Little Bobby Tables
Чтобы скопировать-вставить, я добавил ссылку на документацию прямо в ответ, который охватывает ваш вопрос: вы должны conengineconnection string
van
@van Было бы лучше использовать здесь query.session.connection ()? В противном случае запрос не учитывает непостоянные изменения в сеансе ...
поток данных
1
@dataflow: Думаю, вы правы, но я никогда не проверял это предположение.
фургон
@van - выдает ошибку «TypeError: элемент последовательности 0: ожидаемая строка, DefaultMeta found»; весь день рвал волосы, пытаясь понять, что случилось. Единственное, что я могу понять, это то, что это могло иметь какое-то отношение к попытке извлечь соединение из scoped_session ....
Эндрюпедерсон
89

Чтобы сделать это более понятным для начинающих программистов pandas, вот конкретный пример,

pd.read_sql(session.query(Complaint).filter(Complaint.id == 2).statement,session.bind) 

Здесь мы выбираем жалобу из таблицы жалоб (модель sqlalchemy - жалоба) с id = 2

Чандан Пурохит
источник
1
Я думаю, что это более понятно, когда код основан на ORM.
user40780
О, МОЙ БОГ! Я много боролся с адом sqlAlchemy. Небольшое примечание: вы также можете написать read_sql ('SELECT * FROM TABLENAME', db.session.bind). Спасибо. Приведенный выше ответ помог мне больше, чем принятый.
PallavBakshi
3
Что .statementделать?
кардамон
4
@cardamom возвращает запрос sql.
Nuno André
10

Выбранное решение у меня не сработало, так как я все время получал ошибку

AttributeError: объект 'AnnotatedSelect' не имеет атрибута 'lower'

Я обнаружил, что сработало следующее:

df = pd.read_sql_query(query.statement, engine)
jorr45
источник
4

Если вы хотите скомпилировать запрос с параметрами и диалектом конкретных аргументов, используйте что-то вроде этого:

c = query.statement.compile(query.session.bind)
df = pandas.read_sql(c.string, query.session.bind, params=c.params)
Йохан Далин
источник
3
from sqlalchemy import Column, Integer, String, create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

engine = create_engine('postgresql://postgres:postgres@localhost:5432/DB', echo=False)
Base = declarative_base(bind=engine)
Session = sessionmaker(bind=engine)
session = Session()

conn = session.bind

class DailyTrendsTable(Base):

    __tablename__ = 'trends'
    __table_args__ = ({"schema": 'mf_analysis'})

    company_code = Column(DOUBLE_PRECISION, primary_key=True)
    rt_bullish_trending = Column(Integer)
    rt_bearish_trending = Column(Integer)
    rt_bullish_non_trending = Column(Integer)
    rt_bearish_non_trending = Column(Integer)
    gen_date = Column(Date, primary_key=True)

df_query = select([DailyTrendsTable])

df_data = pd.read_sql(rt_daily_query, con = conn)
Акшай Салви
источник
Импорт selectin df_query = select([DailyTrendsTable])отсутствует. from sqlalchemy import select
Карлос Азеведо
0

Для полноты: в качестве альтернативы функции Pandas read_sql_query()вы также можете использовать функцию Pandas-DataFrame from_records()для преобразования файла structured or record ndarray to DataFrame.
Это пригодится, если вы, например, уже выполнили запрос в SQLAlchemy и у вас уже есть результаты:

import pandas as pd 
from sqlalchemy import Column, Integer, String, create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import scoped_session, sessionmaker


SQLALCHEMY_DATABASE_URI = 'postgresql://postgres:postgres@localhost:5432/my_database'
engine = create_engine(SQLALCHEMY_DATABASE_URI, pool_pre_ping=True, echo=False)
db = scoped_session(sessionmaker(autocommit=False, autoflush=False, bind=engine))
Base = declarative_base(bind=engine)


class Currency(Base):
    """The `Currency`-table"""
    __tablename__ = "currency"
    __table_args__ = {"schema": "data"}

    id = Column(Integer, primary_key=True, nullable=False)
    name = Column(String(64), nullable=False)


# Defining the SQLAlchemy-query
currency_query = db.query(Currency).with_entities(Currency.id, Currency.name)

# Getting all the entries via SQLAlchemy
currencies = currency_query.all()

# We provide also the (alternate) column names and set the index here,
# renaming the column `id` to `currency__id`
df_from_records = pd.DataFrame.from_records(currencies
    , index='currency__id'
    , columns=['currency__id', 'name'])
print(df_from_records.head(5))

# Or getting the entries via Pandas instead of SQLAlchemy using the
# aforementioned function `read_sql_query()`. We can set the index-columns here as well
df_from_query = pd.read_sql_query(currency_query.statement, db.bind, index_col='id')
# Renaming the index-column(s) from `id` to `currency__id` needs another statement
df_from_query.index.rename(name='currency__id', inplace=True)
print(df_from_query.head(5))
тафта
источник