2016 年 3 月

第 31 卷,第 3 期

此文章由机器翻 译。

Python - 面向 C# 开发者的 SciPy 编程简介

通过 James McCaffrey

字词数据科学中,没有正式定义,但我将其视为使用软件程序使用经典统计学和机器学习算法的数据进行分析。直到最近,很多数据科学分析执行通过昂贵的商业产品,但在过去几年中的开放源代码的备选方法使用增加了很大。

基于与我的同事交谈、 数据科学分析的三种最常用的开源方法是 R 语言、 Python 语言与 SciPy ("科学记数法 Python") 库中时,结合使用,并集成 SciLab 和八度音之类的语言和执行环境。

在本文中我将为您使用 SciPy 编程的一个快速教程,以便您可以了解确切它是什么,并且确定您是否希望花时间来了解它。本文假定您有一些经验 C# 或类似的通用编程语言,如 Java 中,但并不假定您了解有关 Python 或 SciPy。

在我看来,有关学习一种新的编程语言或技术的最困难的部分是刚开始,因此我将详细介绍如何安装和卸载) 运行 SciPy 程序所需的软件。然后,我将介绍多种方式来编辑和执行 SciPy 程序并解释了为什么我更喜欢使用集成开发环境 (空闲) 程序。

最后我将逐步引导您完成使用 SciPy 为了演示通过 C# 编程的异同解决线性方程的系统的代表性程序。图 1 显示了演示程序的输出,并让您了解本文所述观点。

代表 SciPy 程序输出
图 1 代表 SciPy 程序输出

安装 SciPy 堆栈

SciPy 堆栈有三个组件 ︰ Python、 NumPy 和 SciPy。Python 语言都有基本功能,如时循环控制结构和通用列表的数据类型,但有趣的是,没有内置数组类型。NumPy 库添加了对数组和矩阵,再加上一些相对简单的函数,如数组搜索和数组排序支持。SciPy 库添加中级和高级函数来处理存储在数组和矩阵中的数据。

若要运行 SciPy 程序 (从技术上讲一个脚本因为 Python 是解释,而不编译),安装 Python,然后 NumPy、 SciPy。安装并不太难,而且您可以安装包括全部三个组件的软件包。一个常见的捆绑包是 Anaconda 分发,这都是由在 Continuum 分析 continuum.io。但是,我将演示如何单独安装这些组件。

Python 支持 Windows 的几乎所有版本。若要安装 Python,请转到 python.org/downloads, ,其中您可以找到此选项以安装 Python 3.x 版或 2.x 版本 (请参阅 图 2)。在两个版本不完全兼容,但上都支持 NumPy 和 SciPy 库。我建议安装 2.x 版本,因为尚不支持 3.x 版某些第三方函数。

安装 Python
图 2 安装 Python

当您单击下载按钮时,则会立即运行.msi 安装程序或将其保存,以便您可以在更高版本运行的选项。您可以单击运行按钮。安装程序将使用一个向导。第一个屏幕询问您是否要为所有用户还是只为当前用户安装。默认情况下了解所有用户,因此请都单击下一步按钮。

下一个屏幕要求您指定的根安装目录。默认值为 C:\Python27 (而不是更常见的 C:\Program Files 目录),我建议使用默认位置,并单击下一步。下面的屏幕允许你包括或排除各种功能,如文档和实用程序工具喜欢 pip ("pip 安装 Python")。默认的 Python 功能可正常,因此,单击下一步按钮。

开始安装,您将看到一个带有一个熟悉的蓝色进度栏的窗口。当安装完成后时,您将看到带有完成按钮的窗口。单击该按钮。

默认情况下,Python 安装过程不会修改您的计算机的 PATH 环境变量。您需要将 C:\Python27 和 C:\Python27\Scripts C:\Python27\Lib\idlelib 添加到 PATH 变量,以便您可以从命令行界面运行 Python 和启动空闲编辑器,而无需导航到其目录位置。

