Python 数学应用(二)(1)https://developer.aliyun.com/article/1506386
工作原理…
贝叶斯技术通过采用先验信念(概率分布)并使用贝叶斯定理将先验信念与给定此先验信念的数据的可能性相结合,形成后验信念。这实际上类似于我们在现实生活中理解事物的方式。例如,当你在某一天醒来时,你可能会相信(来自预报或其他方式)外面下雨的可能性是 40%。打开窗帘后,你看到外面非常多云,这可能表明下雨的可能性更大,因此我们根据这些新数据更新我们的信念,说有 70%的可能性会下雨。
要理解这是如何工作的,我们需要了解条件概率。条件概率涉及一个事件在另一个事件已经发生的情况下发生的概率。用符号表示,事件B发生的情况下事件A发生的概率如下所示:
贝叶斯定理是一个强大的工具,可以用以下方式(符号化)表示:
概率P(A)代表我们的先验信念。事件B代表我们收集到的数据,因此P(B | A)是我们的数据出现在我们先验信念下的可能性。概率P(B)代表我们的数据出现的可能性,P(A | B)代表我们的后验信念给定数据。在实践中,概率P(B)可能很难计算或估计,因此用贝叶斯定理的比例版本替换上面的强等式是非常常见的:
在这个配方中,我们假设我们的先验分布是 beta 分布。Beta 分布的概率密度函数由以下方程给出:
这里,Γ(α)是伽玛函数。可能性是二项分布的,其概率密度函数由以下方程给出:
这里,k是观察次数,j是其中一个成功的次数。在这个配方中,我们观察到m = 122次成功和n = 257 次失败,这给出k = m + n = 379和j = m = 122。要计算后验分布,我们可以使用 beta 分布是二项分布的共轭先验的事实,看到贝叶斯定理的比例形式的右侧是具有参数α + m**和β +* *n**的 beta 分布。这就是我们在这个配方中使用的。Beta 分布是二项随机变量的共轭先验的事实使它们在贝叶斯统计中非常有用。
**我们在这个配方中展示的方法是使用贝叶斯方法的一个相当基本的例子,但它仍然对以系统的方式更新我们的先验信念给出了有用的方法。
还有更多…
贝叶斯方法可以用于各种各样的任务,使其成为一个强大的工具。在这个配方中,我们使用了贝叶斯方法来基于我们对网站表现的先验信念和从用户那里收集到的额外数据来建模网站的成功率。这是一个相当复杂的例子,因为我们将我们的先验信念建模为 beta 分布。这里是另一个使用贝叶斯定理来检验两个竞争假设的例子,只使用简单的概率(0 到 1 之间的数字)。
假设你每天回家时都把钥匙放在同一个地方,但有一天早上你醒来发现它们不在那里。搜索了一会儿后,你找不到它们,于是得出结论它们必须已经从存在中消失了。让我们称这个假设为H[1]。现在,H[1]确实解释了你找不到钥匙的数据D,因此似然P(D | H[1]) = 1。 (如果你的钥匙从存在中消失了,那么你不可能找到它们。)另一个假设是你昨晚回家时把它们放在了别的地方。让我们称这个假设为H[2]。现在这个假设也解释了数据,所以P(D | H[2]) = 1,但实际上,*H[2]比H[1]*更合理。假设你的钥匙完全消失的概率是 100 万分之 1——这是一个巨大的高估,但我们需要保持合理的数字——而你估计你昨晚把它们放在别的地方的概率是 100 分之 1。计算后验概率,我们有以下结果:
这突显了一个现实,那就是你简单地把钥匙放错地方的可能性要比它们突然消失的可能性大 10,000 倍。果然,你很快就发现你的钥匙已经在口袋里了,因为你早上早些时候已经把它们拿起来了。
使用蒙特卡洛模拟估计参数
蒙特卡洛方法广泛描述了使用随机抽样解决问题的技术。当潜在问题涉及某种不确定性时,这些技术尤其强大。一般方法涉及执行大量模拟,每个模拟根据给定的概率分布抽样不同的输入,然后聚合结果,以给出比任何单个样本解更好的真实解的近似。
马尔可夫链蒙特卡洛(MCMC)是一种特定类型的蒙特卡洛模拟,其中我们构建一个马尔可夫链,逐步得到我们寻求的真实分布的更好近似。这是通过在每个阶段基于精心选择的接受概率接受或拒绝随机抽样的提议状态来实现的,旨在构建一个唯一的稳态分布恰好是我们希望找到的未知分布的马尔可夫链。
在这个食谱中,我们将使用 PyMC3 包和 MCMC 方法来估计一个简单模型的参数。该包将处理运行模拟的大部分技术细节,因此我们不需要进一步了解不同 MCMC 算法的工作原理。
准备工作
像往常一样,我们导入 NumPy 包和 Matplotlib pyplot
模块,分别命名为np
和plt
。我们还导入并创建一个默认的随机数生成器,为了演示目的,设置了一个种子:
from numpy.random import default_rng rng = default_rng(12345)
对于这个食谱,我们还需要从 SciPy 包中导入一个模块,以及 PyMC3 包,这是一个用于概率编程的包。
如何做…
执行以下步骤,使用马尔可夫链蒙特卡洛模拟来估计简单模型的参数:
- 我们的第一个任务是创建一个代表我们希望识别的基本结构的函数。在这种情况下,我们将估计二次函数的系数。这个函数接受两个参数,一个是固定的范围内的点,另一个是我们希望估计的变量参数:
def underlying(x, params): return params[0]*x**2 + params[1]*x + params[2]
- 接下来,我们设置
true
参数和一个size
参数,确定我们生成的样本中有多少点:
size = 100 true_params = [2, -7, 6]
- 我们生成将用于估计参数的样本。这将包括由我们在Step 1中定义的
underlying
函数生成的基础数据,以及遵循正态分布的一些随机噪音。我们首先生成一系列x值,这将在整个配方中保持不变,然后使用underlying
函数和我们的随机数生成器上的normal
方法来生成样本数据:
x_vals = np.linspace(-5, 5, size) raw_model = underlying(x_vals, true_params) noise = rng.normal(loc=0.0, scale=10.0, size=size) sample = raw_model + noise
- 在开始分析之前,将样本数据与基础数据叠加在一起是一个好主意。我们使用
scatter
绘图方法仅绘制数据点(不连接线),然后使用虚线绘制基础的二次结构:
fig1, ax1 = plt.subplots() ax1.scatter(x_vals, sample, label="Sampled data") ax1.plot(x_vals, raw_model, "k--", label="Underlying model") ax1.set_title("Sampled data") ax1.set_xlabel("x") ax1.set_ylabel("y")
结果是图 4.6,我们可以看到即使有噪音,基础模型的形状仍然可见,尽管这个模型的确切参数不再明显:
图 4.6:叠加了基础模型的采样数据
- 我们已经准备好开始我们的分析,所以现在导入 PyMC3 包并使用别名
pm
如下:
import pymc3 as pm
- PyMC3 编程的基本对象是
Model
类,通常使用上下文管理器接口创建。我们还为参数创建先验分布。在这种情况下,我们假设我们的先验参数服从均值为 1,标准差为 1 的正态分布。我们需要 3 个参数,因此我们提供shape
参数。Normal
类创建将在蒙特卡洛模拟中使用的随机变量:
with pm.Model() as model: params = pm.Normal("params", mu=1, sigma=1, shape=3)
- 我们为基础数据创建一个模型,可以通过将我们在Step 6中创建的随机变量
param
传递给我们在Step 1中定义的underlying
函数来完成。我们还创建一个处理我们观测值的变量。为此,我们使用Normal
类,因为我们知道我们的噪音在基础数据y
周围是正态分布的。我们设置标准差为2
,并将我们观察到的sample
数据传递给observed
关键字参数(这也在Model
上下文中):
y = underlying(x_vals, params) y_obs = pm.Normal("y_obs", mu=y, sigma=2, observed=sample)
- 要运行模拟,我们只需要在
Model
上下文中调用sample
例程。我们传递cores
参数以加快计算速度,但将所有其他参数保持默认值:
trace = pm.sample(cores=4)
这些模拟应该需要很短的时间来执行。
- 接下来,我们绘制使用 PyMC3 中的
plot_posterior
例程的后验分布。这个例程使用了从进行模拟的采样步骤中得到的trace
结果。我们提前使用plt.subplots
例程创建自己的图和坐标轴,但这并不是严格必要的。我们在单个图上使用了三个子图,并将axs2
的Axes
元组传递给绘图例程的ax
关键字参数:
fig2, axs2 = plt.subplots(1, 3, tight_layout=True) pm.plot_posterior(trace, ax=axs2)
结果图显示在图 4.7中,您可以看到每个分布都近似正态,均值与真实参数值相似:
图 4.7:估计参数的后验分布
- 现在通过使用
trace
中的params
项上的mean
方法检索每个估计参数的均值,这只是一个 NumPy 数组。我们传递axis=0
参数,因为我们想要矩阵参数估计的每一行的均值。我们在终端打印这些估计参数:
estimated_params = trace["params"].mean(axis=0) print("Estimated parameters", estimated_params) # Estimated parameters [ 2.03213559 -7.0957161 5.27045299]
- 最后,我们使用我们估计的参数通过将x值和估计的参数传递给Step 1中定义的
underlying
函数来生成我们估计的基础数据。然后我们在同一坐标轴上绘制这个估计的基础数据和真实的基础数据:
estimated = underlying(x_vals, estimated_params) fig3, ax3 = plt.subplots() ax3.plot(x_vals, raw_model, "k", label="True model") ax3.plot(x_vals, estimated, "k--", label="Estimated model") ax3.set_title("Plot of true and estimated models") ax3.set_xlabel("x") ax3.set_ylabel("y") ax3.legend()
结果图在图 4.8中,这两个模型在这个范围内只有很小的差异:
图 4.8:真实模型和估计模型绘制在同一坐标轴上。估计参数和真实参数之间存在一些小差异
它是如何工作的…
这个示例中代码的有趣部分可以在Model
上下文管理器中找到。这个对象跟踪随机变量,编排模拟,并跟踪状态。上下文管理器为我们提供了一个方便的方法,将概率变量与周围代码分开。
首先,我们为代表我们的参数的随机变量的分布提出了先验分布,其中有三个参数。我们提出了正态分布,因为我们知道参数不能偏离值 1 太远。(例如,通过查看我们在步骤 4中生成的图表可以得知。)使用正态分布将使靠近当前值的值具有更高的概率。接下来,我们添加了与观察数据相关的细节,这些细节用于计算用于接受或拒绝状态的接受概率。最后,我们使用sample
例程启动采样器。这构建了马尔可夫链并生成了所有步骤数据。
sample
例程根据将要模拟的变量的类型设置了采样器。由于正态分布是一个连续变量,sample
例程选择了无 U 转弯采样器(NUTS)。这是一个适用于连续变量的合理通用采样器。NUTS 的一个常见替代品是 Metropolis 采样器,在某些情况下比 NUTS 更快但不太可靠。PyMC3 文档建议尽可能使用 NUTS。
一旦采样完成,我们绘制了轨迹的后验分布(由马尔可夫链给出的状态),以查看我们生成的近似的最终形状。我们可以看到,我们的三个随机变量(参数)都大致上以正确的值为中心呈正态分布。
在幕后,PyMC3 使用 Theano 来加速计算。这使得 PyMC3 能够在图形处理单元(GPU)上执行计算,而不是在中央处理单元(CPU)上,从而大大提高了计算速度。Theano 还支持动态生成 C 代码以进一步提高计算速度。
还有更多…
蒙特卡洛方法非常灵活,我们在这里给出的例子是它可以使用的一个特定情况。蒙特卡洛方法应用的一个更典型的基本例子是估计积分的值,通常是蒙特卡洛积分。蒙特卡洛积分的一个非常有趣的案例是估计π的值≈3.1415。让我们简要地看一下它是如何工作的。
首先,我们取单位圆盘,其半径为 1,因此面积为π。我们可以将这个圆盘包含在一个顶点为(1,1),(-1,1),(1,-1),和(-1,-1)的正方形内。由于边长为 2,这个正方形的面积为 4。现在我们可以在这个正方形上均匀地生成随机点。当我们这样做时,任何一个随机点位于给定区域内的概率与该区域的面积成比例。因此,通过将随机生成的点中位于该区域内的比例乘以正方形的总面积,可以估计出一个区域的面积。特别地,我们可以通过简单地将位于圆盘内的随机生成点的数量乘以 4,并除以我们生成的总点数来估计圆盘的面积。
我们可以很容易地用 Python 编写一个执行这个计算的函数,可能是以下内容:
import numpy as np from numpy.random import default_rng def estimate_pi(n_points=10000): rng = default_rng() points = rng.uniform(-1, 1, size=(2, n_points)) inside = np.less(points[0, :]**2 + points[1, :]**2, 1) return 4.0*inside.sum() / n_points
仅运行此函数一次将给出对π的合理近似:
estimate_pi() # 3.14224
我们可以通过使用更多的点来提高我们的估计准确性,但我们也可以多次运行这个过程并平均结果。让我们运行这个模拟 100 次并平均结果(我们将使用并发 futures 来并行化这个过程,这样我们就可以运行更多的样本):
from concurrent.futures import ProcessPoolExecutor, as_completed from statistics import mean with ProcessPoolExecutor() as pool: fts = [pool.submit(estimate_pi) for _ in range(100)] results = list(ft.result() for ft in as_completed(fts)) print(mean(results))
运行此代码一次会打印出估计的π值为 3.1415752,这是对真实值的更好估计。
另请参阅
PyMC3 软件包有许多功能,有许多示例文档(docs.pymc.io/
)。还有另一个基于 TensorFlow 的概率编程库(www.tensorflow.org/probability
)。
进一步阅读
关于概率和随机过程的一个很好的综合参考书是以下书籍:
- Grimmett, G. and Stirzaker, D. (2009). Probability and random processes. 3rd ed. Oxford: Oxford Univ. Press*.*
对贝叶斯定理和贝叶斯统计的简单介绍如下:
- Kurt, W. (2019).Bayesian statistics the fun way. San Francisco, CA: No Starch Press, Inc*.*****
第六章:处理树和网络
网络是包含n**odes和节点对之间的edges的对象。它们可以用来表示各种真实世界的情况,如分布和调度。在数学上,网络对于可视化组合问题非常有用,并且构成了一个丰富而迷人的理论。
当然,有几种不同类型的网络。我们将主要处理简单的网络,其中边连接两个不同的节点(因此没有自环),任何两个节点之间最多只有一条边,并且所有边都是双向的。树是一种特殊类型的网络,其中没有循环;也就是说,没有节点列表,其中每个节点都通过一条边连接到下一个节点,并且最后一个节点连接到第一个节点。树在理论上特别简单,因为它们用尽可能少的边连接了许多节点。完全网络是一种网络,其中每个节点都通过一条边连接到其他每个节点。
网络可以是有向的,其中每条边都有源节点和目标节点,或者可以携带额外的属性,如权重。在某些应用中,加权网络特别有用。还有一些网络,我们允许两个给定节点之间有多条边。
在本章中,我们将学习如何创建、操作和分析网络,然后应用网络算法来解决各种问题。
在文献中,特别是在数学文本中,网络更常被称为图。节点有时被称为顶点。我们更倾向于使用术语网络,以避免与图常用于表示函数图的更常见用法混淆。
在本章中,我们将涵盖以下配方:
- 在 Python 中创建网络
- 可视化网络
- 获取网络的基本特征
- 为网络生成邻接矩阵
- 创建有向和加权网络
- 在网络中查找最短路径
- 量化网络中的聚类
- 给网络着色
- 查找最小生成树和支配集
让我们开始吧!
技术要求
在本章中,我们将主要使用 NetworkX 包来处理树和网络。可以使用您喜欢的软件包管理器(如pip
)安装此软件包:
python3.8 -m pip install networkx
通常,我们按照官方 NetworkX 文档中建立的约定,将其别名为nx
导入:
import networkx as nx
本章的代码可以在 GitHub 存储库的Chapter 05
文件夹中找到:github.com/PacktPublishing/Applying-Math-with-Python/tree/master/Chapter%2005
。
查看以下视频以查看代码的实际操作:bit.ly/2WJQt4p
。
在 Python 中创建网络
为了解决可以表示为网络问题的多种问题,我们首先需要一种在 Python 中创建网络的方法。为此,我们将利用 NetworkX 包及其提供的例程和类来创建、操作和分析网络。
在这个示例中,我们将创建一个代表网络的 Python 对象,并向该对象添加节点和边。
准备工作
正如我们在技术要求部分中提到的,我们需要导入 NetworkX 包,并使用以下import
语句将其别名为nx
:
import networkx as nx
如何做…
按照以下步骤创建简单图的 Python 表示形式:
- 我们需要创建一个将存储构成图的节点和边的新
Graph
对象:
G = nx.Graph()
- 接下来,我们需要使用
add_node
方法为网络添加节点:
G.add_node(1) G.add_node(2)
- 为了避免重复调用此方法,我们可以使用
add_nodes_from
方法从可迭代对象(如列表)中添加节点:
G.add_nodes_from([3, 4, 5, 6])
- 接下来,我们需要使用
add_edge
方法或add_edges_from
方法在我们添加的节点之间添加边,分别向网络添加单个边或边的列表(作为元组):
G.add_edge(1, 2) # edge from 1 to 2 G.add_edges_from([(2, 3), (3, 4), (3, 5), (3, 6), (4, 5), (5, 6)])
- 最后,通过访问
nodes
和edges
属性,我们可以检索图中当前节点和边的视图:
print(G.nodes) print(G.edges) # [1, 2, 3, 4, 5, 6] # [(1, 2), (2, 3), (3, 4), (3, 5), (3, 6), (4, 5), (5, 6)]
工作原理…
NetworkX 软件包添加了几个类和例程,用于使用 Python 创建、操作和分析网络。Graph
类是表示不包含任何给定节点之间多条边的网络的最基本类,其边是无向的(双向的)。
创建一个空的Graph
对象后,我们可以使用本示例中描述的方法添加新节点和边。在这个示例中,我们创建了保存整数值的节点。然而,节点可以保存除None
之外的任何可散列的 Python 对象。此外,可以通过传递给add_node
方法的关键字参数向节点添加关联数据。在使用add_nodes_from
方法时,还可以添加属性,方法是提供包含节点对象和属性字典的元组列表。add_nodes_from
方法用于批量添加节点,而add_node
用于将单个节点附加到现有网络。
网络中的边是包含两个(不同的)节点的元组。在简单网络中,例如基本的Graph
类表示的网络中,任何两个给定节点之间最多只能有一条边。边是通过add_edge
或add_edges_from
方法添加的,分别向网络添加单个边或边的列表。与节点一样,边可以通过属性字典保存任意关联数据。特别是,可以通过在添加边时提供weight
属性来添加权重。我们将在创建有向和加权网络中提供有关加权图的更多细节。
nodes
和edges
属性分别保存构成网络的节点和边。nodes
属性返回一个NodesView
对象,它是节点及其关联数据的类似字典的接口。类似地,edges
属性返回一个EdgeView
对象。这可以用于检查单个边及其关联数据。
还有更多…
Graph
类表示简单网络,这些网络是指节点之间最多只有一条边相连,并且边是无向的。我们将在创建有向和加权网络中讨论有向网络。有一个单独的类用于表示节点对之间可以有多条边的网络,称为MultiGraph
。所有网络类型都允许自环,这在文献中有时不允许在“简单网络”中,在那里简单网络通常指的是没有自环的无向网络。
所有网络类型都提供了各种方法来添加节点和边,以及检查当前节点和边。还有一些方法可以将网络复制到其他类型的网络中,或者提取子网络。NetworkX 软件包中还有几个实用程序例程,用于生成标准网络并将子网络添加到现有网络中。
NetworkX 还提供了各种例程,用于将网络读取和写入不同的文件格式,例如 GraphML、JSON 和 YAML。例如,我们可以使用nx.write_graphml
例程将网络写入 GraphML 文件,并使用nx.read_graphml
例程进行读取。
可视化网络
分析网络的常见第一步是绘制网络,这可以帮助我们识别网络的一些显著特征。(当然,绘图可能会产生误导,因此我们不应过分依赖它们进行分析。)
在这个示例中,我们将描述如何使用 NetworkX 软件包中的网络绘图工具来可视化网络。
准备工作
对于本示例,我们需要按照技术要求部分中描述的方式导入 NetworkX 包,并且还需要 Matplotlib 包。像往常一样,我们使用以下import
语句将pyplot
模块导入为plt
:
import matplotlib.pyplot as plt
如何做…
以下步骤概述了如何使用 NetworkX 的绘图例程绘制简单的网络对象:
- 首先,我们创建一个简单的示例网络来绘制:
G = nx.Graph() G.add_nodes_from(range(1, 7)) G.add_edges_from([ (1, 2), (2, 3), (3, 4), (3, 5), (3, 6), (4, 5), (5, 6) ])
- 接下来,我们为其创建新的 Matplotlib
Figure
和Axes
对象,准备使用plt
的subplots
例程绘制网络:
fig, ax = plt.subplots()
- 现在,我们可以创建一个布局,用于在图上放置节点。对于这个图,我们将使用
shell_layout
例程使用壳布局:
layout = nx.shell_layout(G)
- 我们可以使用
draw
例程在图上绘制网络。由于我们已经创建了 Matplotlib 的Figure
和Axes
,我们将提供ax
关键字参数。我们还将使用with_labels
关键字参数为节点添加标签,并使用pos
参数指定我们刚刚创建的布局:
nx.draw(G, ax=ax, pos=layout, with_labels=True) ax.set_title("Simple network drawing")
生成的绘图如下图所示:
图 5.1:使用壳布局排列的简单网络的绘图
工作原理…
draw
例程是专门用于绘制网络的专用绘图例程。我们创建的布局指定了每个节点将被放置的坐标。我们使用了壳布局,它将节点排列在同心圆的布局中,这由网络的节点和边确定。默认情况下,draw
例程会创建一个随机布局。
draw
例程有许多关键字参数,用于自定义绘制网络的外观。在本示例中,我们添加了with_labels
关键字参数,根据节点所持有的对象在图中标记节点。节点持有整数,这就是为什么前面的图中的节点被标记为整数。
我们还使用plt.subplots
例程单独创建了一组坐标轴。这并不是严格必要的,因为如果没有提供,draw
例程将自动创建新的图和坐标轴。
还有更多…
NetworkX 包提供了几种生成布局的例程,类似于我们在本示例中使用的shell_layout
例程。布局简单地是一个由节点索引的字典,其元素是节点应该被绘制的位置的x和y坐标。NetworkX 用于创建布局的例程表示了对大多数情况有用的常见布局,但如果需要,您也可以创建自定义布局。不同布局创建例程的完整列表在 NetworkX 文档中提供。还有一些快捷绘图例程,它们将使用特定布局而无需单独创建布局;例如,draw_shell
例程将使用与本示例中给出的draw
调用等效的壳布局绘制网络。
draw
例程接受许多关键字参数来自定义图形的外观。例如,有关键字参数来控制节点的大小、颜色、形状和透明度。我们还可以添加箭头(用于有向边)和/或仅从网络中绘制特定的节点和边。
获取网络的基本特征
网络具有各种基本特征,除了节点和边的数量之外,这些特征对于分析图形是有用的。例如,节点的度是以该节点为起点(或终点)的边的数量。较高的度表明该节点与网络的其余部分连接更好。
在本示例中,我们将学习如何访问基本属性并计算与网络相关的各种基本度量。
准备工作
像往常一样,我们需要将 NetworkX 包导入为nx
。我们还需要将 Matplotlib 的pyplot
模块导入为plt
。
如何做…
按照以下步骤访问网络的各种基本特征:
- 创建一个我们将在本示例中分析的示例网络,如下所示:
G = nx.Graph() G.add_nodes_from(range(10)) G.add_edges_from([ (0, 1), (1, 2), (2, 3), (2, 4), (2, 5), (3, 4), (4, 5), (6, 7), (6, 8), (6, 9), (7, 8), (8, 9) ])
- 接下来,将网络绘制并将节点布置在圆形布局中是一个良好的做法:
fig, ax = plt.subplots() nx.draw_circular(G, ax=ax, with_labels=True) ax.set_title("Simple network")
可以在下图中看到生成的图。正如我们所看到的,网络分为两个不同的部分:
图 5.2:以圆形排列绘制的简单网络。在这个网络中可以看到两个不同的组件
- 接下来,我们使用
nx.info
例程显示有关网络的一些基本信息:
print(nx.info(G)) # Name: # Type: Graph # Number of nodes: 10 # Number of edges: 12 # Average degree: 2.4000
- 现在,我们使用
Graph
对象的degree
属性来检索特定节点的度:
for i in [0, 2, 7]: degree = G.degree[i] print(f"Degree of {i}: {degree}") # Degree of 0: 1 # Degree of 2: 4 # Degree of 7: 2
- 我们可以使用
connected_components
例程获取网络的连接组件,它返回一个我们可以转换为列表的生成器:
components = list(nx.connected_components(G)) print(components) # [{0, 1, 2, 3, 4, 5}, {8, 9, 6, 7}]
- 我们使用
density
例程计算网络的密度,它返回一个介于 0 和 1 之间的浮点数。这代表了满足节点的边与节点可能的总边数之间的比例:
density = nx.density(G) print("Density", density) # Density 0.26666666666666666
- 最后,我们可以使用
check_planarity
例程确定网络是否平面——意味着没有两条边需要绘制交叉——:
is_planar, _ = nx.check_planarity(G) print("Is planar", is_planar) # Is planar True
工作原理…
info
例程生成网络的一个小总结,包括网络的类型(在本示例中是简单的Graph
类型),节点和边的数量,以及网络中节点的平均度。可以使用degree
属性访问网络中节点的实际度,该属性提供类似字典的接口来查找每个节点的度。
如果一组节点中的每个节点都通过边或一系列边连接到其他节点,则称为连接的。网络的连接组件是连接的最大节点集。任何两个不同的连接组件显然是不相交的。每个网络可以分解为一个或多个连接的组件。我们在本示例中定义的网络有两个连接的组件,{0, 1, 2, 3, 4, 5}
和{8, 9, 6, 7}
。这些在前面的图中清晰可见,第一个连接的组件绘制在第二个连接的组件上方。在这个图中,我们可以沿着网络的边从一个组件中的任何节点到达另一个组件中的任何节点;例如,从 0 到 5。
网络的密度衡量了网络中边的数量与网络中节点数量给出的总可能边数之间的比率。完全网络的密度为 1,但一般情况下,密度会小于 1。
如果网络可以在平面表面上绘制而不交叉,则网络是平面的。非平面网络的最简单示例是具有五个节点的完全网络。至多具有四个节点的完全网络是平面的。通过在纸上绘制这些网络的方式进行一些实验,将会发现一个不包含交叉边的图。此外,任何包含至少五个节点的完全图的网络都不是平面的。平面网络在理论上很重要,因为它们相对简单,但在应用中出现的网络中它们较少。
还有更多…
除了网络类中的方法之外,NetworkX 包中还有许多其他例程可用于访问网络中节点和边的属性。例如,nx.get_node_attributes
从网络中的每个节点获取一个命名属性。
Python 数学应用(二)(3)https://developer.aliyun.com/article/1506391