美国职业足球大联盟分析的现状
作者图片
目标:
在此分析中,目标是检查美国职业足球大联盟(MLS)的当前分析状态。俱乐部如何构建他们的分析部门,什么样的职位需要数据相关的任务。大部分美国职业棒球大联盟的特许经营权遵循的文化是在俱乐部的业务方面成为数据驱动的决策者。然而,越来越多的俱乐部在体育方面的决策中使用数据。这份报告的重点是从足球运营的角度。凭借在 MLS 超过四年的分析经验,我想分析联盟内的分析现状。我试图回答的问题如下:
- 每个团队目前的人员配备情况如何?具体来说,我们只关注与数据相关的职位。
- 东部联盟和西部联盟在人员配备上有区别吗?
- 足球运营中不同部门的数据是如何流动的?
作者图片
方法:
**数据收集:**第一步是收集数据以进行分析。我自己从每个俱乐部的网站和通过我在 MLS 分析社区的联系人收集数据。**注意:**迈阿密 FC 过去没有发布任何关于他们分析人员的信息。我不得不使用 LinkedIn 和其他平台自己做一些研究。
**变量:**下面是数据集中所有变量的列表。我还解释了所有的变量,以及一个人要成为那个特定角色的成功候选人需要什么样的技能。
**球队:**分析包括所有参加过 2020 MLS 赛季的球队。
- 亚特兰大联队
- 芝加哥大火
- 科罗拉多急流
- 哥伦布船员
- 华盛顿联合航空公司
- 辛辛那提足球俱乐部
- 达拉斯足球俱乐部
- 休斯顿迪纳摩
- 洛杉矶足球俱乐部
- 洛杉矶银河
- 迈阿密 CF
- 明尼苏达联合队
- 蒙特利尔影响
- 南卡罗来纳州纳什维尔
- 新英格兰革命
- 纽约市足球俱乐部
- 纽约红牛队
- 奥兰多市 SC
- 费城联盟
- 波特兰木材公司
- 真正的盐湖
- 圣何塞地震
- 西雅图海湾者
- 堪萨斯城体育
- 多伦多足球俱乐部
- 温哥华白浪
**体育表演科学负责人:**需要有很强的分析能力。评估受伤运动员的状况时,绩效科学家需要观察受伤情况,并正确分析和解释数据,以确定其程度。
**运动科学家/表现专员:**需要有很强的分析能力,收集和提取数据。体育科学家负责监控运动员的表现,跟踪身体数据,以评估疲劳程度,防止受伤。除了技术和分析技能,他们还需要领导和发展健身训练计划。
**足球分析/数据科学总监:**需要收集和处理数据进行分析。足球分析总监负责将分析和数据科学整合到俱乐部的每个足球方面,包括对手分析、国内和国际球员招募以及表现预测。通常管理绩效分析师和数据分析师。
**绩效分析师:**需要有很强的分析能力。性能分析师负责赛前和赛后分析。性能分析师还负责现场比赛日编码和编辑比赛和训练镜头以供分析。创建各方面性能的报告。向工作人员和运动员解释成绩数据。此外,负责保持统计和视频数据库的更新。
**数据分析师:**需要成为从数据库和其他技术中收集和提取数据的技术专家。职责包括设计和维护数据库,从各种来源挖掘数据,使用统计工具解释数据集,并为员工、球员和行政领导准备报告。
**视频/数据分析师:**参见上文对性能分析师和数据分析师的描述。预算较低的俱乐部雇佣一个人负责视频和数据分析。
**总经理/总经理助理:**随着技术的发展和职业体育特许经营使用更多的数据,总经理需要具备良好的分析技能。他们不处理原始数据,但需要理解和解释在数据帮助下开发的解决方案。数据分析师或绩效分析师被提升为总经理助理甚至总经理的趋势越来越明显。
球探总监/技术总监/助理蔻驰:对教练、球探和技术总监的需求也增加了。他们需要更加适应 Wyscout、Instat、Opta 等技术和平台。越来越多的趋势是助理教练进行视频分析,而不是全职的视频分析师。
**赛区:**球队分为东部(E)赛区和西部(W)赛区。
结果:
截至 2021 年 1 月 14 日,亚特兰大联队和温哥华白浪队在日常任务中在一定程度上使用数据的员工最多。
作者图片
Atlanta United 有一名头衔为“运动性能科学总监”的员工、三名性能分析师和一名足球分析总监。Whitecaps 还有一名头衔为“运动性能科学总监”的员工、两名性能分析师和两名数据科学家。平均而言,每个俱乐部的足球运营部门都有三名全职员工处理数据。
作者图片
当我们按会议(东部和西部)查看员工总数时,我们可以看到两个会议非常接近。东部联盟有 40 名工作人员,西部联盟共有 37 名。一个让我印象深刻的数字是,东部联盟团队中拥有“绩效分析师”头衔的员工几乎是其他团队的两倍。东部联盟球队有 20 名性能分析师,西部联盟球队总共有 11 名。
作者图片
一般来说,关于足球运营中不同部门之间的数据流动,MLS 内的团队之间有一个标准的组织结构。至少有一名在足球操作方面技术熟练的雇员处理原始数据。此人可以是数据科学家、数据分析师或足球分析总监。这很难想象,但你会不时发现没有内部分析部门的俱乐部。然而,他们找到了不同的方法来运行他们的分析。例如,圣何塞地震与当地大学合作,支持他们的分析,或者俱乐部聘请第三方提供商进行分析,就像芝加哥火灾过去所做的那样。
作者图片
当我们看上面的数据流程图时,我们看到三种不同类型的数据。在足球比赛中,数据分为物理数据、事件数据和跟踪数据。为了更好地理解数据如何在不同部门流动,我们首先需要理解每个部门的含义。
**身体数据:**可以想象,身体数据为我们提供了球员身体表现的信息。比如速度、走过的距离、心率等。
**事件数据:**这种类型的数据描述了球上发生的事情。事件数据可以是射门、传球、传中、铲球等。
**追踪数据:**在这里,我们描述所有球员在球上和球下的位置坐标。
如今,事件数据随处可见。大联盟的大多数球队都和 Opta,Instat,Wyscout 合作。另一方面,物理信息通过可穿戴设备变得可用,例如安装在体育场内的弹射器或摄像头。MLS 在 2020 年初与 Second Spectrum 合作,后者使用机器学习和人工智能来收集跟踪、身体和事件数据。结果,光学跟踪系统不得不安装在每个大联盟体育场。点击此处查看第二光谱能提供什么。
如数据流程图所示,数据流经足球运营部门的三个不同部门。一个是体育科学部,两个技术部,三个招聘部。
流经体育科学部的数据主要是身体数据。目的是管理运动员在训练和比赛中的负荷,最终目的是提高成绩和防止受伤。技术和招聘部门在其分析中包括所有三种不同类型的数据。教练将事件数据和关于对手分析、比赛评估或运动员发展的跟踪数据结合起来。除此之外,技术人员还通过他们的现场比赛分析来使用数据。体育数据主要用于根据运动科学团队的建议制定训练计划。招聘部门在日常工作中也包括所有三种数据类型。事件数据主要用于识别玩家的开始阶段。例如,如果一个俱乐部正在寻找一名前锋,一名特定前锋的进球记录是人们首先要看的统计数据。举例来说,如果一个前锋在过去的两个赛季里没有进球,你会认为他不能胜任他的工作。一旦确定了一个玩家,就可以更详细地使用所有三种数据类型。回到我们的前锋例子,身体数据可以方便地看到前锋平均走了多远。如果前锋努力无球跑动,这可能会帮助球探回答这个问题。当涉及到球员比较时,跟踪数据也变得很方便。你可以和其他前锋比较坐标,看看他们的动作是否相似。最终,每个部门的最终目标都是让俱乐部获得相对于其他团队的竞争优势。
结论:
基于我们在数据和研究中的发现,我们可以得出以下结论:
- 平均而言,MLS 中的俱乐部在足球运营部门有三名员工以某种身份处理数据。
- 这两个会议中与数据相关的员工数量几乎相等(东部 40 人,西部 37 人)。
- 东部联盟的性能分析师几乎是西部联盟的两倍。(东部 20 人,西部 11 人)。亚特兰大联队和纽约市足球俱乐部在一线队级别的每个俱乐部的绩效分析师总数方面领先。两者一共三个。
- 即使大多数 MLS 俱乐部在如何将数据和分析纳入运营程序方面有相似的结构,一些球队仍然以不同的方式做事。例如,如上所述,圣何塞地震队是联盟中唯一一个在分析方面完全依赖当地大学合作的团队。身在硅谷很有帮助,因为周围当地大学里精通技术的学生数量和人才储备都很高。尽管许多大联盟俱乐部与当地大学有合作关系,但地震俱乐部是唯一没有内部分析部门的俱乐部。
- 即使大多数俱乐部都有全职的表现分析师,但也有一些俱乐部的助理教练完全负责视频分析。再次,一个例子是圣何塞和他们现任主教练马迪亚斯·阿尔梅达或国际米兰,他们的视频分析师上赛季是他们教练组的一部分。
- 有一种趋势是,以前的绩效分析师或其他数据相关职位晋升为助理总经理或总经理。仅举几个例子,蒙特利尔影响,LAFC,科罗拉多急流,都有来自分析背景的总经理或助理总经理。
我希望这篇分析很有见地,能让你更好地理解 MLS 内部的分析现状。如果你对这个话题有任何问题或者有任何反馈,请随时联系我。如果你能在任何社交媒体平台上分享它,我将不胜感激。谢谢你,下次再见 time️!✌️
用 Python 和 Kafka 制作一个模拟的“实时”数据流
这是一个 Dockerized 教程,提供了将带有时间戳的. csv 文件转换成 Kafka 流所需的一切
阿布拉卡-卡夫卡!(作者画的漫画很差。)
随着越来越多的数据科学工作转向实时管道,数据科学家需要学习编写流分析。虽然有一些伟大的、用户友好的流数据管道工具存在(我最喜欢的显然是 Apache Kafka。)如果没有一个友好的开发环境来实际生成可以测试您的分析的数据流,就很难开发流分析的代码。
实时数据流的简单方法
本文将介绍如何部署一个简单的基于 Python 的 Kafka producer,它从带有时间戳的. csv 文件中读取数据,将数据转换成实时(或者说,实际上是“实时”)Kafka 流,并允许您编写自己的消费者,以便对数据流应用函数/转换/机器学习模型/任何您想要的东西。
佐料
所有资料均可在我的 GitHub 时间系列-卡夫卡-demo repo 中获得。接下来,将 repo 克隆到您的本地环境中。您可以在系统上仅运行 Docker 和 Docker Compose 来运行该示例。
回购有几个不同的组成部分:
- 一个Docker 文件,可用于构建本教程的 Docker 映像(如果您不想在本地安装需求,这也是可选的)
- 一个 Docker 编写文件来运行 Kafka 和 Zookeeper (Kafka 的朋友)
- 一个示例 csv 数据文件显示了输入时间戳数据的格式
- 用于生成数据文件的 Python 脚本
- 读取数据并向 Kafka 发送消息的 Python 脚本
- Python 中的一个例子 Kafka 消费者将数据打印到屏幕上
方向
将 repo 和 cd 克隆到目录中。
git clone https://github.com/mtpatter/time-series-kafka-demo.git
cd time-series-kafka-demo
启动卡夫卡经纪人和动物园管理员
合成文件从 Confluent 的 Docker Hub 存储库中提取 Kafka 和 Zookeeper 版本 6.2.0 的 Docker 映像。(必须固定您的版本!)
docker compose up
这使得 Kafka 和 Zookeeper 在同一个 Docker 网络上开始相互对话。Kafka 代理可以在端口 9092 上本地访问,因为合成文件将本地端口绑定到内部镜像端口。
构建 Docker 映像(可选地,为生产者和消费者)
如果您不想在 requirements.txt 文件中安装 Python 模块,您可以为生产者和消费者脚本使用 Docker 映像。
从主根目录:
docker build -t "kafkacsv" .
这个命令现在应该可以工作了:
docker run -it --rm kafkacsv python bin/sendStream.py -h
启动消费者
我们将首先启动一个消费者来模拟“实时”打印来自流“my-stream”的所有消息。我们在生产者之前启动消费者的原因是,生产者将在每个有时间戳的数据点之间及时再现所有的“暂停”。如果在生产者之后启动消费者,消费者将立即处理队列中已经存在的所有消息。但是如果你喜欢,就去做吧。_(ツ)_/
python bin/processStream.py my-stream
或使用 Docker:
docker run -it --rm \
-v $PWD:/home \
--network=host \
kafkacsv python bin/processStream.py my-stream
消费者主要功能的相关代码如下。请注意,我捕获了未知主题错误消息,并让使用者创建新主题。还要注意,在这个例子中,键盘中断将帮助您关闭消费者。
产生数据流的时间序列
如果您检查数据文件,它有两列:时间戳和(随机产生的)数据值。
timestamp,value
2021-01-01 00:00:00,51
2021-01-01 00:00:04,60
2021-01-01 00:00:06,82
2021-01-01 00:00:07,86
2021-01-01 00:00:11,99
2021-01-01 00:00:12,23
2021-01-01 00:00:21,63
2021-01-01 00:00:23,20
2021-01-01 00:00:24,32
现在(在另一个终端 shell 窗口中),我们将把时间序列从 data/data.csv 发送到主题“my-stream”默认情况下,第二条消息将在第一条消息 4 秒后发送,第三条消息将在 2 秒后发送,依此类推。就像我们回到了过去!(提示休易·路易斯和新闻。)
python bin/sendStream.py data/data.csv my-stream
或使用 Docker:
docker run -it --rm \
-v $PWD:/home \
--network=host \
kafkacsv python bin/sendStream.py data/data.csv my-stream
你也可以加快速度,因为为什么不呢?下面是将速度提高 10 倍的命令:
python bin/sendStream.py data/data.csv my-stream --speed 10
下面是 main 函数的相关代码,展示了时间序列数据到 json 的转换。我们这样做是因为它将保留类型,在这种情况下,数据值中的浮点将保持浮点状态。
如果您观察消费者的输出,它看起来会像这样:
python bin/processStream.py my-stream
2021-08-27 15:54:44 {'2021-01-01 00:00:00': 51.0}
2021-08-27 15:54:48 {'2021-01-01 00:00:04': 60.0}
2021-08-27 15:54:50 {'2021-01-01 00:00:06': 82.0}
2021-08-27 15:54:51 {'2021-01-01 00:00:07': 86.0}
2021-08-27 15:54:55 {'2021-01-01 00:00:11': 99.0}
2021-08-27 15:54:56 {'2021-01-01 00:00:12': 23.0}
2021-08-27 15:55:05 {'2021-01-01 00:00:21': 63.0}
2021-08-27 15:55:07 {'2021-01-01 00:00:23': 20.0}
2021-08-27 15:55:08 {'2021-01-01 00:00:24': 32.0}
看看吧!这个输出是 msg_process()函数的结果,它只打印当前时间和带有时间戳和数值的 json 数据。请注意,接收到的消息保留了这些暂停。msg_process()函数如下所示:
实际的数据输入(带有时间戳的键和值的值的 json,可能应该以不同的方式命名)可以在每个 Kafka 消息中通过调用。值()。
我在这里提供的消费者显然非常简单,因为它所做的只是打印到屏幕上,但是您可以很容易地将 msg_process()函数替换为您想要应用于每个数据点的函数。我推荐使用 json(或者 Apache Avro,如果你想了解模式等等。)因为,就像我说的,它将保留类型。
关闭并清理
用 Return 和 Ctrl+C 停止消费者/生产者。
关闭 Kafka 经纪人系统:
docker compose down
就是这样!希望这就是开始用 Python 编写自己的流数据分析所需的全部内容。
用 Sklearn 管道做一个坚如磐石的 ML 模型!
为什么、如何以及何时使用 Sklearn 的管道!
如果你正在建立一个预测模型,并在没有任何预处理步骤(如清理数据、输入缺失值)的情况下达到预期的精度,那么你恰好是世界上最幸运的人!但是,除非您执行大量的转换和预处理,否则今天的大多数数据都不会说话。涉及到几个步骤,从预处理、转换到建模。当将这些模型投入生产时,我们必须构建一个可以无中断运行的健壮流程,这一点很重要。这就是 Sklearn Pipeline 和其他支持组件发挥作用的地方。
为什么要用 Sklearn 管道?
我会旋转 Jupiter 笔记本,开始探索我的数据,创新新功能,执行预处理,如清理、缩放等。在输入模型之前。然而,我知道在 Jupyter 或类似的 ide 中这样做可能会变得一团糟。在 Jupyter 笔记本中创建一个自动化路径比按 Shift+Enter 更有意义。使用 Sklearn Pipeline 是一种方便的方法,可以通过预处理步骤来执行这些步骤,并确保代码的可再现性。
如果企业每天都依赖机器学习模型来做出决策,生产模型的不稳定和不一致的结果会对企业产生重大影响。
我不是“Jupyter haters 粉丝俱乐部”的成员;我自己构建了 Jupyter Notebook 中的所有内容,然后将其转换为集成 Sklearn 管道的 Python 脚本!
一般 Sklearn 管道工作流程:
如果我们看一个端到端机器学习管道的一般化图表,它看起来会像下面这样:
一旦提供了数据,
- 我们做插补——用平均值、中间值等填充缺失值。
- 接下来是一些特性工程,比如为缺失值创建一个标志,为所提供的分类特性创建 groupby 均值,等等。可以做到。
- 可以使用缩放/归一化来帮助算法更快地收敛,并且可以使用 PCA 来去除一些噪声。
- 最后,对数据进行估计。你可以使用管道来生成对未知数据的预测。
Sklearn 的管道 集成了上面提到的所有组件,并编排了交付模型的流程。如果我们想一想,不同的乐高积木如何有助于实现你的最终产品,不同的积木在管道中和谐地组合在一起,使其发挥作用!让我们一起来简单了解一下几种常用的变压器:
- Pipeline: 它是一系列步骤的集合,包括帮助机器学习工作流程自动化的最终估计器。上图是管道的一个例子。
- **特征联合:**数据流不是线性的,需要多个东西一起预处理。它并行执行所有函数,并将最终结果粘贴在一起。当您想要对一列应用不同的转换时,请使用它。
作者创造的形象
- Column Transformer: 帮助组合应用于不同列的不同类型的转换,并将它们连接起来。
作者创造的形象
- make_column_selector: 这个 Lego 块有助于根据数据类型选择多个列。Regex 也可以用于选择列。
作者创造的形象
- **简单估算器:**该转换器可用于用平均值、中值、最频繁值或常数来填充缺失值。
- BaseEstimator,TransformerMixin: 您可以使用这些基类来创建您的转换器。TransformerMixin 主要用于实现一个转换方法。
- FunctionalTransformer: 这有助于将用户定义的函数转换成易于使用并与管道集成的函数。
简短示例:
让我们在乳腺癌数据集上做一个小的实现,看看使用管道时会有什么不同。
正常 ML 代码:
实施管道:
正如我们从上面的代码中看到的,有几件事很突出:
- 清洁和质量代码
- 易于复制
- 裁员实践中的失业
一些优势:
可读性和再现性
- 可读性是机器学习领域经常被忽视的一个方面。比起关注代码的质量,我们更重视取得成果。代码必须是任何用户/同事在他们的实践中容易想到的,并与你分享。
- 此外,重要的是,编写的代码在以相同的步骤反复运行时能够产生相同的结果。当在代码中进行小的调整以获得更好的 ML 模型而不干扰所有组件时,这非常有帮助。
标准化的工作流程—减少数据泄露的机会
- 当使用管道拟合数据时,工作流程变得非常规范,发生数据泄露的机会也更少。数据泄漏是 ML 从业者在将模型投入生产时面临的最大障碍之一。
出错时易于调试
- 您的管道可能会抛出错误或导致失败。在这种情况下,如果你的代码是干净有效的,那么找出问题的根源就变得更加容易了!
可重用性
- 您可以根据新项目的要求,稍加调整,将已经建成的管道移植到一个项目中。这提高了我的效率,帮助我专注于整个 ML 管道的不同部分。
更多应用
- 我们可以结合管道使用网格搜索
- 使用像 Dask 这样的库将它们并行化。
- 您可以使用多个估算器运行一个循环,并对每个估算器使用管道。
最后但同样重要的是,Sklearn Pipeline 应该真正集成,并成为工作流的重要组成部分。我已经构建了几个项目,并广泛使用了它们。它们的扩展或应用是无限的,如果使用,它们可以成为一个很好的工具!感谢您的宝贵时间!
在 Twitter 或 LinkedIn 上关注我。你也可以通过 pratikkgandhi@gmail.com 联系我
在 R 中制作漂亮的 3D 情节——对讲故事的增强
示例和数据解释
3D 可视化通常比平面可视化更好用。两张图片来自 Unsplash
为什么要数据可视化?当我们处理统计数据时,无论我们正在处理什么数据,使用什么模型,数据可视化总是工作中不可避免的一部分。有时我们可能只是机械地制作图表,低估了可视化的重要性。事实上,数据的可视化不仅使数据更容易消化,而且比文本形式和描述性统计数据告诉我们更多。安斯科姆的四重奏就证明了这一点。
为什么是 3D?有时一个 2D 图(或多个图)可以包含我们需要的足够信息,但是 3D 图通常更直观,因为 3D 空间是我们居住的地方。这就像我们通常在地理教科书中看到的地图或地图集的 2D 投影,但地球仪总是更容易使用——更有趣,更直观。此外,在统计学中,我们经常会遇到更高维度的空间(大于 3 维),3D 可视化可以帮助我们在更高维度的空间中进行概括。
本文将介绍一些有用的 3D 绘图类型,即 3D 表面图、 3D 线条图和 3D 散点图,它们将使用库plotly
或rgl
来实现。此外,我们将看一下图表的解释——我们应该知道图表的含义,因为我们不仅仅是在制作精美的图片。
3D 表面图
图 1.1 使用来自plotly.com的代码制作的图像
使用plotly
库中的plot_ly
函数制作的 3D 表面图非常适合可视化地理数据。使用文档中的代码片段,我们可以从内置的数据集volcano
中绘制出一个非常真实的火山。这个数据很容易解释。数据集包含奥克兰 Maunga Whau 火山的地形信息,以 61×87 矩阵的形式显示。矩阵中的每个元素都是火山在 10 乘 10 的网格中的高度。所以,剧情的结果就是火山的形状。不仅是地理数据(高度),我们还可以用它来表示密度,例如概率密度。首先,我们生成一个概率分布矩阵。
# simulate joint probability distribution (normal)
num.rows <- 60
num.cols <- 60simulate <- function(n.row, n.col) {
# initiate the matrix
prob.n <- matrix(0, nrow=num.rows, ncol=num.cols)
x.seq <- seq(1, n.row)
y.seq <- seq(1, n.col)
xx <- dnorm(x.seq, mean=n.row/2, sd=12)
for (i in 1:n.row) {
y <- dnorm(i, mean=n.row/2, sd=12)
prob.n[i,] <- y * xx
}
prob.n;
}res <- simulate(num.rows, num.cols)
在这个例子中,我们模拟了两个正态分布的独立变量的联合概率。我们可以使用下面的代码来绘制图形,它将输出图 1.2 (a)
# 3D plot of joint probability distribution without projection
fig.n <- plot_ly(z = ~res)
fig.n <- fig.n %>% add_surface()
fig.n
或者我们可以在 x-z 和 y-z 平面上添加投影,这是通过属性contour
设置的。
# add projection
fig.nc <- plot_ly(z = ~res,
contours = list(
z = list(
show=TRUE,
usecolormap=TRUE,
highlightcolor="#ff0000",
project=list(z=TRUE)
),
y = list(
show=TRUE,
usecolormap=TRUE,
highlightcolor="#ff0000",
project=list(y=TRUE)
),
x = list(
show=TRUE,
usecolormap=TRUE,
highlightcolor="#ff0000",
project=list(x=TRUE)
)
)
)
fig.nc <- fig.nc %>% add_surface()
fig.nc
highlightcolor
参数决定当光标悬停在图形上时,图形轮廓的高亮颜色。在这种情况下,它被设置为红色。图表告诉我们什么?X 和 Y 轴表示随机变量 X 和 Y,它们具有正态分布。z 轴显示 X 和 Y 的联合概率——X 和 Y 取特定值的概率(P(X=x,Y=y)),这决定了图 2.2 中“山丘”的高度。在我们的例子中,由于 X 和 Y 是独立的,根据独立性的定义,P(X=x,Y=y) = P(X=x)P(Y=y)。如果 X 和 Y 具有不同的标准偏差,则其中一个投影会较宽,另一个会较窄,山丘的横截面将是椭圆形,而不是圆形。
但是我们需要小心 x-z 和 y-z 平面上投影的含义。看起来它们是 X 和 Y 的边际概率,但事实上,它们不是。投影只告诉我们 P(X=x 和 Y=y)相对于 X 和 Y 的相应值。但是,X 或 Y 的边际概率应该是行或列的总和(想想边际概率表)。
图 1.2 (a)联合分布(b)轮廓©悬停在图像上的联合分布将导致高亮颜色显示。图片由作者提供。
此外,我们可以使用一个颜色代码向量,通过colors
属性来定义定制颜色。在图 1.3 中,颜色模板由coolers生成。
color.vec2 <- rev(c("#F2DC5D", "#F2A359", "#DB9065", "#A4031F", "#240B36"))
# color.vec2 <- rev(c("#F7AEF8", "#B388EB", "#8093F1", "#72DDF7", "#F4F4ED"))
fig.n2 <- plot_ly(z = ~res, colors=color.vec2)
fig.n2 <- fig.n2 %>% add_surface()
fig.n2
图 1.3 自定义颜色。图片由作者提供。
3d 线图和瀑布图
当我们想要很好地显示多条线时,3D 线图会非常有用。它使我们能够在一个图形中展示多种趋势,省去了设计更多图形的麻烦,也增加了美感(这是一点个人看法)。我们以美国的电力消耗数据为例,3d 折线图有助于在同一个图表中展示不同年份和月份的电力使用趋势。
# plot of the electricity usage in the US
data.us <- usmelec
dmn <- list(month.abb, unique(floor(time(data.us))))
# convert to data frame by month, to make data retrieval easier
res.us <- as.data.frame(t(matrix(data.us, 12, dimnames = dmn)))# set the values of the 3d vectors
n <- nrow(res.us)
x.us <- rep(month.abb, times=n)
y.us <- rep(rownames(res.us), each=12)z.us <- as.numeric(data.us)
# we need to append two values to the vector
# converted from the time series and we let them
# equal to the last value in the time series so the
# shape of the graph will not be influenced
n.z <- length(z.us)
z.us[n.z+1] = z.us[n.z]
z.us[n.z+2] = z.us[n.z]data.us <- data.frame(x.us, y.us, z.us)
colnames(data.us) <- data.frame("x", "y", "z")fig.us <- plot_ly(data.us, x = ~y, y = ~x, z = ~z,
type = 'scatter3d', mode = 'lines', color=~y.us)
# to turn off the warning caused by the RColorBrewer
suppressWarnings(print(fig.us))
在本例中,数据存储为时间序列,这需要花费一些精力来设置 x、y 和 z 轴的值。幸运的是,这可以简单地使用rep
函数来完成。
图 2.1 美国用电量的 3D 线图。图片作者/
从 y-z 平面我们可以看到,从 1973 年到 2010 年,人们普遍使用越来越多的电力,从 x-z 平面我们可以看到全年的用电量是如何分布的(这实际上就是所谓的季节性,一些时间序列所具备的一个非常重要的属性)。
瀑布图(它不同于 3D 线图,因为在 3D 线图中不必有多条线——可以简单地有一条线穿过 3D 空间)是一个 3D 图,其中同时显示多条曲线。通常,它用于显示光谱,例如,显示离散傅立叶变换的结果。在 R 中,这可以通过使用lines3d
来实现。下面的代码生成了一个 10 元素数组经过复数傅里叶变换后的频率分量图。
# For displaying the result
options(rgl.printRglwidget = TRUE)x.f <- c(5, 4.2, 9, 3, 5.5, 8.2, 4.8, 6.4, 11, 10.2, 8.9, 10.9)
res.f <- fft(x.f)nx <- length(x.f)
ny <- 70xx <- seq(0, nx, by = 1)
yy <- seq(0, 1, length = ny)aspect3d(4, 2.5, 1)
axes3d()cols <- c("#CBE896", "#AAC0AA", "#FCDFA6", "#A18276", "#F4B886", "#DD99BB", "#7F5A83", "#A1D2CE", "#78CAD2"
, "#62A8AC", "#5497A7", "#50858B")
for (i in 1:nx) {
c <- x.f[i]
a <- Im(res.f[i])[1]
b <- Re(res.f[i])[1]
lines3d(yy, xx[i], c*(sin(a*xx) + cos(b*xx)), col=cols[i], lwd=2)
}
上述代码的输出如图 2.2 所示。为了运行代码,库rgl
是必要的。与之前的示例(图 2.1)不同,这些线是在一个循环中一条一条添加的,而不是立即在 3D 空间中设置点的位置。
图 3.2 频率成分。图片由作者提供。
我们也可以使用瀑布图来显示密度。这可以通过函数slicedens
来实现,该函数可以从 GitHub 库 BivariateSlicer 中获得。我们可以使用source
函数立即导入整个源文件,但是其中的一些例子可能无法正常运行。所以,还是直接运行函数比较好。slicedens
使我们能够在数据中制作切片,并在一个 3D 绘图中呈现它们。
我们使用艾姆斯住宅数据集作为例子,它可以从 Kaggle 获得。
housing <- read.csv2(file='data/AmesHousing.csv', header=TRUE, sep=',')
housing
summary(housing$Gr.Liv.Area)
ground <- housing$Gr.Liv.Area
price <- housing$SalePriceplot(ground, price, cex=.2, col="deepskyblue3", xlab="Above grade living area", ylab="Price")fcol <- c(.3, .8, .8,.12)
lcol <- c(0.1, 0.5, 0.4,.1)
slicedens(ground,price,
fcol=fcol, bcol='white', lcol=lcol,
gboost=0.015)
数据用散点图表示,如图 2.4 所示
图 2.4 房价散点图。图片由作者提供。
想象一下,把散乱的数据切割成几个水平的块,其中一些包含的点多一些,一些少一些。将切片排列成单个图后,结果如图 2.5 所示。
图 2.5 中的图看起来很漂亮,但这可能不是呈现散点数据的最佳方式。当然,我们可以从该图中看到密度分布,但与图 2.4 相比,图 2.5 似乎不那么清晰——我们无法确定哪个区域和价格范围最常见。在这种情况下,如图 2.4 所示的 2D 散点图可能更好。但是因为好看的视觉效果和slicedens
有趣的想法,这篇文章里也介绍了这个 3D 剧情。
图 2.5 房价瀑布图。图片由作者提供。
3d 散点图
散点图是表示离散观察值的好方法。我们以埃德加·安德森的鸢尾数据为例,给出了三种鸢尾的萼片和花瓣的长度和宽度。这通常也用于演示 PCA(主成分分析)。我们将使用下面的代码来绘制花的特征和组件向量的散点图。代码是这个的修改版。如果你不太了解 PCA,也不用担心,它背后的数学需要一些时间来解释,但这个想法很简单——这是一种有助于以最小的信息损失减少数据集维数的技术。
# iris data
summary(iris)
iris$Species <- factor(iris$Species,
levels = c("versicolor","virginica","setosa"))
pca <- princomp(iris[,1:4], cor=TRUE, scores=TRUE)# Scores
scores <- pca$scores
x <- scores[,1]
y <- scores[,2]
z <- scores[,3]# Loadings
loads <- pca$loadingsscale.loads <- 3p <- plot_ly() %>%
# the scatter plot of the data points
add_trace(x=x, y=y, z=z,
type="scatter3d", mode="markers",
marker = list(color=y,
colorscale = c("#FFE1A1", "#683531"),
opacity = 0.7, size=2))ns <- rownames(loads)
# add the vectors of the components
for (k in 1:nrow(loads)) {
x <- c(0, loads[k,1])*scale.loads
y <- c(0, loads[k,2])*scale.loads
z <- c(0, loads[k,3])*scale.loads
p <- p %>% add_trace(x=x, y=y, z=z,
type="scatter3d", mode="lines",
line = list(width=8),
opacity = 1, name=ns[k])
}
# display the graph
print(p)
3D 散点图可以很好地帮助我们了解数据的分布以及“组件”所扮演的角色。
图 3.1 虹膜数据集的 3D 图以及组件的方向。图片由作者提供。
在图 3.1 中,数据没有聚类(按物种着色),颜色只是沿着 y 轴变化。为了绘制聚类数据,我们可以很容易地使用函数plot_ly
来完成。
p <- plot_ly(iris, x=~Sepal.Length, y=~Sepal.Width,
z=~Petal.Length, color=~Species) %>%
add_markers(size=1)
print(p)
图 3.2 聚类虹膜数据。图片由作者提供。
其他有用的 3D 绘图
在这一节中,我们介绍一些其他的有代码的图。
(1)奇特的 3D 直方图来自:http://www . sth da . com/English/wiki/impressive-package-for-3D-and-4d-graph-r-software-and-data-visualization,这是一个显示离散数据频率的好工具,例如关于经度和纬度的地震事件数量(图 3.3)。在图 3.3 中,柱表示每个经度和纬度范围内的地震事件计数(x-y 平面上的小方块)。在网格下面的 x-y 平面上,投影是给定范围内地震事件深度的散点图。
图 3.3 斐济附近地震的位置,使用的是奇特的 3D 直方图。(图片来自 STHDA )图 3.4 gapminderDataFiveYear 数据集的气泡图。(图片来自plotly)
(2)3D 泡泡图出自:https://plotly.com/r/3d-scatter-plots/。它可用于在单个 3D 绘图中显示多个变量的关系。图 3.4 中给出的示例显示了人口、预期寿命、GDP 和国家(每个标记的标签,悬停时可见)以及国家大小(标记的大小)之间的关系。
图 3.4 螺旋面的 3D 图。来自 R 视图的图像
(3) 发展几何直觉的 3D 曲面图来自:https://rviews . r studio . com/2020/12/14/plotting-surfaces-with-r/。本文中的例子对于可视化拓扑形状非常实用。
资源:
[1] Matejka,j .,& Fitzmaurice,G. (2017 年 5 月)。相同的统计,不同的图形:通过模拟退火产生不同外观和相同统计的数据集。载于2017 年中国计算机学会计算系统中人的因素会议论文集(第 1290-1294 页)。
[2] R 数据集:火山,2021 年 8 月 31 日获取
使用 Plotly 和 Mapbox 制作漂亮的空间可视化效果
使用 Python 探索基于切片的 Web 地图的优势
谢栋豪在 Unsplash 上的照片
我最近给写了一篇关于可视化 NOAA 天气数据的文章。我们用 Plotly 处理了数据并制作了一些基本的交互式地图。在本文中,我想使用相同的数据,但重点是我们可用的选项,以改善我们的 Plotly 和 Mapbox 的网络地图。Web 地图有多种用途,拥有一个有助于强调数据点或为数据点提供上下文的高质量底图可以极大地提高可视化的质量。
Mapbox 是一家初创公司,为 Foursquare 和天气频道等网站提供地图,同时开发一些最强大的开源工具来创建网络地图。多年来,Mapbox JavaScript 库一直是最好的网络地图工具,现在我们可以使用 Plotly 利用 Python 中的 Mapbox。
Plotly 中的地图类型
轮廓与平铺地图
Plotly 有两种主要类型的地图,Geo 地图和 Mapbox 地图。地理地图是基于轮廓的,地图框地图是基于切片的。两者的主要区别在于,基于轮廓的地图可以一次绘制所有内容,而基于切片的地图具有以不同缩放级别呈现的信息层次。
如果我有一个以全球比例开始的地图,并且我希望用户能够放大以查看精细的粒度级别,那么基于切片的地图可能比轮廓地图更适合我的需要。由于基于切片的地图不必立即加载所有信息,因此它们的内存消耗较少,但是缩放时必须处理粒度变化会增加切片地图的处理能力开销。这意味着基于切片的地图非常适合提供大量信息而不会使地图变得杂乱,但它们需要更多的数据和处理能力。
这是作为轮廓图和基于图块的地图渲染的同一张地图。试着放大,看看你是否能发现两者之间的一些显著差异。
由作者使用 Plotly 创建
由作者使用 Plotly 和 Mapbox 创建
乍一看,除了使用不同的投影之外,这些地图可能看起来非常相似。但是当你开始放大的时候,你开始看到基于地图的好处。国家边界变得可见,因为它们变得相关,对数据点不那么突出;当您放大到越来越精细的比例时,甚至城市、道路和河流也会加载到底图中。
能够接触到这种细节真的可以让可视化感觉更专业,并且在多尺度下工作时,不同的粒度是有帮助的。如果我们想使用 Plotly 的散点地理函数显示国家边界,我们需要以各种比例绘制它们,如下所示:
由作者使用 Plotly 创建
在这种规模下,这个地块比两个没有国界的地块要繁忙得多。我们仍然缺少平铺地图提供的细节,如国家名称和城市。这些类型的要素可以在轮廓地图上进行配置,但对于可能很快变得混乱的轮廓地图来说,它们并不十分实用。
尽管有这些缺点,轮廓图仍然占有一席之地。当创建较小区域的地图时,我们通常不需要以多种比例渲染数据,这意味着没有理由增加基于图块的地图的数据和处理开销。此外,当制作用于演示的地图时,定制基于轮廓的地图以从远处最大化快速可读性可能是有利的。更不用说会议室并不总是有最好的 wifi,我已经看到我的网络地图加载速度非常慢,让说话的人感到沮丧。
地图框和 Plotly 平铺地图基础
Mapbox GL JS 是一个 JavaScript 库,用于交互式切片地图的 web 制图。当我们调用 Python 中的一个 Plotly Mapbox 函数时,Plotly 指的是 Mapbox JavaScript 库。由于 Mapbox GL JS 库的某些版本是开源的,因此使用这些函数和开源底图不需要 Mapbox API 密钥。然而,作为一家公司,Mapbox 拥有非常高质量的矢量切片底图,需要使用 Mapbox API 密钥。Mapbox 有一个很好的免费层,允许每月多达 50,000 次地图加载,这对大多数用户来说已经足够了。
栅格切片与矢量切片
大多数 web 地图传统上使用栅格切片来构建地图,但是 Mapbox 也允许我们利用矢量切片。如果您熟悉 GIS 数据,则栅格是值(影像)的格网,而矢量数据是几何数据,通常是点、线和面。
栅格切片是一组图像,它们彼此相邻排列以形成完整的地图。放大时,可以加载不同级别的栅格切片,以不同的比例显示新要素。矢量切片的工作方式与此非常相似,但是切片包含适合您正在查看的缩放和位置的预生成矢量数据。
矢量图块有很多优点。它们不需要像栅格切片一样大小完全相同,而是由客户端的计算机渲染,这可以使它们对于主机来说重量轻得多。矢量数据通常也比使用 map snappier 制作的栅格数据小得多,因为您对快速互联网连接的依赖更少。如果需要的话,矢量数据甚至可以渲染栅格,这使得它非常灵活。
平铺底图
使用 Plotly,我们可以访问光栅和矢量平铺基础地图。我们访问的栅格切片底图是开源的,就像 Open Street Map 的底图一样。Mapbox 提供对矢量切片底图的访问,但它们需要 Mapbox API 令牌才能访问。下面我将浏览一些可用的底图,并向你展示实际的区别。
来自公共服务器的栅格切片底图
我们可以访问的所有栅格切片底图都是开源的,不需要 API 令牌就可以使用。以下是一些可用的示例:
打开街道地图
尽管我们使用的是开放街道地图中的底图,但我们仍然调用了“散点图框()”函数。这是因为 Plotly 利用开源的 Mapbox JavaScript 库来绘制切片地图,而不管它们是栅格切片还是矢量切片。
使用开放街道地图中的栅格切片绘制地图(由作者创建)
USGS 影像栅格切片
美国地质调查局有大量的在线图像,包括一些很棒的底图!像这样使用栅格切片图像图层的最简单方法是渲染一个空白的白色画布,然后手动渲染数据或“轨迹”下面的底图。
使用 USGS 中的光栅切片绘制地图(由作者创建)
使用 Plotly 可以如此轻松地创建和共享各种地图,这真是太酷了!像这样的图像可以为气候数据提供真正有用的背景,尽管你可能会注意到我的色标可以使用一些调整来适应这张底图中的深色。由于基于切片的 web 地图是一种流行的标准,因此有许多可轻松访问的底图。
注意:如果你运行的网络连接速度较慢,这些地图可能需要更长时间才能加载。栅格切片可能非常大,这意味着需要将大量数据传输到浏览器中。
USGS 水文栅格切片
如果我们使用此链接作为我们的来源,我们也可以从 USGS 获得水文底图:
source = ["[https://basemap.nationalmap.gov/arcgis/rest/services/USGSHydroCached/MapServer/tile/{z}/{y}/{x](https://basemap.nationalmap.gov/arcgis/rest/services/USGSHydroCached/MapServer/tile/{z}/{y}/{x)}"]
由作者使用 Plotly 创建
你会注意到美国地质勘探局的一些数据产品,北美的数据要完整得多,所以在世界的某些地方可能没有有用的水文信息。总的来说,从美国地质勘探局、美国国家海洋和大气管理局等机构获得的免费和有用的数据令人印象深刻。
USGS 地貌晕渲底图
这里是一个阴影地形底图,作为美国地质勘探局免费提供的最后一个例子。请记住,网上还有其他栅格地图数据集,我将在最后的参考资料部分链接这些数据集。
"source": [ "[https://basemap.nationalmap.gov/arcgis/rest/services/USGSShadedReliefOnly/MapServer/tile/{z}/{y}/{x](https://basemap.nationalmap.gov/arcgis/rest/services/USGSShadedReliefOnly/MapServer/tile/{z}/{y}/{x)}"]
由作者使用 Plotly 和 Mapbox 创建
来自 Mapbox 服务器的矢量切片底图
矢量切片底图是从 Mapbox 的服务器上获取的,这意味着我们需要一个 Mapbox 帐户和 API 令牌来使用它们。如果您的计算机相当现代,您可能会注意到这些矢量平铺地图在响应速度和细节方面有了相当大的改进。由于矢量数据是由您的计算机渲染的,因此从服务器传输到您的计算机的数据较少,这通常意味着更快的加载时间。
地图框深色样式
使用 Mapbox 底图进行绘图的语法与开源替代方法非常相似。我们简单地传递一个带有 API 键和其他参数的“mapbox”字典,而不是指定层。“样式”决定了您的地图使用哪个地图框底图。
由作者使用 Plotly 和 Mapbox 创建
街道风格
如果你正在寻找街道和城市的更多细节,那么“街道”风格将非常适合你的用例。它从以前的地图中去掉了许多户外娱乐的细节,换来了街道、小路和公园的极端细节。这张底图可以让我们放大看到甚至非常小的街道的名称。这张底图甚至包括了我的家乡科罗拉多州博尔德市所有的校园建筑名称和步行路径。
由作者使用 Plotly 和 Mapbox 创建
户外风格
Mapbox 甚至有类似“户外”风格的底图,其中有重要的地标和徒步旅行、骑自行车和其他户外活动的细节。您甚至可以放大到像科罗拉多州布雷肯里奇这样的地区,当您足够接近时,可以看到呈现的滑雪道:
Mapbox 户外风格截图(作者创作)
包扎
我们可以看到,Plotly 和 Mapbox 为我们提供了范围广泛的底图,以满足许多不同用户的需求。即使从 USGS 等第三方加载栅格切片也相当简单。能够使用 Python 中如此强大的 web 制图工具真是太棒了,因为能够在不使用 JavaScript 的情况下制作高质量的地图可以减少工作流程中的许多摩擦。
资源
让沟通成为你的超能力
提高数据专业人员的首要技能
最重要的是,作为一名数据科学家,你如何沟通将是你成功的决定性因素。听听为什么,你能做些什么来把交流变成一种力量。
轻松吸引观众,你要说什么以及如何说很重要(图片由万三叶在 Unsplash 上拍摄)
有抱负的数据科学家问了我很多关于如何改进以及他们应该关注什么的问题。比如:
- 接下来该学什么框架?
- 我应该使用什么型号?
- 接下来该学茱莉亚还是火花?
虽然对大多数人来说,这些都是值得考虑的事情(在正确的环境下),但它们不是你应该关注的地方。成为那种比同龄人更了解朱莉娅的罕见的数据科学家只会在某些情况下有所帮助——这是可能用来区分两个强有力候选人的技能之一。你首先要确保你已经为成为一名强有力的候选人打下了基础。
在我看来,没有什么比沟通更重要。在这篇文章中,我将概述为什么以及如何去改善它们。
沟通的重要性
这是显而易见的,对吗?对于任何职业和生活来说,能够有效地沟通都是一项至关重要的技能,但我想强调它在商业环境中的重要性。
我曾经和几个超级天才一起工作过,他们很难达到你期望的效果,因为他们被他们的交流所阻碍。更糟糕的是,许多有才华的人认为他们超越了这一点,工作应该为自己说话——它不会。
简单化
你的工作是把你的想法翻译成最清晰、最容易理解的形式,而不损害信息的内容。在许多情况下,你将是你所讨论的领域的专家——你将因为你的技术专长而被雇用,并被要求将这种专长翻译给非技术利益相关者,或者你将在一个技术团队中,并将你的项目成果传达给更广泛的团队。无论哪种方式,都与背景有关。这让我想起了许多由伊丽莎白·牛顿在斯坦福大学做的著名实验——研究参与者被要求给其他人弹奏一首曲子,并猜测它被正确猜到的可能性,那些弹奏曲子的人大大高估了它被猜到的容易程度。这被称为“知识的诅咒”,并得到了很好的研究。
从本质上讲,任何人在向他人解释某件事情时,都很难对自己已经知道的东西打折扣。数据专家在这方面尤其糟糕。我们经常假设一定程度的理解(甚至是兴趣!)根本就不存在,并且在完全错误的层面上进行沟通。
尽管我有技术背景,但我告诉我的所有团队写他们的演示、演示和文档时,就好像他们在和一个白痴交流。假设我什么都不知道,你需要以这样一种方式沟通,我得到你的要点。算法或数学可能真的让你感兴趣,但对许多非技术利益相关者来说,却不是。更糟糕的是,许多企业的高层领导可能会认为这是智力势利或浪费时间。
你如何提高这方面的能力?练习。为非技术人员或在你的领域缺乏经验的人安排午餐和学习。问问你不从事科技工作的家人或朋友,你是否可以就此对他们进行测试。正确地做到这一点将会给你带来奇迹,并节省你在不需要的时候陷入技术细节的时间。
学术论文交流
当你记录你的工作和与团队合作时,这是一项需要时间的技能。尽量保持简洁,避免使用复杂的词语或语言。
在我的整个职业生涯中,我都有写日记的习惯。我在读博士的时候就知道,我需要尽可能清楚地写东西,并记录参考文献,就像我的生活依赖于此一样。我们在一个广阔的领域工作,有许多深入的技术领域,很容易忘记你六个月前知道的事情。养成做好笔记的习惯会提高你的写作水平。
不要害怕审查和编辑过程。我写论文时经历了很多痛苦,因此我的写作变得更好了。让别人客观地评论你的作品是金。如果你发现自己写的东西很难读懂,那就开个博客吧,网上有很多人都很乐意帮忙。事实上,如果你在读完这篇文章后开了一个博客,并希望有人评论它,伸出手来,我会告诉你我的想法。
最后,我建议使用 Grammarly 这样的工具,它会让你不再怀疑你的语法,有助于保持一致性。
语言通信
我认为我所认识的大多数数据专业人士在口头交流方面比书面交流更加困难。有很多提高口语的技巧和窍门。在指导这类事情时,我告诉人们的前三条是:
- 许多技术人员倾向于做的一件事就是说得太快,尤其是当他们兴奋的时候。尽量避免这种情况。试着主动放慢速度,想想政治家或演员在排练演讲或独白时的语速。
- 更多停顿,更长时间——这似乎违反直觉,但会给你的演讲增加更长的停顿。留下空白真的很有力量,可以加强你所说的内容。它散发着自信。这也给了其他人一个明确的信号,如果需要,他们可以插入问题或其他观点,而不必超过你或等太久。这也给了你一个很好的机会来回顾你从房间里得到的肢体语言暗示,并相应地调整你的信息。
- 直奔主题——我经常听到非常好的观点因为演讲者跑题或掉进兔子洞而被破坏或错过。你可能见过有人迷路,忘记他们在说什么。在工作环境中,这可能会损害你的声誉,并可能导致每次你开始说话时,人们就不再说话。尽量简明扼要,不跑题,只有在被要求或绝对必要的情况下才详细阐述你的观点。
关于这方面的更多帮助,有一些很棒的 Ted 演讲:
视觉呈现
这可能比列表中提到的任何事情都更伤害我的灵魂。请花一些时间来学习有效地使用视觉效果。求你了。
我经常看到人们用错误的图表类型绘制出色的工作成果,有糟糕的轴,不清楚,丑陋,斜视的视觉效果。无论你是为期刊写作、做演示、写博客,还是仅仅在你的 Jupyter 笔记本上记录你的结果——你的图表是大多数人都会被吸引的东西。让他们真正击中要害。
我坚信,如果我不能立即(在一秒钟内)理解图表中发生的事情,这可能是一个糟糕的视觉效果。如果有人必须向我介绍或解释一个图表,很有可能它会被简化或以更好的形式呈现。
有很多资源可以利用。有时,一个混杂的包但是 r/databasebeautiful 是一个观察其他人在做什么的好地方:
https://www.reddit.com/r/dataisbeautiful/
我建议你熟悉一下《金融时报》的视觉词汇——这是一个值得努力的伟大标准。也有一些类似 Tableau 和 Power BI 的实现。
有时,复杂的数字是必要的,在这种情况下,尽最大努力应用爱德华·塔夫特的量化信息的奇妙视觉显示中的一些要点:
后续步骤
不管怎样,有针对性的练习将是你开始看到全面显著进步所需要的一切。有些事情对你来说可能是自然而然的,而有些则不会。不要害怕接触导师、同事甚至更广泛的社区——没有什么比开博客或参加当地聚会更能磨练你的技能了。你可能会喜欢它。
希望这对您有所帮助。如果你认为我可能遗漏了任何提示,请告诉我——我也一直在学习!
进一步阅读
作为一名数据科学家,如果您正在寻找其他方面的建议,以下一些文章可能会让您感兴趣:
做出正确的决定
实践教程
我女儿在上中学,最近买了她的第一部新手机。她用自己从一个利润丰厚的暑期社区工作(遛狗、看猫、浇花、看孩子)中辛苦赚来的钱。拿起电话的那天,她要做一个决定。她应该购买保险以防意外损坏,还是通过购买手机壳来降低损坏的可能性,还是什么都不做,听天由命?
由阿里·阿卜杜勒·拉赫曼在 Unsplash 上拍摄的照片
她应该如何做出决定?不是恐惧地,也不是愉快地,而是理性地。
理性决定
有 3 种可能的 决策 :
- 买保险(没有手机壳)。假设一年的保险费为 100 美元,如果她需要维修/更换,她必须支付 50 美元的免赔额。
2.买个手机壳(不买保险)。假设对她有吸引力的最简单的案例花费 40 美元。
3.没有任何保险或手机壳的风险。
每个决策有两种可能的 结果 :
- 这款手机在一年内没有任何重大损坏。
- 电话在一年内坏了,需要修理/更换。有了保险,这将花费她免赔额。如果没有保险,她大概要花 200 美元才能拿回一部能用的手机。手机壳会降低手机摔坏的风险。
列举的决策和结果可以被可视化为逻辑树。从根到叶子的每一条路径都代表了几种可能性中的一种。
决策树(图片由作者提供)
她有足够的信息做出理性的决定吗?不完全是。一个关键的缺失部分是每种可能性的 概率 :
- 一年内手机坏掉的概率,无案例。
- 一年内手机坏掉的概率,有案例。
如果这是一场掷骰子、纸牌或掷硬币的游戏,她会客观准确地知道概率。但是现实生活很乱。潜在概率中存在不确定性。这是否注定了一个理性的决定?号码
她先从一些 概率的合理主观估计 开始。鉴于她自称笨手笨脚,她认为她有很大的可能会摔坏手机,比如说 50%。而一个手机套可以减少一半(25%)。然后,她计算出每个决策的 期望值 :
expected value = V + *v_1 x p_1 + v_2 x p_2 + ... + v_n x p_n*where V is the value of the decision, and *v_i, p_i* are the expected value and probability respectively of the *ith* possible outcome.So,
╔═══════════════════════════╦══════════════════════════════════════╗
║ Decision ║ Expected value ║
╠═══════════════════════════╬══════════════════════════════════════╣
║ Insurance: yes, Case: no ║ -100 + -50 x 0.50 + 0 x 0.50 = -125 ║
║ Insurance: no, Case: yes ║ -40 + -200 x 0.25 + 0 x 0.75 = -90 ║
║ Insurance: no, Case: no ║ 0 + -200 x 0.50 + 0 x 0.50 = -100 ║
╚═══════════════════════════╩══════════════════════════════════════╝
具有期望值和概率的决策树(图片由作者提供)
期望值是什么意思?从一个决策反复出现的场景来考虑这个问题。所以,一次又一次,她买了一部手机,带着它生活了一年,并看到了她的决定的结果。然后,在一些重复中,手机会坏掉(例如,当她决定不买一个盒子时,大约一半的重复),而在其他重复中,手机不会坏掉。她在所有重复中的平均收益/损失就是决策的期望值。因此,她做出了理性的决定,通过拒绝保险和购买保护箱(因为-90 > -125 和-90 > -100)来最小化她的预期成本(或者,等价地, 最大化预期价值 )。
你反对在现实生活中她没有机会重复上演这个场景。你说得对。但是她确实演了很多很多不同的场景,而且统计数据显示了所有这些场景。所以,如果她坚持做出最大化她期望价值的决定,她会比非理性地做出决定做得更好。参见运气与此有什么关系了解重复事件与大量不同事件的更详细讨论。
还有别的事困扰着你。你怀疑她的决定是基于对概率的错误估计,而不同的估计会导致不同的理性决定。再说一次,你是对的。处理潜在概率不确定性的方法是找到最优决策发生变化的阈值,然后决定你最有可能在阈值的哪一边。有了保费、免赔额、case、维修的具体数值,结果发现,只要手机坏了没有 case 的概率是< 40%,她还是不买保险和 case 的好。如果可能性高于 40%,那么一个案例最有意义。购买保险从来都不是最好的选择。
作为手机损坏概率函数的每个决策的期望值(图片由作者提供)
女儿靠在一旁小心翼翼,估计自己摔坏手机的几率大于 40%,决定给手机买个保护套。到目前为止,她对自己的决定很满意。
好的决定可能会有坏的结果
如果我女儿在拒绝保险后没几天就摔坏了手机怎么办?然后,她不是简单地支付 150 美元的保险费和免赔额,而是为这个箱子和替换手机支付 240 美元。她一定会后悔自己的决定,不是吗?不要!或者至少她不应该。一个好的决定可能会有一个坏的结果(由于运气不好)。相反,一个糟糕的决定会有一个好的结果(因为运气好)。尽管有这样做的强烈倾向,但永远不要根据结果的质量来评估决策的质量。如果你持续做出好的决定,你会在更多的时候有更好的结果(相比之下,如果你持续做出坏的决定或者持续做出好的决定)。
决策质量与结果质量(图片由作者提供)
为特殊的赌注破例
期望值最大化的决策规则适用于大多数情况。但是,当赌注异常高时,情况就不同了。有时你不得不放弃预期的收益,因为很难实现它。而且,有时你必须承受预期的损失来保护自己免受灾难。
有时放弃预期的收益
假设你有机会以有利的赔率玩彩票。你有百万分之一的机会赢得十亿美元。玩的费用是 100 美元。你想玩几次都可以。
迪伦·诺尔特在 Unsplash 上的照片
游戏的预期价值是 900 美元:
*expected value = -$100 + $1,000,000,000 x 1/1,000,000 = $900*
彩票决策树(图片由作者提供)
所以你应该玩,对吗?如果你输了,你应该继续玩,直到你赢了,对不对?不,除非你愿意先损失数亿美元。赢的几率,从而实现预期的收益,是如此之低,以至于大多数时候你会输。平均来说,你必须玩一百万次才能赢,花费你 1 亿美元。当然,你会在获胜后弥补,但你有那么多的资本和时间来投资吗?
因此,为了修正我们的决策启发法,决定最大化期望值的路径,除非最有可能的情况是亏损。在这种情况下,如果可能的话,最好选择另一个损失可能性不大的决定。不玩彩票。
有时会招致预期的损失
我和我的家人住在一个地震多发地区,那里在未来几十年内会发生一场大地震。我们一直在努力决定是否为我们的房子购买地震保险。假设保险费是每年 1000 美元,在要求保险覆盖我们家的全部损失的情况下,免赔额是 100000 美元。如果没有保险,假设更换/维修费用为 1,000,000 美元。
何塞·安东尼奥·加列戈·巴斯克斯在 Unsplash 上拍摄的照片
如果下一年发生灾难性地震的可能性很低(低于千分之一),那么如果我们不买保险,我们的预期成本是最低的。
地震保险决策树(图片由作者提供)
所以我们应该拒绝为我们的房子投保地震完全损失险,对吗?不,除非我们愿意变得无家可归,虽然可能性很小,但却不容忽视。最坏的情况是灾难性的,足以保证相对较小的成本来防范它。
再次调整我们的决策启发式,决定最大化期望值的路径,除非最有可能的情况是亏损,或者最糟糕的情况是灾难性的。在灾难性的最坏情况下,如果可能的话,更倾向于另一个更容易接受最坏情况的决定。不要拿重要的事情冒险。
使用工具
有了关于如何做出理性决策的启发,你应该如何真正去做,而不是每次面对决策时都陷入计算中?当然,使用适当的工具。如果你是一个电子表格忍者或程序员,你可能会对分析你面临的每个决策的期望值、可能情况、最坏情况和概率阈值感到舒服。对于你们其余的人,我很高兴分享我一直在使用的东西的粗略原型:https://vishesh-khemani.github.io/decisions/decision.html。下面的截图会让你知道它是干什么的。
决策树输入规范的屏幕截图(图片由作者提供)
渲染决策树的屏幕截图(图片由作者提供)
可以调整以找到阈值的自动生成参数的屏幕截图(图片由作者提供)
每个决策的“烛台”值(最小值、预期值、第 90 个百分位数、最大值)的屏幕截图,可快速查看相关指标(图片由作者提供)
摘要
- 理性地做决定,通常选择最大化你的预期价值的选项。
- 如果每个场景的概率有太多的不确定性,确定最佳决策发生变化的阈值概率,并判断阈值的哪一侧最有可能发生变化。
- 如果你坚持做出好的决定,你会有许多应得的好结果和一些不幸的坏结果。永远不要仅仅根据结果的质量来判断一个决定的质量。
- 不要玩彩票:如果最有可能的结果是失败,拒绝期望值最大的决定(除非没有更好的选择)。
- 不要拿最重要的东西冒险:如果最糟糕的结果是灾难性的,拒绝期望值最大的决定(除非没有更好的选择)。
- 使用工具来帮助评估决策。其中一个工具就是我在 https://vishesh-khemani.github.io/decisions/decision.html 的粗略原型。
参考
- 分析思维的格言 —丹·莱维
使可测量:关于算法的主观性,伽利略没有说什么
公平和偏见
关于如何衡量世界的选择意味着,并非所有的数据和算法都像它们看起来那样客观。
由 Diana Polekhina 在 Unsplash 上拍摄
伽利略的格言“测量能测量的,让不能测量的变得可测量”在今天的几乎每一个算法系统中都有回响。但在这些回声中回荡的是这样一个现实,即“制造”某种可测量的东西从来都不是纯粹客观的:它是主观选择的结果。承认这一点是防止算法造成伤害的关键。
将关于一件事物的“数据”视为它的真实表示似乎很直观。例如,我的身高是 5 英尺 9 英寸,伦敦是英国的首都——这两个数据点反映了现实。但是著名的统计学家 David Spiegelhalter 提醒我们,数据“几乎总是不完美的衡量我们感兴趣的东西”他指出,要数清地球上所有的树,我们必须首先定义什么算一棵树。有多种方法可以做到这一点,但并不是每个人都同意哪种方法是正确的。这是一个很好的提醒:有些人会说我更像 5 英尺 8 英寸半,这取决于你如何拿卷尺,爱丁堡、威尔士和贝尔法斯特呢?它们也都是英国的首都。
换句话说,使事物可测量的过程就是选择如何测量它们的过程。一个现象的哪些方面应该算?如何对这些方面进行分类和量化?将使用哪些指标?这些指标捕捉和反映了或没有捕捉和反映什么质量?这些选择通常会推动一个缩减的过程:将复杂的定性现象压缩成简单的数字数据:这个过程被称为数据化。
数据不是世界的完美反映,这一事实在数据科学中是众所周知的(所有的模型都是错误的,但有些是有用的。)然而,数据是许多算法的基础,这些算法越来越多地渗透并支持我们的数字社会。算法采用数据化的还原产物——关于如何使某些东西可测量的主观选择的产物——并通过固定的过程将它们硬编码成理应客观的输出。未能说明数据化的这种还原性质是许多算法伤害和失败的根本原因。
我们可以从两个例子中看出这一点:数字接触追踪和所谓的情感识别。
识别接触和追踪情绪
第一,数字接触追踪。在应对新冠肺炎疫情疫情时,公共卫生专家很快认识到,追踪接触者是应对病毒传播的关键。然而,手动接触追踪是费力的,需要相当多的时间和人力资源。政府和公共卫生机构想要一种可扩展以应对 Covid 挑战的接触者追踪模型。为了实现这一目标,许多国家寻求数字解决方案。
一系列联系追踪应用很快出现,主要基于蓝牙技术和由 Android 和 iOS 开发者支持的。每个应用程序的具体工作方式各不相同,但通常用户会下载他们国家的应用程序并处理日常事务。该应用程序记录了所有与它密切接触的其他手机的日志,通过蓝牙信号记录它们的接近程度。如果手机接触到另一个后来新冠肺炎病毒检测呈阳性的人,该应用程序会发送通知,警告用户进行自我隔离并接受检测。
理论上,这是一个很好的解决方案。不幸的是,数字联系追踪应用程序被证明不如手动联系追踪有效。这有许多可能的原因,从蓝牙传感器的精度到计算暴露的方式。但另一个主要原因是,这些应用根本没有真正进行联系人追踪。手动追踪接触者是一项高度复杂的任务,需要训练有素的专业人员通过面谈和其他方式详细绘制感染者的社会联系图。然而,蓝牙联系追踪不能做到这一点。取而代之的是,它只是记录“联系事件”(两部手机在一段设定的时间内彼此靠近,并开着蓝牙)。
换句话说,数字联系追踪应用是一种选择的产物,它将复杂的联系追踪任务简化为一个不完美的量化指标:手机之间的距离。这与对一个人的社会接触的丰富了解相去甚远,详细了解他们有什么互动以及他们之间传播病毒的可能性有多大。所有数字接触追踪应用程序可以测量的是蓝牙信号,而不是病毒传播。依赖这种主观定义的替代物作为接触的客观衡量标准,有可能导致公共卫生响应方面的差距,并转移人工接触者追踪工作的时间和金钱。
忽视数据化主观性风险的另一个例子是所谓的情感识别技术。在这里,危险远远超出了简单的无效,如蓝牙接触追踪,而是开始触及更深层次的社会不公和更广泛的技术科学伦理问题。
情绪识别系统是人工智能驱动的工具,声称它可以通过分析一个人的面部图像来识别他的情绪。微笑?你有 95%的机会开心。扮鬼脸?85%的几率你会觉得恶心。
问题是,这些系统大多是基于可疑的科学,表明人类情感有有限的、普遍的和特定的类别。此外,他们假设你脸上的表情是你内心情绪状态的直接反映,任何曾经笑过哭过的人都知道那不是真的。
当然,这些系统的支持者会争辩说,笑-哭是一种情绪的表达,这些系统只是需要一个能够识别它的类别。但这没有抓住重点。我们在任何时候的情绪状态都反映了我们的基因、我们的教养、我们的文化、我们目前所处的情况、房间里其他人的情绪、我们当时有多饿等等:情绪同时具有深刻的社会性、文化性和心理性。根据一些预先确定的、简单的类别,将所有这些都压缩到一个单一的、定量的概率计算中,这依赖于关于如何解释和分类所有这些定性因素的许多简化的和主观的选择。结果是,这里可测量的不是人类的情感,而是人脸可能形成的特定形状,一个粗糙的、可能有偏见的分类被置于顶部。
像情绪识别这样的算法工具对一些人来说很有吸引力,因为它们声称可以将难以置信的复杂现象变得简单。对其他人来说,吸引力在于这些算法可以提供比人类单独更深入或更真实的世界分析。但事实是,他们通常都不这样做。情感识别算法并不真正“识别”情感,它们只是检测表情并给它们贴上过于简单的标签。这就像把一个粘土十二面体强行穿过一个方孔,并且一直假装它是一个正方形。
在这两种情况下,接触追踪和情感识别,问题不一定是定量的分析方法。任何称职的统计学家或数据科学家都会指出,当对如何使某些东西可测量做出武断或简化的选择时,可能会出现缺陷。
问题是,这些算法系统采用这些简化的指标,并将其固定在一个没有细微差别、异常值或独特背景的技术流程中。他们采用现象,用基于一套主观的、有价值的和潜在偏见的观点的规则来解释它们,但吐出看起来客观的、没有主观性的陈述,因此被认为是真实的:追踪接触;情感确定。
选择如何使可测量的
回到伽利略,现代数据化和算法也使他的陈述成为简化论的受害者:它们侵蚀了“制造”一词的复杂性。正是在“制造”可测量的事物的过程中,价值、选择、偏见、缺陷、错误、疏忽等等悄悄进入数据集。在这种“使”可测量的过程中,事物被转化为量化的度量,这些度量只反映了那些选择所关注的现象的一部分。一旦这些数据集被纳入算法系统,这些选择就会被硬连线到技术成果中。凯西·奥尼尔的一句现代格言很好地描述了这一过程:“算法是嵌入代码中的观点。”
在我们的现代数字社会中,我们不会停止依赖算法。我们也不应该。当它们被精心设计时,它们会非常有效。但是数据科学和算法设计总是需要数据化的元素:使事物可测量总是需要主观选择。
关键是要更加适应这些选择,认识到这些选择是在何时、如何做出的,以及由谁做出这些选择,并承认算法的输入和输出永远不会不受主观性的影响,不管科技公司的营销可能宣称什么。承认算法的这种主观本质是设计它们对人类和社会产生更多积极和更少有害影响的关键。
让代码发挥最大价值
包装,把它们都包装起来,做自己的巨人
钥匙——作者根据钥匙
一个最聪明的人曾经说过:“如果我看得更远,那是因为我站在巨人的肩膀上”。作为一名数据科学家、数据工程师、机器学习专家、数据分析师或任何每天与数据、python 和朋友打交道的人,我们都知道自己是幸运的。我们站在 StackOverflow 的肩膀上,意思是一个巨大的社区。
做自己的巨人呢?利用你自己的见解,你的研究,以及你从成千上万的 StackOverflow 中提取的信息。我们都想做一些新奇的东西,研究最新的创新、技术和模型,但老实说,我们也经常做同样的模型、图表等。
那么,为什么不利用我们的代码,我们在这么多不同的笔记本中复制粘贴并稍微修改了这么多次?我们可以把它打包,把它们打包,所有这些我们多年来自豪地编写的函数和类打包。
📦构建您的私有包
利用你的工作的一个极好的方法是构建你的私人包。私人的,因为目的是不公开发表在 PyPi.org 或任何其他服务器上。
包装有许多优点:
- 模块化:为了使代码最模块化,你需要大量的函数、类等
- 轻松访问您的代码和更干净的 NB/脚本
- 更多自动化
- 可再现性(“隐藏状态”与可再现性相反,结果可能取决于您运行单元格的顺序,您定义的函数可能由范围外的变量提供)
- 易于共享/协作
- 您可以将模块设置为使用%load_ext autoreload、%autoreload 1 和%aimport module_name 自动重新加载
- 测试(单元、集成)
- 版本控制(众所周知,NB 很难版本控制)
🤔什么是包,它的基本组件是什么?
首先,它说明了构成 python 包的基本组件。我将使用一个基本模板给出结构并解释它们各自的作用。
一个基本的私有 python 包的结构,可以在 GitHub 上找到
pkgtemp
:一个基本 python 包的模板,带有子包和模块- 在
pkgtemp/sub_pkg1/modA.py
中:类和继承的例子,用于说明 - 在
pkgtemp/sub_pkg2/modC.py
中:用特殊方法比较对象的类的例子,用于说明 - 在
pkgtemp/sub_pkg2/modD.py
中:使用特殊方法使对象实例可调用的类的例子 - 在
pkgtemp/__version__.py
中:关于版本、作者等的所有细节。 - 在
pkgtemp/__init__.py
中:默认为空,可以用来绑定上层包的子包和模块(更容易导入) setup.py
构建包的主文件requirements.txt
安装pkgtemp
时被安装的依赖项- 在
envs
中,yaml 文件用于创建 conda python 环境,见下图 README.md
登陆解释文件(包是做什么的,如何安装等等。)
可选文件和文件夹:
- 在
tests
中:单元测试——这是可选的 - 在
docs
中:包文档(通常带有 sphinx) - 在
data
中:您希望与您的包一起分发的数据,您应该提到在 setup.py 中 - 在
documentation
附加文档中,(不随代码一起分发) - 在
notebooks
中,作为文档的替代,突出显示您的代码 - 在
scripts
中,作为文档的替代,突出显示您的代码 - 在
egg-info
中,包构建过程的所有输出——不要关心这个,它们将通过运行 setup.py 生成
模块
模块是一个*。包含 Python 代码的 py 文件。为了模块化,将代码分解成模块。python 模块只是一个包含代码的文件。模块化代码更容易使用。编码时(人类)记忆中保存的东西更少,滚动更少,当你必须找到一个特定的东西时知道确切的位置,把大问题分解成小问题,等等。
包裹
包是包含其他文件夹(子包)或模块(的集合)的文件夹。py 文件),以及特定的文件。如果满足某些要求,可以安装它。
假设您开发了一个包含几个模块的应用程序。随着模块数量的增长,如果将它们放在一个位置,就很难跟踪所有的模块。如果它们具有相似的名称或功能,尤其如此。您可能希望有一种方法来对它们进行分组和组织。包允许使用点符号对模块名称空间进行分层结构。模块有助于避免全局变量名之间的冲突,同样,包也有助于避免模块名之间的冲突。
包可以包含任意深度的嵌套子包。Python 中的每个包都是一个目录,其中必须包含一个名为__init__.py
的特殊文件。这个文件可以是空的,它表明它包含的目录是一个 Python 包,因此可以像导入模块一样导入它
应用程序接口
API:Application Programming Interface(应用编程接口)的缩写,指的是通过抽象底层实现和只暴露开发者需要的对象或动作来简化编程的函数和类的集合。API 是多用途的,并不打算产生一个单一的输出(不是特别的),而是一个可以在不同的项目和任务中重用的工具。
例如,lightGBM 是用 C++编写的,但提供了几个 API:python、R 等。
💡你必须做什么来写你的私人包裹?
💻写一些代码
为了扩大您的工具堆栈,您可以收集并重构您发现对几个(至少两个)项目有用的代码,因为如果它对一个以上的项目有用,它很可能对未来的项目也有用(例如,在您需要执行回归的任何时候,不要从头重写您的 sklearn 管道)
最好是在收集和重构你的代码时,尽量让它不那么具体,更模块化。很可能您为特定的项目编写了代码,带有一些硬编码的名称、参数和变量值。尝试移除任何硬编码的部分,取而代之的是将它们作为函数/类的参数。
另外,尽量将函数和类整理到包含模块(*)的子包(子文件夹)中。py 文件)。例如
包、子包和模块的基本结构,如模板所示
用于收集您喜欢的绘图函数、模型比较类、lightGBM 包装器等。
📃记录您的代码
最好的方法是在重构代码的同时,也记录你的代码。使用您最喜欢的约定(NumPy、Pandas、Google 等)编写适当的文档字符串。).这将有助于你记住目的是什么,并使与同事的合作更加顺利。
🗃️创建了一个专用的 python 环境
创建专用的 python 环境。这将隔离您的项目;您不会破坏您的基础,如果您需要通过 conda 而不是 pip 安装一些包,这将允许您这样做(例如,conda-forge 的一些预建包)
conda create -n proj-env python dependency1 dependency2
conda activate proj-env
- 当你安装了所有的 conda 依赖项,并且在转移到 pip 之前:
conda env export > proj-env.yml
如果你计划跨平台共享(Win-linux 或其他),你可以使用“no-builds”标志 - 继续安装您的 pip 依赖项,并在
requirements.txt
中收集所需的包或使用pip list --format=freeze > requirements.txt
🖊️编辑 _ 版本 __。巴拉圭
编辑__version__.py
以满足你的需求(作者、标题、版本号等)。)
- title = 'pkgtemp ’
- _ _ description _ _ = ’ python-pkg-temp-包模板项目生成器’
- version = '0.0.1 ’
- __ 作者 __ = ‘威尔·史密斯’
- _ _ author _ email _ _ = ’ will . Smith @ Google . mars ’
- __ 许可证 __ = ‘麻省理工学院’
- _ _ URL _ _ = ’ https://github/will Smith/bad boys 4 ’
🖊️编辑 setup.py
原则上,你可以让它保持原样。您可以编辑 EXTRAS_REQUIRE 和/或 classifiers=…以适应您的包。其他输入可在 version 中编辑。py 文件。
⛭安装你的软件包并使用它
非常简单,两步:
conda activate [ENV_NAME]
cd C:/[USER_NAME]/[pkg_name]
的意思,setup.py 在哪里pip install .
如果您还在编辑您的包,请不要忘记时间段或pip install -e .
完成了!您可以导入您的包,并在不同的项目中重用您的函数和类。你可以在本笔记本中找到一个例子。
结论
打包是利用您的艰苦工作并构建一个令人惊叹的函数库和类库的一种极好的方式。这样你就可以重复使用你的代码,与你的同事分享,建立一个文件夹如果你是自由职业者,你可以对它进行版本控制和部署更新。
我们看到了如何构建一个不在公开渠道上发布的包来实现这一点。利用 GitHub,你也可以让它像pip install git+https://github.com/[GH_USER]/[REPO_NAME].git
一样容易安装
我用它来包装我经常使用的库。比如,包装 lightGBM、CatBoost 和 Optuna,使它们具有一致的语法。而且还标准化了我的制图风格、基本 EDA 和我必须执行的所有重复任务。此外,我可以在工作中使用我在个人项目中所做的,反之亦然。我也可以很快地和其他人分享我找到的一些解决方法。
最后,当您对自己的软件包感到满意,并且认为社区可以从您的工作中受益时,您可以考虑以开源方式发布您的软件包。不知何故,偿还社区的债务💸
让熊猫跑得飞快
你不需要再等了
由 Unsplash 上的 CHUTTERSNAP 拍照
我们都喜欢并使用熊猫。当我们作为数据探索者深入探索数据奥秘时,它是我们的日常伴侣。通常,我们处理大量的数据,如果我们的apply
函数能够快速运行,我们会很高兴。
好了,不要再等了,我向你介绍。
我们将看到我们如何从这里开始:
资料来源:https://github.com/nalepae/pandarallel
对此:
来源:https://github.com/nalepae/pandarallel
泛平行函数
来源:https://github.com/nalepae/pandarallel
上面我们看到了可供我们使用的功能。
如何安装?
这个库在 pip 上是可用的,所以,只需要这样做:
pip install pandarallel
怎么用?
我们首先需要导入库。
from pandarallel import pandarallel
在这之后,我们需要调用.initialize()
让pandarallel
发挥它的魔力。
pandarallel.initialize()
你会从 Pandarallel 收到一些关于你的设置的信息。
INFO: Pandarallel will run on 2 workers.
INFO: Pandarallel will use Memory file system to transfer data between the main process and workers.
由于我使用的是 Google Colab,它不提供很多 CPU,所以我们只有 2 个工作人员。当你在你的机器上运行这个的时候,工人的数量将会更多,速度也会加快。所以,我们在这里看到的是加速的下限 T21。
现在,让我们看看魔法。
我们将首先创建一个大型数据框来测试 Pandarallel 的能力。
df = pd.DataFrame(dict(a=np.random.randint(1, 8, df_size), b=np.random.rand(df_size)))
我们创建了一个有五百万行的数据帧。
让我们创建一个应用于此数据框的函数。
**def** func(x):
**return** math.sin(x.a**2) + math.sin(x.b**2)
使用 Pandas 的 apply() 函数:
%%time
res = df.apply(func, axis=1)
我们得到输出:
CPU times: user 2min 12s, sys: 1.64 s, total: 2min 14s
Wall time: 2min 14s
上面我们得到的是未并行化的代码,需要 2 分 14 秒。
让我们看看由 Pandarallel 库并行化的代码**。**
使用 Pandarallel 的 parallel_apply() 函数:
%%time
res_parallel = df.parallel_apply(func, axis=1)
我们得到输出:
CPU times: user 780 ms, sys: 271 ms, total: 1.05 s
Wall time: 2min 2s
所用的时间是 2 分 2 秒,比上面未并行化的代码所用的时间少,即 2 分 14 秒。节省了时间(12 秒),即使我们只使用 2 个工作线程(1 个工作线程= CPU 中的 1 个实际内核)。
如果你不满意,这里有一个库的作者做的基准测试。
来源:https://github.com/nalepae/pandarallel
在 4 个内核(即 4 个工作线程)上,我们看到时间减少得更多。
结论
只需在函数调用中做一个简单的改变,我们就能让我们的熊猫代码获得巨大的加速。这绝对是一件好事,因为数据处理需要花费大量时间,让我们远离数据分析。
阿布舍克·维尔马
使用 CFFI Python 绑定提高 Python 速度
从 Python 中的任何 C 库中调用函数。准备好起飞了吗?
Python 是最用户友好的编程语言之一。它易于学习,免费使用,并且您可以随意扩展它的功能。
此外,Python 可以说是数据科学和机器学习领域最常用的编程语言。优秀的数字库,如 NumPy 和 SciPy,以及卓越的深度学习框架,如 PyTorch 和 TensorFlow,为每个喜欢玩数据和人工神经网络的程序员创建了一个庞大的工具库。
另一方面,Python 是一种解释型语言,它可以设置代码执行速度的上限。在 AI 和大数据的世界里,这不是一件好事。所以,我们确实有矛盾。Python 如何成为该领域使用最多的编程语言?我们能做些什么让它更快?
好吧,我们可以用另一种语言(例如,C 或 C++)编写我们要求高的函数,并利用特定的绑定从 Python 调用这些函数。这就是这些巨大的数字库或深度学习框架所做的事情。因此,如果你是一名数据科学家或机器学习工程师,想要调用 CUDA 函数,这个故事适合你。
学习率是为那些对 AI 和 MLOps 的世界感到好奇的人准备的时事通讯。你会在每周五收到我关于最新人工智能新闻和文章的更新和想法。在这里订阅!
CFFI
在上一篇文章中,我们用标准 Python 库的一部分库ctypes
做了同样的事情。我们看到了什么是编组,以及 Python 在内存管理方面与 C 有何不同。最后,我们通过一个简单的例子看到了ctypes
如何帮助我们调用 C 函数。
在这个故事中,让我们和 CFFI 一起更上一层楼。
CFFI 对 ctypes
ctypes
可能是 Python 标准库的一部分,但是所有这些类型声明都容易出错,并且它不能扩展到大型项目/
此外,ctypes
允许您将共享库直接加载到 Python 程序中。另一方面,使用CFFI
,您可以构建一个新的 Python 模块,并像导入任何其他 Python 库一样导入它。
装置
CFFI
不是标准库的一部分。因此,要使用它,我们需要先安装它。我们可以使用 Python 包管理器pip
轻松做到这一点:
$ python3 -m pip install cffi
请注意,建议使用 Python 虚拟环境来遵循本教程,以避免全局安装 Python 包。这可能会破坏系统工具或其他项目。
简单的例子
在这个例子中,我们将从的m
库中调用一个函数。我们将使用这个函数来得到一个数的平方根。
第一步是创建一个 python 文件,该文件将构建一个具有我们需要的功能的 Python 模块;提供所有好东西的图书馆。
为此,复制并粘贴以下代码:
在这个例子中,您从的m
库中用sqrt
函数创建了一个绑定。首先,在set_source
方法中,传递想要构建的 Python 模块的名称(例如_libmath
)。
您还包括来自的m
库的math
头文件,因为我们使用的是来自C
标准库的库,所以我们不需要在library_dirs
中提供库的位置。
最后,您可以像运行任何其他 Python 脚本一样运行该脚本:
$ python3 build_cffi.py
这将产生.o
和一个.c
文件。让我们看看如何使用这个调用的输出。
从 Python 调用 C
现在您已经准备好从C
调用sqrt
函数。这就像运行下面的代码一样简单:
首先,从您创建的库(即_libmath
)导入 lib 模块。在内部,您可以找到对sqrt
函数的绑定。最后,您可以用任何浮点数调用这个函数。
您也可以创建到其他函数的绑定。例如,要创建与m
库的sin
或cos
函数的绑定,只需在cdef
中声明它们:
ffibuilder.cdef("""
double sqrt(double x);
double sin(double x);
""")
结论
Python 是最用户友好的编程语言之一,也可以说是数据科学和机器学习领域中使用最多的编程语言。
另一方面,Python 是一种解释型语言,它可以设置代码执行速度的上限。但并不是所有的希望都破灭了;我们可以用另一种语言(例如,C 或 C++)编写要求苛刻的函数,并利用特定的绑定从 Python 调用这些函数。
在上一篇文章中,我们讨论了ctypes
以及如何使用它从 Python 中调用C
库。在这篇文章中,我们使用了一种更加自动化的方法,并看到了如何使用CFFI
来实现这一点。
接下来:我们将看到如何利用 Cython 来加速我们的 Python 代码!
关于作者
我的名字是迪米特里斯·波罗普洛斯,我是一名为阿里克托工作的机器学习工程师。我曾为欧洲委员会、欧盟统计局、国际货币基金组织、欧洲央行、经合组织和宜家等主要客户设计和实施过人工智能和软件解决方案。
如果你有兴趣阅读更多关于机器学习、深度学习、数据科学和数据操作的帖子,请关注我的 Medium 、 LinkedIn 或 Twitter 上的 @james2pl 。
所表达的观点仅代表我个人,并不代表我的雇主的观点或意见。
让 Python 和 C 一样快
理解大数据
用 Numba 实现更快的 Python 代码
速度问题
虽然 Python 被广泛接受,主要是因为其精简的语法,可以作为一种很好的原型语言,但它也有一个缺点,在“编程语言战争”式的辩论中经常提到:速度。与其他编程语言相比,Python 对于标准基准算法的运行时间要慢得多。
我自己创造的
从计算机语言基准测试游戏上的二叉树基准测试来看,Python 的 48.03 秒比不上 C++的 0.94 秒或 C 的 1.54 秒。由于是一种解释型和动态类型语言,Python 允许极快的原型速度,但无法与 C++、C、Fortran 以及其他几种编译语言的运行时间竞争。
快速原型制作+快速运行时间= Numba
Numba 通过一个针对 Python 的实时(JIT)编译器,结合了快速原型和快速运行时的优点。真正的意思是你的代码只能在运行时编译,而不能在运行前编译。这样做,Numba 使您能够将 Python 代码的速度提高“一到两个数量级”。[2]然而,使用 Numba 的实际速度增益在很大程度上取决于每个特定的用例,因为该项目侧重于科学计算应用。
安装 Numba
Numba 最大的优势之一是易于使用。与加速 Python 的其他方法所需的或多或少复杂的安装过程相反,Numba 可以使用pip
或conda
完全安装。
这两种方法都非常简单,应该可以在大多数数据科学环境中开箱即用。使用 Anaconda,conda install numba
将安装所有需要的东西。pip install numba
也是如此。
何时使用 Numba
一旦安装,Numba 可以帮助 Python 代码运行得更快。然而,有三个主要的标准作为指导方针来确定 Numba 对于特定任务的适合程度,以及它加速 Python 的潜力。
- 如上所述,Numba 专注于加速科学应用。因此,代码包含的数学运算越多,Numba 就能提供越多的帮助。
- 与第一个标准相关,Numba 与 NumPy 配合得特别好。有可能,专注于数学运算的 Python 代码将包含大量的 NumPy。然而,需要注意的是 Numba 并不支持所有的 NumPy 函数,有些函数可能需要在原始 Python 和受支持的 NumPy 函数中实现才能使用。
- 循环越多越好。通常,当代码包含几个非常长的循环时,Numba 可以节省大量时间。
打开涡轮增压器
使用 Numba 的另一个原因是它与其他 Python 代码集成的方式。使用 Numba 的唯一要求是为需要加速的函数添加一个装饰器。
来自 Numba 文档的代码
在上面的例子中,decorator @jit(nopython=True)
向 Numba 发出信号,让它以 nopython 模式运行,而不是 Numba 的替代对象模式*。默认情况下,nopython 参数设置为 false,即使它生成的代码要快得多,这是因为它限制了所支持的 NumPy 函数的数量。除了@jit(nopython=True)
,你也可以使用简写@njit
。*
由于 for 循环和对数学计算的关注,monte_carlo_pi
函数的代码至少满足了 Numba 的三个有前途的用例中的两个,添加@njit
装饰器应该会提供一个不错的性能提升。
我自己创造的
虽然该函数的纯 pythonic 版本在向其传递大约 1 亿个样本时开始大幅减慢,但等效的 Numba 版本保持了更高的性能水平,两个选项之间的相对差距只会随着计算负载的增加而扩大。
更多装饰
**懒编译是指让 Numba 决定如何优化代码。一般来说,这种方法实现起来很快,并且最大限度地减少了可能出现的错误。装饰器向 Numba 发出懒惰编译的信号。
我自己创造的
此外,惰性编译还将允许您保留更高程度的灵活性。本质上,它可以给 Python 函数一个不错的速度提升,同时仍然允许高原型速度。
**急切编译允许对函数的签名进行更多的控制,这可以提高代码的可读性(以及潜在的性能)。同时,指定签名也会降低代码的灵活性,并可能导致容易被忽略的错误。签名被添加到要优化的函数之上的装饰器中。例如,@jit(int64(int64))
描述了一个将整数作为输入并返回整数的函数。Numba 还允许通过以下语法在签名中指定数组:@jit(int64(int64[:]))
。前面提到的签名将指定一个整数数组作为函数的输入,一个整数作为函数的输出。
我自己创造的
正如在急切编译的介绍中所暗示的,快速原型化会导致未被发现的错误。函数f()
的签名需要一个整数数组,并将返回一个整数。当传递数组[1,2,3,4]
时,正确的结果应该是10 + 2.5 = 12.5
。然而,由于函数的签名,上面的函数调用将返回12
而不抛出异常。例如,产生预期结果的签名应该是@jit(float64(int64[:])
。因此,当使用 Numba 进行急切编译时,正确地制定每个函数的签名是非常必要的。
然而,Numba 的 jit 装饰器的最强大的变体是@jit(nopython=True)
或等价的@njit
。通过激活 nopython 模式,正在讨论的函数将不会使用 python 的 C API,并产生更快的代码。与@jit
相反,强制 Numba 使用 nopython 模式将防止退回到较慢的对象模式,但也需要更多时间密集的开发准备,同时也不太宽容。在对象模式下运行将允许使用其他标准的数据科学包,如 Pandas,而 nopython 模式则不能。
摘要
虽然 Numba 的能力比本文中描述的方法更深入,但是很好地掌握和理解如何使用@jit
decorator 是从看似永无止境的循环向更好地执行科学代码迈出的一大步。
当谈到快速原型和实验时,Numba 通过其快速的安装过程和易用性完美地集成了 Python。不需要安装全新的编译器或者用不同的语言混合 Python 代码,使用 Numba 可以让 Python 编码人员立即投入工作,像没有 Numba 一样专注于他们的代码。
我自己创造的
然而,Numba 并不是一个适合所有人的方法。每个潜在的用例都应该单独检查,因为通过使用 nopython 模式节省更多的时间是以不能使用像 Pandas 这样的标准数据科学包为代价的。因此,根据一般经验,大多数应用程序可能从让 Numba 通过@jit
装饰器完成所有优化工作中受益匪浅。另一方面,需要尽可能少的运行时间的高级用例需要更多的时间投入,以使它们与 Numba 兼容,这是因为该项目具有丰富的生态系统和广泛的功能。
来源:
[1]“二叉树。”计算机语言基准游戏。2021 年 8 月 2 日接入。https://benchmarks game-team . pages . debian . net/benchmarks game/performance/binary trees . html
[2]“农巴。”Numba 文档—Numba 0 . 53 . 1-py 3.7-Linux-x86 _ 64 . egg 文档。2021 年 8 月 2 日接入。https://numba.readthedocs.io/en/stable/index.html.
在一行 Python 代码中实现现有 Matplotlib 绘图的交互
使用 matplotlib 库的交互式小部件,而不是内联绘图
图片由 Michal Jarmoluk 来自 Pixabay
探索性数据分析是数据科学模型开发流程中的一个重要元素。EDA 帮助数据科学家更好地理解数据变量和它们之间的关系,并揭示超越正式建模或假设检验的任务。Matplotlib 是一个流行的 Python 绘图库,它提供了一个面向对象的 API 来将绘图嵌入到应用程序中。
有很多开源库可以生成交互式的情节,包括 Bokeh,Altair,Plotly。在本文中,您可以通过添加几行 Python 代码,了解如何在 Jupyter 笔记本或 Jupyter Lab IDE 中使现有的 matplotlib 内联绘图具有交互性。这可以通过使用两个开源库ipywidgets
和ipympl
来实现。
入门指南
首先安装**ipywidgets**
(也称为 jupyter-widgets)库,该库将在内部用于创建小部件。该软件包为 Jupyter 笔记本和 IPython 内核提供了交互式 HTML 小部件。
**ipymlp**
是另一个将 IPython 笔记本转换成易于编辑的 YAML 文件的 Python 包。使用**ipymlp**
的好处是你可以完全兼容 IPython 笔记本,并且能够在任何文本编辑器中编辑文件。
安装:
这两个包都可以从 PyPl 安装,使用:
**pip install ipywidgets --user
pip install ipympl --user**
安装必要的软件包后,使用以下方式启用widgets
和ipympl
扩展:
**jupyter nbextension enable --py --sys-prefix widgetsnbextension
jupyter nbextension install --py --symlink --sys-prefix ipympl**
现在,由于您希望所有的 matplotlib 图都作为小部件,您可以使用%matplotlib
magic 命令更改外观。
- 使用
%matplotlib inline
magic command,绘图命令的输出在 Jupyter notebook 之类的前端内联显示,就在产生它的代码单元的正下方。 - 使用
%matplotlib widget
将您的所有绘图作为一个小部件
用法:
使用**%matplotlib widget**
**,**更改后端后,使用 matplolib 包生成的图出现在一个小部件框内。figure canvas
元素是一个适当的 Jupyter 交互式小部件,可以以交互式小部件布局格式定位。现在不是静态的内嵌 matplotlib 图,而是具有一些给定功能的交互式图。将出现小组件框:
(图片由作者提供)
使用上面的交互式绘图,您可以更改绘图的大小,平移和缩放绘图。人们也可以选择缩放和呈现给定的 x 和 y 坐标矩形的绘图。
下面的 GIF 描述了一个人如何在时间序列图中盘旋并从中获得洞察力。
(作者 GIF)
结论:
Matplotlib 小部件是一个方便的工具,它的主要优点是不需要在 Python 代码中做任何这样的修改。还有各种其他的 Python 包,包括 Plotly、Altair、Boker,它们可以生成交互式图形,但是需要首先适应新库的 API 功能。
阅读下面提到的的文章作者 Pier Paolo Ippolito 可以更好地了解其他绘图库,包括 Plotly、Bokeh 等。
参考资料:
[1] Ipympl GitHub 库:https://github.com/matplotlib/ipympl
感谢您的阅读
在 Python 中进行库存模拟
在本文中,您将在 Python 中创建一个定期补货策略的模拟。不到 30 行。
本文摘自我的著作 库存优化:模型与模拟 。你可以在这里 阅读我的其他文章 ,在这里 订阅我的博客 。我也活跃在LinkedIn上。
西奥多·加勒(1571-1633)大约在 1600 年(后来着色)雕刻的“新发现”的卷首。来源
在使用 Python 编写库存模拟代码之前,让我们花点时间来定义我们将要模拟的库存策略:一个定期补货的库存策略。
库存政策:定期补货
遵循定期补货政策的产品会根据固定的时间表和最高水平定期订购。
定期补充。来源:我的库存培训
在任何评估期的开始,我们需要订购足够的产品,以使我们的净库存达到水平 S 。从上图中可以看出,订单数量取决于我们下单时的库存数量,因此是可变的。另一方面,订单是按照固定的时间表生成的:两个连续订单之间经过的时间总是相同的。
这种定期补货策略实际上是供应链中最常见的,因为它经常被 MRP/DRP 的广泛使用强加到供应链上。这些工具遵循预定义的时间表—通常是每天或每周—这导致了定期审查策略的隐含使用。
固定评审期政策通常被称为(R,S),其中 R 是固定评审期, S 是 up-to-level。
优势
定期补充允许企业将他们的订单分组给他们的每个供应商。这有助于客户和供应商简化他们的操作,因为他们可以提前计划订单和工作量。
限制
- 🛡️ 风险。这种定期策略比连续策略(您可以在任何时间点下订单)风险更大,因为它会产生盲点:您不能在两个审核期之间下订单。如果你每周五向供应商下订单,但在周一晚上缺货,你将不得不再等四天才能下新订单。同时,你可能会因为缺货而损失销售额。这比固定再订购点政策风险更大,在固定再订购点政策下,你会在周一晚上直接下新订单。
- ️📦可变订货量 y .另一个问题是,每份订单的订货量会有所不同。这可能会破坏平稳的操作流程。例如,如果您有一个货盘化的产品,您可能不想移除整个货盘周围的包装来运输一个单元。
连续保单呢?
在持续策略中,我们根据固定的阈值订购产品。一旦净库存达到阈值(或低于阈值),我们就向供应商要求预定数量的单位(或启动生产批次)。这个阈值称为再订购点或 ROP。
连续保单假设客户可以随时向其供应商订购。实际上,情况可能并非如此。例如,供应商可能一个月只接受一次订单(或者一个月只发一次货——这是一样的)。在这种情况下,认为您会遵循固定的再订购点政策是不合理的,因为供应商实际上是在遵循自己的日历。因此,在实践中,真正的连续策略是例外的(一些完全自动化的生产流程或内部流程可能遵循这些假设)。
定期审查与持续政策。来源:我的库存培训:【https://supchains.com/live-training/
在开始你的第一个库存模拟之前,你可能想阅读我以前的文章,库存优化,它为优化策略奠定了基础。
模拟设置
需求和供应链
让我们从定义变量 time — 开始,它将设置我们模拟的持续时间。然后,我们可以填充遵循正态分布的需求数组。
import numpy as np
time = 200
d_mu = 100
d_std = 25
d = np.maximum(np.random.normal(d_mu, d_std, time).round(0).astype(int),0)
在这个模拟中,我们假设我们的需求是正态分布且严格为正。通过改变你设置它的方式,你可以随意改变这个假设。
然后,我们可以定义策略的其余输入,并计算各种参数。
from scipy.stats import norm
L, R, alpha = 4, 1, 0.95
z = norm.ppf(alpha)
x_std = np.sqrt(L+R)*d_std
请注意,我们将 x_std 计算为超过*风险范围的需求偏差,*风险范围是我在书中虚构的。
接收订单需要等待的最长时间。在此期间,您的库存有被耗尽的风险。
您可以在下图中看到我们的策略(和模拟)在第一个时间步中的表现。在时间步长 0 结束时做出的订单将在时间步长 5 期间可用。
模拟和库存策略的工作原理。来源:库存优化:模型和模拟
库存水平
让我们使用常用的安全库存公式来设置不同的库存水平。
- 📦**周期股 Cs 。**在一个补货周期内满足预期需求(或预测)所需的库存。
- 🛡️ **安全库存不锈钢。**保护供应链免受需求变化(或预测错误)和供应缺乏可靠性影响的库存。
- 🚚**在途库存为。**不同地点之间的在途库存。通常,这些是从供应商处订购的货物,但还没有在我们的仓库中提供给我们的客户购买。
Ss = np.round(x_std*z).astype(int)
Cs = 1/2 * d_mu * R
Is = d_mu * L
S = Ss + 2*Cs + Is
由于我们的传入订单可以在运输途中停留多个周期(在我们的示例中,有 4 个周期),我们将把 transit 定义为一个二维数组。
hand = np.zeros(time, dtype=int)
transit = np.zeros((time,L+1), dtype=int)
正如您在下表中看到的,新订单将从第二维结束时开始( transit[time,-1] ),然后通过第二维久而久之(每周期一层),直到到达最后一个槽( transit[time,0] )。最后一个时段意味着此在途库存将在本期期末收到(换句话说,从下一期开始可用)。
来源:库存优化:模型和模拟
服务水平
我们还必须创建两个对象来跟踪策略的服务级别:
- stock-out_period 将包含一个 Boolean 值,用于标记某一期间是否缺货。
- stock-out_cycle 将为每个订单周期(即审核周期)包含一个布尔值,用于标记我们在上一个周期的任何时候是否缺货。
stock−out_period = np.full(time, False, dtype=bool)
stock−out_cycle = []
模拟初始化
我们现在必须为第一个时间步长初始化这些数组。初始现有库存和净库存是 S 减去第一个期间的需求。第二个期间的在途库存初始化为第一个期间的需求。
hand[0] = S − d[0]
transit[1,−1] = d[0]
如何模拟库存策略
我们现在可以开始模拟了。以下是在每个时间步 t 执行的主要步骤:
- 检查我们在期初是否收到订单( transit[ t-1,0] > 0 )。如果是这样,我们需要通过检查上一期间是否有缺货来计算周期服务水平。
- 通过减去需求 d[t] 并加上接收库存运输[t-1,0] 来更新现有库存
- 在 stockout_period[t] 中指出我们是否缺货。
- 更新净库存头寸 net[t] 。
记住是总在途库存 transit[t]。sum() 加上现有库存手【t】 - 通过将先前时间步长的值偏移 1 来更新在途数组: transit[t,:-1] = transit[t-1,1:] 。这代表了订单在供应链中移动的事实。
- 如果我们处于评审期( t%R==0 ),我们根据当前净库存头寸*net【t】*和合格水平 S 进行订购。然后,订单被存储在在途数组 transit[t,L] 的末端。
for t in range(1,time):
if transit[t−1,0]>0:
stockout_cycle.append(stockout_period[t−1])
hand[t] = hand[t−1] − d[t] + transit[t−1,0]
stockout_period[t] = hand[t] < 0
transit[t,:−1] = transit[t−1,1:]
if 0==t%R:
net = hand[t] + transit[t].sum()
transit[t,L] = S − net
你可以在这里下载我的书的摘录(共 100 页):https://supchains.com/books/
报告
最后,我们可以创建一个数据框架 df 来保存我们的模拟结果,并将它们绘制在类似下图的图表上。
df = pd.DataFrame(data= {'Demand':d, 'On−hand':hand, 'In−transit':list(transit)})
df = df.iloc[R+L:,:] #Remove initialization periods
print(df)
df['On−hand'].plot(title='Inventory Policy (%d,%d)' %(R,S), ylim=(0,S), legend=True)
我们还可以打印周期和期间服务水平。
print('Alpha:',alpha∗100)
SL_alpha = 1−sum(stockout_cycle)/len(stockout_cycle)
print('Cycle Service Level:', round(SL_alpha∗100,1))
SL_period = 1−sum(stockout_period)/time
print('Period Service Level:', round(SL_period∗100,1))
下一步是什么?
模拟对于优化不符合通常假设(常态、固定交付周期、无销售损失等)的策略是有说服力的。).上面的代码只是模拟驱动库存优化的第一步。
如果你想玩这个新工具,
- 查看不同的安全库存水平和提前期可以带来多大的库存变化和服务水平。
- 尝试不同的需求概率函数(如泊松或伽玛),看看它们与正常假设有何不同。您也可以使用自定义发行版。*
攻击神经网络
使用 PGD 攻击为神经网络制造视错觉。
图标由平面图标和 photo3idea-studio 组合而成,作者(【marcelmoos.com】T4
我们来看看下面两张图:
图片来自安娜·杜德科娃的Unsplash
左图是一只德国牧羊犬,右图是一只美丽的鹈鹕……至少根据最先进的神经网络来看是这样的。我们将探索如何从左边的图片为右边的神经网络创建视错觉。您甚至可以创建我们的狗图像的版本,顶级图像分类器可以将它与您喜欢的任何东西相混淆,从花椰菜到敞篷车。如果你更喜欢代码而不是文字,这里有一个交互式 Jupyter 笔记本。通过它,你可以关注和调整这个故事的一切:colab . research . Google . com/drive/1 fl 0 px 0g 4 ppdlycmrerp-PS blf 4 ftzug?usp =共享
答几年前,我在学习神经网络时做的第一件事就是训练一个简单的图像分类器。神经网络可以很好地分辨图像中的内容。然而,有一件事我当时没有问过自己:“这些网络到底在学什么?”
让我用一个例子来解释我的意思:我们人类是如何识别狗是狗的?我认为我们应该寻找一些与众不同的特征,比如尖耳朵、鼻子、尾巴、四条腿以及类似的东西。然而,对于神经网络来说,其他东西可能是必不可少的。他们在寻找什么,比如一只狗,这可能是一个相当神秘的事情。这种现象在我称之为神经网络的视错觉中变得非常明显。令人着迷的是,它们是 而不是 对人类的幻觉。
下图包含一只漂亮的德国牧羊犬。神经网络不仅正确地将它归类为狗,甚至将其归类为正确的品种。而且它对自己的决定有 93 %以上的把握,所以这里一切都好。
现在问题来了。我们来看看下面这张图:
该图像几乎是前一个图像的完美副本。**剧透:不是!**你必须非常仔细地观察才能看出不同。背景不是绿色的,画面似乎有点嘈杂。但对人类来说几乎是察觉不到的。我想我们都同意,每一个接受第一张图片显示的是一只德国牧羊犬的人都会对这张图片说同样的话。所以我们的神经网络也应该仍然声明它看到一只德国牧羊犬有大约 93 %的把握,对吗?错了。我们的神经网络现在 100%确定它看到了一只鹈鹕:
好吧,这里发生了什么?我们使用的分类器是一个 50 层的 残差神经网络 **(resnet)。**它在 ImageNet 数据集上进行了预训练,这是一个包含 1000 个不同对象类的庞大图像集合。接下来,我们使用了一张我们的 resnet 正确分类的德国牧羊犬的图片。
为了给我们的 resnet 创建视错觉,我们取原始图像并稍微改变原始图像的像素。我们只改变正确的像素,resnet 对我们可爱的狗最敏感的像素。
下图显示了我们需要改变多少像素来欺骗我们的分类器。因为像素变化非常小,我把它们放大了 10 倍,所以我们可以看得更清楚:
因此,我们改变了原始图像稍微偏蓝和偏红。还有,一些像素点变亮了一点。在我们看来,这些变化几乎不存在。但是对我们的 resnet 来说…哦,天哪…它改变了一切。现在看到鹈鹕就彻底服气了。最棒的是,它不一定是鹈鹕。这一招基本上适用于任何职业。我们可以欺骗神经网络,让它认为我们的狗是花椰菜、打印机或其他任何东西。此外,每一个像样的图像分类器都容易受到这些视错觉或者——它们在文献中如何被称为——对抗性图像的影响。
现在的问题是,我们如何找到这些混淆我们的分类器的像素变化?我们如何自动创造这些视错觉?
自动愚弄神经网络
我们对狗的形象所做的改变非常微小,但也非常具体。手动寻找这些扰动是不可行的。幸运的是,有各种不同的技术,也称为攻击,可以自动创建这些视错觉。其中一种攻击叫做投影梯度下降(PGD)** 。为了理解 PGD,我们首先需要快速提醒自己神经网络是如何使用梯度下降进行学习的。**
梯度下降
神经网络包含权重。改变权重会影响神经网络的输出。为了训练一个神经网络,我们需要量化它的答案与正确答案的偏差程度。这是通过损失函数来完成的。一旦定义了损失函数,我们就可以计算损失函数相对于网络权重的梯度。直观地说,(负)梯度告诉我们如何改变网络的权重,以使损耗尽可能快地减少**,这意味着它的答案尽可能快地变得更正确。**
基于梯度下降的学习图解。(图片由作者提供)
对训练集中的所有图像反复进行这种操作会得到一个训练好的网络。在高层次上,这概括了基于梯度体面的学习。
PGD 攻击
恭喜,如果你理解梯度下降,你就已经理解了 PGD 攻击。对于 PGD 攻击,我们采用网络并定义一个新的损失函数。对于我们的德国牧羊犬形象,我们希望它被归类为鹈鹕(或任何你喜欢的)。现在,类似于基于梯度下降的训练,我们计算梯度。
然而,我们不是根据网络的权重(那些是固定的)来计算梯度,而是根据图像的像素来计算梯度。直观上,这种梯度告诉我们如何改变图像的像素,以便网络尽快认为这是一只鹈鹕。
PGD 攻击的插图(没有最终投影)。(图片由作者提供)
差不多就这样了。我们应用这些变化并继续,直到神经网络实际上将我们的图像分类为一只鹈鹕。
**你可能会问,PGD 的 P 、投影*在哪里发挥作用。PGD 攻击允许我们为每个像素定义一个变化极限。每个像素的所有颜色通道都采用 0 到 255 之间的值。*对于 PGD 攻击,我们可以指定任何像素都不应该改变超过 10 个点。每当一个像素受到超过 10 个点的干扰时,该像素被投影到其允许的一组值。
Python 包
幸运的是,我们不需要了解所有的细节并从头开始重新实施 PGD 袭击。有一个令人惊叹的 python 包叫做fool box(【github.com/bethgelab/foolbox】T2),它支持 PGD 攻击(以及更多)并兼容 TensorFlow 和 PyTorch。使用 Foolbox,从德国牧羊犬图片生成我们的鹈鹕敌对图像既简单又快捷:**
*criterion = TargetedMisclassification(pelican_label)
attack = PGD()
adv_input = attack.run(net, german_shepherd, criterion, epsilon=10)*
如果你想自己生成对抗性的图像并运行和调整代码,我强烈建议查看 Jupyter 笔记本:colab . research . Google . com/drive/1 fl 0 px 0g 4 ppdlycmrerp-PS blf 4 ftzug?usp =分享
结论
从一张我们的分类器正确标记为德国牧羊犬的照片中,我们创建了一张敌对的图像——神经网络的一种视错觉——显示了同一只狗,但被我们的网络误分类为鹈鹕。创造这些视错觉可能是一项有趣的练习。然而,在现实生活中,敌对的图像可能会产生严重的后果。试想一下,一辆自动驾驶汽车将停车标志误标为限速标志。有一些技术可以抵御敌对的图像,但那是另外一个故事了。
觉得这个故事有趣?你可以在这里成为一个中等会员来支持我的写作:medium.com/@mmsbrggr/membership。你将获得所有媒体的访问权,你的部分会员费将直接支持我的写作。
欢迎在 LinkedIn上向我提出私人问题和评论。如果你喜欢这篇文章,让我告诉你我的时事通讯:marcelmoos.com/newsletter。
以下是另外三个你可能感兴趣的故事:
*** ***
用记忆让你的神经网络更聪明:神经图灵机
将神经网络与记忆系统结合起来,可以像人类一样学习通用算法。请继续阅读,了解更多信息。
Lyman Hansel Gerona 在 Unsplash 上拍摄的照片
计算机很神奇,因为它们执行任意算法的速度比人快几个数量级。神经网络很神奇,因为它们可以学习特定的东西(开车、下棋等。)远胜人类。然而,还没有一个神经网络将这两者结合起来——在任意算法上的卓越性能。直到 2014 年,一篇关于“神经图灵机”的论文发表。本文作者将标准神经网络与类似计算机的记忆系统相结合,发现这种新型网络能够学习一些简单的算法,例如复制粘贴。我认为这是一个惊人的成就,所以让我们仔细看看它是如何工作的。
首先,我们先明确一下“学习简单算法”是什么意思。复制粘贴算法就是这样一种算法。基本上,给定一系列任意大小的输入向量,我们希望输出与输入相同。现在你可能会问——这不就是身份函数吗?当然,即使是最简单的神经网络也能学会!这是真的——神经网络可以学习给定输入大小的的身份函数。神经网络无法学习适用于任何输入大小的任意身份函数**。您可以自己尝试:训练一个神经网络来学习长度为 10 的向量的恒等函数,然后测试长度为 20 的向量的准确性。性能会很差。总而言之——我们的目标是用单个神经网络学习算法的一般形式,而不仅仅是算法的一个具体情况。**
计算机擅长算法的一般形式。例如,当您在文本编辑器中进行复制(Ctrl-C)和粘贴(Ctrl-V)时,所选文本有多长并不重要。计算机将完美地执行任意长度的复制-粘贴。计算机如此擅长这类算法的部分原因是因为它们的记忆系统。基本上,这个记忆系统让计算机存储输入,对这些输入进行处理,并在以后需要时检索它们。相比之下,神经网络拥有的唯一“存储”是网络权重的值。砝码的容量远小于计算机上千兆字节的内存。这就是为什么我们要给神经网络添加一个记忆系统——提供许多算法成功执行所需的存储空间。
那么研究人员是如何实现记忆系统的呢?这个想法很简单。“内存”是一个 N x M 维的大型二维矩阵,N 行中的每一行代表一个内存单元。n 和 M 是网络运行前设置的超参数。下一步是定义如何读写这个内存。这更棘手。根据所学的算法,我们可能只需要读取矩阵中的一行,或者矩阵中的所有行。也有可能一些行比其他行更重要。为了适应所有这些情况,我们引入了长度为 n 的权重向量 w_read 。从存储器的“读取”则是所有矩阵行的加权和:σ(w _ read _ I)*(row _ I)。(在本文的其余部分,向量将被加粗,标量将是常规字体)
写入存储器更复杂。首先,我们需要决定如何处理内存中的现有行。因此,我们需要另一个参数 e 。这个参数是一个长度为 M(与行相同)的向量,具有 0 到 1 之间的各个分量。 e 和 row_i 之间的元素乘积决定了 row_i 剩余多少。例如,如果 e 的所有组件都是 0,这意味着我们不想保留任何 row_i 。如果 e 拥有所有 1 个组件,这意味着我们想要保留所有的 row_i 。而如果 e 在中间某处,我们更想保留 row_i 的某些组件。注意 e 对于所有行都是相同的。
一旦我们决定保留什么,我们接下来必须添加任何新的信息。因此,我们有另一个参数 a 添加到每一行。最后,我们有一个权重向量 w_write ,它决定了对数组中每一行的写操作的大小(或“重要性”)。因此每行的总更新量为row _ I _ new=erow _ I _ old+w _ write _ Ia。
我们现在已经描述了如何从内存矩阵中读取和向内存矩阵中写入单独的行。我们这样做是根据一些参数: w_read,w_write,e,和 a 。自然的问题是——这些参数从何而来?回想一下,总体目标是将神经网络与记忆系统结合起来。因此,我们将这些参数作为我们系统的神经网络部分的输出。更准确地说,我们将建立一个神经网络,它有两个输出区域:读头和写头。网络的读头区负责输出读取所需的参数: w_read 。同样,网络的写头区会输出写参数: w_write,e,和 a 。最后,读取头的输出作为整个系统的输出返回。
图片作者。
这种架构的一个主要优点是整个事情都是可微分的。请注意,所有读/写操作对于每个组件来说都是线性的,因此是可区分的。这使得网络在概念上易于训练:只需找到每个分量相对于输出的梯度,然后进行梯度下降。
丰富
在这一点上,我们有一个原型网络,其中包含了一个记忆系统。然而,仍然有可以改进的地方。这些改进大多围绕着 w_read 和 w_write 向量,如果您还记得的话,它们决定了每个内存行在读或写中的相对重要性。正因为如此, w 向量通常被称为寻址向量,参考了计算机中内存寻址的思想。就像现在的网络一样,寻址向量由读写头直接输出。
我们可以对寻址向量的产生方式进行各种修改。首先,不是让读写头输出 w ,而是让它们输出一个键向量 k 。那么我们可以做几件事:
- 算法中一个常见的用例是根据 k 获取内存中的特定行。实现这一点的直观方法是对所有行使用余弦相似性,并基于这些余弦相似性对 w 的分量进行加权。我们称之为内容聚焦**。**
- 如果我们不想要一个基于内容的特定行( k ),而是想要一个特定的常量行呢?例如,我们可能正在学习一个在第 5 行存储重要内容的算法。我们如何总是访问第 5 行?这被称为位置聚焦**。首先,我们可以让读写头输出一个门加权值 g。门加权值是一个介于 0 和 1 之间的标量,它决定了保留多少先前地址 w_(t-1) 与保留多少当前地址 w_(t) 。换句话说,更新方程是w (t) new =g w _(t-1)+(1—g)w _(t)。在我们强调的用例中(在每个时间步访问第 5 行),神经网络最终会找到一个聚焦于第 5 行的 w ,然后将 g 设置为 1,以在未来的时间步中保持该 w 。
- 另一个常见的用例是循环,我们希望内存地址在每个时间步长增加一个常数。我们可以通过使读写头输出一个代表地址的每个部分增加多少的移位加权来实现这一点。例如,如果 s=2,那么 w 的所有分量都移动 2。因此,如果先前的 w = [0.1,0.7,0.2,0](集中在第 2 行),随着移位的应用 w = [0.2,0,0.1,0.7](集中在第 4 行)。
寻址机制的这些改进为神经网络提供了更多的工具来访问它在内存中想要的行。相应地,我们应该看到准确性的提高。整个网络的更新示意图如下所示:
图片作者。
这是一个简单的版本,论文作者称之为“神经图灵机”(NTM)。在论文中,我们描述的各种组件更加复杂,但思想是相同的。如前所述,尽管 ntm 比常规神经网络要复杂得多,但它们很容易训练,因为所有组件都是可微分的。因此,训练上述 NTM 的方式是将网络/存储器初始化为一些值,决定成本函数,并简单地运行梯度下降。
有用吗?
是的。实验表明,ntm 比常规神经网络更好地学习一些算法。让我们考虑一下我们在开始介绍的复制粘贴问题:取一个输入向量序列,输出相同的序列。论文作者在这项任务中训练了两个不同版本的 NTM,以及一个 LSTM。下表显示了结果:
图片作者。
各行代表每种网络类型的误差(单位为每序列位数)。这些列表示(以千计)到达该点需要多少训练序列。例如,第一列意味着通过 50000 个训练序列,NTM/LSTM 具有接近每个序列 0 比特的误差,NTM/前馈网络具有 0.2 的误差,而 LSTM 具有 8.2 的误差。
正如你所看到的,这两个 NTM 变种学习复制粘贴问题比常规神经网络(LSTM)快得多。一旦学习完成,它们也具有较低的误差(注意,LSTM 的渐近成本约为每个序列 0.5 比特,但是两个 ntm 的渐近成本接近于 0)。研究人员还检查了两个经过训练的 NTM 网络中网络和记忆之间的交互模式,并意识到该模式可以用下面的伪代码来概括:
****初始化矩阵,写/读磁头到开始位置
而(输入不是分隔符):
- 将输入写入写入磁头位置
- 将写磁头增加 1
而(真实):
- 从读取头位置读取输出
- 将读取头增加 1
这太神奇了。虽然并不完美,但这段伪代码显示了 NTM 学会了像人类一样复制粘贴。这是我第一次看到神经网络以如此可理解、直观的方式学习东西。通常神经网络有一个“黑箱”问题——它们学习东西,但我们无法理解它们。这里不是这样的。****
另一件重要的事情是,研究人员采用了在大小为 20 的序列上训练的 NTM,并应用 NTM 来复制粘贴更长的序列。至关重要的是,尽管 NTM 犯了一些错误,但它总体上仍然工作正常。相比之下,常规的神经网络(在 LSTM 的论文中)不能推广到长度超过 20 的序列。前面我们提到过,我们想学习算法的一般形式,而不是特定的版本——看起来 NTM 成功地实现了这个目标。
研究人员还对 ntm 进行了其他任务的训练,如联想回忆和数据分类。我们不会在这里进入细节,但一般来说,NTMs 优于常规神经网络,并能够学习任务的通用算法。更多详情,请阅读原文论文。
ntm 是神经网络架构中一个真正令人兴奋的发展。他们能够学习一般形式的算法,这是以前的网络无法做到的。正因为如此,我认为 ntm 是通往真正智能的关键一步。我很高兴看到这种基于记忆的想法的进一步发展。如果您有任何问题/意见,请告诉我,感谢您的阅读!
让你的神经网络更小:修剪
剪枝是使神经网络更经济的重要工具。请继续阅读,了解它是如何工作的。
神经网络的一个问题是它们的大小。你在在线教程中看到的神经网络足够小,可以在你的计算机上有效运行,但工业中的许多神经网络都很庞大,难以操作。它们通常需要几天来训练,运行它们会消耗大量的计算能力。这就提出了一个问题:在保持测试集准确性的同时,有没有可能减小神经网络的规模?事实证明,是的。有一种叫做“修剪”的技术可以做到这一点。还有一个被称为“彩票假说(LTH)”的想法,它让我们深入了解为什么修剪会有效。在本文中,我们将首先查看剪枝算法,然后讨论 LTH。
修剪是一种简单、直观的算法。有许多变体,但基本思想适用于任何神经网络。想法是这样的。在一个大型的、经过训练的神经网络中,会有一些具有较大幅度的权重和一些具有较小幅度的权重。自然,具有大幅度的权重对网络输出的贡献更大。因此,为了减小网络的大小,我们去除(修剪)小幅度的权重。要修剪的小幅度权重的确切数量由用户设置——大约 10%是合理的。问题是,一旦发生这种情况,网络就不再被正确训练。因此,在修剪小幅度的权重之后,我们需要再次训练网络。我们可以根据我们想要使网络有多小来任意次数地进行这个循环(修剪->训练)。
样本剪枝算法的两次迭代。红线代表修剪后的权重。图片作者。
修剪的一个好处是它确实有效。有大量的经验证据支持它。更具体地说,修剪已经被证实可以在降低网络规模(内存)的同时保持准确性。有什么不好的地方吗?是—完成清理过程可能需要很长时间。prune- >训练循环的每一次迭代都要花费大量时间,尤其是在网络很大的情况下。我们可以通过增加每次迭代期间修剪的权重的数量(例如,从 10%到 20%)来减少循环的数量,但是这样循环的每次迭代需要更长的时间。总而言之,修剪是有效的,但是需要一段时间才能完成。
还有一个问题是,最终修剪后的子网可能会有一个奇怪的体系结构,权重会出现在看似随机的地方。因为现代代码/硬件仅针对少数神经网络架构进行了优化,所以有可能修剪后的网络虽然更小,但实际上可能需要更多的时间来运行。这些权衡是否值得取决于具体的用例。
现在我们转到一个更有趣的问题:为什么修剪会起作用?如前所述,我们知道修剪适用于许多流行的神经网络架构。这导致了一个假设,即许多神经网络被过度参数化。换句话说,存在一个更小但仍然有效的网络(通过剪枝找到)的事实意味着在原始网络中有太多的参数。
另一个问题是为什么剪枝算法选择最终的权重。回答这个问题的一个有趣尝试是彩票假说。这篇论文中的研究人员声称,通过修剪选择的最终子网是因为该子网的初始初始化而被选择的。
他们通过以两种不同的方式执行 prune->train 循环得出了这个结论。第一种方法,在每次训练循环之前,他们随机初始化所有剩余的(未修剪的)权重。第二种方法是,在每次训练循环之前,他们对所有剩余的权重使用初始初始化。他们发现,使用原始初始化显著提高了剪枝算法的最终精度和训练速度。因此,研究人员假设最初的初始化是剪枝算法选择什么子网背后的驱动因素。然而,不清楚为什么会这样。起初,研究人员认为剪枝算法喜欢在训练期间不变的权重(即剪枝算法更喜欢接近最终值的原始权重初始化)。然后他们测试了这个理论,实际上发现正好相反。修剪算法选择在训练期间变化最大的权重。为什么会这样,这意味着什么,这仍然是一个悬而未决的问题。
总之,有实验证据表明,修剪产生的子网络与权重的初始初始化有关。建筑怎么样?所选子网的架构可能很特别,这似乎是很自然的。已经有一些研究表明深度网络比浅层网络更有效地表示某些假设类别(多项式对指数)。对于修剪后的子网来说,可能存在类似的情况。但是,我没有找到任何实证或理论论文来支持这个理论。
总体来说,有两个关键点。首先,修剪起作用(需要一些时间权衡)。第二,修剪肯定与原始网络的初始化有关,也可能与体系结构有关。感谢阅读,并请留下任何问题/评论!
使用 Azure 认知服务制作您自己的有声读物
我们过去购买有声读物的日子已经一去不复返了,现在是时候使用 Python 和 Azure 认知服务了
L ets 说你已经有了一本书的 PDF 版本,这本书是你从某些来源(可能是免费的)获得的,你没有时间一行一行地阅读它。也可能发生的情况是,你不想花钱买它的音频版本,或者可能它的音频版本不可用。那么这篇文章就送给你了。在本文中,您将看到如何使用简单的 python 脚本和 Azure Cognitive service 的文本到语音功能制作自己的有声读物。但是首先让我告诉你一些关于有声读物的事情。
什么是有声读物?
有声读物(或有声读物)是大声朗读的一本书或其他作品的录音。完整文本的阅读被描述为“未删节的”,而较短版本的阅读则是删节的。”— [维基百科](https://en.wikipedia.org/wiki/Audiobook#:~:text=An%20audiobook%20(or%20a%20talking,shorter%20version%20are%20an%20abridgement.)。
市场上有许多服务可以为你提供有声读物。一些受欢迎的有有声、手写、有声读物等等。但是所有这些都要收取订阅费。
所以,让我们开始建立自己的有声读物。为此,请确保您具备以下先决条件。
先决条件:
- 确保你已经在你的电脑上安装了 Python 3.5 或更高版本。万一你没有,那么你可以从官网安装。在我的电脑上,我安装了 Python 3.6,所有这些演示都将基于它。不要担心,如果你使用的是最新的 Python 3.9,不会有任何不同。
- 接下来,我们需要一些能帮助我们处理 PDF 的东西。因为 Python 有一个很棒的库,可以充当 PDF 工具包,名为 PyPDF2 。你也可以看看它的完整文档。
- 我们还需要一个 Azure 账户来使用 Azure 认知服务。但是等等,我们在寻找制作免费有声读物的方法,对吗?是啊!别担心,我会告诉你免费的东西。
安装— 1:
首先,我们将安装 PyPDF2。对于,请打开终端或命令提示符,并键入:
pip install PyPDF2
现在,如果你第一次使用 Python(或者 pip ),你很可能会得到一个错误,比如“PIP 不被识别为内部或外部命令……”。这基本上意味着你没有为 pip 设置路径。下面我会给出一篇文章的链接,详细解释如何解决这个问题。
虽然我们需要一些其他的库,但是让我们先看看如何使用 pdf。
阅读 PDF 文件:
我们的 PyPDF2 库可以对一个 PDF 文件进行各种各样的操作。其中我们会用到它的阅读能力。为此,我们首先需要创建一个 python 文件(一个带有。py 作为扩展名)并导入库。
import PyPDF2
然后我们将创建一个名为 book 的对象,我们将用它在阅读模式下打开我们的 pdf。
book = open(**"yourPdfName.pdf"**,**"rb"**)
这里需要注意的重要一点是 pdf 的位置。上面的代码假设所需的 pdf 与 python 文件位于同一文件夹中。如果它在不同的文件夹中,那么你有两个选择。1:将文件(pdf 或 python)移动到与其他文件相同的文件夹中。2:给出 pdf 文件的确切位置。假设我的 pdf 在桌面中,那么我会写“C:\ \ Users \ \ sag Nik \ \ Desktop \ \ yourpdfname . pdf”。这将解决问题。
现在可能会有一个问题,什么是“rb”写到最后。“rb”基本上是文件处理中使用的一种存取方式。要了解 Python 中其他访问模式或文件处理的更多信息,请参考 Python 文件处理。
https://stackabuse.com/file-handling-in-python/
到目前为止,我们只是打开了我们的 PDF 文件。现在我们需要指定一个读者,他可以阅读我们的 pdf 并从 pdf 文件中逐页提取文本。为此,我们将使用下面几行代码。
reader = PyPDF2.PdfFileReader(book)
**for** num **in** range(0,reader.numPages):
text = reader.getPage(num).extractText()
# print(text)
这里对象读者利用 PyPDF2 库来阅读我们的书。之后,它遍历书的每一页,并通过使用基于页码的 for 循环从中提取文本。如果你想知道它会显示什么样的文本,你可以去掉“#”,看看它会显示什么。甚至你可以将它与你的 pdf 中的文本进行匹配。
现在我们得到了我们的文本,我们唯一剩下的事情就是把它转换成语音并听它。为此,我们将使用 Azure 认知服务。要了解更多关于认知服务的信息,您可以访问下面的链接。
https://azure.microsoft.com/en-in/services/cognitive-services/#overview https://docs.microsoft.com/en-us/azure/cognitive-services/what-are-cognitive-services
文本到音频:
为了使用 Azure 认知服务,你需要一个 Azure 账户。但是我们需要免费得到它,对吗?为此,请访问 Azure 注册页面并选择开始免费来创建您的新 Azure 帐户。你也可以观看这个 YouTube 视频来获得设置免费账户的帮助。
如果你是一名学生(像我一样),那么你有三种不同的选择来获得 Azure 帐户并免费使用。
- 为学生使用 Azure。但是这需要你使用你的大学电子邮件 id (an )登录。edu 邮箱)。如果你的学校没有提供这样的课程,那么你有下面两个选择。
https://azure.microsoft.com/en-in/free/students/
- *使用 GitHub 学生开发者包。*这个包还有大量其他高级设施。这给了你 100 美元的 Azure 信用。
https://education.github.com/pack
- *成为微软学生学习大使。*该计划每月为您提供 150 美元的 Azure 信用点数和许多其他好处,包括 MTC 代金券和免费域名。我个人非常热爱这个社区。
https://studentambassadors.microsoft.com/
在我们获得 Azure 账户后,是时候创建一些 Azure 资源了。但是为什么呢?为此,我们需要了解 Azure 认知服务的定义。
认知服务让每个开发人员都可以接触到人工智能,而不需要机器学习专业知识。**只需一个 API 调用,即可将看、听、说、搜索、理解和加速决策的能力嵌入到您的应用中。**使所有技能水平的开发人员能够轻松地将人工智能功能添加到他们的应用程序中。
现在我们可以清楚地看到,为了嵌入认知服务的工具,我们需要进行 API 调用。要进行 API 调用,我们需要一些键和端点。这将由我们的 Azure 资源提供。
获取我们的语音 API 密钥:
第一次登录 Azure 门户网站。它看起来会像下面这样。
使用我的帐户登录时 Azure portal 的屏幕截图
然后我们将前往创建一个资源并点击它*。寻找左上角的大加号(+),你会在那里找到选项。*
创造言语资源的过程。我的账户截图。
在这里你可以找到 Azure 提供的所有可用服务的列表。现在在搜索栏中搜索语音,如上图所示,并点击回车。然后,您可以看到如下所示的页面。
来自我的帐户的屏幕截图
现在点击创建来创建你的 Azure 语音资源。它现在会问你一个名字。给你的资源起一个有意义的名字。这个名字可以帮助你以后找到你的资源。对我来说,我选择“learnoverflow”作为它的名字,因为我们从这篇文章中的学习应该是溢出的。
在订阅区域选择一个订阅或创建一个您想要使用的新订阅。这基本上决定了 Azure 费用将如何向您收取。但是不要担心,这次你不会收到任何账单。我将在接下来的几个步骤中解释如何操作。
它还要求一个位置。这意味着你想在哪个领域使用资源。Azure 是一个全球平台,它为你提供了大量可以创建资源的位置。但是,为了从您的资源中获得最佳性能,请选择离您最近的位置。
创建语音资源时来自我的 Azure 帐户的图像
接下来是重要的部分,这是定价层。对于语音资源,您将获得两种类型选项:1 .自由 F0 ,2。*标准 S0。*两者各有不同的优势和好处。如果您使用免费的低容量语音服务层,即使在免费试用或服务点数过期后,您也可以保留此免费套餐。显然我们这次将使用免费的 F0 作为我们的资源。这是免费使用这一资源的关键。如果你对所有的定价等级都感兴趣,你可以看看语音服务定价。
在资源组部分,为该语音订阅创建一个新的资源组,或者将该订阅分配给一个现有的资源组。资源组是一个容器,通过在一个地方保存相关资源,帮助您组织各种 Azure 订阅。
现在,您已经填写了所有详细信息,只需单击“Create”并等待几秒钟进行资源分配。
您会看到这样的通知,说明您的部署正在进行中。图片来自部署资源后我自己的帐户。
在这里,您可以看到您的资源得到了部署。来自我自己的 Azure 帐户的图片
现在,单击“转到资源”访问您的资源。
您的资源页面。创建资源后,来自我自己帐户的图像
在上图中,您可以看到资源的所有详细信息。但是我们正在寻找 API 密钥,对吧。在总览页面(每次打开资源时你都会进入的主页面)上,我们没有那么多工作要做。
现在看左边的面板(上图),我用红色标记了一些东西,这是键和端点。我们所有的 API 密钥和其他必需的信息都在这个选项卡中。点击后,你会发现类似下图的东西。
来自我自己的 Azure 帐户的图片
在这里,您可以看到有两个 API 键。任何一个密钥都足以访问你的认知服务 API。我们需要 Python 代码中的一个键和位置。你可以把它们记在记事本上以备将来参考,也可以根据需要直接进入本页查找。
使用我们的 API 密钥:
现在打开您的 Python 代码编辑器,开始实现您刚刚获得的 API。但是在此之前,我们需要准备本地机器来处理这样的 API 调用。为此,我们需要安装一些 Python 包。
安装— 2:
我们需要认知服务语音 SDK Python 包。为此,我们将再次使用 pip 命令。
pip install azure-cognitiveservices-speech
这个包裹对我们会很有帮助。
实现 API 密钥:
我们将从导入刚刚安装的语音库开始。将此内容添加到我们之前编写的上一条 import 语句之后的第二行。
import azure.cognitiveservices.speech as sdk
之后,我们将把 API 键和区域分配给 Python 代码中的一个变量。这是我们订阅信息的基本设置。在我们之前写的 for 循环开始之前保留这些语句。
key = "YourSubscriptionKey"
region = "YourServiceRegion"
用创建 Azure 资源后获得的密钥和区域替换 YourSubscriptionKey 和 YourServiceRegion 。之后,我们将使用下面的代码创建一个带有订阅密钥和服务区域的语音配置实例。
config = sdk.SpeechConfig(subscription=key, region=region)
我们将继续使用默认的扬声器作为音频输出来创建我们的语音合成器。这将作为人工产生的人类声音。
synthesizer = sdk.SpeechSynthesizer(speech_config=config)
现在,我们将进入我们之前创建的 for 循环来阅读我们的书的每一页。我们的方法是将我们阅读的每一页的文本制作成音频。但是如果你愿意,你也可以修改代码来获得整本书的文本,然后通过下面的代码传递它。
result = synthesizer.speak_text_async(text).get()
还记得我们之前创建的变量文本吗?现在是将收到的文本合成语音的时候了。一旦您的 Python 解释器执行了上面的行,那么您就可以期望您的扬声器播放写在特定页面上的文本。
一旦你听到一个声音从你的电脑扬声器或从你的耳机连接到你的电脑,你肯定你终于做了自己的有声读物。万岁!!!
完整实施:
如果你希望看到这个程序的运行,那么在最后我会链接一个 YouTube 视频,你可以通过视频格式浏览整篇文章。但在此之前,让我讨论一些重要的事情。
运行此程序时可能出现的问题:
如果你是第一次使用这个 Azure 语音库,你可能会遇到一些问题。它可能会提示“azure 不被识别为内部或外部命令”,即使你已经用上面写的命令安装了正确的库。为了避免这些,请确保在实际安装 Speech SDK 之前更新您的 pip。让我们假设你已经安装了语音 SDK,现在你得到这些错误,然后如何解决?
在这种情况下,您首先需要卸载您的语音 SDK。
pip uninstall azure-cognitiveservices-speech
然后使用下面的命令升级 pip。总是建议在安装任何库之前确保您的 pip 已更新。
python -m pip install --upgrade pip
然后使用之前使用的相同命令安装 Azure speech SDK。
这将解决你的问题。无论如何,你仍然面临同样的问题,那么你可能需要使用下面的命令安装 azure。
pip install azure
但是不要担心,升级您的 pip 肯定会解决您的问题。
你可以对此做很多改进。要一遍又一遍地听某本书,你不需要再次运行这个程序。实际上,您可以将生成的音频保存为您想要的音频格式。你也可以创造自己的声音。以及更多其他的东西。但是请记住,并不是所有的特性都属于自由层 API。如果你感兴趣的话,你一定可以查看使用这种文本到语音转换功能的完整文档。
查看有声读物的运行情况:
最后,当你成功地制作了有声读物,是时候听它了。但是这篇文章是书面文件,它不可能在这里显示最终输出。因此,下面是一个视频实现的整个过程中,它的建设,如本文所示。跳到 23:00 仅查看其实施情况。希望这能帮助你。
结论:
在本文中,我们介绍了基本有声读物的实现,它可以使用几行 python 代码和 Azure 认知服务阅读整本书。有声读物的最终音频结果并不是最好的,因为文本在馈送到扬声器引擎之前需要一些预处理。
感谢您的阅读!
我是微软学生大使 Sagnik Chattopadhyaya。你可以访问我的网站来了解我。
推特: @sagnik_20 。YouTube: 学习溢出
希望你能从这个故事中学到一些东西。❤
快乐学习!🐱💻
使用 Surprise 制作您自己的书籍和电影推荐系统
Juraj Gabriel 在 Unsplash 上拍摄的照片
我花了几年的博士时间来构建推荐系统,并从头开始对它们进行基准测试,只有当我完成它时,我才听说了 sikit 图书馆的惊喜。
这是我以前就想知道的一个惊喜。这将节省我的时间和精力来实现许多推荐系统基线。
所以我想在这里分享关于这个库的信息,并给出一些关于如何使用它的代码,因为它可能会帮助其中一个读者。让我们开始吧!
什么是惊喜?
Surprise(代表简单 Python 推荐系统引擎)是一个 Python 库,用于构建和分析处理显式评级数据的推荐系统。它提供了各种现成的预测算法,如基线算法,邻域法,基于矩阵分解(奇异值分解,PMF,SVD++,NMF),以及许多其他算法。此外,各种相似性措施(余弦,MSD,皮尔逊…)是内置的。来源:http://surpriselib.com/
我将在两个不同的数据集上使用 Surprise:
- 建立一个电影推荐系统。
- 建立图书推荐系统。
在这两种情况下,我将使用协作过滤技术和基于内容的技术来过滤项目,不用担心我会解释它们之间的区别。
使用 Surprise 构建电影推荐系统
安装惊喜
pip install surprise
读取数据集
我在这里使用的是 MovieLens 数据集。它包含 2500 万用户评级。数据在。/data/raw 文件夹。我们可以直接加载。csv 文件,具有内置的惊喜功能,但为了以后的灵活性目的,通过 Pandas 数据框加载它会更方便。
出于复杂性考虑,我只使用了这个数据集的一个子集,但是你可以全部使用。然后,我将评级上传到一个惊喜数据集对象,该对象按顺序包含以下字段:
- 使用者辩证码
- 电影 Id
- 相应的评级(通常在 1-5 的范围内)
训练和交叉验证协同过滤模型
协同过滤方法依赖于用户的偏好以及系统中其他用户的偏好。这个想法是,如果两个用户是志同道合的(在过去就项目的一些偏好达成一致),与一个用户相关的项目被推荐给另一个用户,反之亦然。他们仅从用户项目评级矩阵中获得推荐。来源:[我的博士论文报告](https://boa.unimib.it › phd_unimib_799490)
Surprise 实现了一些协作算法,如奇异值分解、NMF 等。
0it [00:00, ?it/s]RMSE: 0.9265
RMSE: 1.0959
Computing the msd similarity matrix...
Done computing similarity matrix.1it [00:12, 12.72s/it]RMSE: 0.9919
RMSE: 0.9299
RMSE: 1.1025
Computing the msd similarity matrix...
Done computing similarity matrix.2it [00:26, 13.54s/it]RMSE: 0.9915
RMSE: 0.9270
RMSE: 1.0996
Computing the msd similarity matrix...
Done computing similarity matrix.3it [00:42, 14.14s/it]RMSE: 0.9905
训练基于内容的过滤模型
基于内容的过滤方法分析与用户相关的项目的一组特征,并基于这些特征学习用户简档。过滤过程基本上包括将用户简档的特征与项目内容(即项目简档)的特征进行匹配。来源:[我的博士论文报告](https://boa.unimib.it › phd_unimib_799490)
因此,这里我将直接依赖于项目属性,即电影标题。首先,我必须用属性向量描述一个用户概要文件。然后,我将使用这些向量来生成基于相似性度量的推荐。
大多数基于内容的过滤方法是基于启发式模型,该模型在向量空间模型中将用户和项目表示为 TF-IDF(或 BM25)的向量。
我在这段代码中使用了 TF-IDF,它考虑了在一个文档中频繁出现的术语(TF =term-frequency),但在语料库的其余部分很少出现的术语(IDF = inverse-document-frequency)。
由于项目和用户简档被表示为 TF-IDF 的向量,所以项目以与用户相似度递减的顺序被推荐
(用户简档以项目的相同形式表示)。最常见的度量是余弦相似性,我将在下面使用它来计算两个配置文件之间的相似性。
假设用户想要电影《山达基和信仰的监狱(2015)》中最‘相似’的 10 部电影。
Recommending 10 products similar to Going Clear: Scientology and the Prison of Belief (2015)...
-------
Recommended: Soulless 2 (2015) (score:0.09111541613391295)
Recommended: Víkend (2015) (score:0.09111541613391295)
Recommended: No Longer Heroine (2015) (score:0.06179864475000222)
Recommended: In the Shadow of Women (2015) (score:0.06179864475000222)
Recommended: Marco Polo: One Hundred Eyes (2015) (score:0.05026525144746273)
Recommended: Ugly American, The (1963) (score:0.0)
Recommended: Snitch Cartel, The (El cartel de los sapos) (2011) (score:0.0)
Recommended: Drone (2014) (score:0.0)
Recommended: Kenny Rogers as The Gambler (1980) (score:0.0)
Recommended: Le Rossignol (2005) (score:0.0)
构建图书推荐系统
这将是一个类似于上面的代码,但应用于不同的数据集。
我使用了 Kaggle 上可用的 goodbooks-10k 数据集。它包含评级和图书 csv 文件。第一个包含超过 53,000 个用户对 10,000 本书的评级数据。第二个文件包含 10,000 本书的元数据(标题、作者、ISBN 等。).
我通过 Github 分享了书籍和电影推荐系统。
结论
就是这样!您可以将此推荐系统代码模板应用于任何您想要的数据。在接下来的几天里,我还将分享关于上下文推荐系统以及如何实现它。
所以,敬请期待!
通过缓存让您的 Python 代码运行得更快
使用“functools”中的“cache”和“lru_cache”
提高 Python 代码的速度非常重要。它不仅能节省时间,还能减轻机器的负荷。提高程序性能的一种方法是缓存。
在这篇文章中,我将讨论什么是缓存,什么时候使用缓存,以及如何在 Python 中使用缓存。
查看我以前的一篇文章,关于使用爱因斯坦符号编写更好更快的 Python。
何时使用缓存
您可以通过实现缓存策略来加快 Python 代码(或任何代码)的速度。这个想法是存储一个函数的结果,这样在将来的调用中你就不必重新计算或再次检索结果。这在使用重复参数多次调用资源密集型或时间密集型函数时非常有用。
- 你不应该缓存一个依赖于外部变量的函数。(例如
time()
或random()
)。 - 你不应该缓存有副作用的函数。
从服务器检索数据时,缓存也很有用。我们可以在本地存储(缓存)数据,而不是每次需要数据时都请求服务器。但是,如果我们的缓存空间有限,或者缓存的数据会随时间而变化,我们可能需要一个缓存策略。
缓存也可以在服务器本身上实现。我们可以缓存内容并从缓存中提供给用户,而不是每次用户加载页面时都查询数据库。然后,每隔一段时间更新我们的缓存。
缓存策略
高速缓存可能出现的一个问题是高速缓存太大。虽然您可以创建无限的缓存,但最好不要这样做。为了克服这一点,我们可以在缓存时使用许多策略。这些策略描述了何时从缓存中清除值。
先进先出(FIFO)
FIFO 缓存流程图。图片作者。
第一个添加到缓存中的值是第一个被逐出的值,依此类推。当较新的条目最有可能在我们的程序中被重用时,最好使用这种策略。
后进先出法
后进先出缓存流程图。图片作者。
就像先进先出但相反。最后一个加到缓存中的值是第一个被逐出的值。当旧条目最有可能在我们的程序中重用时,最好使用这种策略。
最近最少使用(LRU)
LRU 缓存流程图。图片作者。
这可能是最著名的策略。名字说明了一切。它会清除最近最少使用的值。但这意味着什么呢?
当您调用缓存函数时,结果被添加到缓存中(这就是缓存的工作方式)。但是当您使用已经缓存的值调用缓存函数时,它会返回缓存的值并将其放在缓存的顶部。
当缓存满时,最底部的值被删除。
在这种策略中,最近使用的条目最有可能被重用。
基于时间的缓存
这更适合缓存函数随时间变化的情况。添加到缓存中的每个条目都有一个过期时间,过期后就会被清除。到期时间通常被称为生存时间(TTL)。
Python 中的缓存
简单缓存实现
让我们考虑一个我们想要缓存的函数func
。一个简单的方法是将我们的函数包装在另一个函数中:cached_func
并使用一个全局字典来保存值。每当叫cached_func
的时候,我们就查字典。如果对于传递的参数存在一个值,我们返回它,否则我们从func
中获取该值,并将它添加到字典中并返回它。
为了简化函数的缓存,我们可以创建一个通用的缓存包装器。
要使用我们的包装器,我们有两个选择。
-
使用简单的函数调用。
-
使用装修工。
使用 functools 中的装饰器
Python 在functools
: cache
和lru_cache
中配备了缓存装饰器。cache
是无界缓存,而lru_cache
是 LRU 缓存。
使用这些装饰器很简单。
lru_cache
接受两个可选参数:
- maxsize=128 : 缓存的最大大小。
- ***typed = False:***函数参数是否被类型化。比如
6
和6.0
是不是一回事。
如果您正在使用方法和类,您可以像使用cache
和lru_cache
一样使用functools
中的cached_property
。
除了我们讨论的以外,还有更多关于缓存的内容。从服务器和 cdn 到 CPU 和 GPU,缓存无处不在。
当处理纯函数时,缓存应该是显而易见的。在 Python 中需要两行代码。然而,还有其他重要的方法可以让你的代码更高效。
使用 pytest fixtures 提高 python 测试的效率
了解 pytest 夹具的基础知识
PC:蓝钻画廊 ( CC BY-SA 3.0 )
如果你是一个测试新手,或者如果你被分配去修复一些测试失败,或者如果你最近实现的一个很棒的新特性崩溃了💥任何现有的测试用例,你都不得不走进软件测试的厄运☠️。
尤其是在使用 pytest 的情况下,您可以看到(👀)许多奇怪的包装器/装饰器位于测试文件中的函数之上。这些包装器中最常见的是@pytest.fixture()
。什么是❓测试夹具
维基说,
一个测试夹具是一个用来持续测试一些项目、设备或软件的环境。在测试电子、软件和物理设备时,可以找到测试夹具。
让我们微调一下我们的问题,什么是计算中的测试夹具???
软件测试夹具通过初始化来建立软件测试过程的系统,从而满足系统可能具有的任何前提条件。
在继续之前,让我先弄清楚我们将在这篇文章中看到什么
- pytest 中的 fixture 是什么?
- 使用固定物有什么好处?
- pytest 夹具的范围有哪些?
奖金🎁,
4.理解夹具范围的示例工作代码。
pytest 中的 fixture 是什么?
为了理解测试夹具,首先,我们必须理解测试实际上是什么。为了简单起见,让我带你看一个简单的例子。
让我们假设你是一个厨艺大师👨🏻🍳你在为一个严肃的美食评论家做饭🚨,你要确保你已经把盐加到了点子上。所以这里唯一要检验的因素就是“盐后味”。为了做这个测试,我们需要四个步骤,
- **安排/设置:**这包括我们在测试阶段之前需要做的所有步骤,在我们的例子中是加盐之前的所有步骤(清洗蔬菜和肉类,切割和切碎它们,准备平底锅,以及加盐之前的所有烹饪步骤)。
- **动作:**这是触发我们想要测试的行为/状态的单一状态改变动作。在我们的例子中,“加盐”是单一动作。
- **断言:**这是我们检查特定行为的结果状态是否与我们预期的相同或不同的阶段,如果与预期的相同,则我们的测试通过,否则失败,在我们的情况下,断言是,加盐后食品测试是否“良好”。
- **清理:**一旦我们做了烹饪和测试味道,我们就需要清理厨房来烹饪另一顿饭,对吗???这正是在这个阶段中所发生的,为这个特定测试创建/设置的测试对象需要被清理,以确保它们不会影响其他测试。
现在回到夹具,是的,我们在安排阶段和数据中所做的任何步骤都被称为夹具。这些是需要测试某个动作的东西。
在 pytest 中,fixture 是我们定义的服务于这些目的的函数,我们可以将这些 fixture 传递给我们的测试函数(测试用例),这样它们就可以运行并设置您执行测试所需的状态。
@pytest.fixture
def db_conn(creds):
# steps to connect to db def test_add_user(db_conn):
user = User(....)
desired_response = "......." response = db_conn.add_user(user)
assert response == desired_response
当 pytest 试图运行测试test_add_user
时,fixture db_conn
将被执行,以建立将用户添加到数据库所需的数据库连接。
我们可以看到更多确凿的例子👨🏻💻在结束之前,让我再回答几个问题。
使用固定物有什么好处?
当然,这可能是你看到上面的例子后想到的问题,让我列出那些是什么,
- 夹具是可重用的,夹具是以模块化的方式实现的,因此每个夹具名称都会触发一个夹具功能。
- 夹具可以使用其他夹具。
- 测试或夹具可以一次请求多个夹具。
- 夹具可以自动使用,有时所有的测试用例都需要夹具,在这种情况下,我们可以使用“自动使用”夹具。
@pytest.fixture(autouse=True)
举个例子。 - 夹具管理从简单的单元测试扩展到复杂的功能测试。我们甚至可以根据配置和组件选项来参数化夹具和测试。
- 无论使用多少夹具,拆卸逻辑都很容易管理。
- 可以为每个 Fixture 设置 Fixture scopes,这将极大地帮助在运行昂贵的测试用例时节省大量时间和计算资源。
在这里,我想讨论更多关于**Fixture Scope**
**,**的问题,让我们跳到下一个问题。
pytest 夹具的范围有哪些?
一些测试可能需要网络活动,如登录到远程服务器或数据库,或者如果您正在为机器学习模型编写测试,您可能必须训练可能由几个测试用例使用的模型,在这种情况下,每次用户请求时运行这些 fixture 函数可能会非常耗时且计算量很大。
在这种情况下,我们可以定义固定物的范围,例如:@pytest.fixture(scope='...')
。作用域仅仅是通过运行 fixture 函数来删除返回的对象。有 5 个不同的范围。
- **功能:**当范围设置为功能时,在请求测试功能终止后,对象将立即被拆除。当另一个测试函数调用该 fixture 时,它将重新运行并创建一个新对象。默认情况下,fixtures 将有一个函数范围。
- module: 当一个函数第一次调用 fixture 时,它将创建对象并保存它以供同一个模块中的测试函数使用。因此,如果同一个模块中的任何测试函数调用 fixture,将返回缓存的对象。该模块中的所有测试完成后,该对象将被拆除。
- **类:**fixture 将被每个类执行一次,它将被同一个测试类的所有测试函数重用。它将在该类的所有测试函数完成后被拆除。
- package: fixture 将在第一次请求时被执行并被缓存,直到该类中的所有测试函数都被执行,之后将被拆除。
- session: 这是 pytest fixture 中的广泛范围,每当我们调用
pytest
时,它就被称为会话。因此,具有会话范围的 fixtures 将在第一次请求时被执行和缓存,它们将被重用,直到所有测试完成。
奖金
哎呀,很多理论📚,让我们深入一些 pytest fixtures 及其范围的例子。
test_dir/conftest.py
conftest.py
是 pytest 的一个特殊文件,您可以在其中添加所有的测试夹具。这些夹具对于同一个测试目录中的所有模块都是可见的。
注意:如果您有多个测试包,您可以为每个包添加一个conftest.py
。这些文件有目录范围。
这里我们声明了三个具有三种不同作用域的 fixtures。
- empty_account: 对于会话范围,每个会话只应执行一次。
- account_20: 对于模块范围,应该每个模块执行一次。
- account_50: 有函数作用域,每次请求时都要执行。
将下面的测试模块和conftest.py
一起添加到你的测试目录中。
测试目录/测试装置 1.py
测试目录/测试装置 2.py
您可以从以下网址获得完整的代码:
https://github.com/Mathanraj-Sharma/python_boilerplate/tree/main/tests/test_fixtures
这些是一些简单的测试用例,用来看看当我们调用具有不同作用域的 fixtures 时会发生什么。运行以下命令来运行测试
python -m pytest --color=yes -s -v --show-capture=no tests/test_dir
PC:作者
如果仔细观察,您会得到类似于下面的输出
- 对空账户夹具整个会话只执行了一次。
- 对于每个模块,account_20 fixture 已经执行了两次。
- account_50 fixture 因为是函数作用域,所以已经按要求执行了多次。
希望这是理解会话、模块和函数范围的好例子。我把类和包的范围留给你的作业,试着为这两个范围实现一个好的例子。
总之,pytest fixtures 是为测试功能安排/设置测试环境所需的功能。测试人员可以定义他们需要的任意多的夹具,并且从不同的测试功能中请求它们任意多的次数。此外,fixture scope 有助于在昂贵的测试中节省时间和资源。
希望这篇文章能给你一个关于测试夹具的好主意。快乐测试🤗!!!