Numpy Vectorize gebruiken op functies die vectoren retourneren

numpy.vectorizeneemt een functie f:a->b en verandert deze in g:a[]->b[].

Dit werkt prima als aen bscalaire waarden zijn, maar ik kan geen reden bedenken waarom het niet zou werken met b als een ndarrayof lijst, bijv. f:a->b[] en g:a[]->b[][]

Bijvoorbeeld:

import numpy as np
def f(x):
    return x * np.array([1,1,1,1,1], dtype=np.float32)
g = np.vectorize(f, otypes=[np.ndarray])
a = np.arange(4)
print(g(a))

Dit levert:

array([[ 0.  0.  0.  0.  0.],
       [ 1.  1.  1.  1.  1.],
       [ 2.  2.  2.  2.  2.],
       [ 3.  3.  3.  3.  3.]], dtype=object)

Ok, dus dat geeft de juiste waarden, maar het verkeerde dtype. En nog erger:

g(a).shape

opbrengst:

(4,)

Dus deze array is vrijwel nutteloos. Ik weet dat ik het kan converteren door:

np.array(map(list, a), dtype=np.float32)

om me te geven wat ik wil:

array([[ 0.,  0.,  0.,  0.,  0.],
       [ 1.,  1.,  1.,  1.,  1.],
       [ 2.,  2.,  2.,  2.,  2.],
       [ 3.,  3.,  3.,  3.,  3.]], dtype=float32)

maar dat is noch efficiënt, noch pythonisch. Kan iemand van jullie een schonere manier vinden om dit te doen?

Bij voorbaat dank!


Antwoord 1, autoriteit 100%

np.vectorizeis slechts een gemaksfunctie. Het maakt de code niet echt sneller. Als het niet handig is om np.vectorizete gebruiken, schrijf dan gewoon je eigen functie die werkt zoals je wilt.

Het doel van np.vectorizeis om functies die niet numpy-aware zijn (bijv. floats als invoer te nemen en floats als uitvoer terug te geven) om te zetten in functies die kunnen werken op (en retourneren) numpy arrays .

Uw functie fis al numpy-aware — het gebruikt een numpy-array in zijn definitie en retourneert een numpy-array. Dus np.vectorizeis niet geschikt voor uw gebruik.

De oplossing is daarom gewoon je eigen functie fte gebruiken die werkt zoals jij dat wilt.


Antwoord 2, autoriteit 38%

Een nieuwe parameter signaturein 1.12.0 doet precies wat je doet.

def f(x):
    return x * np.array([1,1,1,1,1], dtype=np.float32)
g = np.vectorize(f, signature='()->(n)')

Vervolgens geeft g(np.arange(4)).shape(4L, 5L).

Hier wordt de handtekening van fgespecificeerd. De (n)is de vorm van de geretourneerde waarde en de ()is de vorm van de parameter die scalair is. En de parameters kunnen ook arrays zijn. Voor meer complexe handtekeningen, zie Generalized Universal Function API.


Antwoord 3, autoriteit 11%

import numpy as np
def f(x):
    return x * np.array([1,1,1,1,1], dtype=np.float32)
g = np.vectorize(f, otypes=[np.ndarray])
a = np.arange(4)
b = g(a)
b = np.array(b.tolist())
print(b)#b.shape = (4,5)
c = np.ones((2,3,4))
d = g(c)
d = np.array(d.tolist())
print(d)#d.shape = (2,3,4,5)

Dit zou het probleem moeten oplossen en het werkt ongeacht de grootte van uw invoer. “map” werkt alleen voor ééndimensionale ingangen. Het gebruik van “.tolist()” en het maken van een nieuwe ndarray lost het probleem vollediger en mooier op (denk ik). Ik hoop dat dit helpt.


Antwoord 4, autoriteit 2%

Ik heb een functie geschreven, het lijkt aan uw behoefte te voldoen.

def amap(func, *args):
    '''array version of build-in map
    amap(function, sequence[, sequence, ...]) -> array
    Examples
    --------
    >>> amap(lambda x: x**2, 1)
    array(1)
    >>> amap(lambda x: x**2, [1, 2])
    array([1, 4])
    >>> amap(lambda x,y: y**2 + x**2, 1, [1, 2])
    array([2, 5])
    >>> amap(lambda x: (x, x), 1)
    array([1, 1])
    >>> amap(lambda x,y: [x**2, y**2], [1,2], [3,4])
    array([[1, 9], [4, 16]])
    '''
    args = np.broadcast(None, *args)
    res = np.array([func(*arg[1:]) for arg in args])
    shape = args.shape + res.shape[1:]
    return res.reshape(shape)

Probeer het

def f(x):
        return x * np.array([1,1,1,1,1], dtype=np.float32)
amap(f, np.arange(4))

Uitgangen

array([[ 0.,  0.,  0.,  0.,  0.],
       [ 1.,  1.,  1.,  1.,  1.],
       [ 2.,  2.,  2.,  2.,  2.],
       [ 3.,  3.,  3.,  3.,  3.]], dtype=float32)

Je kunt het voor het gemak ook met lambda of gedeeltelijk inpakken

g = lambda x:amap(f, x)
g(np.arange(4))

Let op de DOCSTRING VAN vectorizezegt

De vectorizeFunctie wordt voornamelijk verstrekt voor het gemak, niet voor
uitvoering. De implementatie is in wezen een voor lus.

We zouden dus verwachten dat de amaphier een vergelijkbare uitvoering hebben als vectorize. Ik heb het niet gecontroleerd, elke prestatietest is welkom.

Als de uitvoering echt belangrijk is, moet u iets anders overwegen, b.v. Directe array-berekening met reshapeen broadcastom lus in pure python te voorkomen (beide vectorizeen amapzijn het latere geval ).


5, Autoriteit 2%

U wilt de functie

omgeven

import numpy as np
def f(x):
    return x * np.array([1,1,1,1,1], dtype=np.float32)

Aangenomen dat u een enkele np.float32arrays als resultaat wilt krijgen, moet u dit opgeven als otype. In uw vraag die u echter hebt gespecificeerd otypes=[np.ndarray], wat betekent dat u wilt dat elk element een np.ndarrayis. U krijgt dus correct een resultaat van dtype=object.

De juiste oproep zou

zijn

np.vectorize(f, signature='()->(n)', otypes=[np.float32])

Voor een dergelijke eenvoudige functie is het echter beter om numpysfuncties te gebruiken; np.vectorizeLoopt er gewoon over. Dus in uw geval Herschrijf uw functie gewoon als

def f(x):
    return np.multiply.outer(x, np.array([1,1,1,1,1], dtype=np.float32))

Dit is sneller en produceert minder obscure fouten (meld echter dat de resultaten dtypeafhankelijk zijn van xals u een complex of quad-precisienummer passeert, dus het resultaat).

Other episodes