Aggregation von Werten

Code-Stücke können hier veröffentlicht werden.
Antworten
skirnir
User
Beiträge: 33
Registriert: Sonntag 25. Januar 2015, 10:59

Hallo zusammen!

Ich habe vor, die Werte in einer Liste von Dictionaries auf eine den jeweiligen Keys zugeordnete Funktion anzuwenden.
Ich hoffe, die Beschreibung von Input und Output im untenstehenden Docstring sind selbsterklärend. Ansonsten bitte nachfragen.
Meine bisherige Lösung sieht so aus (für "stuff" denke ich mir noch einen besseren Namen aus, versprochen ;-)):

Code: Alles auswählen

"""
test script: aggregation of values
input = [
    {
        'foo': max,
        'bar': np.mean,
        'baz': sum,
    },
    {
        'foo': 1,
        'bar': 2,
        'baz': 3,
    },
    {
        'foo': 4,
        'bar': 5,
        'baz': 6,
    },
]

output = {
    'foo': 4,   # input[0]['foo']([input[1]['foo'], input[2]['foo'], ..., input[n]['foo']])
    'bar': 3.5, # input[0]['bar']([input[1]['bar'], input[2]['bar'], ..., input[n]['bar']])
    'baz': 9,   # input[0]['baz']([input[1]['baz'], input[2]['baz'], ..., input[n]['baz']])
}
"""
import numpy as np
def aggregate(stuff):
    """
    aggregate values using functions in stuff[0]
    """
    functions = stuff[0]
    names = [key for key in sorted(functions)]
    params = stuff[1:]
    funclist = [x[1] for x in sorted(functions.items())]
    paramlist = np.array([x[1] for elem in params for x in sorted(elem.items())]).reshape(len(params),len(params[0])).T
    values = [f(p) for f, p in zip(funclist, paramlist)]
    result = dict((n, v) for n, v in zip(names, values))
    return result

def main():
    rein = [
        {
            'foo': max,
            'bar': np.mean,
            'baz': sum,
        },
        {
            'foo': 1,
            'bar': 2,
            'baz': 3,
        },
        {
            'foo': 4,
            'bar': 5,
            'baz': 6,
        },
    ]

    output = aggregate(rein)
    print output

if __name__ == '__main__':
    main()

Das funktioniert auch wie erwartet, jedenfalls für das angegebene Beispiel (python 2.7.9). Ich bin mir aber einerseits nicht sicher, wie sinnvoll es ist, das so zu machen, und andererseits, wie lesbar/idiomatisch diese Lösung ist.
Geht das evtl. mit Hilfe von numpy eleganter?
Über Anregung und Kritik würde ich mich freuen.
BlackJack

Mein Ansatz:

Code: Alles auswählen

from collections import defaultdict

def aggregate(stuff):
    stuff = iter(stuff)
    name2function = next(stuff)

    name2values = defaultdict(list)
    for mapping in stuff:
        for name, value in mapping.iteritems():
            name2values[name].append(value)

    return dict((n, f(name2values[n])) for n, f in name2function.iteritems())
skirnir
User
Beiträge: 33
Registriert: Sonntag 25. Januar 2015, 10:59

Das sieht auf jeden Fall eleganter aus als meine Lösung. Wenn man das Transponieren der Parametermatrix spart (bzw. die Erzeugung derselben), lässt es sich auch gleich besser lesen. :D
BlackJack

Eine Variante die nur Werte aggregiert deren Schlüssel im ersten Wörterbuch mit den Funktionen sind:

Code: Alles auswählen

def aggregate(stuff):
    stuff = iter(stuff)
    name2function = next(stuff)

    name2values = defaultdict(list)
    for mapping in stuff:
        for name in name2function.iterkeys():
            name2values[name].append(mapping[name])

    return dict((n, f(name2values[n])) for n, f in name2function.iteritems())
Vorteile: Es werden aus den folgenden Wörterbüchern nur diese Schlüssel aggregiert, zusätzliche werden ignoriert, und es gibt eine Ausnahme wenn eines der Wörterbücher keinen Wert für einen der Schlüssel aus dem ersten hat.
Antworten