Universal functions (ufunc) basics — NumPy v2.0 Manual (2024)

See also

Universal functions (ufunc)

A universal function (or ufunc for short) is a function thatoperates on ndarrays in an element-by-element fashion,supporting array broadcasting, typecasting, and several other standard features. Thatis, a ufunc is a “vectorized” wrapper for a functionthat takes a fixed number of specific inputs and produces a fixed number ofspecific outputs.

In NumPy, universal functions are instances of thenumpy.ufunc class. Many of the built-in functions areimplemented in compiled C code. The basic ufuncs operate on scalars, butthere is also a generalized kind for which the basic elements are sub-arrays(vectors, matrices, etc.), and broadcasting is done over other dimensions.The simplest example is the addition operator:

>>> np.array([0,2,3,4]) + np.array([1,1,-1,2])array([1, 3, 2, 6])

One can also produce custom numpy.ufunc instances using thenumpy.frompyfunc factory function.

Ufunc methods#

All ufuncs have four methods. They can be found atMethods. However, these methods only make sense on scalarufuncs that take two input arguments and return one output argument.Attempting to call these methods on other ufuncs will cause aValueError.

The reduce-like methods all take an axis keyword, a dtypekeyword, and an out keyword, and the arrays must all have dimension >= 1.The axis keyword specifies the axis of the array over which the reductionwill take place (with negative values counting backwards). Generally, it is aninteger, though for numpy.ufunc.reduce, it can also be a tuple ofint to reduce over several axes at once, or None, to reduce over allaxes. For example:

>>> x = np.arange(9).reshape(3,3)>>> xarray([[0, 1, 2], [3, 4, 5], [6, 7, 8]])>>> np.add.reduce(x, 1)array([ 3, 12, 21])>>> np.add.reduce(x, (0, 1))36

The dtype keyword allows you to manage a very common problem that ariseswhen naively using ufunc.reduce. Sometimes you mayhave an array of a certain data type and wish to add up all of itselements, but the result does not fit into the data type of thearray. This commonly happens if you have an array of single-byteintegers. The dtype keyword allows you to alter the data type over whichthe reduction takes place (and therefore the type of the output). Thus,you can ensure that the output is a data type with precision large enoughto handle your output. The responsibility of altering the reduce type ismostly up to you. There is one exception: if no dtype is given for areduction on the “add” or “multiply” operations, then if the input type isan integer (or Boolean) data-type and smaller than the size of thenumpy.int_ data type, it will be internally upcast to the int_(or numpy.uint) data-type. In the previous example:

>>> x.dtypedtype('int64')>>> np.multiply.reduce(x, dtype=float)array([ 0., 28., 80.])

Finally, the out keyword allows you toprovide an output array (or a tuple of output arrays for multi-output ufuncs).If out is given, the dtype argument is only used for the internal computations.Considering x from the previous example:

>>> y = np.zeros(3, dtype=int)>>> yarray([0, 0, 0])>>> np.multiply.reduce(x, dtype=float, out=y)array([ 0, 28, 80])

Ufuncs also have a fifth method, numpy.ufunc.at, that allows in placeoperations to be performed using advanced indexing. Nobuffering is used on the dimensions whereadvanced indexing is used, so the advanced index canlist an item more than once and the operation will be performed on the resultof the previous operation for that item.

Output type determination#

The output of the ufunc (and its methods) is not necessarily anndarray, if all input arguments are notndarrays. Indeed, if any input defines an__array_ufunc__ method,control will be passed completely to that function, i.e., the ufunc isoverridden.

If none of the inputs overrides the ufunc, thenall output arrays will be passed to the __array_wrap__method of the input (besides ndarrays, and scalars)that defines it and has the highest __array_priority__of any other input to the universal function. The default__array_priority__ of thendarray is 0.0, and the default __array_priority__ of a subtypeis 0.0. Matrices have __array_priority__ equal to 10.0.

All ufuncs can also take output arguments which must be arrays or subclasses.If necessary, the result will be cast to the data-type(s) of the providedoutput array(s).If the output has an __array_wrap__ method it is called insteadof the one found on the inputs.

