Как построить 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])["_"]
Другие вопросы по тегам