
SciPy(发音为sai pie)是基于Numpy Python扩展的数学应用程序包。 通过为用户提供用于管理和可视化数据的高级命令和类,它大大扩展了Python的功能。 借助SciPy,交互式Python会话可为复杂系统(如MATLAB,IDL,Octave,R-Lab和SciLab)转变为相同的完整数据处理和原型开发环境。
引言
建议将SciPy软件包作为Anaconda的一部分安装。 建议您熟悉Python的基础知识,并准备好Numpy文档。 为了简洁和方便起见,我们同意将导入以下主要软件包(numpy,scipy和matplotlib):
import numpy as np import matplotlib as mpl import matplotlib.pyplot as plt
这是NumPy,SciPy源代码和文档中使用的官方导入协议。 遵循代码中的这些约定是可选的,但强烈建议您这样做。 建议分别导入每个软件包,如下所示:
from scipy import linalg, optimize
SciPy和NumPy具有完整的文档版本,涵盖了几乎所有功能。 它们可以在https://docs.scipy.org/上以HTML和PDF格式获得。 有些部分可能不完整或过时,例如 文档不断开发中。 由于SciPy是一个志愿者组织,并且依赖于社区,因此欢迎并积极鼓励您从提供反馈到改进文档和代码的参与。
稀疏图算法(scipy.csgraph)

