需要在仪表板上强调变化吗?忘记条形图,用哑铃图代替!
哑铃图易于构建和理解,对于商业受众来说是一种很好的替代图表类型。
尼科埃尔尼诺在 iStockphoto 拍摄的图像
在设计仪表板和其他数据可视化时,图表类型的选择是一个关键的工具,你可以用它来让你的观众关注最重要的信息。尤其是仪表盘,信息丰富。减少你的听众提取关键信息所需的工作量会使他们更加有效和强大。
条形图是数据可视化的主要工具。它们是所有图表类型中最容易理解的,并且能够准确无误地描述变化(假设坐标轴总是从零开始——你只会犯一次新手的错误!反馈是一种天赋,对吗?!).
在仪表板中,任务通常是比较数值——今年与去年,实际值与目标值,等等。大多数软件工具引导我们使用的默认图表类型是簇状柱形图。这是一个不错的选择,但是随着类别数量的增加,它会很快失控…
簇状条形图虽然继承了条形图的许多优点,但并不是比较数值的最有效的图表。为什么?考虑上面的图表,为了了解今年与去年相比的情况,我需要根据每个条形的长度读取条形的值,将其与条形对进行比较,并保留该信息,同时对下一个条形对、下一个条形对等等进行同样的操作。很快我就要记住 24 个数字和 12 个不同点。我不知道你怎么想,但我努力在脑子里记住这么多信息。更不用说处理它来理解是否有某种模式。
我需要的是一个图表类型,重点是在价值的差异。我可以画出这两个值之间的差异,但是如果绝对值对观众很重要,而我们又不想失去这些信息,该怎么办呢?在这里,哑铃图表成为一个伟大的选择。
对商业和非技术受众使用替代图表类型总会有一些风险,但幸运的是哑铃图表不会对大脑造成太大的压力。哑铃图表的伟大之处在于,它允许你:
- 保留关于绝对值的信息
- 垂直轴从非零值开始(意味着您可以突出显示微小的变化),并且
- 通过连接这些点,观众本能地关注它们之间的距离(这是你的图表的要点!).
有什么不喜欢的?但是如果您坚持使用不提供这种图表类型的 Excel 呢?没问题,它们构造起来超级简单。
循序渐进
- 首先用标记构建一个折线图。
2.增加标记大小,删除连接线,并根据您的意愿设置样式。我喜欢直接标记系列,而不是使用图例,因为这样更有效。
3.现在插入高低点线,方法是选择一个系列,然后在功能区的图表工具>设计部分单击“添加图表元素”。悬停在“线”上将显示“高低线”选项。点击就大功告成了!
4.晒晒你的老板和同事的赞赏吧!
想更刺激一点吗?
通过直接标记标记而不是使用轴来获得极简的外观。看啊…
这里没有文本框…使用数据标签对你有利。添加数据标签,使用“中心”标签位置,并根据需要设置它们的样式。
你从来不知道你可以像那样使用数据标签,是吗?!
需要为您的 Python 分析准备一个演示站点吗?想想 Heroku+Flask/Dash
从 Hello World 开始的初学者演练
有一天,当我为一个概念验证项目制作一个数据科学 web 应用程序时,我意识到如何部署该应用程序并不一定是显而易见的。我希望该应用程序能够:
- 全天候运行并提供演示,
- 呈现在任何可通过互联网访问的设备上,以及
- 不像生产应用程序部署那样需要太多的成本和精力。
我最终发现使用“Heroku”以及 python 的 web 框架库(如“Flask”或“Dash ”)最符合我的预期。
在这篇文章中,我将总结一下我当时学到的东西:我们如何配置 Heroku+Flask/Dash app,从 Hello World 到一个专业的例子。
目录:
- 你如何为你的 Web 应用建立演示环境?
- 烧瓶和 Dash 是什么?
- 应用程序部署练习 1:仅限烧瓶——向自己问好
- App 部署练习 2:Heroku+Flask——向世界“你好世界”
- 应用部署练习 3: Heroku + Dash —显示带有动态过滤的 Iris 数据表
- 应用部署练习 4: Heroku + Dash —专业示例
- 结尾注释
1.你如何为你的 Web 应用建立演示环境?
假设您刚刚在 Python 上构建了一个 web 应用程序作为 PoC 工作(因此,作为一名数据科学家,我在这里设想了一个数据科学应用程序(例如,data dashboard),但可能同样适用于任何应用程序),并希望在许多演示机会中演示它,例如讲台演示、投资者推介或网络聚会。
作者用免费插图网站“IRA suto-ya”(【https://www.irasutoya.com/】T4)的材料制作的插图
您所期望的是:
- 你想在任何设备上看到它,以便你的同事或客户访问该应用程序并四处游玩,
- 您希望它全天候运行,并且
- 尽管如此,因为它不是一个生产应用程序,而是一个概念验证阶段,所以您希望在部署它时尽量减少开发本身的额外工作。
现在,让我们看看 web 应用程序部署中的三个可能的例子。
1.1。将应用程序保存在您的电脑上
作者使用免费插图网站“IRA suto-ya”(https://www.irasutoya.com/)的材料制作的插图
在这里,您在 PC 上开发应用程序,并在同一台 PC 上进行部署(类似的案例研究在下面的“应用程序部署练习 1:仅 Flask—“Hello World”to Yourself”中)。
优点:
- 应用程序开发后没有额外的操作。
缺点:
- 除了你没有其他人可以进入。
在这种情况下,你的客户体验应用程序的唯一方式就是你从你的电脑上展示它。
1.2。准备一台服务器 PC 并在其上部署应用程序
作者使用免费插图网站“IRA suto-ya”(【https://www.irasutoya.com/】)的材料和 GitHub 的官方徽标制作的图形
在这种情况下,您需要准备自己的服务器(本地或云实例)并在其上部署应用程序。
优点:
- 高灵活性和对部署环境的控制。
缺点:
- 必须自己管理两个机器环境。一个部署将需要,从您的开发环境推送到存储库,从存储库拉到服务器,在服务器上配置和运行它。
- 维护服务器的额外成本。
**在这种类型中,您花费更多的时间和金钱,但对部署环境有更高的控制力。**您可以授予外部用户访问权限,也可以阻止其中一些用户。
但是,由于这种类型是针对生产架构的,如果您只想在较短的时间内演示您的 PoC 工作,而不是针对永久运行的生产线,这就太多了。
1.3.使用 heroku 进行部署
作者使用免费插图网站“IRA suto-ya”(https://www.irasutoya.com/)的材料以及 GitHub 和 Heroku 的官方标志制作的图形
这里我要介绍一下**Heroku**。****
Heroku 是一个基于网络的服务,用来取代以前架构的服务器部分。一旦它通过您的应用程序连接到您的 GitHub 存储库,它会自动从 GitHub 中提取您的模型,并将其部署到互联网上添加唯一的 URL,而您只需进行最少的配置(我们将在下面的应用程序开发练习 4–6中看到这一点!)应用程序的更新以同样的方式发生:一旦你将新代码推送到 GitHub 存储库,heroku 会检测到变化并自动更新部署。**
还有,对于非制作级别的 app,不用花钱!
优点:
- 比服务器选项更少的配置和维护负担。
缺点:
- 对配置的控制不如服务器选项。因此,不适用于生产应用程序。
2.烧瓶和 Dash 是什么?
我还要提一下【Flask】【Dash】,这些在 Python web app 构建中非常有用,我在下面的 App 部署练习中使用过。
Flask 和 Dash 的官方标志
****“Flask”是一个 python 库,提供微 web 框架,通过 python 编码支持 web 应用的开发。Flask 可以提供你用 python 代码编写的动态应用程序的 web 内容。由于 python 相对于其他语言的优势是易于机器学习建模,所以大量使用 Flask 来创建 web API 以返回 ML 模型预测。
Flask 很好地提供了动态 web 组件,但它本身在视觉上并不漂亮。为了使它更漂亮,我们需要 html 模板和 CSS 文件都编码良好。
" Dash "是另一个让美化视觉更容易的 web 框架。它在 python/R/Julia 中可用,构建在 Flask、plotly 或其他库之上。与 Flask 相比,它更专注于数据科学仪表板 UI,并提供了许多内置道具,使应用程序更漂亮。
仪表板上的数据仪表板示例(https://dash-gallery.plotly.host/dash-oil-and-gas/)
3.应用程序部署练习 1:仅限烧瓶——向自己问好
3.1。安装烧瓶。
3.2。准备 app_helloworld.py 代码。
3.3。运行 app_ hello world. py .**
3.4。去网络浏览器找到“你好世界!”。
在这里,我们现在尝试在您的本地机器上运行一个简单的 Flask 应用程序。如果你的应用不能在本地运行,它将永远不能在 heroku 上运行!该应用程序将显示“Hello World!”在你的浏览器上。
3.1.
首先,通过在终端上运行以下命令,在本地计算机上安装 Flask:
**$ pip install Flask**
3.2.
接下来,准备一个名为 app_helloworld.py 的. py 文件,如下所示:
“app”是 Flask 类的一个实例,它在代码中充当 WSGI 。@app.route('/')
是另一个告诉 Flask 哪个 URL 将触发函数index
的神奇代码。函数index
将打印“你好,世界!”。最后两行定义了当我们通过终端运行这个文件时的行为。
进一步的解释可以参考烧瓶快速启动页面。
3.3.
最后,在您的终端上运行以下程序:
**$ cd <path where you placed app_helloworld.py>
$ python app_helloworld.py**
您将在终端上看到这一行。
3.4.
转到您的 web 浏览器,在 URL 单元格中键入127.0.0.1:5000
(显示的 IP 地址)或localhost:5000
。然后,你会看到这个:
成功!
但是,你必须记住一些缺点:
127.0.0.1
是私人 IP 和《Hello World 仅在您的电脑上可见。你永远不会在你的智能手机或客户的机器上看到同样的东西。- 将你的电脑关机或甚至进入睡眠模式将会或可能会停止应用程序。你必须重新运行代码来保持“Hello World!”活着的东西。
这就是为什么我们需要后端服务器功能来在任何机器上全天候供应应用程序。“heroku”将帮助我们做到这一点。
4.App 部署练习 2:heroku+Flask——向世界“你好世界”
4.1.确保你能运行“Hello World!”否则,请回到上一节。
4.2.准备另外两个关键文件:“requirements.txt”和“Procfile”。
4.3.做一个 GitHub 库,推送准备好的文件。
4.4.配置 heroku 在上面运行和部署应用程序。
4.5.去(任何)网络浏览器找到“你好世界!”。
4.6.(可选)查看当您将代码推送到 GitHub 时,app deploy 是如何更新的。
我们现在想要做的是将应用程序移动到 heroku 服务器,并保持它全天候(而不是本地)运行(不仅仅是在您的 PC 打开时。)
4.1.
作为先决条件,确保你的 hello world 可以在你的本地电脑上运行。如果没有,请回到上一节。
4.2.
接下来准备两个关键文件:“requirements.txt”和“Procfile”。
“requirements.txt”在您为虚拟环境准备它时具有相同的用途。在“你好世界!”例如,我们需要一个烧瓶,但要添加另一个名为“gunicorn”的烧瓶。
你还记得当你在本地运行 Flask 时得到一个错误信息吗?
**WARNING: Do not use the development server in a production environment.
Use a production WSGI server instead.**
使用 gunicorn 是一种解决方案。它将作为 WSGI 服务器工作。
那么,现在 requirements.txt 应该是这样的:
**Flask==1.1.1
gunicorn==19.9.0**
你还需要一个名为‘Procfile’(没有扩展名)的文件来告诉 gunicorn 哪个文件作为应用程序运行。我不解释太多如何写这个文件和你可以参考他们的文件的细节。在我们的 hello world 示例中,以下是“Procfile”中的行:
**web: gunicorn app_helloworld:app**
app_helloworld 是的名称。py 文件在这里。如果您使用不同的名称,请调整它。
4.3.
现在,您准备一个新的 GitHub 存储库,并推送上面准备的三个文件。这是我的例子。
4.4.
现在我们将配置 heroku 环境。
首先创建 heroku 账号。我将跳过如何到这里。
接下来,点击“创建新应用程序”按钮。
决定你的应用程序名称,无论你喜欢什么,但它必须是全球唯一的。然后点击“创建应用”并继续。
选择 Deployment method=GitHub,用你的 app 连接到你的 GitHub 库。
查看您的应用程序是否成功连接到 GitHub 存储库。接下来单击“启用自动部署”,然后单击“部署分支”。
当 heroku 尝试部署时,您会看到一些日志消息。你可以找到烧瓶和 gunicorn 是否安装在 heroku 容器中。
4.5.
当部署成功时,您将看到一个按钮“查看”。当你点击它,一个网页将开始,你可以发现该网页是在唯一的网址。
此 URL 已经全球可用。我可以在手机上看到。
而且只要 heroku app 还活着就可以用。
4.6.
或者,你可以检查当你把你的编辑推送到 GitHub 时,应用程序自动更新的程度。
让我们制作“你好,世界!”到“你好世界!已更新”在您的 app_helloworld.py 中。
一旦你被推送到你的库,heroku 上的自动应用构建就会开始。
构建完成后,您可以刷新页面并立即看到更新后的部署(或在浏览器上经过 TTL 时间后)。
为了更新应用程序,我只将新代码推送到 GitHub。现在,你可以认识到这是多么容易!!
5.应用部署练习 3: heroku + Dash —显示带有动态过滤的 Iris 数据表
5.1.确保您成功运行了之前的“Hello World!”heroku 上的 app。
5.2.准备一个新的。使用破折号的 py 文件。
5.3.编辑配置文件。
5.4.推到 GitHub,等待 heroku 建造。
5.5.在浏览器上打开 app,开心就好。
这个练习不仅仅是 hello world!我们将在 web 应用程序上显示著名的“虹膜数据表”,并允许它根据虹膜类名进行过滤。
5.1.
像往常一样,确保我们都擅长前面的练习。否则,返回并成功运行前面的内容。
5.2.
接下来,准备一个新的。使用破折号的 py 文件。我的例子是一个显示著名的虹膜数据的前 10 行的应用程序,它还允许根据 web 查看者在 web 浏览器上选择的类型来过滤特定的虹膜类型。我的新文件“app_dash_basic.py”在这里:
带破折号的代码在新生眼里看起来很复杂,但是逐行查看,就不一定了。特别是,当你有一些关于 html 的知识时,你会发现它使用了很多 HTML 元素。
5.3.
配置文件需要一些编辑。
就因为我的代码用了破折号和熊猫,requirements.txt 如下:
**Flask==1.1.1
gunicorn==19.9.0
dash==1.12.0
pandas==0.25.1**
Procfile 为:
**web: gunicorn app_dash_basic:server**
注意它使用的是:server
而不是:app
。
不要忘记下载 iris.csv 文件并将其放在本地存储库中。
5.4.
接下来和以前一样,把他们推到 GitHub,等待 heroku 的部署。
********
5.5.
瞧啊。我们现在得到了我们想要的!该应用程序显示 iris.csv 中的前十条记录,还允许动态更改过滤器。
我们可以用相同的网址在你的手机上播放完全相同的歌曲。
6.应用部署练习 4: heroku + Dash —专业示例
6.1.在远程存储库上克隆目标存储库。
6.2.进行相同的部署练习。
6.3.享受 pro 应用程序!
上一节中的 Iris 示例有些廉价,没有展示 Dash 的全部功能。
在这一部分,让我们来看一看你可以在官方 Dash 应用程序库中找到的一个专业示例。
我选择了药物发现的例子。
6.1.
只需找到包含您的目标代码的存储库,将其克隆到您的远程存储库中。只要你只是使用现有的代码,这是一个显而易见的。
********
6.2.
这取决于你在 heroku 中使用的最大应用数量的支付选项,但最有可能的是你删除旧的应用并创建一个新的应用,因为一次只能有一个应用处于活动状态。
接下来,你只需做同样的练习:将 app 连接到你刚刚克隆的 GitHub 库,按下两个按钮,然后等待。
6.3.
给你!
当然,你也可以在手机上享受同样的乐趣。
7.结尾注释
当您想在 PoC 阶段制作一个演示 web 应用程序时,您可能希望它可以通过互联网 URL 获得,但是准备服务器或云实例太多了。“Heroku”是一个强大的选择,可以最大限度地减少这种型号开发的额外成本和负担。
一旦你把你的模型推送到 GitHub,Heroku 会自动检测其中的更新,并更新部署。同样,从金钱的角度来看,小规模部署在 Heroku 是完全免费的。
通过减少部署阶段的成本和工作,您可以将更多精力放在模型/应用程序开发上。
伦敦国际学生的邻里搜索
使用 K-Means 聚类算法的数据科学项目
布鲁内尔大学,威盛, flickr (CC BY-NC-ND 2.0)
凭借其世界公认的大学、多元文化的环境和高技能的学术人员,英国在国际学生中的受欢迎程度持续上升。根据高等教育统计局 2018/2019 年的统计数据,485,645 名国际学生在英国攻读学位,其中 125,035 人在首都伦敦学习。
搬到伦敦继续他们的学业,这些国际学生有关于住宿的疑问-一个重要的是“我应该考虑哪个社区的住宿?”在英国找到合适的住房并不容易。尤其是在最受欢迎的大学所在的城市,这使得学生住宿有些繁琐,而且费用昂贵。学生们更喜欢住在离他们的大学近的地方,住在公共交通方便的地方。当我来到伦敦帝国理工学院商学院攻读硕士学位时,我也有同样的问题:哪些街区离大学很近?邻里关系好吗?
在这个项目中,我将通过探索特定大学附近的社区来解决这个问题。该解决方案是为前往伦敦求学的国际学生设计的。该代码可在 GitHub 中获得。
这个项目是从技术背景过渡到数据科学领域的踏脚石,它利用了通过 IBM 数据科学认证课程获得的知识。
数据要求
对于这个项目,我将使用以下数据:
- 伦敦大学/机构的地理位置数据: 英国学习提供商提供英国所有大学的数据及其地理位置。从这里,我将只提取伦敦大学的这个项目。
显示伦敦学院分布的伦敦地图
- **伦敦邮政编码:**所有伦敦邮政编码及其区和地理数据的列表可在 Doogal.co.uk获得我需要获得邮政编码的前缀,并将它们分组。
- **公共交通无障碍水平数据:**伦敦交通局(TFL-伦敦交通局)开发了一种工具,用于评估伦敦各地公共交通的无障碍水平。公共交通可达性水平(PTAL)评估了到交通站点和车站的步行距离水平,包括测量交通服务水平和更广泛的连通性。这个地区的数据可以在伦敦数据库中找到。每个区域的得分在 0 到 6b 之间,0 分表示公共交通非常差,6b 表示公共交通非常好。
将 PTAL 分数添加到邮政编码数据集中:
- 距离研究所 5 公里以内的街区: Postcodes.io 提供免费 API,调用该 API 时,会生成一个 JSON,其中包含给定邮政编码 5 公里以内的 outcodes(邮政编码区)数据。我将使用 API 来获取邻域的输出代码。
我已经从我们之前的数据集中选择了第一个学院“皇家兽医学院”,以探索其附近更多的场馆。这所大学方圆 5 公里有 38 个不同的区号。将邮政编码数据添加到机构数据中:
皇家兽医学院周围的 38 个街区
- **附近的场馆:**这个数据是使用 Foursquare API 获得的。利用一个地区的地理位置,我们探索每个社区的顶级场馆。确定了学院周边 38 个区号的 2344 个场馆。对应区域代码的 PTAL 分数得出以下数据集:
方法论
**场馆类别和 PTAL 的一键编码:**使用一键编码技术为分类变量-场馆类别和 PTAL 分配数值;
然后将编码后的数据集按“区号”分组,得到 38 个区号中每一个的场馆类别和 PTAL 的含义;
获取每个区域代码中排名前 10 的场馆,这将有助于学生了解该区域代码中排名靠前的场馆类别。
K-均值聚类
我想根据场地类别和 PTAL 将我们的区号分组,因此我们使用 K-means 建模技术来得到我们的聚类。
**寻找用于训练数据的最佳 k 值:**我正在使用肘方法来获得最佳值。下面的第一个图告诉我们,肘点是 3(左),然而,为了证实它,我们创建了另一个图,直到值 6(右)。
使用 k-means =3,将模型拟合到以下数据:
结果
皇家兽医学院周围的 38 个区号分为三组,如下图所示:
- 蓝色-最高连接性(6b)
- 洋红色-良好的连接性(6a)
- 橙色-中等连通性(其他)
第一聚类具有最高 PTAL 分数(6b)的邻居
第二个聚类具有良好的连通性得分(6a)
最终集群具有中等连通性(3,4,5)
聚类技术主要根据 PTAL 评分将社区分为 3 类,该评分评估了到交通站和车站的步行距离水平,包括测量交通服务水平和更广泛的连通性。
基于邻域的连通性对其进行聚类,聚类 0 和 1 比聚类 2 具有最高(6b)和良好(6a)的连通性,聚类 2 具有中等连通性。
一个代表皇家兽医学院周围的邻域分布的条形图清楚地显示了大多数邻域具有最佳的网络连通性,并且将是最可取的位置。
进一步看附近列出的场馆;我们看到大部分街区在饮食点方面分布得很好。基于这些分组,学生可以选择他们喜欢的社区。一个健身爱好者的学生,想住在一个连通性更广的街区,可以在 0 群的 neighborhoods -NW1 3,W1T 7 搜索住宿。
结论
虽然这个项目是为了帮助皇家兽医学院的学生/学生,但也可以扩展到其他学院。该方法不限于机构,也可用于任何探索某个位置(如办公室附近)附近社区的人获得相同的结果。
通过考虑更多因素以获得更精细的输出,可以进一步完善该项目,例如取决于房屋类型的平均租金、每个街区的犯罪率、与大学的平均距离。将这些因素考虑在内将有助于学生确定准确的邻域。大多数学生更喜欢住在离学校近、连通性好、安全的社区。
使用 Foursquare API 进行邻域分割和聚类
在印度海得拉巴建立购物中心
谁说金钱买不到幸福,谁就不知道去哪里购物。
介绍
对于许多购物者来说,在周末和假期逛商场是放松和享受的好方法。房地产开发商也在利用这一趋势建造更多的购物中心来迎合需求。因此,海得拉巴市有许多购物中心,还有更多正在建设中。开设购物中心可以让房地产开发商获得稳定的租金收入。与任何商业决策一样,开一家新的购物中心需要认真考虑,而且比看起来复杂得多。特别是,购物中心的选址是决定购物中心成败的最重要的决策之一。
商业问题
这个项目的目标是分析和选择在印度海得拉巴市开设新购物中心的最佳地点。该项目主要关注海得拉巴市的地理空间分析,以了解哪个地方是开设新商场的最佳地点。该项目使用数据科学方法和聚类等机器学习技术,旨在提供解决方案来回答这个商业问题:在海德拉巴市,如果一家房地产开发商打算开设一家新的购物中心,你会建议他们在哪里开设?
数据
为了解决这个问题,我们需要以下数据:
·海得拉巴的街区列表。这确定了本项目的范围,该范围限于泰伦加纳的首都海德拉巴市,该市位于印度南部
这些街区的经纬度坐标。这是绘制地图和获取场馆数据
所必需的,场馆数据,尤其是与购物中心相关的数据。我们将使用这些数据对邻近区域进行聚类。
数据来源和提取数据的方法
这个 维基百科页面 是海得拉巴的街区列表,有 200 个街区。在 Python 请求和 beautifulsoup 包的帮助下,我使用 web 抓取技术从维基百科页面中提取数据。然后,我们可以使用 Python 地理编码器包来获取街区的经纬度坐标。之后,我使用 Foursquare API 来获取这些街区的场地数据。
Foursquare API 将提供许多类别的场馆数据,为了帮助我们解决业务问题,我们对购物中心类别特别感兴趣。这是一个将利用许多数据科学技能的项目,从网络抓取(维基百科),使用 API (Foursquare),数据清洗,数据争论,到机器学习(K-means 聚类)和地图可视化(foylus)。
方法学
Foursquare API 允许应用程序开发人员与 Foursquare 平台进行交互。API 本身是一个 RESTful 地址集,您可以向其发送请求,所以实际上没有什么需要下载到您的服务器上。
搜索海得拉巴的咖啡馆
四方返回的咖啡馆
在左边,你可以看到所有的咖啡馆和它们的名字,类别,海得拉巴每个地点的地址。在右边,你可以看到左边的场馆地图。
探索咖啡馆
如果你点击第一个是咖啡杯,那么你会被重定向到这个页面,在这里你会看到 Foursquare 数据集中关于咖啡杯的所有信息。这包括它的名字,完整的地址,工作时间,提示和用户发布的关于咖啡馆的图片。同样,你也可以探索海得拉巴市的购物中心。
要探索 Foursquare,点击 此处
网页抓取
使用 Python 请求和 beautifulsoup 包执行抓取,以提取邻居数据列表。
# Send the GET requestdata = requests.get("[https://en.wikipedia.org/wiki/Category:Neighbourhoods_in_Hyderabad,_India](https://en.wikipedia.org/wiki/Category:Neighbourhoods_in_Hyderabad,_India)").text# Parse data from the html into a beautifulsoup objectsoup = BeautifulSoup(data, 'html.parser')# Create a list to store neighbourhood dataneighborhoodList = []# Append the data into the listfor row in soup.find_all("div", class_="mw-category")[0].findAll("li"):
neighborhoodList.append(row.text)# Create a new DataFrame from the listkl_df = pd.DataFrame({"Neighborhood": neighborhoodList})
kl_df.head()
这是抓取数据后创建的数据框。为了能够使用 Foursquare API,我们需要获得经纬度形式的地理坐标。为此,我们将使用 Geocoder 包,它允许我们将地址转换为经度和纬度形式的地理坐标。
# Defining a function to get coordinatesdef get_latlng(neighborhood):
# initialize your variable to None
lat_lng_coords = None
# loop until you get the coordinates
while(lat_lng_coords is None):
g = geocoder.arcgis('{}, Hyderabad, India'.format(neighborhood))
lat_lng_coords = g.latlng
return lat_lng_coords# Call the function to get the coordinates, store in a new list using list comprehensioncoords = [ get_latlng(neighborhood) for neighborhood in kl_df["Neighborhood"].tolist()]
我们已经获得了所有地点的经纬度坐标,因此需要将坐标合并到原始数据框中。
# Create temporary dataframe to populate the coordinates into Latitude and Longitudedf_coords = pd.DataFrame(coords, columns=['Latitude', 'Longitude'])# Merge the coordinates into the original dataframekl_df['Latitude'] = df_coords['Latitude']
kl_df['Longitude'] = df_coords['Longitude']print(kl_df.shape)
kl_df(200,3)
这是包含所有街区和地理坐标的组合数据框。
# Getting the coordinates of Hyderabadaddress = 'Hyderabad, India'
geolocator = Nominatim(user_agent="my-application")
location = geolocator.geocode(address)
latitude = location.latitude
longitude = location.longitude
print('The geograpical coordinate of Hyderabad, India {}, {}.'.format(latitude, longitude))
收集数据后,我们必须将数据填充到 pandas 数据框架中,然后使用 follow 包在地图上可视化邻近区域。
map_kl = folium.Map(location=[latitude, longitude], zoom_start=11)# Adding markers to mapfor lat, lng, neighborhood in zip(kl_df['Latitude'], kl_df['Longitude'], kl_df['Neighborhood']):
label = '{}'.format(neighborhood)
label = folium.Popup(label, parse_html=True)
folium.CircleMarker([lat, lng],radius=5,popup=label,color='blue',fill=True,fill_color='#3186cc',fill_opacity=0.7).add_to(map_kl)
map_kl
包含所有街区的海德拉巴地图
使用 foursquare API 探索社区
CLIENT_ID = '' # your Foursquare ID
CLIENT_SECRET = '' # your Foursquare Secret
VERSION = '20180604'radius = 2000
LIMIT = 100
venues = []
for lat, long, neighborhood in zip(kl_df['Latitude'], kl_df['Longitude'], kl_df['Neighborhood']):# Create the API request URLurl = "https://api.foursquare.com/v2/venues/explore?client_id={}&client_secret={}&v={}&ll={},{}&radius={}&limit={}".format(CLIENT_ID,CLIENT_SECRET,VERSION,lat,long,radius,LIMIT)# Make the GET requestresults = requests.get(url).json()["response"]['groups'][0]['items']# Return only relevant information for each nearby venuefor venue in results:
venues.append((neighborhood,lat,long,venue['venue']['name'],
venue['venue']['location']['lat'],venue['venue']['location'] ['lng'],venue['venue']['categories'][0]['name']))
在提取了所有的场馆之后,我们必须将场馆列表转换成一个新的数据帧。
要了解更多关于 Foursquare 文档,请点击 此处
venues_df = pd.DataFrame(venues)
# Defining the column namesvenues_df.columns = ['Neighborhood', 'Latitude', 'Longitude', 'VenueName', 'VenueLatitude', 'VenueLongitude', 'VenueCategory']
print(venues_df.shape)
venues_df.head()
# Lets check how many venues were returned for each neighbourhoodvenues_df.groupby(["Neighborhood"]).count()# Lets check out how many unique categories can be curated from all the returned valuesprint('There are {} unique categories.'.format(len(venues_df['VenueCategory'].unique())))There are 174 unique categories# Displaying the first 50 Venue Category namesvenues_df['VenueCategory'].unique()[:50]
分析每个邻域
在这里,我们将一个热编码应用于所有的场馆。所以现在列数变成了 175
# One hot encodingkl_onehot = pd.get_dummies(venues_df[['VenueCategory']], prefix="", prefix_sep="")# Adding neighborhood column back to dataframekl_onehot['Neighborhoods'] = venues_df['Neighborhood']# Moving neighbourhood column to the first columnfixed_columns = [kl_onehot.columns[-1]] + list(kl_onehot.columns[:-1])
kl_onehot = kl_onehot[fixed_columns]print(kl_onehot.shape)(6684, 175)
接下来,让我们通过对每个类别的出现频率求和来对邻域行进行分组。
kl_grouped=kl_onehot.groupby(["Neighborhoods"]).sum().reset_index()
print(kl_grouped.shape)
kl_grouped(198, 175)
len((kl_grouped[kl_grouped["Shopping Mall"] > 0]))
海得拉巴有 66 个购物中心,非常高。所以现在我们必须选择一个购物中心较少的合适地点,这样我们在那个地点建立购物中心的机会应该很大。
# Creating a dataframe for Shopping Mall data only
kl_mall = kl_grouped[["Neighborhoods","Shopping Mall"]]
聚集邻居
现在,我们需要将所有的邻居分成不同的组。结果将使我们能够确定哪些街区的购物中心更集中,而哪些街区的购物中心数量较少。根据购物中心在不同街区的出现情况,这将有助于我们回答这样一个问题:哪些街区最适合开设新的购物中心。
# Setting the number of clusters
kclusters = 3
kl_clustering = kl_mall.drop(["Neighborhoods"], 1)# Run k-means clustering algorithm
kmeans = KMeans(n_clusters=kclusters,random_state=0).fit(kl_clustering)# Checking cluster labels generated for each row in the dataframe
kmeans.labels_[0:10]array([0, 0, 0, 1, 0, 1, 1, 0, 0, 0], dtype=int32)
我们将聚类数设置为 3,并运行该算法。在应用 K-Means 聚类算法之后,所有的邻域被分离并形成不同的聚类。
# Creating a new dataframe that includes the cluster as well as the top 10 venues for each neighborhood.kl_merged = kl_mall.copy()# Add the clustering labelskl_merged["Cluster Labels"] = kmeans.labels_
kl_merged.rename(columns={"Neighborhoods": "Neighborhood"}, inplace=True)
kl_merged.head(10)
这里,购物中心列表示特定区域中购物中心的数量,分类标签表示分类编号(0 或 1 或 2)
# Adding latitude and longitude values to the existing dataframekl_merged['Latitude'] = kl_df['Latitude']
kl_merged['Longitude'] = kl_df['Longitude']# Sorting the results by Cluster Labelskl_merged.sort_values(["Cluster Labels"], inplace=True)
kl_merged
现在,我们可以清楚地看到属于第一个群集(群集编号 0)的所有位置。类似地,我们看到所有的分类编号都是按照 0,1,2 的顺序排列的。
可视化产生的集群
# Creating the mapmap_clusters = folium.Map(location=[latitude, longitude], zoom_start=11)# Setting color scheme for the clustersx = np.arange(kclusters)
ys = [i+x+(i*x)**2 for i in range(kclusters)]
colors_array = cm.rainbow(np.linspace(0, 1, len(ys)))
rainbow = [colors.rgb2hex(i) for i in colors_array]# Add markers to the mapmarkers_colors = []for lat, lon, poi, cluster in zip(kl_merged['Latitude'], kl_merged['Longitude'], kl_merged['Neighborhood'], kl_merged['Cluster Labels']):label = folium.Popup(str(poi) + ' - Cluster ' + str(cluster), parse_html=True)
folium.CircleMarker([lat,lon],radius=5,popup=label,color=rainbow[cluster-1],fill=True,fill_color=rainbow[cluster-1],fill_opacity=0.7).add_to(map_clusters)map_clusters
检查星团
len(kl_merged.loc[kl_merged['Cluster Labels'] == 0])
len(kl_merged.loc[kl_merged['Cluster Labels'] == 1])
len(kl_merged.loc[kl_merged['Cluster Labels'] == 2])
结果
在 3 个聚类中最高的聚类 0 中有 132 个地点,并且聚类 0 包含没有购物中心的所有地点。聚类 1 包含 51 个地点,并且它们都恰好包含 1 个购物中心,而聚类 2 包含 15 个地点,其中所有地点都包含 2 个或更多个购物中心。
K-means 聚类的结果表明,我们可以根据“购物中心”的出现频率将街区分为 3 个聚类:
聚类 0:购物中心数量非常少的街区
聚类 1:购物中心中度集中的街区
聚类 2:购物中心高度集中的街区
我们在地图中可视化聚类结果,聚类 0 为红色,聚类 1 为紫色,聚类 2 为薄荷绿。
结论
许多购物中心都集中在海得拉巴市的中心区域。集群 0 的商场数量非常少。这代表着开设新购物中心的巨大机会和高潜力领域,因为现有购物中心几乎没有竞争。同时,由于供过于求和购物中心高度集中,集群 2 中的购物中心可能会面临激烈的竞争。因此,该项目建议房地产开发商利用这些发现,在集群 0 的社区开设新的购物中心,几乎没有竞争。拥有独特销售主张以从竞争中脱颖而出的房地产开发商,也可以在竞争适度的第 1 类社区开设新的购物中心。最后,建议房地产开发商避开第二组中已经有大量购物中心和激烈竞争的街区。
因此,我们可以对大型数据集应用相同的方法,并可以根据类别轻松区分场馆。假设在一个城市里有 400 家餐馆,那么我们可以很容易地把它们分成不同的群。我们不仅可以将这种方法应用于购物中心,还可以应用于餐馆、咖啡店等等。在本项目中,我们只考虑一个因素,即购物中心出现的频率,还有其他因素,如人口和居民收入,可能会影响新购物中心的选址决策。
但是对于建立一个购物中心,我们需要考虑其他因素,如租金成本、购物中心周围的环境、当地的人们——如果这是一个许多人喜欢外出的豪华区域,他们的生活方式将与其他人不同,因此会花费很多。如果我们决定一个竞争较少的地方,那么我们也需要考虑住在那个地方的人们。如果那个地区的人们花费很多并且喜欢外出,那么这将是一个成功。如果住在购物中心附近的人不喜欢外出,那么最好考虑一些竞争较少、人群较好的其他地方。
整个项目可以随时参考我的 GitHub 库 。
“我们曾经建造文明。现在我们建商场。”比利·布莱森
在 LinkedIn 上与我联系
我希望你觉得这篇文章很有见地。我很乐意听到反馈,以便即兴创作,并带来更好的内容。
非常感谢您的阅读!
如何从 Python 查询 Neo4j
在 Python 中使用 Neo4j:Neo4j Python 驱动程序和 Cypher 查询语言的快速入门。
图片作者。Neo4j 浏览器窗口。
根据其网站:
Neo4j 是一个本地图形数据库,从头开始构建,不仅利用数据,还利用数据关系。 Neo4j 连接存储的数据,以前所未有的速度实现前所未有的查询。
在本文中,我们将提供一个关于使用 Python 中的 Neo4j 的简短教程。
安装 Neo4j Desktop 并配置数据库
如果已经安装并配置了图形 DBMS 的实例,则可以跳过本节。为了安装 Neo4j 桌面,请遵循官网上的分步指南。:)正确安装后,您应该会有一个如下图所示的浏览器窗口。
图片作者。Neo4j 浏览器。
您可能已经创建了一些预定义的示例项目。在本教程中,我们将创建并使用一个全新的项目。只需点击新建按钮,并为我们的项目选择一个名称!
图片作者。新项目创建。
最初,项目是空的。我们可以从添加新数据库开始。
图片作者。在 Neo4j 浏览器上的项目中添加数据库。
一旦创建了数据库,启动并打开它。您应该会看到一个如下图所示的窗口,其中有用于从客户端连接到数据库的信息。在我们的例子中,URI 是
bolt://localhost:7687
图片作者。Neo4j 浏览器在一个数据库上打开。
从左侧的垂直栏中,您可以进入用户管理菜单来创建新用户。
图片作者。在 Neo4j 中创建新用户。
在本例中,我们将创建一个管理员用户。
Username: superman
Password: pizza
Role: admin
我们可以通过从左侧垂直栏中选择用户列表来检查新用户的创建。
图片作者。Neo4j 中的用户列表。
现在,我们从服务器端准备好了!让我们转向 Python 吧!
安装 Neo4j Python 驱动程序
在我们的 Python 环境中安装包 neo4j :
pip install neo4j
这是官方支持的驱动程序,所以我们在本教程中坚持使用它。然而,你可以为未来的项目考虑其他设计良好的社区驱动因素。一旦安装了 neo4j 包,可以尝试用 Python 导入。
from neo4j import __version__ as neo4j_version
print(neo4j_version)
如果一切正常,您应该会收到 neo4j 包的版本(在本例中是 4.0.1)作为输出。
现在,我们准备好查询图形数据库了!
用 Python 进行查询
首先,我们需要定义一个连接类来连接图形数据库。
from neo4j import GraphDatabaseclass Neo4jConnection:
def __init__(self, uri, user, pwd):
self.__uri = uri
self.__user = user
self.__pwd = pwd
self.__driver = None
try:
self.__driver = GraphDatabase.driver(self.__uri, auth=(self.__user, self.__pwd))
except Exception as e:
print("Failed to create the driver:", e)
def close(self):
if self.__driver is not None:
self.__driver.close()
def query(self, query, db=None):
assert self.__driver is not None, "Driver not initialized!"
session = None
response = None
try:
session = self.__driver.session(database=db) if db is not None else self.__driver.session()
response = list(session.run(query))
except Exception as e:
print("Query failed:", e)
finally:
if session is not None:
session.close()
return response
上面的类在初始化时需要 url、用户名和密码。在查询方法中,查询字符串要用 Neo4j 的图查询语言来写: Cypher 。更多详情,请查看密码参考卡。
让我们用之前定义的参数创建一个连接实例。
conn = Neo4jConnection(uri="bolt://localhost:7687", user="superman", pwd="pizza")
然后,我们可以进行第一次查询!让我们创建一个名为 coradb 的数据库。
conn.query("CREATE OR REPLACE DATABASE coradb")
我们可以用来自 CORA 数据集的数据填充我们的数据库。
Cora 数据集包括 2708 份科学出版物,分为七类。引文网络由 5429 个链接组成。数据集中的每个出版物由 0/1 值的词向量来描述,该词向量指示字典中相应词的存在与否。这部词典由 1433 个独特的单词组成。
让我们从 CSV 文件创建引用图。我们逐行读取包含节点信息的 CSV 文件,并将每个节点添加到带有标签纸张和属性 id 和类(纸张)的图中。
query_string = '''
USING PERIODIC COMMIT 500
LOAD CSV WITH HEADERS FROM
'[https://raw.githubusercontent.com/ngshya/datasets/master/cora/cora_content.csv'](https://raw.githubusercontent.com/ngshya/datasets/master/cora/cora_content.csv')
AS line FIELDTERMINATOR ','
CREATE (:Paper {id: line.paper_id, class: line.label})
'''conn.query(query_string, db='coradb')
我们对边信息做同样的处理(注意定义边中的“箭头”语法)。
query_string = '''
USING PERIODIC COMMIT 500
LOAD CSV WITH HEADERS FROM
'[https://raw.githubusercontent.com/ngshya/datasets/master/cora/cora_cites.csv'](https://raw.githubusercontent.com/ngshya/datasets/master/cora/cora_cites.csv')
AS line FIELDTERMINATOR ','
MATCH (citing_paper:Paper {id: line.citing_paper_id}),(cited_paper:Paper {id: line.cited_paper_id})
CREATE (citing_paper)-[:CITES]->(cited_paper)
'''conn.query(query_string, db='coradb')
最后,我们准备在我们的引用网络上进行查询!例如,我们可以问网络中有哪些类别的论文:
query_string = '''
MATCH (p:Paper)
RETURN DISTINCT p.class
ORDER BY p.class
'''conn.query(query_string, db='coradb')
或被引用最多的论文列表:
query_string = '''
MATCH ()-->(p:Paper)
RETURN id(p), count(*) as indegree
ORDER BY indegree DESC LIMIT 10
'''conn.query(query_string, db='coradb')
您可以在 Cypher Refcard 上使用任何语法,唯一的限制是您的幻想!
运行图表数据科学算法
除了 Cypher 查询之外,您还可以在 Neo4j 中运行图形算法,例如:路径查找、中心性计算、社区检测等。为此,我们需要激活 Neo4j 图形数据科学库。我们需要返回 Neo4j 浏览器,进入图形数据库的配置菜单(3 点)。
图片作者。图形数据库的配置菜单。
在插件标签下,安装图形数据科学库并重启。在计算任何度量之前,我们需要创建一个图。
query_string = '''
CALL gds.graph.create(
'coraGraph',
'Paper',
'CITES'
)
'''conn.query(query_string, db='coradb')
现在,我们可以计算图中每个节点的 PageRank 和介数并保存在 properties 中。
query_string = '''
CALL gds.pageRank.write('coraGraph', {
writeProperty: 'pagerank'
})
YIELD nodePropertiesWritten, ranIterations
'''conn.query(query_string, db='coradb') query_string = '''
CALL gds.betweenness.write('coraGraph', {
writeProperty: 'betweenness' })
YIELD minimumScore, maximumScore, scoreSum, nodePropertiesWritten
'''conn.query(query_string, db='coradb')
实现算法及其语法的完整列表可以在这里找到。
如果我们现在查询该图,我们可以发现每个节点还有两个属性(pagerank 和介数)。我们可以将输出转换成数据帧,并从那里继续我们的分析。
from pandas import DataFramequery_string = '''
MATCH (p:Paper)
RETURN DISTINCT p.id, p.class, p.pagerank, p.betweenness
'''dtf_data = DataFrame([dict(_) for _ in conn.query(query_string, db='coradb')])dtf_data.sample(10)
上面的代码产生以下输出:
图片作者。从密码输出到熊猫数据框架。
如果我们已经完成了查询,我们可以关闭连接。
conn.close()
Neo4j vs GRAKN 第一部分:基础知识
两个最流行的知识库之间的详尽比较
亲爱的读者,在这一系列文章中,我比较了两个流行的知识库:Neo4j 和 Grakn。很久以前,我就决定应你的要求写这篇比较,然而,命运就是现在😊
这是一个分三部分的详细比较:第一部分致力于技术细节,第二部分深入语义和建模的细节。简介部分给出了 Neo4j 和 Grakn 如何工作的快速信息,以及它们带给我们的新内容的一些细节。如果你已经知道了第一部分,你可以直接进入语义能力部分。第三部分致力于比较图算法,推荐系统的核心和社会网络。该系列将继续图形学习,聊天机器人 NLU 和更多的两个平台的语义。
在本文中,我将简要比较 Neo4j 的做法与 Grakn 的做法有何不同。正如你将遵循的例子,如何创建一个模式,如何插入,删除和查询;你会注意到范式的不同。这部分不是战斗,而是比较。
我知道你等不及看内容了,让我们开始比较吧…以下是一些亮点:
Grakn 的工作原理
Grakn is the knowledge graph, Graql is the query language
notes Grakn 主页。Grakn 是知识图,完全真实;但是 Graql 语言是面向数据的,类似于本体论。Graql 是声明性的,定义数据,操作数据。我们将在接下来的章节中看到更多关于 Graql 的内容。
Grakn:专注于知识表示,而不是放弃图形
在我看来 Grakn 是知识库,Graql 是面向数据的查询语言;所有这些都建立在一个图形数据结构上,但是你永远感觉不到图形在那里。他们的网站是这样描述的:
When writing Graql queries, we simply describe **what** information we would like to retrieve, rather than **how** should it be obtained. Once we specify the target information to retrieve, the Graql query processor will take care of finding an optimal way to retrieve it.
当我第一次见到 Grakn 时,我想this is a knowledge modeler
并且我仍然有同样的感觉。Grakn 到底是什么的最佳解释来自他们自己:
Grakn is a database in the form of a knowledge graph, that uses an intuitive ontology to model extremely complex datasets. It stores data in a way that allows machines to understand the meaning of information in the complete context of their relationships. Consequently, Grakn allows computers to process complex information more intelligently with less human interventionGraql is a declarative, knowledge-oriented graph query language that uses machine reasoning for retrieving explicitly stored and implicitly derived knowledge from Grakn.
正如我以前说过的,尽管他们自己说的是Grakn is a database
,我仍然提出我的反对意见,坚持认为 Grakn 是一个知识库😄
Neo4j 的工作原理
Neo4j 是一个图形外观的知识图😄虽然我在他们的主页上看到过一次connected data
,但是人们必须浏览他们的文档才能看到他们实际带来的语义:
Neo4j:看起来只是一个图形数据库,但实际上是一个知识图。
虽然他们只在头版写了一个graph database
,但我非常不同意。Neo4j 绝对是知识图谱。可以看到关系、类、实例、属性,即模式定义。这是语义学,就是这样。Neo4j 绝对不仅仅是一个图形数据库,一个可以建模的知识。
Grakn 如何战胜 OWL
好吧,如果我们已经可以写下一些 OWL,那么我们为什么要用 Grakn 来代替呢,有人可能会想。Grakn 在他们的帖子中详细解释了这个问题。对我来说,first plus 绝对是 Graql,读写方便。OWL 通常由的门徒或其他类似的框架创建,生成的 XML 基本上是不可读的。下面在 OWL-XML 和 Graql 中找到相同的descends
关系:
Graql 和 OWL-XML 中的下降关系。
从生产的角度来看,Grakn
- 可攀登的
- 高效
- 拥有 Python、Java、Node.js 客户端
- 被包装成一个完整的框架
…即生产就绪。从发展的角度来看
- 查询得到了高度优化
- 底层数据结构对于语义建模是灵活的
- 还提供了图形算法。
从语义学的角度来看,Grakn 更有力量;数据模型
- 易于更新/添加
- 允许抽象类型(我们将谈到这一点)
- 与 OWL 相比有一个巨大的优势,它保证了逻辑完整性。OWL 有一个开放世界的假设,而 Grakn 提供了开放世界和封闭世界假设的别致组合。您可以在逻辑完整性部分了解更多信息。
- 通常允许更多的抽象细节。例如,底层超图允许 n 元关系。在 OWL 中建模 n 元关系有点痛苦,这里有一个完整的描述。
Neo4j 带来了什么
老实说,我不知道从哪里开始。Neo4j 以提供连接数据在 NoSQL 世界大放异彩,以其卓越的后端和高性能在图形数据库中大放异彩。与许多其他图形数据库不同,Neo4j 提供了
- 快速读写
- 高可用性
- 水平缩放。水平扩展是通过两种类型的集群实现的:高可用性集群和因果集群。
- 缓存分片
- 多聚类
- 没有连接。数据通过边连接,不需要复杂的连接来检索连接/相关的数据
- 粒度安全。
就我个人而言,在阅读 Neo4j 无与伦比的可伸缩性技能时,我从椅子上摔了下来。我强烈推荐访问相应的页面。警告:你也可能坠入爱河,❤️
从数据角度来看,Neo4j 提供
- 时态数据支持
- 3D 空间数据支持
- 实时分析。
从左上开始:寻找有价值的异常值,以不同的方式表示连通性,查询和可视化空间数据。全部摘自 Neo4j 主页。
如果你想建立一个社交网络模型,建立一个推荐系统或为任何其他任务建立连接数据模型;如果您想要操作时间或空间数据,想要一个可伸缩、高性能、安全的应用程序,那么 Neo4j 是您的最佳选择。这里不再多言。
入门指南
开始使用 Grakn 很容易:首先一个安装Grakn,然后制作一个grakn server start
。之后,可以使用 Grakn 控制台或 Grakn 工作台工作。
同样适用于 Neo4j,下载和安装以非常专业的方式提供。如果你需要,Docker 配置和框架配置手册也在那里。之后可以下载 Neo4j 浏览器开始玩或者可以发现后端更多。你也可以不用通过他们的沙箱下载就可以试验 Neo4j,我真的很喜欢。您可以在没有任何下载拥挤的情况下玩游戏。
Neo4j 和 Grakn 下载页面
我必须说,我完全爱上了 Neo4j 文档,因为边注,努力和专业水平是巨大的。
开发环境和可视化
Grakn 和 Neo4j 都提供了易于使用和可视化的 ide。
Grakn workbase 提供了两个功能:知识图的可视化和与模式交互的简单方式。您可以在工作库中执行match-get
查询和path
查询。
使用 Grakn workbase 进行查询,照片取自他们的 Github
Neo4j 提供他们的浏览器也有两个目的:简单的交互和可视化。人们还可以在他们的图形中探索模式、聚类和遍历。
在 Neo4j 浏览器中查询,摘自他们的文档
此外,Neo4j 提供了更多用于可视化的 Neo4j Bloom 和其他用于可视化的开发工具,主要是 JS 集成。使用 Neo4j Bloom,您可以发现集群、模式等。
聚类和链接探索与布鲁姆,图片取自他们的网站和媒体开发博客
这还没有结束,Neo4j 有甚至更多的可视化工具用于空间数据和 3D。一般来说,Neo4j 是空间数据的大师,但可视化工具将 Neo4j 带到了一个不同的水平:
Neo4j 地图数据可视化,取自他们的媒体开发者博客
两个平台都提供了很好的可视化效果,Neo4j 由于较老而提供了更多😄
我们已经介绍了这两个平台的基础知识,现在我们可以进入开发细节了。
底层数据结构
简称 Grakn 为超图,Neo4j 为有向图。如果你进一步对 Neo4j 如何存储他的节点、关系和属性感兴趣,你可以访问他们的开发者手册或者堆栈溢出问题。
数据建模
数据建模是我们从老猫头鹰那里学到的方法。
Neo4j 使用知识图概念:节点(实例)、标签(类)、关系、关系类型(属性)和属性(数据属性)。
具有图形概念的建模知识,图片取自 Neo4j 文档
Grakn 风格的知识建模更接近于本体方式、声明性和更面向语义。观念是:
- 实体(类)
- 例子
- 属性
- 角色
- 规则
- 类型层次结构
- 抽象类型
Grakn 建模,实体关系,子实体,三元关系。摘自他们的头版。
如果你更多地走上本体论一边,Grakn 的思维方式确实是类似的。建模容易,提供语义能力和效率。
查询语言
Graql 是声明性的,更面向数据。Neo4j 的 Cypher 也是声明性的,但是风格是 SQL。对我来说,Graql 感觉像本体,Cypher 感觉像数据库查询。比较以下两种语言中的简单查询:
//CypherMATCH (p:Person { name: "Duygu" })
RETURN p//Graql
match
$p isa person, has name "Duygu";
get $p;
我个人认为 Graql 在语义上更友好。
创建模式
在 Grakn 中创建模式很容易,记住 Graql 是声明性的。基本上,你打开一个.gql
文件,开始创建你的模式,就是这样😄。这是他们首页的一个例子:
define
person sub entity,
has name,
plays employee;
company sub entity,
has name,
plays employer;
employment sub relation,
relates employee,
relates employer;
name sub attribute,
datatype string;
Neo4j 创建实体和实例的方式是CREATE
。一旦创建了这些节点,就可以进行一个MATCH
查询来获得相应的节点,并在它们之间创建一个关系。一个创建节点、边及其属性:
CREATE (d:Person { name:"Duygu"})
CREATE (g:Company {name: "German Autolabs"})MATCH (a:Person),(b:Company)
WHERE a.name = 'Duygu' AND b.name = 'German Autolabs'
CREATE (a)-[r:Employed { since: '2018' }]->(b)
RETURN type(r), r.name
询问
Graql 是声明性的,查询又是本体论的方式。通过match
子句进行查询。以下是一些关于银行客户的简单查询:
match $p isa customer; get;match $p isa customer, has first-name "Rebecca"; get;match $p isa customer, has full-name $fn; { $fn contains "Rebecca"; } or { $fn contains "Michell"; }; get;
到目前为止一切顺利。现在,我们可以从客户那里获得一些见解。这是对average debt of the Mastercard owner customers, who are younger than 25
的查询:
match
$person isa customer, has age < 25;
$card isa credit-card, has type "Mastercard";
(customer: $person, credit-card: $card) isa contract, has debt $debt;
get $debt; mean $debt;
看起来很干净。Neo4j 怎么样?密码查询也是由MATCH
子句完成的,但是语法完全不同;更确切地说是一个带有WHERE
的数据库匹配风格。有WHERE
的时候,也可以玩一些串法游戏😄:
MATCH (customer:Customer)
RETURN customerMATCH (customer:Customer)
WHERE customer.first_name = 'Rebecca'
RETURN customer//or equivalently with a bit syntactic sugarMATCH (customer:Customer {first_name: "Rebecca"})
RETURN customer//MATCH (customer:Customer)
WHERE p.first_name STARTS WITH 'Steph'
RETURN p
谈到关系,我们需要通过箭头关注边缘方向。以下是对驾驶奥迪的银行客户的查询;注意DRIVES
关系是从客户指向他们的汽车的:
MATCH (car:Car {brand: "Audi"})<-[:DRIVES]-(customers)
RETURN customers.first_name
聚合也类似于 SQL,下面是对年轻的万事达卡用户客户的债务的相同查询:
MATCH (customer:Customer)-[:OWNS]->(card:CreditCard {type: "Mastercard"})
WHERE customer.age < 25
RETURN AVG(card.debt)
SQL 语法适用于一般的 Cypher 查询,如果你喜欢写 SQL,那么你一定会对 Cypher 如鱼得水。这是一个查找具有最多属性的节点的查询:
MATCH (n)
RETURN labels(n), keys(n), size(keys(n)), count(*)
ORDER BY size(keys(n)) DESC
回到语义,可以查询实体/实例如何相关:
MATCH (:Person { name: "Oliver Stone" })-[r]->(movie)
RETURN type(r) //DIRECTED
“连接”呢,即关于相关节点的查询?人们通常像在 Grakn 中一样处理这样的查询,只是多了一些实例和更多的关系箭头:
//Name of the movies that Charlee Sheen acted and their directorsMATCH (charlie { name: 'Charlie Sheen' })-[:ACTED_IN]->(movie)<-[:DIRECTED]-(director)
RETURN movie.title, director.name
我一次又一次地强调 Neo4j 是一个图形,现在让我们看看它是如何工作的……我们可以用 Cypher 生成path queries
,或者用path restrictions
生成常见的语义查询。
假设您有一个社交网络,您想要查找与 Alicia 的距离为 2 的所有人,以及谁在哪个方向跟随谁并不重要:
MATCH (p1:Person)-[:FOLLOWS*1..2]-(p2:Person)
WHERE p1.name = "Alicia"
RETURN p1, p2
当然,最短路径是社交网络中的经典,你可能想知道艾丽西娅和艾蜜蕊的社交联系有多紧密:
MATCH p = shortestPath((p1:Person)-[*]-(p2:Person))
WHERE p1.name = "Alicia" AND p2.name = 'Amerie'
RETURN p
这样,我们可以通过任何关系类型寻找最短路径。如果我们需要,我们可以用[:FOLLOWS]
代替[*]
来指定我们需要这个关系。(可能还有其他关系类型,上同一所大学,住在同一座城市……)
正如你所看到的,Cypher 提供的远比你看到的要多。诚然,语法看起来像 SQL …但故事是非常不同的,图的概念和语义的概念满足燃料 Neo4j。
推理
语义推理机从 RDF 时代就存在了。从图式知识和已有的数据中推断新的关系和事实对人脑来说很容易,但对知识库系统来说就不那么简单了。我们是否允许开放世界的假设(如果你不确定某件事并不意味着它是错的,你只是不知道);世界是由我们已经知道的东西组成的吗(当一个看不见的实体来到我们的封闭世界时会发生什么),我们应该推断多少,我们应该允许长路径吗… Hermit 是 OWL 的一个流行选择(我也用过),它可以作为 Protégé的一个插件使用。
Neo4j 中的推理没有内置工具。在这里我将介绍 Grakn 是如何处理它的。
Grakn 中的推理通过rules
处理。下面是他们如何描述规则的:
Grakn is capable of reasoning over data via pre-defined rules. Graql rules look for a given pattern in the dataset and when found, create the given queryable relation.Graql reasoning is performed at query time and is guaranteed to be complete.
让我们看一个兄弟姐妹规则的例子,如果两个人有相同的父母;那么可以推断他们是兄弟姐妹。如何用 Grakn 表示这个推论如下:
兄弟推理规则,取自 Grakn 文档
Grakn 规则的创建是直观的:when
满足一些条件,then
我们应该推断出以下事实。我发现语法令人耳目一新。特别是在药物发现和任何其他种类的发现任务中,人们需要 100%的推理。如果我参与到发现类型的任务中,我只会因为这个原因而使用 Grakn。
逻辑完整性
一旦您创建了您的模式并且可以推断新的关系,之后您会希望您的模式保持不变,并且不允许不正确的数据进入您的模型。例如,你可能不希望在一个person
和一个carpet
之间有一个marriage
关系(尽管人+树在世界的某些地方是合法的😄).
尽管 Neo4j 在他们的文档中有约束,但是这些只是用于数据验证和空值检查的数据库约定:
Neo4j helps enforce data integrity with the use of constraints. Constraints can be applied to either nodes or relationships. Unique node property constraints can be created, as well as node and relationship property existence constraints.
Graql 通过roles
确保逻辑完整性,正如你在上面的例子中看到的,每个角色来自一个类。同样,对于发现类型的任务,这是必需的。
可量测性
两者都是超级可扩展的。我在上面讲了很多关于 Neo4j 可伸缩性的内容,但是对 Grakn 的可伸缩性保密😄Grakn 利用了幕后的 Cassandra,因此 Grakn 非常具有一致性、可伸缩性和容错性。
图形算法
是下一篇文章的主题。你将不得不等待 2 个以上的职位😉
Grakn 世界 vs Neo4j 世界
…看起来非常不同,但同时又非常相似。Grakn 的方式更面向知识,Neo4j 感觉图形味道更浓一些。两个框架都很吸引人,那么只剩下一个问题:谁更好?接下来即将到来的是 Grakn 和 Neo4j 之间的语义大战😉
亲爱的读者们,这篇令人筋疲力尽的文章已经接近尾声,但我还不想挥手告别😄请继续阅读第二部分,和我一起探索迷人的语义世界。在那之前,请小心,愉快地黑吧。👋
Neo4j vs GRAKN 第二部分:语义
语义之战:谁对数据建模更好?
亲爱的读者,在这一部分,我将带你进入语义学的世界。在第一部分中,我们从总体上比较了 Grakn 和 Neo4j 世界,以了解范式差异如何导致不同的做事方式。在本文中,Grakn 和 Neo4j 将在建模和表示能力方面展开竞争,这是一场旨在征服语义世界的肉搏战😄
在我职业生涯的早期,我从事知识工程(我从事搜索引擎维基化组件)。让我惊讶的是,在过去的 10 年里,知识图在可伸缩性、语言支持、开发者/用户界面、高效存储方面有了巨大的改进;但是就语义表达能力而言,没有什么能改变那么多。仍然有实例、关系、阶级和我们,那些努力尝试赋予意义的人。不管你是初学者还是更高级的,都没有区别。我们都知道这样一个事实:对任何一种语义收费都是困难的。这完全是另一个故事,我将在图形学习和知识库支持的聊天机器人帖子(即将推出)中详细介绍。
我不会让你等太久,让战斗开始吧:
1.文档和社区
对于任何开源项目来说,最重要的是文档和社区支持。
Neo4j 提供了文档、 视频教程、 一个胖 Github 、一个社区和一个 Stackoverflow 存档。如果我搜索Neo4j tutorial
和GRAKN tutorial
,我会看到:
看起来相当棒
Grakn 提供了文档,教程,充满示例的 Github repo, 一个社区以及自己的 Stackoverflow 空间。我看到了精彩的文档和社区对这两者的支持。
Neo4j: 10/10,Grakn: 10/10
2.语言支持
Grakn 提供 Java、Node.js 和 Python 客户端,涵盖了现代开发栈的相当一部分。Neo4j 在这方面提供了更多的变化;他们正式支持的驱动程序。NET、Java、JavaScript、Go 和 Python。Ruby、PHP、Erlang 和 PERL 驱动程序由社区贡献者提供。我发现 Grakn 的语言支持对于现代开发来说足够且体面;然而,Neo4j 因其提供的多样性而大放异彩。Neo4j 相对轻松地赢得了这一轮。不支持 C++,我各扣一分😄
Neo4j: 9/10,Grakn: 8/10
3.OWL 导入
如果一个人从事知识表示或关联数据方面的工作,很可能他已经拥有大量 OWL 格式的本体资源。Grakn 没有直接导入 OWL 文件的方法,必须解析文件并创建自己的 Grakn 本体。更多关于这个问题的消息来自他们自己的口:【https://discuss.grakn.ai/t/migrating-owl-to-grakn/556】T2,尽管他们有一个 XML 导入器。加载器是 JSON、CSV 和 XML。
来到 Neo4j 情况就有点不一样了。Neo4j 团队似乎花了很大力气给他们的图形方法带来更多语义,并构建了neosemantics
。这个贡献包括了关于 RDF 导入、 OWL 导入的综合页面以及对它们的推理/推论。我觉得这种努力令人印象深刻。Neo4j
还如预期支持 XML、CSV、JSON 导入。你也可以阅读这篇文章了解更多猫头鹰& Neo4j duo,我真的很喜欢它。
Neo4j: 8/10 表示努力,Grakn: 8/10 表示提供了一种本体语言
4.语义关系
OWL 支持许多属性特征,包括自反性、非自反性、传递性、对称性、非对称性……如果你想建立一个友谊网络你需要对称性(友谊是相互的,如果 Carry 是 Barry 的朋友,Barry 当然是 Carry 的朋友);如果你需要一个希腊神话的模型,你需要及物性:奥德修斯是赫尔墨斯的曾孙,特勒戈诺斯是奥德修斯和泰坦喀尔刻的儿子,那么显然特勒戈诺斯是赫尔墨斯的后代。
下降关系:传递和不对称。猫头鹰风格,在被保护人内部拍摄的照片
这是 OWL 在游戏中发挥其全部语义力量的地方,使自己区别于 RDFS 和其他三联商店或图形信息持有者。OWL 从来没有像 T2 一样成为一个裸露的信息容器,它允许开发者对有意义的现实世界关系进行建模。
OWL 支持的酒店特征,在被保护人内部拍摄的照片
语义关系是充分发挥语义力量的必要条件。我尝试了一下如何定义与 Grakn 的对称关系:
friendship isa relation-type
relates friend;person isa entity-type,
plays-role friend;
定义传递关系有点棘手,但是可行😉Grakn 的思维方式通常侧重于本体和知识表示(即使底层存储是一个图),如果您熟悉 OWL,Grakn 会有宾至如归的感觉。
来到 Neo4j 思维方式,Neo4j 是知识图,他喜欢做图。那么在这一点上,人们应该只把关系看作是边。我看不出在创建时间上限制关系的直接方法。Neo4j 通常不允许无方向的边,然后创建一个具有任意方向的关系,并在查询时丢弃边方向,就像这样:
出于效率和空间原因,创建具有任意方向的边。摘自上面的链接。
查询看起来像:
//find all partner companies of Neo4j MATCH (neo)-[:PARTNER]-(partner) which is union of MATCH (neo)-[:PARTNER]->(partner) (edge is directed to partner)
and
MATCH (neo)<-[:PARTNER]-(partner) (edge is directed to neo)(notice the edge directions)
传递关系和传递性在 Neo4j 中非常重要😉我们在图形算法的文章中探讨了这个问题。更多关于 Neo4j 的关系,你可以访问。
我们已经看到 Grakn 喜欢做超图。与 Neo4j 不同,超图提供了一个宽松的节点/边概念,并允许 Grakn 关系涉及两个以上的角色。
define
law-suit sub relation,
relates court,
relates judge,
relates DA,
relates defendant;person sub entity,
has name,
plays judge,
plays DA,
plays defendant;name sub attribute,
datatype string;
你可以在 Grakn 博客上阅读更多关于这个主题的内容。这是对真实世界数据建模的一个非常重要的特性,否则人们必须经历许多不必要的痛苦,类似于 OWL ways 。
5.推理者
Grakn 中的推理通过inference rules
工作。如何定义规则相当简单,在模式创建期间创建推理规则集。根据文档,规则不是直接存储在图中,而是根据查询时间进行推断。推理规则看起来像
define rule-id sub rule,
when LHS then RHS;
显而易见的是:如果满足左侧的条件,那么请推断右侧的条件。让我们看一个例子,这是人们如何自然地定义一个 being siblings 关系:
define
people-with-same-parents-are-siblings sub rule,
when {
(mother: $m, $x) isa parentship;
(mother: $m, $y) isa parentship;
(father: $f, $x) isa parentship;
(father: $f, $y) isa parentship;
$x != $y;
}, then {
($x, $y) isa siblings;
};
RHS 可以描述一个推断的关系,或者一个实体的一些属性。例如,在这个如何推断城市大洲的例子中,RHS 是关于实体-属性关系的推断,即has:
city-in-continent sub inference-rule,
when {
(contains-city: $country1, in-country: $city1) isa has-city; $country1 has continent $continent1;
}then
{
$city1 has inf-continent $continent1;
};
如你所见,人们可以推断实体、属性和关系,也就是节点、属性和边。我真的很喜欢 Grakn 的方式,Graql 的语义美在这一点上闪闪发光。效率对我的观察也很好。如果想了解更多,可以跳转到这篇帖子。
如你所见,推理是 Graql 的核心部分,不幸的是 Cypher 没有内置推理。Neo4j 仍然没有放弃,并且新语义再次发挥作用以获得更多语义。正如在文档中所描述的,可以推断出WHERE
子句中导入的本体的节点和关系:
CALL semantics.inference.nodesLabelled('Crystallography',
{ catNameProp: "dbLabel", catLabel: "LCSHTopic", subCatRel: "NARROWER_THAN" })
YIELD node
RETURN node.identifier as id, node.title as title, labels(node) as categories
不幸的是,在 Neo4j 图表上,我看不到简单直接的推理方式。这并不奇怪,因为 Neo4j 喜欢做一个图表。实际上,这里的比较根本不适用。
Neo4j: 4/10 为努力;Grakn: 10/10 完美推理。格拉克无疑是这里的赢家。
6.语义力量
正如我多次写 Neo4j 喜欢做图;他们的正式名称是一个图形数据库。另一方面,Grakn 喜欢成为知识图并且更加面向知识;他们仍然没有牺牲语义,提供一种本体语言来创建和查询图形。我真的很喜欢 Graql 面向语义,它隐藏了底层的图形,使它看起来像只是本体编写和推理。这是纯粹的美。如果你像我一样站在语义学的角度,你会喜欢这种烟雾和镜子。
Cypher 看起来像一个简单的数据库查询(尽管它包含很多语义并提供路径查询)。这感觉就像为您的旧日志数据库编写 SQL 查询,虽然它非常正确和高效,但并不令人兴奋。
在建模关系类别中,Grakn 凭借支持n-ary
关系胜出。老实说,我很惊讶为什么以前没有人实现n-ary
关系,把我们从巨大的痛苦中拯救出来。
另一个巨大的优势是,Grakn 提供了逻辑完整性,这是 NoSQL 和图形数据库普遍缺乏的,Neo4j 也是如此。同时,它可以像 Neo4j 提供的 NoSQL 一样水平扩展。这两个平台的水平扩展能力给我留下了非常深刻的印象,而没有从语义上妥协;但是 Grakn 以其逻辑完整性赢得了我的心。
我不会坚持太久的,胜利者是格拉克。我发现 Grakn 在语义上比 Graql 更具表现力,能够以一种有机的方式表达子类和子关系,定义抽象实体、属性和关系的能力,允许 n 元关系,有一个内置的推理机…使 Grakn 成为这里的赢家。
neo4j:7/10;2010 年 9 月
8.文本支持
这一节是根据我的口味加的,因为我喜欢文字,你懂的😉(抱歉地理编码/空间人,这是我的博客😅)
Grakn match
支撑regex
和contains
。典型的查询可能如下所示:
match $phone-number contains "+49"; get;match $x like "eat(ery|ing)"; get;
非常方便的方法startswith
和endswith
根本没有实现,我有点不高兴(不要说用^
写一个正则表达式,所有的美都在于方法的名字)。
另一方面,Neo4j 的[WHERE](https://neo4j.com/docs/cypher-manual/current/clauses/where/)
支持regex
、contains
、startswith
和endswith
。人们可以这样询问:
MATCH (n)
WHERE n.name STARTS WITH 'Mar'
RETURN n.name, n.age
虽然方法非常相似,但我非常喜欢文档中对字符串方法的强调。Neo4j 在这里征服我的心。
Neo4j: 10/10,Grakn:9/10;由于以上原因,我的评价可能不太客观😄
获胜者是…
Grakn。
Neo4j 和 Cypher 竞争很激烈,但是激烈到没有足够的语义来打倒 Grakn 和 Graql。
下一步是什么
亲爱的读者们,我们到达了比赛的终点;我希望你喜欢这场血腥的战斗。接下来是图形算法的对比,猜猜谁是杀手对手😄我们将通过构建两个推荐系统来探索图算法,一个在 Grakn 中,另一个在 Neo4j 中。同时你可以访问我的其他文章,也可以在https://duy gua . github . io上访问我。在那之前保持快乐和和谐👋
霓虹。生活:你真正的虚拟助手,这次是真的吗?
氖的剖析与理论建构。生命,三星的新“人造人”
A nyone 看过 Blade Running 2049 一定还记得‘Joi’,一个人造人漂亮精致的全息投影。她和你说话,帮你做家务,给你讲笑话,陪你,还有更多…就像一个真正的人一样。她甚至对你有自己的记忆,并随着时间的推移形成了自己的性格。除了“她”不是人类。她只是一个超级复杂的真人“模型”,可以像真人一样说话、行动和反应。然而,仍有相当多的人暗地里希望他们也能有自己的“Joi”。她可能没有你想的那么远。进入霓虹,三星的新人造人。
来自 Gfycat 的 Joi
遭遇
我上周在 CES 2020 展会上走在地板上。像过去几年一样,出现了许多新的装置和新技术。也有大量的展示。小的,大的,巨大的,可折叠的,半透明的,应有尽有。其中,有一件展品引起了我的注意。一个栩栩如生的人站在一个显示器内,带着温暖的微笑看着我,还用丰富的手势谈论一些事情。这是干什么用的?也许是新的远程视频服务?出于好奇,我走近查看。结果,这些非常逼真的人物都不是人。他们被称为 NEONs,是三星支持的公司 STAR Labs 创造的人造人。
霓虹,我们的第一个人造人来了。霓虹是一个通过计算创建的虚拟生物,它的外表和行为都像一个真实的人,具有表现情感和智能的能力。—星际实验室
来自霓虹。生活
这个样子,表情,手势是如此自然,我真的不能告诉它是否是预先录制的视频或 CGI。仔细看,明明是 CGI,只是非常非常‘真实’。真正意义上的“类人”而非“高分辨率”。我深入挖掘,发现这些是基于预先录制的真实人类视频的人工智能生成的 CGI 镜头,是对人类演员视频的“再创造”。非常像詹姆斯·卡梅隆在电影《阿凡达》中第一次做的事情。
但是霓虹并没有就此止步,它把事情推得更远。这些 NEONs 可以脱离剧本,发展自己的“个性”。它可以从自己独特的“个性”中产生新的表情、手势和反应。这些“个性”也可以被训练并适应外部刺激。现在,这不仅仅是模仿面部表情!三星是如何做到这一点的,这意味着什么?展会上没有透露太多细节。我内心的数据科学家立刻被打开了,让我们试着弄清楚(猜测)这是如何实现的,它会对我们的社会和行业产生什么影响,好吗?
解剖
那么他们是怎么做到的呢?我们先来看看它能做什么。为了实现他们所宣称的,NEON 需要做几件事:
注意:下面的部分只是我的“有根据的预测”,因此与霓虹实际上是如何工作或创造的没有任何关系。三星尚未公布 NEON 的更多细节。
物理建模:真人视频到 CGI,穿越恐怖谷
给定一个演员的一些视频数据集,该模型需要学习如何将视频转换为 CGI 镜头,越相似越好。这项技术随着头像和表演捕捉的兴起,已经得到了很好的发展。演员被拍摄时,他们的脸上有一些彩色点网格,用来记录他们的面部表情,并将这些面部表情网格运动转换成 CGI 角色表情。这是一项相当成熟的技术。霓虹所做的只是有点不同。它可能没有网格作为参考,但使用深度学习,深度神经网络找到任务所需的特征并不太难,它可能比像面部网格这样的简单模型做得更好。
**数据输入:**视频短片
**数据输出:**面部/身体网格运动时间序列数据。
表情建模:表情/手势投影到 CGI
因此,从视频片段中,我们现在有一个网格运动时间序列数据集,如果我们可以用不同的表达式标记这些数据集,我们应该能够训练某种自动编码器,可以将 CGI 时间序列编码为表达式编码,然后重新生成相同的 CGI 时间序列数据。然后我们可以看看编码,弄清楚什么是微笑、哭泣、惊讶、生气等等。这也是一个已经解决的问题。你可以在下面找到一个例子:
数据输入: CGI 时间序列数据集
**数据输出:**表达式编码/嵌入层数
个性建模:从情感到表达
所以现在我们可以通过表情编码来控制我们的 CGI 化身的表情,下一步就是将情感映射到表情上。表达是情感的外化,但映射并不总是直截了当的。你可能认为人们开心的时候会笑,但是人性远比这复杂。一些外向的人会笑出声来,而内向的人可能只是微妙地傻笑。控制情绪到表情映射的是性格。现在我们如何塑造人格?这需要大量的领域知识(这也是三星声称他们仍在努力的部分,我认为最具挑战性。因为人性,你知道…)。
**数据输入:**情感标签(多个标签,因为一个表情后面可能有多个情感)
**数据输出:**表示给定化身的情绪的表情编码/嵌入
三星关于 NEON 的说法包括
从演示中,我们知道氖实际上由三部分组成:
- 行为神经网络(表达模型?)
- 进化生成智能(人格模型?)
- 计算现实(物理模型?)
我上面的假设是真的吗?我们将在不久的将来了解更多,但如果你有不同的想法,请留下一些回应!
理论加工
那么这些部分是如何组合在一起形成霓虹的呢?它可以像管道一样工作:
首先使用一个人类演员的视频镜头来训练一个可以生成 CGI 网格时间序列数据的神经网络。这将赋予控制 CGI 化身尽可能像人类一样的能力。神经网络将不可避免地学习人类手势和人类面部表情的模式,这为进一步的抽象奠定了基础。
利用 CGI 网格运动时间序列数据,我们可以训练一个自动编码器,它可以进行某种降维,创建一些中间层编码器,然后重新生成 CGI。一旦重新生成的 CGI 与原始时间序列数据足够相似,我们将获得视频、表情和手势的更抽象层(表情编码器)。一旦我们有了编码器或嵌入,我们就可以玩一玩,看看哪种权重组合可以生成特定的表情,例如微笑、生气、惊讶等。然后,我们可以使用这些权重(可能需要做一些主成分分析,使其更易于管理)来控制我们的化身的表情,使其微笑、哭泣等。
就这一点而言,虚拟角色的表情可以在很大程度上由人工控制,但现在还没有。我们需要的是让虚拟角色自己“生成”表情,对外界刺激做出反应。这就是人格模型发挥作用的地方。使用心理学、情绪科学的领域知识,可以开发许多不同的个性特征,它们与表达和外部刺激的关系可以被建模。如果我们以某个名人(比如说李小龙)为例,通过对他的行为(表情)和外界刺激(言语的情绪,手势等)进行标签化。),我们可以开发一个“个性”模型,反映名人对不同情绪的不同表达会有什么反应。然后,我们使用这个个性模型来根据外部情绪(可能是从情绪分类器神经网络输出的)控制霓虹灯的反应表达。
在霓虹官方视频中,霓虹的“情绪激活”状态是实时可视化的,表明霓虹对外界刺激的反应。相当酷。
霓虹实时的情绪地图,灯泡就是她当前状态所在的地方,文字就是不同的情绪状态。
除此之外,NEON 可以学习领域知识,提供更多的价值。三星声称他们有另一个名为 Spectra 的云人工智能平台,第三方开发者将能够为 NEON 开发这些“技能”。认为 Siri 有漂亮的脸蛋,迷人的声音,还能教你功夫。😜
我不得不说,即使他们在 CES 2020 上展示的演示远非完美,但 NEON 是开创性的。这个团队的想法很宏大,并且打下了良好的基础和框架。此刻的计算饥饿应用可能意味着 NEON 不能生活在边缘,但随着 5G 的快速发展和更好的云平台,我相信 NEON 的未来潜力是巨大的。
视力
你想要哪种霓虹灯?瑜伽导师?魔术师?商务助理?私人摄影师?
这是文章中允许我放肆的部分。让我们看看霓虹灯能做什么:
- 博彩业中的完美 NPC。在 RPG 游戏中,NPC 通常都很笨。他们的面部表情和反应太假了。NEON 可以改变这一点,给玩家一个非常真实的体验。
检查整个系列,它是金色的…
- 重现你逝去的亲人或朋友。如果你读过这篇文章讲的是一个男孩试图创造一个聊天机器人,说话和他爸爸一模一样,并在他爸爸去世后一直让他陪着,你知道我在说什么。NEON 可以走得更远,不仅聊天机器人可以像他的父亲一样生成文本,它可以看起来像他的父亲,听起来像他的父亲,甚至具有相似的“个性”。(也许有点令人毛骨悚然,但我有什么资格评判……)
- **各种服务助理。**喜欢瑜伽导师,理财规划师,或者只是让你有个好的性格作伴。
- 残疾人的向导/帮手。
- 粉丝崇拜的名人“副本”
- 自闭症治疗师
这个清单可以一直列下去,但是你要明白。由于它是一个开放的平台,它很可能成为技术领域的下一个大事件。
结论
表面上看,CES 是关于工程创新而不是科学突破的,但我认为 NEON 有点处于中间位置。这支队伍还很年轻,我很兴奋看到他们在不久的将来能做什么,并了解他们的方法。这真的是一个隐藏的宝石,我觉得必须向我的读者介绍。你认为人造人能做什么?做不到。还是永远不应该做?
关于 CES 舞台上展示的更多细节,你可以看看这个详细的视频:
觉得这篇文章有用?在 Medium 上关注我(李立伟)或者你可以在 Twitter @lymenlee 或者我的博客网站wayofnumbers.com上找到我。你也可以看看我下面最受欢迎的文章!
为什么 CS50 特别适合巩固你的软件工程基础
towardsdatascience.com](/this-is-cs50-a-pleasant-way-to-kick-off-your-data-science-education-d6075a6e761a) [## 一枚硬币的两面:杰瑞米·霍华德的 fast.ai vs 吴恩达的 deeplearning.ai
如何不通过同时参加 fast.ai 和 deeplearning.ai 课程来“过度适应”你的人工智能学习
towardsdatascience.com](/two-sides-of-the-same-coin-fast-ai-vs-deeplearning-ai-b67e9ec32133) [## 你需要了解网飞的“朱庇特黑仔”:冰穴📖
是时候让 Jupyter 笔记本有个有价值的竞争对手了
towardsdatascience.com](/what-you-need-to-know-about-netflixs-jupyter-killer-polynote-dbe7106145f5)
NeRF:将场景表示为用于视图合成的神经辐射场
ECCV-2020 口头论文评论
从 2D 图像绘制三维模型是计算机视觉领域的一个挑战性问题。即使我们对每个图像都有摄像机位置监控,以前的 3D 渲染模型也不足以在实践中使用。在 ECCV 被选为口头论文的 NeRF 提出了一种最先进的方法,该方法利用 2D 图像及其相应的相机位置来构建 3D 模型。
视频 1。NeRF 作者的官方视频,经常被作者推荐观看。
术语
- 射线:从摄像机中心连接的线,由摄像机位置参数决定,在特定方向上,由摄像机角度参数决定。
- 颜色:每个 3D 体所具有的 RGB 值。
- 体积密度:决定它对最终色彩决定的影响程度。
- 光线颜色:我们跟随光线时可以观察到的 RGB 值。公式 1 给出了正式定义。
什么是 NeRF?
NeRF 是第一篇介绍神经场景表示的论文。它有利于渲染真实物体的高分辨率真实感新视图。本文的主要思想是预测沿光线的颜色值和不透明度值,这由五个外部相机参数(3 个相机位置,两个相机角度)确定。
最终,使用估计的颜色和不透明度,NeRF 确定光线的预期颜色(等式 1)。对于实际实施,NeRF 将整数近似为有限和(等式 2),称为分层采样。实验中的样本数为 64,128,256。
等式 1。输入光线的预期颜色。
等式 2。方程式 1 的近似值。
NeRF 架构不需要保留每个特性;因此,NeRF 是由 MLP 而不是 CNN 组成的。它还使用两种技术来提高其性能:位置编码和分层体积采样。
位置编码
NeRF 没有使用五个简单的相机参数,而是使用位置编码,这种编码经常在 NLP(自然语言处理)中使用。在颜色和几何图形的高频变化中,使用简单输入通常表现不佳。位置编码便于网络通过容易地将输入映射到更高维空间来优化参数。 **NeRF 表明,使用高频函数映射原始输入能够更好地拟合包含高频变化的数据。**消融研究是这一论点的证据(表 1)。
等式 3。位置编码。
图一。NeRF 的整体架构,它使用位置编码而不是简单的输入。
表 1。位置编码的效果。使用位置编码提高了每个指标的性能。
分层体积取样
NeRF 提出了一个层次结构。整个网络架构由两个网络组成:一个是粗网,另一个是细网。
粗糙网络使用 N_c 个采样点来评估光线的预期颜色。顾名思义,它首先从粗采样开始优化。等式 4 是粗略网络的正式定义。
等式 4。粗糙网络
精细网络使用 N_c + N_f = N 个采样点来评估光线的预期颜色。这个等式与等式 2 相同。表 2 显示了关于等级结构影响的消融研究。然而,依我拙见,表 2 中的(4)和(7)之间的比较并不公平。是因为(4)和(7)之间的 N_c + N_f 不一样。如果(4)中的 N_f 值是 192(=128 + 64),这可能是一个更公平的比较。
表二。等级结构效应的烧蚀研究。
损失函数
该网络的最终目标是正确预测光线的预期颜色值。由于我们可以使用地面真实 3D 模型来估计地面真实光线颜色,因此我们可以使用具有 RGB 值的 L2 距离作为损失。好在每一步都是可微的;因此,我们可以通过光线的预测 RGB 值来优化网络。
作者设计损失函数以实现两个目标。
- 很好地优化了粗网络。
- 好好优化精细网络。
等式 5。损失函数
结果
-评估指标
PSNR (峰值信噪比):PSNR 越高,MSE 越低。较低的 MSE 意味着地面真实图像和渲染图像之间的差异较小。因此,PSNR 越高,模型越好。
SSIM (结构相似度指标):检查与地面真实图像模型的结构相似度。SSIM 越高,模型越好。
LPIPS (学习感知图像块相似度):确定与感知视图的相似度;使用 VGGNet。LPIPS 越低,型号越好。
-实验结果(定量结果)
表二。实验结果
NeRF 在所有任务中都实现了一流的性能。
-可视化(定性结果)
图二。用可比较的模型可视化 NeRF。
- NeRF 还解决了视图依赖问题模型根据视图有不同的颜色。如图 3 所示,与其他模型不同,NeRF 架构会自动学习视图相关的颜色值。
图 3。NeRF 的视图相关结果。
结论
NeRF 证明,将场景表示为 5D 神经辐射场比以前占主导地位的训练深度卷积网络输出离散体素表示的方法产生更好的渲染。作者期望 NeRF 模型可以用不同的结构进一步优化。此外,NeRF 的可解释性不如以前的方法,如体素和网格。
论文链接:https://arxiv.org/abs/2003.08934
NeRF 官方链接:【https://www.matthewtancik.com/nerf
随时联系我!" jeongyw12382@postech.ac.kr "
嵌套交叉验证—超参数优化和模型选择
深入研究嵌套交叉验证
交叉验证也被称为抽样外技术是数据科学项目的一个基本要素。这是一个重采样程序,用于评估机器学习模型并评估该模型在独立测试数据集上的表现。
超参数优化或调整是为机器学习算法选择一组超参数的过程,该算法对特定数据集表现最佳。
交叉验证和超参数优化都是数据科学项目的一个重要方面。交叉验证用于评估机器学习算法的性能,超参数调整用于找到该机器学习算法的最佳超参数集。
没有嵌套交叉验证的模型选择使用相同的数据来调整模型参数和评估模型性能,这可能导致模型的乐观偏置评估。由于信息泄露,我们对训练或测试数据中的错误估计很差。为了解决这个问题,嵌套交叉验证应运而生。
使用支持向量分类器比较虹膜数据集的非嵌套和嵌套 CV 策略的性能。你可以观察下面的性能图,从这篇文章。
来源:sci kit-了解嵌套与非嵌套交叉验证
什么是嵌套交叉验证及其实现?
嵌套交叉验证 ( Nested-CV )嵌套交叉验证和超参数调整。它用于评估机器学习算法的性能,还用于估计底层模型及其超参数搜索的泛化误差。
除了嵌套循环外,还有几种循环策略,阅读下面提到的文章以了解更多关于不同循环程序的信息。
交叉验证及其类型的深入解释
towardsdatascience.com](/understanding-8-types-of-cross-validation-80c935a4976d)
第 1 步:列车测试分离:
将给定的预处理数据集分割成训练和测试数据,训练数据可用于训练模型,测试数据被隔离以评估最终模型的性能。
(图片由作者提供),将数据分为训练和测试数据
这不是一个强制步骤,整个数据可以用作训练数据。将数据分为训练数据和测试数据对于观察未知测试数据的模型性能至关重要。用测试数据评估最终模型表明了该模型对于未来未知点的性能。
第二步:外部简历:
基于机器学习算法在嵌套交叉验证的外环上的性能来选择机器学习算法。 k 倍交叉验证或 StaratifiedKfold 过程可以根据数据的不平衡性在外环实现,将数据平均分成 k 倍或组。
- k 倍 CV: 此程序将数据分割成 k 倍或组。(k-1)组将被分配用于培训,其余组用于验证数据。对 k 个步骤重复该步骤,直到所有组都参与了验证数据。
- StatifiedKfold CV: 该程序类似于 k-fold CV。这里,数据集被划分成 k 个组或折叠,使得验证和训练数据具有相同数量的目标类标签实例。这确保了一个特定的类不会过度出现在验证或训练数据中,尤其是当数据集不平衡时。
(图片由作者提供),左: k-fold 交叉验证,**右:**分层 k-fold 交叉验证,每个 fold 都有相同的目标类实例
根据数据集的不平衡程度,可以为外部 CV 选择 k-fold 或 Stratifiedkfold CV。
第三步:内部简历:
然后,将内部 CV 应用于来自外部 CV 的(k-1)个折叠或组数据集。使用 GridSearch 优化参数集,然后用于配置模型。然后使用最后一个折叠或组来评估从 GridSearchCV 或 RandomSearchCV 返回的最佳模型。该方法重复 k 次,通过取所有 k 个分数的平均值来计算最终 CV 分数。
步骤 4:调整超参数:
Scikit-learn 包提供了 GridSearchCV 和randomsearccv实现。这些搜索技术通过调整给定的参数返回最佳的机器学习模型。
第五步:拟合最终模型:
现在,您已经有了最佳模型和一组超参数,它们对该数据集表现最佳。您可以针对看不见的测试数据评估模型的性能。
Python 实现:
信用卡欺诈数据集用于从 Kaggle 下载的以下实现中。
(作者代码)
嵌套 CV 的成本:
使用 Nested-CV 极大地增加了模型的训练和评估。如果为非嵌套 CV 训练 nk 个模型,那么要训练的模型的数量增加到 kn*k
结论:
当同一个数据集用于使用 CV 调整和选择模型时,很可能会导致对模型性能的乐观偏见评估。因此,嵌套 CV 比非嵌套 CV 更常用,因为它有助于克服偏差。
使用 nested-CV 极大地增加了模型评估的数量,从而增加了时间复杂度。当数据集太大或者您的系统功能不强大时,不建议使用它。
感谢您的阅读
嵌套数据结构和嵌套循环
循环中的循环…循环中的循环。
我在熨斗学校参加兼职数据科学项目的第二周,到目前为止一切都很顺利!在考虑了什么样的职业道路最适合我之后,我决定从事数据科学方面的职业。我拥有心理学和哲学的双学士学位,但直到最近,我还没有在这些领域中找到我特别热爱的领域。多亏了新冠肺炎疫情和几个月的隔离,我终于能够在熨斗公司追求我在技术、统计和编码方面的兴趣。我喜欢我的老师和我的友好的同龄人,我发现到目前为止我对材料有很好的理解。
不过,有一件事让我陷入了一个循环(绝对是双关语):Python 中的嵌套循环。
在这个项目开始的时候,我们被鼓励在博客上写下我们可能会发现具有挑战性的材料的各个方面,以便帮助我们自己更深入地理解它,同时也帮助其他人学习。起初,嵌套循环对我来说很棘手,因为它们是其他循环中的循环。我发现,在构建嵌套循环结构的过程中,分解它并一次只关注一个循环会有很大帮助。
我写这篇文章所做的工作还包括理解 Python 中的字典和列表理解,这两个主题一开始也有点难。和大多数障碍一样,我越努力克服它,它就变得越容易。考虑到这一点,下面是使用嵌套循环在 Python 中处理嵌套数据的一些工作。
我先说一些代表我家的数据。
my_family = [ { "family_name": "Tunnicliffe", "num_people": 4, "local": True, "city": "Bethpage, NY", "date_established": 2014, "names": ["Diane", "Steve", "Dylan", "Landon"], "number_of_children": 2, "children": [ { "name": "Dylan", "age": 5, "favorite_color": "black", "nickname": "Dillybeans", "loves": "Super Mario", }, { "name": "Landon", "age": 2, "favorite_color": "blue", "nickname": "Landybean", "loves": "trucks", } ] }, { "family_name": "Agulnick", "num_people": 5, "local": False, "city": "Newton, MA", "date_established": 1987, "names": ["Ellen", "Mark", "Diane", "Joshua", "Allison"], "number_of_children": 3, "children": [ { "name": "Diane", "age": 31, "favorite_color": "pink", "nickname": "Dini", "loves": "unicorns", }, { "name": "Joshua", "age": 28, "favorite_color": "red", "nickname": "Joshie", "loves": "trains", }, { "name": "Allison", "age": 26, "favorite_color": "purple", "nickname": "Alli", "loves": "candy", } ] }]
上面,我使用了变量my_family
来存储一个包含两个字典的列表:一个用于我居住的家庭单元(我自己、我丈夫和我们的两个孩子),另一个用于我出生的家庭单元(我的父母、我自己和我的兄弟姐妹)。Python 中的字典是以键:值对的形式存储数据的对象。所以在第一个字典中,对于"family name"
的键,我们有"Tunnicliffe"
的值。这在使用键和值来访问信息时变得很有用,尤其是在嵌套循环中。
由 Marco Secchi 在 Unsplash 上拍摄的照片
现在,假设我想访问我居住的城市。为了获得这些数据,我将调用第一个字典并寻找与键"city"
相关联的值:
print(my_family[0]['city'])
作为回应,我会得到:
Bethpage, NY
这样做是因为我知道代表我居住的家庭的数据存储在第一个列表对象中,即一个字典,索引为my_family[0]
。由于‘city’
是该词典的一个属性,因此很容易理解,例如:
print (f"I live in {my_family[0]['city']}.")
我们会有:
I live in Bethpage, NY.
好的,这很有意义,也很简单。现在,如果我想创建一个两个家庭中每个人的名字的列表,作为一个组合列表,该怎么办呢?为此,我可以使用嵌套的 For 循环:
names = []for unit in my_family: for name in unit['names']: names.append(name)print(names)
就在那里。循环中的循环。外部循环由for unit in my_family:
指定,它访问 my_family 中包含的我的列表中的两个字典。而for name in unit['names']
是内部循环,它访问两个字典的键names
的所有值。最后,我们的 append 方法意味着,对于列表my_family
中的每个家庭单元字典,以及该家庭单元的名称列表中的每个名称,将这些名称添加到名为names
的列表中。因此,作为回应,我们得到:
['Diane', 'Steve', 'Dylan', 'Landon', 'Ellen', 'Mark', 'Diane', 'Joshua', 'Allison']
酷!现在我们有进展了。值得注意的是,在我写unit
和name
的地方,你可以放任何你想要表示元素的变量。例如,我本来可以写for blob in my_family
或for narwhal in unit['names'].
,但是这里的目标是让事情更有意义,而不是更少,所以我选择了更符合逻辑(尽管不那么有趣)的变量名。让我们更进一步。现在我要一份孩子的名单。我可以这样得到:
children = []for unit in my_family: for child in unit['children']: children.append(child['name'])print(children)
我会:
['Dylan', 'Landon', 'Diane', 'Joshua', 'Allison']
或者如果我想要他们的昵称列表:
children = []for unit in my_family: for child in unit['children']: children.append(child['nickname'])print(children)
我会看到这个列表:
['Dillybeans', 'Landybean', 'Dini', 'Joshie', 'Alli']
请注意,我在这里使用的“儿童”一词相当宽松,因为我把我自己和我的兄弟姐妹列为儿童,而我们在技术上都是成年人。(虽然我们可能感觉不像成年人,我们的兴趣,在我们的数据集中由关键字'loves':
指定,当然说明了这一点。)如果我想找到我出生的家庭单元中的孩子的年龄,记住我们将这个单元指定为非本地单元(从地理上来说)会有所帮助。因此,要访问这些信息,我可以这样写:
child_ages = []for unit in my_family: if unit['local'] == False: for child in unit['children']: name_and_age = child['name'], child['age'] child_ages.append(name_and_age)print (child_ages)
我们会看到非本地家庭单元中儿童的姓名和年龄列表:
[('Diane', 31), ('Joshua', 28), ('Allison', 26)]
事实上,我和我的兄弟姐妹都是成年人了。
为了加深我们对这一切是如何工作的理解,让我们再做几个。假设我想找出哪个孩子对超级玛丽真正感兴趣。我可以这样做:
loves_Mario = Nonefor unit in my_family: for child in unit['children']: if child['loves'] == 'Super Mario': loves_Mario = child['name']print (f"{loves_Mario} really loves Super Mario.")
照片由 Cláudio Luiz Castro 在 Unsplash 上拍摄
我们的书面回复是:
Dylan really loves Super Mario.
是真的!我的儿子迪伦对超级马里奥非常着迷。当我写这篇文章的时候,他穿着超级马里奥睡衣和布瑟袜子。注意,这里使用了==
操作符来证明与键child[‘loves']
关联的值和与"Super Mario"
关联的值是相同的。与上面使用的类似类型的代码公式可以用于访问任何信息,从兴趣到喜欢的颜色。例如:
loves_trains = Noneloves_trains_age = Nonefor unit in my_family: for child in unit['children']:
if child['loves'] == 'trains': loves_trains = child['nickname'] loves_trains_age = child['age']print (f"{loves_trains} is still very much into trains, even at the age of {loves_trains_age}.")
我们有:
Joshie is still very much into trains, even at the age of 28.
或者:
likes_blue = Nonefor unit in my_family: for child in unit['children']: if child['favorite_color'] == 'blue': likes_blue = child['name']print (f"{likes_blue}'s favorite color is blue.")
我们的答案是:
Landon's favorite color is blue.
一旦掌握了访问嵌套信息的诀窍,它就变得像访问 Python 中的任何其他字典项一样合乎逻辑。最后,让我们看看能否找到my_family
最大和最小的孩子。为此,我将使用嵌套循环和排序,这个任务在之前的实验中让我非常头疼。但是,我现在已经多练习了一点,所以让我们试一试。
oldest_child = Noneyoungest_child = Nonechildren = []for unit in my_family: for child in unit['children']: children.append(child)sorted_children = (sorted(children, key = lambda child: child['age'], reverse = True))oldest_child = sorted_children[0]['name']youngest_child = sorted_children[-1]['name']print(f"The oldest child is {oldest_child}. The youngest child is {youngest_child}.")
请击鼓…
The oldest child is Diane. The youngest child is Landon.
我自己,我丈夫和我的孩子。我的(本地)家庭,在上面的数据中引用为 my_family[0]。
代码不会说谎。我是数据集中最大的孩子,我最小的儿子兰登是最小的。考虑到这整个嵌套循环的概念在几天前对我来说是多么令人沮丧,我不得不说写这个实际上比我预期的更有趣。(我不知道我的兄弟姐妹们会对这次冒险有什么感觉,所以我们不要告诉他们火车、糖果以及在网上列出他们的年龄。)
我认为到目前为止我学到的最重要的事情是,当谈到 Python 时,任何问题都可以通过结合信息(无论是来自课堂材料、教师和同龄人,还是 Google)和大量实践来解决。我现在可以看到一段代码,并且通常可以计算出它的输出是什么。当涉及到嵌套数据时,它可以被一行一行地分解成小块,以准确地计算出您试图访问的信息。
。NET 核心 API —深入研究 C#
为英雄角之旅教程构建一个后端 web 服务器
如果你一直跟随系列大纲,并且有一个工作数据库,一个角游英雄 App ,一个通用。我们现在已经进入了这个系列的核心部分,为我们的。NET API。
要继续学习,您可以下载。NET API+Docker MSSQL DB+Angular app 来自我的 GitHub repo。
本文中我们将遵循的流程是:
- 连接字符串和 Startup.cs
- 模型
- 脚手架控制器
- 控制器路由
- 邮递员测试
- 克-奥二氏分级量表
- 修改角度应用程序
我们开始吧!
如果你没有看过这个系列的介绍,我强烈推荐。如果你刚入门,已经有了一个通用的 API,那太好了!如果从头开始,只需从命令行运行:
dotnet new webapi -n Heroes.API
这将创建一个样本。用假天气预报数据的 NET 核心 API。我有**。NET Core 3.0.2** 安装在我的机器上,所以所有的依赖项都将针对 3.0.2。
从示例 API 中,删除WeatherForecast.cs
和WeatherForecastController.cs
。
导入包
我们需要几个包裹来装我们的。NET 核心应用程序来工作。您可以使用 NuGet 软件包管理器或从命令行安装这些软件包:
dotnet add package Microsoft.EntityFrameworkCore --version 3.0.2
dotnet add package Microsoft.EntityFrameworkCore.SqlServer --version 3.0.2
dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design --version 3.0.0
dotnet add package Microsoft.EntityFrameworkCore.Design --version 3.0.2
模型
在名为 Model 的目录下新建一个文件夹,在 Model 文件夹下新建一个名为Hero.cs
的 C#类。
该模型将定义数据库和应用程序中数据的属性,因此我们只需添加id
和name
的值。
英雄。API/Model/Hero.cs
namespace Heroes*.*API*.*Model{
*public* class HeroValue
{
*public* int id { get; set; }
*public* string name { get; set; }
}
}
现在,我们需要添加一个数据库上下文来管理。NET 核心数据模型和数据库。我们将我们的上下文类命名为HeroesContext
,它将从 DbContext 继承。HeroesContext
将包含一个 DbSet HeroValues
,它对应于我们在 SQL 中的表名。
创建一个名为Data
的文件夹,并添加一个名为HeroesContext.cs
的新 C#文件。将以下代码添加到您的HeroesContext.cs
中。
英雄。API/Data/HeroesContext.cs
using Microsoft.EntityFrameworkCore;
using Heroes.API.Model;namespace Heroes.API.Data
{ public class HeroesContext : DbContext
{
public HeroesContext (DbContextOptions<HeroesContext> options)
: base(options)
{
} public DbSet<HeroValue> HeroValue { get; set; }
}
}
我们还需要在我们的Startup.cs
文件中添加一个对 DbSet 的引用,这样。NET 知道使用HeroesContext
作为与数据库交互的上下文。我们还将把数据库的连接字符串添加到我们的appsettings.json
文件中。
将这个添加到您的public void ConfigureServices
下的Startup.cs
文件中。在我们的例子中,我们使用 Docker MSSQL 数据库进行测试和开发。
services.AddDbContext<HeroesContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DockerDB")));
将“DockerDB”连接字符串添加到appsettings.json
。
"ConnectionStrings": {
"DockerDB": "Server=localhost,1433;Database=heroes;User ID=SA;Password=Password1!"
}
脚手架控制器
为了创建HeroesController
,我们将使用 aspnet-codegenerator 工具为我们搭建控制器。
首先,我们必须从命令行安装该工具。然后,从项目的根目录中,运行aspnet-codegenerator
。
# Install the tooldotnet tool install --global dotnet-aspnet-codegenerator --version 3.0.0# Run the aspnet-codegeneratordotnet aspnet-codegenerator controller -name HeroesController -async -api -m HeroValue -dc HeroesContext -outDir Controllers
完成后,您将看到在控制器文件夹下创建了一个名为 HeroesController 的新文件。打开它可以看到为您创建的基本控制器。这个工具对于把你的控制器的基本结构安排到位是非常有用的。现在,让我们编辑控制器,以匹配来自我们的 Angular 应用程序。
使用代码生成器有一个意想不到的副作用。它为 DbContext 名称的configure services:services.AddDbContext
中的连接字符串的Startup.cs
添加了一个值,这是有意义的,但不是我们想要在这个开发环境中用于连接字符串的值。替换“HeroesContext”的连接字符串值,使您的services.AddDbContext
再次看起来像这样:
services.AddDbContext<HeroesContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DockerDB")));
说到这里,在Startup.cs
的配置方法中,让我们将注释掉 app。usehttps redirection();。我们的应用程序不需要它。
控制器方法
在我们的控制器中,我们将有 5 个基本方法来与数据库交互,并将值返回给 Angular 应用程序。这 5 个方法由从 Angular 到。NET app。搞什么。NET app 寻找的是带有提供的路由的 HTTP 请求。这些路线是:
- GET: api/Heroes — 这个方法将返回数据库中的所有英雄
- GET: api/Heroes/5 — 这个方法将从数据库中返回一个特定的英雄。在这种情况下,它将返回 Id 为 5 的英雄。
- PUT: api/Heroes/5 — 这个方法会更新一个特定英雄的信息。
- POST: api/Heroes — 这个方法会向数据库发布一个新的英雄。
- DELETE: api/Heroes/5— 这个方法将从数据库中删除一个特定的英雄。
让我们从头开始。
获取:API/英雄
Angular app 会用这个路由从. NET 中获取所有英雄的 IEnumerable 列表对象,这个方法对应 Angular’ hero.service.ts
中的getHeroes()
方法。
用下面的代码替换第一个 GET 方法,这在很大程度上得益于微软文档为控制器添加了一个搜索功能。记住,Angular 应用程序对几个不同的组件使用 GET: api/Heroes 请求。它需要能够返回英雄仪表板,英雄列表页面的列表,也可以在仪表板上的搜索框中搜索英雄。
我们通过一个异步 ActionResult 来实现这一点,它从 GET 请求中的查询字符串获得信息。该查询可以是 no information,返回所有英雄,也可以是 search string,只返回名字中包含该字符串的英雄。
// *GET: api/Heroes*[HttpGet]
public async Task<ActionResult<IEnumerable<HeroValue>>> GetHeroValue([FromQuery] string name)
{
// Define a LINQ query
var heroquery = from h in _context.HeroValue select h;
// If the string is not empty, find items 'h' whose name contains the query string
if (!String.IsNullOrEmpty(name))
{
heroquery = heroquery.Where(
h => h.name.Contains(name));
}// Return an asynchronous list of heroes that satisfy query
return await heroquery*.OrderBy*(num => num*.id*).*ToListAsync*();
}
让我们使用 Postman 测试 GET 控制器方法。Postman 提供了一种用户友好的方式来测试您的 API。首先,启动你的 Docker 数据库。运行您的。NET 核心 API,方法是从 API 的根目录运行dotnet watch run
。当 API 启动并监听端口 5000 时,在 Postman 中,我们将发送 2 个请求来测试我们想要的结果:
- 向
[http://localhost:5000/api/Heroe](http://localhost:5000/api/Heroes,)s
发出GET
请求。观察返回状态为200 OK
的 JSON 数组。这将满足我们的仪表板和英雄列表的 GET 请求。 - 对
[http://localhost:5000/api/Heroes/?name=wonde](http://localhost:5000/api/Heroes/?name=wonder)r
的GET
请求。观察"id": 1, "name": "Wonder Woman\r"
的返回值
恭喜你。您有一个工作的 API!我们建出来按 Id 找英雄吧。
GET: api/Heroes/5
这是我们的 aspnet-codegenerator 真正闪光的地方。我们不需要对这个控制器方法做任何改动。它已经准备就绪,代码已经生成。它只是在路线中找到传递给它的具有匹配 id 的英雄。你的方法应该是:
[HttpGet("{id}")]
public async Task<ActionResult<HeroValue>> GetHeroValue(int id)
{
var heroValue = await _context.HeroValue.FindAsync(id); if (heroValue == null)
{
return NotFound();
} return heroValue;
}
在邮递员中测试:
- 对
[http://localhost:5000/api/Heroes/2](http://localhost:5000/api/Heroes/2)
运行一个GET
请求,观察一个 JSON 结果“id”:5,“name”:“Bat Man \ r”。
PUT: api/Heroes/5
这也是 aspnet-codegenerator 为我们完美搭建的一个方法。只需运行一个邮差测试来进行测试:
- 向
[http://localhost:5000/api/Heroes/5](http://localhost:5000/api/Heroes/5)
运行一个PUT
请求。在请求的 Body 下,将 TEXT 更改为 JSON,并填写请求正文,如下所示:
{
"id": 5,
"name": "Dr. Strange"
}
如果成功,您将收到请求状态204 No Content
。
岗位:API/英雄
你需要能够发布新的英雄到数据库。此外,您的 API 需要为新英雄创建 Id,Angular 只提供名称。您的 API 使用 DbSet _context.HeroValue
的当前上下文来访问数据库中的值。使用。Max 找到数据库中最高的 Id,用 1 枚举,并将这个新 Id 分配给herovalue.id
。然后用.Add
和.SaveChangesAsync
将新的保存到数据库,并返回一个 CreatedAtAction 结果,产生一个状态 201 Created 响应。
[HttpPost]
public async Task<ActionResult<HeroValue>> PostHeroValue(HeroValue heroValue)
{
if (heroValue == null)
{
return BadRequest();
} // Generate Hero Id
//[https://github.com/lohithgn/blazor-tour-of-heroes/blob/master/src/BlazorTourOfHeroes/BlazorTourOfHeroes.Server/Controllers/HeroesController.cs](https://github.com/lohithgn/blazor-tour-of-heroes/blob/master/src/BlazorTourOfHeroes/BlazorTourOfHeroes.Server/Controllers/HeroesController.cs)
// Grab the current context of the DbSet HeroValue
// Find the max id
// increase by 1
heroValue.id = _context.HeroValue.Max(h => h.id) + 1;
_context.HeroValue.Add(heroValue);
await _context.SaveChangesAsync(); return CreatedAtAction("GetHeroValue", new { id = heroValue.id }, heroValue);
}
在邮递员中测试:
向[http://localhost:5000/api/Heroes/](http://localhost:5000/api/Heroes/)
发送一个POST
请求,JSON 主体为:
{
"name": "Poison Ivy"
}
观察Status: 201 Created
。成功!
删除:api/Heroes/5
最后,你需要能够删除英雄。这是在 angular 向 API 发送一个 HTTP DELETE 请求时完成的,该请求带有要删除的 hero Id。幸运的是,我们的 DELETE 方法是由 aspnet-codegenerator 完美地生成的。
[HttpDelete("{id}")]
public async Task<ActionResult<HeroValue>> DeleteHeroValue(int id)
{
var heroValue = await _context.HeroValue.FindAsync(id);
if (heroValue == null)
{
return NotFound();
} _context.HeroValue.Remove(heroValue);
await _context.SaveChangesAsync(); return heroValue;
}
关于控制器方法的思考
aspnet-codegenerator 确实帮了我们大忙。我们真正需要考虑的方法只有GET: api\Heroes
中的搜索功能和POST: api\Heroes
的 Id 生成器。现在我们有了一个可以从 Postman 测试的功能控制器,我们将添加代码来允许跨源资源共享,更好的说法是 CORS。
跨产地资源共享— CORS
Mozilla 文档给了我们一个关于 CORS 的很好的定义。
跨源资源共享 ( CORS )是一种机制,它使用额外的 HTTP 头来告诉浏览器,让运行在一个源的 web 应用程序访问来自不同源的选定资源。
我们有一个 angular 应用程序运行在一个端口上。NET API 运行在另一个端口上。NET 不喜欢另一个应用程序未经许可访问它的资源。我们需要使它能够接收来自特定位置的请求,即 angular 应用程序的本地主机端口。
在 ConfigureServices 类上方,创建一个只读字符串:
*readonly* string MyAllowSpecificOrigins = “_myAllowSpecificOrigins”;
在 ConfigureServices 类中,添加AddCors
服务。您的 ConfigureServices 类应该如下所示:
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy(MyAllowSpecificOrigins,
builder =>
{
builder.WithOrigins("[http://localhost:4200](http://localhost:4200)")
.AllowAnyHeader()
.AllowAnyMethod();
});
});
services.AddControllers();
services.AddDbContext<HeroesContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DockerDB")));
}
我们正在创建一个构建器,只允许来自[http://localhost:4200](http://localhost:4200)
的请求,我们的 angular 应用程序正在该端口运行。此外,从该端口,我们允许任何头和任何方法访问我们的可用资源。NET API。
我们需要为 CORS 做的最后一件事是将我们刚刚创建的服务添加到我们在Startup.cs
中的 Configure 方法中。将此添加到您的配置方法中:
app*.UseCors*(MyAllowSpecificOrigins);
你的 API 完成了!现在我们只需将 angular 应用指向您的 API 端点。
回到 Angular 应用程序
我们已经非常接近完成了!只需要对 angular 应用程序进行一些更改,它就可以从我们的 API 请求数据。
应用程序模块
我们需要禁用您在英雄之旅教程中构建的内存 web api。在app.module.ts
中注释掉这些导入
//*import { HttpClientInMemoryWebApiModule } from 'angular-in-memory-web-api';*//*import { InMemoryDataService } from './in-memory-data.service';*
然后,注释掉@ngModule() imports
下的导入
// *HttpClientInMemoryWebApiModule.forRoot(InMemoryDataService, { dataEncapsulation: false })*
hero.service.ts
我们的公共 HeroService 类包含我们的 angular 应用程序与。NET API。我们应该首先更改 heroesURL 以匹配我们的 API 正在监听的端点:
*private* *heroesUrl* = 'http://localhost:5000/api/Heroes';
我必须更改 HeroService 类的另一件事是为 updateHero 添加一个 const URL。传递的 URL 不正确,导致 405 方法不允许错误。将 URL 添加到 updateHero 类中,使您的类如下所示:
updateHero (hero: Hero): Observable<any> { // Create the route - getting 405 Method not allowed errors
const url = `${this.heroesUrl}/${hero.id}`; return this.http.put(url, hero, this.httpOptions).pipe(
tap(_ => this.log(`updated hero id=${hero.id}`)),
catchError(this.handleError<any>(`updateHero`))
);
}
现在从 Angular 应用程序的根目录运行ng serve --open
。成功!
就这样,您已经构建了一个. NET API!
您可以下载完整版本的。NET API+Docker MSSQL DB+Angular app 来自我的 GitHub repo。
感谢您的阅读!
。英雄角游 App 的 NET 核心 API 简介
从本地开发到云部署的史诗之旅!
亚历克斯·阿扎巴赫在 Unsplash 上的照片
继续之前:
本文假设你已经搭建了 Angular 官方教程——英雄之旅 app。如果你还没有建立这个,你应该检查从这里下载的角度文件或。其他一切都将在本系列文章中处理!
我们将完成什么
在这个系列中,我们将开始一次神奇的探索。NET Core,Angular,Docker,Azure,Azure SQL Server。
我们将熟悉一些技术,比如 Docker 容器、C#、JavaScript、SQL 和 Azure 命令行界面。
这些文章的最终结果将是一个带有. NET 核心 API 的前端,它与 Azure SQL server 对话,所有这些都托管在 Azure 上。
系列大纲
- 这篇文章 MVC 的环境设置和解释。
- 微软 SQL 服务器(mssql)的本地 Docker 设置。
- 建造。NET API。
- 将您的 API 部署到 Azure。
- 将您的 Angular 应用程序部署到 Azure。
开始所需的工具
- Visual Studio 代码(是免费的)
- Angular CLI (应该已经从英雄之旅教程中设置好了)
- 。NET Core — 该 API 将使用版本 3.0.2
- 邮递员— 用于测试 API
- Docker — 用于本地开发数据库,以及封装我们的应用
使用我的 GitHub repo 跟进。
我们开始吧!
安德里亚·莱奥帕尔迪在 Unsplash 上拍摄的照片
测试 Angular 应用
首先,从应用程序的根目录运行ng serve --open
,启动英雄之旅 Angular 应用程序。您应该看到应用程序运行在端口 4200 上。
当你创建最初的应用程序时,它有一个内置的网络服务器来模拟真实的服务器。我们将用 C#编写的 web API 替换这个人工服务器。NET 框架。让我们关闭应用程序,开始使用 API。
使用创建一个 web API。网络核心 CLI
确保。运行dotnet --version
安装 NET Core CLI。的。NET Core CLI 有许多模板可供选择。用dotnet new -h
检查这些选项标志。你看到 ASP.NET 核心 Web API 的模板了吗?它的简称是 webapi。通过运行以下命令从命令行创建 API:
dotnet new webapi -n Heroes.API
你应该会得到一个Restore succeeded
的结果。输入cd Heroes.API
进入新目录。检查ls
以查看已经创建的文件夹和文件,并通过键入(带句点)code .
启动该目录中的 VS 代码。
让我们通过运行dotnet run
来确保 API 启动。在 web 浏览器中,导航到https://localhost:5001/weather forecast并接受不安全的目的地,以查看您的 API 以 JSON 格式返回的示例输出。这很好,因为它让你知道。NET 核心 API 通过使用模型和控制器来工作。我们将创建自己的模型和控制器,还将添加一个数据库上下文。
模型-视图-控制器
MVC 是一种 web 开发理念,它建议将应用程序的各个部分分离成逻辑组件。这使得你可以单独构建每一个部分,并确认其工作正常,然后将你的部分连接成一个完整的整体。来自 FreeCodeCamp.org 的这篇文章很好地解释了 MVC 就像在酒吧里点一杯酒。饮料菜单就是视图,你向调酒师(控制器)提出请求,调酒师根据配料和说明(模型)制作饮料,并把你完成的饮料返回给吧台(视图)。
在我们的应用程序中,这意味着:
- 视图:Angular app 会接收输入,发出请求,显示数据。
- 控制器: C#类,接受传入的请求,解密请求的路由,并根据接收到的路由使用指定的模型与数据库进行交互。
- MODEL: C#类,定义传递给数据库或从数据库中检索的数据。这将意味着定义像 id 是一个整数,name 是一个字符串这样的东西,这样 C#就可以用 SQL 可以解释的方式传递这些值。数据库上下文对模型很重要
由作者创建
微软已经发布了关于。NET Core,我强烈推荐查看:
下一步:数据库
此时,您应该有两个资源:
- 一个工作角度的英雄之旅应用程序。
- 一个仿制药。NET 核心 API。
我们需要创建一个可以处理来自 API 控制器的请求的数据库。我们最终将迁移到托管的 Azure SQL 数据库,但现在,让我们启动一个 Microsoft SQL 服务器的本地 Docker 容器。这个容器是 SQL Server 的一个轻量级版本,我们可以轻松地进行开发。
前往构建一个 MSSQL Docker 容器来设置数据库!
构建一个. NET API 来处理来自 Angular 的请求
打造你的。NET API,在 aspnet-codegenerator 的帮助下。该应用程序将处理对这些路线的 API 请求:
- GET: api/Heroes — 该方法将返回数据库中的所有英雄
- GET: api/Heroes/5 — 这个方法将从数据库中返回一个特定的英雄。在这种情况下,它将返回 Id 为 5 的英雄。
- PUT: api/Heroes/5 — 这个方法会更新一个特定英雄的信息。
- POST: api/Heroes — 这个方法将向数据库发布一个新的英雄。
- DELETE: api/Heroes/5 — 这个方法将从数据库中删除一个特定的英雄。
在本文结束时,您将拥有 Angular 应用程序的本地版本。NET API 和数据库。
云部署
最后,您将创建一个 Azure SQL server 并部署您的。NET API 到 Azure 远程 git repo,并与 web 应用一起提供服务。
然后你将把你的 angular 应用作为 Docker 容器 web 应用部署到 Azure。
感谢您的阅读!