2014-03-04 8 views
10

Ich möchte einige numerische (Double, d. H. Float64) Daten aus einer MySQL-Tabelle lesen. Die Größe der Daten beträgt ~ 200.000 Zeilen.Schnellste Möglichkeit zum Laden numerischer Daten in Python/Pandas/Numpy Array von MySQL

MATLAB Referenz:

tic; 
feature accel off; 
conn = database(...); 
c=fetch(exec(conn,'select x,y from TABLENAME')); 
cell2mat(c.data); 
toc 

verstrichene Zeit ~ 1 Sekunde.

tut das gleiche in Python, die verschiedene Beispiele in hier (ich habe versucht, sie alle, das heißt mit Pandas read_frame, frame_query und der __processCursor-Funktion): How to convert SQL Query result to PANDAS Data Structure?

Referenz Python-Code:

import pyodbc 
import pandas.io.sql as psql 
import pandas 
connection_info = "DRIVER={MySQL ODBC 3.51 \ 
Driver};SERVER=;DATABASE=;USER=;PASSWORD=;OPTION=3;" 
cnxn = pyodbc.connect(connection_info) 
cursor = cnxn.cursor() 
sql = "select x,y from TABLENAME" 
#cursor.execute(sql) 
#dataframe = __processCursor(cursor, dataframe=True) 
#df = psql.frame_query(sql, cnxn, coerce_float=False) 
df = psql.read_frame(sql, cnxn) 
cnxn.close() 

dauert ~ 6 Sekunden. Profiler sagt, dass die gesamte Zeit in read_frame verbracht wurde. Ich fragte mich, ob jemand mir ein paar Hinweise geben könnte, wie schnell der Matlab-Code zumindest noch schneller sein könnte. Und wenn das bei Python überhaupt möglich ist.

EDIT:

Der Engpass scheint innerhalb der cursor.execute (in pymysql Bibliothek) oder cursor.fetchall() in Pyodbc Bibliothek. Der langsamste Teil liest die zurückgegebenen MySQL-Daten Element für Element (Zeile für Zeile, Spalte für Spalte) und konvertiert sie in den Datentyp, den sie zuvor von derselben Bibliothek abgeleitet hat.

Bisher habe ich es geschafft, dies zu beschleunigen, um MATLAB zu schließen, indem diese wirklich schmutzige Lösung tun:

import pymysql 
import numpy 

conn = pymysql.connect(host='', port=, user='', passwd='', db='') 
cursor = conn.cursor() 
cursor.execute("select x,y from TABLENAME") 
rez = cursor.fetchall() 
resarray = numpy.array(map(float,rez)) 
finalres = resarray.reshape((resarray.size/2,2)) 

Die obige cur.execute DIE pymysql NICHT AUSFÜHREN! Ich habe es geändert, in der Datei "connections.py". Erstens hat die Funktion def _read_rowdata_packet, jetzt statt:

rows.append(self._read_row_from_packet(packet)) 

mit

self._read_string_from_packet(rows,packet) 

Hier _read_string_from_packet ersetzt ist eine vereinfachte Version von _read_row_from_packet mit dem Code:

def _read_string_from_packet(self, rows, packet): 
    for field in self.fields: 
     data = packet.read_length_coded_string() 
     rows.append(data) 

Dies ist ein Über schmutzige Lösung, die eine Beschleunigung von 6 Sekunden bis 2,5 Sekunden ermöglicht. Ich habe mich gefragt, ob all das irgendwie vermieden werden könnte, wenn man eine andere Bibliothek benutzt/einige Parameter übergibt?

Die Lösung wäre also, die gesamte MySQL-Antwort in einer Liste von Strings zu lesen und dann die Konvertierung in numerische Datentypen zu ändern, anstatt dieses elementweise zu machen. Gibt es sowas schon in Python?

+1