Broadcasting#

See also

Broadcasting basics

Each universal function takes array inputs and produces array outputsby performing the core function element-wise on the inputs (where anelement is generally a scalar, but can be a vector or higher-ordersub-array for generalized ufuncs). Standardbroadcasting rules are appliedso that inputs not sharing exactly thesame shapes can still be usefully operated on.

By these rules, if an input has a dimension size of 1 in its shape, thefirst data entry in that dimension will be used for all calculations alongthat dimension. In other words, the stepping machinery of theufunc will simply not step along that dimension (thestride will be 0 for that dimension).

Type casting rules#

Note

In NumPy 1.6.0, a type promotion API was created to encapsulate themechanism for determining output types. See the functionsnumpy.result_type, numpy.promote_types, andnumpy.min_scalar_type for more details.

At the core of every ufunc is a one-dimensional strided loop thatimplements the actual function for a specific type combination. When aufunc is created, it is given a static list of inner loops and acorresponding list of type signatures over which the ufunc operates.The ufunc machinery uses this list to determine which inner loop touse for a particular case. You can inspect the .types attribute for a particular ufunc to see which typecombinations have a defined inner loop and which output type theyproduce (character codes are usedin said output for brevity).

Casting must be done on one or more of the inputs whenever the ufuncdoes not have a core loop implementation for the input types provided.If an implementation for the input types cannot be found, then thealgorithm searches for an implementation with a type signature towhich all of the inputs can be cast “safely.” The first one it findsin its internal list of loops is selected and performed, after allnecessary type casting. Recall that internal copies during ufuncs (evenfor casting) are limited to the size of an internal buffer (which is usersettable).

Note

Universal functions in NumPy are flexible enough to have mixed typesignatures. Thus, for example, a universal function could be definedthat works with floating-point and integer values. Seenumpy.ldexp for an example.

By the above description, the casting rules are essentiallyimplemented by the question of when a data type can be cast “safely”to another data type. The answer to this question can be determined inPython with a function call: can_cast(fromtype, totype). The example below shows the results of this call forthe 24 internally supported types on the author’s 64-bit system. Youcan generate this table for your system with the code given in the example.

Example

Code segment showing the “can cast safely” table for a 64-bit system.Generally the output depends on the system; your system might result ina different table.

>>> mark = {False: ' -', True: ' Y'}>>> def print_table(ntypes):...  print('X ' + ' '.join(ntypes))...  for row in ntypes:...  print(row, end='')...  for col in ntypes:...  print(mark[np.can_cast(row, col)], end='')...  print()...>>> print_table(np.typecodes['All'])X ? b h i l q n p B H I L Q N P e f d g F D G S U V O M m? Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y - Yb - Y Y Y Y Y Y Y - - - - - - - Y Y Y Y Y Y Y Y Y Y Y - Yh - - Y Y Y Y Y Y - - - - - - - - Y Y Y Y Y Y Y Y Y Y - Yi - - - Y Y Y Y Y - - - - - - - - - Y Y - Y Y Y Y Y Y - Yl - - - - Y Y Y Y - - - - - - - - - Y Y - Y Y Y Y Y Y - Yq - - - - Y Y Y Y - - - - - - - - - Y Y - Y Y Y Y Y Y - Yn - - - - Y Y Y Y - - - - - - - - - Y Y - Y Y Y Y Y Y - Yp - - - - Y Y Y Y - - - - - - - - - Y Y - Y Y Y Y Y Y - YB - - Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y - YH - - - Y Y Y Y Y - Y Y Y Y Y Y - Y Y Y Y Y Y Y Y Y Y - YI - - - - Y Y Y Y - - Y Y Y Y Y - - Y Y - Y Y Y Y Y Y - YL - - - - - - - - - - - Y Y Y Y - - Y Y - Y Y Y Y Y Y - -Q - - - - - - - - - - - Y Y Y Y - - Y Y - Y Y Y Y Y Y - -N - - - - - - - - - - - Y Y Y Y - - Y Y - Y Y Y Y Y Y - -P - - - - - - - - - - - Y Y Y Y - - Y Y - Y Y Y Y Y Y - -e - - - - - - - - - - - - - - - Y Y Y Y Y Y Y Y Y Y Y - -f - - - - - - - - - - - - - - - - Y Y Y Y Y Y Y Y Y Y - -d - - - - - - - - - - - - - - - - - Y Y - Y Y Y Y Y Y - -g - - - - - - - - - - - - - - - - - - Y - - Y Y Y Y Y - -F - - - - - - - - - - - - - - - - - - - Y Y Y Y Y Y Y - -D - - - - - - - - - - - - - - - - - - - - Y Y Y Y Y Y - -G - - - - - - - - - - - - - - - - - - - - - Y Y Y Y Y - -S - - - - - - - - - - - - - - - - - - - - - - Y Y Y Y - -U - - - - - - - - - - - - - - - - - - - - - - - Y Y Y - -V - - - - - - - - - - - - - - - - - - - - - - - - Y Y - -O - - - - - - - - - - - - - - - - - - - - - - - - - Y - -M - - - - - - - - - - - - - - - - - - - - - - - - Y Y Y -m - - - - - - - - - - - - - - - - - - - - - - - - Y Y - Y