您应该验证正确安装了 Python。启动命令行解释器并导航到您系统的根目录通过输入 cd \ 命令。现在,输入命令 python-版本 (请注意这两个短划线字符)。如果 Python 做出响应,它是已成功安装。

安装 NumPy 和 SciPy

则可以使用 Python pip 实用程序的源代码从安装 NumPy 和 SciPy 包。Pip 方法适用于纯 Python 代码的包但 NumPy 和 SciPy 有挂钩到已编译的 C 语言代码,因此将它们安装使用 pip 是十分复杂。

幸运的是,Python 社区的成员已为 NumPy 和 SciPy 创建预编译的二进制安装程序。我建议使用的那些保留在 SourceForge 存储库中。若要安装 NumPy,请转到 bit.ly/1Q3mo4M, ,其中将会看到各种版本的链接。我建议使用具有最多下载活动的最新版本。

您将看到的链接的列表。查找具有类似于 numpy-1.10.2-win32-撰写-python2.7.exe,名称的链接中所示 图 3。请确保您的 Python 版本具有对应的可执行文件并单击该链接。在短暂延迟后您将获取立即运行自解压缩可执行安装程序的选项或将其保存到更高版本安装。单击运行按钮。

安装 NumPy
图 3 安装 NumPy

NumPy 安装程序使用一个向导。第一个屏幕只显示介绍性的启动画面窗口。单击下一步按钮。下一个屏幕要求您指定的安装目录。安装程序将查找现有的 Python 安装,并建议安装 NumPy C:\Python27\Lib\site-packages 目录中。接受此位置,然后单击下一步。

下一个屏幕使您上一次机会取消安装,但不会区分二者。单击下一步按钮。在安装过程中,将看到进度窗口,并密切关注,如果您将看到一些有趣的日志记录消息。当 NumPy 安装完成后时,将显示带有完成按钮。单击它。然后,您将看到最终的安装程序已完成窗口与关闭按钮。单击它。

安装 NumPy 后下, 一步是安装 SciPy 包,这是与安装 NumPy 相同。转到 bit.ly/1QbwJ0z 并查找最近的、 合理运用的目录。进入该目录和找到的一个名似 scipy-0.16.1-win32-撰写-python2.7.exe 的可执行文件的链接,单击它以启动自解压缩可执行安装程序。

SciPy 堆栈的一个很好的特征是很容易卸载组件。您可以转到 Windows 控制面板、 程序和功能,选择的组件 (即,Python,或 NumPy 或 SciPy) 以删除,然后单击卸载按钮,并且该组件将快速地完全删除。

编辑和运行 SciPy 程序

如果您编写程序使用.NET 语言,没有可用的很多选项,您几乎可以肯定使用 Visual Studio。但是,您编写的 Python 程序时有许多选项。我建议使用空闲的编辑器和执行环境。

默认情况下,在 C:\Python27\Lib\idelib 目录位于 idle.bat 程序启动器文件。如果此目录添加到您的系统路径环境变量中时,您可以通过打开命令 shell 并输入空闲命令启动空闲。上半部分中所示,这将启动空闲 Python Shell 程序 图 4

编辑和运行使用空闲的某个程序
图 4 编辑和运行使用空闲的某个程序

您可以通过单击该文件创建新的 Python 源代码文件 |在菜单栏上的新文件项。此将打开类似查看单独的编辑器窗口的下半部分中所示 图 4。在编辑器窗口中键入这些七个语句 ︰

# test.py
import numpy as np
import scipy.misc as sm
arr = np.array([1.0, 3.0, 5.0])
print arr
n = sm.factorial(4)
print "4! = " + str(n)

然后将您的程序保存为 test.py 任何方便的目录中。现在你可以通过单击运行的运行程序 |在编辑器窗口中,或按快捷键 f5 运行模块菜单项。将在 Python 外壳窗口中显示程序的输出。简单 !

一些经验丰富的 Python 开发人员需要在空闲 potshots,因为它非常简单。但这正是我喜欢的原因。您不会获得附近的 Visual Studio 中,复杂的编程环境,但在您编写了不正确的代码时,却看到的语法着色和良好的错误消息生成器。

