Como converter o resultado da Consulta SQL em Estrutura de Dados PANDAS?

116

Qualquer ajuda neste problema será muito apreciada.

Então, basicamente, eu quero executar uma consulta ao meu banco de dados SQL e armazenar os dados retornados como a estrutura de dados do Pandas.

Anexei o código para consulta.

Estou lendo a documentação do Pandas, mas tenho problemas para identificar o tipo de retorno da minha consulta.

Tentei imprimir o resultado da consulta, mas não dá nenhuma informação útil.

Obrigado!!!!

from sqlalchemy import create_engine

engine2 = create_engine('mysql://THE DATABASE I AM ACCESSING')
connection2 = engine2.connect()
dataid = 1022
resoverall = connection2.execute("
  SELECT 
      sum(BLABLA) AS BLA,
      sum(BLABLABLA2) AS BLABLABLA2,
      sum(SOME_INT) AS SOME_INT,
      sum(SOME_INT2) AS SOME_INT2,
      100*sum(SOME_INT2)/sum(SOME_INT) AS ctr,
      sum(SOME_INT2)/sum(SOME_INT) AS cpc
   FROM daily_report_cooked
   WHERE campaign_id = '%s'", %dataid)

Então, eu meio que quero entender qual é o formato / tipo de dados da minha variável "resoverall" e como colocá-la na estrutura de dados PANDAS.

user1613017
fonte
Basicamente, qual é a estrutura / tipo da variável "resoverall" e como convertê-la na estrutura de dados do Pandas.
user1613017
Pandas parece muito interessante, eu não tinha ouvido falar sobre isso antes, mas essa pergunta mal faz sentido. Você pode tentar esclarecer o que entende por "não fornece nenhuma informação útil"?
tadman de
1
Porque a consulta que executei dá um retorno, apenas me perguntando como devo manipular esse retorno e torná-lo uma estrutura de dados do pandas. Eu sou muito novo em Python e, portanto, não tenho muito conhecimento, como o que fazemos em PHP é apenas fazer um sql_fetch_array e temos dados "utilizáveis". =)
user1613017

Respostas:

120

Este é o código mais curto que fará o trabalho:

from pandas import DataFrame
df = DataFrame(resoverall.fetchall())
df.columns = resoverall.keys()

Você pode ir mais elaborado e analisar os tipos como na resposta de Paulo.

Daniel Velkov
fonte
1
Isso funcionou para mim por 1.000.000 de registros obtidos de um banco de dados Oracle.
Erdem KAYA
8
df = DataFrame(cursor.fetchall())retorna ValueError: DataFrame constructor not properly called!, parece que a tupla de tuplas não é aceitável para o construtor DataFrame. Também não há um .keys()cursor no modo de dicionário ou tupla.
Mobigital de
3
Observe que o método keys só funcionará com resultados obtidos com sqlalchemy. Pyodbc usa o atributo de descrição para colunas.
Filip
Isso pode funcionar para bancos de dados Postgres? Estou tentando obter nomes de colunas para o dataframe de resultado com keys()função, mas não consigo fazê-lo funcionar.
Bowen Liu
1
@BowenLiu Sim, você pode usar com psycopg2df.columns=[ x.name for x in recoverall.description ]
Gnudiff
136

Edição: março de 2015

Conforme observado abaixo, o pandas agora usa SQLAlchemy para ler ( read_sql ) e inserir em ( to_sql ) um banco de dados. O seguinte deve funcionar

import pandas as pd

df = pd.read_sql(sql, cnxn)

Resposta anterior: Via mikebmassey de uma pergunta semelhante

import pyodbc
import pandas.io.sql as psql

cnxn = pyodbc.connect(connection_info) 
cursor = cnxn.cursor()
sql = "SELECT * FROM TABLE"

