【问题标题】:Relationship between SciPy and NumPySciPy 和 NumPy 之间的关系
【发布时间】:2011-09-06 06:04:09
【问题描述】:

SciPy 似乎在其自己的命名空间中提供了 NumPy 的大部分(但不是全部 [1])函数。换句话说,如果有一个名为numpy.foo 的函数,那么几乎肯定有一个scipy.foo。大多数时候,两者看起来完全一样,甚至经常指向同一个函数对象。

有时,它们是不同的。举一个最近出现的例子:

  • numpy.log10 是一个 ufunc,它为否定参数返回 NaN;
  • scipy.log10 为负参数返回复数值,并且似乎不是 ufunc。

loglog2logn 也可以这样说,但log1p 则不然[2]。

另一方面,numpy.expscipy.exp 似乎是同一个 ufunc 的不同名称。 scipy.log1pnumpy.log1p 也是如此。

另一个例子是numpy.linalg.solve vs scipy.linalg.solve。它们相似,但后者比前者提供了一些额外的功能。

为什么会出现明显的重复?如果这意味着将 numpy 批量导入到 scipy 命名空间中,为什么会有细微的行为差异和缺少的功能?是否有一些总体逻辑可以帮助消除混淆?

[1] numpy.minnumpy.maxnumpy.abs 和其他一些在 scipy 命名空间中没有对应项。

[2] 使用 NumPy 1.5.1 和 SciPy 0.9.0rc2 测试。

【问题讨论】:

  • 我在答案中读到all of those functions are available without additionally importing Numpy 因为the intention is for users not to have to know the distinction between the scipy and numpy namespaces。现在我想知道,因为我关注了一些关于 numpy 和 scipy 的帖子并自己使用它。而且我几乎总是看到 numpy 被单独导入(作为 np)。所以他们失败了?
  • scipy 和 numpy 在 FFT 方面存在一些差异,我曾经被一个问题所困扰,最终追查到 scipy 和 numpy 的 rfft 版本定义不同
  • SciPy 和 NumPy 的 FFT 不同。 SciPy 使用 Fortran 库 FFTPACK,因此命名为 scipy.fftpack。 NumPy 使用名为 fftpack_lite 的 C 库;它的功能较少,仅支持 NumPy 中的双精度。 Enthought公司已修补他们的 numpy.fft 以使用英特尔 MKL 进行 FFT,而不是 fftpack_lite。
  • NumPy 最初命名为 scipy.core。 NumPy 和 SciPy 是密切相关的项目。分离的主要原因是确保数组库 (NumPy) 精简和平均,因为并不总是需要大量 SciPy。此外,科学家们决定退出数组包 numeric (MIT) 和 numarray (NASA) 以支持 scipy.core,因此它得名 NumPy。 SciPy 仍未达到 1.0,而 NumPy 目前发布为 1.8.1。 NumPy 有一些用于 FFT 和线性代数的工具,但不如 SciPy 广泛。
  • @SturlaMolden 很高兴了解 Enthought,您知道 Anaconda 是同时优化两者还是只优化 numpy?

标签: python numpy scipy


【解决方案1】:

上次查了一下,scipy__init__方法执行了一个

from numpy import *

以便在导入 scipy 模块时将整个 numpy 命名空间包含到 scipy 中。

您描述的log10 行为很有趣,因为两个 版本都来自numpy。一个是ufunc,另一个是numpy.lib 函数。为什么 scipy 更喜欢库函数而不是 ufunc,我不知道。


编辑:事实上,我可以回答log10 的问题。查看 scipy __init__ 方法我看到了这个:

# Import numpy symbols to scipy name space
import numpy as _num
from numpy import oldnumeric
from numpy import *
from numpy.random import rand, randn
from numpy.fft import fft, ifft
from numpy.lib.scimath import *

你在 scipy 中得到的log10 函数来自numpy.lib.scimath。查看该代码,它说:

"""
Wrapper functions to more user-friendly calling of certain math functions
whose output data-type is different than the input data-type in certain
domains of the input.

For example, for functions like log() with branch cuts, the versions in this
module provide the mathematically valid answers in the complex plane:

>>> import math
>>> from numpy.lib import scimath
>>> scimath.log(-math.exp(1)) == (1+1j*math.pi)
True

Similarly, sqrt(), other base logarithms, power() and trig functions are
correctly handled.  See their respective docstrings for specific examples.
"""

似乎该模块覆盖了sqrtloglog2lognlog10powerarccosarcsinarctanh 的基本 numpy ufunc。这解释了您所看到的行为。这样做的根本设计原因可能隐藏在某处的邮件列表中。

