питонический способ агрегирования массивов (numpy или нет)

StackOverflow https://stackoverflow.com/questions/1829340

Вопрос

Я хотел бы создать приятную функцию для объединения данных в массив (это массив записей numpy, но это ничего не меняет)

у вас есть массив данных, которые вы хотите объединить по одной оси:например, массив из dtype=[(name, (np.str_,8), (job, (np.str_,8), (income, np.uint32)] и вы хотите иметь средний доход на одну работу

Я выполнил эту функцию, и в приведенном примере она должна вызываться как aggregate(data,'job','income',mean)


def aggregate(data, key, value, func):

    data_per_key = {}

    for k,v in zip(data[key], data[value]):

        if k not in data_per_key.keys():

            data_per_key[k]=[]

        data_per_key[k].append(v)

    return [(k,func(data_per_key[k])) for k in data_per_key.keys()]

проблема в том, что я нахожу это не очень приятным, я бы хотел, чтобы это было в одной строке:у вас есть какие-нибудь идеи?

Спасибо за ваш ответ, Луис

PS:Я хотел бы сохранить функцию в вызове, чтобы вы также могли запросить медиану, минимум...

Это было полезно?

Решение

Возможно, функция, которую вы ищете, это matplotlib.mlab.rec_groupby матплотлиб.mlab.rec_groupby:

import matplotlib.mlab

data=np.array(
    [('Aaron','Digger',1),
     ('Bill','Planter',2),
     ('Carl','Waterer',3),
     ('Darlene','Planter',3),
     ('Earl','Digger',7)],
    dtype=[('name', np.str_,8), ('job', np.str_,8), ('income', np.uint32)])

result=matplotlib.mlab.rec_groupby(data, ('job',), (('income',np.mean,'avg_income'),))

урожайность

('Digger', 4.0)
('Planter', 2.5)
('Waterer', 3.0)

matplotlib.mlab.rec_groupby возвращает повторное сопоставление:

print(result.dtype)
# [('job', '|S7'), ('avg_income', '<f8')]

Вы также можете быть заинтересованы в том, чтобы проверить панды, который имеет даже более универсальные средства для обработки группировка по операциям.

Другие советы

Ваш if k not in data_per_key.keys() может быть переписан как if k not in data_per_key, но вы можете сделать еще лучше с defaultdict.Вот версия, которая использует defaultdict чтобы избавиться от проверки существования:

import collections

def aggregate(data, key, value, func):
    data_per_key = collections.defaultdict(list)
    for k,v in zip(data[key], data[value]):
        data_per_key[k].append(v)

    return [(k,func(data_per_key[k])) for k in data_per_key.keys()]

Здесь это рецепт, который довольно хорошо эмулирует функциональность matlabs accumarray.Он довольно хорошо использует итераторы pythons, тем не менее, с точки зрения производительности он отстой по сравнению с реализацией matlab.Поскольку у меня была та же проблема, я написал реализацию, используя scipy.weave.Вы можете найти его здесь: https://github.com/ml31415/accumarray

Наилучшая гибкость и удобочитаемость - это использование панды:

import pandas

data=np.array(
    [('Aaron','Digger',1),
     ('Bill','Planter',2),
     ('Carl','Waterer',3),
     ('Darlene','Planter',3),
     ('Earl','Digger',7)],
    dtype=[('name', np.str_,8), ('job', np.str_,8), ('income', np.uint32)])

df = pandas.DataFrame(data)
result = df.groupby('job').mean()

Уступает :

         income
job
Digger      4.0
Planter     2.5
Waterer     3.0

Pandas DataFrame - отличный класс для работы, но вы можете вернуть свои результаты по мере необходимости:

result.to_records()
result.to_dict()
result.to_csv()

И так далее...

Наилучшая производительность достигается с помощью ndimage.иметь в виду От сципи.Это будет в два раза быстрее, чем принятый ответ для этого небольшого набора данных, и примерно в 3,5 раза быстрее для больших входных данных:

from scipy import ndimage

data=np.array(
    [('Aaron','Digger',1),
     ('Bill','Planter',2),
     ('Carl','Waterer',3),
     ('Darlene','Planter',3),
     ('Earl','Digger',7)],
    dtype=[('name', np.str_,8), ('job', np.str_,8), ('income', np.uint32)])

unique = np.unique(data['job'])
result=np.dstack([unique, ndimage.mean(data['income'], data['job'], unique)])

Будет уступать:

array([[['Digger', '4.0'],
        ['Planter', '2.5'],
        ['Waterer', '3.0']]],
      dtype='|S32')

Редактировать:с помощью bincount (быстрее!)

Это примерно в 5 раз быстрее, чем принятый ответ для небольшого примера ввода, если вы повторите данные 100000 раз, это будет примерно в 8,5 раз быстрее:

unique, uniqueInd, uniqueCount = np.unique(data['job'], return_inverse=True, return_counts=True)
means = np.bincount(uniqueInd, data['income'])/uniqueCount
return np.dstack([unique, means])

http://python.net /~goodger/проекты/pycon/2007/идиоматический/раздаточный материал.html#dictionary-get-method

должно помочь сделать его немного красивее, более питоническим, возможно, более эффективным.Я вернусь позже, чтобы проверить твои успехи.Может быть, вы можете отредактировать функцию с учетом этого?Также смотрите следующие пару разделов.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top