2

Unless numpy itself is programmed to return a Series when a Series is passed to it, it is very confusing. yet the documentation on this function doesn't mention that it returns a Series when a Series is passed to it.

Understand that i come from a java background and i am new to python.

ayhan
  • 70,170
  • 20
  • 182
  • 203
  • 2
    The output of `np.exp` is a `numpy.ndarray` as even written in doc `out : ndarray or scalar`. Clarify, what do you need? – meW Jan 02 '19 at 08:19
  • 1
    It is simple. You pass it a scalar, it gives you a scalar. You pass it a 1-d array of length n, it gives you back a 1-d array of length n. You pass it an array of shape nxm and it gives you back an array of shape nxm – Sheldore Jan 02 '19 at 08:21
  • @meW i was just suprised that it returned a Series when i expected an ndarray or scalar. but user2357112 explained it well for me. it looks like i have a long journey to go in python. – Yosan Girma Jan 03 '19 at 07:27

2 Answers2

6

The NumPy ufunc machinery has built-in hooks to customize how objects are treated by ufuncs. In this particular case, the numpy.exp ufunc calls the Series's __array__ method to get an array to work with, computes the exponential over the array, and then calls the Series's __array_wrap__ method on the resulting array to post-process it.

__array__ is how the ufunc gets an object it knows how to work with, and __array_wrap__ is how the result gets converted back to a Series instead of an array.

You can see the same mechanisms in action by writing your own class with those methods:

In [9]: class ArrayWrapper(object):
   ...:     def __init__(self, arr):
   ...:         self.arr = arr
   ...:     def __repr__(self):
   ...:         return 'ArrayWrapper({!r})'.format(self.arr)
   ...:     def __array__(self):
   ...:         return self.arr
   ...:     def __array_wrap__(self, arr):
   ...:         return ArrayWrapper(arr)
   ...:     

In [10]: numpy.exp(ArrayWrapper(numpy.array([1, 2, 3])))
Out[10]: ArrayWrapper(array([ 2.71828183,  7.3890561 , 20.08553692]))
user2357112
  • 260,549
  • 28
  • 431
  • 505
  • That's exactly what i wanted to know. Thank you. The ufunc thing u mentioned is very new to me. What should i study to have a deep understanding in this? – Yosan Girma Jan 03 '19 at 07:24
  • @YosanGirma: The [Special attributes and methods](https://docs.scipy.org/doc/numpy-1.15.1/reference/arrays.classes.html#special-attributes-and-methods) section of the docs covers most of the hooks for customizing how objects interact with ufuncs. The most important under-the-hood part of NumPy to understand is definitely how array memory layout works; I'm not sure what the best thing to read for that is, but [this other section](https://docs.scipy.org/doc/numpy-1.15.1/reference/arrays.ndarray.html#internal-memory-layout-of-an-ndarray) of the docs looks reasonable. – user2357112 Jan 05 '19 at 04:58
  • Knowing how the various forms of [array indexing](https://docs.scipy.org/doc/numpy-1.15.1/reference/arrays.indexing.html#arrays-indexing) work is also very useful. – user2357112 Jan 05 '19 at 04:59
0

The difference between a Series and a ndarray object is that the Series object allows you to define your own labeled index and to access the elements of the Series using that index, which can be strings, floats, ints etc. whereas the ndarray object has fixed index starting from 0. The downside is that Series is about 10 times slower than a ndarray.

The Series is the primary building block of pandas. A Series represents a one-dimensional labeled indexed array based on the NumPy ndarray. Like an array, a Series can hold zero or more values of any single data type. A Series can be created and initialized by passing either a scalar value, a NumPy ndarray, a Python list, or a Python Dict as the data parameter of the Series constructor.
For more info, see pandas and NumPy arrays explained

ycx
  • 3,155
  • 3
  • 14
  • 26