Logic1
Logic1

Reputation: 1847

Creating an array with a single staggered values in each row?

I need to generate an nX3 array of zeros but one of the columns has the value (1.0) staggered in each row.

Desired Output:

n = 10
array([[ 1.,  0.,  0.],
       [ 0.,  1.,  0.],
       [ 0.,  0.,  1.],
       [ 1.,  0.,  0.],
       [ 0.,  1.,  0.],
       [ 0.,  0.,  1.],
       [ 1.,  0.,  0.],
       [ 0.,  1.,  0.],
       [ 0.,  0.,  1.],
       [ 1.,  0.,  0.]])

I tried to slice and reshape an array but some rows contain all zeros.

My Attemtp:

n = 10
verts_color = np.zeros(3 * n)
verts_color[::4] = 1
verts_color.reshape((n,3))
array([[ 1.,  0.,  0.],
       [ 0.,  1.,  0.],
       [ 0.,  0.,  1.],
       [ 0.,  0.,  0.],
       [ 1.,  0.,  0.],
       [ 0.,  1.,  0.],
       [ 0.,  0.,  1.],
       [ 0.,  0.,  0.],
       [ 1.,  0.,  0.],
       [ 0.,  1.,  0.]])

I couldn't find an example or figure out how to solve this myself and was hoping someone here might be able to help me accomplish this?

Upvotes: 1

Views: 527

Answers (3)

Divakar
Divakar

Reputation: 221564

Approach #1

def broadcasting_based(n, m): # n :number of cols, m : number of rows
    return (np.mod(np.arange(m),n)[:,None] == np.arange(n)).astype(float)

Approach #2

def tile_based(n, m): # n :number of cols, m : number of rows
    M = 1+((m-1)//n)
    return np.tile(np.eye(n),M).T[:m]

Approach #3

def initialization_based(n, m): # n :number of cols, m : number of rows
    M = 1+((m-1)//n)
    r = np.arange(n)
    out = np.zeros((M,n,n))
    out[:,r,r] = 1
    out.shape = (-1,n)
    return out[:m]

One more initialization based, a shorter one and making use of stepsize to slice -

def initialization_based_v2(n, m): # n :number of cols, m : number of rows
    out = np.zeros((m,n))
    for i in range(n):
        out[i::n,i] = 1
    return out

Sample runs -

In [93]: broadcasting_based(n=3, m = 10)
Out[93]: 
array([[ 1.,  0.,  0.],
       [ 0.,  1.,  0.],
       [ 0.,  0.,  1.],
       [ 1.,  0.,  0.],
       [ 0.,  1.,  0.],
       [ 0.,  0.,  1.],
       [ 1.,  0.,  0.],
       [ 0.,  1.,  0.],
       [ 0.,  0.,  1.],
       [ 1.,  0.,  0.]])

In [94]: np.allclose(broadcasting_based(3,10), tile_based(3,10))
Out[94]: True

In [95]: np.allclose(broadcasting_based(3,10), initialization_based(3,10))
Out[95]: True

In [162]: np.allclose(broadcasting_based(3,10), initialization_based_v2(3,10))
Out[162]: True

Runtime test

Other approaches -

# @kmario23's soln
def using_concatenate(m, n):
    return np.concatenate([np.identity(m)]* (n//m + 1), axis=0 )[:n, :]

# @Michael's soln    
def initialization_modassign(m, n):
    x = np.zeros((n,m))
    i = np.arange(n)
    x[i, i%m] = 1
    return x

Timings -

In [188]: %timeit broadcasting_based(3,10000)
     ...: %timeit tile_based(3,10000)
     ...: %timeit initialization_based(3,10000)
     ...: %timeit initialization_based_v2(3,10000)
     ...: %timeit using_concatenate(3,10000)
     ...: %timeit initialization_modassign(3,10000)
     ...: 
1000 loops, best of 3: 225 µs per loop
10000 loops, best of 3: 40.9 µs per loop
10000 loops, best of 3: 23.6 µs per loop
10000 loops, best of 3: 20.7 µs per loop
1000 loops, best of 3: 480 µs per loop
10000 loops, best of 3: 177 µs per loop

In [189]: %timeit broadcasting_based(5,10000)
     ...: %timeit tile_based(5,10000)
     ...: %timeit initialization_based(5,10000)
     ...: %timeit initialization_based_v2(5,10000)
     ...: %timeit using_concatenate(5,10000)
     ...: %timeit initialization_modassign(5,10000)
     ...: 
1000 loops, best of 3: 238 µs per loop
10000 loops, best of 3: 63.6 µs per loop
10000 loops, best of 3: 33.3 µs per loop
10000 loops, best of 3: 33.3 µs per loop
1000 loops, best of 3: 275 µs per loop
10000 loops, best of 3: 176 µs per loop

So, on performance initialization_based_v2 seems to be working quite well.

Upvotes: 1

Michael H.
Michael H.

Reputation: 3483

import numpy as np
n = 10
x = np.zeros((n,3))
i = np.arange(n)
x[i, i%3] = 1

Upvotes: 1

kmario23
kmario23

Reputation: 61355

It's a bit quirky but it works by realizing the fact that you've (vertically) stacked identity arrays but you need to adjust the rows based on n

n = 10
np.vstack([np.identity(3)]* (n//3 + 1))[:n, :]

Alternatively, you can use np.concatenate if you want improved performance:

np.concatenate([np.identity(3)]* (n//3 + 1), axis=0 )[:n, :]

For a general use case & improved performance use the following where (n, m) are the rows and columns respectively.

np.concatenate([np.identity(m)]* (n//m + 1), axis=0 )[:n, :]

Example:

In [151]: n = 10

In [152]: np.vstack([np.identity(3)]* (n//3 + 1))[:n, :]
Out[152]: 
array([[ 1.,  0.,  0.],
       [ 0.,  1.,  0.],
       [ 0.,  0.,  1.],
       [ 1.,  0.,  0.],
       [ 0.,  1.,  0.],
       [ 0.,  0.,  1.],
       [ 1.,  0.,  0.],
       [ 0.,  1.,  0.],
       [ 0.,  0.,  1.],
       [ 1.,  0.,  0.]])


# number of columns
In [170]: m = 3

# number of rows
In [171]: n = 10

In [172]: np.concatenate([np.identity(m)]* (n//m + 1), axis=0 )[:n, :]
Out[172]: 
array([[ 1.,  0.,  0.],
       [ 0.,  1.,  0.],
       [ 0.,  0.,  1.],
       [ 1.,  0.,  0.],
       [ 0.,  1.,  0.],
       [ 0.,  0.,  1.],
       [ 1.,  0.,  0.],
       [ 0.,  1.,  0.],
       [ 0.,  0.,  1.],
       [ 1.,  0.,  0.]])

Performance:

In [176]: def using_concatenate(n, m):
     ...:     return np.concatenate([np.identity(m)]* (n//m + 1), axis=0 )[:n, :]
     ...: 

In [177]: %timeit using_concatenate(n, m)
10000 loops, best of 3: 22.8 µs per loop

Upvotes: 1

Related Questions