【讨论】:

  • 在全职使用这些包一段时间后,我的感觉是:NumPy 是一个用于数值数组的库,供任何在 Python 中需要此类对象的人使用。 SciPy 旨在成为科学家/工程师的图书馆,因此它旨在实现更严格的理论数学(因此包括 log10 的复数版本等)。主要的困惑来自于 NumPy 保留了许多旧的子模块(应该进入 Scipy),这些子模块在 SciPy/NumPy 之间的界限不像今天那样清晰时被包含在内。
  • @PhilMacKay 嗨,Phil,我从 2013 年开始阅读这篇文章和你针对这个 numpy/scipy 问题的其他帖子。我的问题是你的意见是否仍然是最新的,正如你在上面的评论中所说的那样?我看到张贴者说 scipy 中有一些非等价物并列出了 abs、max 和 min 作为示例,但我明白 abs 只是 numpy.absolute 的别名,并且有 scipy.absolute、scipy.maximum 和 scipy 。最低限度。因此,根据您迄今为止的经验,如果您已经需要 scipy,您是否曾经需要导入 numpy?
  • @PhilMacKay 似乎是普遍的共识是针对其相关用例使用SciPy的子模块库,然后针对核心的NumPy操作专门导入NumPy(而不是顶层的SciPy,否则您需要导入)。出于某种原因,其他人以及 SciPy 文档本身都将其声明为更好的编码实践,我试图理解为什么它很重要。我认为这是因为这是一个约定问题,因此是可读性问题。你目前的看法是什么?
  • @DanBoschen 截至 2018 年 11 月,我仍然坚持我的上述评论。在只需要 NumPy 时导入 SciPy 可能有点矫枉过正。另一方面,在加载 SciPy 时会导入 NumPy,因此除了 SciPy 之外,无需导入 NumPy。当然,遵循文档有很好的论据,因此请随意做与您自己的情况最相关的事情。
  • @PhilMacKay 感谢您的意见。经过我的猜测,为什么建议导入 numpy(即使一切都可以在 scipy 中完成)是一个约定问题,因此是共享代码的可读性。如果所有特定于 numpy 的代码都专门绑定到 numpy 库,那么它也可以更容易地从绑定到更大的 scipy 库中断开,其中包含更多可能并不总是需要的内容。这就是说我的想法(对于我自己的方法)是导入 numpy,然后不导入顶级 scipy,而只根据需要导入 scipy 子包。
【解决方案2】:

来自 SciPy 参考指南:

...所有的 Numpy 函数都有 被归入scipy 命名空间,以便所有这些 功能不可用 另外导入 Numpy。

这样做的目的是让用户不必知道scipynumpy 命名空间之间的区别,但显然您发现了一个例外。

