2

is it possible to store references of specific rows of an numpy array in another numpy array?

I have an array of 2D nodes, e.g.

nodes = np.array([[1, 2], [2, 3], [3, 4], [4, 5], [5, 6]])

Now I want to select only a few of them and store a reference in another numpy array:

nn = np.array([nodes[0], nodes[3]])

If I modify a entry in nn the array nodes remains unchanged. Is there a way to store a reference to nodes in the ndarray nn?

3
  • 1
    Do you have to store it in numpy arrays? Commented Dec 22, 2017 at 18:57
  • 1
    does normal slicing/indexing not work for your desired outcome? Commented Dec 22, 2017 at 18:59
  • Have you considered using a mask to indicate which locations to change? This could be a viable option if you want to make the same changes to all the references in nn Commented Dec 22, 2017 at 18:59

4 Answers 4

2

If the reference can be created with basic indexing/slicing, then you get a view (an array that does not own its data, but refers to another array’s data instead) of the initial array where changes propagate:

>>> nn = nodes[0:4:3] # reference array for rows 0 and 3
>>> nn[0][0] = 0
>>> nodes
array([[0, 2],
       [2, 3],
       [3, 4],
       [4, 5],
       [5, 6]])

Otherwise, you get a copy from the original array as in your code, and updates do not propagate to the initial array.

Sign up to request clarification or add additional context in comments.

2 Comments

That was easy, thank you! But is there a way to slice specific rows?
@joe-92 There is indeed. You can do nodes[[0, 3]] = some_array and it will work so long as the dimensions match. You can give an array instead of [0, 3]. You can also give a boolean array like so: nodes[[True, False, False, True, False]] = some_array.
1

You can store an index to the rows you want in a numpy array:

ref = np.array([0, 3])

You can use the reference in an indexing expression to access the nodes you want:

>>> nn = nodes[ref]
>>> nn
array([[1, 2],
       [4, 5]])

nn will be a deep copy with no connection to the original in this case. While nn[foo] = bar won't affect the original array, you can use ref directly:

>>> nodes[ref, 1] = [17, 18]
>>> nodes
array([[ 1, 17],
       [ 2,  3],
       [ 3,  4],
       [ 4, 18],
       [ 5,  6]])

Alternatively, you can use a mask for ref:

>>> ref2 = np.zeros(nodes.shape[0], dtype=np.bool)
>>> ref2[ref] = True
>>> ref2
array([ True, False, False,  True, False], dtype=bool)

You can do almost all the same operations:

>>> nn2 = nodes[ref2]
>>> nn2
array([[1, 2],
       [4, 5]])

Modifications work too:

>>> nodes[ref2, 1] = [19, 23]
>>> nodes
array([[ 1, 19],
       [ 2,  3],
       [ 3,  4],
       [ 4, 23],
       [ 5,  6]])

The only thing that is more convenient with an array of indices is selecting a particular node from within the selection:

 >>> nodes[ref[0], 0]
 1

4 Comments

Thanks for your answer. But is nn and nn2 indeed only a view? nn.flags.owndata and nn2.flags.owndata is true in both cases.
@joe-92. nn and nn2 are not views. They are totally separate arrays, deep copied from the original segments.
@joe-92. Thanks for pointing that out. I fixed the poor wording that probably led to your comment.
Now I get what you meant by read only, thanks! Do you know if there is way to only store a view? I'd like to store the nn ndarray in an object where I don't have access to the nodes ndarray.
0

In Numpy, you can get a view of an array that can be edited. In your example, you can do this:

import numpy as np
nodes = np.array([[1, 2], [2, 3], [3, 4], [4, 5], [5, 6]])
node_idx = np.array([0, 3])
nodes[node_idx] = np.array([[1, 5], [2, 5]])
nodes

Output:

array([[1, 5],
       [2, 3],
       [3, 4],
       [2, 5],
       [5, 6]])

You can also replace it with boolean arrays:

import numpy as np
nodes = np.array([[1, 2], [2, 3], [3, 4], [4, 5], [5, 6]])
node_mask = np.array([True, False, False, True, False])
nodes[node_mask] = np.array([[1, 5], [2, 5]])
nodes

Which produces the same result. Of course, this means you can do magic like this:

import numpy as np
nodes = np.array([[1, 2], [2, 3], [3, 4], [4, 5], [5, 6]])
nodes[nodes[:, 0] == 3] = [1, 5]
nodes

Which replaces all rows with the first element equal to 3 with [1, 5]. Output:

array([[1, 2],
       [2, 3],
       [1, 5],
       [4, 5],
       [5, 6]])

Comments

0

Method 1

First, initialize a Numpy array of None with dtype=object. (It don't have to be None. My guess it that you just cannot put references at initialization as Numpy somehow just creates an deep copy of it.)

Then, put the reference into the array.

nodes = np.array([[1, 2], [2, 3], [3, 4], [4, 5], [5, 6]])
# nn = np.array([nodes[0], nodes[1]],dtype=object) would not work
nn = np.array([None, None], dtype=object)
nn[0] = nodes[0]
nn[1] = nodes[3]
# Now do some modification.
nn[0][1] = 100

Output of nodes:
array([[  1, 100],
       [  2,   3],
       [  3,   4],
       [  4,   5],
       [  5,   6]])

# make it a function
def make_ref(old_array, indeces):
    ret = np.array([None for _ in range(len(indeces))])
    for i in range(len(indeces)):
        ret[i] = old_array[indeces[i]]
    return ret

nn = make_ref(nodes, [0, 3])

Method 2
If you don't need to put it in Numpy arrays, just use a list to host the references.

nn = [nodes[0], nodes[1]]

4 Comments

If you do np.array([nodes[0], nodes[3]]), of course it's going to make a deep copy: the memory they occupy is non-contiguous.
What about np.array([nodes[0], nodes[1]])? I am curious that is this considered contiguous? @MadPhysicist
It just might be. I'll try it later and let you know.
Try setting copy=False. I don't think that's the default.

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.