Chris@87
|
1 """
|
Chris@87
|
2 ===================
|
Chris@87
|
3 Universal Functions
|
Chris@87
|
4 ===================
|
Chris@87
|
5
|
Chris@87
|
6 Ufuncs are, generally speaking, mathematical functions or operations that are
|
Chris@87
|
7 applied element-by-element to the contents of an array. That is, the result
|
Chris@87
|
8 in each output array element only depends on the value in the corresponding
|
Chris@87
|
9 input array (or arrays) and on no other array elements. Numpy comes with a
|
Chris@87
|
10 large suite of ufuncs, and scipy extends that suite substantially. The simplest
|
Chris@87
|
11 example is the addition operator: ::
|
Chris@87
|
12
|
Chris@87
|
13 >>> np.array([0,2,3,4]) + np.array([1,1,-1,2])
|
Chris@87
|
14 array([1, 3, 2, 6])
|
Chris@87
|
15
|
Chris@87
|
16 The unfunc module lists all the available ufuncs in numpy. Documentation on
|
Chris@87
|
17 the specific ufuncs may be found in those modules. This documentation is
|
Chris@87
|
18 intended to address the more general aspects of unfuncs common to most of
|
Chris@87
|
19 them. All of the ufuncs that make use of Python operators (e.g., +, -, etc.)
|
Chris@87
|
20 have equivalent functions defined (e.g. add() for +)
|
Chris@87
|
21
|
Chris@87
|
22 Type coercion
|
Chris@87
|
23 =============
|
Chris@87
|
24
|
Chris@87
|
25 What happens when a binary operator (e.g., +,-,\\*,/, etc) deals with arrays of
|
Chris@87
|
26 two different types? What is the type of the result? Typically, the result is
|
Chris@87
|
27 the higher of the two types. For example: ::
|
Chris@87
|
28
|
Chris@87
|
29 float32 + float64 -> float64
|
Chris@87
|
30 int8 + int32 -> int32
|
Chris@87
|
31 int16 + float32 -> float32
|
Chris@87
|
32 float32 + complex64 -> complex64
|
Chris@87
|
33
|
Chris@87
|
34 There are some less obvious cases generally involving mixes of types
|
Chris@87
|
35 (e.g. uints, ints and floats) where equal bit sizes for each are not
|
Chris@87
|
36 capable of saving all the information in a different type of equivalent
|
Chris@87
|
37 bit size. Some examples are int32 vs float32 or uint32 vs int32.
|
Chris@87
|
38 Generally, the result is the higher type of larger size than both
|
Chris@87
|
39 (if available). So: ::
|
Chris@87
|
40
|
Chris@87
|
41 int32 + float32 -> float64
|
Chris@87
|
42 uint32 + int32 -> int64
|
Chris@87
|
43
|
Chris@87
|
44 Finally, the type coercion behavior when expressions involve Python
|
Chris@87
|
45 scalars is different than that seen for arrays. Since Python has a
|
Chris@87
|
46 limited number of types, combining a Python int with a dtype=np.int8
|
Chris@87
|
47 array does not coerce to the higher type but instead, the type of the
|
Chris@87
|
48 array prevails. So the rules for Python scalars combined with arrays is
|
Chris@87
|
49 that the result will be that of the array equivalent the Python scalar
|
Chris@87
|
50 if the Python scalar is of a higher 'kind' than the array (e.g., float
|
Chris@87
|
51 vs. int), otherwise the resultant type will be that of the array.
|
Chris@87
|
52 For example: ::
|
Chris@87
|
53
|
Chris@87
|
54 Python int + int8 -> int8
|
Chris@87
|
55 Python float + int8 -> float64
|
Chris@87
|
56
|
Chris@87
|
57 ufunc methods
|
Chris@87
|
58 =============
|
Chris@87
|
59
|
Chris@87
|
60 Binary ufuncs support 4 methods.
|
Chris@87
|
61
|
Chris@87
|
62 **.reduce(arr)** applies the binary operator to elements of the array in
|
Chris@87
|
63 sequence. For example: ::
|
Chris@87
|
64
|
Chris@87
|
65 >>> np.add.reduce(np.arange(10)) # adds all elements of array
|
Chris@87
|
66 45
|
Chris@87
|
67
|
Chris@87
|
68 For multidimensional arrays, the first dimension is reduced by default: ::
|
Chris@87
|
69
|
Chris@87
|
70 >>> np.add.reduce(np.arange(10).reshape(2,5))
|
Chris@87
|
71 array([ 5, 7, 9, 11, 13])
|
Chris@87
|
72
|
Chris@87
|
73 The axis keyword can be used to specify different axes to reduce: ::
|
Chris@87
|
74
|
Chris@87
|
75 >>> np.add.reduce(np.arange(10).reshape(2,5),axis=1)
|
Chris@87
|
76 array([10, 35])
|
Chris@87
|
77
|
Chris@87
|
78 **.accumulate(arr)** applies the binary operator and generates an an
|
Chris@87
|
79 equivalently shaped array that includes the accumulated amount for each
|
Chris@87
|
80 element of the array. A couple examples: ::
|
Chris@87
|
81
|
Chris@87
|
82 >>> np.add.accumulate(np.arange(10))
|
Chris@87
|
83 array([ 0, 1, 3, 6, 10, 15, 21, 28, 36, 45])
|
Chris@87
|
84 >>> np.multiply.accumulate(np.arange(1,9))
|
Chris@87
|
85 array([ 1, 2, 6, 24, 120, 720, 5040, 40320])
|
Chris@87
|
86
|
Chris@87
|
87 The behavior for multidimensional arrays is the same as for .reduce(),
|
Chris@87
|
88 as is the use of the axis keyword).
|
Chris@87
|
89
|
Chris@87
|
90 **.reduceat(arr,indices)** allows one to apply reduce to selected parts
|
Chris@87
|
91 of an array. It is a difficult method to understand. See the documentation
|
Chris@87
|
92 at:
|
Chris@87
|
93
|
Chris@87
|
94 **.outer(arr1,arr2)** generates an outer operation on the two arrays arr1 and
|
Chris@87
|
95 arr2. It will work on multidimensional arrays (the shape of the result is
|
Chris@87
|
96 the concatenation of the two input shapes.: ::
|
Chris@87
|
97
|
Chris@87
|
98 >>> np.multiply.outer(np.arange(3),np.arange(4))
|
Chris@87
|
99 array([[0, 0, 0, 0],
|
Chris@87
|
100 [0, 1, 2, 3],
|
Chris@87
|
101 [0, 2, 4, 6]])
|
Chris@87
|
102
|
Chris@87
|
103 Output arguments
|
Chris@87
|
104 ================
|
Chris@87
|
105
|
Chris@87
|
106 All ufuncs accept an optional output array. The array must be of the expected
|
Chris@87
|
107 output shape. Beware that if the type of the output array is of a different
|
Chris@87
|
108 (and lower) type than the output result, the results may be silently truncated
|
Chris@87
|
109 or otherwise corrupted in the downcast to the lower type. This usage is useful
|
Chris@87
|
110 when one wants to avoid creating large temporary arrays and instead allows one
|
Chris@87
|
111 to reuse the same array memory repeatedly (at the expense of not being able to
|
Chris@87
|
112 use more convenient operator notation in expressions). Note that when the
|
Chris@87
|
113 output argument is used, the ufunc still returns a reference to the result.
|
Chris@87
|
114
|
Chris@87
|
115 >>> x = np.arange(2)
|
Chris@87
|
116 >>> np.add(np.arange(2),np.arange(2.),x)
|
Chris@87
|
117 array([0, 2])
|
Chris@87
|
118 >>> x
|
Chris@87
|
119 array([0, 2])
|
Chris@87
|
120
|
Chris@87
|
121 and & or as ufuncs
|
Chris@87
|
122 ==================
|
Chris@87
|
123
|
Chris@87
|
124 Invariably people try to use the python 'and' and 'or' as logical operators
|
Chris@87
|
125 (and quite understandably). But these operators do not behave as normal
|
Chris@87
|
126 operators since Python treats these quite differently. They cannot be
|
Chris@87
|
127 overloaded with array equivalents. Thus using 'and' or 'or' with an array
|
Chris@87
|
128 results in an error. There are two alternatives:
|
Chris@87
|
129
|
Chris@87
|
130 1) use the ufunc functions logical_and() and logical_or().
|
Chris@87
|
131 2) use the bitwise operators & and \\|. The drawback of these is that if
|
Chris@87
|
132 the arguments to these operators are not boolean arrays, the result is
|
Chris@87
|
133 likely incorrect. On the other hand, most usages of logical_and and
|
Chris@87
|
134 logical_or are with boolean arrays. As long as one is careful, this is
|
Chris@87
|
135 a convenient way to apply these operators.
|
Chris@87
|
136
|
Chris@87
|
137 """
|
Chris@87
|
138 from __future__ import division, absolute_import, print_function
|