Comment convertir le résultat de la requête SQL en structure de données PANDAS?

116

Toute aide sur ce problème sera grandement appréciée.

Donc, fondamentalement, je veux exécuter une requête sur ma base de données SQL et stocker les données renvoyées sous forme de structure de données Pandas.

J'ai joint le code pour la requête.

Je lis la documentation sur Pandas, mais j'ai du mal à identifier le type de retour de ma requête.

J'ai essayé d'imprimer le résultat de la requête, mais cela ne donne aucune information utile.

Merci!!!!

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)

Donc je veux en quelque sorte comprendre quel est le format / type de données de ma variable "resoverall" et comment la mettre avec la structure de données PANDAS.

user1613017
la source
Fondamentalement, quelle est la structure / le type de variable «resoverall» et comment la convertir en structure de données Pandas.
user1613017
Pandas semble assez intéressant, je n'en avais pas entendu parler auparavant, mais cette question n'a guère de sens. Pouvez-vous essayer de clarifier ce que vous entendez par «ne donne aucune information utile»?
tadman
1
Parce que la requête que j'ai exécutée donne un retour, je me demande simplement comment manipuler ce retour et en faire une structure de données pandas. Je suis très nouveau en python et je n'ai donc pas beaucoup de connaissances, comme ce que nous faisons en PHP est juste de faire un sql_fetch_array et nous avons des données "utilisables". =)
user1613017

Réponses:

120

Voici le code le plus court qui fera le travail:

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

Vous pouvez aller plus loin et analyser les types comme dans la réponse de Paul.

Daniel Velkov
la source
1
Cela a fonctionné pour moi pour 1.000.000 enregistrements provenant d'une base de données Oracle.
Erdem KAYA
8
df = DataFrame(cursor.fetchall())renvoie ValueError: DataFrame constructor not properly called!, il semble que le tuple de tuples n'est pas acceptable pour le constructeur DataFrame. Il n'y a pas non plus .keys()de curseur sur en mode dictionnaire ou en tuple.
Mobigital
3
Notez simplement que la méthode keys ne fonctionnera qu'avec les résultats obtenus à l'aide de sqlalchemy. Pyodbc utilise l'attribut description pour les colonnes.
Filip
Cela peut-il fonctionner pour les bases de données Postgres? J'essaye d'obtenir des noms de colonne pour le dataframe de résultat avec la keys()fonction mais je n'arrive pas à le faire fonctionner.
Bowen Liu
1
@BowenLiu Oui, vous pouvez utiliser avec psycopg2df.columns=[ x.name for x in recoverall.description ]
Gnudiff le
137

Edit: mars 2015

Comme indiqué ci-dessous, pandas utilise maintenant SQLAlchemy pour lire à partir de ( read_sql ) et insérer dans ( to_sql ) une base de données. Ce qui suit devrait fonctionner

import pandas as pd

df = pd.read_sql(sql, cnxn)

Réponse précédente: Via mikebmassey d'une question similaire

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()
barbe
la source
Cela semble être la meilleure façon de le faire, car vous n'avez pas besoin d'utiliser manuellement .keys () pour obtenir l'index de la colonne. La réponse de Daniel a probablement été écrite avant que cette méthode n'existe. Vous pouvez également utiliser pandas.io.sql.read_frame ()
RobinL
1
@openwonk où serait implémenté pd.read_sql()dans l'extrait de code ci-dessus?
3kstc
En fait, depuis ma dernière réponse, j'ai utilisé pyodbcet pandasensemble pas mal. Ajout d'une nouvelle réponse avec exemple, FYI.
openwonk
33

Si vous utilisez l'ORM de SQLAlchemy plutôt que le langage d'expression, vous voudrez peut-être convertir un objet de type sqlalchemy.orm.query.Query en bloc de données Pandas.

L'approche la plus propre consiste à obtenir le SQL généré à partir de l'attribut d'instruction de la requête, puis à l'exécuter avec la read_sql()méthode de pandas . Par exemple, en commençant par un objet Query appelé query:

df = pd.read_sql(query.statement, query.session.bind)
Nathan Gould
la source
5
Une approche plus efficace consiste à obtenir l'instruction de sqlalchemy et à laisser les pandas faire la requête eux-mêmes pandas.read_sql_query, en lui passant query.statement. Voir cette réponse: stackoverflow.com/a/29528804/1273938
LeoRochael
Merci @LeoRochael! J'ai édité ma réponse. Vraiment plus propre!
Nathan Gould
23

Modifier le 30/09/2014:

pandas a maintenant une read_sqlfonction. Vous voulez certainement l'utiliser à la place.

Réponse originale:

Je ne peux pas vous aider avec SQLAlchemy - J'utilise toujours pyodbc, MySQLdb ou psychopg2 selon les besoins. Mais ce faisant, une fonction aussi simple que celle ci-dessous a tendance à répondre à mes besoins:

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
la source
Je pense que vous devez importer des décimales quelque part en haut?
joefromct
@joefromct Peut-être, mais cette réponse est tellement obsolète que je devrais vraiment frapper le tout et montrer les méthodes pandas.
Paul H
Cela peut être pertinent pour certains ... la raison pour laquelle j'étudiais cela était à cause de mon autre problème, en utilisant read_sql () ici stackoverflow.com/questions/32847246
...
C'est pertinent pour ceux qui ne peuvent pas utiliser SQLAlchemy qui ne prend pas en charge toutes les bases de données.
lamecicle
@lamecicle n'est pas du tout d'accord. IIRC, read_sqlpeut toujours accepter les connexions non-SQLAlchemy via, par exemple, pyodbc, psychopg2, etc.
Paul H
16

Connecteur MySQL

Pour ceux qui fonctionnent avec le connecteur mysql, vous pouvez utiliser ce code comme point de départ. (Merci à @Daniel Velkov)

Réfs utilisés:


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
la source
9

Voici le code que j'utilise. J'espère que cela t'aides.

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
la source
9

Voici une réponse courte et claire à votre problème:

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
la source
9

1. Utilisation de 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. Utilisation de 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
la source
réponse simple et excellente!
Lucas Aimaretto
5

Comme Nathan, je souhaite souvent vider les résultats d'une requête sqlalchemy ou sqlsoup dans une trame de données Pandas. Ma propre solution pour cela est:

query = session.query(tbl.Field1, tbl.Field2)
DataFrame(query.all(), columns=[column['name'] for column in query.column_descriptions])
Janak Mayer
la source
1
Si vous avez un objet de requête. Il est plus efficace d'obtenir l'instruction de sqlalchemy et de laisser les pandas faire la requête eux-mêmes pandas.read_sql_query, en lui passant query.statement. Voir cette réponse: stackoverflow.com/a/29528804/1273938
LeoRochael
4

resoverallest un objet sqlalchemy ResultProxy. Vous pouvez en savoir plus à ce sujet dans la documentation sqlalchemy , cette dernière explique l'utilisation de base du travail avec les moteurs et les connexions. L'important ici est queresoverall c'est dict comme.

Les pandas aiment les objets comme des dict pour créer leurs structures de données, voir la documentation en ligne

Bonne chance avec sqlalchemy et pandas.

Wouter Overmeire
la source
4

Utilisez simplement pandaset pyodbcensemble. Vous devrez modifier votre chaîne de connexion ( connstr) en fonction des spécifications de votre base de données.

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))

J'ai utilisé pyodbcavec plusieurs bases de données d'entreprise (par exemple SQL Server, MySQL, MariaDB, IBM).

openwonk
la source
Comment réécrire cette dataframe sur MSSQL en utilisant Pyodbc? Otherthan using sqlalchemy
Ramsey
Utilisez la to_sqlméthode sur l' DataFrameobjet. Cette méthode utilise par défaut SQLite, vous devez donc lui transmettre explicitement un objet pointant vers la base de données MSSQL. Voir la documentation .
openwonk
J'ai essayé celui ci-dessous et j'ai environ 200K lignes avec 13 colonnes. Il n'est pas terminé après 15 minutes également. Des idées? df.to_sql ('tablename', engine, schema = 'schemaname', if_exists = 'append', index = False)
Ramsey
Cela semble lent ... J'aurais probablement besoin de voir tout le code en action, désolé. J'aurais aimé pandasêtre plus optimisé pour le travail ETL léger, mais hélas ...
openwonk
3

Cette question est ancienne, mais je voulais ajouter mes deux cents. J'ai lu la question comme "Je veux exécuter une requête sur ma [ma] base de données SQL et stocker les données renvoyées sous forme de structure de données Pandas [DataFrame]".

D'après le code, il semble que vous vouliez dire la base de données mysql et que vous vouliez dire 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)

Par exemple,

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

Cela importera toutes les lignes de testTable dans un DataFrame.

Guppys à bulles
la source
1

Voici le mien. Juste au cas où vous utilisez "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
la source
1

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

Devrait changer pour utiliser pandas.DataFrame.to_sql https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.to_sql.html

Il existe une autre solution. PYODBC vers Pandas - DataFrame ne fonctionne pas - La forme des valeurs transmises est (x, y), les indices impliquent (w, z)

Depuis Pandas 0.12 (je crois), vous pouvez faire:

import pandas
import pyodbc

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

data = pandas.read_sql(sql, cnn)

Avant la version 0.12, vous pouviez faire:

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

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

data = read_frame(sql, cnn)
江明哲
la source
C'est de loin le moyen le plus simple
Wilmer E. Henao
0

Longtemps depuis le dernier message, mais peut-être que cela aide quelqu'un ...

Chemin court que Paul H:

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

meilleure façon de faire ça

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

Si le type de résultat est ResultSet , vous devez d'abord le convertir en dictionnaire. Ensuite, les colonnes DataFrame seront collectées automatiquement.

Cela fonctionne sur mon cas:

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