You should note that, while included in the table for completeness,the ‘S’, ‘U’, and ‘V’ types cannot be operated on by ufuncs. Also,note that on a 32-bit system the integer types may have differentsizes, resulting in a slightly altered table.

Mixed scalar-array operations use a different set of casting rulesthat ensure that a scalar cannot “upcast” an array unless the scalar isof a fundamentally different kind of data (i.e., under a differenthierarchy in the data-type hierarchy) than the array. This ruleenables you to use scalar constants in your code (which, as Pythontypes, are interpreted accordingly in ufuncs) without worrying aboutwhether the precision of the scalar constant will cause upcasting onyour large (small precision) array.

Use of internal buffers#

Internally, buffers are used for misaligned data, swapped data, anddata that has to be converted from one data type to another. The sizeof internal buffers is settable on a per-thread basis. There canbe up to \(2 (n_{\mathrm{inputs}} + n_{\mathrm{outputs}})\)buffers of the specified size created to handle the data from all theinputs and outputs of a ufunc. The default size of a buffer is10,000 elements. Whenever buffer-based calculation would be needed,but all input arrays are smaller than the buffer size, thosemisbehaved or incorrectly-typed arrays will be copied before thecalculation proceeds. Adjusting the size of the buffer may thereforealter the speed at which ufunc calculations of various sorts arecompleted. A simple interface for setting this variable is accessibleusing the function numpy.setbufsize.

Error handling#

Universal functions can trip special floating-point status registersin your hardware (such as divide-by-zero). If available on yourplatform, these registers will be regularly checked duringcalculation. Error handling is controlled on a per-thread basis,and can be configured using the functions numpy.seterr andnumpy.seterrcall.

Overriding ufunc behavior#

Classes (including ndarray subclasses) can override how ufuncs act onthem by defining certain special methods. For details, seeStandard array subclasses.

Universal functions (ufunc) basics — NumPy v2.0 Manual (2024)
Top Articles
Latest Posts
Article information

Author: Geoffrey Lueilwitz

Last Updated:

Views: 6222

Rating: 5 / 5 (80 voted)

Reviews: 87% of readers found this page helpful

Author information

Name: Geoffrey Lueilwitz

Birthday: 1997-03-23

Address: 74183 Thomas Course, Port Micheal, OK 55446-1529

Phone: +13408645881558

Job: Global Representative

Hobby: Sailing, Vehicle restoration, Rowing, Ghost hunting, Scrapbooking, Rugby, Board sports

Introduction: My name is Geoffrey Lueilwitz, I am a zealous, encouraging, sparkling, enchanting, graceful, faithful, nice person who loves writing and wants to share my knowledge and understanding with you.