Как преобразовать результат SQL-запроса в структуру данных PANDAS?


118

Мы будем благодарны за любую помощь по этой проблеме.

Итак, в основном я хочу запустить запрос к моей базе данных SQL и сохранить возвращенные данные в виде структуры данных Pandas.

Я прикрепил код для запроса.

Я читаю документацию по Pandas, но не могу определить тип возвращаемого значения моего запроса.

Я попытался распечатать результат запроса, но он не дает никакой полезной информации.

Спасибо!!!!

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)

Так что я вроде как хочу понять, каков формат / тип данных моей переменной «resoverall» и как поместить ее в структуру данных PANDAS.


В основном, какова структура / тип переменной "resoverall" и как ее преобразовать в структуру данных Pandas.
user1613017

Pandas звучит довольно интересно, я не слышал об этом раньше, но этот вопрос почти не имеет смысла. Не могли бы вы прояснить, что вы имеете в виду под словами «не дает никакой полезной информации»?
tadman

1
Поскольку запрос, который я выполнил, дает возврат, просто интересно, как мне манипулировать этим возвратом и превратить его в структуру данных pandas. Я очень новичок в python и поэтому не имею особых знаний, например, то, что мы делаем в PHP, - это просто sql_fetch_array, и у нас есть «пригодные для использования» данные. =)
user1613017

Ответы:


120

Вот самый короткий код, который выполнит эту работу:

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

Вы можете пойти более изысканно и проанализировать типы, как в ответе Пола.


1
Это сработало для меня для 1.000.000 записей, полученных из базы данных Oracle.
Erdem KAYA

8
df = DataFrame(cursor.fetchall())возвращается ValueError: DataFrame constructor not properly called!, похоже, что кортеж кортежей неприемлем для конструктора DataFrame. Также нет .keys()курсора ни в режиме словаря, ни в режиме кортежа.
Mobigital

3
Обратите внимание, что метод ключей будет работать только с результатами, полученными с помощью sqlalchemy. Pyodbc использует атрибут описания для столбцов.
Филип

Может ли это работать для баз данных Postgres? Я пытаюсь получить имена столбцов для фрейма данных результата с keys()функцией, но не могу заставить его работать.
Боуэн Лю,

1
@BowenLiu Да, вы можете использовать psycopg2df.columns=[ x.name for x in recoverall.description ]
Gnudiff

138

Изменить: март 2015 г.

Как указано ниже, pandas теперь использует SQLAlchemy как для чтения из ( read_sql ), так и для вставки в ( to_sql ) базы данных. Следующее должно работать

import pandas as pd

df = pd.read_sql(sql, cnxn)

Предыдущий ответ: Via mikebmassey из аналогичного вопроса

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

Кажется, это лучший способ сделать это, поскольку вам не нужно вручную использовать .keys () для получения индекса столбца. Вероятно, ответ Даниила был написан до того, как этот метод существовал. Вы также можете использовать pandas.io.sql.read_frame ()
RobinL

1
@openwonk, где бы реализовать pd.read_sql()в приведенном выше фрагменте кода?
3kstc

Собственно, с момента моего последнего ответа я довольно много использовал pyodbcи pandasвместе. Добавление нового ответа с примером, FYI.
openwonk

33

Если вы используете ORM SQLAlchemy, а не язык выражений, вы можете захотеть преобразовать объект типа sqlalchemy.orm.query.Query во фрейм данных Pandas.

Самый чистый подход - получить сгенерированный SQL из атрибута оператора запроса, а затем выполнить его с помощью read_sql()метода pandas . Например, начиная с объекта Query с именем query:

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

5
Более эффективный подход - получить оператор из sqlalchemy и позволить пандам выполнить сам запрос pandas.read_sql_query, передав query.statementему. См. Этот ответ: stackoverflow.com/a/29528804/1273938
LeoRochael,

Спасибо, @LeoRochael! Я отредактировал свой ответ. Определенно чище!
Натан Гулд,

23

Изменить 2014-09-30:

pandas теперь имеет read_sqlфункцию. Вы определенно захотите использовать это вместо этого.

Оригинальный ответ:

Я не могу помочь вам с SQLAlchemy - я всегда использую pyodbc, MySQLdb или psychopg2 по мере необходимости. Но при этом мне подходит такая простая функция, как приведенная ниже:

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

Я думаю, вам нужно импортировать десятичную дробь где-нибудь наверху?
joefrom

@joefromct Возможно, но этот ответ настолько устарел, что я действительно должен просто поразить все это и показать методы панд.
Paul H

