Pergunta

Gostaria de fazer uma função agradável para agregar dados entre um conjunto (é um array de registro numpy, mas isso não muda nada)

Você tem uma matriz de dados que deseja agregada entre um eixo: por exemplo, uma série de dtype=[(name, (np.str_,8), (job, (np.str_,8), (income, np.uint32)] e você quer ter a renda média por posto de trabalho

Eu fiz esta função, e no exemplo que deve ser chamado como 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()]

O problema é que eu acho que não muito bom eu gostaria de tê-lo em uma linha:? Você tem alguma ideias

Obrigado por sua resposta Louis

PS: Eu gostaria de manter o func na chamada para que você também pode pedir mediana, mínimo ...

Foi útil?

Solução

Talvez a função que você está procurando é matplotlib.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'),))

rendimentos

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

matplotlib.mlab.rec_groupby retorna um recarray:

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

Você pode também estar interessado em verificar para fora pandas , que tem mesmo more versátil instalações para lidar com grupo de operações .

Outras dicas

Seu if k not in data_per_key.keys() poderia ser reescrita como if k not in data_per_key, mas você pode fazer ainda melhor com defaultdict . Aqui está uma versão que usa defaultdict para se livrar do cheque existência:

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()]

Aqui é uma receita que emula a funcionalidade da matlabs accumarray muito bem. Ele usa jibóias iterators muito bem, no entanto, em termos de performance que suga em comparação com a implementação Matlab. Como eu tive o mesmo problema, eu tinha escrito uma implementação usando scipy.weave. Você pode encontrá-lo aqui: https://github.com/ml31415/accumarray

Melhor flexibilidade e legibilidade é começar usando pandas :

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()

Os rendimentos para:

         income
job
Digger      4.0
Planter     2.5
Waterer     3.0

Pandas trama de dados é uma grande classe para trabalhar, mas você pode obter de volta os resultados que você precisa:

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

E assim por diante ...

Melhor desempenho é conseguido usando ndimage.mean de scipy . Este será duas vezes mais rápido do que resposta aceite para este pequeno conjunto de dados, e cerca de 3,5 vezes mais rápido para as entradas maiores:

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)])

Será que ceder a:

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

EDIT: com bincount (! Mais rápido)

Este é cerca de 5x mais rápido do que resposta aceita para o exemplo de entrada pequena, se você repetir os dados 100000 vezes será em torno de 8,5x mais rápido:

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 / projetos / PyCon / 2007 / idiomática / handout.html # dicionário-get-método

deve ajudar a torná-lo um pouco mais bonito, mais Python, mais eficiente, possivelmente. Vou voltar mais tarde para verificar o seu progresso. Talvez você pode editar a função com isso em mente? ver também o próximo par de seções.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top