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

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.