2014-10-24 9 views
9

Wie der Titel sagt, nehme ich an, dass ich eine Zeichenfunktion schreiben will (vergessen wir Zeichen (0)), offensichtlich erwarten wir sign (2) = 1 und sign (array ([- 2, -2,2])) = array ([- 1, -1,1]). Die folgende Funktion funktioniert jedoch nicht, da sie keine numpigen Arrays verarbeiten kann.Eine Python-Funktion, die als Argument entweder ein skalares oder ein numpy Array akzeptiert

Die nächste Funktion wird auch nicht funktionieren, da x kein Formelement hat, wenn es nur eine einzelne Zahl ist. Selbst wenn ein Trick wie y = x * 0 + 1 verwendet wird, wird y keine [] -Methode haben.

def sign(x): 
    y = ones(x.shape) 
    y[x<0] = -1 
    return y 

Auch mit der Idee von einer anderen Frage (how can I make a numpy function that accepts a numpy array, an iterable, or a scalar?), wird die nächste Funktion nicht, wenn x eine einzelne Zahl ist, weil in diesem Fall x.shape und y.shape sind nur() und die Indizierung y illegal.

def sign(x): 
    x = asarray(x) 
    y = ones(x.shape) 
    y[x<0] = -1 
    return y 

Die einzige Lösung scheint entscheiden, dass der erste zu sein, wenn x ein Array oder eine Zahl ist, aber ich möchte wissen, ob es etwas besser ist. Das Schreiben von verzweigtem Code wäre umständlich, wenn Sie viele kleine Funktionen wie diese haben.

+1

Die Indizierung von 'y' mit einer Maske * ist * legal: Das Problem hier ist, dass' x <0' wiederum ein Skalar ist und kein 0-d Array. Wenn Sie 'y [asarray (x <0)] versuchen, sollte es funktionieren. –

+0

Haben Sie über die Möglichkeit nachgedacht, das eingebaute 'np.sign' zu verwenden? – Jaime

+0

@MarkDickinson Dies ist ein guter, aber es gibt Fehler, wenn x eine einzelne Zahl ist, weil jetzt y auch eine einzelne Zahl sein muss - dann kann y nicht indiziert werden ... – Taozi

Antwort

1

Ich frage mich, ob es eine vektorisiert Funktion ist, die Sie wollen:

>>> import numpy as NP 

>>> def fnx(a): 
     if a > 0: 
      return 1 
     else: 
      return -1 

>>> vfnx = NP.vectorize(fnx) 

>>> a = NP.random.randint(1, 10, 5) 
array([4, 9, 7, 9, 2]) 

>>> a0 = 7 

>>> vfnx(a) 
array([1, 1, 1, 1]) 

>>> vfnx(a0) 
array(1) 
+0

Das ist nett, aber ist es wahr, wie von shx2 erwähnt, dass die vektorisierte Funktion langsam ist und die Geschwindigkeit von numpy nicht nutzt? Auch wenn diese Methode verwendet wird, muss jede Funktion zweimal definiert werden - eine ist die bescheidene Version, die sich auf eine einzelne Nummer konzentriert, eine ist die vektorisierte Version, deren Name nahe, aber verschieden sein sollte, ist das korrekt? – Taozi

+0

nach den Dokumenten, eine vektorisierte Fn ist als eine Python for-Schleife implementiert und in der Tat ist ein Hauptgrund für NumPy Leistung Array-orientierte Berechnung (nur eine einzige for-Schleife in der C-Quelle), die zweite Schleife für Python vermeidet. Aber du brauchst keine Sekunde fn; Der Zweck eines vektorisierten fn besteht darin, sowohl NumPy-Arrays als auch Skalare unter Verwendung desselben fn in einem einzelnen fn-Aufruf zu behandeln. – doug

0

Sie die Nummer auf eine Einzelelement-Array zuerst umwandeln kann,

und dann konzentrieren sich auf Arrays auf Betriebs .

Sie haben immer noch die Art von x zu überprüfen, obwohl

+0

Aber dann gibt die Funktion ein einzelnes Elementarray zurück, das von der Clientseite entpackt werden muss. – Taozi

2

np.vectorize verwendet werden kann, um das zu erreichen, würden aber langsam sein, weil alle es tut, wenn Ihre eingerichtete Funktion mit einem Array genannt wird, durch das Array wird looping Elemente und wenden Sie die Skalarfunktion auf jeden an, dh nicht numpy Geschwindigkeit zu nutzen.

Verfahren I für Vektorisierung Funktionen nützlich Einbeziehung if-else np.choose verwendet, ist:

def sign_non_zero(x): 
    return np.choose(
     x > 0, # bool values, used as indices to the array 
     [ 
      -1, # index=0=False, i.e. x<=0 
      1, # index=1=True, i.e. x>0 
     ]) 

