Какой самый эффективный способ преобразования результата MySQL в массив NumPy?

Я использую MySQLdb и Python. У меня есть некоторые основные запросы, такие как:

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

Мне нужны "результаты" как массив NumPy, и я считаю экономичным использование памяти. Кажется, что копирование данных по строкам было бы невероятно неэффективным (в два раза потребуется память). Есть ли лучший способ преобразования результатов запроса MySQLdb в формат массива NumPy?

Причина, по которой я хочу использовать формат массива NumPy, заключается в том, что я хочу, чтобы я мог легко срезать и кубировать данные, и это не похоже на то, что python очень дружелюбен к многомерным массивам в этом отношении.

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

Спасибо!

Ответ 1

Метод fetchall фактически возвращает итератор, а numpy имеет метод fromiter для инициализации массива из интернатора. Таким образом, в зависимости от того, какие данные находятся в таблице, вы можете легко объединить эти два или использовать генератор адаптера.

Ответ 2

Это решение использует метод Kieth fromiter, но более интуитивно обрабатывает структуру двумерных табличных данных SQL. Кроме того, он улучшает метод Doug, избегая всех изменений и сглаживания в типах данных python. Используя структурированный массив, мы можем читать почти сразу из результата MySQL в numpy, почти полностью вырезая типы данных python. Я говорю "почти", потому что итератор fetchall все еще производит кортежи python.

Однако есть одно предостережение, но это не biggie. Вы должны знать тип данных своих столбцов и количество строк заранее.

Знание типов столбцов должно быть очевидным, так как вы знаете, что такое запрос, предположительно, иначе вы всегда можете использовать 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.fecthall() is the iterator as per Kieth 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 a field not column)
ratings = A['f1'] #ratings is an array of the second colum

См. документацию numpy для dtype и ссылку выше о структурированных массивах для указания типов данных столбцов и имен столбцов.

Ответ 3

Метод NumPy fromiter кажется лучшим здесь (как в ответе Кита, который предшествовал этому).

С помощью ofiter для преобразования набора результатов, возвращенного вызовом метода курсора 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 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 для возврата только части одной строки таблицы MySQL, передав параметр для count).

Тем не менее, вам придется восстановить 2D-форму, следовательно, предикатный вызов метода rowcount метода курсора. и последующий вызов для изменения в последней строке.

Наконец, аргумент по умолчанию для параметра count равен '-1', который просто извлекает весь итерируемый