No Body
No Body

Reputation: 1

How to make a "mutable" number in an array (numpy)?

I am trying to make an array, which includes the same number multiple times, but if I change it in any place, it should change everywhere. Like with lists:

a = b = [1]
>>> a.append(1)
>>> print(b)
>>> [1, 1]

So I want an array like this:

number = 10   
arr = numpy.zeros((10, 10))
arr[0][0] = number
arr[1][1] = number
arr[0][0] += 1
print(arr[1][1])

Here it should output "11". So is there a way to have multiple references to a number? I know you could replace all the 10s with 11s, but firstly this would be too inefficient, secondly this could mess things up since another number may be coincidentally a 10 too.

Thanks.

Upvotes: 0

Views: 697

Answers (1)

Paul Panzer
Paul Panzer

Reputation: 53079

A zero dimensional numpy array behaves in many respects like a mutable number:

>>> import numpy as np                                                  
>>>                                                                                                                 
>>> a = 2.0            # ordinary immutable number
>>> am = np.array(2.0) # "mutable number"                                                                                          
>>>                                                                                                                 
>>> A = np.array([[a, a, am, a], [am, am, a, a]], dtype=object)
>>> A
array([[2.0, 2.0, array(2.), 2.0],                                                                                  
       [array(2.), array(2.), 2.0, 2.0]], dtype=object)

That's a bit ugly, so below I'll convert to float before printing:

>>> A.astype(float)                                                          
array([[2., 2., 2., 2.],                                                                                            
       [2., 2., 2., 2.]])

These twos are not all equal, The top left is immutable

>>> A[0, 0] += 1

Assigning to it only directly addressed cell is affected:

>>> A.astype(float)
array([[3., 2., 2., 2.],
       [2., 2., 2., 2.]])
>>> a
2.0

The third two is mutable

>>> A[0, 2] -= 1

Assigning to it all references are affected:

>>> A.astype(float)
array([[3., 2., 1., 2.],
       [1., 1., 2., 2.]])
>>> am
array(1.)

Direct assignment requires [()] syntax:

>>> A[0, 2][()] = 1.5
>>> am
array(1.5)

Otherwise the link will break:

>>> A[0, 2] = 1.8
>>> A.astype(float)
array([[3. , 2. , 1.8, 2. ],
       [1.5, 1.5, 2. , 2. ]])

Update:

In a comment OP specifies the following desired behavior:

If the mutable number am occurs k times in A, then in A * 3 the mutable number should be multiplied with 3**k, not 3.

We can get this behavior using the inplace (*=) operator and some trickery; if we want to preserve the original A, we must first make a copy:

Because numpys copying machinery normalizes our 0D arrays away, some more trickery is required to make a good copy:

>>> import copy
>>>
>>> B = np.empty_like(A)
>>> B.ravel()[...] = copy.deepcopy((*A.ravel(),))

Now do the inplace multiplication on the copy:

>>> import operator as op
>>> 
>>> C = np.frompyfunc(op.imul, 2, 1)(B, 3)
>>> 
>>> A
array([[2.0, 2.0, array(2.), 2.0],
       [array(2.), array(2.), 2.0, 2.0]], dtype=object)
>>> C
array([[6.0, 6.0, array(54.), 6.0],
       [array(54.), array(54.), 6.0, 6.0]], dtype=object)

Note that B will contain some useless hybrid data in the end and should be discarded.

>>> B
array([[2.0, 2.0, array(54.), 2.0],
       [array(54.), array(54.), 2.0, 2.0]], dtype=object)
>>> del B

Final note: this is more trickery than sane programming, so please regard it as a can-be-done rather than a should-be-done.

Upvotes: 1

Related Questions