Dies funktioniert, wenn x entweder Skalar oder Array und ist schneller als in Python-Raum Looping.

Der einzige Nachteil der Verwendung von np.choose ist, dass es nicht intuitiv ist, If-Else-Logik auf diese Weise zu schreiben, und der Code ist weniger lesbar. Wann immer ich es verwende, füge ich Kommentare wie die oben genannten hinzu, um es dem Leser leichter zu machen, zu verstehen, was vor sich geht.

+0

>>> sign_non_zero ([1,2,3]) ergibt 1 # sollte 1,1,1 sein >>> sign_non_zero ([1,2, -3]) ergibt 1 # hätte 1 sein sollen, 1, -1 –

+0

@BHATIRSHAD, rechts, wie derzeit, 'sign_non_zero' unterstützt Skalare und numpy Arrays. Um auch Listen zu unterstützen, können Sie einfach 'x' durch' np.asarray (x) 'ersetzen. – shx2

+1

@ bhat-irshad Dieser ist perfekt für die Implementierung der Zeichenfunktion, nachdem x durch np.asarray (x) ersetzt wurde. Die Verwendung von Auswahl scheint jedoch nur dann praktisch, wenn das Ergebnis Ja oder Nein ist. Wenn Sie jetzt eine dreifache Entscheidung treffen müssen (nehmen Sie an, Zeichen (0)), dann wählen Sie Funktion ist nutzlos und wir müssen uns wieder der alten Frage stellen - wenn x indiziert werden kann, aka, wenn x eine Zahl oder ein ist Array. – Taozi

0

Hier ist eine Lösung:

>>> def sign(x): 
...  if type(x)==int: 
...   if x>0: return 1 
...   else: return -1 
...  else: 
...   x=np.array(x) 
...   pos=np.where(x>=0) 
...   neg=np.where(x<0) 
...   res=np.zeros(x.shape[0]) 
...   res[pos]=1 
...   res[neg]=-1 
...   return res.tolist() 
... 
>>> sign(56) 
1 
>>> sign(-556) 
-1 
>>> sign([23,4,-3,0,45,-3]) 
[1.0, 1.0, -1.0, 1.0, 1.0, -1.0] 
>>> sign(np.array([23,4,-3,0,45,-3])) 
[1.0, 1.0, -1.0, 1.0, 1.0, -1.0] 
+0

was wäre die Ausgabe von' sign (56L) '? oder 'zeichen (np.int32 (56))'? 'Zeichen (56.)'? Der springende Punkt ist dabei, die Doppelung der Logik zu vermeiden. – shx2

+0

@ shx2 Genau das, was ich fragen möchte, das Problem mit der Typbeurteilung ist, dass es so viele Typen gibt. Es ist möglich, wenn type (numpyarray) == 'ndarray', aber diese Zweige sind alles, was ich vermeiden möchte. – Taozi

1

Hier ist eine Lösung:

import numpy as np 

def sign(x): 
    y = np.ones_like(x) 
    y[np.asarray(x) < 0] = -1 

    if isinstance(x, np.ndarray): 
     return y 
    else: 
     return type(x)(y) 

Dies sollte einen Wert des gleichen Typs wie die Eingabe zurück. Zum Beispiel gibt sign(42)1, sign(42.0) ergibt 1.0. Wenn Sie ein NDarray geben, funktioniert es wie np.sign.

Im Allgemeinen können Sie mit der Annahme fortfahren, dass Ihre Eingabe ein ndarray ist.Wenn Sie versuchen, auf ein Attribut oder eine Methode zuzugreifen, die ein ndarray besitzt, aber Ihre Eingabe nicht, dann greifen Sie auf einen Skalartyp zurück. Verwenden Sie Ausnahmen, um dies zu implementieren. Zum Beispiel:

def foo_on_scalars(x): 
    # do scalar things 

def foo(x): 
    try: 
     # assume x is an ndarray 
    except AttributeError: 
     foo_on_scalars(x) 
0

Der Ansatz, den ich vor genommen haben viel wie Ihr letztes Beispiel ist, aber das Hinzufügen einer zusätzlichen Prüfung für Skalare am Anfang:

def sign(x): 
    if isscalar(x): 
     x = (x,) 
    x = asarray(x) 
    y = ones(x.shape) 
    y[x<0] = -1 
    return y 
0

einfache Lösung, die Skalare und numpy Arrays behandelt:

>>> import numpy as np 

>>> def sign_non_zero(x): 
     return (x > 0) * 1 + (x < 0) * -1 

>>> sign_non_zero(2) 
1 

>>> sign_non_zero(np.array([-2, -2, 2])) 
array([-1, -1, 1]) 
Verwandte Themen