16

私は MySQLdb と Python を使用しています。次のようないくつかの基本的なクエリがあります。

c=db.cursor()
c.execute("SELECT id, rating from video")
results = c.fetchall()

「結果」を NumPy 配列にする必要があり、メモリ消費量を節約したいと考えています。行ごとにデータをコピーするのは非常に効率が悪いようです (2 倍のメモリが必要になります)。MySQLdb クエリの結果を NumPy 配列形式に変換するより良い方法はありますか?

私が NumPy 配列形式を使用しようとしている理由は、データを簡単にスライス アンド ダイスできるようにしたいからです。その点で、Python は多次元配列に非常に適しているとは思えません。

e.g. b = a[a[:,2]==1] 

ありがとう!

4

3 に答える 3

24

このソリューションは Kieth のfromiter手法を使用しますが、SQL 結果の 2 次元テーブル構造をより直感的に処理します。また、Python データ型のすべての再形成と平坦化を回避することで Doug の方法を改善します。構造化された配列を使用すると、MySQL の結果からほとんど直接 numpy に読み取ることができ、Python のデータ型をほぼ完全に切り取ることができます。「ほぼ」と言ったのは、fetchallイテレーターがまだ Python タプルを生成しているからです。

ただし、注意点が 1 つありますが、大したことではありません。列のデータ型と行数を事前に知っておく必要があります。

おそらくクエリが何であるかを知っているので、列の型を知ることは明らかなはずです。

行数を知るということは、クライアント側のカーソルを使用する必要があることを意味します (これがデフォルトです)。MySQLdb と MySQL クライアント ライブラリの内部については十分に理解していませんが、クライアント側のカーソルを使用すると、結果全体がクライアント側のメモリにフェッチされると理解していますが、実際にはバッファリングとキャッシュが関係していると思われます。これは、カーソルのコピーに 1 回、配列のコピーに 1 回、結果に 2 つのメモリを使用することを意味するため、結果セットが大きい場合は、できるだけ早くカーソルを閉じてメモリを解放することをお勧めします。

厳密に言えば、事前に行数を指定する必要はありませんが、そうすると、配列メモリが事前に一度割り当てられ、巨大なパフォーマンスの向上。

それで、いくつかのコード

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

列のデータ型と列名を指定する方法については、dtype の numpy ドキュメントと構造化配列に関する上記のリンクを参照してください。

于 2013-08-15T17:26:50.460 に答える
16

このfetchallメソッドは実際にはイテレーターを返し、numpyにはinteratorから配列を初期化するためのfromiterメソッドがあります。したがって、テーブルにあるデータに応じて、2つを簡単に組み合わせるか、アダプタージェネレーターを使用できます。

于 2011-08-15T05:49:01.690 に答える
6

ここでは、 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)

fromiterは1D NumPY 配列を返すことに注意してください。

(もちろん、fromiterを使用して、 countのパラメーターを渡すことにより、単一の MySQL テーブル行の一部だけを返すことができるため、これは理にかなっています)。

それでも、2D 形状を復元する必要があるため、述語はカーソル メソッドrowcountを呼び出します。そして、最後の行でreshapeを呼び出します。

最後に、パラメータ数のデフォルトの引数は「-1」で、イテラブル全体を取得するだけです

于 2011-08-15T06:51:21.917 に答える