0

I am working on my first agent-based model (abm) in my life and I have to do operations on an array of arrays. Each agent of my model is an array with numbers that are added by the algorithm when certain conditions are met. Sometimes I have to divide or multiply all arrays by the same number. I saw in numpy array I can do this:

vector = np.array([1, 2.1, 3])

when I do

2 / vector

gives me, as expected, array([ 2. , 0.95238095, 0.66666667]).

But if I want an array of arrays like for example

arrayofarrays = np.array([[1,2,3],[1.1,6],[1]])

it has by default dtype=object and I guess it is this that doesn't allow me to do

2 / arrayofarrays

which gives

unsupported operand type(s) for /: 'int' and 'list'

Also

2 / arrayofarrays[0]

gives same error. Instead if you use a single array's value, as

2/arrayofarrays[0][1]

it works: 1.0.

Can you help me? Thanks

Ged
  • 5
  • 1
  • Your arrayofarrays is actually an array of lists. Python lists don't support arithmetic. – khelwood Nov 11 '21 at 15:05
  • Thank you. So what can I do? – Ged Nov 11 '21 at 15:07
  • There's probably a better way, but you could do `arrayofarrays = np.array([np.array(x) for x in [[1,2,3],[1.1,6],[1]]])` – khelwood Nov 11 '21 at 15:09
  • Numpy is not Python! Python has lists of lists and inner lists are allowed to have each a different length. On the other hand, numpy uses *multi dimensional arrays*. The difference is that the basic type shall be the same for all elements (the numpy *dtype*), and all subarrays of same level shall have the same size. If those conditions are met, and if the dtype is numeric, numpy allows to broadcast operations over the whole array or over inner arrays if dimensions match. As you have a numpy array with *object* dtype it only supports object operations (meaning no operation at all...) – Serge Ballesta Nov 11 '21 at 15:09
  • @SergeBallesta, with object dtype arrays, `numpy` tries to apply the operator to each element. The results depends on class of the element. This is done at list comprehension speeds. – hpaulj Nov 11 '21 at 16:48

1 Answers1

0

Your ragged array - an array of lists:

In [31]: arr = np.array([[1,2,3],[1.1,6],[1]])
<ipython-input-31-4887f672b831>:1: VisibleDeprecationWarning: Creating an ndarray from ragged nested sequences (which is a list-or-tuple of lists-or-tuples-or ndarrays with different lengths or shapes) is deprecated. If you meant to do this, you must specify 'dtype=object' when creating the ndarray.
  arr = np.array([[1,2,3],[1.1,6],[1]])
In [32]: arr
Out[32]: array([list([1, 2, 3]), list([1.1, 6]), list([1])], dtype=object)

Math on object dtype arrays is performed element by element (at list comprehension speeds). How it works depends on how the corresponding method works the elements:

In [33]: arr *2
Out[33]: 
array([list([1, 2, 3, 1, 2, 3]), list([1.1, 6, 1.1, 6]), list([1, 1])],
      dtype=object)

list*2 is replication operation, not multiplication.

In [34]: arr / 2
Traceback (most recent call last):
  File "<ipython-input-34-855a165721c4>", line 1, in <module>
    arr / 2

Divide is not defined for lists.

TypeError: unsupported operand type(s) for /: 'list' and 'int'

We can apply a simple function to each element with frompyfunc:

In [38]: np.frompyfunc(lambda x: np.array(x)/2, 1,1)(arr)
Out[38]: 
array([array([0.5, 1. , 1.5]), array([0.55, 3.  ]), array([0.5])],
      dtype=object)

This function takes care of the conversion to array as well as the division.

Equivalent list comprehension (just as fast):

In [40]: [np.array(x)/2 for x in arr]
Out[40]: [array([0.5, 1. , 1.5]), array([0.55, 3.  ]), array([0.5])]

or the pure list version (may be faster)

In [41]: [[y/2 for y in x] for x in arr.tolist()]
Out[41]: [[0.5, 1.0, 1.5], [0.55, 3.0], [0.5]]
hpaulj
  • 221,503
  • 14
  • 230
  • 353
  • Thank for your answer. But now I'm really confused: I can't understand why `2 / vector` (my example) worked properly! It seems that if np.array is just an array you can multiply and divide, otherwise not – Ged Nov 12 '21 at 01:40
  • `arrayofarrays[0]` is a list. `arrayofarrays[0][1]` is a number. – hpaulj Nov 12 '21 at 01:55
  • but `vector=np.array([1, 2.1, 3])` and `2/vector` works. Instead `arr = np.array([[1,2,3],[1.1,6],[1]])` and `2/arr` not – Ged Nov 12 '21 at 02:10
  • Ok...maybe I'm starting to understand now... There are things that are actually different but I thought they were the same thing: one can create a list of lists or can create a list of np.array or also a np.array of np.arrays. For example, I did some tests and I see that `test = np.array([ np.array([1,2,3]), np.array([1.1,6]), np.array([1]) ])` is a `dtype=object` but it works properly with `2/test` – Ged Nov 12 '21 at 02:20
  • Object dtype arays can hold anything, list, array, number, string, etc. Math on such an array delegates the action to the elements, at python iteration speeds. Whether it works is hit-or-miss. – hpaulj Nov 12 '21 at 03:01