Для некоторых это может быть актуально ... причина, по которой я это изучал, была связана с моей другой проблемой, использующей здесь read_sql () stackoverflow.com/questions/32847246/…
joefromct

Это актуально для тех, кто не может использовать SQLAlchemy, который поддерживает не все базы данных.
lamecicle

@lamecicle несколько не согласен. IIRC, read_sqlвсе еще может принимать соединения, отличные от SQLAlchemy, например, через pyodbc, psychopg2 и т. Д.
Пол Х

16

Коннектор MySQL

Для тех, кто работает с соединителем mysql, вы можете использовать этот код в качестве начала. (Спасибо @Daniel Velkov)

Использованные ссылки:


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

9

Вот код, который я использую. Надеюсь это поможет.

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)

9

Это короткий и четкий ответ на вашу проблему:

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)

9

1. Использование 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. Использование 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)

простой и отличный ответ!
Лукас Аймаретто

5

Как и Натан, я часто хочу выгружать результаты sqlalchemy или sqlsoup Query во фрейм данных Pandas. Мое собственное решение для этого:

query = session.query(tbl.Field1, tbl.Field2)
DataFrame(query.all(), columns=[column['name'] for column in query.column_descriptions])

1
Если у вас есть объект запроса. Более эффективно получить оператор из sqlalchemy и позволить пандам выполнять сам запрос pandas.read_sql_query, передавая query.statementему. См. Этот ответ: stackoverflow.com/a/29528804/1273938
LeoRochael,

4

resoverallявляется объектом sqlalchemy ResultProxy. Вы можете узнать больше об этом в документации sqlalchemy , последняя объясняет базовое использование работы с движками и соединениями. Важно здесь то, что resoverallтакое изречение.

Pandas любит dict like objects для создания своих структур данных, см. Онлайн-документацию

Удачи с sqlalchemy и пандами.


4

Просто используйте pandasи pyodbcвместе. Вам нужно будет изменить строку подключения ( connstr) в соответствии со спецификациями вашей базы данных.

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

Я использовал pyodbcнесколько корпоративных баз данных (например, SQL Server, MySQL, MariaDB, IBM).


Как снова записать этот фрейм данных обратно в MSSQL с помощью Pyodbc? Другое, чем использование sqlalchemy
Рэмси

Используйте to_sqlметод на DataFrameобъекте. По умолчанию этот метод использует SQLite, поэтому вам необходимо явно передать ему объект, указывающий на базу данных MSSQL. См. Документы .
openwonk

Я попробовал приведенный ниже, и у меня около 200 тыс. Строк с 13 столбцами. Он также не завершается через 15 минут. Любые идеи? df.to_sql ('tablename', engine, schema = 'schemaname', if_exists = 'append', index = False)
Рэмси

Это кажется медленным ... Мне, наверное, нужно увидеть весь код в действии, извините. Я хотел бы быть pandasболее оптимизированным для легкой работы с ETL, но, увы ...
openwonk

3

Это старый вопрос, но я хотел добавить свои два цента. Я прочитал вопрос как «Я хочу выполнить запрос к моей [моей] базе данных SQL и сохранить возвращенные данные как структуру данных Pandas [DataFrame]».

Из кода похоже, что вы имеете в виду базу данных mysql и предполагаете, что имеете в виду 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)

Например,

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

Это импортирует все строки testTable в DataFrame.


1

Вот мой. На всякий случай, если вы используете 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

1

pandas.io.sql.write_frame УСТАРЕЛ. https://pandas.pydata.org/pandas-docs/version/0.15.2/generated/pandas.io.sql.write_frame.html

Следует изменить на использование pandas.DataFrame.to_sql https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.to_sql.html

Есть другое решение. PYODBC в Pandas - DataFrame не работает - форма переданных значений (x, y), индексы подразумевают (w, z)

Начиная с Pandas 0.12 (я считаю) вы можете:

import pandas
import pyodbc

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

data = pandas.read_sql(sql, cnn)

До версии 0.12 вы могли:

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

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

data = read_frame(sql, cnn)

Это, безусловно, самый простой способ
Уилмер Э. Энао,

0

Прошло много времени с последнего поста, но, возможно, это кому-то поможет ...

Короткий путь, чем у Пола Н:

my_dic = session.query(query.all())
my_df = pandas.DataFrame.from_dict(my_dic)

0

лучший способ сделать это

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

0

Если тип результата - ResultSet , вы должны сначала преобразовать его в словарь. Затем столбцы DataFrame будут собираться автоматически.

Это работает в моем случае:

df = pd.DataFrame([dict(r) for r in resoverall])
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.