2014-10-24 10 views
9

Come dice il titolo, supponiamo di voler scrivere una funzione di segno (dimentichiamo il segno (0) per ora), ovviamente ci aspettiamo di firmare (2) = 1 e sign (array ([- 2, -2,2])) = array ([- 1, -1,1]). Tuttavia, la seguente funzione non funzionerà perché non può gestire gli array numpy.Una funzione python che accetta come argomento un array scalare o numpy

def sign(x): 
    if x>0: return 1 
    else: return -1 

La funzione successiva non funziona poiché x non ha un membro di forma se è solo un numero. Anche se viene usato qualche trucco come y = x * 0 + 1, y non avrà un metodo [].

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

Anche con l'idea da un'altra domanda (how can I make a numpy function that accepts a numpy array, an iterable, or a scalar?), la funzione successiva non funziona quando x è un numero unico perché in questo caso x.shape e y.shape sono solo() e indicizzazione y è illegale.

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

L'unica soluzione sembra essere quella prima di decidere se x è un array o un numero, ma voglio sapere se c'è qualcosa di meglio. Scrivere codice branchy sarebbe scomodo se hai molte piccole funzioni come questa.

+1

indicizzazione 'y' con una maschera * è * legale: il problema qui è che' x <0 'è di nuovo uno scalare piuttosto che un array di 0-d. Se provi 'y [asarray (x <0)]' dovrebbe funzionare. –

+0

Hai preso in considerazione la possibilità di utilizzare il 'np.sign' incorporato? – Jaime

+0

@MarkDickinson Questo è buono, ma dà errori quando x è un numero singolo perché ora y deve essere un singolo numero - quindi y non può essere indicizzato ... – Taozi

risposta

1

Mi chiedo se si tratta di una funzione vectorized che si desidera:

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

Questo è bello, ma è vero come menzionato da shx2 che la funzione vettoriale è lenta e non sfrutta la velocità di Numpy? Inoltre, se si utilizza questo metodo, ciascuna funzione deve essere definita due volte: una è la versione umile che si concentra su un singolo numero, una è la versione vettoriale il cui nome deve essere vicino ma diverso, è corretto? – Taozi

+0

secondo i documenti, una fn vettorizzata è implementata come un python per il ciclo e in effetti una delle ragioni principali per le prestazioni di NumPy è il calcolo orientato all'array (solo un ciclo for nel sorgente C) che evita il secondo python per il ciclo. Ma non hai bisogno di un secondo fn; lo scopo di un vettore fn è di gestire sia gli array NumPy che gli scalari usando lo stesso fn in una singola chiamata fn. – doug

0

è possibile convertire il numero a una matrice a elemento singolo prima,

e poi concentrarsi sul funzionamento su array .

si devono ancora verificare il tipo di x se

+0

Ma poi la funzione restituisce un array di elementi singolo che deve essere decompresso dal lato client. – Taozi

2

np.vectorize può essere utilizzato per raggiungere questo, ma sarebbe lento perché tutto quello che fa, quando la funzione decorato viene chiamata con una matrice, viene scorrendo la matrice elementi e applicare la funzione scalare a ciascuno, cioè non sfruttando la velocità di Numpy.

Metodo trovo utile per le funzioni Vettorizzazione coinvolgono if-else sta utilizzando np.choose:

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

Questo funziona quando x è o scalare o un array, ed è più veloce di loop in python-spazio.

L'unico svantaggio dell'utilizzo di np.choose è che non è intuitivo scrivere la logica if-else in questo modo e il codice è meno leggibile. Ogni volta che lo uso, includo commenti come quelli sopra, per rendere più facile al lettore capire cosa sta succedendo.

+0

>>> sign_non_zero ([1,2,3]) dà 1 # avrebbe dovuto essere 1,1,1 >>> sign_non_zero ([1,2, -3]) dà 1 # dovrebbe essere stato 1, 1, -1 –

+0

@BHATIRSHAD, a destra, così com'è attualmente, 'sign_non_zero' supporta scalari e array numpy. Per supportare anche gli elenchi, puoi semplicemente sostituire 'x' con' np.asarray (x) '. – shx2

+1

@ bhat-irshad Questo è perfetto per implementare la funzione di segno dopo che x è stato sostituito con np.asarray (x). Tuttavia, l'utilizzo di choose sembra conveniente solo quando il risultato è yes-or-no. Se hai una triplice decisione da prendere (supponiamo tu consideri il segno (0)), allora scegli la funzione è inutile e dobbiamo affrontare di nuovo la vecchia domanda - se x può essere indicizzato, aka, se x è un numero o un array. – Taozi

0

Ecco una soluzione:

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

quale sarebbe l'output di' sign (56L) '? o 'sign (np.int32 (56))'? 'Segno (56)'? Inoltre, il punto è evitare la duplicazione della logica. – shx2

+0

@ shx2 Esattamente quello che voglio chiedere, il problema con il giudizio del tipo è che ci sono così tanti tipi. È possibile usare se type (numpyarray) == 'ndarray', ma quei rami sono tutto ciò che voglio evitare. – Taozi

1

Ecco una soluzione:

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) 

Ciò dovrebbe restituire un valore dello stesso tipo come ingresso. Ad esempio sign(42)1, sign(42.0)1.0. Se gli dai un narray, funzionerà come np.sign.

In generale, è possibile procedere presupponendo che l'input sia un narray.Se si tenta di accedere ad un attributo o metodo che ha un narray, ma l'input non lo fa, si ricomincia a operare su un tipo scalare. Usa le eccezioni per implementare questo. Per esempio:

def foo_on_scalars(x): 
    # do scalar things 

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

L'approccio che ho preso prima è un sacco come il tuo ultimo esempio, ma l'aggiunta di un ulteriore controllo per scalari all'inizio:

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

soluzione semplice che gestisce scalari e array numpy:

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