2016-07-27 9 views
0

prüfen dieses Stück Code-Schnipsel,dupliziert Ausgänge np.apply_along_axis wenn ein dict zurückgegeben

import numpy as np  
a = np.arange(20).reshape(2,10) 

# the result is right if there is only 1 key 
func = lambda x: dict(k1=len(x)) 
print np.apply_along_axis(func, -1, a) 
out[1]: [[{'k1': 10}] 
     [{'k1': 10}]] 

# but if there are more than 1 key in the returned dict 
# the results are duplicated 
func = lambda x: dict(k1=1, k2=len(x)) 
print np.apply_along_axis(func, -1, a) 
out[2]: [[{'k2': 10, 'k1': 1} {'k2': 10, 'k1': 1}] 
      [{'k2': 10, 'k1': 1} {'k2': 10, 'k1': 1}]] 

func = lambda x: dict(k1=1, k2=2, k3=len(x)) 
print np.apply_along_axis(func, -1, a) 
out[3]: [[{'k3': 10, 'k2': 2, 'k1': 1} {'k3': 10, 'k2': 2, 'k1': 1} {'k3': 10, 'k2': 2, 'k1': 1}] 
     [{'k3': 10, 'k2': 2, 'k1': 1} {'k3': 10, 'k2': 2, 'k1': 1} {'k3': 10, 'k2': 2, 'k1': 1}]] 

Das Problem in den Kommentaren beschrieben wurde, und die Ergebnisse wurden auch gezeigt.

Antwort

1

Es scheint wie np.apply_along_axis versucht herauszufinden, was die resultierende Form sein sollte, basierend auf dem Ergebnis des Aufrufs func. Wenn Ihr Eingabe-Array die Form (n, m) hat und Ihr func einen Wert mit der Länge k zurückgibt, gibt np.apply_along_axis(func, -1, a) ein Array mit der Form (n, k) zurück. Dies gilt auch dann, wenn Ihre Funktion etwas anderes als eine Liste oder ein Array zurückgibt. Wenn Ihre Funktion einen Skalar zurückgibt, lautet die resultierende Form (n,).

Beispiele:

# np.diff(a[0]) has length 9. 
>>> np.apply_along_axis(lambda x: np.diff(x), -1, a).shape 
(2, 9) 
# sorted(a[0]) has length 10 
>>> np.apply_along_axis(lambda x: sorted(x), -1, a).shape 
(2, 10) 
# len(a[0]) is a scalar 
>>> np.apply_along_axis(lambda x: len(x), -1, a).shape 
(2,) 

nun in Ihrem Fall, da Sie eine dict mit einer Länge von 2, die sich ergebende Form ist (2, 2) sind zurück. Eine einfache Problemumgehung wäre, das Wörterbuch in etwas zu verpacken, das ein Skalar ist. Aber anscheinend mag numpy benutzerdefinierte Skalare nicht. Also, wenn Sie versuchen, eine benutzerdefinierte DictWrap Klasse wie folgt zu verwenden:

class DictWrap(object): 
    def __init__(self, *args, **kwargs): 
     self._d = dict(*args, **kwargs) 

... es funktioniert nicht:

>>> np.apply_along_axis(lambda x: DictWrap(k1=1, k2=len(x)), -1, a) 
... 
TypeError: object of type 'DictWrap' has no len() 

Also entweder wir müssen eine benutzerdefinierte __len__() Methode DictWrap hinzufügen, die zurückgibt 1, oder wir können das Wörterbuch in einer Liste wickeln:

>>> np.apply_along_axis(lambda x: [dict(k1=1, k2=len(x))], -1, a) 
array([[{'k2': 10, 'k1': 1}], 
     [{'k2': 10, 'k1': 1}]], dtype=object) 

Dies hat eine Form (2, 1). Sie können squeeze() auf nennen es ein 1-d-Array zu erhalten:

>>> r = np.apply_along_axis(lambda x: [dict(k1=1, k2=len(x))], -1, a) 
>>> r.squeeze() 
array([{'k2': 10, 'k1': 1}, {'k2': 10, 'k1': 1}], dtype=object) 

Eine andere, und vielleicht die einfachste, würde Weg sein, die zusätzlichen Dimensionen loszuwerden selbst:

>>> r = np.apply_along_axis(lambda x: dict(k1=1, k2=len(x)), -1, a) 
>>> r[:, 0] 
array([{'k2': 10, 'k1': 1}, {'k2': 10, 'k1': 1}], dtype=object) 

zu sehen, wie genau nummerische Griffe in verschiedenen Fällen, siehe documentation of apply_along_axis (besonders ab if isscalar(res):).

+0

Ich muss sagen, "numpy" macht selbstgefällige Dinge – Lee