df = psql.frame_query(sql, cnxn)
cnxn.close()
beardc
fonte
Esta parece ser a melhor maneira de fazer isso, já que você não precisa usar manualmente .keys () para obter o índice da coluna. Provavelmente a resposta de Daniel foi escrita antes que esse método existisse. Você também pode usar pandas.io.sql.read_frame ()
RobinL
1
@openwonk onde implementaria pd.read_sql()no trecho de código acima?
3kstc de
Na verdade, desde minha última resposta, usei pyodbce pandasjunto um pouco. Adicionando nova resposta com exemplo, FYI.
openwonk de
33

Se você estiver usando o ORM do SQLAlchemy em vez da linguagem de expressão, talvez queira converter um objeto do tipo sqlalchemy.orm.query.Queryem um quadro de dados do Pandas.

A abordagem mais limpa é obter o SQL gerado a partir do atributo de instrução da consulta e executá-lo com o read_sql()método do pandas . Por exemplo, começando com um objeto Query chamado query:

df = pd.read_sql(query.statement, query.session.bind)
Nathan Gould
fonte
5
Uma abordagem mais eficiente é obter a instrução de sqlalchemy e deixar que os próprios pandas façam a consulta pandas.read_sql_query, passando query.statementpara ela. Veja esta resposta: stackoverflow.com/a/29528804/1273938
LeoRochael
Obrigado @LeoRochael! Eu editei minha resposta. Definitivamente mais limpo!
Nathan Gould
23

Editar 30/09/2014:

pandas agora tem uma read_sqlfunção. Você definitivamente quer usar isso.

Resposta original:

Não posso ajudá-lo com SQLAlchemy - eu sempre uso pyodbc, MySQLdb ou psychopg2 conforme necessário. Mas, ao fazer isso, uma função tão simples como a abaixo tende a atender às minhas necessidades:

import decimal

import pydobc
import numpy as np
import pandas

cnn, cur = myConnectToDBfunction()
cmd = "SELECT * FROM myTable"
cur.execute(cmd)
dataframe = __processCursor(cur, dataframe=True)

def __processCursor(cur, dataframe=False, index=None):
    '''
    Processes a database cursor with data on it into either
    a structured numpy array or a pandas dataframe.

    input:
    cur - a pyodbc cursor that has just received data
    dataframe - bool. if false, a numpy record array is returned
                if true, return a pandas dataframe
    index - list of column(s) to use as index in a pandas dataframe
    '''
    datatypes = []
    colinfo = cur.description
    for col in colinfo:
        if col[1] == unicode:
            datatypes.append((col[0], 'U%d' % col[3]))
        elif col[1] == str:
            datatypes.append((col[0], 'S%d' % col[3]))
        elif col[1] in [float, decimal.Decimal]:
            datatypes.append((col[0], 'f4'))
        elif col[1] == datetime.datetime:
            datatypes.append((col[0], 'O4'))
        elif col[1] == int:
            datatypes.append((col[0], 'i4'))

    data = []
    for row in cur:
        data.append(tuple(row))

    array = np.array(data, dtype=datatypes)
    if dataframe:
        output = pandas.DataFrame.from_records(array)

        if index is not None:
            output = output.set_index(index)

    else:
        output = array

    return output
Paul H
fonte
Eu acho que você precisa importar decimal em algum lugar no topo?
joefromct
@joefromct Talvez, mas essa resposta é tão obsoleta que eu realmente deveria atacar a coisa toda e mostrar os métodos dos pandas.
Paul H
Pode ser relevante para alguns ... o motivo pelo qual eu estava estudando isso foi por causa do meu outro problema, usando read_sql () aqui stackoverflow.com/questions/32847246/…
joefromct
É relevante para aqueles que não podem usar SQLAlchemy, que não oferece suporte a todos os bancos de dados.
lamecícula
@lamecicle discorda um pouco. IIRC, read_sqlainda pode aceitar conexões não-SQLAlchemy, por exemplo, pyodbc, psychopg2, etc.
Paul H
16

Conector MySQL

Para aqueles que trabalham com o conector mysql, você pode usar este código como um começo. (Obrigado a @Daniel Velkov)

Referências usadas:


import pandas as pd
import mysql.connector

