2016-03-28 16 views
3

Gegeben x, ich möchte x, log(x) als eine numpy-Array, wobei x hat Form s, das Ergebnis hat Form (*s, 2). Was ist der beste Weg, dies zu tun? x kann nur ein Schwimmer sein, in diesem Fall möchte ich ein Ergebnis mit der Form .effiziente numpy Array-Erstellung

Eine hässliche Art und Weise, dies zu tun ist:

import numpy as np 

x = np.asarray(x) 
result = np.empty((*x.shape, 2)) 
result[..., 0] = x 
result[..., 1] = np.log(x) 

Antwort

4

Es ist wichtig, Ästhetik von Leistung zu trennen. Manchmal ist der hässliche Code schnell. In der Tat ist das hier der Fall. Obwohl das Erstellen eines leeren Arrays und die anschließende Zuweisung von Werten zu Slices nicht schön aussehen, ist es schnell.

import numpy as np 
import timeit 
import itertools as IT 
import pandas as pd 

def using_empty(x): 
    x = np.asarray(x) 
    result = np.empty(x.shape + (2,)) 
    result[..., 0] = x 
    result[..., 1] = np.log(x) 
    return result 

def using_concat(x): 
    x = np.asarray(x) 
    return np.concatenate([x, np.log(x)], axis=-1).reshape(x.shape+(2,), order='F') 

def using_stack(x): 
    x = np.asarray(x) 
    return np.stack([x, np.log(x)], axis=x.ndim) 

def using_ufunc(x): 
    return np.array([x, np.log(x)]) 
using_ufunc = np.vectorize(using_ufunc, otypes=[np.ndarray]) 

tests = [np.arange(600), 
     np.arange(600).reshape(20,30), 
     np.arange(960).reshape(8,15,8)] 

# check that all implementations return the same result 
for x in tests: 
    assert np.allclose(using_empty(x), using_concat(x)) 
    assert np.allclose(using_empty(x), using_stack(x)) 


timing = [] 
funcs = ['using_empty', 'using_concat', 'using_stack', 'using_ufunc'] 
for test, func in IT.product(tests, funcs): 
    timing.append(timeit.timeit(
     '{}(test)'.format(func), 
     setup='from __main__ import test, {}'.format(func), number=1000)) 

timing = pd.DataFrame(np.array(timing).reshape(-1, len(funcs)), columns=funcs) 
print(timing) 

Erträge, die folgenden timeit Ergebnisse auf meinem Rechner:

using_empty using_concat using_stack using_ufunc 
0  0.024754  0.025182  0.030244  2.414580 
1  0.025766  0.027692  0.031970  2.408344 
2  0.037502  0.039644  0.044032  3.907487 

So using_empty ist die schnellste (die Optionen tests angewandt getestet).

Beachten Sie, dass np.stack tut genau das, was Sie wollen, so

np.stack([x, np.log(x)], axis=x.ndim) 

sieht recht hübsch, aber es ist auch die langsamste der drei Optionen geprüft.


Note, die zusammen mit viel langsamer zu sein, using_ufunc ein Array von Objekt dtype zurückgibt:

In [236]: x = np.arange(6) 

In [237]: using_ufunc(x) 
Out[237]: 
array([array([ 0., -inf]), array([ 1., 0.]), 
     array([ 2.  , 0.69314718]), 
     array([ 3.  , 1.09861229]), 
     array([ 4.  , 1.38629436]), array([ 5.  , 1.60943791])], dtype=object) 

, die nicht das gleiche wie das gewünschte Ergebnis ist:

In [240]: using_empty(x) 
Out[240]: 
array([[ 0.  ,  -inf], 
     [ 1.  , 0.  ], 
     [ 2.  , 0.69314718], 
     [ 3.  , 1.09861229], 
     [ 4.  , 1.38629436], 
     [ 5.  , 1.60943791]]) 

In [238]: using_ufunc(x).shape 
Out[238]: (6,) 

In [239]: using_empty(x).shape 
Out[239]: (6, 2) 
+0

Ich mag 'Stapel "Am besten eigentlich. Normalerweise optimiere ich meinen Python-Code nicht für Geschwindigkeit :) –

+0

Würde es Ihnen etwas ausmachen, den '@ ufunc'-Dekorator als eine andere Möglichkeit hinzuzufügen, dies zu tun? –

+0

Ich bin nicht vertraut mit dem '@ ufunc' Dekorateur. Beziehen Sie sich auf [diese] (https://mail.python.org/pipermail/python-dev/2013-June/126864.html)? – unutbu