不要使用 IDLE 来编辑和运行程序,您可以使用任何文本编辑器,其中包括记事本,来编写和保存的 Python 程序。然后您可以执行该程序从命令行如下 ︰

C:\IntroToPython> python test.py

这假定您已经在您的系统路径环境变量的 python.exe 解释程序的路径。输出将显示在命令行界面。

有许多 Python Ide。一个流行的开放源 IDE,专门用于与 SciPy 一起使用是科学记数法 Python 开发环境 (Spyder) 程序。您可以找到有关它在信息 pythonhosted.org/spyder

空闲和 Spyder 的有趣替代方法是开放源代码 Python Tools for Visual Studio (PTVS) 插件。顾名思义,PTVS 将允许您编辑和运行使用 Visual Studio 的 Python 程序。您可以找到有关在 PTVS 信息 microsoft.github.io/PTVS

SciPy 演示程序

看一看中的 Python 程序 图 5, ,或更好的做法,键入或本文附带到 Python 编辑器将文件下载并运行该程序。演示不应为一组全面的 SciPy 示例,但是它旨在使您很好地了解何谓 SciPy 编程。

图 5 代表 SciPy 程序

# linear_systems.py
# Python 2.7
import numpy as np
import scipy.linalg as spla
def my_print(arr, cols, dec, nl):
  n = len(arr)
  fmt = "%." + str(dec) + "f" # like %.4f
  for i in xrange(n):  # alt: for x in arr
    if i > 0 and i % cols == 0:
      print ""
    print fmt % arr[i],
  if nl == True:
    print "\n"
def main():
  print "\nBegin linear system using SciPy demo \n"
  print "Goal is to solve the system: \n"
  print "3x0 + 4x1 - 8x2 = 9"
  print "2x0 - 5x1 + 6x2 = 7"
  print " x0 + 9x1 - 7x2 = 3"
  print ""
  A = np.matrix([[3.0, 4.0, -8.0],
                 [2.0, -5.0, 6.0],
                 [1.0, 9.0, -7.0]])
  b = np.array([9.0, 7.0, 3.0])      # b is an array
  b = np.reshape(b, (3,1))           # b is a col vector
  print "Matrix A is "
  print A
  print ""
  print "Array b is "
  my_print(b, b.size, 2, True)
  d = spla.det(A)
  if d == 0.0:
    print "Determinant of A is zero so no solution "
  else:
    Ai = spla.inv(A)
    print "Determinant of A is non-zero "
    print "Inverse of A is "
    print Ai
    print ""
  Aib = np.dot(Ai, b)
  print "A inverse times b is "
  print Aib
  print ""
  x = spla.solve(A, b)
  print "Using x = linalg.solve(A,b) gives x = "
  print x
  print ""
  try:
    A = np.array([[2.0, 4.0], [3.0, 6.0]])
    print "Matrix A is "
    print A
    print ""
    print "Inverse of A is "
    Ai = spla.inv(A)
    print Ai
  except Exception, e:
    print "Fatal error: " + str(e)
  print "\nEnd SciPy demo \n"
if __name__ == "__main__":
  main()

演示程序的两个注释行开头 ︰

# linear_systems.py
# Python 2.7

Python 2.x 和 3.x 版本并不完全兼容,因为它不是显式的有关您使用哪一版本的 Python 个好主意。接下来,演示加载整个 NumPy 模块和一个 SciPy 子模块 ︰

import numpy as np
import scipy.linalg as spla

您可以将这些语句为类似于 C# 程序为 Microsoft.NET Framework DLL 中添加一个引用,然后将引入使用 using 作用域的程序集的语句。线性代数代表 linalg 子模块。SciPy 被组织成 16 个主要子模块,加上两个实用程序子模块。接下来,演示实现程序定义的函数,以显示数组 ︰

