10

Lets say I have a numpy array like

x = np.arange(10)

is it somehow possible to create a reference to a single element i.e.

y = create_a_reference_to(x[3])
y = 100 
print x
[  0   1   2 100   4   5   6   7   8   9]

4 Answers 4

12

You can't create a reference to a single element, but you can get a view over that single element:

>>> x = numpy.arange(10)
>>> y = x[3:4]
>>> y[0] = 100
>>> x
array([0, 1, 2, 100, 4, 5, 6, 7, 8, 9])

The reason you can't do the former is that everything in python is a reference. By doing y = 100, you're modifying what y points to - not it's value.

If you really want to, you can get that behaviour on instance attributes by using properties. Note this is only possible because the python data model specifies additional operations while accessing class attributes - it's not possible to get this behaviour for variables.

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

1 Comment

Is it maybe possible to wrap my variable up in a class to get this behavior ?
2

No you cannot do that, and that is by design.

Numpy arrays are of type numpy.ndarray. Individual items in it can be accessed with numpy.ndarray.item which does "copy an element of an array to a standard Python scalar and return it".

I'm guessing numpy returns a copy instead of direct reference to the element to prevent mutability of numpy items outside of numpy's own implementation.

Just as a thoughtgame, let's assume this wouldn't be the case and you would be allowed to get reference to individual items. Then what would happen if: numpy was in the midle of calculation and you altered an individual intime in another thread?

2 Comments

Are you sure about this ? For example it is possible to create a view of an array i.e. y = x[4:6] (see answer above). If I manipulate this view I also have changes in the original array.
@jrsm jsalonen's point is that you can't sidestep numpy. Using a view, you'll be calling numpy behind-the-scenes every time you assign to a element of the view.
0

@goncalopp gives a correct answer, but there are a few variations that will achieve similar effects.

All of the notations shown below are able to reference a single element while still returning a view:

x = np.arange(10)
two_index_method = [None] * 10
scalar_element_method = [None] * 10
expansion_method = [None] * 10
for i in range(10):
    two_index_method[i] = x[i:i+1]
    scalar_element_method[i] = x[..., i]  # x[i, ...] works, too
    expansion_method[i] = x[:, np.newaxis][i]  # np.newaxis == None

two_index_method[5]  # Returns a length 1 numpy.ndarray, shape=(1,)
# >>> array([5])
scalar_element_method[5]  # Returns a numpy scalar, shape = ()
# >>> array(5)
expansion_method[5]  # Returns a length 1 numpy.ndarray, shape=(1,)
# >>> array([5])
x[5] = 42  # Change the value in the original `ndarray`
x
# >>> array([0, 1, 2, 3, 4, 42, 6, 7, 8, 9])  # The element has been updated
# All methods presented here are correspondingly updated:
two_index_method[5], scalar_element_method[5], expansion_method[5]
# >>> (array([42]), array(42), array([42]))

Since the object in scalar_element_method is a dimension zero scalar, attempting to reference the element contained within the ndarray via element[0] will return an IndexError. For a scalar ndarray, element[()] can be used to reference the element contained within the numpy scalar. This method can also be used for assignment to a length-1 ndarray, but has the unfortunate side effect that it does not dereference a length-1 ndarray to a python scalar. Fortunately, there is a single method, element.item(), that can be used (for dereferencing only) to obtain the value regardless of whether the element is a length one ndarray or a scalar ndarray:

scalar_element_method[5][0]  # This fails
# >>> IndexError: too many indices for array
scalar_element_method[5][()]  # This works for scalar `ndarray`s
# >>> 42
scalar_element_method[5][()] = 6
expansion_method[5][0]  # This works for length-1 `ndarray`s
# >>> 6
expansion_method[5][()]  # Doesn't return a python scalar (or even a numpy scalar)
# >>> array([6])
expansion_method[5][()] = 8  # But can still be used to change the value by reference
scalar_element_method[5].item()  # item() works to dereference all methods
# >>> 8
expansion_method[5].item()
# >>> [i]8

TLDR; You can create a single-element view v with v = x[i:i+1], v = x[..., i], or v = x[:, None][i]. While different setters and getters work with each method, you can always assign values with v[()]=new_value, and you can always retrieve a python scalar with v.item().

Comments

0

You can use np.newaxis when indexing an element, so that instead of returning a Python scalar, numpy returns a reference to an array element. Below is a minimum working example.

>>> arr = np.asarray([0, 1, 2, 3])
>>> arr
array([0, 1, 2, 3])

>>> element2 = arr[2, np.newaxis]
>>> element2
array([2])

# Changing the element will modify the original array
>>> element2[:] = 100
>>> element2
array([100])

>>> arr
array([  0,   1, 100,   3])

Hope this helps.

EDIT -- according to jared's comment below, None can also be used in place of np.newaxis.

1 Comment

Also, np.newaxis and None are equivalent, so this could be done as element2 = arr[2, None].

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.