Как построить np.array с fromiter
Я пытаюсь построить np.array
путем выборки из генератора Python, который дает одну строку массива за вызов next
, Вот пример кода:
import numpy as np
data = np.eye(9)
labels = np.array([0,0,0,1,1,1,2,2,2])
def extract_one_class(X,labels,y):
""" Take an array of data X, a column vector array of labels, and one particular label y. Return an array of all instances in X that have label y """
return X[np.nonzero(labels[:] == y)[0],:]
def generate_points(data, labels, size):
""" Generate and return 'size' pairs of points drawn from different classes """
label_alphabet = np.unique(labels)
assert(label_alphabet.size > 1)
for useless in xrange(size):
shuffle(label_alphabet)
first_class = extract_one_class(data,labels,label_alphabet[0])
second_class = extract_one_class(data,labels,label_alphabet[1])
pair = np.hstack((first_class[randint(0,first_class.shape[0]),:],second_class[randint(0,second_class.shape[0]),:]))
yield pair
points = np.fromiter(generate_points(data,labels,5),dtype = np.dtype('f8',(2*data.shape[1],1)))
extract_one_class
Функция возвращает подмножество данных: все точки данных, принадлежащие одной метке класса. Я хотел бы, чтобы очки были np.array
с shape = (size,data.shape[1])
, В настоящее время приведенный выше фрагмент кода возвращает ошибку:
ValueError: setting an array element with a sequence.
Документация fromiter
утверждает, что возвращает одномерный массив. Третьи уже использовали fromiter для создания массивов записей в numpy ранее (например, http://iam.al/post/21116450281/numpy-is-my-homeboy).
Могу ли я предположить, что могу создать массив таким способом? Или моя няня просто не совсем права?
3 ответа
Вы можете изменить generate_points
чтобы получить одиночные поплавки вместо np.arrays, используйте np.fromiter
сформировать одномерный массив, а затем использовать .reshape(size, -1)
чтобы сделать это 2D массив.
points = np.fromiter(
generate_points(data,labels,5)).reshape(size, -1)
Как вы заметили, документация np.fromiter
объясняет, что функция создает одномерный массив. Вы не сможете создать двумерный массив таким способом, и метод @unutbu для возврата одномерного массива, который вы впоследствии изменили, является верным способом.
Тем не менее, вы действительно можете создавать структурированные массивы, используя fromiter
, как показано:
>>> import itertools
>>> a = itertools.izip((1,2,3),(10,20,30))
>>> r = np.fromiter(a,dtype=[('',int),('',int)])
array([(1, 10), (2, 20), (3, 30)],
dtype=[('f0', '<i8'), ('f1', '<i8')])
но посмотри, r.shape=(3,)
, то есть, r
на самом деле это всего лишь 1D массив записей, каждая запись состоит из двух целых чисел. Потому что все поля имеют одинаковые dtype
мы можем взглянуть на r
как 2D массив
>>> r.view((int,2))
array([[ 1, 10],
[ 2, 20],
[ 3, 30]])
Итак, да, вы можете попробовать использовать np.fromiter
с dtype
лайк [('',int)]*data.shape[1]
: вы получите одномерный массив длины size
, что вы можете просмотреть этот массив как ((int, data.shape[1]))
, Вы можете использовать плавающие вместо целых, важная часть состоит в том, что все поля имеют одинаковый тип d.
Если вы действительно этого хотите, вы можете использовать довольно сложные dtype
, Рассмотрим для примера
r = np.fromiter(((_,) for _ in a),dtype=[('',(int,2))])
Здесь вы получаете одномерный структурированный массив с 1 полем, поле, состоящее из массива из 2 целых чисел. Обратите внимание на использование (_,)
чтобы убедиться, что каждая запись передается как кортеж (иначе np.fromiter
дроссели). Но вам нужна эта сложность?
Обратите внимание также, что, как вы знаете, длину массива заранее (это size
), вы должны использовать counter
необязательный аргумент np.fromiter
для большей эффективности.
Следуя некоторым предложениям здесь, я придумал довольно общую замену для numpy.fromiter()
что удовлетворяет требованиям ФП:
import numpy as np
def fromiter(iterator, dtype, *shape):
"""Generalises `numpy.fromiter()` to multi-dimesional arrays.
Instead of the number of elements, the parameter `shape` has to be given,
which contains the shape of the output array. The first dimension may be
`-1`, in which case it is inferred from the iterator.
"""
res_shape = shape[1:]
if not res_shape: # Fallback to the "normal" fromiter in the 1-D case
return np.fromiter(iterator, dtype, shape[0])
# This wrapping of the iterator is necessary because when used with the
# field trick, np.fromiter does not enforce consistency of the shapes
# returned with the '_' field and silently cuts additional elements.
def shape_checker(iterator, res_shape):
for value in iterator:
if value.shape != res_shape:
raise ValueError("shape of returned object %s does not match"
" given shape %s" % (value.shape, res_shape))
yield value,
return np.fromiter(shape_checker(iterator, res_shape),
[("_", dtype, res_shape)], shape[0])["_"]