def my_print(arr, cols, dec, nl):
  n = len(arr)
  fmt = "%." + str(dec) + "f" # like %.4f
  for i in xrange(n):  # alt: for x in arr
    if i > 0 and i % cols == 0:
      print ""
    print fmt % arr[i],
  if nl == True:
    print "\n"

Python 使用缩进,而不是大括号字符来分隔代码块。在这里,我使用缩进的两个空格以节省空间;大多数 Python 程序员用于缩进的四个空格。

函数 my_print 具有四个参数 ︰ 用于显示要显示值的每个值和一个标志,指示是否打印新行的小数位数的列数的数组。Len 函数将返回数组的大小 (即单元格的数目)。一种替代方法是使用数组大小属性 ︰

n = arr.size

Xrange 函数返回一个迭代器,并是标准的方法来遍历数组。一种替代方法是使用"适用于在 arr x"模式中的,这类似于 C# foreach 语句。

Python 和 C# 中的 C 语言具有根,因为很多 Python 语法是 C# 程序员所熟悉的。在演示中,%是取模运算符,但它也可以用来设置浮点点值输出; 的格式用作一个逻辑运算符而不是 (& a) (& a)= = 是一项检查等于运算符。和 True 和 False (首字母大写) 都是布尔值常量。

接下来,演示将创建一个名为程序定义函数 main,以启动与一些解释要解决的问题的 print 语句 ︰

def main():
  print "\nBegin linear system using SciPy demo \n"
  print "Goal is to solve the system: \n"
  print "3x0 + 4x1 - 8x2 = 9"
  print "2x0 - 5x1 + 6x2 = 7"
  print " x0 + 9x1 - 7x2 = 3"

目标是查找变量 x0,x1 和 x2 值以便满足所有三个等式。主名称不是 Python 关键字,因此它无法调用了任何内容。具有某种类型的主要功能并不是必需。对于短程序 (通常小于一页的代码),我通常摒弃了在主函数,并且只是以可执行语句开头。

接下来,演示程序问题可通过设置将系数值放入名为 A,将常量转换为一个名为 b ︰ 的 NumPy 数组 NumPy 3x3 矩阵

A = np.matrix([[3.0, 4.0, -8.0],
               [2.0, -5.0, 6.0],
               [1.0, 9.0, -7.0]])
b = np.array([9.0, 7.0, 3.0])

矩阵和数组此处的函数实际上接受 Python 列表 (由方括号) 具有硬编码值作为其参数。您还可以创建矩阵和数组使用 NumPy 零函数,并可从文本文件中读取数据到一个矩阵,则使用 loadtxt 函数为数组。

如果您生成了一个代数课,您可能还记得,若要解决的等式 Ax 系统 = x,其中 A 是矩形矩阵的系数和 b 是列的矩阵 (即,n 行而只有 1 列) 的常量,您必须找到的一个逆矩阵,反转矩阵乘法接着定时列矩阵 b b。

此时在演示中,b 是一个具有三个单元格而不是 3 x 1 列的矩阵数组。若要转换到列的矩阵 b,该演示程序使用调整形状函数 ︰

b = np.reshape(b, (3,1))

NumPy 库都有许多可以操作数组和矩阵的函数。例如,平展函数会将一个矩阵转换为数组。现在,事实证明,SciPy 矩阵乘法函数有足够的智能来推断你想如果乘矩阵且数组以便进行调整的调用不东西真的有必要在这里。

接下来,演示程序显示的值矩阵 A 和 b:

print "Matrix A is "
print A
print ""
print "Array b is "
my_print(b, b.size, 2, True)

在 Python 2.x,打印是一条语句,而不是函数,因为它是在 Python 中 3.x 中,因此括号是可选的。程序定义 my_print 函数不返回值,因此它相当于 void 的 C# 函数,如您所料调用。Python 支持命名参数调用,因此可能曾经为调用 ︰

my_print(arr=b, cols=3, dec=2, nl=True)

接下来,演示程序会查找的逆矩阵 a:

d = spla.det(A)
if d == 0.0:
  print "Determinant of A is zero so no solution "
