0

I am creating a 2d numpy array from a function applied to a 1d numpy array (which contains a conditional) and would like to know a more efficient way of doing this. This is currently the slowest part of my code. x is a 1d numpy array, and the output is a 2d numpy array. There is a switch to construct a different array element based on whether x is less than or greater than 0. In the future, there could be an arbitrary number of switches.

def basis2(x) :

  final = []
  for i in x :
    if i > 0 :
        xr = 2.0*(i-0.5)
        final.append(np.array([0.0, 0.0, 0.0, 0.5*xr*(xr-1.0),-1.0*(xr+1)*(xr-1), 0.5*xr*(xr+1.0)]))
    else :
        xl = 2.0*(i+0.5)
        final.append(np.array([0.5*xl*(xl-1.0),-1.0*(xl+1)*(xl-1),0.5*xl*(xl+1.0),0.0,0.0,0.0]))


return np.array(final)

Ideally, I would be able to eliminate the for loop - but so far I have not managed to do this properly, using 'where' for example. Thanks, for any help.

1
  • Can you split your x into two arrays, on where x<=0 and the other x>0? And then evaluate each expression with a whole array? What's the essential difference between the two expressions? Give a sample of x. Commented Oct 17, 2019 at 19:49

2 Answers 2

1

With your function:

In [247]: basis2(np.array([1,.5,0,-.5,-1]))                                     
Out[247]: 
[array([ 0.,  0.,  0.,  0., -0.,  1.]),
 array([ 0.,  0.,  0., -0.,  1.,  0.]),
 array([ 0., -0.,  1.,  0.,  0.,  0.]),
 array([-0.,  1.,  0.,  0.,  0.,  0.]),
 array([ 1.,  0., -0.,  0.,  0.,  0.])]
In [248]: %hist 245                                                             
basis2_1(np.array([1,.5,0,-.5,-1]))

With some superficial changes:

def basis2_1(x) :
    xr = 2.0*(x[x>0]-0.5)
    res1 = np.array([0.0*xr, 0.0*xr, 0.0*xr, 0.5*xr*(xr-1.0),-1.0*(xr+1)*(xr-1), 0.5*xr*(xr+1.0)])
    xl = 2.0*(x[x<=0]+0.5)
    res2 = np.array([0.5*xl*(xl-1.0),-1.0*(xl+1)*(xl-1),0.5*xl*(xl+1.0),0.0*xl,0.0*xl,0.0*xl])
    return res1, res2

In [250]: basis2_1(np.array([1,.5,0,-.5,-1]))                                   
Out[250]: 
(array([[ 0.,  0.],
        [ 0.,  0.],
        [ 0.,  0.],
        [ 0., -0.],
        [-0.,  1.],
        [ 1.,  0.]]), 
 array([[ 0., -0.,  1.],
        [-0.,  1.,  0.],
        [ 1.,  0., -0.],
        [ 0.,  0., -0.],
        [ 0.,  0., -0.],
        [ 0.,  0., -0.]]))

Joining the two subarrays:

In [251]: np.hstack(_)                                                          
Out[251]: 
array([[ 0.,  0.,  0., -0.,  1.],
       [ 0.,  0., -0.,  1.,  0.],
       [ 0.,  0.,  1.,  0., -0.],
       [ 0., -0.,  0.,  0., -0.],
       [-0.,  1.,  0.,  0., -0.],
       [ 1.,  0.,  0.,  0., -0.]])

Obviously that needs refinement, but it should be enough to get you started.

For example you might make a result = np.zeros((5,x.shape[0])) array, just insert the respective non-zero elements (saving all those 0.0*xr terms).

Looking at those blocks in Out[251]:

In [257]: x = np.array([1,.5,0,-.5,-1])                                         
In [258]: Out[251][3:,np.nonzero(x>0)[0]]                                       
Out[258]: 
array([[ 0., -0.],
       [-0.,  1.],
       [ 1.,  0.]])
In [259]: Out[251][:3,np.nonzero(x<=0)[0]]                                      
Out[259]: 
array([[ 0., -0.,  1.],
       [-0.,  1.,  0.],
       [ 1.,  0., -0.]])
Sign up to request clarification or add additional context in comments.

Comments

1

Here is a vectorized approach that exploits a symmetry:

col = np.s_[...,None]

def basis2_v(x):
    h,w = np.arange(x.size)[col],np.arange(3,dtype=np.int8)
    # if the terms for xr are the same as the formulas for xl applied to -xr
    # we can therefore unify the code and apply it to |x|:
    # there are three factors in total each term consists of two of them
    aux = (np.abs(x[col])-w/2)*(2/(1^-(w&1)))
    # the easiest is multiplying all three and then dividing one out again
    aux = -aux.prod(-1)[col]/aux
    # fix degenerate cases
    aux[np.isnan(aux)] = 1
    # finally, we need to embed the terms in a zero matrix
    out = np.zeros((x.size,6),x.dtype)
    # the xor trick maps 2,1,0 to -3,-2,-1 if x>0
    out[h,(-(x[col]>0).view(np.int8))^w[::-1]] = aux
    return out

# make small test
x = np.random.randn(10)
# this should pass
assert np.allclose(basis2(x),basis2_v(x))

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.