TowardsDataScience 博客中文翻译 2021(七百一十七)

原文:TowardsDataScience Blog

协议:CC BY-NC-SA 4.0

使用 MLFlow 跟踪和版本化机器学习模型

原文:https://towardsdatascience.com/using-mlflow-to-track-and-version-machine-learning-models-efd5daa08df0?source=collection_archive---------11-----------------------

循序渐进的编码实践

当我们调整机器学习模型的参数时,我们可能需要多次训练它,以便选择最佳模型。如果训练次数过多,我们可能会遇到两个问题。

  • 我们如何跟踪每个模型的参数和度量?将它们手动写入 excel 文件?那将是乏味且容易出错的。
  • 我们如何对每个模型进行版本控制?用不同的名字保存到磁盘上?很难记住哪个模型来自什么参数。

MLFlow 正是我们解决这些问题所需要的。它是一个强大的 MLOps 工具,用于 ML 模型跟踪、版本控制、打包等等。在这篇博客中,我们将关注跟踪和版本控制。关于跟踪,MLFlow 可以跟踪参数、度量和模型。关于版本控制,MLFlow 将模型存储在一个模型注册表中,然后用户可以很容易地选择一个特定的版本。

MLFlow 可以在本地运行或从 docker 容器运行,也可以部署到 kubernetes。它有 Python、Java、R 和 REST 的 API。

在这篇博客中,我们将展示如何使用 mlflow 来跟踪和版本化 mnist 分类模型。我们将首先使用 tensorflow 运行一个 MNIST 示例,然后扩展代码以集成 mlflow。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片来自Unsplashis AAC Smith

MNIST 的例子

首先创建一个虚拟环境,pip 安装 tensorflow 和 mlflow。

# my version is 2.6.0
pip install tensorflow
# my version is 4.4.0
pip install tensorflow_datasets
# my version is 1.9.1
pip install mlflow

下面是使用 tensorflow 运行 mnist 分类的代码。

张量流中的 MNIST 分类

运行代码,您应该会看到如下所示的日志。它还将模型保存在模型文件夹中。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

mnist.py 的日志(图片由作者提供)

物流跟踪

现在,让我们跟踪参数、度量以及工件(模型)。见下面的代码,(也见底部的完整代码)。首先,我们需要命名一次跑步。如果我们愿意,我们甚至可以命名一个实验(更高级别的运行)。然后,我们使用函数log_paramlog_metriclog_artifacts来记录参数、度量和工件。

import mlflowwith mlflow.start_run(run_name=run_name):
  mlflow.log_param("batch_size", batch_size)
  mlflow.log_param("learning_rate", learning_rate)
  mlflow.log_param("epochs", epochs)
  mlflow.log_metric("train_loss", train_loss)
  mlflow.log_metric("train_accuracy", train_acc)
  mlflow.log_metric("val_loss", val_loss)
  mlflow.log_metric("val_accuracy", val_acc)
  mlflow.log_artifacts("./model")

运行 mlflow 代码后,我们可以看到磁盘中有一个名为mlruns 的新文件夹。这是本地存储参数、指标和工件的地方。

然后,我们可以做一些参数调整,例如,改变批量大小和学习速率。每次运行都将被记录到 mlflow 中。

现在,让我们在 mlflow 的 UI 上查看所有运行。在虚拟环境中,键入:

mlflow ui

然后,在 http://localhost:5000/ 浏览。我们可以看到所有运行都记录在那里。在一个页面上可以清楚地看到参数、指标和运行名称。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在 mlflow 追踪上运行(图片由作者提供)

如果我们点击一次跑步,我们可以看到关于这次跑步的更多细节。除了参数和度量,左下方的工件部分显示了我们的工件(模型)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

mlflow 运行的详细信息(图片由作者提供)

每次运行的 url 地址具有以下格式。每个实验中的运行 id 都是唯一的。

http://localhost:5000/#/experiments/<experiment id>/runs/<run id>

MLFlow 模型版本化

MLflow Model Registry 是存储和版本化模型的中心位置。有了它,一个模型就有了从(例如)v1,v2,…到 v10 的迭代版本。对于每个模型和版本,我们可以写一个降价描述(例如详细的参数),这样我们以后就知道这个模型代表了什么。此外,我们可以用StagingProductionArchived来标记一个版本。

为了建立模型注册,我们需要一个数据库后端来存储模型,参见这里的获取说明。之后,我们可以将 tensorflow 模型上传到 mlflow 注册表。下面是一个代码示例。

import mlflow.tensorflow
from tensorflow.python.saved_model import signature_constantstag=[tf.saved_model.tag_constants.SERVING]
key=signature_constants.DEFAULT_SERVING_SIGNATURE_DEF_KEYmlflow.tensorflow.log_model(tf_saved_model_dir="./model",
                            tf_meta_graph_tags=tag,
                            tf_signature_def_key=key,
                            artifact_path="model",
                            registered_model_name="mnist")

总之,MLFlow 是一个强大的 MLOps 工具,用于跟踪和版本化机器学习模型。它支持 python、java、R 等语言的 API。通过其漂亮的用户界面,我们可以清楚地跟踪、存储和比较不同的模型版本。MLFlow 已经成为许多 ML 项目中流行的 MLOps 工具。

下面是 tensorflow 中带有 mlflow 跟踪的 mnist 分类的完整代码。

张量流中 MNIST 分类的完整代码

使用现代艺术博物馆的收藏数据集来可视化谁的故事不见了

原文:https://towardsdatascience.com/using-momas-collection-dataset-to-visualize-whose-stories-are-missing-76a8960a33c2?source=collection_archive---------18-----------------------

数据新闻

艺术如何影响我们对边缘群体的参考点

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

上面的橙色代表 MoMA 从单身白人男性艺术家那里获得的作品。图片作者。

在最近的一个班级项目中,我使用 MoMA 在 GitHub 上发布的收藏数据集创建了一个互动装置,揭示了被放大的主导叙事。我对 MoMA 在 1930 年至 2019 年间获得艺术品的 15,222 名艺术家的性别和种族进行了细分。我们如何将现代艺术博物馆的藏品分成几个部分来评论主流的叙事?

保持机构的问责制

2019 年 9 月, MoMA 经过紧张的改造后首次对外开放。目标是:扩大博物馆,把“毕加索和莫奈放在更近的、多样化的艺术家旁边。”这已经成为大多数机构和公司发出的一个普遍的前瞻性信息。多元化和包容性的对话是受欢迎的,但我们很少通过行动积极创造实质性的影响。

研究论文“美国主要博物馆艺术家的多样性”详细介绍了艺术博物馆馆长协会(AAMD)如何发现其成员机构中 72%的工作人员认为自己是白人。他们还揭示了领导职位中的性别差距,60%的博物馆工作人员是女性,但只有 43%的管理职位由女性担任。

艺术机构是系统的一个具体例子,这个系统曾经无情地启用与白人主导文化一致的标准。更具体地说,通过欧洲现代主义的镜头来评价艺术品。现在,恐惧正因其排他性和自命不凡的白盒基调而受到批评。MoMA 试图重新思考我们谈论和评价艺术品的方式。我想创造一种体验,来帮助说明主要的故事和完全缺失的故事。

抓取数据集

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片作者。

  1. **种族:**首先,我使用论文美国主要博物馆中艺术家的多样性(Topez,Chad M 等人)中的数据,可视化了 MoMA 中不同种族艺术家的百分比。
  2. **性别:**我使用 MoMA 的收藏数据,提取了从 1930 年到 2019 年每十年获得的每件艺术品中不同性别艺术家的比例。我用颜色来形象化这些百分比。
  3. **前 10 名女艺术家:**从那以后,我把注意力集中在获得最多艺术品的前 10 名女艺术家身上。这是用一个视觉单词云来表示的,其中名字的大小与获得的艺术品数量相关。

通过体验体现数据

视频演练“谁重要?”安装。

**第一步:在你认识的名字旁边贴上尽可能多的标签。**在每张纸网上散布着在特定的十年里被 MoMA 获得最多艺术品的前 10 位女性艺术家的名字。名字越大,他们的作品就越多。获得的艺术品越多,它们占据的空间就越大。随着时间的推移,来自艺术观众的集体数据输入将揭示艺术家获得的艺术品数量是否影响他们的曝光水平。

第二步:向后拉黑色手柄。纸网形成三维形状。将显示每十年男性、女性、多位艺术家的百分比,以及未找到的性别记录。

**第三步:后退一步,查看整个棋盘。**钉板的周长用每个种族的百分比进行颜色编码。当你退后一步,绕着棋盘走一圈,主导 3D 条形图的颜色和棋盘的周长揭示了哪个群体主导了博物馆的收藏。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

装置的互动性引发了参与者之间的对话。图片作者。

洞察力

  • 随着时间的推移,最知名的女艺术家将会涌现出来。我和家人一起完成了贴纸活动。我们在认出的女艺术家旁边贴了贴纸。黑板上的名字越大(获得的艺术品越多),我们就越有可能知道他们的作品。这也促使人们思考遗漏了哪些名字。
  • **现代艺术博物馆主要从男性和白人艺术家那里获得作品。**自 1930 年至 2019 年,MoMA 从其获得艺术品的大多数艺术家都是男性。近几十年来,女性艺术家略有增加。
  • **绕深蓝色边框(代表白人艺术家)花的时间最长。**黑人和拉丁裔艺术家的作品在纽约现代艺术博物馆的展示明显较少。绝大多数艺术家是白人。
  • 这个装置的互动部分引发了参与者之间的对话。当我向家里的人演示这个装置时,这些活动促使我们反思我们最初的反应。我们互相询问我们是如何知道某个女艺术家的,并讨论了我们对 MoMA 多样性的差距有多惊讶(或不惊讶)。同时反思我们在艺术行业中的角色。

我们的参考点受到缺乏代表性的影响

淡化反亚裔情绪

我们周围的叙事中的代表性水平成为我们如何看待文化和其他边缘化群体的参考点。在 MoMA,可以说是最有影响力的博物馆之一,我们主要看到白人男性艺术家的作品。在我们的家庭、教室和人际关系中,我们周围有哪些观点或经历?

亚特兰大枪击案中八人丧生,其中六人是亚裔女性,这在亚裔美国人社区中造成了巨大的反感。反亚裔仇恨的增加揭示了助长种族主义、仇外心理和厌恶女性攻击的故事有多危险。无论是我们在原始博物馆看到的艺术,还是我们在网上听到的言论,这些都有助于我们与“他人”联系在一起的参考点。

例如,《今日美国》报道称,去年秋天川普被诊断患有新冠肺炎后,反亚裔情绪上升了 85%,ADL 当时发现。从他上任到今天,特朗普一直在困扰人们通过使用极端种族主义术语“中国病毒”来联系亚洲人的参照点。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片作者。

艺术在日益喧闹的世界中的作用

博物馆为故事提供了空间。对于当代艺术来说,对所谓的“艺术”有一种松散的控制。这种控制欢迎抽象和混乱的演绎表演、装置、绘画、混合媒体、雕塑等等。这通常是对文化和社会问题的一种共同的集体理解,它让我们与某些艺术作品产生共鸣。

从我们屏幕上的内容到与他人的对话,都有越来越分裂和政治狂热的背景噪音。我们受益于参与各行各业艺术家的作品,他们的故事与我们的叙述不同。它为自己提供了一个反思和对话的机会。

创建真正多样化和包容性的参考点

尽管我讲述了我的背景和目前的身份,但总会有一层阴霾围绕着我。由种族主义和我的韩裔美国人血统的减少构成的阴霾。有时如此微妙,有时我告诉自己没什么可担心的。对 AAPI 社区来说,这从来就不是蓝天。

当我反思自己作为一名亚洲女性的背景和身份时,我使用的参考点是什么?我的社区在艺术、媒体、领导职位、坦诚对话等方面的表现,助长了许多基于我的种族对我的概括。通过摒弃这些有害的刻板印象,我们可以减轻边缘化群体的非人化。

露丝·阿莎娃变形钢丝雕塑

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

4 件露丝·阿莎娃钢丝雕塑。在大卫·兹沃纳展览上看到的。图片由维基百科作者 Njdancer15 提供。这张图片是在知识共享署名-共享 4.0 国际许可下授权的。

随着我对日裔美国艺术家露丝·阿莎娃的了解越来越多,我就越觉得她的作品与我作为一名韩裔美国人的经历相关。

在 1946 年夏天到达黑山学院之前,浅泽出生在加利福尼亚的诺沃克。她的父母是来自日本的移民,感受到了针对日本人的歧视性法律的压力。他们不能拥有土地,成为美国公民,或者梦想成为卡车农民。由于不利的经济形势和对日本人的持续歧视,浅泽在大萧条时期的成长经历和股票市场崩盘导致她的家庭陷入困境。他们被安置在两个拘留营里。

浅泽的生物形态般的线雕塑提醒我,我们的情感,无论多么强大和微弱,都不必像金属线一样僵硬和令人恐惧。相反,它们可以转变成一个通风的,动态的,令人难忘的美丽的线雕塑,展示给每个人。她的钢丝雕塑赋予抽象和杂乱的形状。

我在她的作品中感受到了一种艺术反叛,这种反叛存在于一个歧视和种族主义盛行的体系中。她追求将每一点小小的快乐都投射到自己的影子里,以此来定义自己的存在,这种追求形成了一种反叛。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

梁杰森Unsplash 上的照片。

“靠自己的力量振作起来”的谬论

这个使用 MoMA 的收藏数据集的装置是我思考变化有多慢的一种方式。我们可以庆祝 BIPOC 社区取得的进步,但人们强烈呼吁对被系统抹去的故事进行问责和深度投资。黑人、亚洲人、拉丁人、西班牙人、土著人和非二元艺术家的观点一直存在。我们如何重新思考用来评价和思考艺术品的类别?作为观众,我们的角色是什么?

我想知道当我的祖父在 80 年代从韩国移民到波特兰时,在一家卡车工厂工作时介绍自己是什么感觉。通过证明他知道卡车的零件,他能够证明他是一个高度熟练的工人,尽管他不会说英语。他并不比那里的任何人差——这一点必须不断得到证明。

在一个行为主义的时代,我们必须后退。你的故事很重要。正如米歇尔·奥巴马在《T4》变成 中写道:“如果你不走出去定义自己,你会很快被别人错误地定义。”分配给边缘化群体的空间越多,我们就越能颂扬和尊重他们的文化和精彩故事。

捐:gofundme.com/f/support-aapi-community-fund
幕:stopaapihate.org/actnow
学:anti-asianviolenceresources.carrd.co
看:youtube.com/watch?v=14WUuya94QE

在 Python 中使用 Mongo 数据库

原文:https://towardsdatascience.com/using-mongo-databases-in-python-e93bc3b6ff5f?source=collection_archive---------1-----------------------

用 PyMongo 介绍 MongoDB

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