else:
  Ai = spla.inv(A)
  print "Determinant of A is non-zero "
  print "Inverse of A is "
  print Ai

SciPy det 函数将返回一个方矩阵的行列式。如果系数线性方程的系统的矩阵具有一个决定等于零,则不能逆转矩阵。Python if-else 语句应该为您所熟悉。Python 具有的巧妙"elif"关键字,如果 else-if 控制结构,例如 ︰

if n < 0:
  print "n is negative"
elif n == 0:
  print "n equals zero"
else:
  print "n is positive"

接下来,演示解决了使用通过 NumPy 点函数的矩阵乘法的等式的系统 ︰

Aib = np.dot(Ai, b)
print "A inverse times b is "
print Aib

因此命名为点函数,因为矩阵乘法是一种所谓的点积。

接下来,演示程序直接解决的等式系统,使用 NumPy 解决函数 ︰

x = spla.solve(A, b)
print "Using x = linalg.solve(A,b) gives x = "
print x

许多 SciPy 和 NumPy 函数具有可选参数的默认值,这是某种程度上等效于 C# 方法重载。SciPy 解决函数具有五个可选参数。不同之处在于,当您看到 SciPy 或 NumPy 示例函数调用,即使您认为您了解该示例时,它是最好看一看文档以查看是否有任何有用的可选参数。

还有一些 NumPy 和 SciPy 库之间的重叠。例如,NumPy 包还具有一个求解函数的 linalg 子模块。但是,NumPy 解决函数没有任何可选参数。

接下来,演示程序的范例 Python try-除非机制 ︰

try:
  A = np.array([[2.0, 4.0], [3.0, 6.0]])
  Ai = spla.inv(A)
  print Ai
except Exception, e:
  print "Fatal error: " + str(e)

如果您曾经使用过 C# try catch 语句,这种模式应该为您所熟悉。在 C# 中,当连接字符串,您可以操作隐式。例如,在 C# 中可以编写 ︰

int n = 99;
Console.WriteLine("The value of n is " + n);

但时连接在 Python 中的字符串,必须明确执行使用强制转换使用 str 函数 ︰

n = 99
print "The value of n is " + str(n)

演示程序最后使用 print 语句和特殊的 Python incantation:

print "\nEnd SciPy demo \n"
if __name__ == "__main__":
  main()

演示程序的最后一个语句本来可以只是 main (),会被解释为一个指令,调用程序定义的函数主要,和该程序将运行正常。添加 if __name__ = ="__main__"模式 (请注意,有两个下划线字符之前和之后的名称和 main) 建立当前模块作为程序入口点。当 Python 程序开始执行时,该解释器内部标记所在的初始模块 ︰

"__main__"

因此,假设您有一些其他程序定义具有的模块可执行语句,并且其导入。如果 if 检查没有 Python 解释器将会看到已导入的模块中的可执行语句,并且执行它们。如果将放入稍有不同,向程序定义 Python 文件中添加 if 检查,这些文件可以由其他 Python 程序导入,并且不会导致问题。

那么,该点是什么?

这篇文章为您的第一反应也可能是类似,"嗯,这是所有有点有趣,但在日常工作中我实际上不需要解决的线性公式的系统或使用不明确的数学函数。" 是我的回答,"嗯,的确如此,但也许您不使用其中 SciPy 库的原因之一是功能的您不知道哪些类型的问题可以解决"。

另一个角度来讲,在我看来,开发人员往往会处理为其拥有的工具的问题。例如,如果您知道 Windows Communication Foundation (WCF),则您将使用 WCF (以及具有我慰问)。如果您向您的个人技能集中添加 SciPy,可能会发现具有您可以将转换为有用的信息的数据。


Dr.James McCaffrey供职于华盛顿地区雷蒙德市沃什湾的 Microsoft Research。他参与过多个 Microsoft 产品的工作,包括 Internet Explorer 和 Bing。Scripto可通过 jammc@microsoft.com 与 McCaffrey 取得联系。

感谢以下的微软技术专家对本文的审阅: Dan Liebling 和 Kirk Olynyk