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

Posted

技术标签:

【中文标题】从 MySQL 将数值数据加载到 python/pandas/numpy 数组中的最快方法【英文标题】:Fastest way to load numeric data into python/pandas/numpy array from MySQL 【发布时间】:2014-04-06 02:25:55 【问题描述】:

我想从 mysql 表中读取一些数字(双精度,即 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 中是可能的。

编辑:

瓶颈似乎在 cursor.execute (在 pymysql 库中)或 cursor.fetchall() 在 pyodbc 库中。最慢的部分是逐个元素(逐行、逐列)读取返回的 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 执行的! 我已经在文件“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中是否已经存在类似的东西?

【问题讨论】:

***.com/questions/7061824/… 上的答案建议使用fetchall,后跟np.fromiter(可能会进行一些调整)。 感谢 hpaulj,我已经测试过了 - 与其他方法相同(慢)的速度。我认为的问题是将数据从网络导入本机 python 数据类型。因此,在 MySQLdb 和 pymysql 中,这都发生在 cursor.execute() 中,如果我理解正确,这是一个不正确的实现,因为在简单地执行 SQL 语句后,不应将任何内容“提取”到本机数据类型中。 【参考方案1】:

“问题”似乎是 MySQLdb、pymysql 和 pyodbc 对数据所做的从 MySQL 的十进制类型到 python 的十进制类型的类型转换。通过将 MySQLdb 中的 converters.py 文件(在最后几行)更改为:

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

而不是十进制。十进制似乎完全解决了这个问题,现在下面的代码:

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)

运行不到一秒! 有趣的是,十进制。十进制似乎从来都不是分析器中的问题。

类似的解决方案应该在 pymysql 包中工作。 pyodbc 比较棘手:它都是用 C++ 编写的,因此您必须重新编译整个包。

更新

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

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 倍!

【讨论】:

我猜您的字段被定义为 DECIMAL(或 NUMERIC),而不是 FLOAT 或 DOUBLE。转换器使用float 作为默认值,除非decimal 可用。 decimal 是纯 Python,所以会很慢。分析器可能看不到转换器调用,因为它们是由 _mysql 中的编译代码执行的。【参考方案2】:

还可以使用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 倍。

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

【讨论】:

以上是关于从 MySQL 将数值数据加载到 python/pandas/numpy 数组中的最快方法的主要内容,如果未能解决你的问题,请参考以下文章

通过消除重复将数据从文本文件加载到mysql数据库

将数据从 MySQL 加载到 HDFS 时出现 Sqoop 错误

使用查询将数据从 EXCEL/CSV 加载到 MYSQL?

如何从 Mysql Server 将数据加载到仪表板视图?

sqoop export 无法将数据从 hive 仓库文件夹加载到 mysql

将整数值从我的视图发送到控制器以加载 jQuery 数据表