0

I'm trying to decipher some complicated code. Below, I've simplified the code, of what I do understand of it, to get to the heart of my question.

scales = (2**arange(8, 12, .25)).astype(int)

It seems to me that arange() creates an array of values, ranging from 8 to 11, with values 0.25 apart.

But then what does 2**arange do? I know ** is for exponentiation, but it doesn't make sense to me that one could exponentiate an array! Exponentiating the values inside the array make sense, sure. But that seems like very strange syntax for it!

riteshtch
  • 8,629
  • 4
  • 25
  • 38
philosonista
  • 65
  • 2
  • 7
  • 1
    Never heard of vectorized operations? If you have an array of numbers you may want to perform the same operation over all elements. How is `some_array + 1` which adds `1` to all elements of an array different from `2**some_array` which turns an array of "exponents" into the result of `2` to the power of element? – Bakuriu Oct 31 '16 at 08:23
  • Definitely have not heard of that before! But now I have, thank you! – philosonista Oct 31 '16 at 08:25

3 Answers3

0

arange returns a numpy array, which supports vectorized operations.

For example, 2 ** [1 2 3] returns [2 4 8], just as 2 + [1 2 3] returns [3 4 5].

DeepSpace
  • 78,697
  • 11
  • 109
  • 154
0

Numpy arrays let you apply numeric operators to all elements in the array. So array * 3 would apply the multiplication to all elements in the array, producing a new array with the results. You can use an array on either side of such an expression; after all, not all operators are commutative.

Using 2 ** array simply applies each element in the array as an exponent of 2, producing an array with the 2 ** <input item> calculation:

>>> arange(8, 12, .25)
array([  8.  ,   8.25,   8.5 ,   8.75,   9.  ,   9.25,   9.5 ,   9.75,
        10.  ,  10.25,  10.5 ,  10.75,  11.  ,  11.25,  11.5 ,  11.75])
>>> 2**arange(8, 12, .25)
array([  256.        ,   304.43702144,   362.03867197,   430.53896461,
         512.        ,   608.87404288,   724.07734394,   861.07792922,
        1024.        ,  1217.74808576,  1448.15468787,  1722.15585844,
        2048.        ,  2435.49617153,  2896.30937574,  3444.31171688])

So the input is an array with 8, 8.25, 8.5, etc., and the resulting array contains the result of 2 ** 8, 2 ** 8.25, 2 ** 8.5, and so on.

The array.astype(int) operation then floors the results:

>>> (2 ** arange(8, 12, .25)).astype(int)
array([ 256,  304,  362,  430,  512,  608,  724,  861, 1024, 1217, 1448,
       1722, 2048, 2435, 2896, 3444])
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
0

Every type in Python can implement methods that are the backing methods for various Python operators or operations. Specifically in this case, numpy.arange().__rpow__(int()) is invoked due to the operator and operands (respectively ** and int().__pow__(numpy.arange()) previously having returned NotImplementedError). This allows not only definitions of operations on new types, but also on existing types that would not be aware of how to handle the new type operand themselves.

Ignacio Vazquez-Abrams
  • 776,304
  • 153
  • 1,341
  • 1,358