刘易斯·卡罗尔(Lewis Carroll),1877年圣诞节快乐
考虑使用scipy.csgraph包作为1877年圣诞节由Lewis Carroll发明的儿童游戏“ 文字阶梯”的示例。 在这个游戏中,您需要查找单词之间的路径,一次替换一个字母。 例如,您可以通过如下连接“猿”和“人”来跟踪从猴子到人的路径:
请注意,每个步骤仅涉及更改单词的一个字母。 这只是从猴子到人的一条可能路径,但这是最短的路径吗? 因此,有必要找到两个给定单词之间的最短路径(阶梯)。 可以使用scipy.sparse.csgraph包解决此问题。
首先,我们需要一个有效单词列表。 许多操作系统都有这样的内置列表。 例如,在Linux中,可以在以下位置之一找到单词列表:
/usr/share/dict /var/lib/dict
另一个词源是词典,可在Internet上的各种站点上找到(在您喜欢的搜索引擎中查看)。 系统单词列表是一个文件,每行一个单词。 要使用系统单词列表,您需要阅读以下内容:
word_list = open('/usr/share/dict/words').readlines() word_list = map(str.strip, word_list)
因为 我们正在寻找三个字母的单词,那么我们将从常规列表中仅选择必要的单词。 还应排除以大写字母(专有名词)开头或包含非字母字符(例如撇号和连字符)的单词。 最后,检查生成的单词列表的大小。
word_list = [word for word in word_list if len(word) == 3] word_list = [word for word in word_list if word[0].islower()] word_list = [word for word in word_list if word.isalpha()] word_list = list (map (str.lower, word_list)) len (word_list)
591
现在,我们有了591个三字母单词的列表(确切的数字可能会根据所使用的特定词典而有所不同)。 这些单词中的每一个都是图表的顶部。 创建连接仅相差一个字母的顶点(单词对)的边。
我们使用一种相当有效的方式,这需要对numpy数组进行一些操作:
word_list = np.asarray(word_list) word_list.sort()
dtype('<U3')
现在我们有了一个数组,其中每个条目包含三个Unicode字符。 我们想找到所有只有一个字符的对。 让我们首先将每个单词转换为三维向量:
word_bytes = np.ndarray((word_list.size, word_list.itemsize), dtype='uint8', buffer=word_list.data)
(591, 3)
我们记得汉明距离是相同长度的两个向量中不同字符的数量。 使用汉明距离,我们确定连接单词对的边的长度。 每两个单词连接到单词阶梯的距离等于 在哪里 -一个单词中的字母数:
from scipy.spatial.distance import pdist, squareform from scipy.sparse import csr_matrix hamming_dist = pdist (word_bytes, metric = 'hamming') graph = csr_matrix (squareform (hamming_dist < 1.5 / 3))
比较距离时,使用非严格等式,因为否则结果对于浮点值可能不稳定。 如果单词中的两个字符不匹配,则不等式将提供所需的结果。 现在已经给出了图,我们将在图上的任何两个词之间执行最短路径搜索:
i1 = word_list.searchsorted ('ape') i2 = word_list.searchsorted ('man')
现在,您需要在图形上找到这两个顶点之间的最短路径。 为此,我们使用Dijkstra算法,该算法可让您找到指定顶点的所有可能距离:
from scipy.sparse.csgraph import dijkstra distances, predecessors = dijkstra(graph, indices=i1, return_predecessors=True) print(distances[i2])
5.0
因此,我们看到猴子和人之间的最短路径仅包含五个步骤。 我们可以使用算法返回的前任恢复此路径:
path = [] i = i2 while i != i1: path.append(word_list[i]) i = predecessors[i] path.append(word_list[i1]) print(path[::-1])
['ape', 'apt', 'opt', 'oat', 'mat', 'man']
楼梯的长度比初始示例少三步。 使用该模块的其他工具,您可以找到其他问题的答案。 例如,在单词阶梯中是否存在未连接三个字母的单词? 这是确定图的连通性的任务:
from scipy.sparse.csgraph import connected_components N_components, component_list = connected_components(graph) print(N_components)
15
在我们的示例中,对于三个字母的单词,有15个相关的组成部分:即15个不同的单词集,它们之间没有路径。 每组中有多少个单词? 我们可以从组件列表中找到:
[np.sum(component_list == i) for i in range(N_components)]
[577, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
我们得到了一个大图,其中包括577个顶点以及14个孤立图。 让我们看看这些词:
[list(word_list[np.where(component_list == i)]) for i in range(1, N_components)]
[['aha'], ['chi'], ['ebb'], ['gnu'], ['ism'], ['khz'], ['nth'], ['née'], ['ova'], ['qua'], ['ugh'], ['ups'], ['urn'], ['use']]
您可以找出在两个单词之间有最大长度的阶梯。 这可以通过计算邻接矩阵来确定。 请注意,两个未连接点之间的距离被认为是无限的,因此应将它们排除在外:
distances, predecessors = dijkstra(graph, return_predecessors=True) max_distance = np.max(distances[~np.isinf(distances)]) print(max_distance)
13.0
因此,在我们的示例中有这样的单词对,它们之间最短的楼梯包含13个台阶! 让我们确定这些对是什么:
i1, i2 = np.where(distances == max_distance) list(zip(word_list[i1], word_list[i2]))
[('imp', 'ohm'), ('imp', 'ohs'), ('ohm', 'imp'), ('ohm', 'ump'), ('ohs', 'imp'), ('ohs', 'ump'), ('ump', 'ohm'), ('ump', 'ohs')]
我们看到了成对的单词的所有可能组合,它们之间的距离为13(最大程度彼此分开)。 如果仔细观察,在楼梯的一侧是单词“ imp”和“ ump”,它们彼此不同,只有一个字母。 在楼梯的另一侧是单词“ ohm”和“ ohs”,它们也仅以一个字母来区分。 这些单词之间的步骤列表将几乎相同。 可以通过与上述相同的方式找到它:
path = [] i = i2[0] while i != i1[0]: path.append(word_list[i]) i = predecessors[i1[0], i] path.append(word_list[i1[0]]) print(path[::-1])
['imp', 'amp', 'asp', 'ass', 'ads', 'ids', 'ins', 'inn', 'ion', 'won', 'woo', 'who', 'oho', 'ohm']
单词阶梯只是scipy中稀疏图上快速算法的可能用途之一。 图论被用于数学,数据分析和机器学习的许多领域。 Scipy稀疏图工具足够灵活,可以处理各种各样的任务。
如果您在评论中写scipy的哪一部分将在下一篇文章中读到,我将很高兴。