7

I would like to insert multiple rows and columns into a NumPy array.

If I have a square array of length n_a, e.g.: n_a = 3

a = np.array([[1, 2, 3],
              [4, 5, 6],
              [7, 8, 9]])

and I would like to get a new array with size n_b, which contains array a and zeros (or any other 1D array of length n_b) on certain rows and columns with indices, e.g.

index = [1, 3] 

so n_b = n_a + len(index). Then the new array is:

b = np.array([[1, 0, 2, 0, 3],
              [0, 0, 0, 0, 0],
              [4, 0, 5, 0, 6],
              [0, 0, 0, 0, 0],
              [7, 0, 8, 0, 9]])

My question is, how to do this efficiently, with the assumption that by bigger arrays n_a is much larger than len(index).

EDIT

The results for:

import numpy as np
import random

n_a = 5000
n_index = 100

a=np.random.rand(n_a, n_a)
index = random.sample(range(n_a), n_index)

Warren Weckesser's solution: 0.208 s

wim's solution: 0.980 s

Ashwini Chaudhary's solution: 0.955 s

Thank you to all!

4 Answers 4

10

Here's one way to do it. It has some overlap with @wim's answer, but it uses index broadcasting to copy a into b with a single assignment.

import numpy as np

a = np.array([[1, 2, 3],
              [4, 5, 6],
              [7, 8, 9]])

index = [1, 3]
n_b = a.shape[0] + len(index)

not_index = np.array([k for k in range(n_b) if k not in index])

b = np.zeros((n_b, n_b), dtype=a.dtype)
b[not_index.reshape(-1,1), not_index] = a
Sign up to request clarification or add additional context in comments.

2 Comments

yes, good! this is the best way and that's what I was trying to do with b[not_index,:][:,not_index] = a (which doesn't work). your method should be approx twice as fast as mine.
I don't know how it will affect speed, but I propose generating the not_index array using sets instead of a list comprehension: index = {1, 3} n_b = a.shape[0] + len(index) # this remains the same not_index = np.array(list( set(range(n_b)) - index))
3

You can do this by applying two numpy.insert calls on a:

>>> a = np.array([[1, 2, 3],
              [4, 5, 6],
              [7, 8, 9]])
>>> indices = np.array([1, 3])
>>> i = indices - np.arange(len(indices))
>>> np.insert(np.insert(a, i, 0, axis=1), i, 0, axis=0)
array([[1, 0, 2, 0, 3],
       [0, 0, 0, 0, 0],
       [4, 0, 5, 0, 6],
       [0, 0, 0, 0, 0],
       [7, 0, 8, 0, 9]])

4 Comments

This works, however I am looking for a more general anwser on what to do after an empty array is created (arr[::2, ::2] = a works for this case only).
For this 3x3 size, this solution is 5-10 times slower. But with a 100x100 matrix, it is faster than the other 2 (2x). Internally insert uses the build a large filled matrix, and then populates it with the old data. So the speed difference must come (mostly) from operating first on rows and then columns.
I posted the comparison of the anwser's above. The solution from @Warren Weckesser is still faster.
-(-1) I stand corrected, I thought np.insert would have to shift a lot of data but it seems it's implemented better than I've assumed.
1

Since fancy indexing returns a copy instead of a view, I can only think how to do it in a two-step process. Maybe a numpy wizard knows a better way...

Here you go:

import numpy as np

a = np.array([[1, 2, 3],
              [4, 5, 6],
              [7, 8, 9]])

index = [1, 3]
n = a.shape[0]
N = n + len(index)

non_index = [x for x in xrange(N) if x not in index]

b = np.zeros((N,n), a.dtype)
b[non_index] = a

a = np.zeros((N,N), a.dtype)
a[:, non_index] = b

Comments

1

Why can't you just Slice/splice? This has zero loops or for statements.

xlen = a.shape[1]
ylen = a.shape[0]
b = np.zeros((ylen * 2 - ylen % 2, xlen * 2 - xlen % 2))  #accomodates both odd and even shapes
b[0::2,0::2] = a

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.