(图片来自https://www.pexels.com/)

MongoDB 是一个基于文档的数据库,具有动态数据模式。它支持的 JavaScript Object Notation (JSON)非常适合使用现代编程语言(如 JavaScript、Python 等)中的对象。这为 SQL 等更传统的关系数据库管理系统(RDBMS)提供了一种替代方案。MongoDB 是 NoSQL 数据库的一个例子。这些数据库通常使用文档集合,而不是 RDBMS 中使用的表。这些数据库支持动态数据库模式,使它们能够响应数据结构的变化。

这篇简短的入门文章给出了一些通过使用 pymongo 库将 MongoDB 与 Python 结合使用的例子。本文最后介绍了在现代软件项目中使用 Mongo 数据库的 mongoengine ,以及如何将它们转换成 dataframe 对象以供进一步分析。假设读者能够轻松地下载和设置 MongoDB,并且有一些使用 Python 的基本经验。

结构

文档数据库的结构不同于以行(记录)和列(字段)存储数据的关系数据库。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

显示来自关系数据库的示例股票表的图像(作者提供的图像)

在这种结构中,每一列应该只包含相同类型的数据。例如,我们只希望在库存商品列中看到库存商品数据。任何新数据都需要创建一个新列或新表,然后使用唯一标识符(主键)定义表之间的关系,该唯一标识符在后续表中称为外键。更改此数据的结构,尤其是当它已经包含数据时,会更加复杂,可能需要使用迁移工具。

与此相反,MongoDB 使用键/值对将数据存储为文档集合:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

包含数据的一组文档(图片由作者提供)

与我们必须在表中创建新列来存储信息的关系数据库不同,数据可以被嵌入。这意味着我们只需要存储相关的内容,而不是制造冗余。

入门指南

Pymongo 是 mongoDB 的 Python 驱动程序,允许您使用 Python 与 Mongo 数据库进行交互。您首先需要在您的系统上安装 MongoDB。如果你还没有这样做,你可以在这里阅读如何做:https://docs.mongodb.com/manual/installation/

要使用 pymongo,首先需要安装库,例如在 Python 提示符下使用 pip:

pip install pymongo

接下来,我们需要将 pymongo 库导入到 Python 文件或 Jupyter 笔记本中。

import pymongo

然后连接到 Mongo 客户端。这将连接到默认的主机和端口。

client = pymongo.MongoClient(“mongodb://localhost:27017/”)

然后我们可以创建一个数据库来存储一些数据。在本例中,它将为医疗系统存储一些患者的详细信息。

db = client[“med_data”]

接下来,我们可以向数据库添加一个集合。每个数据库可以包含多个集合。这个集合将被称为 patient_data ,我们将使用变量 my_collection 在 Python 中引用这个集合。

my_collection = db["patient_data"]

插入数据

然后,我们可以将一些数据(文档)添加到集合中。假设我们想存储一个病人的一些基本信息。这可能包括他们的姓名、年龄、生物性别和心率。我们还将存储他们的血压,通常用代表收缩压和舒张压的两个数字来显示,通常以毫米汞柱(mmHg)来测量,例如 156/82。在 MongoDB 中,使用 JavaScript 对象符号将字段(数据项)封装在大括号({})中。每个字段由一个键/值对组成。字段名(键)用引号括起来,后跟冒号和相关值。文本(文本数据)值也用引号括起来,数字(数值数据)则不然。值也可以包含其他对象和数组。数组可以存储数据列表和其他键值对,并用方括号([])表示。在这里,我们可以存储收缩压(sys)和舒张压(dia)的键和值以及数据值。

patient_record = {
   "Name": "Maureen Skinner",
   "Age": 87,
   "Sex": "F",
   "Blood pressure": [{"sys": 156}, {"dia": 82}],
   "Heart rate": 82
}

只需在右括号后添加一个逗号并添加其他对象,就可以添加多个文档。根据需要,不同的对象还可以包含完全不同的数据字段。

一旦我们创建了文档,我们就可以将它添加到集合中。要添加单个文档,我们首先指定要添加的集合,后面跟一个点,然后我们可以使用 insert_one 函数(对于许多文档,我们使用 insert_many )传入文档对象变量:

my_collection.insert_one(patient_record)

为了查看集合的内容,我们可以循环遍历集合中的每一项并打印出来。

for item in my_collection.find():
    print(item)

这将输出如下数据:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

以这种方式查看数据会使阅读变得非常困难,尤其是当您有许多字段和文档要输出时。幸运的是,Python 有一个非常好的打印库来实现这个目的。如果我们修改代码来导入库并使用函数(注意打印中的双“p”):

from pprint import pprintfor item in my_collection.find():
    pprint(item)

您可以看到,它以更易于阅读的格式输出数据:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

注意,MongoDB 会自动添加一个 ObjectId 来惟一地标识每个文档。这是一个 12 字节的十六进制字符串,由时间戳、随机生成的值和递增计数器组成。这些 id 在数据输出时显示。如果需要,您也可以通过为“_id”字段提供您自己的值来覆盖它。

我们可以使用 insert_many 函数一次添加多条记录:

patient_records = [
 {
   "Name": "Adam Blythe",
   "Age": 55,
   "Sex": "M",
   "Blood pressure": [{"sys": 132}, {"dia": 73}],
   "Heart rate": 73
 },
 {
   "Name": "Darren Sanders",
   "Age": 34,
   "Sex": "M",
   "Blood pressure": [{"sys": 120}, {"dia": 70}],
   "Heart rate": 67
 },
 {
   "Name": "Sally-Ann Joyce",
   "Age": 19,
   "Sex": "F",
   "Blood pressure": [{"sys": 121}, {"dia": 72}],
   "Heart rate": 67
 }
]my_collection.insert_many(patient_records)

更新数据

我们可能还想更新以前添加到集合中的数据。同样,我们可以更新单个或多个记录。假设我们不小心为达伦·桑德斯和莎莉·安·乔伊斯添加了相同的心率。达伦的应该是 88 岁。这里,我们可以使用 update_one 函数传递我们想要更新的字段,搜索键/值对“name”和“Darren Sanders ”,然后我们使用$set 选项(前面有一个美元符号)指定键(心率)和新值(88)。这将用新值覆盖初始值。

my_collection.update_one({"Name": "Darren Sanders"}, {"$set":{"Heart rate": 88}})

如你所见,我们可以将多层对象和数组嵌套在一起,从而嵌入数据。另一种选择是在单独的集合中分离出数据并链接到它。我们将着眼于嵌入和链接以及问题,以帮助您确定哪一个是最好的。

嵌入或链接数据

我们可以通过嵌入来嵌套数据。假设我们想要存储某个病人的一些医学测试结果。这可能包括一些血液检查结果和心电图/EKG 图像,用于心脏病发作的一些调查和一些血液检查,包括:

  • 肌酸激酶(CK)
  • 肌钙蛋白 I (TROP)
  • 天冬氨酸氨基转移酶

我们可以从创建一个包含数组的名为“测试结果”的字段开始。

patient_record = {
  "Hospital number": "3432543",
  "Name": "Karen Baker",
  "Age": 45,
  "Sex": "F",
  "Blood pressure": [{"sys": 126}, {"dia": 72}],
  "Heart rate": 78,
  "Test results" : []
}

在这个数组中,我们可以存储 ECG 的对象(图像文件的路径)和存储生化结果的另一个数组。

patient_record = {
  "Hospital number": "3432543",
  "Name": "Karen Baker",
  "Age": 45,
  "Sex": "F",
  "Blood pressure": [{"sys": 126}, {"dia": 72}],
  "Heart rate": 78,
  "Test results" : [
    {
      "ECG": "\scans\ECGs\ecg00023.png"
    },
    {
      "BIOCHEM": []
    }
  ]
}

最后,我们可以将血液结果添加为键/值对:

patient_record = {
  "Hospital number": "3432543",
  "Name": "Karen Baker",
  "Age": 45,
  "Sex": "F",
  "Blood pressure": [{"sys": 126}, {"dia": 72}],
  "Heart rate": 78,
  "Test results" : [
   {
     "ECG": "\scans\ECGs\ecg00023.png"
   },
   {
     "BIOCHEM": [{"AST": 37}, {"CK": 180}, {"TROPT": 0.03}]
   }
  ]
}

我们可以把这些写在同一行上,就像我们写血压一样,或者写在不同的行上,以增加可读性。

以这种方式嵌入数据的替代方法是链接到它。链接数据也称为引用。这包括将数据存储在不同的集合中,并通过 id 引用它。决定是否链接或嵌入数据取决于某些考虑因素,例如:

  • 您需要多久访问一次嵌入的信息?
  • 使用嵌入信息查询数据吗?
  • 嵌入的数据会经常改变吗?
  • 在没有嵌入数据的其他信息的情况下,您需要访问嵌入数据的频率是多少?

根据这些问题的答案,您可能需要链接到数据。考虑下面的例子。您可能希望存储一些关于给定患者开了什么药的信息。您可以嵌入这些信息,但是如果您还想存储更多关于药物的通用信息,该怎么办呢?在这里,您可以有一个单独的集合,其中包含您可以链接到的信息。

medication_data = [
 {
   "_id": ObjectId('60a3e4e5f463204490f70900'),
   "Drug name": "Omeprazole",
   "Type": "Proton pump inhibitor",
   "Oral dose": "20mg once daily",
   "IV dose": "40mg",
   "Net price (GBP)": 4.29
 },
 {
   "_id": ObjectId('60a3e4e5f463204490f70901'),
   "Drug name": "Amitriptyline",
   "Type": "Tricyclic antidepressant",
   "Oral dose": "30–75mg daily",
   "IV dose": "N/A",
   "Net price (GBP)": 1.32
 }
]

我们可以使用 id 和 DBRef 函数来引用另一个集合中的数据。例如:

from bson.dbref import DBRefpatient_records = [
 {
   "Hospital number": "9956734",
   "Name": "Adam Blythe",
   "Age": 55,
   "Sex": "M",
   "Prescribed medications": [
     DBRef("medication_data", "60a3e4e5f463204490f70900"),
     DBRef("medication_data", "60a3e4e5f463204490f70901")
   ]
 },
 {
   "Hospital number": "4543673",
   "Name": "Darren Sanders",
   "Age": 34,
   "Sex": "M",
   "Prescribed medications": [
     DBRef("diagnosis_data", "60a3e4e5f463204490f70901")
   ]
 }
]

查询数据

有几种方法可以查询数据。所有的方法都使用了 find() 函数。可以提供一个查询,后跟您希望在表单中返回的一个或多个字段:

collection.find({ <query> }, { <field(s)> })

要查找单个条目,例如姓名为“Darren Sanders”的患者,我们可以使用查找功能并打印列表中的第一项:

pprint(my_collection.find({"Name": "Darren Sanders"})[0]

我们也可以使用一个循环来输出结果。我们还可以将查询存储在一个单独的变量中,首先将这个变量传递给 find 函数。当查询可能很复杂时,这很有用,因为它有助于提高代码的可读性:

query = {"Name": "Darren Sanders"}doc = my_collection.find(query)
for i in doc:
  pprint(i)

最后,如果我们只想要一个结果,我们可以使用 find_one() 函数:

my_collection.find_one({"Name": "Darren Sanders"})

数据库的常见做法是根据特定标准查询数据子集。我们可以使用比较运算符来检索数据子集。例如,我们可以使用大于运算符($gt)来搜索心率大于 70 次/分钟的所有患者姓名。

for heart_rate in my_collection.find({"Heart rate": {"$gt": 70}}, {"Name"}):
    pprint(heart_rate)

有许多这样的比较运算符可用,包括:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

(图片由作者提供)

使用逻辑运算符可以进一步增强这一功能。例如,我们可以使用心率< 70 beats per minute, and who are aged above 20 years.

result = my_collection.find({
 "$and" : [
     {
         "Heart rate": {"$lte": 70}
     },
     {
         "Age": {"$gt": 20}
     }
   ]
})for pt in result:
    pprint(pt)

Logical operators include:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

(Image by author)

You might be wondering how we find data that’s contained in arrays. This can be done by using a period (dot). For example you may recall that we stored the patients’ systolic and diastolic blood pressure like so:

"Blood pressure": [{"sys": 126}, {"dia": 72}]

We could query patients with a systolic (sys) blood pressure less than 140 mmHG (mm of mercury) like so:

for normal in my_collection.find({"Blood pressure.sys": {"$lt": 140}}):
    pprint(normal)

Note that we reference the key “blood pressure” add a period (dot) and then the key inside the array, for example sys for systolic.

Working with existing data

One of the great things about MongoDB is that it is really straight forward to load JSON files and add them to collections. For example if we had some JSON data stored in a JSON file, we could use the json library to read in this data and add it to a MongoDB collection:

import jsonwith open('data_file.json') as f:
  file_data = json.load(f)

my_collection.insert_many(file_data)

You wouldn’t want to output the entire contents of a database with hundreds of thousands of documents. To view the file and see the structure of the data, you may instead output the first n 文档来搜索患者。例如前 10 个文档。这可以通过使用**限制()**功能来实现。

for item in my_collection.find().limit(10):
    pprint(item)

要检查集合中的文档数量,我们可以像这样使用 count_documents 函数:

my_collection.count_documents({})

同样,我们可以在这里添加一个查询来计算满足某些感兴趣的标准的所有文档。

聚合

通常,当处理数据时,我们不仅仅希望使用查询来提取数据的子集,而是希望从现有数据中产生新的信息。这通常包括进行各种计算,比如求某个值的平均值或总和。例如员工的平均工资。

让我们看一个简单的例子,使用一个包含餐馆数据细节的样本数据集(数据可以在这里找到:https://docs . atlas . MongoDB . com/sample-data/available-sample-datasets/)。

下面是一个示例文档:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

(图片由作者提供)

你可以看到详细的餐厅地址,它是在哪个区,菜肴的类型,名称,身份证和详细的等级授予相关的分数。假设我们想要计算餐馆的平均分数。为此,我们可以使用聚合函数。

result = my_collection.aggregate(
  [
     {"$unwind": "$grades"},
     {"$match”: {}}, 
     {"$group": {"_id": "$name", "Avg grade": {"$avg": "$grades.score"}}}
  ]
)

我们将一个数组传递给聚合函数。 u n w i n d 参数用于解构 g r a d e s 数组,以便为每个元素输出一个文档。接下来,我们使用 unwind 参数用于解构 grades 数组,以便为每个元素输出一个文档。接下来,我们使用 unwind参数用于解构grades数组,以便为每个元素输出一个文档。接下来,我们使用match 参数,包括所有内容(通过使用左括号和右括号)。我们可以通过提供附加标准来进一步过滤。接下来,我们使用 g r o u p 参数对要应用计算的数据进行分组。最后,我们创建一个名为“ A v g g r a d e ”的新关键字,并将 group 参数对要应用计算的数据进行分组。最后,我们创建一个名为“Avg grade”的新关键字,并将 group参数对要应用计算的数据进行分组。最后,我们创建一个名为Avggrade的新关键字,并将avg (average)参数应用于引用分数后跟一个点和分数关键字的分数。

产生以下输出(为简洁起见,将其缩短):

{'Avg grade': 15.2, '_id': 'Red Star Restaurant'}
{'Avg grade': 13.0, '_id': 'Weather Up'}
{'Avg grade': 9.4, '_id': 'La Nueva Playitas'}
{'Avg grade': 13.0, '_id': “Marcella’S Pizzeria & Catering”}
{'Avg grade': 9.0, '_id': 'Hot Wok'}
{'Avg grade': 9.333333333333334, '_id': '99 Favor Taste'}
{'Avg grade': 18.0, '_id': 'Flavors Corner'}
{'Avg grade': 10.666666666666666, '_id': 'Corona Restaurant'}
{'Avg grade': 9.0, '_id': 'Mila Cafe'}
{'Avg grade': 8.0, '_id': 'Circle Line Manhattan'}
{'Avg grade': 15.6, '_id': “The Old Time Vincent’S”}
{'Avg grade': 10.833333333333334, '_id': 'Riko'}
{'Avg grade': 10.0, '_id': 'Fresh Tortillas'}
{'Avg grade': 10.333333333333334, '_id': 'Le Village'}
{'Avg grade': 13.2, '_id': 'Ruay Thai Restaurant'}
{'Avg grade': 12.0, '_id': 'Lechonera Don Pancholo'}
{'Avg grade': 11.0, '_id': 'Pepe Rosso Social'}
. . .

还有许多其他参数可用于常见计算,如 s u m 、 sum、 summin、$max 等。

我们还可以根据需要添加额外的功能。例如,我们可能希望按升序或降序对返回的进行排序。我们可以简单地添加另一行,使用 sort 参数指定根据哪个字段进行排序。1(升序)或-1(降序)。

result = my_collection.aggregate(
  [
      {"$unwind": "$grades"},
      {"$match": {}}, 
      {"$group": {"_id": "$name", "Avg grade": {"$avg": "$grades.score"}}},
      {"$sort": {"Avg grade": -1}}
  ]
)

不使用聚合函数进行排序的另一个选项是使用直接传入字段名的排序函数,例如按名称排序:

for item in my_collection.find().sort("name").limit(10):
    pprint(item)

我们可以通过在要排序的字段后添加 1 或-1 来选择升序/降序:

for item in my_collection.find().sort("name", -1).limit(10):
    pprint(item)

在软件项目和数据科学中使用 MongoDB

使用 JSON 格式的 MongoDB 的主要优势之一是它提供了与使用类似格式的编程语言的互操作性。这使得在应用程序中处理数据和从数据库中存储/检索数据几乎是无缝的。

将数据库集成到代码中的更好的方法是使用对象关系映射(ORM)之类的方法,或者在 MongoDB 中使用对象文档映射器(ODM)。这是通过将 Python(或其他一些语言)代码翻译成 MongoDB 语句来检索数据的。然后,这些数据被传递回 Python 对象。这样做的好处是确保您只需要使用一种语言(例如 Python)来访问和使用数据库。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

(图片由作者提供)

做这件事的一个好库是 mongoengine 。在这里,我们导入库并连接到一个 Mongo 客户端,我们称之为 odm_patients

from mongoengine import *
connect('odm_patients')

下面的例子展示了我们如何创建一个 Python 类来建模一些数据,创建该类的几个实例并将其写入数据库。按照前面的例子,我们将创建一个类来存储关于病人的数据。

class Patient(Document):
    patient_id = StringField(required=True)
    name = StringField()
    age = IntField()
    sex = StringField(max_length=1)
    heart_rate = IntField()

我们可以使用 Python 类创建一个对象来操作数据库。在这里,我们通过指定数据项是什么类型的字段来创建数据项。例如,可以使用 StringField() 函数和带有 IntField() 的整数来创建文本/字符串数据。还可以添加其他参数,例如字符串中的字符数量以及字段是否不能为 null/空。

我们现在可以用 Python 中的标准方式创建这个类的实例。在这里,我们可以创建一对名为 Maxine 和 Hamza 的患者。注意,我们在行尾添加了 save() 函数,将这些数据写入数据库。

maxine_patient = Patient(patient_id = "342453", name = "Maxine Smith", age = 47, sex = "F", heart_rate = 67).save()hamza_patient = Patient(patient_id = "543243", name = "Hamza Khan", age = 22, sex = "M", heart_rate = 73).save()

我们可以使用循环输出这些对象。要访问特定的字段,我们可以使用迭代器,一个点,然后是我们希望输出的字段。例如患者的姓名、id 和年龄。

for patient in Patient.objects:
    print(patient.name, patient.patient_id, patient.age)

它产生以下输出:

Maxine Smith 342453 47
Hamza Khan 543243 22

除了将 Mongo 数据库集成到软件项目中,我们还可以将其用于研究和数据科学/分析任务。有一种简单的方法可以将 Mongo 数据库中的数据转换成表格形式,作为 Panda 的 dataframe 对象。首先,我们导入熊猫库。

import pandas as pd

接下来,我们使用标准查询选择所需的数据,例如,我们将检索 Bronx 区面包店的所有名称。接下来,我们将结果转换成列表数据结构。

extracted_data = my_collection.find({},{"borough": "Bronx", "cuisine": "Bakery", "name": 1})
bronx_bakeries = list(extracted_data)

最后,我们使用 from_dict 函数创建一个数据框,将字典转换成表格数据框:

pd.DataFrame.from_dict(bronx_bakeries)

这会产生以下输出:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

(图片由作者提供)

总之,MongoDB 是一个强大的可伸缩数据库,当数据模式易于频繁更改时,它非常有用。这有助于轻松与现代软件系统集成,并且在分析 JSON 格式的数据(如一些移动应用程序数据或 Twitter 数据)时,也可以用作数据分析管道的一部分。MongoDB 是最受欢迎的 NoSQL 数据库之一,了解它是什么以及它是如何工作的对于软件工程师和数据科学家来说是必须的。

使用 Neo4j 图形数据库分析 Twitter 数据

原文:https://towardsdatascience.com/using-neo4j-graph-database-to-analyze-twitter-data-6e3d38042af1?source=collection_archive---------10-----------------------

收集 Twitter 数据,将其存储在图形数据库中,并使用图形算法进行分析。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片作者。Neo4j 浏览器生成的图形。推特数据收集于 2021 年 2 月 19 日。地点是泰国。黄色圆圈代表一条推文。靠近中心的红色圆圈代表发布推文的用户。该用户获得最高的中心性分数。蓝色圆圈代表转发。蓝色圆圈周围的红色圆圈代表发布转发的用户。

我们将开发一个应用程序来收集 Twitter 数据,并将它们存储在 Neo4j 图形数据库中。然后,我们将使用一些图形查询、中心性算法和社区检测算法来分析数据。在本文中,我们将讨论:

  • Neo4j 图形数据库
  • Twitter 数据建模
  • 收集 Twitter 数据
  • 分析数据

Neo4j 图形数据库

图形数据库是 NoSQL 数据存储技术之一。有一些图形数据库实现。在本文中,我们将使用 Neo4j 图形数据库。以下是它的优点:

  • Neo4j 社区版是开源和免费的。
  • 易于安装和使用。
  • 数据库驱动程序有几种语言版本,包括 Java 和 Python。

如果我们的读者对 Neo4j 图形数据库没有任何经验,我们建议从这里下载 Neo4j 桌面。然后,看一些教程。安装之后,为 Twitter 数据添加一个本地数据库。

Twitter 数据建模

在这一部分,我们将讨论如何在图形数据库中建模 Twitter 对象。一般规则是:

  • 一个对象可以是一个节点。对象类型或类将是一个标签。
  • 对象关联是一种关系。关联类型将是关系类型。

我们感兴趣的 Twitter 对象将是图形数据库中的节点。这些是标签:

  • 自录音再现装置发出的高音
  • 转发
  • 用户
  • 标签

这些对象之间的关系将是图形数据库关系。这些是关系:

  • POST 代表用户发布推文。
  • RT 表示用户发布了一条转发消息。
  • 标签代表用户用标签来标记推文。
  • REPLY 代表对另一条推文的回复。
  • 引用是指引用另一条推文的推文。
  • 提及表示提及用户的推文。

下图显示了节点标签和关系类型。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片作者。它是由下面的 cypher 命令生成的。

我们已经使用 Neo4j 浏览器通过使用以下命令来生成图片:

Neo4j cypher 命令生成上面的模式图像

我们还需要在一些节点标签上创建约束,以确保我们不会创建重复的对象。每个 Twitter 对象都有一个唯一的 id。以下是命令:

Neo4j cypher 命令在节点标签上创建约束

收集 Twitter 数据

在开始分析之前,我们需要收集数据。我们将构建一个 Java 应用程序来检索推文、转发、用户、标签和相关数据。所有这些对象都是相关的。使用图模型对它们建模是很自然的。然后,我们将它们存储在图形数据库中。

TweetsSavior 类

下面的代码显示了 TweetsSavior 类。

“main”方法从命令行参数中获取参数。变量名是不言自明的。然后,创建 TweetData 对象。实例化流处理程序对象。最后,调用流处理程序对象的 run 方法。

TweetData 接口

这个接口是应用程序和存储 Twitter 对象的数据库之间的抽象层。有了这一层,将来我们可以很容易地用一种类型的数据库替换另一种类型的数据库。比如用 neo4j 数据库替换 SQL 数据库。这个接口有一个重要的方法。saveTweet 将处理并保存 Tweet 和相关对象到底层数据库。

TweetDataFactory 类

我们已经使用简单的工厂类创建了一个实现 TweetData 接口的对象。在这种情况下,工厂将实例化一个 TweetGraphData 类的对象。

TweetGraphData 类

以下代码显示了 TweetGraphData 类。它是 TweetData 接口的一个实现,将数据存储在一个图形数据库中。

saveTweet 方法创建一个可调用的任务,并将其提交给 executor 服务。当许多推文在短时间内到来时,利用 executor 服务是有益的。executor 服务将为我们管理线程和任务队列。

TweetGraphTask 类

这是一个可调用的任务,执行处理和保存 tweet 和相关对象的实际工作。代码依赖于:

  • 不管这条微博是不是转发。
  • 这条推文是否是对另一条推文的回复。
  • 不管它是否引用了另一条推文。
  • 这条推文是否有标签,或者是否提到了其他用户。

代码很长,但是很简单。它使用一些 Neo4j Cypher 查询来创建或更新图形节点。代码可以在这里找到。

StreamHandler 类

下面的代码显示了收集 tweet 数据的 StreamHandler 类。

我们有 run 方法作为我们的切入点,并且有主要的逻辑。它执行以下操作:

  • 创建一个 Twitter 流对象。
  • 当新的 tweet 到达时,它将调用 Twitter 流对象的 onStatus 方法。
  • 我们的 onStatus 调用“Tweet data*”*对象的 saveTweet 方法,并传递 tweet 对象。
  • 在 while 循环中,它每 15 分钟获取一次当前的 Twitter 趋势。然后,它使用 Twitter 趋势作为我们希望从 Twitter 流中获得的过滤关键字。

回顾一下这个应用程序,下面的序列图显示了我们的关键类之间的交互。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片作者。序列图是由 Certiv Analytics 在 eclipse 上的序列插件生成的

我们把地点定在泰国,2021 年 2 月 19 日运行程序,时间是早上 8 点到第二天早上 8 点。我们得到了 159,276 条推文,3,078,281 条转发,11,047 个标签和 403,991 个用户。注意,这些并不是所有的用户,而是当天参与活动的用户。我们的读者须知,那天有一项对政府的不信任动议。Twitter 上的活动可能会比平时多一点。
所以,我们收集了大量的 Twitter 数据。让我们看看我们能用它做什么。

分析数据

我们使用 Cypher 语言查询 Neo4j 图形数据库。就像我们用 SQL 语言查询关系数据库一样。Cypher 只是一种不同的语言,因为图形数据库有不同的体系结构。我们的读者可以在这里查阅 Cypher 语言文档

我们将从使用一些 Cypher 查询来分析数据开始。然后,我们将对我们的数据应用图中心算法。它应该能够识别网络中心的用户。最后,我们将尝试一种图社区检测算法。它根据用户的交互将用户分成不同的组。

追随者计数

我们想知道的一件重要事情是谁是有影响力的用户。这很简单,因为我们已经有了这样的属性。每个用户节点都有一些属性。“关注者计数”告诉用户有多少关注者。我们使用下面的查询来查找按关注者数量排序的前 10 名用户。下面显示了 Cypher 查询和结果。

让我们稍微讨论一下 Cypher 查询。

  • 第一行是匹配子句。这意味着我们需要带有用户标签的节点。
  • 第二行是 where 子句。这意味着我们需要具有特定属性的节点。
  • 第三行是 where 子句的一部分。这意味着我们只需要关注者少于 3,700,000 的用户,因为关注者更多的用户可能是非本地用户。
  • 第四行是 return 子句。它告诉我们需要什么属性作为回报。
  • 最后一行告诉数据库按照追随者计数降序排列结果,并只返回特定数量的行。

因此,我们在数据库中找出最有影响力的用户。这些用户拥有数百万粉丝。

接下来我们想知道的是关注者计数的分布。我们认为很少用户有很多追随者。大多数用户只有几个追随者。让我们看看是真是假。因此,我们将绘制追随者计数直方图。我们将使用 JFreechart 库编写一个 Java 代码来绘制图表。Java 源文件如下:

这是结果直方图:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

在图表中,我们将最大关注人数限制为 400,000。此外,我们需要在频率轴上使用对数标度,以使图形看起来更好。否则,我们将只能看到第一个条形。第一个箱的频率比其余箱的频率大得多。(第一个箱频率是 400,577。第二箱频率是 1,207。)

从图表来看,很明显大多数用户只有几个追随者。很少有人有很多追随者。

接下来,我们将计算追随者计数的百分位数。在我们继续之前,我们需要安装 APOC 库。详情请看这篇文章

密码查询和结果如下。

以下是百分位值:

  • 50%的用户关注者少于 60 人。
  • 75%的用户拥有不到 200 名关注者。
  • 90%的用户拥有不到 536 个关注者。
  • 99%的用户拥有不到 6,222 名关注者。

有趣的是,平均关注人数是 1374 人。然而,90%的人只有不到 536 个追随者。平均值没有意义,因为跟随者计数分布不是正态分布。它严重倾斜。

热门标签

接下来,我们将确定热门标签。我们使用下面的查询来完成。

这个查询比前一个稍微复杂一点。

  • 第一行是匹配模式。这里,我们希望匹配标签、推文或转发以及用户。
  • 第二行是 where 子句。这意味着模式中的变量 t 要么是 tweet,要么是 retweet。
  • 第三条和第四条是退货条款。它告诉数据库返回 hashtag 文本、tweet 和 retweet 计数、用户计数和每个用户的 tweet 计数。
  • 在最后一行,我们按照 tweet 计数降序排列结果。然后,取特定数量的顶行。

接下来,我们将制作热门标签的图表。我们将使用 Cypher query 和一些 Java 代码来实现它。这里的代码是。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

从结果中,我们注意到以下情况:

  • 热门标签是政治、流行歌手/偶像或电视节目。
  • 我们有每个用户的推文。它显示了用户平均产生多少条推文和转发。我们注意到,对于一些标签,每个用户的 tweets 很高。看起来这些用户做得很好,让他们的标签在 Twitter 上流行起来。
  • 从我们的数据来看,每个用户发布超过十条推文的标签是流行歌手/偶像。

按时间推文量

这种分析是为了观察推文量在一天中的变化。高峰期是什么时候?对于每个 Tweet 或 Retweet 节点,都有一个存储创建日期时间的 createdAt 属性。我们需要将其转换为 Cypher 可以理解的类型,然后将其分解为年、月、日和小时。然后,计算每小时的推文和转发次数。
下面是要做的查询任务。

  • 第一行是匹配节点。
  • 第二行是 WHERE 子句,指定每个节点必须是 tweet 或 retweet。
  • 第三行是 WITH 子句。我们调整时区,然后将时间戳转换为日期时间类型。
  • 第四行是另一个 WITH 子句。它将日期时间值分解为年、月、日和小时。
  • 第 5 行和第 6 行计算 tweet 和 retweet 节点。我们使用 case-when 语句来区分 tweets 和 retweets。
  • 第 7 和第 8 个是 WHERE 子句。它只包括特定年份、月份、日期范围和时间的推文或转发。
  • 第 9 行是退货条款。它返回年、月、日、小时、tweet 计数和 retweet 计数。
  • 最后一行是排序结果。

我们使用查询结果来绘制 tweet 音量。这里是代码。图表如下。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

以下是我们的观察结果:

  • 正如所料,转发量远远高于推文量。
  • 转发量在 13:00–23:00 左右达到峰值并趋于平缓。
  • 推文音量在 18:00-22:00 左右达到峰值并趋于平缓。
  • 午夜后交易量减少,6 点左右又回升。

安装图形数据科学库

我们稍后将使用的中心性和社区检测算法是图形数据科学库的一部分。我们需要将它安装在图形数据库中。详情请看这篇文章

用户投影

在我们继续之前,我们需要建立一个用户预测。这是一个内存中的图表,显示了一个用户如何连接到另一个用户。图形算法稍后会在上面运行。

我们确定了三种类型的用户连接:

  • 用户转发、引用或回复推文。另一个用户发布了这条推文。
  • 用户发布转发、引用或回复推文。那条推文提到了另一个用户。
  • 一个用户发布了一条提到另一个用户的推文。

以下是创建投影图的命令。

  • 第一行是对图形数据科学库中的过程的调用。
  • 第二行是图形投影名称。我们稍后会提到它。
  • 第 3 行指定了要包含在投影中的节点。我们取所有用户节点。
  • 第 4-11 行定义了图表中包含的关系。有三种结合在一起的关系类型。
  • 最后一行是选项。

中心

我们将使用中心算法来识别位于网络中心的用户。有一些算法可用。我们决定使用 PageRank 算法。该算法根据传入关系的数量来衡量每个节点的重要性。它还考虑了源节点的重要性。
我们使用下面的命令来运行 PageRank 算法。

  • 第一行是对图形数据科学库中算法的调用。它还指定了算法将运行的投影。这是我们之前创建的投影。
  • 第二行存储从算法到变量的输出。
  • 第三和第四个指定返回值。
  • 第 5 个告诉数据库按照 PageRank 分数对结果进行排序,只返回前 10 个用户。

根据中心性结果,以下是我们的观察结果:

  • 和热门标签结果一样,最高分属于政治人物、新闻机构和流行偶像。
  • 最高 PageRank 分数的用户是 MP。他在当天的不信任案中进行了辩论。
  • 名单中十个有七个来自反对党。
  • 列表中的用户具有较高的追随者计数。但没那么高。一个拥有不到 10k 的用户仍然进入了我们的十大名单。所以,要看当天的活动。

社区检测

社区检测算法评估节点是如何聚集或划分的。这个库有一些算法可以使用。我们决定使用卢万算法。下面是 Cypher 查询和结果。

  • 第一行是对库中 Louvain 算法的调用。投影名称是一个参数。
  • 第二行说明了输出、节点 id 和社区 id。
  • 第三行使用 WITH 子句计算社区大小并将结果存储在变量中。
  • 第四行是 WHERE 子句。我们只想要拥有超过 1000 名用户的社区。
  • 第五行是返回子句。我们返回社区 id 和社区大小。注意,社区 id 只是一个数字,没有任何意义。
  • 在最后一行,我们按照大小降序排列社区。

因此,我们确定了几十个社区,但我们对每个社区一无所知。我们可以做进一步的分析,看看每个社区都在发什么话题。下面的 Cypher 查询可以做到这一点。

  • 第 1-5 行调用 Louvain 算法。它做的事情与前面的查询相同。
  • 第 6-9 行是查询的另一部分。它会识别每个社区的标签或被提及的用户。
  • 第 10-13 个提取标签或屏幕名称
  • 第 14 列指定了我们想要的结果列。我们也只返回每个社区的前 5 个标签或提到的昵称。
  • 在最后一行,我们按照大小降序排列社区。

让我们讨论一下结果。

  • 最大的社区拥有 230,000+用户。其中三家拥有超过 10,000 名用户。
  • 每个社区讨论政治、流行偶像或电视节目。
  • 政治是那天的热门话题。一些社区的用户除了偶像标签之外还讨论政治。

图形数据库的利弊

在使用 Neo4j 图表数据库进行 Twitter 分析后,我们发现了这些优点和缺点。

优点:

  • Cypher query 比 SQL query 可读性更强、更紧凑,尤其是在有关系的情况下。
  • Neo4j 图形数据库有一些图形算法可供使用。

缺点:

  • Neo4j 数据库是一个比较新的产品。可用的工具不多。

从性能上来说,已经够快了。凭借 300 多万个节点和 600 多万个关系,我们可以在我们老化的 MacBook Pro 2014 年中期模型上在几秒钟内运行大多数查询。

结论

在本文中,我们讨论开发一个 Java 应用程序来收集 Twitter 数据。然后,存储到 Neo4j 图形数据库中。我们以几种不同的方式分析数据:

  • 我们用追随者数量的最高值来识别用户。绘制跟踪计数直方图。
  • 我们确定热门标签。
  • 我们想象推特的流量在一天中是如何变化的。
  • 我们找出位于网络中心的顶级用户。
  • 最后,我们根据用户的活动将他们划分到社区中。

这是我们分析数据的几个样本。我们可以做得更多。在图形数据科学库中,我们还没有研究的其他算法是可用的。我们希望我们的读者了解我们可以用图形数据库做什么。

用神经网络求解常微分方程

原文:https://towardsdatascience.com/using-neural-networks-to-solve-ordinary-differential-equations-a7806de99cdd?source=collection_archive---------4-----------------------

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

约翰·莫塞斯·鲍恩在 Unsplash 上拍摄的照片

如何使用神经网络解决常微分方程的快速指南(包括张量流实现)

使用神经网络解决常微分方程的想法首先由 Lagaris 等人提出。其背后的想法基本上是训练神经网络来满足微分方程所需的条件。换句话说,我们需要找到一个导数满足 ODE 条件的函数。本文将介绍这个概念的数学基础,然后我们将使用 TensorFlow 实现它。所有的内容都可以在谷歌协作笔记本这里中找到。

数学基础

假设我们有一个颂歌系统,由下式给出:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

因此,我们可以将微分运算理解为具有已知初始条件 u (0)= u 0 的域 t 上的函数。众所周知,神经网络被称为通用逼近器。我们将利用神经网络的这一特性,用它们来逼近给定常微分方程的解:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

同样,我们可能同意 NN ( t )的导数会给我们一个类似的等式:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

所以,如果 NN ( t )真的接近于真解,那么我们可以说它的导数也接近于真解的导数,即:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

因此,我们可以把这个条件转化为我们的损失函数。我们有给定的导数函数 f ( ut ),我们可以计算每一步的神经网络导数NN’(t)。这激发了下面的损失函数(这是两个值的均方误差):

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

你可能还记得初始条件,我们仍然需要处理它。最直接的方法是在成本函数中加入一个初始条件项。它看起来像这样:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

虽然这可行,但可能不是最好的方法。我们都知道损失函数对神经网络训练的至关重要性,我们也知道这个函数的项数将直接影响我们训练的稳定性。损失函数中的更多项(通常)意味着不稳定的训练。为了避免这种情况,我们可以更有效地将初始条件编码到损失中。让我们定义一个新函数并使用它,而不是直接使用神经网络:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

很容易看出, g ( t )将总是满足初始条件,因为 g (0)将导致 tNN ( t )=0,只留下表达式上的初始条件。这样就可以训练 g ( t )来满足 ODE 系统而不是神经网络。然后,它会自动成为导函数的一个解。我们可以将这个新的想法融入我们的损失函数:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Python 实现

我们将使用 TensorFlow 库在 python 中实现所描述的方法。为了更好地理解该方法,我们将使用一个低层次的设计,避免库提供的许多可能的优化。目前,我们的重点是清楚地理解和实现 ODE-solver 神经网络。为此,我们也将选择一首简单的颂歌:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们可以通过整合解决方案的双方来轻松解决这个问题,导致u+C=x+C,在拟合 C 以满足初始条件后,我们有 u = x +1。然而,让我们尝试使用神经网络来解决它,而不是解析地解决它。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

对于此示例,我们将创建一个具有两个隐藏层、sigmoid 激活函数和梯度下降优化算法的 MLP 神经网络。也可以使用其他拓扑,这只是一个例子。我们鼓励你尝试这种方法的不同途径。

定义变量

f0 = 1
inf_s = np.sqrt(np.finfo(np.float32).eps)
learning_rate = 0.01
training_steps = 5000
batch_size = 100
display_step = 500# Network Parameters
n_input = 1     # input layer number of neurons
n_hidden_1 = 32 # 1st layer number of neurons
n_hidden_2 = 32 # 2nd layer number of neurons
n_output = 1    # output layer number of neuronsweights = {
'h1': tf.Variable(tf.random.normal([n_input, n_hidden_1])),
'h2': tf.Variable(tf.random.normal([n_hidden_1, n_hidden_2])),
'out': tf.Variable(tf.random.normal([n_hidden_2, n_output]))
}biases = {
'b1': tf.Variable(tf.random.normal([n_hidden_1])),
'b2': tf.Variable(tf.random.normal([n_hidden_2])),
'out': tf.Variable(tf.random.normal([n_output]))
}# Stochastic gradient descent optimizer.
optimizer = tf.optimizers.SGD(learning_rate)

定义模型和损失函数

# Create model
def multilayer_perceptron(x):
  x = np.array([[[x]]],  dtype='float32')
  layer_1 = tf.add(tf.matmul(x, weights['h1']), biases['b1'])
  layer_1 = tf.nn.sigmoid(layer_1)
  layer_2 = tf.add(tf.matmul(layer_1, weights['h2']), biases['b2'])
  layer_2 = tf.nn.sigmoid(layer_2)
  output = tf.matmul(layer_2, weights['out']) + biases['out']
  return output# Universal Approximator
def g(x):
  return x * multilayer_perceptron(x) + f0# Given EDO
def f(x):
  return 2*x# Custom loss function to approximate the derivatives
def custom_loss():
  summation = []
  for x in np.linspace(-1,1,10):
    dNN = (g(x+inf_s)-g(x))/inf_s
    summation.append((dNN - f(x))**2)
  return tf.sqrt(tf.reduce_mean(tf.abs(summation)))

注意,我们在损失函数中没有任何参数。通常,损失函数会将预测值与实际数据进行比较。在我们的例子中,我们不需要数据点

因为我们知道支配这个模型的 ODE 函数,所以我们可以计算每个点 x 的期望值。您可能还注意到,我们总是使用区间 {-1,1}中的 10 个点来计算损失。对于所有可能的函数来说,这可能还不够,但是考虑到我们例子的简单性,我想我们会做得很好。

神经网络导数由代码中的变量 dNN 表示。我们只是使用差异化定义:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

列车功能

def train_step():
  with tf.GradientTape() as tape:
    loss = custom_loss()
  trainable_variables=list(weights.values())+list(biases.values())
  gradients = tape.gradient(loss, trainable_variables)
  optimizer.apply_gradients(zip(gradients, trainable_variables))# Training the Model:
for i in range(training_steps):
  train_step()
  if i % display_step == 0:
    print("loss: %f " % (custom_loss()))

标绘结果

# True Solution (found analitically)
def true_solution(x):
  return x**2 + 1X = np.linspace(0, 1, 100)
result = []
for i in X:
  result.append(g(i).numpy()[0][0][0])
S = true_solution(X)plt.plot(X, result)
plt.plot(X, S)
plt.show()

这将给我们带来以下情节:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

这是一个非常近似的结果,如果我们还记得没有用于训练的数据集的话。如果我们使用更多的搭配点来计算损失函数,或者如果我们保持训练时间长一点,可能会更好。我们邀请读者尝试这些代码并进行这些调整,甚至尝试其他代码。你可以在谷歌协作笔记本这里找到这篇文章的所有内容。玩得开心!谢谢你的阅读。

为数据科学使用新的吉拉工作管理项目

原文:https://towardsdatascience.com/using-new-jira-work-management-projects-for-data-science-1a75d148a885?source=collection_archive---------36-----------------------

吉拉简单的新功能如何支持数据分析团队

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Unsplash空中聚焦拍摄的照片

吉拉工作管理是专为营销,人力资源,财务和其他业务团队。因此,您可以根据自己的需求与其他人一起工作和协作。我真正喜欢这个新功能的是简单的设计,以及以前没有的日历、列表和时间线等工具现在也可以使用了。如果你没有账户,你可以在吉拉云中创建一个用于测试的账户,然后和最多十个同事一起试用。

日历

特别是对于 bug 修复或上线约会,日历视图非常方便。每个人都可以很容易地跟踪重要的待办事项并安排它们。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

吉拉日历—作者图片

时间表/路线图

类似于吉拉的新一代项目,你也可以按照时间表或路线图来规划任务。我已经发现这个功能非常有用,例如在管理演示中显示未来几天、几周和几个月要做的事情。而且对于自己粗略规划的项目来说,这个功能也是极有帮助的。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

为您的任务绘制时间线—按作者排序的图像

形式

使用新的用于构建块的拖放式表单,在几秒钟内为任何项目创建有用的表单[1]。例如,在这里,我将它用于客户仪表板请求。这样,内部和外部数据用户可以提交想法和请求。通过指定表单,您已经可以尝试在这里建立定义良好的用户故事。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

仪表板表单—按作者分类的图像

其他有价值的功能

我还发现列表视图很有用。在这里,您可以看到任何和所有任务及其各自的优先级。不幸的是,清单在这里不是内置的,但是我发现了有用的附加软件智能清单,用它你可以,例如,代表验收标准【2】。像报告这样的常用功能,例如评估 sprints,仍然可用。

摘要

在我看来,吉拉新的工作管理功能对于快速轻松地绘制数据分析项目非常有用。特别是,如果我想避免项目管理的开销,而是想在小团队中工作。除了项目之外,这些功能对于产品的进一步开发也非常有用,例如,通过表单,客户可以提交请求和想法。我最喜欢的功能是时间线/路线图视图,我喜欢用它来进行管理演示。

GIPHY 的 GIF 图

资料来源和进一步阅读

[1]吉拉,工作管理 (2021)

[2] Atlassian Marketplace,适用于吉拉的智能清单。企业 (2021)

使用自然语言处理识别药物不良事件

原文:https://towardsdatascience.com/using-nlp-to-identify-adverse-drug-events-ades-7a0194f1966a?source=collection_archive---------16-----------------------

制药和医疗保健公司如何利用 NLP 自动检测非结构化文本中的 ade

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

莎伦·麦卡琴在 Unsplash 上的照片

这是怎么回事?

药物不良事件 (ADE)定义为患者因接触药物而遭受的伤害。大量关于药物相关安全性问题(如不良反应)的信息发布在医学病例报告中,由于其非结构化的性质,这些信息通常只能由人类读者来研究。

在本教程中,我们将训练一个自然语言处理(NLP)模型来识别给定文本中的 ade。我们将使用来自拥抱面部数据集中枢的 ADE 数据集来教导模型 ADE 相关和非 ADE 相关文本之间的区别。我们将使用拥抱脸和亚马逊 SageMaker 来训练和部署模型,并用我们自己的短语进行测试。

为什么这很重要?

根据疾病预防和健康促进办公室的数据,ade 具有严重的影响:在住院环境中,ade 约占所有医院不良事件的三分之一,每年影响约 200 万次住院,并延长住院时间 1.7 至 4.6 天。在门诊环境中,ade 包括超过 350 万次医生诊室就诊,估计有 100 万次急诊就诊,以及大约 125,000 次住院。

能够使用人工智能模型自动标记非结构化文本数据中的 ADE 相关短语有助于预防 ADE,并带来更安全、更高质量的医疗保健服务、更低的医疗保健成本、更知情和更积极的消费者以及更好的健康结果。

数据

对于本教程,我们将使用 ADE 语料库 V2 数据集。该数据集于 2021 年 5 月发表在拥抱脸网站上,并公开发布。它由 3 个子集组成,(1)ADE 分类语料库,(2)药物 ADE 关系语料库,和(3)药物剂量关系语料库。对于本教程,我们将专注于分类子集,它根据句子是否与 ADE 相关(标签=1)或不相关(标签=0)对句子进行分类。数据集看起来是这样的:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

行动(或活动、袭击)计划

训练 NLP 模型以识别 ADE 相关短语需要几个步骤:首先,我们需要下载并处理数据集,这导致单独的训练、验证和测试数据集。然后,我们用训练和验证数据集训练该模型。之后,我们将它部署到一个端点,在那里我们可以测试模型。下图说明了步骤的顺序:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

本教程的所有代码都可以在这个 Github repo 中找到。repo 被分成 4 个笔记本,它们反映了上面概述的步骤:1_data_prep.ipynb(数据处理),2_inspect_data_optional.ipynb(这使我们可以在训练之前查看已处理的数据),3_train.ipynb(模型训练),4_deploy.ipynb(模型部署和测试)。

处理数据

为了处理数据,我们将利用亚马逊 SageMaker 上的拥抱面部处理工作。这允许我们启动一个执行处理脚本的计算实例。工作完成后,计算实例将自动关闭,我们只需支付处理数据的时间。

让我们看看处理脚本的一些关键部分:

处理脚本做一些事情:下载数据,删除重复数据,重组和分割数据,标记数据,并将其保存到 S3。

洗牌和分割数据

有许多方法可以加载数据集、删除重复项并将其分成几个部分。在这个脚本中,我们利用 PandasNumpy 库来完成这个任务,但是数据集类也有方法来过滤分割数据集。

将数据集混洗并分成三部分的一种方法是使用 Numpy 的 split()函数。它允许我们在定义的阈值分割数据集。例如,要将一个数据集混洗并拆分成 70%的训练数据、20%的验证数据和 10%的测试数据,我们可以使用这行代码:

将数据符号化

为了使数据集为模型训练做好准备,我们将在将数据集保存到 S3 之前对其进行标记化:

训练模型

在 SageMaker 上训练拥抱脸模型很简单,这要归功于拥抱脸和 AWS 之间的合作关系。Github repo 中的培训笔记本遵循了如何培训 NLP 模型的最佳实践,并使用 SageMaker 培训作业来旋转短暂的计算实例以培训模型。这些计算实例仅用于训练模型,并在训练完成后立即拆除。该模型保存在 S3 上,可以从那里下载并在任何地方托管该模型:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

部署模型

在本教程中,我们将通过获取我们刚刚训练的模型并像这样将其部署在 S3 上来简化我们的生活(参见 Github repo 中的部署笔记本):

这将创建一个 SageMaker 端点,然后我们可以通过 SDK 与之交互。这使得我们可以很容易地测试和摆弄这个模型。

测试模型

一旦部署了模型,我们就可以用测试数据(或一些编造的句子)来测试它:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

结论

在本教程中,我们训练了一个 NLP 模型来识别给定短语中的药物不良事件(ade)。我们使用一个带注释的语料库来训练 NLP 模型,并将其部署到 SageMaker 端点,该语料库旨在支持从医学病例报告中提取有关药物相关不良反应的信息。接下来的步骤可能包括使用专用评估脚本进行自动模型评估,并在 Hugging Face 的模型中心发布模型。

引用

本教程使用的数据集是本文中描述的工作的产物:

Gurulingappa 等人,《支持药物副作用信息提取的基准语料库》,JBI,2012 年。
http://www . science direct . com/science/article/pii/s 1532046412000615,DOI:https://doi.org/10.1016/j.jbi.2012.04.008

利用 OpenAI 的 CLIP 搜索外观设计专利

原文:https://towardsdatascience.com/using-openais-clip-to-search-for-design-patents-7fcc63d91033?source=collection_archive---------32-----------------------

使用自然语言搜索和查找自 2002 年以来进入公共领域的六万多项工业设计

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

过期的美国外观设计专利样本,来源:美国专利商标局,图片由作者提供

每个人都知道利用专利来保护新发明。除非你是专利律师,否则你可能只知道三种主要类型中的一种,即实用专利。然而,还有另外两种类型,设计工厂专利。目前,已授权的实用专利超过 1000 万项,设计专利 91 万项,植物专利近 3.2 万项。

如果你猜测植物专利是针对植物新品种的,那你就对了!但是什么是外观设计专利呢?这是美国专利商标局的定义:

专利法规定授予任何人为制造品发明任何新的和非显而易见的装饰性设计的人外观设计专利。外观设计专利只保护物品的外观,而不保护其结构或功能特征。授予外观设计专利的相关程序与授予其他专利的相关程序相同,但有一些不同……”—USPTO[1]

实用专利和外观设计专利的区别之一是期限,即所有者对专利项目拥有垄断权的时间。目前,实用专利的默认期限是自专利申请之日起 20 年。外观设计专利的默认期限是自专利被授予之日起 15 年。一旦期限届满,或者如果所有者提前放弃专利,作品就进入了公共领域。到那时,任何人都可以使用该设计,而无需获得过期专利所有者的许可。在 91 万项外观设计专利中,超过一半属于公共领域,可以免费使用。

概观

在本文中,我将向您展示如何使用 OpenAI 的新剪辑编码器对自 2002 年以来一直处于公共领域的超过 67,000 项设计专利执行基于图像和文本的语义搜索。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图表

该系统基于 CLIP,这是 OpenAI 的一个开源模型,可以对文本和图像进行语义搜索。该模型有两个功能,将文本和图像编码成“嵌入”,即代表原始数据要点的数字串。剪辑模型在 4000 万对带有文本标签的图像上进行预训练[2],使得从图像编码的嵌入将类似于从文本标签编码的嵌入。

我编写了一个 Python 程序,从 USPTO 下载五年的每周专利公报,并解析文件以提取设计专利名称和相应的图像。然后,我分别通过剪辑文本和图像编码器运行所有的标题和图像,并将结果保存为两组嵌入。

要执行搜索,首先要输入一个简短的查询。该系统通过剪辑文本编码器运行查询,并比较结果嵌入,以使用外观设计专利的文本和图像嵌入来找到最佳匹配。系统显示从索引数组中获得的顶部结果的相应标题和图像。

例如,如果你搜索“折叠蒲团沙发”,下面是前五个结果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

基于片段的语义搜索结果,来源:USPTO

在下面的部分中,我将讨论设计专利,并更详细地描述语义搜索过程。这里有谷歌实验室,所以你可以自己试试。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

乔治·布鲁斯,图片来源:wikimedia.org,**和他的双小异食癖手稿,**图片来源:乔治·布鲁斯的儿子&公司。这两个图片都属于公共领域。

外观设计专利

正如我上面提到的,外观设计专利保护的是外观,而不是发明的功能方面。第一项外观设计专利于 1842 年 11 月 9 日授予纽约的乔治·布鲁斯,他发明了一种叫做双小十二点活字的新字体。

专利局将每一项设计专利分为 33 类[4],如下所列。(简要说明:乍一看,似乎有 35 个类别,D1-D34 加上 D99 杂项。但奇怪的是,类别编号 D31 和 D33 从列表中消失了。也许 USPTO 对时光机和永动机的花饰有秘密分类?😃)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外观设计专利类别,来源:USPTO

自 20 世纪 60 年代以来,美国专利商标局每年授予的外观设计专利数量呈指数增长。2019 年,有近 35,000 项设计专利获得授权。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片由作者提供,数据来源: USPTO

USPTO 每周都会发布最新的在线专利公报,列出最近授予的实用、设计和植物专利[4]。专利公报可以在这里批量下载,https://bulkdata.uspto.gov/。这个网站有从 2002 年 7 月到今天的公报。

对于这个项目,我只下载和处理了已经过期的设计专利,所以我抓取了从 2002 年 7 月到 2007 年 2 月的公报,产生了 67,578 个设计专利。

外观设计专利的一个有趣的方面是使用“虚线”来显示所描绘的物体的元素,这些元素不被视为外观设计专利的一部分[5]。

虚线公开应理解为仅用于说明目的,并不构成所要求保护的设计的一部分。不属于要求保护的设计的一部分,但被认为是显示使用该设计的环境所必需的结构,可在图样中用虚线表示—美国专利商标局

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

情妇玛侬·贝利和马丁·w·f·迪恩,来源:美国专利商标局

在上面的例子中,你可以看到带有点划线图案的虚线不包括在专利设计中,但鞋顶的缝线包括在内。请注意,所有行都包含在提供给剪辑图像编码器的图像中。

外观设计专利的预处理

我编写了一些 Python 代码来下载批量专利公报,解压缩它们,并遍历 HTML 页面来查找设计专利。它们很容易找到,因为外观设计专利以前缀“USD”开头。例如,以下是 HML 文档和相应 PNG 图像的路径:

gazettes/1314-1/OG/html/1314-1/**USD**0534338-20070102.html
gazettes/1314-1/OG/html/1314-1/**USD**0534338-20070102.png

在 HTML 文档中,我获取专利标题并将其保存到文本文件中。我还将图像调整大小并填充为 224x224 像素,这是剪辑图像编码器使用的大小。完整的源代码可以在这里找到。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

使用剪辑创建嵌入,图片作者

使用剪辑创建文本和图像嵌入

对文本和图像进行预处理后,创建要搜索的嵌入内容就相当容易了。OpenAI 已经发布了 CLIP 的预训练模型。下载他们的模型后,只需要几行 Python 代码就可以生成嵌入。每次嵌入都是 512 个数字的列表。你可以在这里看到源代码是。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

美国专利局,大约 1925 年,来源:美国国会图书馆,公共领域

搜索外观设计专利

现在我们已经有了超过 67,000 项设计专利的文本和图像嵌入,运行文本搜索变得又快又容易。

搜索从输入查询文本开始,例如,“弯曲的水龙头”。然后,系统使用剪辑文本编码器为查询创建文本嵌入。您可以选择三种搜索方法中的一种。

1。文本到文本搜索— 这将查询的嵌入与外观设计专利标题的文本嵌入列表进行比较,并返回最佳结果。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图表

2。文本到图像搜索— 将查询嵌入与图像嵌入列表进行比较,并返回最佳结果。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图表

3。text to combined text/image search—这将复制查询嵌入,并将组合对与组合的文本和图像嵌入列表进行比较,并返回最佳结果。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图表

注意,使用文本和图像嵌入进行语义搜索并不新鲜。例如,您可以阅读这些论文中的最新进展,郭晓晓等人的“基于对话的交互式图像检索”[6]和等人的“通过探索高阶注意力和分心进行视觉语义匹配”[7]。

讨论

在对该系统进行试验后,我发现使用文本和图像的组合嵌入通常会产生最好的结果。外观设计专利的说明文字都很高级,像“男式长靴”。它们通常不包含描述任何关键特征的文本。然而,图像嵌入经常会找到“错误的”对象,而这个对象恰好看起来像被搜索的对象。例如,如果您搜索“体育奖杯”,它可能会显示一个烛台。但是图像嵌入会捕捉设计的一些关键方面,比如上面的“弯曲水龙头”例子。将两种嵌入结合起来进行搜索通常是两全其美的。文本嵌入将搜索限制在查询的范围内,而图像嵌入通过识别关键特征来帮助改进搜索。

请查看下面的附录,查看一些示例搜索结果。

未来的工作

有可能训练一个生成性对抗网络(GAN)来基于来自 USPTO 的图像生成新的设计。这类似于 OpenAI 对 DALL E 系统所做的,它使用一个可变的自动编码器从文本中生成新的图像[8]。

源代码

这个项目的所有源代码都可以在 GitHub 上获得。图像文件和标签可在 Kaggle 上获得。您可以使用这个 Google Colab 来试验代码。这些源在 CC BY-SA 许可下发布。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

归属共享相似

感谢

我要感谢詹尼弗·林和奥利弗·斯特里普尔对这个项目的帮助。

参考

[1]美国专利商标局,“关于专利的一般信息”,2015 年 10 月 15 日,https://www.uspto.gov/patents/basics#heading-30

[2] A .拉德福德,J. W .金,c .哈勒西,a .拉梅什,g .戈,s .阿加瓦尔,g .萨斯特里,a .阿斯克尔,p .米什金,j .克拉克等人,《从自然语言监督中学习可转移的视觉模型》,2021 年 1 月 5 日,https://cdn . open ai . com/papers/Learning _ Transferable _ Visual _ Models _ From _ Natural _ Language _ Supervision . pdf

[3] G. Quinn,IP Watchdog,“第一项外观设计专利”,2008 年 11 月 6 日,https://www . IP Watchdog . com/2008/11/06/The-First-Design-Patent-2/

[4]美国专利商标局,“官方公报”,https://www . USPTO . gov/learning-and-resources/Official-Gazette

[5] USPTO,《外观设计专利申请指南》,https://www . USPTO . gov/patents/basics/types-Patent-applications/Design-Patent-Application-Guide # broken

[6] X .郭,h .吴,y .程,s .雷尼,g .特索罗,R. S .费里斯,“基于对话的交互式图像检索”,2018 年 12 月 20 日,

[7] Y .李,d .张,Y. Mu3,“探索高阶注意和注意力分散的视觉-语义匹配”,2020 年 6 月 13 日,https://open access . the CVF . com/content _ CVPR _ 2020/papers/Li _ Visual-Semantic _ Matching _ by _ Exploring _ 高阶注意 _ and _ 注意力分散 _CVPR_2020_paper.pdf

[8] A .拉梅什,m .巴甫洛夫,g .高,S.t .格雷,《达勒:从文本中创造图像》,2021 年 1 月 5 日,https://openai.com/blog/dall-e

附录-搜索结果

鸡尾酒摇壶

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

组合搜索“不锈钢鸡尾酒调酒器”,作者图片,来源:USPTO

发刷

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

**综合搜索“带刷毛的发刷”,**作者图片,来源:USPTO

躺椅

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

组合搜索“贵妃椅”,作者图片,来源:USPTO

为了无限制地访问 Medium 上的所有文章,成为的会员,每月 5 美元。非会员每月只能看三个锁定的故事。

使用 OrgChartJS 来可视化我的家谱中的 8 代人——160 多个亲戚

原文:https://towardsdatascience.com/using-orgchartjs-to-visualize-8-generations-160-relatives-in-my-family-tree-78468c39e39a?source=collection_archive---------25-----------------------

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

莱昂·保罗克霍夫在 Unsplash 上的照片

将编码技能应用到对个人有意义的项目中。涵盖的技术:OrgChartJS、JavaScript/React、MongoDB 和 Heroku。

并不是每天都有人能够将他们的编码技能应用到对个人有意义的项目中。今年 1 月,我就这么做了,我利用我的网络开发技能建立了一个应用程序/网站,直观地展示了我家 160 多人 8 代的历史,一直追溯到 19 世纪晚期!在这篇文章中,我们将讨论我的项目的动机,我如何选择工具开发过程,先睹为快最终结果,以及我接下来要做什么来推进项目。

那么,为什么要建立我自己的家谱呢?

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

凯文·杰瑞特在 Unsplash 上拍摄的照片

  1. 记录我的过去、现在和未来— 作为一个津巴布韦的小男孩,我总是对家族历史着迷,对那些在我之前的人充满好奇。他们面临哪些挑战?是什么让他们微笑或者相爱?了解你的家族史是一种特权,它给我一种不同于我经历过的任何事情的根植感。有人说,如果你不知道你从哪里来,就很难对你要去哪里有一个清晰的愿景!
  2. **与我的家人分享——**这份家族历史应该由最关心它的人——我的家人和亲戚——所有 167 个人分享、构建和维护!这是一个连接点,把它想象成我们自己家庭的社交媒体。在一个全球化的世界里,我们都分散在津巴布韦各地,散居各地,我有一种感觉,我们的后代将感谢我们保持这一信息的跟踪,尽管我们之间的物理距离。
  3. 当前的替代方案不充分— 我对当前大品牌家族史、家谱、祖先网站和应用程序的研究揭示了一个很大的缺口—这个数据库中没有任何非洲人的记录。我的家人不是乘坐五月花号来美国的,所以他们的记录不会在大型网站上被捕获,任何搜索都是徒劳的。从数据收集的角度来看,目前的市场选择对我来说毫无用处。
  4. 保护我们的隐私— 这是大事!构建自己的应用程序可以让你控制数据并保证其安全——尤其是因为家庭细节是你可以放在那里的一些最敏感的内容。我不知道所有现有的商业应用程序如何处理我的数据,所以我宁愿避开并实现我自己的解决方案。
  5. 提升我的技能— 最后,我学到了很多自己从头开始研究和实现初稿的知识。但是最终能够使用和理解像 OrgChartJS 这样的实用程序库向我展示了什么是可能的,以及我如何能够利用它而不用在一个项目上花费数周时间。在故障排除过程中,我也更加熟悉了 javascript 数据结构和其他特性。

为工作选择正确的工具——组织图表

我使用 JavaScript 已经 4 年多了,最近,我学会了使用 React 库。所以我的第一次尝试是从头开始构建一个简单的版本,但它在视觉上并不令人满意,而且要花很多时间来构建我想要的所有功能,如搜索、缩放、树枝和树叶之间的无缝连接。我还尝试了其他具有层次化视觉效果的图表库,但它们都不太符合要求。不是每个项目都需要你从头开始——为了尽可能高效地执行,你需要为工作选择合适的工具。所以我最终选择了 Balkan 的 OrgChartJS 库,它有更好的视觉效果和即插即用的代码。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

来自https://balkan.app/?aspxerrorpath=/O的分层视觉样本

项目开发过程

该项目有 4 个主要部分:数据采集前端后端托管/部署

  1. 数据收集 —这包括联系我所有的亲戚,为我提供信息以填补空白。我们从家庭的每个分支中选出一个代表,通常是长子,与这个代表协调以获得整个分支的信息会更有效。
  2. 前端/用户体验 —我使用 JavaScript 和 React 来设置项目(使用可信的 create-react-app 实用程序),然后从https://balkangraph.com/js/latest/OrgChart.js下载 OrgChart.js 文件,将它与我的 main.js 文件一起放在我的 public/scripts 文件夹中。下面是我的文件夹结构是如何设置的:(i) archive —包含旧的代码文件。(ii) 控制器 —包含具有用户登录和注销功能的文件,(iii) 模型 —包含与数据库接口的用户功能,即 CRUD 和用户数据的任何验证和清理,(iv) 公共 —图像、脚本和样式文件夹。脚本文件夹有 orgchart.js 和 main.js 文件,(v) 视图 —包含。主页和树页面的 js 文件。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我的家谱项目的文件夹结构

2。后端/数据管理— 我使用 MongoDB 来处理我的用户名逻辑。我们只需要一套整个家庭的凭证,因为我只期望定期流量。对于家谱数据,我将它们作为常规对象存储在 main.js 文件中。这不是最佳实践,但对这个 MVP 版本来说是可行的。关于设置对象和链接到 OrgChartJS 函数的入门演示,请参见这里:https://balkan.app/OrgChartJS-Demos/

3。托管/部署— 我使用 Heroku 进行托管/部署。它又快又简单,而且免费!

现在先睹为快最后的结果!

决赛成绩

1.登录页面

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

带有家庭住宅旋转图像的登录页面

不算太坏,它完成了任务!

2.家谱页面

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

家谱页面和叶节点弹出窗口—部分,为隐私而编辑

我对结果很满意,所以我制作了一个私人的 YouTube 教程视频,和证书一起发给了我的大家庭。他们都喜欢提供的所有功能,例如搜索、缩放、叶节点弹出等。

后续步骤

我们从概念上了解了如何使用 OrgChartJS、Javascript/React、MongoDB 和在 Heroku 上部署来开发私有家谱。我从这次经历中最大的收获是如何为工作选择正确的工具——作为一名开发人员并不意味着你必须从头开始“开发”一切。只要您理解了基本原理,您就可以继续下去并利用那里的实用程序库。我的家人和亲戚可以享受探索他们的祖先。在未来,我计划增加一些功能,比如(i) 上传照片、(ii) 出生或死亡日期、(iii) 地址、(iv) 简历等。

请分享这篇文章,并在下面写下你对这种方法的看法,或者如果你有任何问题。这对我来说是一次很好的学习经历,我有一个很酷的、有意义的产品可以展示。

编码快乐!

通过神游在 Spark 上使用 Pandera 进行数据验证

原文:https://towardsdatascience.com/using-pandera-on-spark-for-data-validation-through-fugue-72956f274793?source=collection_archive---------13-----------------------

本教程将展示如何用赋格将熊猫库引入 Spark 和 Dask。在这个例子中,我们将使用 Spark 上的 Pandera 数据验证库。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

照片由EJ·斯特拉特Unsplash 上拍摄

数据有效性

数据验证是进行适当的检查,以确保数据符合我们期望的格式和规范。随着数据管道变得越来越互联,无意中破坏其他管道的变化的机会也增加了。验证用于保证上游的更改不会破坏下游数据操作的完整性。常见的数据验证模式包括检查空值或检查数据框形状,以确保转换不会丢失任何记录。其他经常使用的操作是检查列的存在和模式。使用数据验证可以避免数据流程的无声失败,在这种情况下,一切都可以成功运行,但提供的结果不准确。

数据验证可以放在数据管道的开始,以确保任何转换顺利进行,也可以放在最后,以确保在输出提交到数据库之前一切正常。这就是可以使用像潘德拉这样的工具的地方。在这篇文章中,我们将制作一个小熊猫数据框来展示例子。有三列,州、城市和价格。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

样本数据—每个位置的价格

潘德拉

Pandera 是一个轻量级的数据验证框架,有很多内置的验证器来验证 DataFrame 模式和值。当验证失败时,它提供信息性错误,并且它对已经编写的代码也是非侵入性的,因为decorator可以与其他函数一起使用来执行验证。下面是我们将如何检查我们的数据,以确保价格在推向生产之前是合理的。

基本的 Pandera 用法

潘德拉代码是直观的。第 3–5 行定义了在上执行的检查。我们正在检查价格值是否在 5 到 20 之间。第 7–10 行只是一个包装器函数(为了后面的目的),但是我们真正需要的是调用第 9 行的 validate 方法来应用验证。

对火花(或 Dask)的需求

如果数据变得太大,熊猫无法有效处理(超过 15GB),我们该怎么办?为了加快我们的工作,我们需要使用分布式计算框架。这就是火花和达斯克的用武之地。计算操作是在一群机器上执行的,而不是在一台机器上。

在很多情况下,我们已经为熊猫写好了逻辑,但想把它带到 Spark。一个用例是写出一个非常具体的大型验证模式。我们需要在 Spark 中重新创建该功能。不幸的是,Pandera 只适用于熊猫,这意味着我们需要从头开始重新创建。问题是定制的业务逻辑需要 Pandas 和 Spark 的两个实现。

赋格入门

这就把我们带到了赋格。Fugue 是一个抽象层,允许用户将 Python 或 Pandas 代码移植到 Spark 或 Dask。逻辑与执行解耦,用户只需修改一行代码就可以选择自己需要的引擎。我们可以用下面的代码来激发上面熊猫特有的逻辑。注意,第 12 行之前的所有内容都是从前面的 Pandera 代码片段中复制的。

第 15 行中的fugeworkflow类包含一个执行引擎。如果没有传递任何内容,则默认使用熊猫。在这个具体的例子中,我们传递了 SparkExecutionEngine ,它在 Spark 中执行我们所有的逻辑。 price_validation 函数被映射到 Spark 分区。这将加快大型数据集的验证操作。

按分区验证

当前的数据验证框架有一个缺点。根据我们掌握的数据,CA 和 FL 的价格范围相差很大。因为验证是按列应用的,所以我们无法为每个位置指定不同的价格范围。然而,如果我们可以对每组数据应用不同的检查,那将是理想的。这就是我们所说的分区验证**。**

这个操作对于赋格来说变得非常琐碎。我们可以稍微修改上面的例子来达到这个目的。在下面的代码片段中,第 6 行到第 12 行只是前面验证的两个版本。一个用于 FL,一个用于 CA。我们在第 14 行将它们打包成一个字典。

我们的 price_validation 函数也做了一些调整。首先,我们的函数现在是在假设传入的数据帧只包含一个状态(CA 或 FL)的情况下编写的。我们从第一行的 State 值中提取位置,从字典中找到适当的验证,并应用它。

另一个变化是第 24 行现在在验证之前按照州对数据进行了划分。这基本上意味着 Spark 数据帧被分割成更小的 Pandas 数据帧,并且对每个数据帧分别进行操作。 price_validation 函数为 CA 数据调用一次,为 FL 数据调用一次。这种验证是通过 Spark 执行引擎并行完成的。

结论

在这篇博客文章中,我们简要回顾了什么是数据验证。我们将 Pandera 库视为执行数据验证的一种方式。由于该库只在 Pandas 中可用,我们使用 Fugue 将它带到 Spark,这是一个抽象层,允许用户将 Python 和 Pandas 代码移植到 Spark 和 Dask。使用 Fugue,我们可以在每个数据分区上应用 Python/Pandas 包,这允许我们在这里通过分区执行验证。

神游也可以将其他基于熊猫的库引入 Spark。这个例子只是针对数据验证的。有关更多信息,请查看下面的参考资料。

资源

如果你对使用赋格感兴趣,想给我们反馈,或者有任何问题,我们很乐意在 Slack 上聊天!我们还将为对在数据工作流中应用 Fugue 感兴趣的数据团队举办更详细的研讨会。

潘德拉

赋格

文档
Git 回购
社区闲置

使用“个性测验”和最近邻居进行推荐(英雄联盟)

原文:https://towardsdatascience.com/using-personality-quizzes-nearest-neighbors-for-recommendations-league-of-legends-9ad399e7ea34?source=collection_archive---------8-----------------------

一个初学者友好的 N 维空间,最近邻,欧几里德距离和更多的演练

这是关于推荐方法的 3 部分系列的最后一部分(在这里找到: 1 & 2 ,尽管我建议在点击离开之前继续阅读),然而我认为这实际上是迄今为止我所写的任何文章中最入门的**,因此我建议从这里开始。对于有经验的你来说,我希望仍然有价值——但是你可能想跳过一些介绍性的部分。我会在文章里说清楚这些点在哪里。**

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

最终产品,一个能够推荐一个英雄联盟冠军的性格竞猜!自己试试这里。图片作者。

之前,我写过两种推荐方法:

【用户-用户协同过滤】 : 相似的用户喜欢相似的产品。

【基于内容的模型】 : 如果用户喜欢一个产品,他们也会喜欢类似的产品。

今天,我们来看看标志性的方法:一个性格测试。根据用户对调查问卷的回答,找到与用户最相似的产品。这在“现实生活”中是最不受欢迎的,因为它增加了太多的摩擦,而且相对不可靠。然而,它很简单,并且对推荐引擎的主题提供了很好的介绍,同时允许有更多的时间来详细了解核心概念。此外,他们很有趣。

使用的数据科学技术被称为**“最近邻居”**,这个领域的许多人会因为我称之为人工智能而排斥我。无论如何,它构成了后来许多应用程序的主干,因此清楚地理解它永远不应该被低估。

根据我的大多数文章,我使用在线游戏英雄联盟(LoL)来应用这些技术。你不需要理解游戏来理解文章,但是如果你想知道更多,这里有一个游戏的介绍。

N 维空间导论

在本文的某个地方你会看到*“21 维空间”*。这是我失去一些人的地方。对于非数学家来说,有时会有这样的假设,那些提到第四维度以外的任何维度的人可能事实上正在与神交流,预见未来或者是部分蜥蜴类外星人。如果你在这一点上点头同意,最好完整地阅读这一部分。如果你非常熟悉 N 维空间的含义,请直接跳到最近邻的介绍。

让我们从简单的开始。如果我在研究一群人,我可能会从测量他们的体重开始。我创建了一个一维空间,名为“公斤体重”。一个人只能比下一个人更重或更轻。只有一个行进方向。它可以用一条简单的直线来表示。我们越向左走,它们越轻,我们越向右走,它们越重。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

一维空间,没有比这更简单的了!图片作者。

现在我们通过增加它们的高度来移动到二维空间。我们可以绘制出那个空间里每个人的体重,以及他们的身高。一切都很容易想象。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

身高和体重的 2D 空间。图片作者。

当我们把他们的年薪计算在内时,事情在三维空间变得更加棘手。想象一个立方体,想象你自己在最近的左下角。你沿着宽度到他们的体重,到他们的高度,然后根据他们的工资深入立方体。你甚至可以拿着尺子跳进盒子里,测量两个人之间的距离,看看他们有多相似。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

一个 3D 空间,其中点的不透明度表示年收入。图片作者。

然而,当我添加鞋号时会发生什么?我们现在有了四维空间。我们不再能够在脑海中描绘出这将会是什么样子——然而一切都没有改变!那些点仍然出现在一个空间里,只是这个空间在我们这个非常无聊的 3D 世界里无法直观的展现出来。我们甚至还可以测量两点之间的距离,只是我们必须把尺子拿开,用数学方法来计算。

无论我们有 4 维、5 维还是 4500 维,都是如此。没关系!我们不能想象任何超过 3 的东西,但是所有相同的原理和技术都可以以相同的方式应用(数学上),不管我们达到什么样的维度。

最近邻居介绍

不管你是跳过这里,还是读过上面,我希望当我说“N 维空间”时,我们都在同一页上。这意味着我们可以转移到实际的技术本身。

最近邻(NN) 就是确切地说就是罐头上写的。对于空间中的任意一点,找出 N 个最近邻。从字面上看,你只需选择一个点,并找到 N 个最接近该点的点。“N”可以是任何数字,但是标准 Python 实现(sklearn)默认为 5。

希望下面的观想能让这一点变得清晰。蓝色是示例点,红色是 5 个最近的邻居:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

2D 空间中最近邻的直观解释。图片作者。

有一点需要明确的是,当我们说“最近”时,我们指的是什么。在上面的 2D 空间中,我们可以在屏幕上按下一把尺子,然而在难以想象的 4 维空间和更高的空间中会发生什么呢?好吧,我们用数学上相当于尺子的:欧几里德距离

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

欧几里德距离方程。来源:维基百科

再次,【vector】这个词是许多人的另一个导火索。因此,让我们明确我们的意思(如果你知道所有这些,到应用与你最近的邻居!).

回到我们的人口例子。假设我们有两个人的体重和身高。

人 1:70 公斤,170 厘米

人二:90kg,185cm。

重量是矢量(70,90),高度是矢量(170,185)。根据上面的公式,如果我们想找到它们之间的欧几里德距离,我们做每个点之间的平方差的和。像这样:

重量平方差:(人 1 重量-人 2 重量)=(90–70)= 20 = 400

身高平方差:(人 1 身高-人 2 身高)=(185–170)= 15 = 225

(体重平方差+身高平方差)的平方根)=平方根(400 + 225) =平方根(625) = 25。

个体一(70kg,170cm)和个体二(90kg,185cm)的欧氏距离为 25。

如果我们改为处理 4 个维度(体重、身高、收入和鞋码),这些值可能是:

人 1:70 公斤,170 厘米,7 万美元,10 号鞋

人 2:90 公斤,185 厘米,95000 美元,12 号鞋

计算是相同的,除了不仅仅是身高和体重的平方差,我们还包括年薪和鞋码。试着自己算出欧几里德距离。答案在文章底部*。

将最近邻应用于个性测验

但这一切如何应用于推荐一个基于性格测验的英雄联盟冠军(可玩角色)?

这是如何做到的:

  1. 创建一个多维空间,其中的功能有助于描述游戏中的每个冠军(目前有 152 个冠军可供选择)。
  2. 让神经网络算法适合这个空间。
  3. 创建一个调查,试图把这个人放在那个空间。
  4. 使用神经网络算法来确定与该人的调查代表最接近的冠军。

注意: 如果这还没有意义,不要担心,我们将详细介绍它。

**第一步。**是目前为止最繁琐最耗时的。我们构建了一个脚本,从 Riot API 中获取游戏,并从中提取出所玩游戏的冠军以及在哪条泳道中。我们也抓取一些我们认为有助于描述冠军的特征,比如他们杀了多少人,他们对目标造成了多少伤害,他们阻挡了多少伤害,等等…

我有大约 800 万个游戏的信息,但那是因为我是从其他项目那里得到的。如果你想亲自尝试,我不建议你去那么高的地方。

下面是表格的一个例子,每行代表一名冠军在一场比赛中的表现:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

显示冠军在一场比赛中的表现的表格。图片作者。

然后,我们可以使用数据帧上的分组来获得所有游戏中每个通道/冠军组合的平均值。看起来是这样的:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

显示冠军在多场比赛中平均表现的表格。图片作者。

最后,我去掉了少于 5000 场比赛的球道/冠军组合。这消除了真正偏离元的选择(<0.25% pick rate) and ensures there’s sufficient data for the averages to converge around their true mean (中心极限定理)。

然后,我们通过按通道过滤将数据帧分成 5 份。你不需要这样做,但我觉得这是一个更干净的过程,我喜欢为每条车道提供个人建议的想法。

我还从原始数据中创建了一些我认为会很有趣的附加特性。例如,我有一个冠军在没有帮助的情况下杀死别人的次数(单杀),我也有他们杀死的总次数。我创造了一个新的特征,那就是单杀占总杀死数的百分比(单杀百分比=单杀/总杀死数)。

下一步是将数据帧转换成矩阵,其中每行代表一个冠军,有 21 个关于他们在游戏中的表现的各种统计数据,平均跨越许多样本。

然后,我们对数据进行归一化处理,并根据矩阵拟合最近邻算法,默认 N = 5。

关于标准化的补充说明

同样,这篇文章真的是为任何水平的读者准备的——所以我不想只写“我们规范化了数据”而不解释和证明自己。因此,如果您对我们如何/为什么这样做感到满意,请跳过这一部分。如果不是,这里有一个简单的解释:

我们正在使用的最近邻方法完全依赖于两点之间的欧几里德距离。我们的问题是每个特征都在不同的尺度上。例如,让我们以一个冠军为例,他平均造成 50,000 次伤害,每场比赛赚 10,000 金币。

然后,我们想测量冠军和一个造成 50,000 次伤害,但只获得 8,000 金币的冠军之间的欧几里德距离。花一点时间考虑一下…

…答案是 2000。因为第一个数字(伤害:50,000)不变,所以所有的距离都来自“黄金”特性,区别在于:

10,000 – 8,000 = 2,000.

然后我们将两个平方差求和:

2,000² + 0² = 2,000²

最后,求平方根:

平方根(2000)= 2000

所以,为了减少 20%的杀戮,欧几里德距离是 2000。

现在,想象一个冠军只造成 40,000 点伤害,但仍然得到 10,000 金币。为了减少 20%的伤害,欧几里德距离现在是 10000!这意味着最近邻算法对伤害属性的依赖是杀死属性的 5 倍。希望这说明了这一点:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

观察值 X:伤害,Y:黄金。说明距离对伤害的依赖远远超过黄金。图片作者。

那么,我们该怎么解决呢?我们将数据标准化。或者换句话说,我们确保所有的统计数据都在 0 和 1 之间的相同范围内。为此,我们使用以下方法:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片作者。

如果 5 个冠军的平均每场杀数是:[3,1,5,6,11]。

最小值(x) = 1,最大值(x) = 11

从列表的开头开始,x=3:

(x-min(x))/(max(x)-min(x))=(3–1)/(11–1)= 2/10 = 0.2

如果我们对列表中的所有数字都这样做,[3,1,5,6,10]=>[0.2,0,0.4,0.5,1]

杀戮现在被限制在 0 和 1 之间。我们对造成的伤害做同样的事情。现在杀戮的变化和伤害的变化一样强大。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

除了两个统计数据都被归一化之外,与上面的图相同。现在伤害和黄金的变化同样强大。图片作者。

将问卷转换成数据

在我们开始之前,我跟你说实话。这部分既是科学也是艺术。其实大部分是艺术,带着一大把盐往前走。

所以,我们有了这个 21 维空间(我告诉过你它要来了)。每个点代表一个冠军,他们的位置取决于这 21 个统计数据的组合。我们的下一个目标是创建一个问卷,让我们将用户放入这个空间。

为了做到这一点,我们需要根据调查者的“个性”找出 21 个统计数据中每一个的粗略近似值。换句话说,对于每一个统计数据,都需要有一个相应的问题和一种将答案转化为可用数字的方法。

为此,我选择了*“你有多同意以下说法”*式的提问。效果好的原因是因为他们的反应是有规模的,我把它设定为:

完全不同意、不同意、中立、同意、完全同意

然后我可以将这些陈述转换成百分位数,然后再转换成数据。一个例子应该有助于说明这一点:

我们数据集中的第一个统计数据是:在没有帮助的情况下完成的冠军猎杀的百分比。我(巧妙地)把它翻译成了下面这句话:

《我是独狼》

为什么?高百分比的冠军是那些在单独行动时获得大部分杀戮的人。那些百分比低的人依靠他们的队友来确保安全捕杀。因此,孤狼。这是科学的艺术部分。

因此,那些回答“完全同意”的人被认为是统计数据中前 20%的人,而那些回答“完全不同意”的人是后 20%的人。说中立的人在 40-60%之间。鉴于我们有所有冠军的统计数据,我们只是计算出他们价值的粗略估计。

为了帮助澄清这一点,让我们想象我只有 10 个冠军,我在看他们平均每场比赛杀死的数量:

每场比赛的死亡数= [1,1,2,3,3,3,4,5,7,8]

如果有人说“完全不同意”一个关于每场比赛杀球的陈述(比如“我喜欢杀很多球”),这将使他们在这个统计中处于倒数 20%。所以,10 个冠军中倒数 20%就是倒数 2 个。对于这个人,我们会给他们每局 1 次的杀戮。为什么?上面列表中最低的 2 个值是 1 和 1。

如果其他人说“完全同意”,他们会排在前 20%。所以每场比赛要么杀 7 个要么杀 8 个。

“同意”怎么样?它在 60-80%之间,所以是 4 或 5。你明白了。

然后,我们继续从 21 个可用的统计数据中创建问题,每次都将它们的答案转换为实际值。问题及其相关统计数据的完整列表可在此处找到:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

所有提问的表格,以及相关的统计数据。图片作者。

现在,当有人完成“性格测验”时,我们将每个答案转换为统计数据,归一化这些值(使用我们在原始数据集上使用的相同最小/最大值),然后,我们将他们的性格转换为我们 21 维空间中的位置!

最后一步是使用我们之前拟合的神经网络算法来确定哪些原始冠军最接近此人的回答,并向他们提供前 5 名作为推荐!

最后一点

你们中的一些人可能会问“但在上面的例子中,你说完全同意可能是 7 或 8,你如何选择?”。答案是,我没有。我使用了一个随机数生成器在这个范围内选择一个值。这意味着每次你回答问题时,你都会得到稍微不同的答案,因为你在空间中的点随机在 20%的范围内移动,所以不同的冠军变得更近/更远。一种替代方法是坚持静态答案(即 7 或 8 的中点= 7.5),但是这意味着您在回答中获得的冠军多样性较少。另一种选择是说“在 1 到 1000 的范围内,您对该陈述的赞同程度如何”,但这感觉对用户不太友好。这里没有正确的答案。

如果你想亲自尝试成品,你可以在这里找到它。我很想听听你是否同意这个结果!

这篇文章比我通常写的要详细得多,我花了很多时间来解释更基本的概念,如维度空间和规范化。这是因为我想找个地方介绍一个完全陌生的人进入这个领域(因为我在工作中遇到了很多这样的人)。然而,我希望仍然有一些老兵发现了足够有趣的材料,值得一读。

*我在 NN 简介中设置的问题答案:25000.013

你已经看到文章的结尾了!我叫 Jack J,是一名将人工智能应用于竞技游戏和电子竞技的专业数据科学家。我是iTero 的创始人。GGjung.gg 。你可以在 Twitter 上关注我,加入 iTero Discord 或者给我发邮件到 jack@itero.gg 。下一场见。

最初发表于:https://itero.gg/blog

使用带 RLlib 的 PettingZoo 进行多智能体深度强化学习

原文:https://towardsdatascience.com/using-pettingzoo-with-rllib-for-multi-agent-deep-reinforcement-learning-5ff47c677abd?source=collection_archive---------4-----------------------

免责声明

对于 PettingZoo 和 RLlib 的新版本,本教程不再是最新的。自从这篇文章发表以来,PettingZoo 经历了一些重大的修改,现在是 Farama 基金会的一部分。有关最新文档和教程,请参见 https://pettingzoo.farama.org/的

通过 RLlib 强化学习库使用 PettingZoo 多代理环境的教程

感谢尤里·普洛特金罗汉·波特达尔本·布莱克卡安·奥兹多格鲁,他们各自创作或编辑了本文的大部分内容。

本教程概述了如何使用 RLlib Python 库和 PettingZoo 环境进行多智能体深度强化学习。关于使用 PettingZoo 环境的更多细节可以在最近的博客文章中找到。在下面的文章中,我们将介绍如何使用两个 PettingZoo 环境来训练和评估 RLlib 策略:

活塞球——不存在非法行为,所有特工同时行动

Leduc Hold’em —非法行动掩蔽,基于回合的行动

宠物动物园和滑雪球

PettingZoo 是为多代理强化学习模拟开发的 Python 库。当前的软件提供了一个标准的 API,在使用其他著名的开源强化学习库的环境中进行训练。你可以把这个库想象成类似于 OpenAI 的 Gym 库,然而,它是为多代理强化学习而定制的。与其他类似,API 的基本用法如下:

from pettingzoo.butterfly import pistonball_v5env = pistonball_v5.env()
 env.reset()
 for agent in env.agent_iter():
 observation, reward, done, info = env.last()
 action = policy(observation, agent)
 env.step(action)

Pistonball 是一个合作的 PettingZoo 环境,如下图所示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

环境的目标是训练活塞协同工作,尽可能快地把球移到左边。

每个活塞都作为一个独立的代理,由一个策略 π 控制,这个策略是用函数逼近技术训练的,比如神经网络(因此是深度强化学习)。每个代理的观察空间是每个活塞上方和侧面的一个窗口(见下文)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

作者图片

我们假设完全可观察,策略 π 返回一个动作,用于将活塞从+4 像素升高或降低到-4 像素(图像尺寸为 84x84 像素)。

随着每个活塞的动作,环境输出两个全局奖励

(δx/xₑ)* 100+τt

其中δx是球的 x 位置的变化, Xₑ 是球的起始位置, τ 是时间惩罚(默认值为 0.1)乘以时间长度 t

关于宠物动物园环境的更多细节,请查看以下描述

代码

让我们更详细地浏览一下代码。

首先,为了运行强化学习环境,我们导入所需的库:

from ray import tunefrom ray.rllib.models import ModelCatalogfrom ray.rllib.models.torch.torch_modelv2 import TorchModelV2from ray.tune.registry import register_envfrom ray.rllib.env.wrappers.pettingzoo_env import ParallelPettingZooEnvfrom pettingzoo.butterfly import pistonball_v5import supersuit as ssimport torchfrom torch import nn

多主体强化学习环境需要分布式训练。为了设置环境,我们使用开源库 Ray 。Ray 是为构建分布式应用程序提供通用 API 而开发的框架。 Tune 是一个构建在 Ray 之上的库,用于分布式强化学习中的可伸缩超参数调优。因此,我们只使用 Tune 在 RLlib 中执行一次训练运行。由于我们将需要使用一个定制模型来训练我们的策略 π ,我们首先在 RLlib 的 ModelCatalog 中注册这个模型。为了创建自定义模型,我们从 RLlib 中继承了 TorchModelV2 类的子类。

为了将 PettingZoo 环境与 Tune 一起使用,我们首先使用 register_env 函数注册环境。parallelpettingzooeenv是一个包装器,用于 PettingZoo 环境,如 Pistonball,以与 RLlib 的多代理 API 接口。SuperSuit 是一个为健身房和宠物动物园环境提供预处理功能的库,我们将在下面看到。

最初,我们在 Pytorch 中创建一个卷积神经网络模型来训练我们的策略 π :

class CNNModelV2(TorchModelV2, nn.Module):def __init__(self, obs_space, act_space, num_outputs, *args, **kwargs):TorchModelV2.__init__(self, obs_space, act_space, num_outputs, *args, **kwargs)nn.Module.__init__(self)self.model = nn.Sequential(nn.Conv2d( 3, 32, [8, 8], stride=(4, 4)),nn.ReLU(),nn.Conv2d( 32, 64, [4, 4], stride=(2, 2)),nn.ReLU(),nn.Conv2d( 64, 64, [3, 3], stride=(1, 1)),nn.ReLU(),nn.Flatten(),(nn.Linear(3136,512)),nn.ReLU(),)self.policy_fn = nn.Linear(512, num_outputs)self.value_fn = nn.Linear(512, 1)def forward(self, input_dict, state, seq_lens):model_out = self.model(input_dict[“obs”].permute(0, 3, 1, 2))self._value_out = self.value_fn(model_out)return self.policy_fn(model_out), statedef value_function(self):return self._value_out.flatten()

你可以在这里找到更多关于如何使用 RLlib 库的定制模型的信息。然后我们需要定义一个函数来创建和返回环境:

def env_creator(args):env = pistonball_v4.parallel_env(n_pistons=20, local_ratio=0, time_penalty=-0.1, continuous=True, random_drop=True, random_rotate=True, ball_mass=0.75, ball_friction=0.3, ball_elasticity=1.5, max_cycles=125)env = ss.color_reduction_v0(env, mode=’B’)env = ss.dtype_v0(env, ‘float32’)env = ss.resize_v0(env, x_size=84, y_size=84)env = ss.frame_stack_v1(env, 3)env = ss.normalize_obs_v0(env, env_min=0, env_max=1)return env

我们使用 PettingZoo 的并行 API 来创建环境。函数的参数控制环境的属性和行为。我们另外使用 SuperSuit 的包装函数进行预处理操作。

第一个函数用于将环境产生的全色观察图像转换为灰度图像,以降低计算复杂度和成本。随后,我们将像素图像数据类型从 uint8 转换为 float32,将其缩减为 84x84 网格表示,并归一化像素强度。最后,为了测量球的方向变化δX(用于计算总奖励),将 3 个连续的观察帧堆叠在一起,以提供一种简单的学习策略。

这些预处理操作的更详细的解释可以在之前的教程中找到。

在定义了模型和环境之后,我们可以使用配置字典中的参数,使用 tune.run()函数运行训练器。你可以在这里详细了解这些超参数。值得注意的是,我们实例化了一个多代理特定配置,其中我们使用字典映射来指定我们的策略:

policy_id 字符串→元组(policy_cls,obs_space,act_space,config)。

policy_mapping_fn 是一个将 agent_ids 映射到 policy_ids 的函数。在我们的特殊情况下,我们使用参数共享来训练活塞,如之前的教程中所述,即所有活塞都由 id 为‘policy _ 0’的同一策略 π 控制。

if __name__ == “__main__”:env_name = “pistonball_v4”register_env(env_name, lambda config: ParallelPettingZooEnv(env_creator(config)))test_env = ParallelPettingZooEnv(env_creator({}))obs_space = test_env.observation_spaceact_space = test_env.action_spaceModelCatalog.register_custom_model(“CNNModelV2”, CNNModelV2)def gen_policy(i):config = {“model”: {“custom_model”: “CNNModelV2”,},“gamma”: 0.99,}return (None, obs_space, act_space, config)policies = {“policy_0”: gen_policy(0)}policy_ids = list(policies.keys())tune.run(“PPO”,name=”PPO”,stop={“timesteps_total”: 5000000},checkpoint_freq=10,local_dir=”~/ray_results/”+env_name,config={# Environment specific“env”: env_name,# General“log_level”: “ERROR”,“framework”: “torch”,“num_gpus”: 1,“num_workers”: 4,“num_envs_per_worker”: 1,“compress_observations”: False,“batch_mode”: ‘truncate_episodes’,# ‘use_critic’: True,‘use_gae’: True,“lambda”: 0.9,“gamma”: .99,# “kl_coeff”: 0.001,# “kl_target”: 1000.,“clip_param”: 0.4,‘grad_clip’: None,“entropy_coeff”: 0.1,‘vf_loss_coeff’: 0.25,“sgd_minibatch_size”: 64,“num_sgd_iter”: 10, # epoc‘rollout_fragment_length’: 512,“train_batch_size”: 512*4,‘lr’: 2e-05,“clip_actions”: True,# Method specific“multiagent”: {“policies”: policies,“policy_mapping_fn”: (lambda agent_id: policy_ids[0]),},},)

现在,我们可以看到我们训练过的策略在环境中自动执行。在训练期间,根据 checkpoint_freq 指定的频率,在每个检查点保存策略。默认情况下,RLlib 将检查点存储在~/ray_results 中。我们首先指定检查点的路径:

checkpoint_path = “foo”

然后,我们可以加载并恢复我们训练过的策略 π ,并在每个时间步渲染环境时使用该策略来选择动作。我们将渲染后的视频保存为 GIF 格式:

with open(params_path, “rb”) as f:config = pickle.load(f)# num_workers not needed since we are not trainingdel config[‘num_workers’]del config[‘num_gpus’]ray.init(num_cpus=8, num_gpus=1)PPOagent = PPOTrainer(env=env_name, config=config)PPOagent.restore(checkpoint_path)reward_sum = 0frame_list = []i = 0env.reset()for agent in env.agent_iter():observation, reward, done, info = env.last()reward_sum += rewardif done:action = Noneelse:action, _, _ = PPOagent.get_policy(“policy_0”).compute_single_action(observation)env.step(action)i += 1if i % (len(env.possible_agents)+1) == 0:frame_list.append(PIL.Image.fromarray(env.render(mode=’rgb_array’)))env.close()print(reward_sum)frame_list[0].save(“out.gif”, save_all=True, append_images=frame_list[1:], duration=3, loop=0)

解决环境的 gif 可以在上面看到。

本教程中详细介绍的整个培训代码可以在这里找到。并且渲染的代码可以在这里找到。对于代码库的任何改进或本文遇到的任何问题,请在 PettingZoo 资源库中创建一个问题。

Leduc 德州扑克

Leduc Hold’em 是 AI 研究中流行的一种扑克变种详细介绍这里这里;我们将使用双人模式。这种环境是值得注意的,因为它是一个纯粹的回合制游戏,有些动作是非法的(例如,需要动作屏蔽)。

首先,我们需要的初始代码看起来非常相似:

from copy import deepcopyimport osimport rayfrom ray import tunefrom ray.rllib.agents.registry import get_agent_classfrom ray.rllib.env import PettingZooEnvfrom pettingzoo.classic import leduc_holdem_v2from ray.rllib.models import ModelCatalogfrom ray.tune.registry import register_envfrom gym.spaces import Boxfrom ray.rllib.agents.dqn.dqn_torch_model import DQNTorchModelfrom ray.rllib.models.torch.fcnet import FullyConnectedNetwork as TorchFCfrom ray.rllib.utils.framework import try_import_torchfrom ray.rllib.utils.torch_ops import FLOAT_MAXtorch, nn = try_import_torch()

通过参数动作屏蔽使用动作屏蔽初始化合适的策略,如下所示:

class TorchMaskedActions(DQNTorchModel):“””PyTorch version of above ParametricActionsModel.”””def __init__(self,obs_space,action_space,num_outputs,model_config,name,**kw):DQNTorchModel.__init__(self, obs_space, action_space, num_outputs,model_config, name, **kw)obs_len = obs_space.shape[0]-action_space.norig_obs_space = Box(shape=(obs_len,), low=obs_space.low[:obs_len], high=obs_space.high[:obs_len])self.action_embed_model = TorchFC(orig_obs_space, action_space, action_space.n, model_config, name + “_action_embed”)def forward(self, input_dict, state, seq_lens):# Extract the available actions tensor from the observation.action_mask = input_dict[“obs”][“action_mask”]# Compute the predicted action embeddingaction_logits, _ = self.action_embed_model({“obs”: input_dict[“obs”][‘observation’]})# turns probit action mask into logit action maskinf_mask = torch.clamp(torch.log(action_mask), -1e10, FLOAT_MAX)return action_logits + inf_mask, statedef value_function(self):return self.action_embed_model.value_function()

Leduc Hold’em 中的渲染功能类似于 Pistonball,使用以下代码片段:

import rayimport pickle5 as picklefrom ray.tune.registry import register_envfrom ray.rllib.agents.dqn import DQNTrainerfrom pettingzoo.classic import leduc_holdem_v4import supersuit as ssfrom ray.rllib.env.wrappers.pettingzoo_env import PettingZooEnvimport PILfrom ray.rllib.models import ModelCatalogimport numpy as npimport osfrom ray.rllib.agents.registry import get_agent_classfrom copy import deepcopyimport argparsefrom pathlib import Pathfrom rllib_leduc_holdem import TorchMaskedActionsos.environ[“SDL_VIDEODRIVER”] = “dummy”parser = argparse.ArgumentParser(description=’Render pretrained policy loaded from checkpoint’)parser.add_argument(“checkpoint_path”, help=”Path to the checkpoint. This path will likely be something like this: `~/ray_results/pistonball_v4/PPO/PPO_pistonball_v4_660ce_00000_0_2021–06–11_12–30–57/checkpoint_000050/checkpoint-50`”)args = parser.parse_args()checkpoint_path = os.path.expanduser(args.checkpoint_path)params_path = Path(checkpoint_path).parent.parent/”params.pkl”alg_name = “DQN”ModelCatalog.register_custom_model(“pa_model”, TorchMaskedActions)# function that outputs the environment you wish to register.def env_creator():env = leduc_holdem_v4.env()return envnum_cpus = 1config = deepcopy(get_agent_class(alg_name)._default_config)register_env(“leduc_holdem”,lambda config: PettingZooEnv(env_creator()))env = (env_creator())# obs_space = env.observation_space# print(obs_space)# act_space = test_env.action_spacewith open(params_path, “rb”) as f:config = pickle.load(f)# num_workers not needed since we are not trainingdel config[‘num_workers’]del config[‘num_gpus’]ray.init(num_cpus=8, num_gpus=0)DQNAgent = DQNTrainer(env=”leduc_holdem”, config=config)DQNAgent.restore(checkpoint_path)reward_sums = {a:0 for a in env.possible_agents}i = 0env.reset()for agent in env.agent_iter():observation, reward, done, info = env.last()obs = observation[‘observation’]reward_sums[agent] += rewardif done:action = Noneelse:print(DQNAgent.get_policy(agent))policy = DQNAgent.get_policy(agent)batch_obs = {‘obs’:{‘observation’: np.expand_dims(observation[‘observation’], 0),‘action_mask’: np.expand_dims(observation[‘action_mask’],0)}}batched_action, state_out, info = policy.compute_actions_from_input_dict(batch_obs)single_action = batched_action[0]action = single_actionenv.step(action)i += 1env.render()print(“rewards:”)print(reward_sums)

与之前类似,本教程中使用的完整训练代码可以在这里找到,渲染代码可以在这里找到。如果您对这段代码有任何改进,或者对本文有任何问题,请在 PettingZoo 资源库上创建一个问题,我们很乐意提供帮助。

使用 pgfplots 在 LaTeX 中制作经济图表

原文:https://towardsdatascience.com/using-pgfplots-to-make-economic-graphs-in-latex-bcdc8e27c0eb?source=collection_archive---------1-----------------------

实践教程

用行业标准排版语言制作光滑、专业图形的通俗易懂的指南

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

**图 0–1:**工资弹性和总收入。(图片由作者提供)

1。简介

经济学大量使用图表来说明重要的概念和现象。然而,当在这个领域打一篇论文时,人们可能注意到的第一件事是使用文字处理器制作这些图表的困难。这就是乳胶发挥作用的地方。本指南将解释我们如何使用 pgfplots 包在 LaTeX 中制作精美的经济图表。

1.1。不熟悉乳胶?

有些读者可能不熟悉 LaTeX。它是一种功能强大的排版语言,能够对文本、数学公式、表格以及 word 和 Google Docs 等文字处理器无法处理的许多其他元素进行排版。它的功能通过包来扩展。本指南将重点介绍使用为绘制图形而设计的包——pgfplots——制作图形,PGF plots 本身依赖于为制作图形而设计的包——TikZ。

LaTeX 强大的能力和可定制性带来了良好的学习曲线。不要绝望。本指南是在假设读者熟悉 LaTeX 的基础知识的情况下编写的,但仅此而已。(在第 8 节:资源中,您可以使用一些资源来设置和学习 LaTeX 的基础知识。)如果您可以创建一个文本文档,加载到包中,并在 LaTeX 的显示数学模式下编写一个方程,那么您不需要知道其他任何东西。尽管如果您已经熟悉 pgfplots 和 TikZ,您可能仍然会发现这里使用的一些技术对于经济图表的非常具体的特性非常有用。

1.2.GitHub 项目资源库

此处显示的所有完成的图表都可以在本指南的 GitHub 资源库中找到。你可以在 https://github.com/jackypacky/pgf-econ-graphs访问知识库。在那里,可以找到所有图形的 TeX 源代码和 pdf。

1.3.包装

我们将使用 pgfplots 来绘制经济图表,因此应该调用 pgfplots 包。这也将调用 TikZ 和 xcolor。因为我们希望能够对曲线下的区域进行着色,所以应该调用 fillbetween pgfplots 库。因为我们希望能够指定坐标周围的位置,并在 TikZ 中使用各种箭头,所以也应该调用定位和 arrows.meta TikZ 库。此外,由于我们希望能够使用 LaTeX 的数学模式,我们需要调用 amsmath 包。总的来说,我们在文档的开头有以下代码片段:

出于个人喜好,我们将使用 Times New Roman 12 号字体,行距为 1.25 倍(相当于 1.5 倍行距)。因此,我们将使用此文档序言:

2.概述轴

首先,我们需要画出绘制经济图表的坐标轴。由于 pgfplots 是 TikZ 的依赖项,我们需要用\begin{tikzpicture}\end{tikzpicture}打开和关闭 TikZ 环境。在这个环境中,为了构建 pgfplots 环境,我们插入了\begin{axis}\end{axis}。既然我们希望经济图表位于页面的中心,那么所有这些都应该在中心环境中,用\begin{center}\end{center}包含代码。所以我们有:

对于 axis 环境,参数影响整个图形(即缩放比例、轴刻度、网格线),而命令影响特定的绘图(即函数、线、坐标点)。从语法上来说,需要注意的一点是,参数用逗号分隔,而命令用分号分隔。

2.1.axis 环境

在开始绘制函数之前,我们需要指定我们希望显示笛卡尔平面的哪个区域。为了指定域,我们使用参数xmin = 0xmax = 10,其中 0 和 10 是域的最小和最大边界。类似地,为了指定范围,我们使用参数ymin = 0ymax = 10分别指定最小和最大范围界限。到目前为止,我们已经有了这个图形的基本轮廓代码:

运行这段代码会产生 Figure 2–1。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

**图 2–1:**x 轴和 y 轴,带勾和框。(图片由作者提供)

2.2.轴线方向

我们需要做一些修改,使其适合经济图表。图表框架的默认样式是框形,图的四周都是黑线。虽然这对于折线图和散点图(pgfplots 的初衷)很好,但经济图通常有一个 L 形框架。要改变框架样式,我们需要使用参数axis lines = style,样式可以是leftrightcenterboxnone。figure 2–2 显示了轴线的一些样式。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

**图 2–2:**左、中和右轴线。(图片由作者提供)

显然,我们需要使用axis lines = left作为参数来制作大多数经济图所具有的 L 形框架。此外,轴末端的箭头可以通过在axis lines的末端附加一个星号来删除,所以我们使用axis lines* = left

2.3.轴刻度、比例和剪裁

经济图表通常没有沿着轴运行的数字。这是因为它们中的大多数都是理论图表,并且不管涉及的数量级如何都适用。为此,我们需要删除沿 x 轴和 y 轴的线条和数字,它们分别称为轴刻度和轴刻度标签。实现这一点的参数分别是 x 轴和 y 轴的xtick = {tick numbers}ytick = {tick numbers}。在xtickytick参数后面的花括号中,我们放置了一个由逗号分隔的数字列表,我们希望这些数字沿着各自的轴运行。由于经济图表在很大程度上保持零在左下方,我们将把它保持在 x 刻度上xtick = {0}。因为我们不希望 y 轴上有轴记号,所以我们写ytick = \empty。空轴刻度数列表是一种特殊情况。因为如果花括号内没有任何内容,pgfplots 将使用默认的轴记号,所以我们需要用\empty指定它是空的。为了制作非空白的轴线——例如,具有特定经验值的图表——第 4 节:经验曲线涵盖了网格线和轴刻度。

考虑到页面上图形的大小,默认的大小太小了,不能在不弄乱可用空间的情况下展示太多的插图。为了稍微增加图形的大小,我们编写了参数\scale = 1.2。当然,如果你有一个更复杂或更重要的插图,可以使用更大的因子来创建更大的图形。另一方面,较小的因子可以用来创建较小的图形。1.2 的比例因子占据了大约三分之一的页面。

我们需要添加的最后一个参数是clip = false。通常情况下,pgfplots 会切断图形外部的所有绘图,这对于自动限制函数非常有用。但是,我们希望能够将标签放在图形之外,所以我们用这个参数禁用了这个特性。相反,我们将手动指示所绘制函数的域和范围。

2.4 轴标签

最后,在图表上绘制任何东西之前,我们必须考虑的最后一个问题是轴标签。将它们放在轴的末端比通常放在底部(对于 x 轴)和左侧(对于 y 轴)更方便,因为我们希望将该空间用于其他东西(如可变标签和尺寸线)。要在 x 轴和 y 轴的末端放置标签,命令采用以下形式:

提醒一下,分号应该在这些行的末尾,因为它们是命令,而不是到目前为止一直使用的参数。(第 3.5 小节:标签更详细地解释了该命令工作的原因。)为了演示,我们将分别使用$x$$y$的 x 轴和 y 轴标签。将美元符号放在 x 和 y 周围会将它们置于 LaTeX 的数学模式,允许我们编写 LaTeX 已知的数学表达式。

在考虑了所有这些因素后,我们最终得到了图 2–3:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

**图 2–3:**成品 x 轴和 y 轴,毛坯和 L 形。(图片由作者提供)

现在,图表已经准备好在其上绘制图形(例如函数)。产生图 2–3 的代码如下:

3.绘图、颜色和标签(例如无差别地图和预算约束)

有了轴,我们就可以开始在图上画画了。假设我们想用无差异曲线来解释为什么需求是向下倾斜的——这意味着需求量随着价格的上升而下降。更具体地说,我们想在商品 A 和 B 之间绘制一个无差异图,预算约束显示商品 A 的价格增加对应于商品需求量的减少。

3.1 绘图功能

首先,我们想画一条无差异曲线, U₁ ,定义为,

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这是通过以下命令完成的:

这是一个很长的命令。让我们把它拆成碎片。domain = min:max的 addplot 参数将绘制的函数限制在由最小 x 值、 min (在本例中为 0)和最大 x 值、 max (在本例中为 10)限定的范围内。类似地,restrict y to domain = min:max将函数限制在介于最小值最大值之间的范围内(在本例中为 0 和 10)。(将 y 限制在一个“域”中有点用词不当,因为 y 值集的正确数学术语是“范围”。)注意,我们需要定义域和范围的原因,如前所述,是因为我们禁用了 pgfplots 的裁剪特性。addplot 参数samples = n告诉 pgfplots 取一定数量的 x 值, n ,在这些值处计算函数并通过所有计算出的坐标画一条线。实际上,样本数量越多,绘制的曲线越平滑。最后一个 addplot 参数color = colour name不言自明。它将线条着色为颜色名称。颜色将在下一小节中详细介绍。在第 3.2 小节“颜色”中,有一个预定义颜色名称的列表,以及关于如何添加更多预定义颜色集和定义新颜色名称的解释。

转到花括号内的数学表达式,这是我们放置函数表达式10/(x^2)+1的地方。需要注意的是,传统的乘法并置符号(将数字和字母并排放置)会产生错误。相反,星号—“*”—只能表示乘法。否则,传统的计算机符号适用,加法、减法、除法和分组分别用“+”、“-”、“/”和“()”表示。绘制上述命令会产生 Figure 3–1。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

**图 3–1:**绘制好 A 和好 b 之间的无差别曲线。(图片由作者提供)

产生图 3–1 的代码是:

关于图 3–1 及其代码,需要注意的一点是 x 轴标签是 A ,y 轴标签是 B 。这是因为,在这个例子中,我们想表明商品 A 价格的增加会导致该商品需求量的减少。

自然,无差异贴图包含不止一条曲线。因此,让我们通过将第一条曲线向右平移一个单位和向上平移一个单位来绘制多条曲线。在变换符号中,这对应于(x,y)→(x1,y+1)。

U₁ 上相继运用这种变换产生了 U₂U₃U₄U₅

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

绘制所有这五条曲线会产生图 3–2。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

**图 3–2:**绘制好 A 和好 b 之间的差异图。(图片由作者提供)

图 3–2 中的无差异曲线由以下代码片段绘制:

请注意,每个函数的域限制是不同的。 U₁ 被限制为 A ∈ (0,10),而 U₂ 被限制为 A ∈ (1,10), U₃ 被限制为 A ∈ (2,10),以此类推。这是因为每条无差异曲线的方程都有不同的垂直渐近线,对于 U₁A = 0,对于 U₂A = 1,对于 U₃A = 2 等等。因为我们只关心在垂直渐近线右侧的方程的图形,所以我们将效用函数限制在垂直渐近线右侧的区域。figure 3–3 说明了这种域限制。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

**图 3–3:**限制无差异曲线的范围。(图片由作者提供)

现在我们已经绘制了无差异图,我们需要绘制响应商品 A 价格上涨的变化的预算约束。实际上,这意味着两条对角线:一条从正 B 截距到正 A 截距,另一条从 B 截距到减少的正 A 截距。这代表了在商品 B 的最大数量保持不变的情况下,由于价格上涨,商品 A 的最大购买数量发生了变化。此外,因为我们想显示在预算约束收缩后消费者决策的变化,这两个预算约束必须与无差异曲线相切。我们将使用 U₃A = 4.7 处与原始预算约束相切,使用 U₂A = 3.3 处与新预算约束相切。原始和新的预算约束分别计算为等式B= 9.16-1.02AB= 9.16-1.59A(这些是实际切线的近似值)。从逻辑上讲,这些预算约束的方程式可以预先计算或近似计算,可以手动计算,也可以用 WolframAlpha 或 Desmos 等软件计算。(比如用 WolframAlpha,输入tangent at x = 4.7 for y = 10/((x-2)^2)+3就能找到原来的预算约束方程,输出y = 9.14744–1.01611x。)使用我们之前用来绘制无差异曲线的 addplot 命令,我们可以绘制两个预算约束。

最后,为了让预算约束线在无差异图中脱颖而出,我们需要加粗它们。这是通过向 addplot 添加参数thick来实现的。预设的线条粗细有ultra thinvery thinthinthickvery thickultra thick——或者我们可以用line width = width参数定义自己的粗细,其中width以长度为单位。总的来说,预算约束行的代码片段如下:

将它们标绘在图上会产生图 3–4。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

**图 3–4:**在图表上绘制预算约束。(图片由作者提供)

3.2.旗帜

在前面的小节中,我们使用两种颜色——红色和蓝色——来绘制函数。你可能想知道我们可以使用多少种颜色,或者如何定义新的颜色。记住,我们称之为 xcolor 包是因为它扩展了 LaTeX 处理和使用颜色的有限基础能力。

LaTeX 预定义了 19 种默认颜色:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

(我在第 6.2 小节解释了如何制作这些彩色方块,生成红色方块的命令是\fcolorbox{black}{red}{\textcolor{red}{\rule{\fontcharht\fontX}{\fontcharht\fontX}}}。)

然而,如果这些颜色太有限,xcolor 提供了多种选项来定义更多的颜色。例如,dvipsnames 选项定义了另外 68 种颜色:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

需要注意的是,这些颜色名称区分大小写。意思是 color = violet 不同于 color = VIOLET,color = Violet 会抛出错误。

为了添加 dvipsnames 调色板,我们使用 dvipsnames 选项调用 xcolor 包,所以,\usepackage[dvipsnames]{xcolor}。如果在 pgfplots 之后调用 usepackage 命令,将会引发错误。这是因为 TikZ 默认调用 xcolor,与这个新的 usepackage 命令冲突。所以要确保在 TikZ 包之前调用 xcolor 包。或者,如果这不起作用,那么我们可以将参数xcolor = {dvipsnames}添加到文档类命令中。对我们来说,我们会用,\documentclass[12pt, xcolor = {dvipsnames}]{article}

除了 dvipsnames 之外,还有其他调色板,比如定义了 151 种颜色的 svgnames,或者定义了 317 种颜色的 x11names。此外,可使用命令\definecolor{name}{model}{variable 1, variable 2, variable 3}手动定义新颜色,其中模式为 rgb、HTML、cmyk 等。例如,浅棕色可由\definecolor {name}{rgb}{0.95, 0.95, 0.92}定义。

乳胶可以做很多颜色。我们可以给文本着色,使用背景色,并使页面颜色不同于标准的白色(尽管考虑到打印机的压力,这是不明智的)。其中大多数都超出了本指南的范围,因此不在讨论之列。第 8 节:资源中提供了资源,其中包括关于 xcolor 包的文档和指南。

3.3.绘制虚线部分

回到我们正在制作的无差异图和预算约束图,下一步是在上面画虚线。Pgfplots 有一个用于线图的命令,它会用一条穿过坐标的线将所有输入的坐标依次连接起来。原始决策点的坐标(原始预算约束与无差异曲线相切的位置)计算为(4.7,4.37)。因此,虚线将从 A 截距(4.7,0)到决策点(4.7,4.37),再到 B 截距(0,4.37)。执行此操作的命令如下:

正如所见,这条线经过的坐标应该列在花括号内。此外,因为我们希望这条线是虚线,所以我们包含了参数dashed。其他两个参数color = blackthick已经在前面的小节中介绍过了。它们分别将生成的线条涂成黑色,并使其线宽变粗。对(3.3,3.9)处的新决策点重复此过程,得到图 3–5。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

**图 3–5:**在图表上绘制虚线。(图片由作者提供)

3.4.绘制坐标点

方便的是,在图形上施加黑色坐标点非常类似于画一条线。事实上,为了制作散点图,pgfplots 使用了以前用于绘制连接线段的相同命令。然后添加两个参数。第一个是mark = *,在线连接的每个坐标处画一个实心圆。可以使用“*”以外的标记,如“x”、“+”、“|”、“o”(所有这些看起来都像代表它的字符),以及许多其他标记。其次,为了删除这条线,我们编写了参数only marks。此外,为了增加标记的大小,我们添加了参数mark size = 3。总之,我们有以下命令来绘制坐标点:

在图上使用此命令会产生 Figure 3–6。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

**图 3–6:**在图形上绘制坐标点。(图片由作者提供)

3.5.贴标签于

最后,为了完成这个图表,我们需要标注所有相关的部分。具体来说,我们需要在 A 截距和 B 截距、坐标点和函数端点附近放置字母。要标记图形的一部分,该命令采用以下形式:

该命令将在指定的坐标旁边放置一些文本,即标签。此外,标签在坐标附近的位置取决于位置,它可以是坐标的leftrightabovebelow。例如,要将标签 Qₐ 放在(4.7,0)下面,从原始决策点向下延伸的虚线与 a 轴相交的地方,我们编写以下命令:

我们之前在标注轴时已经使用了节点命令的一种形式。但是,请注意,轴标签的坐标点是不同的,因为它指的是轴的末端。使用标注命令来标注图形的其余部分,例如函数和坐标,我们最终得到图 3–7。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

**图 3–7:**商品 A 的价格上涨对商品 A 和商品 b 之间的预算约束和无差异图的影响。(图片由作者提供)

最后,我们完成了无差异图,解释了需求下降的原因。这是因为预算从 M 转移到m′q′ₐ少于 Qₐ 。更重要的是,我们用 LaTeX 来说明这一点!figure 3–7 由以下代码生成,结合了本节中涉及的所有元素:

4.经验曲线(例如市场均衡)

在本指南的开始,我假设我们正在处理一个空白图。也就是说,x 轴和 y 轴没有任何刻度,因为它们没有任何关联的值。这些类型的图表对理论模型很有用——适用于相关情况,无论数量级如何。然而,有时我们实际上有经验数据。在这种情况下,我们可能需要网格线,在网格线上绘制诸如函数、坐标、虚线等图形。需要做一些修改来将我们的“空白”图转换为服务经验值。具体来说,我们希望添加网格线,然后解决易读性问题,例如在图表中远离轴标签并在标签后面放置白色背景色以避免混乱。让我们转换一个市场均衡曲线,图 4-1,这样就能反映经验数据。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

**图 4–1:**小工具市场的市场均衡(空白图上)。(图片由作者提供)

Figure 4–1 中的元素已经在前面的章节中解释过了。它由以下代码生成:

4.1.主网格线和次网格线

首先,我们希望轴记号和数字沿着两个轴运行。这在第 2.3 小节:轴刻度、缩放和剪裁中有简要介绍,但我们在这里主要是讨论如何移除它们。提醒一下,控制刻度的轴参数是xtick = {tick numbers}ytick = {tick numbers}。我们希望它们是符合我们数据的数字,而不是只包含 0 或为空。在这种情况下,他们两个的滴答数集合将是{0,1,2,3,4,5,6, 7,8,9,10}。接下来,我们要在图表上添加网格线。我们仍然在使用轴参数,而不是命令。有用的是,有两种类型的网格线,主要网格线和次要网格线。主网格线从轴记号和数字开始延伸。同时,次要网格线从主要记号之间的较小记号延伸,并且没有与之关联的编号。他们都有不同的风格,因此可以传达更多的审美精度。要显示两个网格,包括轴参数grid = both。类似地,如果我们只需要一个网格或者不需要任何网格,我们将使用majorminornone的值——尽管默认情况下两个网格都不显示。要设置主刻度之间的副刻度数,使用轴参数minor tick num = number,其中数字是副刻度数。在这种情况下,我们希望在主要刻度之间有一个次要刻度,否则会太混乱,所以次要刻度的数量是 1。此外,我们希望主要网格线是实线,次要网格线是虚线。这样,半增量和全增量在视觉上是明显的。为了将主网格线和次网格线的样式改为实线和虚线,我们分别使用grid style = solidminor grid style = dotted。更多款式包括dashedthinthick。将网格样式定义为实心是多余的,因为这是默认的,但是我选择将其显式化。到目前为止,我们已经添加或修改了以下代码片段中的参数:

此代码片段在 x 轴和 y 轴上以 1 为增量添加从 1 到 10 的轴刻度。此外,它还显示次要网格线和主要网格线,将主要刻度之间的次要刻度数设置为一个刻度,并将主要网格线设置为实线,次要网格线设置为虚线。将这些参数用于空白市场均衡图,结果如图 4–2 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

**图 4–2:**在图形上添加网格线。(图片由作者提供)

啊!太丑了。虽然我们得到了我们正在寻找的轴记号和网格线,但这导致了该图可读性的其他问题。具体来说, QₑPₑ 的标签需要远离轴编号,轴标签应该稍微远离图形,并且 ESD 的标签需要具有白色背景以减少混乱。另外,原点有两个零——一个就够了。

4.2.分隔标签,给标签背景着色

从前一个问题开始,x 和 y 截距标签需要从图表中移走,以便为轴数腾出空间。回想一下标签的形式:

虽然在大多数情况下,位置可以很好地确定标注离坐标的距离,但有时我们希望增加这个距离。在这种情况下,可以用position = distance指定距离。因此,例如, Qₑ 的位置可以更改为below = 10pt。对 Pₑ 标签重复此操作,我们得到以下代码片段:

转到后一个问题。去掉 y 轴标签 0 和移动轴标签都可以通过改变图中显示的域和范围来完成。为了去除 y 轴上的 0,我们将范围的最小值定义为 0.01(任何非常小的数字都可以,尽管这取决于数据的数量级)。所以我们有ymin = 0.01作为轴参数。为了使轴标签远离,域和范围的最大值都应该扩展到 10.5(这也根据数量级而变化)。总之,我们有以下代码片段:

最后,我们想把图上标签的背景涂成白色。否则,我们将被穿过 ESD 的线卡住,这看起来不太好。这也涉及到一个相对简单的调整。我们将参数fill = white添加到标签的节点命令中。已更改标签的代码片段如下:

E 的标签也已使用上一段中解释的定位参数隔开 5 磅。这样做是为了避免用白色背景覆盖供需线。有其他方法可以解决这种不便。例如,因为 pgfplots 按顺序对这些图进行分层(后面的命令覆盖前面的命令),所以只需将标签的节点命令放在供应线和需求线曲线的上方即可。这样,供应和需求曲线的命令将在标签之后执行,并将覆盖白色背景。

综合所有这些调整,我们最终得到了图 4–3。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

**图 4–3:**小工具市场的市场均衡。(图片由作者提供)

因此,我们已经完成了将空白图表调整为带有刻度和网格线的图表,以便它可以容纳经验数据并表示特定值。图 4–3 的代码是:

5.阴影、箭头和图表并排(例如。供应增加,价格接受公司)

通过绘制曲线、直线和坐标点可以做很多事情。但有时我们需要指出特征或指出图上的区域。这可以通过画箭头和用颜色在图上画阴影来实现。让我们用这些工具来解释丰收悖论:为什么农民的收入在大丰收后会下降。丰收对应于市场均衡图中供应向右移动,此时需求相对缺乏价格弹性。这在 Figure 5–1 中进行了描述。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

**图 5–1:**丰收对农产品市场的影响。(图片由作者提供)

figure 5–1 是使用上述章节中已经解释过的工具绘制的。代码如下:

5.1.不透明度和透明度

现在,两条供给曲线都是同样不透明的红色。为了使原始供给曲线和新供给曲线变得明显,我们将使新供给曲线略微透明。我们通过向 addplot 命令添加参数opacity = opacity value来实现这一点,其中不透明度值是绘图不透明的百分比,范围从 0(完全透明)到 1(完全不透明)。使新曲线基本透明似乎表明它是新的。所以我们为它的 addplot 命令写opacity = 0.3。使新的供给曲线更加透明的结果如图 5-2 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

**图 5–2:**使图表上的新供应曲线更加透明。(图片由作者提供)

需要注意的是,xcolor 包——在 3.2 小节:颜色中提到过——提供了一种更简洁但可读性稍差的方法来使颜色透明。当使用一种颜色时,可以在颜色名称后使用一个感叹号,紧接着是不透明度值— colour name!opacity value。这个范围从 0 完全透明到 100 完全不透明。比如red!10多为透明,red!50为半透明,red!90多为不透明。(不过,如果我可以补充的话,那种“基本透明的红色”看起来更像鲑鱼。)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

5.2.用颜色在方框中添加阴影

既然我们已经区分了哪条供给曲线是新的(不仅仅是使用标签S′),我们还想指出代表农民总收入损失和总收入增加的区域。这样,我们可以用图表显示总收入的损失远大于总收入的增加。用颜色填充图形区域的命令采用以下形式:

在这个命令中,(A)、(B)、©。。。,是一个不定数量的坐标。命令用指定的颜色在由坐标定义的多边形中着色。看图,原来的均衡点是(5.5,7.67),新的均衡点是(7,3.67)。这意味着,对于损失区域,我们需要将坐标(0,7.67)、(0,3.67)、(5.5,3.67)和(5.5,7.67)定义的正方形涂成橙色。类似地,我们将由坐标(5.5,0)、(5.5,3.67)、(7,3.67)和(7,0)定义的增益区域涂成绿色。到目前为止,我们有以下代码片段:

因为我们希望这些彩色区域不引人注目,所以颜色大部分需要透明。这可以用与前面小节“5.1 小节:不透明度和透明度”中所示的绘制方法相同的方法来完成,或者通过 TikZ 的不透明度参数,或者通过 xcolor 的感叹号符号。所以图表中使用的片段是:

不幸的是,我们不能简单地将这些命令放到 axis 环境中。有几个注意事项需要解决,以便这些彩色区域不会干扰和重叠图表上的其他要素。因为 pgfplots 按顺序处理命令,所以后面的图会叠加在前面的图上,所以填充命令应该在每隔一个命令之前,否则彩色区域会覆盖图形的另一部分。此外,由于填充命令不能在轴参数之前,应添加轴参数axis on top。这可确保轴线不会被绘图和其他命令覆盖。彩色区域如图 5–3 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

**图 5–3:**给图上的区域着色。(图片由作者提供)

5.3。直箭头和弯箭头

虽然此图通过阴影区域的相对大小说明了大丰收,但它缺少关于这些阴影区域代表什么的信息。当然,读者可以从上下文中推断出橙色区域代表总收入损失,绿色区域代表总收入增加,但我们也可以使图表对他们来说更具可读性。所以让我们用弯曲的箭头标出阴影部分。此外,由于我们想说明供给曲线从原始曲线到新曲线的移动,我们可以画一个从原始曲线到新曲线的直线箭头。

从后者开始,因为这更容易,绘制箭头的命令采用以下形式:

该命令绘制一个从(A)开始到(B)结束的箭头。B 处的箭头尖端由arrowhead值指定。TikZ 可供选择的箭头有限,所以这就是为什么我们在 1.3 小节:包中加载了arrows.meta TikZ 库。有了这个库,我们可以访问更多的箭头,如TriangleCircleSquareDiamond等等,这些箭头都是自描述的。Triangle箭头最适合显示从原始供应曲线到新供应曲线的转变。

使用此命令,我们将从原始供应曲线的右侧到新供应曲线的左侧绘制一个箭头。这对应于箭头为Triangle的从(6.3,8.5)到(8.3,8.5)的箭头。因此,我们有以下代码片段:

虽然这可以完成工作,但是我们应该做一些改变来提高箭头的美观性和可读性。首先,如果箭头是几乎透明的红色,以显示它正在修改供应曲线,那就更好了。方便的是,这就像我们之前做的用颜色填充区域和着色图一样。我们将red, opacity = 0.3作为参数写入箭头的绘制命令。第二,默认情况下,箭头很小。我们想让供应曲线移动箭头稍微大一点。这稍微难一点。首先,我们用花括号将箭头值括起来,表示我们正在使用自定义箭头。于是我们有了-{Triangle}。然后,作为箭头值的参数*,我们添加参数length = 4mm, width = 2mm来修改箭头的尺寸。相应地,我们得到了供应转移箭头的最终代码片段:*

使用此命令在图表上绘制箭头,结果如图 5–4 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

**图 5–4:**从原始供给曲线到新供给曲线画一条直线。(图片由作者提供)

接下来,我们将标记收益和损失区域。简单地从 x 轴下方放一个垂直箭头到绿色区域会显得过于杂乱。我们将让标签 Q 、*Q′和一个箭头共享这个小空间。相反,让我们画一个弯曲的箭头,从轴的右下角指向绿色区域的中心,以逆时针方向弯曲,这样就可以避开 QQ′*标签。使用绘图命令绘制弯曲箭头,其形式为:

请注意,除了一处增加之外,这几乎与直线箭头的命令相同。在“到”后面的方括号内,参数指定了从起点坐标到终点坐标的角度,曲线的线应在该角度处绘制。它们以弧度表示,意思是从 0 度向右,到 90 度向上,到 180 度向左,到 270 度向下,再到 360 度向右,以及其间的一切。我事先发现,弯曲箭头的起始坐标是(10,-1.5),终止坐标是(6.5,0.7)。由于箭头应该从起点坐标向上弹出,并从右侧进入终点坐标,因此出角为 90 °,入角为 0°。所以产生指向增益区的弯曲箭头的代码片段是:

对损失区域、从顶部到橙色框的箭头重复此操作,并添加文本标签(在 3.5 小节:标签中讨论过),我们有以下代码片段:

将直箭头和弯箭头组合起来,就产生了 Figure 5–5。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

**图 5–5:**大丰收悖论:为什么农民的收入在大丰收后会减少。(图片由作者提供)

figure 5–5 由以下代码生成:

5.4.并排排列图表

在经济学中,通常将市场均衡图与接受价格的厂商图并列排列。这有助于显示市场的变化是如何决定单个企业的运营价格的。更一般地说,我们将使用这个例子来演示如何将图表并排放置。假设我们想说明市场均衡中的价格下降是如何导致个体农民的总经济利润下降的。这种情况下的取价图如图 5–6 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

**图 5–6:**丰收对个体农民的影响。(图片由作者提供)

上图完全是用前面几节已经介绍过的工具和技术制作的。它是绘图、坐标点、标签、颜色和透明度等的组合。figure 5–6 由以下代码生成:

我们希望市场均衡图(图 5-5)位于面板的左侧,而价格接受公司(图 5-6)位于面板的右侧。令人惊讶的是,这其实很简单。我们所需要做的就是在第一个 axis 环境之后创建另一个 axis 环境,但是仍然在同一个 tikzpicture 环境中。这将把两个图形重叠在一起。然后,我们将参数shift = (axis cs: x-shift, y-shift)添加到第二个轴环境,其中 x 和 y 位移是第二个图形将从其原始位置移开的量。因为我们想将第二个图(价格接受公司)移到第一个图(市场均衡)的右边,所以我们将使用 17 的 x 位移和 0 的 y 位移。所以我们有:

最后,我们需要进行调整以适应页面上的图形。由于图片的宽度大于页边距所允许的范围,我们告诉 TikZ 通过加宽图片框架来忽略页边距。为此,我们在\begin{tikzpicture}前和\end{tikzpicture}后写下\hspace*{-3cm},分别将图片的框架向左和向右加宽 3 厘米。总之,将所有这些放在一起会产生图 5–7。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

**图 5–7:**丰收对农产品市场(L)和个体农民®的影响。(图片由作者提供)

产生 Figure 5–7 的代码是:

使用命令和参数执行代码,分别生成图 1 和图 2 的图 5–5 和图 5–6。(同样,图 5–7 的完整代码可以在 GitHub 库https://github.com/jackypacky/pgf-econ-graphs上获得。)

5.5.曲线下区域的阴影

结束这一部分,我们将简要讨论如何对曲线下的区域进行着色,以及如何对两条曲线之间的区域进行着色,因为这在前面的示例中没有涉及。假设我们想用透明的蓝色在商品 A 和商品 B 之间的生产可能性边界下的区域涂上阴影,以说明存在的不一定有效的产出可能性。生产可能性边界如图 5-8 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

**图 5–8:**A 和 B 之间的生产可能性边界(无阴影)。(图片由作者提供)

产生 Figure 5–8 的代码是:

为了在两个函数之间的区域进行着色,我们使用了一个命令,其形式为:

其中parameters是修改 addplot 命令的参数,fg是函数的名称路径。对于参数,我们将使用blueopacity = 0.1来创建一个透明的蓝色,类似于我们在第 5.1 小节:不透明度和透明度中所做的。

然而,为了输入名称路径fg,我们首先需要预先定义名称路径。为了定义曲线的名称路径,我们将参数name path = label添加到 addplot 命令中,其中label是我们想要分配给它的名称。因此,我们将使用 addplot 参数name path = frontier作为绘制生产可能性曲线的命令。因为我们需要另一个函数,在这个函数之间我们可以对这个区域进行着色,并且因为我们想要对曲线下的区域进行着色,我们需要绘制一条由公式 y = 0 定义的新曲线,并将其命名为axis。对于这个新的轴曲线,我们添加 addplot 参数line width = 0pt使其不可见——因为我们不希望它出现在图表上。

总的来说,生产可能性曲线下区域的阴影如图 5-9 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

**图 5–9:**A 和 b 之间的生产可能性边界

figure 5–9 由以下代码生成:

6.轴尺寸线、标签中的换行符和图例(例如消费税)

继续我们关于增强图表可读性的讨论,现在让我们在图表上加上尺寸线和图例。我们可以添加一个图例来显示每个区域所代表的内容,而不是在图表上放置一个标签来指向彩色区域。此外,为了表明两个轴标签之间的差异和含义,可以使用尺寸线。尺寸线是在图表、图形和绘图中用来表示大小、数量和距离(尺寸)的线。在尺寸线的每一端,都有一条垂直线与之相连,就像“T”一样。(尺寸线看起来有点像这样:“|—–|”。)

我们将使用消费税图表来说明尺寸线的用途。假设我们想说明汽油消费税的税收归宿。图 6–1 显示了这一点。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

**图 6–1:**汽油消费税的影响。(图片由作者提供)

产生图 6–1 的代码是:

6.1.在标签中添加尺寸线和换行符

添加尺寸线的方式与绘制箭头的方式相同。回想一下,箭头命令采用以下形式:

我没有告诉你的是箭头可以被附加到箭头的末端和起点。因此,尾部和头部带有箭头的箭头的形状为:

尺寸线使用箭头“|”,这是对箭头外观的一种很好的符号描述。我们有:

Qₑ 和*q′的区别在于征收消费税后消耗的气体量减少。为了在图上进行说明,我们将从q′的底部到 Qₑ 绘制一个尺寸指示器。在此之下,我们将添加一个带有文本“消费减少”的标签由于q′*处于 Q = 4.14,而 Qₑ 处于 Q = 5.05,我们有:

我们想对消费者事件——在 Pₑ 和*p′*之间——和生产者事件——在 PₛPₑ 之间重复这一点。然而,简单地复制上面的内容是行不通的,因为标签宽度会太大。相反,我们需要在两个标签中的两个单词之间插入一个换行符。这是通过添加参数align = left来告诉堆叠的文本应该如何对齐(左对齐、右对齐或居中),并通过添加两个反斜杠—“\ \”—在我们希望换行的地方来完成的。因此,消费者和生产者事件的尺寸线和标签代码为:

添加一条从 Pₛ 到*p′*的尺寸线来表示总税收,并添加到目前为止提到的所有尺寸线和标签,结果如图 6–2 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

**图 6–2:**向图形添加尺寸线。(图片由作者提供)

6.2.添加图例

使用图例,我们可以指定图形上的区域 ABCF 代表什么。添加图例结合了上一小节中使用的几个命令和参数。虽然 pgfplots 有自己内置的图例功能,但使用起来相当笨拙。相反,我们将使用一个文本标签,并将其放在图表的右上角。标签应该允许换行符,并有一个黑色的边框。因此,我们将使用:

回想一下,向节点命令添加参数align允许在标签中使用换行符——使用\\。此外,由于对齐方式设置为左对齐,文本行将向左对齐。这些在前面的小节中已经介绍过了。然而,新的是draw节点参数,它在标签周围画了一个黑色边框。在该命令中,图例的左上角将位于(10.5,10),这对应于图形的右上角,也就是我们希望图例所在的位置。添加描述这些区域代表什么的文本,我们有:

使用上面的节点命令向图表添加一个图例,我们得到 Figure 6–3。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

**图 6–3:**向图表添加图例。(图片由作者提供)

照目前的情况来看,这个传说看起来有点无聊。为了提高图例的美观性,我们可以给字母 ABCF 添加一个彩色背景,以便在视觉上将图例与它们所指向的彩色区域联系起来。如果 A 被涂成非常透明的蓝绿色, B 被涂成非常透明的紫罗兰色,以此类推,效果会很好。

幸运的是,xcolor 包有一个命令可以做到这一点。向文本添加彩色背景的 xcolor 命令采用以下形式:

在该命令中,框架颜色是环绕彩色背景边缘的颜色。背景色是文本后面的颜色。对于图例中的 A 文本,因为我们希望它的背景是非常透明的蓝绿色,所以我们需要的命令是:

请注意,该命令没有以分号结尾,这是因为它不是 TikZ 命令。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

虽然 A 看起来不错,但是背景颜色的宽度是由其中文本的宽度决定的。因此,当我们对不同宽度的文本使用多种背景颜色时,遇到了一个障碍。例如, M 的背景颜色比 l 的背景颜色要宽。除了彼此,不一致的宽度是显而易见的。为了解决这个问题,我们将使用 makebox LaTeX 命令(\makebox[width]{text}),宽度为“X”,高度为(\fontcharht\fontX`)。这意味着我们将用来为字母制作彩色背景的命令是:

如果不把它分成几个部分来处理的话,看起来会让人不知所措。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

你可能不熟悉重音符“”。在传统的美式键盘上,它位于左上角。它在 LaTeX 中有多种用途,包括引号和给字符添加重音符。(例如,使用`a`给“a”添加重音符会产生“à”。)这样, M 正好和 l 一样宽。不幸的是,控制高度要困难得多,我将在本指南中跳过它。如果我们想要一个没有文字的彩色框,我们可以使用:

运行上面的命令产生一个透明的蓝绿色的盒子。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这是因为\rule{width}{height}产生了一个矩形的墨水滴。顺便说一句,这就是我在本指南中介绍颜色的方式。最后,因为添加图例通常会使图表的宽度大于页边距所允许的宽度,所以我们在\begin{tikzpicture}之前和\end{tikzpicture}之后添加了\hspace*{-3cm}。当我们遇到同样的问题时,我们也在 5.4 小节:并排排列图表中这样做了。

总的来说,我们有以下图例的代码片段:

将此添加到图表中,我们得到图 6–4。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

**图 6–4:**汽油税对消费、生产和总税收的影响。(图片由作者提供)

还有维奥拉。我们添加了尺寸线和图例,进一步传达了图表不同部分的含义。现在,任何人看到这张图表都会明白,消费者承担了大部分税收负担和汽油消费税的盈余损失——以及原因。(假设供给相对价格弹性,需求相对价格弹性。)产生图 6–4 的代码是:

7.结论

有了这个工具箱,你几乎可以创建任何经济图表。学习 pgfplots 和 LaTeX 的努力是值得的。一方面,大多数文字处理器,如 Google Docs(带 Google Sheets)和 word(带 Excel)可以提供经济图表的必需品——轴、线、曲线和标签——PGF plots 具有更大的灵活性,从阴影区域到添加虚线,再到添加尺寸线。一切都很有型。

当然,既然是乳胶,这只是冰山一角。每个人都站在巨人的肩膀上。首先,Donald Knuth 创建了 TeX 来排版文档。然后 Leslie Lamport 创造了 LaTeX 作为 TeX 的标记。然后 Till Tantau 在这个基础上创造了 TikZ 来创造图形。在这一系列事件之后、之前和之间,还有许多其他对我们所站的巨人做出贡献的人。最后,我们偶然发现,我们可以为经济学创造美丽而流畅的图表。

然而,我们总是可以做得更多。例如,我们可以通过定义变量和自动计算交点来自动创建图形。但是,这超出了本指南的范围。

如果我成功地完成了我的工作,你已经部分地扩展了 LaTeX 在制作图表这一领域的著名学习曲线。如果我没有,你就被抛弃了,没有任何关于如何开始你的提升的指导(至少,没有来自我的指导!).让我们期待前者。

8.资源

【LaTeX 入门和学习

  • 德州直播:http://www.tug.org/texlive/。这是一个安装 TeX、TeXworks IDE 和一些普遍存在的包(包括 pgfplots 和 xcolor)的门户。
  • 背面:【https://www.overleaf.com/】T2。如果安装 LaTeX 是一项太大的任务,那么有许多在线 LaTeX 编辑器足以完成本指南。背面已经安装了 pgfplots 和 xcolor,所以您不需要担心这一点。在定价方面,他们提供了一个免费版本,其中包含了所有的基本要素,只缺少一些专业功能,如 GitHub 集成或实时协作。
  • 维基百科上的乳胶:【https://en.wikibooks.org/wiki/LaTeX】T4。本指南解释了 LaTeX 的基本工作原理。从安装 LaTeX 到制作文档,再到更多的技术方面,如盒子,Wikibook 都有解释和附带的代码。
  • TeX-LaTeX 栈交换:https://tex.stackexchange.com/。Stack Exchange 是一个论坛,人们可以在这里提问和回答问题。该分论坛专门处理包括 pgfplots 在内的 LaTeX。当然,如果你有一个问题,首先使用搜索功能来检查它是否已经回答过。然后,看规则,问吧。

Pgfplots 和 TikZ 导轨

包安装链接和文档

虽然上面的 LaTeX 编辑解决方案——TeX Live 和 over leaf——已经预装了这些包,但是如果你没有这些包或者想要最新的版本,你可以通过这些 CTAN 链接访问它们。此外,软件包手册可以在 CTAN 上找到,但要注意它们是数百页的技术文档。

  • https://ctan.org/pkg/pgfplots CTAN PGF plots 套餐:。这个 pgfplots 包安装链接也是 TikZ 附带的。
  • https://ctan.org/pkg/xcolor CTANxcolor 套餐

9.其他示例

同样,本指南中所有完成的图表 PDFs 和代码——都可以在 GitHub 存储库中找到,包括下面的例子。该知识库可以在 https://github.com/jackypacky/pgf-econ-graphs访问。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

**图 9–1:**租金管制对租赁市场的影响。(图片由作者提供)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

**图 9–2:**小经济体关税对国内市场(L)和进口市场®的影响。(图片由作者提供)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

**图 9–3:**两个消费者 A 和 B 以及两种商品 x 和 y 之间的市场均衡 W′(图片由作者提供)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值