# Setup MySQL connection
db = mysql.connector.connect(
    host="<IP>",              # your host, usually localhost
    user="<USER>",            # your username
    password="<PASS>",        # your password
    database="<DATABASE>"     # name of the data base
)   

# You must create a Cursor object. It will let you execute all the queries you need
cur = db.cursor()

# Use all the SQL you like
cur.execute("SELECT * FROM <TABLE>")

# Put it all to a data frame
sql_data = pd.DataFrame(cur.fetchall())
sql_data.columns = cur.column_names

# Close the session
db.close()

# Show the data
print(sql_data.head())
Thomas Devoogdt
fonte
9

Aqui está o código que uso. Espero que isto ajude.

import pandas as pd
from sqlalchemy import create_engine

def getData():
  # Parameters
  ServerName = "my_server"
  Database = "my_db"
  UserPwd = "user:pwd"
  Driver = "driver=SQL Server Native Client 11.0"

  # Create the connection
  engine = create_engine('mssql+pyodbc://' + UserPwd + '@' + ServerName + '/' + Database + "?" + Driver)

  sql = "select * from mytable"
  df = pd.read_sql(sql, engine)
  return df

df2 = getData()
print(df2)
Murali Bala
fonte
9

Esta é uma resposta curta e nítida para o seu problema:

from __future__ import print_function
import MySQLdb
import numpy as np
import pandas as pd
import xlrd

# Connecting to MySQL Database
connection = MySQLdb.connect(
             host="hostname",
             port=0000,
             user="userID",
             passwd="password",
             db="table_documents",
             charset='utf8'
           )
print(connection)
#getting data from database into a dataframe
sql_for_df = 'select * from tabledata'
df_from_database = pd.read_sql(sql_for_df , connection)
DeshDeep Singh
fonte
8

1. Usando MySQL-connector-python

# pip install mysql-connector-python

import mysql.connector
import pandas as pd

mydb = mysql.connector.connect(
    host = 'host',
    user = 'username',
    passwd = 'pass',
    database = 'db_name'
)
query = 'select * from table_name'
df = pd.read_sql(query, con = mydb)
print(df)

2. Usando SQLAlchemy

# pip install pymysql
# pip install sqlalchemy

import pandas as pd
import sqlalchemy

engine = sqlalchemy.create_engine('mysql+pymysql://username:password@localhost:3306/db_name')

query = '''
select * from table_name
'''
df = pd.read_sql_query(query, engine)
print(df)
Lintang Wisesa
fonte
resposta simples e ótima!
Lucas Aimaretto
5

Como Nathan, geralmente quero despejar os resultados de uma consulta sqlalchemy ou sqlsoup em um quadro de dados do Pandas. Minha própria solução para isso é:

query = session.query(tbl.Field1, tbl.Field2)
DataFrame(query.all(), columns=[column['name'] for column in query.column_descriptions])
Janak Mayer
fonte
1
Se você tiver um objeto de consulta. É mais eficiente obter a instrução de sqlalchemy e permitir que os pandas façam a própria consulta pandas.read_sql_query, passando query.statementpara ela. Veja esta resposta: stackoverflow.com/a/29528804/1273938
LeoRochael
4

resoverallé um objeto sqlalchemy ResultProxy. Você pode ler mais sobre isso nos documentos do sqlalchemy , o último explica o uso básico de trabalhar com motores e conexões. O importante aqui é o que resoverallé dito.

O Pandas gosta de objetos ditados para criar suas estruturas de dados, veja a documentação online

Boa sorte com sqlalchemy e pandas.

Wouter Overmeire
fonte
4

Basta usar pandase pyodbcjuntos. Você terá que modificar sua string de conexão ( connstr) de acordo com as especificações do seu banco de dados.

import pyodbc
import pandas as pd

# MSSQL Connection String Example
connstr = "Server=myServerAddress;Database=myDB;User Id=myUsername;Password=myPass;"

# Query Database and Create DataFrame Using Results
df = pd.read_sql("select * from myTable", pyodbc.connect(connstr))