Antworten auf http://stackoverflow.com/questions/7061824/whats-the-most-efficient-way-to-convert-a-mysql-result-set-to-a-numpy-array vorschlagen 'fetchall mit 'gefolgt von' np.fromiter' (mit einer möglichen Umformung). – hpaulj

+1

Danke hpaulj, ich habe es getestet - gleiche (langsame) Geschwindigkeit wie andere Methoden. Das Problem besteht meines Erachtens darin, die Daten aus dem Netzwerk in einen nativen Python-Datentyp zu importieren. Daher tritt dies sowohl in MySQLdb als auch in pymysql in der cursor.execute() auf, was, wenn ich es richtig verstehe, eine falsche Implementierung ist, da nichts in native Datentypen nach dem Ausführen einer SQL-Anweisung "geholt" werden sollte. –

Antwort

9

Das "Problem" scheint die Typumwandlung gewesen zu sein, die von MySQLs Dezimaltyp zu Pythons Dezimalzahl vorkommt.Dezimal, dass MySQLdb, pymysql und pyodbc auf die Daten wirken. Durch die Änderung der Datei converter.py (in den letzten Zeilen) in MySQLdb zu haben:

statt dezimal.Dezimal scheint das Problem vollständig zu lösen und jetzt der folgende Code:

import MySQLdb 
import numpy 
import time 

t = time.time() 
conn = MySQLdb.connect(host='',...) 
curs = conn.cursor() 
curs.execute("select x,y from TABLENAME") 
data = numpy.array(curs.fetchall(),dtype=float) 
print(time.time()-t) 

Läuft in weniger als einer Sekunde! Was lustig ist, dezimal.Dezimal schien nie das Problem im Profiler zu sein.

Ähnliche Lösung sollte in pymysql-Paket funktionieren. pyodbc ist komplizierter: es ist alles in C++ geschrieben, daher müssten Sie das gesamte Paket neu kompilieren.

UPDATE

Hier ist eine Lösung nicht die MySQLdb Quellcode zu ändern erfordert: Python MySQLdb returns datetime.date and decimal Die Lösung wird dann numerische Daten in Pandas zu laden:

import MySQLdb 
import pandas.io.sql as psql 
from MySQLdb.converters import conversions 
from MySQLdb.constants import FIELD_TYPE 

conversions[FIELD_TYPE.DECIMAL] = float 
conversions[FIELD_TYPE.NEWDECIMAL] = float 
conn = MySQLdb.connect(host='',user='',passwd='',db='') 
sql = "select * from NUMERICTABLE" 
df = psql.read_frame(sql, conn) 

Beats MATLAB um den Faktor ~ 4 beim Laden 200k x 9 Tisch!

+3

Ich vermute, Ihre Felder sind als DECIMAL (oder NUMERIC) definiert, nicht FLOAT oder DOUBLE. Konverter verwendet 'float' als Standard, es sei denn,' decimal' ist verfügbar. 'dezimal' ist reine Python also wäre langsam. Der Profiler sieht die Konverteraufrufe möglicherweise nicht, da sie von kompiliertem Code in '_mysql' ausgeführt werden. – hpaulj

4

Überprüfen Sie auch diese Vorgehensweise mit dem turbodbc Paket. Um Ihr Ergebnis Transformationssatz in eine OrderedDict von NumPy Arrays, tun gerade dies:

import turbodbc 
connection = turbodbc.connect(dsn="My data source name") 
cursor = connection.cursor() 
cursor.execute("SELECT 42") 
results = cursor.fetchallnumpy() 

diese Ergebnisse zu einem Datensatz Transforming sollten einige zusätzliche Millisekunden benötigen. Ich kenne die Beschleunigung für MySQL nicht, aber ich habe Faktor 10 für andere Datenbanken gesehen.

Die Beschleunigung wird hauptsächlich durch Verwendung von Massenoperationen anstelle von zeilenweisen Operationen erreicht.

Verwandte Themen