【讨论】:

    【解决方案3】:

    SciPy FAQ 看来,NumPy 的一些函数是出于历史原因而存在的,而它应该 仅在 SciPy 中:

    NumPy 和 SciPy 有什么区别?

    在理想世界中,NumPy 只包含数组数据类型和 最基本的操作:索引、排序、整形、基本 逐元素函数等。所有数字代码将驻留在 科学派。然而,NumPy 的重要目标之一是兼容性,因此 NumPy 试图保留其前任所支持的所有功能。因此 NumPy 包含一些线性代数函数,尽管这些更多 正确地属于 SciPy。无论如何,SciPy 包含更全功能 线性代数模块的版本,以及许多其他数值 算法。如果你正在使用 python 进行科学计算,你应该 可能同时安装 NumPy 和 SciPy。大多数新功能都属于 SciPy 而不是 NumPy。

    这就解释了为什么scipy.linalg.solve 提供了一些优于numpy.linalg.solve 的附加功能。

    我没有看到 SethMMorton 对related question的回答

    【讨论】:

      【解决方案4】:

      introduction to SciPy 文档末尾有一个简短的注释:

      另一个有用的命令是source。当给定一个用 Python 编写的函数作为参数时,它会打印出该函数的源代码列表。这有助于了解算法或准确理解函数是什么 做它的论点。也不要忘记 Python 命令 dir 可以是 用于查看模块或包的命名空间。

      我认为这将允许对所涉及的所有包有足够了解的人来准确区分 some scipy 和 numpy 函数之间的区别(它对我的 log10 问题没有帮助)全部)。我绝对没有这方面的知识,但 source 确实表明 scipy.linalg.solvenumpy.linalg.solve 以不同的方式与 lapack 交互;

      Python 2.4.3 (#1, May  5 2011, 18:44:23) 
      [GCC 4.1.2 20080704 (Red Hat 4.1.2-50)] on linux2
      >>> import scipy
      >>> import scipy.linalg
      >>> import numpy
      >>> scipy.source(scipy.linalg.solve)
      In file: /usr/lib64/python2.4/site-packages/scipy/linalg/basic.py
      
      def solve(a, b, sym_pos=0, lower=0, overwrite_a=0, overwrite_b=0,
                debug = 0):
          """ solve(a, b, sym_pos=0, lower=0, overwrite_a=0, overwrite_b=0) -> x
      
          Solve a linear system of equations a * x = b for x.
      
          Inputs:
      
            a -- An N x N matrix.
            b -- An N x nrhs matrix or N vector.
            sym_pos -- Assume a is symmetric and positive definite.
            lower -- Assume a is lower triangular, otherwise upper one.
                     Only used if sym_pos is true.
            overwrite_y - Discard data in y, where y is a or b.
      
          Outputs:
      
            x -- The solution to the system a * x = b
          """
          a1, b1 = map(asarray_chkfinite,(a,b))
          if len(a1.shape) != 2 or a1.shape[0] != a1.shape[1]:
              raise ValueError, 'expected square matrix'
          if a1.shape[0] != b1.shape[0]:
              raise ValueError, 'incompatible dimensions'
          overwrite_a = overwrite_a or (a1 is not a and not hasattr(a,'__array__'))
          overwrite_b = overwrite_b or (b1 is not b and not hasattr(b,'__array__'))
          if debug:
              print 'solve:overwrite_a=',overwrite_a
              print 'solve:overwrite_b=',overwrite_b
          if sym_pos:
              posv, = get_lapack_funcs(('posv',),(a1,b1))
              c,x,info = posv(a1,b1,
                              lower = lower,
                              overwrite_a=overwrite_a,
                              overwrite_b=overwrite_b)
          else:
              gesv, = get_lapack_funcs(('gesv',),(a1,b1))
              lu,piv,x,info = gesv(a1,b1,
                                   overwrite_a=overwrite_a,
                                   overwrite_b=overwrite_b)
      
          if info==0:
              return x
          if info>0:
              raise LinAlgError, "singular matrix"
          raise ValueError,\
                'illegal value in %-th argument of internal gesv|posv'%(-info)
      
      >>> scipy.source(numpy.linalg.solve)
      In file: /usr/lib64/python2.4/site-packages/numpy/linalg/linalg.py
      
      def solve(a, b):
          """
          Solve the equation ``a x = b`` for ``x``.
      
          Parameters
          ----------
          a : array_like, shape (M, M)
              Input equation coefficients.
          b : array_like, shape (M,)
              Equation target values.
      
          Returns
          -------
          x : array, shape (M,)
      
          Raises
          ------
          LinAlgError
              If `a` is singular or not square.
      
          Examples
          --------
          Solve the system of equations ``3 * x0 + x1 = 9`` and ``x0 + 2 * x1 = 8``:
      
          >>> a = np.array([[3,1], [1,2]])
          >>> b = np.array([9,8])
          >>> x = np.linalg.solve(a, b)
          >>> x
          array([ 2.,  3.])
      
          Check that the solution is correct:
      
          >>> (np.dot(a, x) == b).all()
          True
      
          """
          a, _ = _makearray(a)
          b, wrap = _makearray(b)
          one_eq = len(b.shape) == 1
          if one_eq:
              b = b[:, newaxis]
          _assertRank2(a, b)
          _assertSquareness(a)
          n_eq = a.shape[0]
          n_rhs = b.shape[1]
          if n_eq != b.shape[0]:
              raise LinAlgError, 'Incompatible dimensions'
          t, result_t = _commonType(a, b)
      #    lapack_routine = _findLapackRoutine('gesv', t)
          if isComplexType(t):
              lapack_routine = lapack_lite.zgesv
          else:
              lapack_routine = lapack_lite.dgesv
          a, b = _fastCopyAndTranspose(t, a, b)
          pivots = zeros(n_eq, fortran_int)
          results = lapack_routine(n_eq, n_rhs, a, n_eq, pivots, b, n_eq, 0)
          if results['info'] > 0:
              raise LinAlgError, 'Singular matrix'
          if one_eq:
              return wrap(b.ravel().astype(result_t))
          else:
              return wrap(b.transpose().astype(result_t))
      

      这也是我的第一篇文章,所以如果我应该在这里更改一些内容,请告诉我。

      【讨论】:

      • 底层包装器非常不同。 NumPy 使用用 C 编写的薄层。SciPy 使用由 f2py 自动生成的层。 SciPy 总是与外部 LAPACK 库链接。 NumPy 使用了自己的 f2c'd lapack_lite,以防找不到外部 LAPACK。
      【解决方案5】:

      来自维基百科(http://en.wikipedia.org/wiki/NumPy#History):

      数字代码被改编为 它更易于维护和灵活 足以实现新颖的功能 数字阵列。这个新项目是一部分 的科学派。避免安装一个整体 打包只是为了得到一个数组对象, 这个新包装是分开的 称为 NumPy。

      scipy 依赖于 numpy 并将许多 numpy 函数导入其命名空间以方便使用。

      【讨论】:

        【解决方案6】:

        关于 linalg 包 - scipy 函数将调用 lapack 和 blas,它们在许多平台上以高度优化的版本提供,并提供非常好的性能,特别是对于相当大的密集矩阵上的操作。另一方面,它们不是易于编译的库,需要一个 fortran 编译器和许多特定于平台的调整才能获得完整的性能。因此,numpy 提供了许多常见线性代数函数的简单实现,这些函数通常足以满足多种用途。

        【讨论】:

        • numpy 1.10 有一个不错的模块 dual:“如果您想使用 numpy 版本(如果可用),则该模块应该用于 numpy 和 scipy 中的函数,否则使用 scipy 版本。”用法---from numpy.dual import fft, inv
        【解决方案7】:

        来自“Quantitative Economics”的讲座

        SciPy 是一个包,其中包含基于 NumPy 构建的各种工具,使用其数组数据类型和相关功能

        其实我们在导入SciPy的时候也得到了NumPy,从SciPy初始化文件可以看出

        # Import numpy symbols to scipy name space
        import numpy as _num
        linalg = None
        from numpy import *
        from numpy.random import rand, randn
        from numpy.fft import fft, ifft
        from numpy.lib.scimath import *
        
        __all__  = []
        __all__ += _num.__all__
        __all__ += ['randn', 'rand', 'fft', 'ifft']
        
        del _num
        # Remove the linalg imported from numpy so that the scipy.linalg package can be
        # imported.
        del linalg
        __all__.remove('linalg')
        

        但是,明确使用 NumPy 功能更为常见和更好的做法

        import numpy as np
        
        a = np.identity(3)
        

        在 SciPy 中有用的是其子包中的功能

        • scipy.optimize、scipy.integrate、scipy.stats 等

        【讨论】:

        • 我看到你的评论是明确使用 NumPy 功能是更好的做法,我看到这在其他地方也有呼应,包括在 SciPy 教程中,但为什么这是更好的做法?似乎没有人回答这个问题。如果您已经在导入 SciPy 并且它包含 NumPy 功能,为什么仍然导入 NumPy 会更好?是不是当我们在 SciPy 中导入子包时,我们并没有导入顶层,因此我们不应该采取专门导入 SciPy 的步骤,而应该只为那些核心数组处理功能导入 Numpy?
        【解决方案8】:

        除了SciPy FAQ描述的重复主要是为了向后兼容之外,在NumPy documentation中进一步澄清说

        可选的 SciPy 加速例程 (numpy.dual)

        可能被 Scipy 加速的函数的别名。

        可以构建 SciPy 以使用加速或其他改进的库 用于 FFT、线性代数和特殊函数。该模块允许 开发人员透明地支持这些加速功能时 SciPy 可用,但仍支持仅安装过的用户 NumPy。

        为简洁起见,这些是:

        • 线性代数
        • FFT
        • 第一类修正贝塞尔函数,0阶

        另外,来自SciPy Tutorial

        SciPy 的顶层还包含 NumPy 和 numpy.lib.scimath。但是,最好直接从 改为 NumPy 模块。

        因此,对于新应用程序,您应该更喜欢在 SciPy 的顶层中重复的数组操作的 NumPy 版本。对于上面列出的域,您应该首选 SciPy 中的域,并在必要时检查 NumPy 中的向后兼容性。

        以我个人的经验,我使用的大部分数组函数都存在于 NumPy 的顶层(random 除外)。但是,所有特定领域的例程都存在于 SciPy 的子包中,所以我很少使用 SciPy 顶层的任何东西。

        【讨论】:

          猜你喜欢
          • 2015-08-05
          • 2012-10-10
          • 2017-04-22
          • 2013-02-26
          • 2012-12-20
          • 2013-02-15
          • 2017-07-28
          • 2017-09-16
          • 2015-09-11
          相关资源
          最近更新 更多