Eu usei pyodbccom vários bancos de dados corporativos (por exemplo, SQL Server, MySQL, MariaDB, IBM).

openwonk
fonte
Como escrever este dataframe novamente para MSSQL usando Pyodbc? Outro que não usar sqlalchemy
Ramsey
Use o to_sqlmétodo no DataFrameobjeto. Esse método é padronizado como SQLite, então você deve passar explicitamente um objeto apontando para o banco de dados MSSQL. Veja a documentação .
openwonk
Tentei o abaixo e tenho cerca de 200 mil linhas com 13 colunas. Também não é concluído após 15 minutos. Alguma ideia? df.to_sql ('tablename', engine, schema = 'schemaname', if_exists = 'append', index = False)
Ramsey
Isso parece lento ... Eu provavelmente precisaria ver o código inteiro em ação, desculpe. Eu gostaria de pandasestar mais otimizado para trabalhos leves de ETL, mas, infelizmente ...
openwonk
3

Esta pergunta é antiga, mas eu queria adicionar meus dois centavos. Eu li a pergunta como "Eu quero executar uma consulta ao meu [meu] banco de dados SQL e armazenar os dados retornados como estrutura de dados do Pandas [DataFrame]."

Pelo código, parece que você quer dizer banco de dados mysql e assume que quer dizer pandas DataFrame.

import MySQLdb as mdb
import pandas.io.sql as sql
from pandas import *

conn = mdb.connect('<server>','<user>','<pass>','<db>');
df = sql.read_frame('<query>', conn)

Por exemplo,

conn = mdb.connect('localhost','myname','mypass','testdb');
df = sql.read_frame('select * from testTable', conn)

Isso importará todas as linhas de testTable para um DataFrame.

BubbleGuppies
fonte
1

Aqui é minha. Por precaução, se você estiver usando "pymysql":

import pymysql
from pandas import DataFrame

host   = 'localhost'
port   = 3306
user   = 'yourUserName'
passwd = 'yourPassword'
db     = 'yourDatabase'

cnx    = pymysql.connect(host=host, port=port, user=user, passwd=passwd, db=db)
cur    = cnx.cursor()

query  = """ SELECT * FROM yourTable LIMIT 10"""
cur.execute(query)

field_names = [i[0] for i in cur.description]
get_data = [xx for xx in cur]

cur.close()
cnx.close()

df = DataFrame(get_data)
df.columns = field_names
Kennyut
fonte
1

pandas.io.sql.write_frame está DESCONTINUADO. https://pandas.pydata.org/pandas-docs/version/0.15.2/generated/pandas.io.sql.write_frame.html

Deve mudar para usar pandas.DataFrame.to_sql https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.to_sql.html

Existe outra solução. PYODBC para Pandas - DataFrame não funciona - A forma dos valores passados ​​é (x, y), os índices implicam (w, z)

No Pandas 0.12 (eu acredito) você pode fazer:

import pandas
import pyodbc

sql = 'select * from table'
cnn = pyodbc.connect(...)

data = pandas.read_sql(sql, cnn)

Antes do 0.12, você podia fazer:

import pandas
from pandas.io.sql import read_frame
import pyodbc

sql = 'select * from table'
cnn = pyodbc.connect(...)

data = read_frame(sql, cnn)
江明哲
fonte
Esta é de longe a maneira mais fácil
Wilmer E. Henao
0

Muito tempo desde a última postagem, mas talvez ajude alguém ...

Caminho curto do que Paul H:

my_dic = session.query(query.all())
my_df = pandas.DataFrame.from_dict(my_dic)
Antonio Fernandez
fonte
0

melhor maneira que eu faço isso

db.execute(query) where db=db_class() #database class
    mydata=[x for x in db.fetchall()]
    df=pd.DataFrame(data=mydata)
Berto
fonte
0

Se o tipo de resultado for ResultSet , você deve primeiro convertê-lo em dicionário. Em seguida, as colunas DataFrame serão coletadas automaticamente.

Isso funciona no meu caso:

df = pd.DataFrame([dict(r) for r in resoverall])
tanza9
fonte