pythonnumpy

Numpy min of a function returns the function it self


Okey I don't even know where to ask this or what I am exactly asking, but is basically what the title says: if the argument of numpy.min is a function, it doesn't matter if its defined by the user or not, the output is such function.

Let me put an example:

import numpy as np

def get_third_value(x): 
   return x[2]

xx = np.arange(10)

print(np.min(get_third_value)) # output:  <function the_third at 0x7ffb58c2b240>
print(np.min(max)) # output: <built-in function max>

# you can even use them: 
print(np.min(max)(xx)) # output : 9
print(np.min(get_third_value)(xx)) #  output: 2

Why this happens? Is this the intended behavior? If so, why?

I have checked other numpy functions and the same happens in some of them


Solution

  • In my current ipython session I have a foo function. In python functions are 'first class objects', that is, they can be passed around like other objects such as numbers and strings.

    In [361]: foo
    Out[361]: <function __main__.foo(a, b_list)>
    
    In [362]: np.min(foo)
    Out[362]: <function __main__.foo(a, b_list)>
    

    np.min, like many numpy functions, turns its non-array arguments into an array first:

    In [363]: np.array(foo)
    Out[363]: array(<function foo at 0x000001595F1CBF60>, dtype=object)
    

    In this case, and possibly many more, np.min just returns the only element of such a length 1 (0d) array.

    Compare this case of a python float:

    In [364]: np.min(1.23)
    Out[364]: np.float64(1.23)
    
    In [365]: np.array(1.23)
    Out[365]: array(1.23)
    
    In [366]: _.dtype
    Out[366]: dtype('float64')
    

    That float was made into an array with np.float64 dtype.

    Look at what happens when we give it two functions:

    In [367]: np.min([foo,foo])
    TypeError: '<=' not supported between instances of 'function' and 'function'
    

    Now it says it can't compare them. Now it's trying to do some sort of <=> comparison between the elements of this list. As an experiment, define a custom class, with (or without) at least one of the comparison methods, and see what np.min does.

    Another exception to returning a single item:

    In [368]: np.min('astring')
    UFuncTypeError: ufunc 'minimum' did not contain a loop with signature matching types (dtype('<U7'), dtype('<U7')) -> None
    
    
    In [369]: np.array('astring')
    Out[369]: array('astring', dtype='<U7')
    

    The string was turned into a numpy string dtype.

    Functions np.min have a catalog of signatures, telling it what can be worked on. (I first wrote that np.min is a ufunc. That's not true. np.minimum is a ufunc. I have an idea of how to look up the signatures of ufunc. I don't know about this type.)

    If the string is first made into an object dtype array, it behaves just like your function case:

    In [370]: np.min(np.array('string',object))
    Out[370]: 'string'
    

    In general when trying to understand the bahavior with non-array inputs, first look at what np.array(...) does with that. Without digging further into the compiled code it will be hard to expain exactly what/why this is happending.

    python min

    Compare what the python min does. It expacts an iterable, like a list:

    In [372]: min(foo)
    TypeError: 'function' object is not iterable
    
    
    In [373]: min([foo])
    Out[373]: <function __main__.foo(a, b_list)>
    
    In [374]: min([foo,foo])
    TypeError: '<' not supported between instances of 'function' and 'function'