从MySQL加载数字数据到python / pandas / numpy数组的最快方法

时间:2014-03-04 13:54:01

标签: python mysql numpy pandas mysql-python

我想从MySQL表中读取一些数字(double,即float64)数据。数据大小约为200k行。

MATLAB参考:

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

经过的时间约为1秒。

在python中做同样的事情,使用这里找到的几个例子(我已经尝试了所有这些,即使用pandas read_frame,frame_query和__processCursor函数): How to convert SQL Query result to PANDAS Data Structure?

参考python代码:

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

需要约6秒。 Profiler说所有花费的时间都在read_frame中。 我想知道是否有人能给我一些提示如何加速至少匹配MATLAB代码。如果在python中完全可以的话。

修改

瓶颈似乎在pyodbc库中的cursor.execute(在pymysql库中)或cursor.fetchall()内。最慢的部分是按元素(逐行,逐列)读取返回的MySQL数据元素,并将其转换为先前由同一个库推断的数据类型。

到目前为止,我已经设法通过这个非常脏的解决方案来加速这一点接近MATLAB:

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

上面的cur.execute不是pymysql EXECUTE!我在文件“connections.py”中修改了它。首先,函数def _read_rowdata_packet现在代替:

rows.append(self._read_row_from_packet(packet))

替换为

self._read_string_from_packet(rows,packet)

这里_read_string_from_packet是_read_row_from_packet的简化版本,代码如下:

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

这是一个超级便宜的解决方案,可以将速度从6秒降低到2.5秒。我想知道,如果通过使用不同的库/传递一些参数可以以某种方式避免所有这些?

因此,解决方案是将整个MySQL回复批量读取到字符串列表,然后批量转换为数值数据类型,而不是逐个元素地执行。 python中是否存在类似的内容?

2 个答案:

答案 0 :(得分:9)

“问题”似乎是从MySQL的十进制类型到python的decimal.Decimal发生的类型转换,MySQLdb,pymysql和pyodbc对数据做了。通过更改MySQLdb中的converters.py文件(在最后一行)来:

conversions[FIELD_TYPE.DECIMAL] = float
conversions[FIELD_TYPE.NEWDECIMAL] = float

而不是decimal.Decimal似乎完全解决了问题,现在是以下代码:

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)

运行不到一秒钟! 有趣的是,decimal.Decimal似乎从未成为探查器中的问题。

类似的解决方案应该在pymysql包中起作用。 pyodbc更棘手:它全部用C ++编写,因此你必须重新编译整个包。

<强>更新

这是一个不需要修改MySQLdb源代码的解决方案: Python MySQLdb returns datetime.date and decimal 然后解决方案将数值数据加载到pandas:

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)

在加载200k x 9表格时,将MATLAB击败~4倍!

答案 1 :(得分:4)

还可以使用turbodbc包检查这种做事方式。要将结果集转换为NumPy数组的OrderedDict,只需执行以下操作:

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

将这些结果转换为数据集应该需要几毫秒的时间。我不知道MySQL的加速,但我已经看到其他数据库的因子10。

加速主要通过使用批量操作而不是行式操作来实现。