scipy function always returns a numpy array

本秂侑毒 提交于 2019-12-04 17:12:57

The first line of the implementation in scipy.optimize.fsolve is:

x0 = array(x0, ndmin=1)

This means that your scalar will be turned into a 1-element sequence, and your 1-element sequence will be essentially unchanged.

The fact that it seems to work is an implementation detail, and I would refactor your code to not allow sending a scalar into fsolve. I know this might seem to go against duck-typing, but the function asks for an ndarray for that argument, so you should respect the interface to be robust to changes in implementation. I don't, however, see any problem with conditionally using x_guess = array(y, ndmin=1) for converting scalars into an ndarray in your wrapper function and converting the result back to scalar when necessary.

Here is the relevant part of docstring of fsolve function:

def fsolve(func, x0, args=(), fprime=None, full_output=0,
           col_deriv=0, xtol=1.49012e-8, maxfev=0, band=None,
           epsfcn=0.0, factor=100, diag=None):
    """
    Find the roots of a function.

    Return the roots of the (non-linear) equations defined by
    ``func(x) = 0`` given a starting estimate.

    Parameters
    ----------
    func : callable f(x, *args)
        A function that takes at least one (possibly vector) argument.
    x0 : ndarray
        The starting estimate for the roots of ``func(x) = 0``.

    ----SNIP----

    Returns
    -------
    x : ndarray
        The solution (or the result of the last iteration for
        an unsuccessful call).

    ----SNIP----

Here's how you can convert Numpy arrays to lists and Numpy scalars to Python scalars:

>>> x = np.float32(42)
>>> type(x)
<type 'numpy.float32'>
>>> x.tolist()
42.0

In other words, the tolist method on np.ndarray handles scalars specially.

That still leaves you with single-element lists, but those are easy enough to handle in the usual way.

I guess wims answer really already says it mostly, but maybe this makes the differences clearer.

The scalar returned by numpy should with array[0] should be (almost?) fully compatible to the standard python float:

a = np.ones(2, dtype=float)
isinstance(a[0], float) == True # even this is true.

For the most part already the 1 sized array is compatible to both a scalar and list, though for example it is a mutable object while the float is not:

a = np.ones(1, dtype=float)
import math
math.exp(a) # works
# it is not isinstance though
isinstance(a, float) == False
# The 1-sized array works sometimes more like number:
bool(np.zeros(1)) == bool(np.asscalar(np.zeros(1)))
# While lists would be always True if they have more then one element.
bool([0]) != bool(np.zeros(1))

# And being in place might create confusion:
a = np.ones(1); c = a; c += 3
b = 1.; c = b; c += 3
a != b

So if the user should not know about it, I think the first is fine the second it is dangerous.

You can also use np.asscalar(result) to convert a size 1 array (of any dimension) to the correct python scalar:

In [29]: type(np.asscalar(a[0])) Out[29]: float

If you want to make sure there are no surprises for a user who is not supposed to know about numpy, you will have to at least get the 0's element if a scalar was passed in. If the user should be numpy aware, just documentation is probably as good.

As @wim pointed out, fsolve transforms your scalar into a ndarray of shape (1,) and returns an array of shape (1,).

If you really want to get a scalar as output, you could try to put the following at the end of your function:

if solution.size == 1:
    return solution.item()
return solution

(The item method copies an element of an array and return a standard Python scalar)

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!