the*_*att 14 python etl numpy mysql-python
我正在使用MySQLdb和Python.我有一些基本的查询,例如:
c=db.cursor()
c.execute("SELECT id, rating from video")
results = c.fetchall()
Run Code Online (Sandbox Code Playgroud)
我需要"结果"作为NumPy数组,而且我希望我的内存消耗更经济.似乎逐行复制数据会非常低效(需要双倍的内存).有没有更好的方法将MySQLdb查询结果转换为NumPy数组格式?
我想要使用NumPy数组格式的原因是因为我希望能够轻松地对数据进行切片和切块,并且在这方面看起来python对于多维数组似乎并不友好.
e.g. b = a[a[:,2]==1]
Run Code Online (Sandbox Code Playgroud)
谢谢!
sir*_*ark 23
该解决方案使用Kieth的fromiter技术,但更直观地处理SQL结果的二维表结构.此外,它通过避免python数据类型中的所有重新整形和展平来改进Doug的方法.使用结构化数组,我们可以直接从MySQL结果读取到numpy,几乎完全删除python数据类型.我说"差不多"因为fetchall迭代器仍然会产生python元组.
但有一点需要注意,但这不是一个大问题.您必须事先知道列的数据类型和行数.
知道列类型应该是显而易见的,因为你可能知道查询是什么,否则你总是可以使用curs.description,以及MySQLdb.FIELD_TYPE.*常量的映射.
知道行数意味着您必须使用客户端游标(这是默认值).我对MySQLdb和MySQL客户端库的内部结构还不太了解,但我的理解是,当使用客户端游标时,整个结果被提取到客户端内存中,尽管我怀疑实际上存在一些缓冲和缓存.这意味着为结果使用双内存,一次用于光标复制,一次用于数组复制,因此如果结果集很大,最好关闭光标以释放内存.
严格来说,您不必提前提供行数,但这样做意味着数组内存预先分配一次,并且不会随着更多行从迭代器进入而不断调整大小,这意味着提供巨大的性能提升.
有了这个,一些代码
import MySQLdb
import numpy
conn = MySQLdb.connect(host='localhost', user='bob', passwd='mypasswd', db='bigdb')
curs = conn.cursor() #Use a client side cursor so you can access curs.rowcount
numrows = curs.execute("SELECT id, rating FROM video")
#curs.fetchall() is the iterator as per Kieth's answer
#count=numrows means advance allocation
#dtype='i4,i4' means two columns, both 4 byte (32 bit) integers
A = numpy.fromiter(curs.fetchall(), count=numrows, dtype=('i4,i4'))
print A #output entire array
ids = A['f0'] #ids = an array of the first column
#(strictly speaking it's a field not column)
ratings = A['f1'] #ratings is an array of the second colum
Run Code Online (Sandbox Code Playgroud)
有关如何指定列数据类型和列名的详细信息,请参阅dtype的numpy文档和上面有关结构化数组的链接.
NumPy的fromiter方法在这里看起来最好(如Keith的回答,在此之前).
使用fromiter重新调整通过调用MySQLdb游标方法返回到NumPy数组的结果集很简单,但是有一些细节可能值得一提.
import numpy as NP
import MySQLdb as SQL
cxn = SQL.connect('localhost', 'some_user', 'their_password', 'db_name')
c = cxn.cursor()
c.execute('SELECT id, ratings from video')
# fetchall() returns a nested tuple (one tuple for each table row)
results = cursor.fetchall()
# 'num_rows' needed to reshape the 1D NumPy array returend by 'fromiter'
# in other words, to restore original dimensions of the results set
num_rows = int(c.rowcount)
# recast this nested tuple to a python list and flatten it so it's a proper iterable:
x = map(list, list(results)) # change the type
x = sum(x, []) # flatten
# D is a 1D NumPy array
D = NP.fromiter(iterable=x, dtype=float, count=-1)
# 'restore' the original dimensions of the result set:
D = D.reshape(num_rows, -1)
Run Code Online (Sandbox Code Playgroud)
请注意,fromiter返回1D NumPY数组,
(当然,这是有道理的,因为您可以通过传递count参数来使用fromiter返回单个MySQL Table行的一部分).
但是,您必须恢复2D形状,因此对游标方法rowcount的谓词调用.以及随后的调用重塑最后一行.
最后,参数count的默认参数是'-1',它只检索整个iterable