TowardsDataScience-博客中文翻译-2021-七十三-

龙哥盟 / 2024-10-22 / 原文

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

原文:TowardsDataScience Blog

协议:CC BY-NC-SA 4.0

面向 Excel 用户的 Python 第 2 部分

原文:https://towardsdatascience.com/python-for-excel-users-part-2-2a85721560cd?source=collection_archive---------27-----------------------

修改和操作 Excel 文件(更新销售报告)

图片由作者提供。来自 Unsplash 的背景图片。

本教程系列面向所有想开始使用 python 而不是 Excel 的人,例如,自动化任务或提高速度和可伸缩性。或者只是出于好奇。

这个博客文章系列是为你准备的,当:

  • 您可以使用 Excel 合并各种工作表或表格(使用诸如 Index、Match、VLookup
  • 使用简单的数学运算,如求和与*均
  • 使用条件聚合函数(例如,每个类别的交易总和)或数据透视表

它不会涵盖您需要了解的转储 Excel 的所有内容,也不是本系列的目标。第 1 部分意在让你开始并改变你的思维模式,从 Excel 到 Python/Pandas。第 2 部分将介绍如何通过 python 更新给定的 Excel 报表。

虽然 Excel 有它自己的位置,而且非常快速和方便,但我希望这个博客文章系列能够帮助您探索除 Excel 之外的其他数据处理方式。

你可以在 Jupyter 笔记本上找到这篇博文,以及我们在 HOSD 指导 GitHub repo 中使用的数据。

目标:

在这一部分中,我们面临的任务是用新数据更新某个 Excel 文件,即销售报告。我们将看到两种方法,其中一种是有意义的,我们将在笔记本的最后讨论进一步使用第二种方法的原因和动机。

要求:

虽然 python 的一些基本经验会有所帮助,但你不必成为一名程序员或数据科学家来遵循本教程。理想情况下,您应该听说过熊猫和 jupyter notebook,并亲自花了一些时间使用 python。

但是不要气馁。试着跟上并阅读我们没有涉及的内容。你可以随时回到这里或者重访其他地方。如果你需要一些个人指导,我们在 www.hosd-mentoring.com的 HOSD 辅导公司提供免费的一对一数据辅导

1.形势

在第一部分中,我们假设你刚开始是一家大型在线零售商销售部门的数据分析师。在过去的几周里,你深入研究了企业产生的各种数据。当然,你还要负责制作定期的销售报告。

该公司以 Excel 模板的形式提供了这些报告的一般结构。最重要的报告之一是月度销售报告。许多企业领导人喜欢这种一站式报告,因为它以数字和视觉的方式展示了企业的健康和发展。

作为一名新员工,你发现这种报告已经过时。您与您的老板交谈,建议使用直接连接到数据源的报告和可视化工具,如 PowerBI 或 Tableau。然而,在这一点上,由于一些奇怪的业务原因,这个选项是不可行的,所以我们被迫尽我们所能处理给定的 Excel 报告。

这就是报告的样子。

作者图片

虽然本教程不是关于做分析,而是关于更新和维护一个给定的 Excel 报表的过程,但是会出现一些策略问题,这些问题可能会导致当前报表的有用扩展。

几个战略问题:

  • 随着时间的推移,某些品牌是如何发展的(绝对和相对)?
  • 产品类别的发展(绝对和相对)
  • 5 大增长和亏损产品

由于现在是 8 月,公司要求我们用下面显示的 7 月数据更新报告。

作者图片

我们可以在 Excel 中完全做到这一点,例如通过创建数据透视表,一个用于品牌,一个用于类别,然后将汇总的销售数字复制到七月份的汇总表中。

然而,虽然这种只有四个品牌和类别以及不到 20 种产品的手工工作是可行的,但该公司预计将在未来几个月内大幅增加其产品。因此,开始建立一个更加可扩展和自动化的过程是合理的。

2.使用 Python 更新 Excel 报表

我们已经知道如何使用 pandas 通过 pd.read_csv 方法读取 csv 文件。幸运的是,熊猫也有读取 Excel 文件的方法: pd.read_excel() 。

让我们看看它看起来怎么样。

import pandas as pd

# Read sales for july
sales_july = pd.read_csv("data/sales_july_export.csv")
sales_july.head()

作者图片

# Read excel file with pandas
monthly_sales_report = pd.read_excel("data/Monthly Sales Report.xlsx")
monthly_sales_report

作者图片

呃!这看起来很奇怪,一点也不像我们在 Excel 中打开的报告。

原因是 pandas 只是试图将每个单元格的内容显示为数据帧中的一个值。因此,格式以及合并和组合的单元格都会被忽略。曾经漂亮的视觉效果不再是有 nan 的空牢房。

通常,在 pandas 中使用 Excel 数据需要清理数据框架,例如删除所有不必要的行和列(如标题)。这个过程被称为切片,你可以在熊猫官方文档中读到更多关于索引和选择数据的信息。

例如,这是品牌表的样子。

# create copy of full report df to slice
monthly_brand_df = monthly_sales_report.copy()

# use first row as header (alternative: set header parameter when reading excel file)
monthly_brand_df.columns = monthly_brand_df.iloc[5]

# only keep relevant rows with data for brand sales
monthly_brand_df = monthly_brand_df[6:11]

# first column is always empty as it exists purely for formatting reasons; drop it
monthly_brand_df = monthly_brand_df.iloc[:,1:]

# reset index after slicing
monthly_brand_df.reset_index(drop=True, inplace=True)

# change column names
monthly_brand_df.columns = ['Brand', 'January', 'February', 'March', 'April',
                            'May', 'June', 'July', 'August', 'September',
                            'October', 'November',  'December', 'Full Year']

monthly_brand_df

作者图片

然而,在这个场景中,我们感兴趣的不是仅仅处理数据,而是用新数据更新报告。事实上,我们只需要接触几个单元格,即对应于七月的单元格。

因此,假设报表的结构保持不变,我们正在寻找一种用指定值填充指定单元格的方法。

因此,我们的方法是:

  • 步骤 1:在品牌和产品类别级别上计算七月份的销售额(两个汇总表)
  • 步骤 2:将数据写入 Excel 报表,并保存新的更新版本

第一步:计算 7 月份汇总表

本质上,我们将做与使用 excel 中的数据透视表相同的事情,在 excel 中,我们使用品牌作为一行来合计销售额

作者图片

接下来,我们使用产品类别而不是销售做同样的事情。这就是我们如何使用熊猫在 python 中实现的。

sales_july.head(5)

作者图片

# sales by brand
sales_july_by_brand = sales_july.groupby(["Brand"], as_index=False).agg({"Sales":"sum"})
sales_july_by_brand

作者图片

# sales by category
sales_july_by_category = sales_july.groupby(["Product Category"], as_index=False).agg({"Sales":"sum"})
sales_july_by_category

作者图片

2 步:将数据写入 Excel 报表

好了,现在我们已经计算了我们的小例子中需要的所有销售数字,我们可以更新 Excel 文件了。

例如,我们知道我们需要用品牌 a 的七月销售数字更新第行第 6 和第列未命名:8 中的单元格。

# helper functions to color certain cells green
def highlight_cell(x):
    df = x.copy() 
    df.loc[:,:] = '' 
    df.at[6,"Unnamed: 8"] = 'background-color: lightgreen'
    return df 

def highlight_cells(x):
    df = x.copy() 
    df.loc[:,:] = '' 
    df.at[6,"Unnamed: 8"] = 'background-color: lightgreen'
    df.at[7,"Unnamed: 8"] = 'background-color: lightgreen'
    df.at[8,"Unnamed: 8"] = 'background-color: lightgreen'
    df.at[9,"Unnamed: 8"] = 'background-color: lightgreen'
    return df 

def highlight_grandtotal(x):
    df = x.copy() 
    df.loc[:,:] = '' 
    df.at[10,"Unnamed: 8"] = 'background-color: yellow'
    return df# ugly imported excel file
monthly_sales_report.head(15).style.apply(highlight_cell, axis=None)

作者图片

# take sales for Brand A from sales_july_by_brand and write to monthly_sales_report to a specified cell
monthly_sales_report.at[6, "Unnamed: 8"] = sales_july_by_brand.at[0, "Sales"]

# see updates
monthly_sales_report.head(15).style.apply(highlight_cell, axis=None)

作者图片

我们可以通过循环遍历这些行四次来更新其他品牌,并重复上面的操作。

# update July sales for all brand
for i in range(4):
    monthly_sales_report.at[i+6, "Unnamed: 8"] = sales_july_by_brand.at[i, "Sales"]

# see updates
monthly_sales_report.head(15).style.apply(highlight_cells, axis=None)

作者图片

要更新所有品牌的总销售额,我们需要对所有品牌求和,并将结果写入单元格 monthly_sales_report.at[10," named: 8"]

monthly_sales_report.head(15).style.apply(highlight_grandtotal, axis=None)

作者图片

# calculate sum over all brands for July
total_sum = sum(sales_july_by_brand["Sales"])
print(total_sum)

# write total sum to table
monthly_sales_report.at[10, "Unnamed: 8"] = total_sum

# show updates
monthly_sales_report.head(15).style.apply(highlight_grandtotal, axis=None)11319

作者图片

# Same approach for updating sales per category
for i in range(4):
    monthly_sales_report.at[i+29, "Unnamed: 8"] = sales_july_by_category.at[i, "Sales"]

# see updates
monthly_sales_report

作者图片

# save excel file
monthly_sales_report.to_excel("monthly_sales_report_updated_1.xlsx")

作者图片

替代方案:将数据直接写入 Excel 报表(目前是真的)

当你在阅读最后一节时翻了翻白眼,问自己为什么要用 python 来更新如此复杂的简单 Excel 文件时,我为你提供了另一个选择,而不是使用 pandas。

上面的方法有两个缺点。首先,它太复杂了。第二,从 Excel 加载数据,在数据框架中操作数据,并将其保存回 Excel,这将删除所有格式设置和图形等非数据内容。上面的截图显示。

或者,我们希望找到一种方法,不将报告数据作为数据帧导入,而是将数据直接写入 Excel 报告中的指定单元格。包装 xlwings 可用于此目的。

import xlwings as xw

excel_app = xw.App(visible=False)
excel_book = excel_app.books.open("data/Monthly Sales Report.xlsx")
sheet = excel_book.sheets[0]

# Directly write July sales for Brand A to corresponding cell in Excel
sheet.range("I8").value = sales_july_by_brand.at[0, "Sales"]
excel_book.save()

#excel_book.close()
#excel_app.quit()

当我们切换到 Excel 时,我们可以看到单元格 I8 不再是空的。

作者图片

我们可以使用这种方法将计算出的七月份品牌级别和类别级别的销售额写入相应的单元格。

为此,我们定义了单元格的范围,例如品牌级销售的 I8:I11,并使用了转置选项。否则,数据将从左向右写入一行,而不是一列。

# update a list of cells with a list of values (brand-level and category-level sales) caluclated above
sheet.range('I8:I11').options(transpose=True).value = sales_july_by_brand["Sales"].values
sheet.range('I31:I34').options(transpose=True).value = sales_july_by_category["Sales"].values

作者图片

请注意,与之前的方法不同,我们不需要再次计算总和,因为 Excel 公式仍然保持不变

此外,图会自动更新,因为它们是基于单元格值的。

因此,这种使用 xlwings 更新 Excel 报表的方法应该是首选方法。

3.但是为什么不继续使用 Excel 呢?

如果完全在 Excel 中完成,你可能会快得多——明白了。我之所以在这里使用这个简单的例子,是为了说明由于 xlwings,使用 Python 向 Excel 中指定的单元格写入值是多么容易,所有的公式和连通图都保持不变。

现在,你为什么想用 python 向 Excel 写值呢?

比方说,你必须更新销售数据,不是每月,而是每天(频率),不是四个品牌,而是数百个其他子类别(数据量)。

更新的频率会使手工工作不是一个非常有吸引力的选择,而运行预定义的灵活脚本可以在几秒钟内处理更新过程。

子类别的数量,您需要单独的数据透视表或更复杂的条件公式,只支持这一点。与 Excel 相比,Python 在处理更大的数据集时表现更好,因此外包数据处理是有意义的。

此外,Excel 本身无法完成的计算类型怎么办?

假设您想要写入 Excel 文件的输出值不是简单的条件聚合的结果(例如品牌 1 在 7 月的销售额),而是复杂的机器学习模型的结果(例如使用神经网络预测的销售额)。

当然,您可以导出模型结果并将 Excel 文件连接到输出文件,但是不难想象,将结果直接写入 Excel 表格可能是更好的方式。

Python 作为 Excel 的插件

另一种方法是使用 xlwings 插件在 Excel 中使用 python。我对这个选项不是很熟悉,但是对于一些必须尽可能多地在 Excel 中工作的人来说,这似乎是一个很好的工作流程,例如,对于那些向不能或不愿意使用 jupyter 笔记本或普通 python 脚本的客户交付数据产品的顾问来说。

如果您对此感兴趣,并且希望进一步了解 xlwings 的功能,这里有几篇文章可供跟进:

  • Python Excel 与 Xlwings 的集成
  • 如何用 Python 为 Excel 增压
  • 用 Python 桥接 Excel:XL wings

在这一点上,我还想指出,xlwings 的创始人 Fleix Zumstein 在 2021 年 3 月出版了一本 O'Reilly 的书 Python for Excel 。

他在这段 YouTube 视频中描述了他的书的内容。

图片来自 https://www.xlwings.org/book

虽然我个人对此没有任何经验,但使用 xlwings 作为 Excel 中的插件来直接运行 python 脚本而不是复制输出,从而为客户端提供或多或少自包含的 Excel 解决方案的可能性对于某些用例来说可能是一种很大的可能性,因此值得探索。

4.摘要

我们试图通过 pandas 使用我们在第 1 部分中学到的方法更新一个给定的 Excel 报表。然而,**导入和导出 Excel 文件会改变格式,删除公式和图表**。

python 包 xlwings 允许我们直接在指定的 Excel 单元格中编写输出,因此只在我们希望的地方更改文件,同时保持公式和连接图的完整性。

在这样一个简单的用例中,将数据处理外包给 python 可能没有必要。然而,假设品牌和产品的数量以及更新频率显著增加,这证明我们已经在考虑自动更新报告的方法

此外,一旦我们离开 Excel 的舒适环境,我们使用 python 的能力将是无穷无尽的。我们可以或多或少地使用机器学习模型的输出来扩展报告,而不需要手动导入它们。这些复杂的定制模型中有许多不能仅仅在 Excel 中定义和计算。

因此,这里概述的方法将是我们数据分析师工具箱中的一项好技能。

我再次鼓励你考虑一下你在工作中的情况或者你的个人数据项目,像 xlwings 这样的工具可以帮助你,然后尝试一下。开始总是艰难的,但坚持下去,你会比任何 Excel 专业人员更有效、更快。

如果你需要一些关于数据分析和数据科学是否可以成为你的职业道路的一般性指导,我们 HOSD 辅导提供免费的一对一辅导。只需在我们的网站上点击一下,您就可以预约我们的导师。

不要害羞:)

金融 Python 你应该知道的 7 个有用的库

原文:https://towardsdatascience.com/python-for-finance-7-useful-libraries-that-you-should-know-e422b9e9aaba?source=collection_archive---------4-----------------------

使用这些库开始学习金融技术

DocuSign 在 Unsplash 上拍摄的照片

在这篇文章中,我将分享金融和金融技术中使用的必须知道的 7 个 Python 库/模块。假设你想知道一些公司是如何在短时间内变得更加知名的。在这种情况下,主要是因为他们将 Python 模块整合到了决策过程中。他们能够消除中间商、外包公司和其他处理效率低下的环节,从而在没有任何外国影响的情况下迅速做出决策,更好地了解市场力量。机器学习在金融和金融技术领域的发展中发挥着非常重要的作用。

创建自己的决策模型是获得更大成功的一个好方法。我今天要和大家分享的这些库,也会帮助你获得一些经验。巨大的资源,尤其是如果你打算申请金融科技公司。许多行业越来越依赖编程和计算机科学。

以下是清单:

  • PyAlgoTrade
  • Pyfolio
  • 滑索
  • SciPy
  • Scikit-learn
  • Finmarketpy
  • 数字价格

我们开始吧!

1。PyAlgoTrade

Python 上涉及数据科学和金融评估的第一个模块叫做 PyAlgoTrade。该模块是一个基于算法的模块,用于辅助纸面交易和交易营销的实时评估。它在 Bitstamp 上工作,是使用 Python 2.7/3.7 版开发的。该模块非常适合以最小的工作量检索结果,因为结果是基于对历史数据及其模式的研究而提供的。

官方页面: PyAlgoTrade

2。投资组合

单子上的下一个模块是文件夹。与数据科学相关的 python 模块有助于分析金融投资组合的风险。Pyfolio 于 2015 年公开,有一个开源库可供参考。该模块使用基于退货构建的样张,并将交易与贝叶斯分析相结合。Pyfolio 更多的功能包括绘图和基于统计函数创建的时间序列。

官方页面: Pyfolio

3。滑索

Zipline 是另一个专注于财政援助的 Python 库。但是,它是基于事件而不是基于算法的。Zipline 最适合回溯测试和现场交易。它的结构包括一段用 Python 写的 Zipline 代码。此外,财务模块负责通过消除任何偏差来处理每个成本和订单延迟。它由 Quantopian 监管,定期更新,以帮助用户将最新的分析用于金融用途。

官方页面: 滑索

4。SciPy

下一个 python 模块被称为 SciPy。它使用数学算法和函数来达到预期的结果。考虑一下 SciPy,它是 NumPy 模块的扩展,在用户中也很有名。SciPy 使用稀疏矩阵的数值积分和微分方程。然而,它主要是一个 scipy 类和例程的数据库。SciPy 包含的一些函数是 NPV、IRR、IPMY 和 PPMT 的计算。

官方页面: SciPy

说到 SciPy 库,我想分享一下饶彤彤写的这篇关于如何在金融中使用 SciPy 的伟大文章:用 SciPy 进行投资组合优化。

5。Scikit-learn

我们列表中的下一个是 scikit-learn。该模块的功能非常多样和快速,其范围超出了 Python 中可用的财务模块。Scikit-learn 主要用于处理数据、对信息进行分类和消除数据聚类,以帮助进行关于数据科学的复杂分析。该模块是许多处理接口的基础,这些接口仍用于根据库存水*监控生产以及向供应商付款。

官方页面: Scikit-learn

6。金融市场

我们已经接*尾声了,让我们继续读下去。Finmarketpy 是另一个基于金融的 python 模块。它用于市场分析和交易策略,因为该模块带有一个简单的数据库界面,带有预先安装的模板,可快速操作。您可以使用该库在特定时间范围内运行策略,以了解季节性挑战。

Finmarketpy 也是一个优秀的模块,可以用各种参数进行调查和观察数据。Finmarketpy 模块也依赖于 SciPy 中的库。

官方页面: Finmarketpy

7。NumPy

最后,让我们讨论 NumPy,另一个基于数学和计算的有用的财务模块。NumPy 主要用于 Python 中的数学和科学函数。最初的几个版本为它提供了一个长期的财务框架。它最*被更新以计算可用于理解大量数据科学结构的矩阵。正如我们之前所读到的,您可以意识到它的重要性,其中一些财务模块是如何基于 NumPy 模块的。

官方页面: NumPy

结论

在这些财务模块的帮助下,许多企业能够先于竞争对手发现市场中的低效和缺陷。这些模块是用于预测目的和简化解释的最佳工具,否则解释将会变得复杂和令人厌倦。当计算技术在我们的手指下可用时,为什么不使用它呢?希望你今天学到了新的东西。

我们来连线。查看我的博客和 youtube 以获得灵感。谢谢你,

为您提供更多 Python 和金融文章

Python 从 A 到 Z (1)

原文:https://towardsdatascience.com/python-from-a-to-z-8a37e0573773?source=collection_archive---------22-----------------------

你应该知道的 26 个概念——第一部分:从 A 到 J

图片由亚历山大·奈特在 Unsplash 拍摄

Python 是全球领先的编程语言之一。它被用在从数据科学、机器人、网络开发到游戏和快速原型制作的许多环境中。其简单的语法使 Python 程序非常容易阅读和编写,确保了快速的学习曲线。此外,Python 有“电池包”——多个库(标准库和第三方库),这将极大地方便程序员的工作。虽然与其他编程语言相比,可以更快地达到基本编程水*,但掌握 Python 肯定需要一些时间。在这一系列文章中,我们将详细解释 Python 的 26 个特性,帮助您发现 Python 拥有的巨大能力范围。

蟒蛇

Anaconda 是一个用 Python 管理包和环境的程序。它广泛用于数据科学领域,因为在安装 Anaconda 时,默认情况下会安装数据科学中最常用的包。

如果需要,Anaconda 还允许您通过在 anaconda 提示符下键入命令conda install package_name来安装任何包。安装包时,也可以一次安装多个包,比如命令conda install pandas numpy会同时安装 pandas 和 NumPy。Anacondas 安装最新版本的软件包;但是,如果您的项目需要以前的版本,您可以通过添加版本号来指定它,如下所示:conda install pandas=0.22

通过分别使用命令conda remove package_nameconda update package_name,Anaconda 还可以用于在您的环境中卸载和更新软件包。

除了包管理器之外,Anaconda 还可以用来创建隔离项目的环境。虚拟环境允许在特定项目的隔离目录中本地安装包。当我们需要在不同的 Python 版本和安装包版本之间切换时,这种方法特别有用。

用 Anaconda 创建虚拟环境非常简单。您只需要在 Anaconda 提示符下键入conda create -n environment_name [python==X.X] [list_of_packages](括号中提供的参数是可选的)。例如,您可以基于 Python 版轻松创建一个名为myenv的虚拟环境,如下所示:conda create -n myenv python==3.6。在这种特殊情况下,我们在创建环境时没有指定要安装的软件包列表。一旦环境被激活,也可以在以后安装附加的软件包。

创建新环境后,您需要通过键入conda activate environment_name来激活它。最后,要离开当前的活动环境,在提示符下键入conda deactivate命令。重要的是要记住,这只是介绍如何将 Anaconda 用作包和环境管理器。由于篇幅有限,还有许多操作和命令没有在本简介中详细说明。

美味的汤

互联网最重要的信息资源之一;然而,在大多数情况下,很难从网页中获取数据,因为信息是嵌入在 HTML 代码中的。这不像下载一个 CSV 文件那么简单。因此, Python 提供了各种各样的工具来简化从互联网上提取数据的过程

美汤是一个广泛使用的第三方库,用于从一个 HTML 文档中提取任何数据。它允许您轻松地交互和浏览 HTML 代码,并获得您需要的特定信息(例如页面上的所有图像)。该库提供了不同的方法和属性来从网页中识别和提取所需的信息——使用 Python 字符串方法将是一项非常复杂和耗时的任务(但却是可能的)。

创建一个漂亮的汤对象是任何漂亮的汤项目的第一步。可以用BeautifulSoup构造函数创建一个漂亮的 Soup 对象,传递一个字符串(HTML 代码)作为参数。在下面的例子中,我们使用请求库获得网页(【https://en.wikipedia.org/wiki/Madrid】)的 HTML 代码(作为字符串)。然后,我们用BeautifulSoup解析 HTML 代码,使其更易于获取信息。

提取的 HTML 代码看起来相当混乱,但是BeautifulSoup将这些代码转换成易于解析的树形结构。如上所述, Beautiful Soup 包含了一些从代码中提取信息的简便方法。例如,您可以通过使用find_all('a')方法获得文档中的所有锚标记。此外,您还可以通过在查询中添加关键字参数idclass_来根据 ID 或类名查找 HTML 元素。

您可以使用更多的函数和属性从 HTML 文档中提取信息(在文档中有详细的解释)。

https://beautiful-soup-4.readthedocs.io/en/latest/

需要指出的是,为了从代码中提取特定的信息,您总是可以使用浏览器的开发工具来交互式地浏览 HTML 代码并找到信息所在的位置,然后您可以编写最佳的漂亮的 Soup 查询来检索这个特定的信息。

面向对象编程允许将变量和函数组合成一种数据类型,称为。对于大型程序, OOP 给你的代码增加了组织性,把你的程序分解成更小的部分,更容易理解和调试。

Python 中的类是创建对象的蓝图,由方法和属性组成。要定义一个类,您将使用class关键字,后跟类名(使用 CapWords 约定)。下面的代码定义了由两个属性和一个方法组成的Circle类。

班级圈(图片由作者创作)

属性描述了对象的特征(在本例中是颜色和半径),并在__init__函数中定义。该函数放在类的开头,在类启动时自动执行。

方法是一个类可以采取的动作(例如计算圆的面积)。它们与函数非常相似(都使用def关键字),不同之处在于方法是在类内部定义的,而不是在类外部定义的。

定义了类之后,您可以创建一个对象。创建对象的过程称为实例化。在这种情况下,对象将是一个特定的圆,例如,一个半径为 5 的红色圆。

不同的圆形对象(图片由作者创建)

随后,我们可以再次使用Circle类来创建该类的更多实例。所有对象都有相同数量的属性和方法,基本上是因为它们来自同一个蓝图——Circle类。

一旦red_circle对象被创建,你可以使用点符号访问它的属性。同样使用点符号,您可以调用一个类的方法,但是在这种情况下,您必须在括号内指定输入参数。

一个defaultdict工作起来非常像一个 Python 字典,意味着两个类共享方法和操作。defaultdict功能在模块collections中定义。这个模块是 Python 标准库的一部分,意味着不需要额外安装。要访问该功能,我们必须在程序的开头包含from collections import defaultdict

字典中,当我们试图访问一个尚未定义的键(不存在的键)时,会引发KeyError异常。

然而,当使用defaultdict时,将创建一个新的键(使用提供给defaultdict构造函数的参数),而不会引发任何异常。该参数必须是一个可调用的对象或无。

在下面的代码中,当试图访问一个不存在的键时,调用函数int。该函数返回的值,在本例中为 0,将被分配给丢失的键。

提供给defautdict构造函数的参数也可以是用户定义的函数(在本例中是 lambda 函数),如下所示。

编码(字符)

一个字符编码提供了计算机中的字节(原始零和一)和真实字符之间的映射(翻译)。

编码(图片由作者创建)

多年来,编程语言使用 ASCII 作为字符的内部表示。本标准包括 128 个字符使用 7 位信息。它涵盖所有英文字符(它最初是为美国的电子通信开发的);但是,它未能涵盖出现在其他世界语言中的字符,如带有口音的字符。由于这个原因,在过去的几年中,出现了向 Unicode 编码的转变。该标准包含更广泛的字符和符号,根据编码类型使用 8、16 或 32 位信息,与 ASCII 相比自然需要更多的空间。

除了 ASCII 和 Unicode,Python 还支持多种编码。Python 中可用编码的完整列表可以在codecs模块的 Python 标准文档中查阅。

在处理文本数据时,编码是必须考虑的因素。重要的是要记住,Python 用于需要编码的操作(比如读取文件)的默认编码是 UTF-8。**处理文件时,由于编码不一致而导致字符显示不正确或异常是一个常见问题。**因此,在读取文件时,您需要通过在open函数中包含参数encoding来指定所使用的编码(当它与默认编码不匹配时)。

Findall (re 模块)

使用正则表达式通常可以简化文本处理。正则表达式是用于匹配字符串中字符组合的模式。它们在测试操作中非常有用,这就是为什么 Python 标准库有一个专门处理正则表达式模式的模块——re 模块。这个模块提供了各种各样的函数,在处理文本数据时您肯定会遇到的一个函数是findall函数。

re.findall(pattern, string)函数从字符串(string)中提取正则表达式(pattern)的所有非重叠匹配。函数的第一个参数是一个正则表达式,而第二个参数是我们要搜索的字符串。该函数返回一个字符串列表,其中每个元素都是非重叠匹配的。

Findall 函数(图片由作者创建)

下面的代码从字符串中提取所有数字(包括十进制数字)。我们不打算深入讨论正则表达式如何工作的细节。为此,我们需要再写一篇文章😛。你会盲目地相信这个模式可以提取数字。

如上所示,findall函数返回一个字符串列表,包含所有不重叠的匹配。提取数据后,我们可以很容易地使用 list comprehensions 将列表中的每个字符串转换为 float。

在这种情况下,使用的模式非常简单;然而,正则表达式可以变得更加复杂。它们可以方便地从文档中提取网页、电子邮件、密码或电话号码,是从文本数据中获取信息的一个非常强大的工具。

Get(字典法)

在 Python 中使用字典时,KeyError异常是常见的异常。当用户试图访问一个不在字典中的key时,就会引发这个异常。

在下面的代码示例中,当试图访问字典中不存在的key时,会引发一个KeyError异常。

为了处理这个问题,除了使用try except模块,一个常见的解决方案是采用get方法。该方法返回在指定的key找到的值(如果key可用)。相反,如果key在字典中不可用,get方法返回None或一个自定义值,从不引发异常。

永远记住,为了避免使用字典时出现KeyError异常,您可以从直接访问字典的key切换到使用get方法。这将防止在代码执行过程中出现意外异常。

帮助

在 Python 中工作时,记住每个函数的语法有时是一个挑战,尤其是那些你很少使用的函数。Python help函数提供了对特定函数文档的访问,显示了该函数的功能及其语法。

以下代码总结了hasattr功能。如下所示,help函数打印关于该函数如何工作及其定义的基本信息。

除了help功能之外,Python 还提供了更详细的文档,可以在线查阅。

索引错误

Python 中的有序容器(例如列表或元组)通过位置(索引)来标识它们的元素。 Python 遵循一种称为从零开始的索引的约定,这意味着有序容器中的第一个元素位于索引 0 处。

要访问列表中的元素,可以使用一个索引操作符,它由一对方括号([])和一个索引(从 0 开始)组成,指定要检索的元素。

访问列表元素(由作者创建的图像)

如果您试图访问列表中超出可用元素范围的项目,您将得到一个IndexError异常,如下所示。

出现这种错误是很常见的,尤其是如果您刚刚开始学习 Python 的话。如果您来自 R,错误的索引是您将面临的常见错误,因为 R 与 Python 不同,它使用基于一的索引。

Join(字符串方法)

join方法用于将包含在 iterable 中的字符串连接成一个字符串。该方法的语法如下所示,其中string表示插入 iterable 的每个元素之间的分隔符,而iterable是我们想要连接的字符串的序列(必需参数)。该序列可以是例如列表、元组、字典、集合或生成器。

string.join(iterable)

下面的代码块展示了我们如何使用 Python 中的join方法来连接字符串。正如您所看到的,join方法返回一个字符串,该字符串由 iterable 中的字符串串联而成(在本例中是一个列表)。

iterable 的所有元素都应该是字符串类型的。如果没有,将引发一个TypeError异常,如下所示。要连接一个包含数字的迭代器,我们应该先用str()函数将它们转换成字符串,因为 Python 不做隐式字符串转换。

重要的是要记住join是一个字符串方法,而不是一个列表方法,因为我们称它为字符串而不是可迭代的(列表)。然而,字符串序列(iterable)是join方法的主要参数。

您可能知道,我们还可以使用+操作符连接字符串;但是,这不是连接大量字符串的有效方式。主要是因为+操作符需要在每次使用时创建一个新的对象,导致性能下降。如果您想更多地了解为什么join方法优于+操作符,请阅读下面的文章💚

字符串对象有更多的方法。要了解有关字符串方法的更多信息,可以参考官方 Python 文档,网址为:

这个简短的介绍展示了使用 Python 编程时可能会遇到的一些主要功能和问题。它的可读性、一致性和丰富的库使 Python 成为世界上最重要的编程语言之一,是任何数据科学家都应该拥有的基本资产。

阿曼达·伊格莱西亚斯

Python:从菜鸟到摇滚明星

原文:https://towardsdatascience.com/python-go-from-rookie-to-rockstar-d03fa07a32e8?source=collection_archive---------4-----------------------

数据科学

你还在等什么?

图片由 安德里亚·皮亚卡迪奥 来自 像素

Python 是什么?

Python 是一种解释的(逐行运行代码)高级(隐语表示不那么痛苦)通用(尽情发挥你的想象力)编程语言。

Python 是由吉多·范·罗苏姆在 20 世纪 80 年代末创建的,现在它无处不在。数据科学(pandas、matplotlib 等)、机器学习(Tensorflow、PyTorch 等)、网络应用(Django、Flask 等)、Linux(OS 安装程序)、GUI(PyQT)、视频游戏(PyGame)、3D 动画(作为 Maya、3DSMax 等的脚本语言)以及你能想到的每一个角落。

它受欢迎的原因是它的易用性和全面的标准库。这导致大多数用例的大多数库都是用 Python 编写的,因此,Python 成为进入这些领域(尤其是人工智能和数据科学)的第一扇门。

基础知识

对于任何编程语言来说,一开始都会遇到变量、条件和循环这三种情况。

来源:作者

那我们继续吧。

变量

变量用来在程序内部存储信息,做一些有用的事情(或者有趣的事情)。就像其他语言一样,=用来给变量赋值。最有用的变量类型有:数字、浮点数(带小数的数字)、字符串和布尔值(真或假)。

条件式

条件帮助代码根据条件决定做什么。就像在我们的生活中,如果我们心情好,我们就开心,否则,我们就脾气暴躁。对于 Python,它应该是这样的:

但是,如果我们想在代码中容纳更多的条件呢?别担心,有办法的。延伸我们之前的例子,我们可以说,如果心情好,我们就快乐;如果心情不好,我们脾气暴躁,否则,我们正好处于中间状态。让我们看看 Python 是如何做到这一点的:

对于条件句,你只需要记住大部分的时候,if-else,有时候,if-elif-else。

PS: ==是比较运算符,用于在左侧和右侧的人之间进行比较。

循环用于一次又一次地重复相同的任务。如果我让你把数字从 1 加到 10 亿会怎么样?在 Python 中,如果没有循环,它看起来会像这样:

这将是一项真正令人痛苦和沮丧的任务。这就是为什么需要循环的确切原因。这是用 Python 实现上述操作的方法(简单快捷的方法):

range是一个 Python 函数,用于创建一个范围内的值(在我们的例子中,从 1 到 10 亿)。+=表示先求和后赋值,表示*answer += i*也可以写成answer = answer + i

for循环将会是你经常使用的一个。除此之外,我们还有while循环,它也可以用来解决从 1 到 10 亿以上的数字相加问题,就像这样:

forwhile回路可以互换使用。一般来说,for循环用于遍历一列数字或一些数据,而while循环用于等待一个条件。

现在,基本的东西都不碍事了。如果我们能有一些东西允许我们一次存储多个值,那将是非常有用的。假设您正在考虑自动邀请参加聚会,您需要将所有这些姓名和电子邮件地址存储在某个地方。

数据结构

瞧,我向您介绍了数据结构的世界(存储和组织数据的技术)。我们还有另外三样东西:列表、字典和集合。

来源:作者

目录

List 就像日常生活中的任何列表一样,处理以直线方式收集的数据。在 Python 中,一个班级的学生名字、一家公司的股票价格、购物清单以及生活中出现的许多其他清单都可以很好地放在[]中。这里有一个例子:

我们能够把所有这些值存储在漂亮的小盒子里,这很好,但是我们如何把它们取出来呢?重温我们的学生时代,我们都有点名号码(或类似的东西),我们回答那个号码。这里也一样。只有一点小小的不同,这里的编号从 0 开始。

让我们看一个例子:

正如我们所看到的,杂货清单中的第一个元素(“苹果”)对应于点名 0,以此类推。

在 Python 中,每个数据结构都有一些特性。让我们看看下面的例子。

这里有三个突出的新东西:appendlenpop

append是一种用于在列表末尾添加元素的方法。

len是获取列表长度的方法。

pop用于抛出列表中的 nᵗʰ元素。(我们给 n = 0,所以第一个元素被抛出)。

字典

字典用于存储相互之间有关系的数据。比如国家及其首都,家庭关系或者一个人的信息。举个例子,

在上面的例子中,我们用括号来表示这是一个字典,并且在括号之间有一对括号来表示信息。但是,我们如何称呼这些信息呢?

接着是字典名,在两个方括号之间,你放上键名,就这样。

让我们看看如何在字典中添加键值对。

使用同样的方法,我们使用访问值和赋值操作符(' = '),我们可以很容易地将新值添加到字典中。

让我们看看如何从字典中删除一个键值对。

为了删除键-值对,我们将键传递给 pop 函数(同名的方法也用于从列表中删除值)。

设置

集合对于存储唯一的项目非常有用。他们有你在高中读过的所有东西。有时,您可能希望检查某个值是否重复,或者只保留唯一的项目(一个大列表中唯一城市的数量),set 就是您想要的。让我们看一个例子。

首先,set 也使用括号但是没有 key:value 对,这对 Python 理解发生了什么没有任何问题。接下来,由于a = {}将被 Python 当作一个空字典,我们使用a = set()创建一个新的空集。

从上面的第 1 行到第 4 行,我们看到即使在第一个语句中,我们已经放置了重复的元素,但是仍然只保留了唯一的值。

从第 6 行开始,我们看到在一个空集里,即使我们把相同的值放两次,也只能保留 1。

上面,我们还学习了如何在集合中添加元素。现在,让我们看看如何删除它们。

为了从集合中删除一个元素,只需给 remove 方法一个值。

现在,正如我所承诺的,让我们来看一个高中的例子。

快速复习:

  • 两个集合的并集获取两个集合中的所有唯一元素。
  • 两个集合的交集得到集合之间的公共元素。
  • 两个集合的差得到第一个集合中不在第二个集合中的元素。
  • 两个集合的对称差在两个集合中都得到唯一的元素,除了在两个集合中共同的元素。

因此对于 Python 中的集合:

  • | =联合
  • & =交集
    • =差异
  • ^ =对称差

遍历数据结构

有必要遍历我们创建的数据结构。Python 的好处在于,对于上面的任何一种数据结构,我们的做法都没有太大的不同。让我们来看看。

一个简单的 for 循环可以用来遍历任何数据结构。是在浏览数据结构时的代表。

相同的 for 循环可用于遍历字典键,我们已经知道如何获取字典中某个键的值,因此也可以访问它。

最后,不做任何修改,同样的循环的也可以用于集合中的值。

利用我们上面学到的,你可以用 Python 做任何事情。我们将在前面学到的东西会让我们减少这样做的努力。这也将有助于在将来我们不得不做更改时维护我们的代码。

功能

在 Python 中,函数非常有用,可以帮助我们一次又一次地重复同一套指令。就在上面我们已经看到了同样的一组指令可以用来查看列表、字典和集合中的值。当你继续用 Python 构思时,事情会变得复杂,一遍又一遍地写同样的代码不是对你的时间和动机的最好利用。这就是函数有用的地方。让我们看一个例子。

在上面第 4 行的例子中,我们看到函数*显示。*

每个函数都以关键字 def 开头,后跟函数名,在我们的例子中是 show ,在后面的括号中,参数用逗号分隔。在我们的例子中,我们只有一个自变量数据。

在随后的几行中,我们编写了一组我们不想再重复编写的重复指令。在函数中,我们对函数的参数进行操作。

以前,我们必须编写 example_listexample_set 来遍历它,但是现在,我们使用数据来做同样的事情。数据在我们的函数中充当示例 _ 列表示例 _ 集合的占位符。这允许我们通过将 example_listexample_set 视为相同来避免再次编写相同的代码。

还有更复杂的函数,需要更多的代码行来完成它们的工作,这就是你将真正意识到函数的用途的地方。除此之外,在未来,如果你想改变你的代码,你只需要在一个地方进行修改。

但是,随着系统变得越来越大,这些代码的大小也在增长。一个人再也无法处理这件事了。此时此刻需要更多的结构。除了结构的需要,一个大的代码必然会有一部分代码具有相似的功能和相似的数据。为了适应这一点,类的概念出现了。

班级

我们举个例子。假设您有一些数字数据,对于这些数据,您需要计算它的*均值(所有数字的*均值)和方差(整个数据与*均值相差多少)。我们要做的计算会变成函数,是的,我们可以把数字数据列表传给他们,我们就能得到答案。但是,如果它们可以打包在一起就好了,因为它们是相关的。

如果需要这些函数,我们可以在类中找到。否则,他们可能会迷失在代码的海洋中。作为人类,我们渴望混乱中的结构。现在,让我们看看我们的数值数据类是什么样子的。

PS: 类是一个对象的蓝图。正是通过对象,我们使用类中的功能。

sum()是 Python 中的一个函数,用于计算列表的总和

上面我们有一个类的例子。创建一个类首先需要的是关键字 class ,正如我们在第 1 行中看到的。在第 2 行,我们看到了 *__init__* 函数,它不是创建一个类所必需的,但是我在这里展示它是为了演示它的用途。 *__init__* 函数允许存储将由该类的所有方法共享的内容。在我们的例子中,这就是我们要计算*均值和方差的数组。

下一个突出的是自我。 Self 代表从类中创建的对象。因此*,self.data* 是一个属于 self 的变量,并且由于 self 被提供给类中的每个函数,我们可以很容易地使用该数据,而不用将其作为参数传递。

在第 16 行,我们看到对象 *num。*为了创建它,我们首先编写类名,然后在括号中加上 arr 。它实际上是在调用我们在类定义中编写的 init 函数。 init 也称为构造函数,因为它有助于对象的构造。因此,传递给类以创建对象的内容由 init 函数或构造函数决定。

类允许您在代码上编织一个全局结构,并使其易于导航未来的变化。

结论

有了上面的知识,你现在可以认为自己是 Python 中的摇滚明星了。实践上面展示的所有内容,阅读更多关于 Python 的功能。这是一个伟大的美丽和奇妙的世界,给你无限的可能性。

探索愉快!

Python 曲棍球初学者分析教程

原文:https://towardsdatascience.com/python-hockey-analytics-tutorial-b0883085938a?source=collection_archive---------7-----------------------

快速掌握 Python 的基础知识。

作为教程的一部分,您将构建此图表。

目录

  • 什么是 Python?
  • 要求
  • 安装 Python
  • 安装和升级 Pip
  • 安装上下曲棍球刮刀
  • 安装 Jupyter Lab
  • 打开 Jupyter 实验室
  • 开始教程
  • 导入我们的包
  • 刮数据
  • 清理数据
  • 让数据好看
  • 准备用于分析的数据
  • 分析数据
  • 可视化数据
  • 进行更深入的分析

Python 是什么?

Python 是一种开源编程语言,可用于各种应用,如数据分析、数据科学和数据可视化、软件和 web 开发,以及为系统编写脚本。Python 如此强大,以至于 MacOS 的某些部分实际上依赖于它,这种强大的功能与 Python 直观、用户友好的语法相结合,使它成为世界上最流行的编程语言之一。

如果这听起来像是你想学的东西——尤其是在分析曲棍球统计数据的背景下——那你来对地方了。在本教程结束时,您不仅将对 Python 作为一种编程语言有一个基本的了解,而且您将能够熟练地使用 Python 来自己执行小范围的数据分析。

本教程相当全面,应该需要几个小时才能完成。完成其中的一部分,休息一下,然后在不同的时间完成剩下的部分,这完全没问题;你不是在比赛。

要求

为了完成本教程,您需要以下内容:

  • 能够运行 Python 3.6 或更高版本的工作计算机。
  • 使用曲棍球分析学 Python 的愿望。

就是这样!其他的一切都将通过本教程提供给你。本教程将引导您完成安装 Python 的过程和您将要编码的环境,并且假设您没有 Python 或任何其他编程语言的知识。

安装 Python

这是我最不喜欢的部分。到目前为止。老实说,这可能是最难的。但是它需要被完成。

本教程利用了一个需要 Python 版的包。即使您安装了 Python,在确认您的版本足够之前,也不要跳过这一步。

不管你的操作系统是什么,你都需要在安装过程中进入你的终端/命令提示符。对于不熟悉在 terminal 中工作的人来说,这可能是非常令人畏惧的,但是一旦你投入进去,事情就不会太糟糕。

如果你在这个过程中遇到任何问题,或者你正在使用 Linux,我建议你看看来自 Real Python 的 Python 3 安装指南 **。**此外,如果您使用的是 Windows,您可能会发现这个视频很有帮助: Python + JupyterLab 安装&路径集指南 — Windows

根据您的操作系统,请按照以下步骤开始操作:

Windows 操作系统

按下键盘左下角的 windows 键,键入 cmd,然后打开命令提示符。进入后,输入以下命令查看您安装的 Python 版本:

python --version

如果您还没有安装 Python,或者您已经安装了 Python,但是您没有版本 3.9.4(本文撰写时的最新版本)或更高版本,您将需要打开 Microsoft store 并通过它安装最新版本。(如果您安装了 3.9.4 或更高版本,请跳到下一步。)

使用键盘左下方的 Windows 键打开 Microsoft Store,并进行搜索。然后,进入商店后,在应用商店的搜索栏中搜索 Python,并选择最新版本。点按屏幕右上角的“安装”来下载应用程序。下载完成后,在终端中输入以下内容:

python --version

你应该看到 Python 3.9.4。如果是的话,那么恭喜你,你已经安装了 Python!您现在可以前进到安装和升级 pip 。如果没有,你可能在某个地方走错了。如果您仍然看到 3.6 或更高的版本,您可能会没事。如果不是,你需要重新追踪你的步骤…

马科斯

如果你有一台 Mac,你可能已经安装了 Python,但是你使用哪个版本的 Python 将取决于你的操作系统。为了确定哪个版本,请通过按 command + space 打开 spotlight search,键入 Terminal,然后按 enter。打开终端后,通过分别输入以下命令来检查 Python 版本、Python 2 版本和 Python 3 版本:

python --versionpython2 --versionpython3 --version

这里唯一真正重要的是 Python 3 版本的输出。您将需要使用最新版本的 Python(本文发布时是 3.9.4),所以如果您的 Python 3 版本不是这个版本,或者您根本没有 Python 3,您将需要安装它。(如果没有 Python 3,可能一输入就提示安装。请忽略此提示,并按照这些步骤进行操作。)只需几个步骤就可以安装 Python 3:

1)导航至 Mac OS X 的官方 Python 下载页面。

2)点击最新的 Python 3 版本。

3)点击适合您的 macOS 版本的 macOS 安装程序。(这很可能是 macOS 64 位 Intel 安装程序。)这样做会下载官方的安装向导。官方安装程序非常简单,会指导你完成剩下的过程。

恭喜你!你刚刚安装了 Python。现在,您可以关闭安装窗口,并将安装包移到垃圾箱中。

安装和升级 pip

Pip 是 Python 的一个包管理工具。根据您的操作系统,您可能已经有了 pip,但是我们不想在这里讨论任何可能,所以在终端/命令提示符下运行以下命令来确定您拥有的是哪个版本:

**Windows** python -m pip --version**macOS** python3 -m pip --version

如果您已经安装了 pip,您可以通过运行以下命令来升级它:

**Windows** python -m pip install --upgrade pip**macOS** python3 -m pip install --upgrade pip

如果你能做到这一点,继续前进安装顶部向下曲棍球刮刀。如果您没有安装 pip,您需要遵循以下步骤:

  1. 点击此链接,在另一个选项卡中打开一个巨大的 Python 文件。

2)右键单击选项卡并选择另存为。将文件保存为名为 get-pip 的 Python 文件。确保将 get-pip 保存在您将从中运行命令的用户目录中。

3)返回到您的终端,输入以下命令:

**Windows** python get-pip.py**macOS**
python3 get-pip.py

这会运行您刚刚下载的 Python 脚本,并安装一个名为 pip 的模块。Pip 是一个软件包安装程序,它将使你今后的生活更加轻松。

安装自上而下曲棍球刮刀

现在有趣的部分来了:安装 TopDownHockey 刮刀。在您的终端中,只需输入以下命令:

**Windows**
python -m pip install TopDownHockey_Scraper**macOS**
python3 -m pip install TopDownHockey_Scraper

这不仅会安装最新版本的 scraper(在撰写本文时是 1.0.6),还会安装其他必需的包,如 bs4、numpy 和 requests,前提是您还没有安装它们。如果您在安装时看到警告,请记下它们,但现在先把它们放在一边,然后进入下一步。

安装 Jupyter 实验室

Jupyter Lab 是一个集成开发环境(IDE)。对于那些不熟悉这个术语的人来说,你基本上可以把 IDE 想象成一个运行代码的地方。Jupyter 实验室特别为运行 Python 提供了一个非常用户友好的界面,我已经将本教程的后半部分打包成了一个 Jupyter 笔记本,您可以在 Jupyter 实验室访问它。下载它有一点痛苦,但是环境本身会比最初的下载给你带来更多的麻烦。

要下载这款出色的 IDE,请进入终端并输入以下命令:

**Windows**
python -m pip install jupyterlab**macOS**
python3 -m pip install jupyterlab

如果你能够毫无问题地安装它,你可以前进到打开 Jupyter Lab 并忽略接下来的步骤。但是,如果在安装过程中,您收到如下消息:

WARNING: The scripts jlpm.exe, jupyter-lab.exe, jupyter-labextension.exe and jupyter-labhub.exe are installed in 'C:\Users\YourName\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.9_qbz5n2kfra8p0\LocalCache\local-packages\Python39\Scripts' which is not on PATH

您需要更新您的路径。这个过程将取决于您的操作系统。

Windows 操作系统

  1. 通过在搜索栏中键入控制面板并打开它来打开控制面板。
  2. 选择用户帐户,然后在用户帐户中,再次选择用户帐户。
  3. 选择更改我的环境变量。
  4. 在用户的环境变量中,突出显示“Path”变量并选择 edit。
  5. 在编辑环境变量屏幕中,选择“New”并逐个输入三个新路径:
C:\Users\YourName\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.9_qbz5n2kfra8p0\LocalCache\local-packages\Python39\ScriptsC:\Users\YourName\AppData\Local\Programs\Python\Python39\C:\Users\YourName\AppData\Local\Programs\Python\Python39\Scripts\

(一定要把你的名字改成你的用户名!)

完成这些步骤后,请确保单击确定以确认您的更改,然后离开控制面板。(如果你对这个过程有疑问,我制作了这个视频,可能也会有所帮助。)

macOS

我没有苹果电脑作为我的个人电脑,所以我不能在这里建立一个视频或验证过程。但是,根据 JupyterLab 文档,这是修复路径的方法:

如果使用pip install --user安装,您必须将用户级bin目录添加到您的PATH环境变量中,以便启动jupyter lab。如果你使用的是 Unix 的衍生版本(FreeBSD,GNU / Linux,OS X),你可以通过使用export PATH="$HOME/.local/bin:$PATH"命令来实现。

尝试在终端中输入导出路径命令。

开放 Jupyter 实验室

一旦您安装了 Jupyter Lab 并准备打开它,请打开一个新的终端窗口并在其中运行以下命令:

**Windows**
python -m jupyter lab**macOS**
python3 -m jupyter lab

如果你成功打开了 Jupyter 实验室,你现在可以开始教程。

**如果您仍然无法让 Jupyter Lab 在路径上运行,**但是您已经安装了 Python,请尝试以下两个命令:

**Windows** python -m pip install jupyter notebook
python -m notebook**macOS** python3 -m pip install jupyter notebook
python3 -m notebook

这应该会打开一个 Jupyter 笔记本浏览器,和 Jupyter 实验室很像。它没有一些相同的功能,但在最坏的情况下,它仍然足以完成本教程。

开始教程

如果您已经成功打开 Jupyter Lab(或 Jupyter Notebook),“终端”将在您当前使用的窗口中打开一个新标签。它应该看起来像这样。

这是 Jupyter 实验室的主屏幕。如果你在这里成功了,你基本上就自由了。如果你只是设法打开 Jupyter 笔记本,你会看到更多这样的东西:

如果您看到以下内容:

进入你的终端,从命令提示符下复制并粘贴两个 URL 中最上面的一个到你的浏览器中。

本教程的其余部分对您来说应该相对容易。现在你已经进入了,是时候为这个教程下载交互式 Python 笔记本(ipynb)了。

然后在 Jupyter Lab 中,单击屏幕左上角的文件夹选项卡,导航到下载它的目录,双击打开它。如果在 Jupyter Notebook 中,默认情况下会列出您的目录,您可以简单地通过这种方式导航到那里。

无论哪种方式,一旦您打开教程,您应该会看到类似这样的内容:

如果你和我一样,这对你来说太亮了。在做任何其他事情之前,对自己好一点,将鼠标悬停在设置、JupyterLab 主题上,然后切换到 JupyterLab 黑暗模式,将事情更改为黑暗模式:

那好多了。也可能不是,在这种情况下,您可以随意切换回光照模式。不管怎样,我们开始吧。

这是一个交互式 Python 笔记本。它使运行代码变得容易一百万倍。在 Jupyter Lab 中,您可以同时打开多个笔记本。继续操作,再次点击左上角的文件夹图标将其最小化,因为我们不再需要它,同时关闭启动器选项卡。下面屏幕截图中可见的单元格是 markdown 单元格,构建这些单元格是为了直观地显示文本,而不是为了执行代码:

您可以通过查看选项卡正下方(Jupyter 实验室内)并看到该单元格类型被列为减价单元格来确认这一点。如果您按 shift + enter,Python 将有效地“执行”markdown 单元格(在本例中不做任何事情)并移动到下一个单元格。现在你在下一个包含代码的单元格中,Jupyter 实验室将它列为代码单元格。

现在,关于 Jupyter Lab,您真正需要知道的是使用 shift + enter 运行命令。然而,如果你有兴趣了解更多关于这个接口的信息或者你有问题,我建议你参考官方文档。

从现在开始,我将在这篇中型文章中提供一个演示,它基本上与 Jupyter 实验室中的演示相同,只是提供了一些截图来显示您的输出应该是什么样子。如果你愿意,你可以随意关闭这个浏览器,把 Jupyter 实验室切换到全屏,在那里完成这个教程;只需记住使用 shift+enter 来运行单元格,如果您对输出的外观感到困惑,请参考本教程。或者,如果您执意要在 Jupyter Lab 之外的不同环境中运行您的代码,您可以在该环境中键入屏幕截图中的所有命令。

事不宜迟,让我们开始吧。

导入我们的包

这些包中的每一个都已经被放到了 TopDownHockey_Scraper 中,所以你已经把它们安装到了你的电脑中。以下代码行将它们导入到您当前的 Python 会话中。

您实际上是在运行五个独立的大型 Python 代码库,每个代码库在您的会话中存储各种函数和变量。特别是 TopDownHockey Scraper 在其代码末尾有几个 prints 语句,它写下了您所看到的消息。

搜集数据

我们将使用TopDownHockey_Scraper包中的TopDownHockey_EliteProspects_Scraper module从精英潜在客户那里收集数据。

  • 一个模块是一个包含 Python 语句和定义的文件。它也可以被认为是一个代码库。Python 包是模块的集合。

请注意,我们将每个模块都作为全名的缩写导入。这是因为为了从一个模块中调用一个函数,我们每次都需要在函数前面键入模块的名称,而键入这些较短的名称要容易得多。例如,当我们稍后从TopDownHockey_EliteProspects_Scraper模型中调用get_skaters函数时,我们将键入tdhepscrape.get_skaters。这比敲TopDownHockey_EliteProspects_Scraper.get_skaters更有效率。

get_skaters函数有两个参数:一个或多个赛季和一个或多个联赛。在这种情况下,我们将收集过去两个赛季的 AHL 和 NHL 数据,这意味着我们将构建两个lists,然后将它们提供给我们的函数。

  • 列表是以一定顺序存在的一组可变元素。在这种情况下,我们的列表将由两个字符串组成。

我们的函数将抓取我们提供的联赛和赛季的所有数据,并返回一个dataframe

  • 数据帧是包含行和列的二维数据结构。

我们将把这个数据帧分配给一个名为ahl_nhl_skaters_1719的对象。

下一个单元格顶部的“时间魔法”功能会告诉你刮花了多长时间。它应该需要大约一分钟。

清理数据

数据清理不是任何人的最爱,但它是数据分析的关键部分。您在分析开始时花在清理数据上的每一分钟都是对最终产品的投资,大多数情况下,您将获得正的投资回报。在早期识别和解决问题要容易得多。

在清理数据之前,我们想做的第一件事是查看一下数据。我们之前的命令将函数的输出存储为一个名为 ahl_nhl_skaters_1719 的数据帧,其中包含了我们需要的所有数据。让我们从看看这个对象开始这个过程。

我们打印的数据帧的左下角告诉我们,我们正在处理 4,088 行和 14 列。当我们查看我们的专栏时,我们会看到一个链接、一些识别信息和一些 boxcar 统计数据(对于那些不熟悉这个术语的人来说,这只是一个术语,指的是通过加号/减号进行的游戏的统计数据)。

Ou 播放器和 playername 专栏其实差不多,可以去掉一个;理想情况下,丑陋的球员列包含冗余的位置数据。为了做到这一点,我们将使用drop

  • Drop 允许您指定要从列或行中删除的标签。

如果你对事物是全新的,下面的线可能看起来令人生畏,但是它实际上是非常简单的。以下是指挥系统的工作方式:

  • 我们的代码行以ahl_nhl_skaters_1719 =开始,这意味着在此之后的输出将被分配给一个同名的对象。
  • 我们已经创建的对象(也称为 ahl_nhl_skaters_1719)是我们将要应用函数的对象。(本质上,我们将覆盖它。)
  • ahl_nhl_skaters_1719.drop中的句点告诉我们,我们将在该对象上使用 drop。
  • 在括号中,我们指定了将要删除的内容:名为 player 的列。

如您所见,没有输出被打印出来,因为我们将命令的输出赋给了一个新变量,而不是只打印它。让我们通过打印来再次检查我们是否做对了:

好吧,我们做了我们想做的。这个新的数据框架更好,但还远远不够理想。我们有我们的球队,然后是我们的 boxcar 统计,然后是一个大的丑陋的链接,只有在这之后我们才能真正看到赛季,联赛,球员和位置。理想情况下,这个关键标识符信息应该在开始,我们的 boxcar 统计数据应该在后面,只有这样我们才能看到链接。

为什么我们要看到链接栏呢?又大又丑,好像也没加什么东西。理想的布局难道不是没有这一列吗?

在完美的世界里,是的。但在这个不完美的世界里,我们偶尔会把两个完全不同的同名玩家搞混。为了演示,我们将使用loc只过滤出名字与 Sebastian Aho 相同的玩家。

  • loc 允许我们根据一组行和列的标签是否满足某个标准来定位它们。(官方文档没有确认 loc 是否代表任何东西,但我喜欢认为它代表 locate,它可能会帮助你这样想。)

在我们运行它并查看我们的 Sebastian Ahos 之前,让我们回顾一下这行代码的命令链:

  • ahl_nhl_skaters_1719.loc中的句号告诉我们,我们将在这个对象上使用loc函数。
  • 在括号中的loc函数中,ahl_nhl_skaters_1719.playername中的句点告诉我们,我们正在从该数据帧中选择 playername 列。这将返回一个series。(A 系列是带轴标签的一维数组)。
  • 然后我们设置playername系列等于 Sebastian Aho(使用两个等号。在 Python 中,我们一般用一个等号来给对象赋值,用两个来判断对象是否相等。
  • 本质上,这仅定位我们选择的系列中的值——playername列中的值——正好等于 Sebastian Aho 的行和列。

等等,什么?为什么在我们的数据框架中没有一个球员的名字和塞巴斯蒂安·阿霍一模一样?岛民组织中的防守队员可能是一个狂热的梦想,但绝对有一个塞巴斯蒂安·阿霍在卡罗莱纳飓风队中担任中锋。他在 2018-2019 赛季场均得分超过 1 分!这里没有叫塞巴斯蒂安·阿霍的人,这不对吧?

从技术上来说,它是可以的,事实上也的确如此。不幸的是,我们的数据带有一些空白。为了演示这一点,我们将使用iloc

这里,我们将使用 iloc 来定位轴标签为 0 的行。(在 Python 中,索引以 0 而不是 1 开始。)下面是这段代码的命令链的运行方式:

  • 就其本身而言,ahl_nhl_skaters_1719.playername返回一个包含数据帧中每个球员名字的序列,其顺序与他们在数据帧中的顺序相同。
  • 在这个列表中,iloc函数将定位我们的系列中所有轴标签为 0 的值。

嗯,你看看这个:在特里的 y 和表示名字结尾的单引号之间有一个空格。克里斯·特里显然存在于数据库中,但如果我们过滤名字完全是克里斯·特里的球员,我们将一无所获。亲自尝试一下:

有几种方法可以处理这个问题,但最简单的方法是清除 playername 字段中的空白。为了做到这一点,我们将使用str.strip()

让我们分解下一个单元格中的命令链:

  • 我们没有将语句的输出分配给 ahl_nhl_skaters_1719 数据帧,而是将其分配给ahl_nhl_skaters_1719.playername列。
  • 我们在原来的列上使用.str.strip()来删除这个字符串两端的空白。
  • 完成后,我们只想看看我们的塞巴斯蒂安·阿霍斯和克里斯·特里。我们不是只传递一个语句给loc,而是传递两个,并使用|来定位满足第一个或第二个标准的情况。(|在这种情况下可以解释为或。我希望 Python 能让我输入 or,但是乞丐不能挑肥拣瘦。)

这证实了两件事:

  1. 我们的 str.strip()函数起作用了,我们成功地去掉了克里斯·特里名字周围的空格。
  2. 有两个塞巴斯蒂安·阿霍。

理论上,我们可以通过使用名称和位置作为标识符,或者使用包含 playername 旁边的位置数据的原始 player 列来解决 Aho 问题。因为一个塞巴斯蒂安·阿霍是前锋,一个是防守队员,这将允许我们区分他们两个。

这种方法的问题是同名球员并不总是踢不同的位置。每一对塞巴斯蒂安·阿霍斯和科林·怀特打两个不同的位置,就有一对埃里克·古斯塔夫松或埃里克·卡尔松打相同的位置。(另一个叫埃里克·卡尔松的防守队员从未在 NHL 打过球,但他确实存在,他毁了我的 NHL 模型。)虽然这个项目的范围很小,但是您最终可能会过渡到范围更大的项目,并且需要一个适当的过程来处理这些问题。

值得庆幸的是,每个玩家都有自己独特的精英前景页面,因此也有自己独特的链接。再看看我们的塞巴斯蒂安·阿霍斯;布里奇波特的防守队员和卡罗莱纳的前锋有着不同的联系。他们并不都在这里,但两个埃里克·古斯塔夫森也是如此。这就是我们保持联系的原因。因为我们已经有了联系,我们不需要再担心像塞巴斯蒂安·阿霍这样的搭档。我们一开始并不真的需要清理我们的数据,但这是一个很好的实践,可以确保在继续前进之前做到这一点。

让数据看起来不错

我们陷入了塞巴斯蒂安·阿霍斯的兔子洞,完全偏离了主题,但请记住我之前为我们的数据设定的理想顺序:球员、赛季、球队、联赛和位置,然后我们的 boxcar 统计数据按照它们出现的顺序排列,最后是那个可怕但有用的链接。我们将使用 loc 告诉 Python 要保留哪些列,并实际上按照我们想要的顺序传递所有列。我们还将rename那个丑陋的 playername 专栏。

  • 重命名改变轴标签。

让我们在下面几行代码中分解命令链:

  • 我们在数据帧上使用了loc功能。
  • 我们使用两组支架,而不是一组支架。在外层括号中,我们简单地从:, 开始。这通知 Python 我们将处理列而不是行。
  • 在第二组括号中,我们传递一个我们希望保留的列名列表。在这种情况下,我们实际上保留了所有内容,只是顺序与之前不同。
  • 一旦第一行完成,ahl_nhl_skaters_1719 现在已经被覆盖,以反映我们的变化。在下一行中,我们使用 rename 语句。这类似于我们之前使用的 drop 语句,只是我们输入列的原始名称,一个冒号,然后输入您希望该列更改的名称。

这看起来棒极了!我们的数据是干净的,我们准备向前迈出一步。

为分析准备数据

还记得我们过去是如何用 loc 过滤掉名字叫克里斯·特里或者塞巴斯蒂安·阿霍的球员的吗?现在我们将使用 loc 构建两个独立的数据框架:一个用于 2017-2018 AHL 赛季,一个用于 2018-2019 NHL 赛季。

既然我们已经设置了两个独立的数据帧,我们将merge这两个数据帧并创建一个名为 ahl_1718_nhl_1819 的新数据帧。

  • merge 将共享公共列或索引的两个数据帧放在一起。

在我们运行下一行命令之前,让我们用一幅有用的图像来分解命令链:

  • 我们在 ahl_skaters_1718 上执行merge函数,并将我们的 nhl_skaters_1819 作为第一个参数传递给我们的函数。这意味着我们将合并 2017-2018 款阿勒和 2018-2019 款 NHLers。
  • 我们在链接上合并,这意味着我们使用玩家链接作为公共字段。本质上,我们添加了 2018-2019 年的 NHL 数据,用于与 2017-2018 年的 AHL 选手有相同联系的选手。
  • 我们将使用的合并类型是inner合并。下面的文氏图显示了不同类型的合并。(在这种情况下,合并和连接是可以互换的。)
  • 我们内部的合并意味着我们将只保留 2017-2018 年的 AHL 球员,他们也在 2018-2019 年出现在 NHL。

呀!我们的合并是成功的,我们得到了一个相当于 302 名球员的可靠样本,他们这一年在 AHL 打球,下一年在 NHL 打球,但是我们得到了一堆难看的列,上面有 x 和 y。

当我们合并两个具有相同名称的列的数据帧时,如果我们不在这些列上连接,就会发生这种情况:具有相同名称的列会重复。

这不是世界末日。其实挺好办的。我们知道新数据框的左侧(其列名附有 x)包含 2017-2018 年的 AHL 数据,我们知道右侧包含 2018-2019 年的 NHL 数据。(如果我们忘记了,我们的数据帧的名称实际上是一个提醒。)我们还知道,这些球员在不同的赛季不会改变名字或位置,所以我们可以只保留数据左侧的球员和位置,而删除右侧的球员和位置。

所以我们有 6 列要删除。让我们先做那件事。

看起来已经好多了,但还是有点乱。在我们做任何事情之前,我们应该将我们的链接字段移动到数据帧的最右侧,尽可能地远离我们的视线和头脑。不过,我们不是按照我们想要的顺序输入所有 19 个列名,而是要加快这个过程,并且用列表和附加到列表上的函数来获得一点乐趣。

  • 列表可以用许多不同的方式操作。

让我们具体分析下一个单元格中的内容:

  • 我们首先创建一个列表,它只是我们的数据框架中的列的名称。
  • 我们在列表中使用基于列表的remove方法来删除列表中的第一个项目“link”。
  • 请注意,与将更改分配给其他对象(如数据帧)不同,列表不需要使用等号进行严格的分配,而是会被自动覆盖。简单地在这里输入第二行代码将会永久地改变列表。
  • 然后,我们使用基于列表的append方法将“link”添加到列表的末尾。
  • 然后我们打印清单,以确保我们做对了事情。

现在我们已经得到了一个按照我们想要的顺序排列的列列表。下一步是在我们之前使用的方法中使用 loc 函数,首先给它提供:,来表明我们对列而不是行感兴趣,然后传递我们想要的列的列表。完成后,我们将重命名这些列,使它们更容易理解,然后打印新的 dataframe,以确保一切看起来都是正确的。

嘿,这开始看起来像是我们可以实际工作的东西了!我们在一边清楚地展示了 AHL 的数据,在另一边清楚地展示了 NHL 的数据。虽然我们已经取得了很大的进步,但我们还有一些地方需要改进。为了确定这些变化是什么,让我们使用dtypes

  • dtypes 返回数据帧中每一列的类型。

我们的数据框架中的每一列目前都是一个object类型。为了对它们执行数学函数,我们需要将它们转换成float类型。

  • float 类型是浮点数。整数在很多情况下是可以的,但是如果你像我们一样使用小数,你需要把它们转换成浮点数。

让我们用一个专栏来测试一下,只用 AHL 每场比赛的积分。这里的命令链非常简单:

  • 我们使用ahl_1718_nhl_1819.ahl_ppg对该列进行操作,将其视为一个系列。
  • 我们使用astype函数并传递float作为参数,将其改为浮点类型。
  • astype 将数据帧中的对象转换为特定类型。

不要把它分配给一个新对象,让我们打印它…

啊哦!看起来我们在这个列中有一些值仅仅是-,Python 不能将它们转换成浮点类型,这是有意义的,因为它们不包含数字数据。

让我们看一下具有该值的每一行,以确认情况确实如此:

好吧,所以在我们做其他事情之前我们需要先处理这个。我们还会在某个时候使用我们的“已玩游戏”列,因此我们还应该确保我们也清理了该列,以防其中也存在一些讨厌的值。

为了进行这种清理,我们将使用numpy模块中的where函数来将所有值从-更改为 0,并将所有其他值保持不变。请记住,我们将 numpy 作为 np 导入,因此当我们调用该模块的 where 函数时,我们只需键入 np.where 而不是 numpy.where。

  • numpy.where 如果满足条件则返回一个值,否则返回另一个值。

在这种情况下,以下是这些行的命令链:

  • 我们根据np.where语句的结果为 4 个不同的列分配新值。
  • 在我们的 np.where 语句中,如果当前值是-,我们将赋值 0。如果当前值不等于-,我们简单地返回之前的值。
  • 我们对四个字段重复这个过程——打了 NHL/AHL 的比赛和每场比赛的 NHL/AHL 积分——然后打印 ahl_ppg 值等于的行,以确保这是有效的。(我们应该看不到。)

酷,这很有效。现在我们可以把这些都改成浮点类型。然后,在我们新的浮动类型栏中,我们可以只过滤出在两个联赛中至少打了 20 场比赛的球员,并查看他们。

我们的数据现在处于可以真正开始工作的位置,这太棒了。

不幸的是,我们的样本量受到了很大的冲击;数据框中的行数告诉我们只剩下 113 名选手了。为了确定这是多大的问题,让我们通过构建两个独立的数据帧来看看我们有多少前锋和防守队员:一个只包含前锋,另一个只包含防守队员,然后使用len来确定这些数据帧的长度。

前锋和防守队员的数量目前被存储为两个独立的变量。我们不只是打印它们,而是使用 Python 的print函数来打印这些带有消息的值。

  • 打印打印输出。

在我们这样做之前,我们必须创建变量的新版本,现在是整数,也就是字符串。我们通过使用str来做到这一点。

  • str 将一个对象转换成一个字符串。

将字符串相互添加并打印出来非常容易,但是必须小心确保传递给打印函数的所有内容都是字符串。

所以我们有 77 名前锋和 36 名后卫。这不是很好,但对于一些基本的分析来说已经足够了。

分析数据

我们要做的第一件事是确定哪些球队在 2018-2019 年拥有最多的 NHL 前锋,他们在前一年从 AHL 跳了出来。

(展望未来,我将把进行这次跳跃的滑冰运动员称为“过渡球员”,他们在 2017-2018 年参加了至少 20 场 AHL 比赛,在 2018-2019 年参加了 20 场 NHL 比赛如果我特别提到进行这种跳跃的前锋或防守队员,我会称他们为“过渡前锋”或“过渡防守队员”)

为了做到这一点,我们将使用groupby按团队对我们的前锋数据帧进行分组,并计算每个团队的球员人数。

  • groupby 以某种方式对一个对象进行分组,并在组合结果之前对该组应用某种函数。

让我们看看下一行代码的命令链:

  • 我们将 nhl_team 作为参数传递给 groupby 这一组由 NHL 队列。
  • 我们从我们的组中提取玩家系列。
  • 我们用count()来统计每支队伍在这个系列赛中的人数。Count 是一个可以应用于组的函数。

现在我们有一个系列来告诉我们每支球队有多少球员。这太棒了!但是我们更希望有一个数据框架。它更容易使用和处理。我们将使用来自我们的pandas模块的DataFrame函数(我们将其作为pd导入)将它转换成一个数据帧。

现在,我们将使用sort_values函数来查看哪支球队拥有最多的球员。

  • sort_values 沿任一轴按值排序。默认设置是 x 轴或行。

我们还想从大到小排序,所以我们将 ascending 设置为 False。(默认情况下,它被设置为 true。)

最常见的团队是“totals”,可以更容易地解释为“multiple teams”。这是有道理的。在那之后,为每支球队效力的转换前锋的数量似乎相当均匀地分布在 1、2、3 和 4 之间。

但是还有一些东西很突出。还记得我们构建的每一个其他数据帧都有一个从 0 到最左边的值的索引吗?你可能没有注意到它,但是如果你向上翻,你会发现它是为他们每一个人准备的。这个没有那个。保存索引是一个很好的做法。为了添加一个,我们简单地使用reset_index

我们不仅要重置索引,还要再次对值进行排序,因为我们实际上并没有将带有排序值的数据帧赋给一个新变量。

对于初学编程的人来说,一行一行地写完全没问题,但是如果可能的话,用更少的行将多个语句链接在一起会更有效。对于这一个,只是做一点练习,我们将在一行中做所有的事情:首先按我们的 player 列中的值排序,然后重置其值被排序的新数据帧的索引。一旦我们完成了这个,让我们把它打印出来,以确保我们做对了。

现在我们已经有了一个索引,是时候仔细看看它了。它从 0 到 29,这意味着这里只有 30 个不同的值。totals 实际上也不是一支 NHL 球队,这意味着我们这里只有 29 支球队。NHL 有 31 支球队,所以肯定有两支球队在 2018-2019 年没有打过一场过渡前锋。

如果我们想知道这些团队是谁呢?我们可以去 NHL.com,一个接一个地寻找名单上的每支球队,找到我们遗漏的两支球队。会有用的。但这也是完全低效的,而且容易出错。

有一个更好的方法。让我们从使用 NHL 选手的完整数据框架中的团队栏上的set开始,以获得每个 NHL 团队的集合,然后看一看它。

  • set 返回对象中的每个唯一值。

好的,我们已经召集了所有国家冰球联盟的队伍。下一步是确定这个集合中的哪些值没有出现在我们的向前转换中。我们将使用isin来完成这项工作。

  • isin 确定数据帧中的元素是否包含在其他值中。

请注意 isin 是如何具体确定数据帧中的元素是否存在于其他值中的?我们不是在处理一个数据框架,我们是在处理一个集合。我们需要遵守规则,将此集合更改为数据帧。让我们这样做,看看它:

这看起来不错。我们的指数上升到 31,这意味着我们有 32 个值,但最后一个是“总数”,它实际上不是一个团队,所以我们实际上有 31 个团队。

但是,有一个问题:列名。是 0。当您构建一个没有列名的 dataframe 时,就会发生这种情况。

让我们用之前用过的 rename 函数来快速改变一下,只是这一次,我们不是以字符串的形式传递要重命名的列名,而是以整数的形式传递,因为这就是 0。(因此,我们的 0 周围没有引号。)

现在我们的专栏命名为 nhl_team,可以进入正题了。请记住,我们的最终目标是这个数据框架的一个版本,它只包含在我们向前过渡中不存在的球队。我们的最终目标是在我们的过渡前锋数据框架中建立一个球队列表,然后过滤我们的 NHL 球队数据框架,只包括没有出现在该列表中的球队。

不过,在我们这样做之前,我们实际上只是要看看在我们的过渡前锋名单中的球队,只是为了熟悉 isin 功能。下面是命令链的工作方式:

  • 第一行将 forward_counts.nhl_team 系列转化为一个名为teams_in_transitioning_forwards的列表。
  • 第二行使用带有条件的典型 loc 语句。我们首先指定我们将使用来自团队系列的值作为指标。然后我们对过滤器应用isin,并将我们新的 teams _ in _ transitioning _ forwards 列表传递给 isin。

该指数最高为 31,这不太合理。如果原始数据帧中有 2 个值不满足这个条件,它不应该在 29 处达到顶点吗?

技术上来说,没必要。在打印出来的数据帧中必须有30 个不同的值(包括讨厌的“总计”值),但实际上有;索引没有从原始位置重新设置。如果你仔细看索引,你会设法找到一些遗漏的队伍。不相信我?看看我们数据帧的长度。

所以,过滤器确实起作用了。现在是时候找到不符合这个条件的队伍了。这只需要一个简单的步骤:将~操作符添加到我们的 filter 语句的开头。该过滤器返回不符合条件的内容,而不是符合条件的内容。

这就对了。两支拥有零过渡前锋的球队是哥伦布蓝夹克队和底特律红翼队。我不会把它解读成一个小问题的答案,但是当这些问题出现时,培养回答这些问题的技巧是很重要的。

早些时候我说过,每支球队的转换前锋人数看起来相当均匀,在 14 人之间。这显然是不正确的,因为有两个值为 0 的团队我没有考虑。在我们进一步仔细检查最初的声明之前,我们应该修复我们已经知道完全错误的部分,并将这些团队及其零值添加到我们的原始数据框架中。

不过,很快地,让我们回忆一下原始数据帧的样子。我们不打印全部内容,我们只打印head,因为我们只想看到它的结构。

这和我们的失踪队数据框有相同的结构,只是多了一个球员栏。我们知道,在缺失球队的数据帧中,每个球队的球员值都是 0,所以我们可以使用assign在另一个数据帧中创建一个新列,然后向球员列提供值 0。

  • 分配给数据帧分配新列或覆盖旧列。

我们丢失的团队数据帧现在具有与原始数据帧完全相同的结构。我们将通过使用来自pandasconcat函数来组合它们。

  • concat 连接特定访问中的对象。

这看起来几乎完美,但我们的指数有点偏离。注意它是如何从 1 到 29,然后到 12,再到 17 的?我们需要重置索引,但与之前不同的是,这有点复杂,因为这个数据帧已经有了一个索引。为了显示这种复杂性,我们将只打印该数据帧的头部,并重置其索引:

在已经有索引的数据帧上重新设置索引会创建一个新的“索引”列,这很难看,而且对任何人都没有帮助,因为它不是实际的索引。在继续前进之前,我们会想要摆脱它。

好了,我们的数据框架准备好了。

将数据可视化

现在是时候可视化我们的数据了。为了做到这一点,我们将使用前面作为sns导入的seaborn模块中的countplot函数。

所以,至少有一名前锋的队伍实际上*相当*均地分布在 1 到 4 名前锋之间,但是没有前锋的两个队伍有点偏离。*

现在让我们来玩一点数据可视化。在所有转型的球员中,不仅仅是前锋,在 AHL 的得分预测在 NHL 的得分有多准确?我喜欢通过确定两个变量的相关性来回答这样的问题。我们可以使用来自numpy(我们导入为np)的corrcoef函数来计算这两者之间的相关系数。

是的,那是一种丑陋的输出。我们并不真的需要这整个相关系数矩阵;我们只需要第一个和第二个值之间的相关性,我们可以通过在命令的末尾添加[0, 1]来提取它。这将告诉 Python 输入两个数组中的第一个(索引为 0 的那个)并提取第二个项目(索引为 1)。请看下面:

好多了。这是两个变量之间的相关系数。但我更喜欢 R,它告诉我们一个变量的方差有多少可以用另一个来解释。为了提取 R,给定相关系数,简单地将初始值乘以它本身。这可以通过在数字的末尾加上**2来实现。

这里我们真的不需要超过两位小数点,所以让我们把这个输出赋值给一个叫做 RSQ 的变量,然后把它四舍五入到两位小数。

酷,我们已经得到了 R。现在是时候构建另一个数据可视化了,只需要比我们在计数图中投入更多一点努力。这一次,我们将使用regplot

  • regplot 绘制数据和线性回归模型拟合。

我们将把我们的第一列——x 变量——作为 x 输入 regplot,然后对 y 做同样的处理,然后选择指定蓝绿色,因为我喜欢它。

厉害!正如我们所看到的,这些值并没有在我们的线上紧密地组合在一起,但是有一个可靠的相关性。这已经很有用了,但是我们还可以做更多来改进它。我想到的第一件事是添加标签,通过添加更容易解释的列名来清楚地说明这里发生了什么。

为了添加这些信息,我们首先写出同一行代码。这让 Python 知道我们将打印这个图。然后,我们使用 matplotlib.pyplot(我们导入为 plt)中的 xlabel 和 ylabel 函数来更改我们要打印的绘图的 x 标签。

那看起来好多了!不过,在左上角有很多空白的地方;plt.text 函数允许我们在绘图的选定位置打印文本。我们将重复这个单元格,只是底部有 plt.text。

干得好!这是一个非常可靠的数据可视化。没什么太花哨的,但也不一定。

进行更深入的数据分析

现在是时候使用 Gabriel Desjardins 在联赛等效性中提出的方法建立一个非常基本的 NHL 等效性模型了。这是他的公式:

  • 联赛 x 的质量=(第 2 年 NHL 的*均 PPG)/(第 1 年联赛 x 的*均 PPG)

这是一个非常简单的公式,但它足够好,可以开始使用,而且效果惊人。我们将通过使用 numpy 的均值函数来计算每个联盟每场比赛的*均得分,然后将它们除以另一个。

因此,AHL 的一个点相当于 NHL 的 0.45 个点,这与 Desjardins 在他的论文中使用旧数据得出的值相同。有趣的是这是怎么回事。

本教程非常接*完成。只剩下一件事要做:定义我们自己的函数,为一个在 AHL 打过球的球员获取一个 NHLe 值。

您已经对函数有些熟悉了,因为在本教程中您已经使用了一些函数。给你一个正式的定义,函数是一段代码,只在你调用它的时候运行。我们将构建一个函数,用户输入每场比赛的 AHL 积分值,该函数获取该值并自动给出 NHLe 分数。下面是定义函数的工作方式:

  • 在第 1 行中,我们定义了函数的名称,并在括号中输入函数接受的每个参数。我们用冒号结束第一行。
  • 函数中第 1 行之后的每一行都缩进。通知 Python 该行是函数的一部分。
  • 在函数中,我们编写代码。

我们的功能被定义了。什么都没发生,因为我们只是存储了函数,还没有调用它。现在是时候结束了。

简单来说,我们说的是一个在 AHL 的 40 场比赛中得了 40 分的球员,因此他的 AHL 场均得分为 1。如果我们将他每场比赛的 AHL 点数作为参数传递给我们的函数,我们会收到他的 NHLe:

因此,根据我们建立的非常基本的 NHLe 模型,每场比赛 1 分的 AHL 分数就有每 82 场比赛 37 分的 NHLe。在我看来很合理。

祝贺您完成 Python 曲棍球分析教程!

如果您现在参加一个 Python 测验,其中只包含我们在本教程中介绍的内容,您可能仍然不会取得非常好的成绩。没关系!计算机编程最酷的地方在于,你不一定需要记住任何东西。这很有帮助,但是除非你在面试,否则你总是可以回头看看你以前的工作,想想你是怎么做的。

虽然这标志着我们教程的结束,但我希望它只是标志着你进入曲棍球分析和计算机编程之旅的开始。社区需要更多聪明的人来提问和寻找答案,如果你有能力完成本教程,我们可以使用你。

如果你对改进本教程有任何其他意见、问题、顾虑或建议,请不要犹豫,通过 Twitter @TopDownHockey 联系我,或者直接发电子邮件到patrick.s.bacon@gmail.com给我。

Python:用 pyenv、pyenv-virtualenv & pipX 创建干净的学习环境

原文:https://towardsdatascience.com/python-how-to-create-a-clean-learning-environment-with-pyenv-pyenv-virtualenv-pipx-ed17fbd9b790?source=collection_archive---------0-----------------------

设置您的 Python 学习环境,而不污染您未来的开发环境

萨法尔·萨法罗夫在 Unsplash 上拍摄的照片

我最*写了一篇文章,详细介绍了我学习 Python 编程语言的早期经历。这篇文章的主要焦点是在学习之旅的一开始就建立一个合适的开发环境的重要性。这是许多针对初学者的教程和在线课程经常忽略的内容,可能会导致许多学习者在准备开始自己的一些项目时,陷入一个复杂而不可行的开发环境。这是我从惨痛的教训中学到的!

https://medium.com/pythoneers/want-to-learn-python-what-nobody-tells-you-until-it-is-too-late-16bb1f473095

设置一个合适的 Python 环境,使您能够有效地管理不同 Python 版本的多个安装,并将所有包/库及其相关的依赖项与您的全局系统和用户环境隔离开来,这一点的重要性肯定是不可低估的。

在本文中,我打算向您介绍我当前的 Python 开发环境,并列出允许您复制它的步骤。以下步骤与我在运行 macOS Big Sur 的 MacBook Pro 上使用默认 zsh shell 的设置相关。我将向您展示如何:

  • 使用 pyenv 安装和管理 Python 版本
  • 使用 pyenv-virtualenv 创建虚拟环境,并为学习和项目目录/存储库提供隔离
  • 使用 pipX 安装基于 CLI 的工具和软件包,以便在所有目录和虚拟环境中使用

这不是一个完整的教程,解释这些工具必须提供的所有功能,而是简单地解释如何安装每个工具,以及如何使用它们来隔离您的学习和后续项目环境。

系统 Python 版本 2.7 —请勿触摸!

**但首先发出警告!**您的 Mac 将已经安装了 Python 2.7 版本。Mac 操作系统使用此 Python 安装(位于 usr/bin/python 中)来执行您的计算机*稳运行所需的关键任务。请务必不要从您的计算机上删除这个版本的 Python,并且强烈建议您不要将它用于您自己的编程活动,因为您可能会使您的计算机变得无用!

终端(命令行界面)

如果您希望发展开发人员的技能,或者只是想提高您对计算机的掌握程度,您需要学习如何使用一个关键工具,那就是命令行界面(CLI)。这是通过在 Mac 上打开“终端”应用程序来启动的(应用程序→实用程序)。这是一个应用程序,它在您的计算机上打开一个文本窗口(称为 Shell ),并允许您键入基于文本的命令或一系列指令,然后您的计算机将直接执行这些命令或指令。

有许多免费的课程和教程可以帮助您学习如何有效地使用 CLI,但是对于本文的目的和设置您的 Python 环境,我将简单地提供一些命令,您可以将它们复制并粘贴到终端窗口的提示符中。

预设置和目录结构

在安装一些特定于 Python 的工具之前,您首先需要为您的 Mac 提供帮助创建和管理 Python 环境所需的工具。

1.安装命令行工具

虽然您的 Mac 已经有了大量有用的命令和工具,可以通过命令行界面来指导您的计算机的活动,但为了完成我们的 Python 设置,这些需要用一些开发人员特定的工具来增强。这些可以通过直接安装苹果 Xcode 开发者应用中包含的工具子集来获得。

只需打开终端,输入以下命令,然后按照屏幕上出现的一系列弹出窗口中的说明进行操作:

xcode-select --install

2.安装自制软件包管理器

家酿是一个软件包管理器的 Mac,让您轻松地安装和管理免费的开源软件应用程序和工具,直接从终端窗口。我们将使用 Homebrew 来安装 pyenv、pyenv-virtualenv 和 pipX,但是很可能 Homebrew 将成为您将来需要的更多工具的软件包管理器的选择。

要安装 Homebrew,只需将下面一行复制到您的终端窗口,然后按照屏幕上的任何指示操作:

/bin/bash -c “$(curl -fsSL [https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh](https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh))"

3.创建目录结构

在您安装 Python 并开始创建虚拟环境之前,创建一个简单的目录结构非常重要,您可以在其中组织您的项目和相关文件。下图所示的结构对我很有用,因为它让我能够圈住我最初的学习活动,同时创造一个空间,一旦我发展了足够的技能,就可以开始从事我自己的一些具体项目。

--> Python
   --> Learning
      --> Python_Playground
      --> Training_Course1
      --> Training_Course2
   --> Projects
      --> Project1
      --> Project2

在随后的章节中,您将了解如何为每个文件夹安装/分配特定的 Python 版本,以及如何为每个文件夹创建单独的虚拟环境,从而完全控制每个文件夹中安装的库和模块。

您会注意到,在学习目录中,除了特定的培训课程文件夹之外,我还创建了一个名为 Python Playground 的文件夹。我使用 Python Playground 文件夹作为一个安全的地方来试验和尝试我从培训教程中学到的东西的变体。我在这个环境中安装了最新版本的 Python,并自由地安装了我想要试验的所有库和模块,安全地知道它们将被完全隔离,不会影响我的整体开发环境。

在特定的培训课程文件夹中,我可以安装符合特定培训课程的 Python 版本,以及完成课程或教程所需的任何特定库或模块。

这个阶段的项目文件夹是为将来的使用而创建的——指定的、隔离的环境,在那里我可以开始构建我的第一个项目。

使用 pyenv 安装 Python

我们将使用名为 pyenv 的工具安装最新版本的 Python,而不是简单地通过 Homebrew、APT 或从 Python.org 直接安装到我们的全球系统环境中。Pyenv 允许您安装多个版本的 Python,并让您完全控制希望使用哪个版本,何时何地使用特定版本,并让您能够快速简单地在这些版本之间切换。

要使用 Homebrew 安装 pyenv,您可能首先需要安装一些关键的依赖项。将以下内容复制到终端中。

brew install openssl readline sqlite3 xz zlib

现在,您可以通过在终端中输入以下内容来安装 pyenv。

brew install pyenv

安装完成后,您还有一步要完成,以确保 pyenv 可以添加到您的路径中,允许它在命令行上正确工作,并启用填充和自动完成。

在终端中依次输入以下各行,这将更新。包含所需信息的 zshrc 文件。

echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.zshrc
echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.zshrc
echo -e 'if command -v pyenv 1>/dev/null 2>&1; then\n  eval "$(pyenv init -)"\nfi' >> ~/.zshrc

现在只需关闭并重新加载终端窗口,这将确保 Shell 拾取这些更改。

祝贺您,您现在已经准备好使用 pyenv 安装您的第一个 Python 版本了。

要查看可以通过 pyenv 安装的 Python 的所有可用版本的列表,只需在终端中键入以下内容。

pyenv install --list

一旦您确定了想要的版本,请输入以下命令进行安装(用您选择的版本替换 3.9.1)。您可以根据需要多次重复此步骤,以安装所有需要的版本。

pyenv install 3.9.1

现在您已经安装了多个版本的 Python,您可以开始选择在哪些实例中使用哪个版本。

输入命令pyenv versions将显示您当前安装在计算机上的所有 Python 版本。要将特定版本设置为默认的全局版本,只需输入以下命令(用您选择的版本替换 3.9.1)。

pyenv global 3.9.1

您还可以设置要在特定目录中使用的特定 Python 版本,方法是导航到该目录,然后运行以下命令(用您选择的版本替换 3.8.1)

pyenv local 3.8.1

或者,您可以通过输入以下命令,简单地设置要在当前 Shell 中使用的 Python 版本(用您选择的版本替换 3.8.1)。

pyenv shell 3.8.1

您可以通过重新输入相关命令并输入不同的版本号来更改分配给上述任何场景的 Python 版本。

pyenv-virtualenv

JESHOOTS.COM在 Unsplash 上拍照

既然您已经习惯了使用 pyenv 安装和管理多个版本的 Python,我们就继续创建一个虚拟环境。这将允许您为特定的目录/文件夹分配特定版本的 Python,并确保安装在该文件夹中的所有库和依赖项都是完全隔离的。除非您明确地将它们安装在其他特定的虚拟环境中或者全局安装它们,否则它们对其他目录不可用(不推荐,我将在下一节向您展示如何使用 pipX 来实现这一点)。

首先,您需要安装 pyenv-virtualenv。您可以通过在终端中输入以下命令来做到这一点。

brew install pyenv-virtualenv

您现在可以创建您的第一个虚拟环境。在本例中,您将为 Python Playground 目录创建一个虚拟环境。同样的步骤也适用于为您希望的任何目录创建虚拟环境。

使用终端,导航到将要创建虚拟环境的目录,在本例中是 Python Playground。

cd Python/Learning/Python_Playgrounds

接下来,您需要设置您希望在环境中使用的 Python 版本(在这个例子中我们将使用 3.9.1,但是您可以用您需要的版本替换)。

pyenv local 3.9.1

要创建虚拟环境,请输入下面的命令。pyenv virtualenv是创建环境的实际命令。版本号与您刚刚设置为环境的本地版本的 Python 版本一致,最后一部分是虚拟环境的名称。我个人的偏好是在名称前加上“venv ”,然后将虚拟环境的名称与它相关的目录的名称对齐。

pyenv virtualenv 3.9.1 venv_python_playground

最后一步是将目录的本地 python 版本设置为虚拟环境。

pyenv local venv_python_playground

当您想要在虚拟环境中工作时,您需要激活它。一旦激活,将使用指定的 Python 版本,并且您安装的任何库都将包含在环境中。在虚拟环境中完成工作后,请务必记住停用虚拟环境,以便返回到您的全球环境。使用以下命令可以激活和停用环境。

pyenv activate venv_python_playground
pyenv deactivate

要在进出相关目录时启用虚拟环境的自动激活和停用,只需在终端中输入以下命令来更新。包含所需信息的 zshrc 文件。

echo 'eval "$(pyenv virtualenv-init -)"' >> ~/.zshrc

现在您知道了如何为您的每个培训课程和未来项目创建一个隔离的 Python 环境。

pipX

随着您 Python 学习经验的进步,并开始从事自己的一些项目,您可能会开始确定您希望在每个项目中使用的某些包或工具。您可以使用一个名为 pipX 的工具,而不是在每个虚拟环境中复制安装。

pipX 将创建一个特定的全局虚拟环境,在该环境中可以安装这些工具和软件包,这些工具和软件包可以通过命令行、所有全局目录和所有虚拟环境进行访问。这对于 Python formatters (Autopep8 或 Black)、Pylint 等 linters 或 Jupyter Notebooks 等您可能需要在所有项目中使用的工具尤其有用。

要安装 pipX,请在您的终端中运行以下命令。

brew install pipx
pipx ensurepath

然后用 pipX 安装、升级或卸载软件包和工具,您只需使用pipx installpipx upgradepipx uninstall,就像使用 pip 管理您的库一样。您还可以使用pipx list来查看您已经用 pipX 安装的包的列表。

结论

干得好!您现在有了一个很好的 Python 开发设置,允许您自由地进行实验和学习,使您能够尝试尽可能多的包和库,安全地知道一切都隔离在一个组织良好和受控的环境中。

此外,您已经学习了一些进入 Python 开发旅程下一阶段所需的工具和技能,并且拥有一个干净整洁的项目基础设施,随时等待您充分利用。

后续步骤

既然已经建立了基本的开发环境,为什么不花些时间探索一下开发设置中更有趣和可定制的元素呢?下面列出了一些可供您进一步探索的领域…

  1. 选择并安装您选择的 IDE,并定制主题和扩展
  2. 用 iTerm2 &哦,我的 ZSH 升级你的基本 mac 终端
  3. 安装并学习如何使用 GIT 和 GitHub

参考

如果您希望探索我在本文中提到的一些工具,并增长您对它们全部功能的了解,那么下面的链接可能会很有用…

  1. https://github.com/pyenv/pyenv
  2. https://realpython.com/intro-to-pyenv/
  3. 【https://github.com/pyenv/pyenv-virtualenv
  4. https://github.com/pipxproject/pipx

用于数据科学的 Python:初学者指南

原文:https://towardsdatascience.com/python-i-data-types-and-operators-variable-assignment-and-print-6f2eafb1b899?source=collection_archive---------37-----------------------

学习 Python 基础知识,以便在数据科学项目中使用它。

图片来自皮克斯拜的约翰逊·马丁

一、关于 Python🐍

由荷兰程序员吉多·范·罗苏姆在Centrum wisk unde&Informatica 创建的 Python 于 1991 年首次亮相。三十多年来,它广受欢迎,赢得了“编程语言的瑞士军刀”的美誉。以下是几个原因:

  • 一个强大的社区已经开发了大量的免费库和包,允许 Python 与不同领域的技术相结合。Python 在数据科学方面很受欢迎,但是你也可以用它来进行 web 开发、金融、设计游戏等等。
  • Python 也是一种多范例语言——换句话说,它既允许结构化/功能性的编程,也允许面向对象的编程。
  • 作为一种高级语言,语法被设计得直观易读。这一特性对初学者以及 Instagram 和谷歌这样的大公司都很有吸引力。

在数据科学、人工智能和机器学习等新兴领域,强大的社区、丰富的包、范式灵活性和语法简单性使初学者和专业人士能够专注于见解和创新。

三。数据类型

Python 大约有十二种不同的数据类型,在本教程中我将向您介绍其中的五种: stringintegerfloatboolean ,以及 list 。我还将向您展示一些可以在 Python 中用于您的项目的基本函数和方法。这应该足以让你开始,并为下一个教程做好准备:熊猫 I 。

1.Python 字符串

字符串是一系列字符。它们可以是数字和非数字字符。在 Python 中,它们用引号括起来。你会注意到在字符串的上方,有一个 hashtag 后面的描述。这叫做评论。标签后面的任何内容都是评论。

# This is a comment. The line below is a string in Python.
“This is a string.”   # you can also use them inline

在 Python 中,我们将任何类型的值赋给 变量 以便使用它们。看起来像这样:

the_string = “This is a string.”

单等号(=) 用于给变量赋值。以下是一些关于变量的注意事项:

  • 命名约定表明变量应该是小写的
  • 字母和数字都可以,但是数字不能没有字母(例如“a1”可以,而“1”不行)。
  • 不允许使用特殊字符和空格。下划线**(" _ "**应该用来代替空格
  • 你可以给一个变量起任何你想要的名字,但是有几个名字你不能用,因为它们已经是 Python 关键字了。请参见下面的列表:
False      class      finally    is         return
None       continue   for        lambda     try
True       def        from       nonlocal   while
and        del        global     not        with
as         elif       if         or         yield
pass       else       import     assert
break      except     in         raise

要打印所有输出,包括 Python 中的字符串,我们使用 print() :

[in]  print(the_string)
[out] "This is a string."

你可以连接或者像这样把字符串放在一起:

[in]  another_string = “This is another string.”
      a_combo_string = the_string + another string
      print(a_combo_string)
[out] "This is a string. This is another string."

字符串是不可变的,这意味着它们不能被改变。它们需要被重新分配来改变(如果你愿意,你可以回收这个变量)。

如果想知道任意值或变量的数据类型,可以使用 type()

[in]  print(type(the_string))
[out]  <class 'str'>

2.Python 整数

Python 中的整数是不带小数点的数字。它们可以是积极的,也可以是消极的。

[in]  int_num = 1
      print(int_num)
[out] 1

您可以使用算术运算符对数字数据类型整数执行运算。

[in]  print(1 + 2)
[out] 3

您可以将它们赋给变量,也可以执行操作。

[in]  a = 5
      b = 6
      c = a — b 
      print(c)
[out] -1

你也可以乘除:

[in]  print(a * b / c)
[out] -30.0

3.Python 浮动

像整数一样,浮点数也是一种数字数据类型。它们也可以是积极的和消极的。然而,他们是浮点小数,这意味着他们有一个小数点,而不是整数。然而,我们可以在整数上执行的大多数操作都可以在浮点数上执行。

[in]  int_float = 2.5
      print(num_float)
      d = 1.33
      e = 4.67
      f = d + e
      g = f — 7
      print(g)
[out] 2.5
      1.0

4.Python 布尔值

布尔值不是就是

[in]  print(type(True))
      print(type(False))
[out] <class 'bool'>
      <class 'bool'>

您可以使用布尔运算符来比较值。

[in]  print(d > e)   # d greater than g 
[out] False [in]  print(c >= g)  # c greater than or equal to g 
[out] True[in]  print(a < e)   # a less than e
[out] False[in]  print(d <= g)  # d less than or equal to g 
[out] False[in]  print(g == c)  # g equals c
[out] True

5.Python 列表

Python 中的列表是存储值的容器。它们用括号括起来,我们一般把它们赋给变量。

[in]  our_list = [a, b, c, d, e, f, g]
      print(our_list)
[out] [5, 6, -1, 1.33, 4.67, 6.0, -1.0]

它们可以是数值,也可以是非数值。

[in]  i = “Ice”
      j = “Jewel”
      k = “Karate”
      l = “Lemon”
      another_list = [i, j, k, l]

它们可以是**排序()**也可以。

[in]  sorted(our_list)
[out] [-1, -1.0, 1.33, 4.67, 5, 6, 6.0]

但是,如果我们希望列表在操作时保持排序,我们需要重新分配它:

[in] our_sorted_list = sorted(our_list)

我们可以用 append() 添加列表:

[in]  h = 7
      our_list.append(h)
      print(our_list)
[out] [5, 6, -1, 1.33, 4.67, 6.0, -1.0, 7]

我们可以用 remove() 删除东西:

[in]   our_list.remove(a)
       print(our_list)
[out]  [6, -1, 1.33, 4.67, 6.0, -1.0, 7]

我们可以连接(放在一起)两个列表:

[in]  combined_list = our_list + another_list
      print(combined_list)
[out] [6, -1, 1.33, 4.67, 6.0, -1.0, 7, 'Ice', 'Jewel', 'Karate', 'Lemon']

我们也可以使用 add-assign 操作符 (+=) 将列表放在一起:

[in]  another_combo_list = []
      another_combo_list += combined_list
      print(another_combo_list)
[out] [6, -1, 1.33, 4.67, 6.0, -1.0, 7, 'Ice', 'Jewel', 'Karate', 'Lemon']

最后,我们可以使用等式操作符(" ==") 来比较列表,以获得一个布尔输出。

[in]  print(combined_list == another_combo_list)
[out] True

二。我们做了什么?

  1. 在 Python 中发现了五种不同的数据类型:整型、浮点型、布尔型、字符串型和列表型。
  2. 讨论了变量赋值、print()、type()和注释。
  3. 学习算术运算符和布尔运算符。
  4. 尝试列表。

四。下一步是什么?

在 Pandas I 中,您将学习如何使用 Python 和 Pandas 来分析 Metal Bands by Nation 数据集。

超参数优化的网格搜索和随机搜索的 Python 实现

原文:https://towardsdatascience.com/python-implementation-of-grid-search-and-random-search-for-hyperparameter-optimization-2d6a82ebf75c?source=collection_archive---------11-----------------------

使用 Scikit-learn GridSearchCV 和 RandomizedSearchCV 函数进行超参数优化的指南

照片由迪查茨在 Unsplash 上拍摄

如果不进行任何超参数优化(调整),就无法从机器学习模型中获得最佳效果。默认的超参数值并不是您的数据的最佳模型。sikit-learn-Python 机器学习库为超参数优化提供了两个特殊函数:

  • GridSearchCV — 进行网格搜索
  • RandomizedSearchCV — 进行随机搜索

如果你是数据科学和机器学习领域的新手,你可能不熟悉这些词。在这篇文章中,我将重点介绍网格搜索和随机搜索的 Python 实现,并解释它们之间的区别。读完这篇文章后,您将能够获得使用 Scikit-learn 实现网格搜索和随机搜索的实践经验。

先决条件

强烈建议对k-折叠交叉验证有良好的了解。我们也推荐构建决策树模型的知识,因为我们在这里使用这样的模型来实现网格搜索和随机搜索。如果你不熟悉这些东西,不要担心。我也为他们写了内容。首先,阅读它们,然后继续阅读这一个。以下是链接:

对于k-折叠交叉验证:

对于决策树:

首先,我们将区分模型 参数超参数

模型参数与超参数

模型参数 在训练过程中学习它们的值。我们不手动设置这些值。他们从我们提供的数据中学习。例如,线性回归模型的模型系数可以被认为是模型参数。

相反, 模型超参数 不从数据中学习它们的值。所以,我们必须手动设置它们。我们总是在创建特定模型时(即在训练过程之前)设置超参数的值。例如,我们可以通过为 规格化 超参数设置一个布尔值来指定是否规格化线性回归模型的输入特征(X):

from sklearn.linear_model import LinearRegressionlr_1 = LinearRegression(*normalize=False*) # No any normalization
lr_2 = LinearRegression(*normalize=True*) # Normalization applied

lr_1lr_2 模型给出两种不同的输出,因为它们具有不同的 归一化 超参数值。模型超参数可以控制模型参数。这意味着模型超参数会影响模型的性能。因此,我们有责任设置模型超参数值,以便它们给出模型的最优或最佳可能输出。

在大多数情况下,即使简单的模型也有两个或更多的超参数。因此,我们必须同时考虑所有这些超参数,以找到每个超参数的最佳值。寻找模型超参数最优组合的过程称为 超参数优化(调优) 。我们不能手动这样做,因为有许多超参数,每个超参数有许多不同的值。幸运的是,Scikit-learn 提供了 GridSearchCVRandomizedSearchCV 函数来自动化优化(调整)过程。

超参数搜索空间

这是超参数调整过程中一个非常重要的概念。搜索空间包含用户定义的超参数值的所有不同组合。下图显示了两个不同超参数的二维搜索空间— 最大深度最小样本分割

(图片由作者提供)

如果有 3 个不同的超参数,则搜索空间是三维的。同样,随着超参数数量的增加,搜索空间可以是高维的。你应该知道的一般事情是:

  • 搜索空间中的维数定义了超参数的数量。(例如 2 维-2 个超参数)
  • 搜索空间中的每个点定义了超参数值的每个组合。点 (8,30) 定义了最大 _ 深度的值 8 和最小 _ 样本 _ 分割的值 30。

我们可以将搜索空间定义为一个 Python 字典,其中包含作为关键字的超参数名称和作为值列表的这些超参数的值。这种字典的一般格式是:

search_space = {'param_1':[val_1, val_2],
                'param_2':[val_1, val_2],
                'param_3':['str_val_1', 'str_val_2']}

现在,我们区分网格搜索和随机搜索。

网格搜索与随机搜索

网格搜索 在搜索空间中搜索用户定义的所有不同的超参数组合。这将耗费大量的计算资源,并且当搜索空间是高维的并且包含许多值的组合时,通常具有高的执行时间。当有少量超参数和有限(固定)数量的超参数值时,这种方法是理想的。

网格搜索(作者图片)

相比之下,随机搜索在寻找最优组合时不会检查所有不同的超参数组合。相反,它检查在 RandomizedSearchCV 函数的 n_iter 中指定的随机选择的固定数量的组合。随机搜索在随机选择的组合中找到最佳超参数组合的概率非常高。当搜索空间是高维的并且包含许多值的组合时,该方法对于快速有效地找到最优超参数组合是非常有用的。

随机搜索(图片由作者提供)

构建基础模型

现在,我们在“heart _ disease”数据集上构建决策树分类模型,而不做任何超参数调整。在整篇文章中,我们将使用这个模型作为 基础模型 ,以便可以与使用网格搜索和随机搜索调优的其他模型进行比较。看看下面的 Python 代码,它构建了我们的基本模型。

基本模型(等到加载 Python 代码!)

(图片由作者提供)

正如您从输出中看到的,我们的基本模型不是很好。它已经很好地学习了训练数据,但它无法对新的输入数据(测试集)进行归纳。用技术术语来说,我们的基本模型显然是过度拟合的。决策树模型通常倾向于过度拟合。

我们现在可以使用网格搜索和随机搜索方法来提高我们模型的性能(测试准确度分数)。

首先,我们将尝试网格搜索。

网格搜索的 Python 实现

网格搜索的 Python 实现可以使用 Scikit-learnGridSearchCV函数来完成。它具有以下重要参数:

  • estimator — (第一个参数)一个 Scikit-learn 机器学习模型。换句话说,这是我们的基本模型。
  • param_grid — 如前所述的搜索空间的 Python 字典。我们的搜索空间是三维的,包含 576 种不同的组合。这意味着我们用网格搜索训练 576 个不同的模型!
  • 评分— 用于衡量模型性能的评分方法。对于分类,我们一般用‘准确度’或‘roc _ AUC’。对于回归,首选“r2”或“负均方误差”。由于我们的基础模型是一个分类模型(决策树分类器),我们使用“准确性”作为评分方法。要查看可用评分方法的完整列表,请点击此处。
  • n_jobs — 指定执行网格搜索时要运行的并行作业的数量。如果您的计算机处理器有许多内核,请为此设置一个较高的值。 -1 值使用所有可用的内核。这将加快执行过程。
  • cv — 交叉验证的折叠数。标准数字是 5,10。当 cv 为 10 时,每个超参数组合重复 10 次。因此,总迭代次数为 5760 (576 x 10)。

看看下面的 Python 代码,它在我们的基本模型上执行网格搜索。

网格搜索(等到加载 Python 代码!)

(图片由作者提供)

这是一个用几个 print() 函数格式化的很好的输出。您可以将这个输出与我们基本模型的先前输出进行比较。该模型的性能现已得到明显改善。这一次,模型并没有过度拟合。它在训练集和测试集上都表现良好。除此之外,调整超参数后,假阳性和假阴性都显著减少。

现在,让我们来关注一下这种情况下网格搜索的执行时间。大约只有 22.5 秒。运行 5760 次迭代在处理器所有内核都启用的情况下只需要 22.5 秒( n_jobs=-1 )!

接下来,我们尝试随机搜索。

随机搜索的 Python 实现

随机搜索的 Python 实现可以使用 Scikit-learn 的 RandomizedSearchCV 函数来完成。大多数参数与 GridSearchCV 函数中的参数相同。这里,搜索空间由参数 _ 分布而不是参数 _ 网格定义。除此之外,

  • n_iter — 指定随机选择的超参数组合的数量。这是因为随机搜索不会检查搜索空间中定义的所有超参数组合。相反,它只考虑组合的随机样本。这里,n_iter=10 意味着它执行大小为 10 的随机样本任务,该样本包含 10 个不同的超参数组合。所以随机搜索只训练 10 个不同的模型(之前是 576 个带网格搜索的模型)。
  • random_state — 控制每次不同执行时获取超参数组合样本的随机化。我们可以使用任何整数。

这里,总迭代次数是 100 (10 x 10),这比前一种情况(5760 次迭代)少得多。

现在,看看下面的 Python 代码,它在我们的基本模型上执行随机搜索。

随机搜索(等到加载 Python 代码!)

(图片由作者提供)

模型性能与网格搜索完全相同。然而,最佳超参数值是不同的。现在,执行时间仅为 0.51 秒,比前一个版本(22.5 秒)少得多。在这种情况下,随机搜索比网格搜索快 44 倍(22.5 / 0.51)。这是因为随机搜索只少执行 57.6 倍(5760 / 100)的迭代!

结论

在我们的例子中,您可以尝试网格搜索和随机搜索,因为这两种方法只需要不到半分钟的时间。然而,请记住,随机搜索的力量。在我们的例子中,它快了 44 倍(22.5 / 0.51)。这意味着如果随机搜索将花费 1 分钟的执行时间,网格搜索将花费大约 44 分钟!所以,当搜索空间是高维的,包含很多不同的超参数组合时,我强烈推荐你使用随机搜索。

运行网格搜索或随机搜索后,您将获得最佳超参数组合。例如,我们在网格搜索中得到以下最优超参数组合:

{'max_depth': 6, 'min_samples_leaf': 6, 'min_samples_split': 2}

因此,我们可以将最优模型定义如下:

from sklearn.tree import DecisionTreeClassifierdtclf_optimal = DecisionTreeClassifier(**max_depth=6**,
                                       **min_samples_leaf=6**,
                                       **min_samples_split=2**,
                                       random_state=42)

然而,我们不需要这样写代码。 GridSearchCVrandomzedsearchcv函数都有一个名为 best_estimator_ 的属性,用来获得具有最优超参数的模型。因此,

gs.best_estimator_ 

会给出同样的 dtclf_optimal 模型。这里, gs 是拟合的 GridSearchCV 模型。

此外,请注意,网格搜索和随机搜索一次考虑所有超参数,而不是一个接一个。这就是为什么网格搜索和随机搜索得到的不同超参数值给出了相同的准确率分数。如果您想查看单个超参数的影响,我建议您使用验证曲线 —一种图形技术。我写的下面这篇文章解释了我们如何使用它。

我的读者可以通过下面的链接注册成为会员,以获得我写的每个故事的全部信息,我将收到你的一部分会员费。

报名链接:https://rukshanpramoditha.medium.com/membership

非常感谢你一直以来的支持!下一个故事再见。祝大家学习愉快!

除封面图片、代码样本、其他内容链接和文字内容外的所有图片均归作者版权所有。特别要感谢 Unsplash 上的迪查茨,他是封面图片的所有者。非常感谢,迪查茨,为我在本帖中提供了一张出色的封面图片!

鲁克山普拉莫迪塔 2021–06–07

Python 一个简单应用程序中的重要概念

原文:https://towardsdatascience.com/python-important-concepts-in-one-application-classes-staticmethod-classmethod-decorators-c44a0d588da5?source=collection_archive---------19-----------------------

类,staticmethod,classmethod,自定义装饰器

澳门图片社在 Unsplash 上拍摄的照片:装饰让一切看起来都很美,甚至你的代码...

从前..不,不,只是几天前,我的一个朋友让我解释一些 python 的概念。其中一些是类、装饰器、classmethod、staticmethod。

我在网上搜索,给她发一些好的链接来理解这些概念,但是我找不到一个地方能以简明易懂的方式解释这些概念。我找不到一个简单的方法来解释这些,对于那些对 OOPS 概念不是很有效率的人来说。

所以,我花了一些时间创建了一个小应用程序来主要解释使用这些的为什么。然后我想为什么不分享一下呢?

现在,让我们直接去理解这些概念。我正在创建一个名为计算器的应用程序。有三个用户想要访问计算器并使用该计算器执行不同的操作。

计算器应用

这是应用程序,有加,减,乘操作,不同的用户应该能够与应用程序交互和执行操作。

无类别方式(未创建类别):

所以,让我们从构建一个名为 Calculator 的类开始。但是等等..它的蟒蛇..我们真的需要上课吗?难道我就不能创建四个函数,名为加、减、乘、除,让人们使用吗?当然,我可以,但是请记住,您可以按照您想要的任何方式编码,但是构建一个定义上下文的代码并在其中分离操作(封装)是创建一个可读且可维护的代码的方法。

首先,让我们采用非类的方式:创建四个方法:

计算器应用程序的加、减、乘方法

现在,假设有三个用户想要访问它:Aron、Ben 和 Cathy

Aron 调用 add(2,3)并得到结果。但是如果你想追踪是谁打来的呢?在这种情况下,您可能希望将函数修改为:

加,减,乘的方法来记录谁打电话

以人为输入的运算符方法!!这是正确的方法吗?

这里,我们更新了方法,在“person”变量中传递人名,我们可以知道哪个人调用了特定的方法。Aron 调用这个方法为:add(2,3,“Aron”)并得到结果。它成功了,我们完成了!!

不,还没有。想一想,用人名作为输入来执行计算的方法好吗?不对,对!!为什么不将打印姓名与执行这些操作分开呢?

救援来了...类别和对象:

优雅的方式:

类是封装内部功能的一种方式,因此可以创建特定的对象。对象是为特定的实例创建的,可以重用这些实例对对象的现有状态执行不同的操作。哼!!太啰嗦了。如果我刚才说的你一个字也没听懂,就忽略它,继续前进。我们通过例子来了解一下。

class Calculator():
    def __init__(self, person_name):
        self.per_name = person_name

    def add(self, a, b):
        print("Operation called by", self.per_name)
        return a+b

    def subtract(self, a, b):
        print("Operation called by", self.per_name)        
        return a-b

    def multiply(self, a, b):
        print("Operation called by", self.per_name)
        return a*b

在这里,我创建了一个名为 calculator 的类,加、减、乘方法只有它们特定的功能。 init 方法是一个构造函数,将用于为特定的人创建对象。让我们为 Aron 创建它:

if __name__ == '__main__':
    aron_calculator = Calculator('Aron')
    ben_calculator = Calculator('Ben')
    cathy_calculator = Calculator('Cathy')
    print(aron_calculator.add(6, 5))
    print(aron_calculator.subtract(6, 5))
    print(ben_calculator.subtract(6, 5))
    print(cathy_calculator.multiply(12, 3))**Output:**
Operation called by Aron
11
Operation called by Aron
1
Operation called by Ben
1
Operation called by Cathy
36

到底是什么意思?

在这里,您可以想象 Aron 登录到 Calculator 应用程序,并收到一个 aron_calculator 对象作为响应。这将设置 Aron 的对象,并传递人名。现在,他可以使用该对象来执行任何计算,我们将知道 Aron 正在执行这些操作。**“self”**包含与 Aron 和 self.per_name 相关的详细信息,提供 Aron(因为我们在为他创建对象时已经在 init 中设置了它。)

这些方法被称为实例方法。现在你知道了类和实例方法(需要 self)以及我们为什么需要它们。先说 classmethod 和 staticmethod。

@静态方法:

静态方法是那些不附属于任何对象或类的方法。这些都是独立的方法(就像 python 函数一样),它们被保留在类中的原因是只有其他类方法使用它们。让我们创建一个方法作为 staticmethod,并看看如何以及何时使用它:

class Calculator():
    def __init__(self, person_name):
        self.per_name = person_name

    def add(self, a, b):
        print_operator_name(self.per_name)
        return a+b

    def subtract(self, a, b):
        print_operator_name(self.per_name)        
        return a-b

    def multiply(self, a, b):
        print_operator_name(self.per_name)
        return a*b **@staticmethod    
    def print_operator_name(person_name):
        print("Operation called by", person_name)**

这里,正如您所看到的,我们没有在方法中使用 print 语句,而是创建了一个 static method print _ operator _ name,它负责打印细节。实例方法可以调用这个 staticmethod(它不需要 self,和类外的 python 函数一样好)来打印操作符名称。

@classmethods:

这是 python 提供的内置 decorator(我稍后会谈到 decorator ),它影响类,而不仅仅是特定的对象。因此,通过 classmethod 更新的任何东西都可以更新类级别的变量,然后所有的对象都可以看到这些变量被更新了。让我们看一个例子:

class Calculator:
    **status = False**

    def __init__(self, person_name):
        **if not self.status:
            raise ValueError("Calculator is not running. Please update status.")**
        self.per_name = person_name

    def add(self, a, b):
        self.print_operator_name(self.per_name)
        return a+b

    def subtract(self, a, b):
        self.print_operator_name(self.per_name)
        return a-b

    def multiply(self, a, b):
        self.print_operator_name(self.per_name)
        return a*b

    @staticmethod    
    def print_operator_name(person_name):
        print("Operation called by", person_name)
 **@classmethod
    def set_status(cls):
        cls.status = True

    @classmethod
    def reset_status(cls):
        cls.status = False

    @classmethod
    def getStatus(cls):
        return cls.status**

这里,我在计算器类级别创建了一个状态:

class Calculator():
    status = False

状态=假!!这是什么意思?

这决定了我们计算器的*状态。*最初,是假的。你可以想象你需要通过设置它为真来启动你的计算器。然后 Aron,Ben,Cathy 可以通过创建它们的对象来操作它们。否则,即使在创建对象时,他们也会得到一个异常,如果没有对象,他们就不能操作计算器。

为此,由于 status 是一个类级别的变量,我们需要 classmethod。set_status 将其设置为 True,reset_status 将其设置为 False,get_status 返回状态。**“cls”**是一个类作用域变量,用来访问类的变量(就像 self 可以访问 per_name 这样的对象级变量一样)。 cls,self 只是标准,你可以用任何你想要的字符串替换它,但是这是推荐的,因为它有助于保持代码的一致性。

让我们运行它:

if __name__ == '__main__':
 **Calculator.reset_status()**    print(Calculator.getStatus())
    aron_calculator = Calculator('Aron')
    ben_calculator = Calculator('Ben')
    cathy_calculator = Calculator('Cathy')
    print(aron_calculator.add(5, 6))
    print(aron_calculator.subtract(4, 2))
    print(cathy_calculator.subtract(3, 1))
    print(cathy_calculator.multiply(4, 5))

我们得到一个异常,声明状态不为真,因为构造函数不允许状态为假,因此不会创建对象。这意味着,没有人可以访问计算器,因为它是关闭的(状态为假):

Output:
False
Traceback (most recent call last):
  File "C:\Users\tushar_seth\PycharmProjects\calculator_medium\Calculator.py", line 49, in <module>
    aron_calculator = Calculator('Aron')
  File "C:\Users\tushar_seth\PycharmProjects\calculator_medium\Calculator.py", line 13, in __init__
    raise ValueError("Calculator is not running. Please update status.")
ValueError: Calculator is not running. Please update status.

**想运行计算器?使状态为真

if __name__ == '__main__':
 **Calculator.set_status()**    print(Calculator.getStatus())
    Calculator.set_status()
    aron_calculator = Calculator('Aron')
    ben_calculator = Calculator('Ben')
    cathy_calculator = Calculator('Cathy')
    print(aron_calculator.add(5, 6))
    print(aron_calculator.subtract(4, 2))
    print(cathy_calculator.subtract(3, 1))
    print(cathy_calculator.multiply(4, 5))

我们得到适当的输出:

Output:
True
Operation called by Aron
11
Operation called by Aron
2
Operation called by Cathy
2
Operation called by Cathy
20

这显示了 staticmethod 和 classmethod 的能力。

装修工

现在,让我们进入下一个主要概念:装饰者。您看到了@staticmethod 和@classmethod,它们是内置的 python 装饰器。如果你想用自己的功能创建自己的装饰器呢?

但是首先,你为什么要创造自己的装饰者呢?

装饰器在很多编程语言中使用,主要是为了使代码简洁,并用几个词隐藏样板代码。如果您想在每次调用某个方法时都以特定的方式运行该方法,那么您可以只应用装饰器,甚至不用接触实际的方法。

继续我们之前的计算器课程。您是否注意到,我们需要记录操作符和操作细节,为此,我们创建了一个 staticmethod,并从每个方法中一次又一次地调用。如果我们不想接触那些方法,假设我们已经构建了复杂的方法,并且我们不想弄乱它们的功能,该怎么办?在这种情况下,我们可以用公共方法的方法名来修饰操作符方法。

别拿理论烦我,给我看看例子!!

让我们直接跳到例子来理解这一点,我们在类外创建一个方法(这是最简单的方法。理想情况下,我们应该创建一个内部类,并在其中定义 decorator。但是我的范围是让你理解功能。要查看在类中创建 decorator 的内部类方法,请查看以下链接:https://medium . com/@ vadimpushtaev/decorator-inside-python-class-1e 74d 23107 F6

**def logging_decorator(incoming_function):
    def inner_function(self, a, b):
        print('Operation called by', self.per_name)
        return incoming_function(self, a, b)
    return inner_function**

在这里,logging_decorator 接受一个函数,这个函数本来就是要有这个 decorator 的。然后 inner_function 接受(加、减、乘)方法得到的参数。然后我们可以改变调用该函数的行为,首先打印我们的日志,然后将实际的方法调用返回给被调用者。

现在,我们可以使用 decorator 用这个函数来修饰我们的方法,这很简单,只需在方法上写@logging_decorator,因此我们的最终代码如下:

def logging_decorator(incoming_function):
    def inner_function(self, a, b):
        print('Operation called by', self.per_name)
        return incoming_function(self, a, b)
    return inner_function

class Calculator:
    status = False

    def __init__(self, person_name):
        if not self.status:
            raise ValueError("Calculator is not running. Please update status.")
        self.per_name = person_name

    @logging_decorator
    def add(self, a, b):
        return a+b

    @logging_decorator
    def subtract(self, a, b):
        return a-b

    @logging_decorator
    def multiply(self, a, b):
        return a*b

    @staticmethod    
    def print_operator_name(person_name):
        print("Operation called by", person_name)

    @classmethod
    def set_status(cls):
        cls.status = True

    @classmethod
    def reset_status(cls):
        cls.status = False

    @classmethod
    def getStatus(cls):
        return cls.status

if __name__ == '__main__':
    Calculator.set_status()
    print(Calculator.getStatus())
    aron_calculator = Calculator('Aron')
    ben_calculator = Calculator('Ben')
    cathy_calculator = Calculator('Cathy')
    print(aron_calculator.add(5, 6))
    print(aron_calculator.subtract(4, 2))
    print(cathy_calculator.subtract(3, 1))
    print(cathy_calculator.multiply(4, 5))

它的输出将与前一个完全相同:

True
Operation called by Aron
11
Operation called by Aron
2
Operation called by Cathy
2
Operation called by Cathy
20

因此,我们改变了方法调用的行为方式,甚至没有触及方法(corona time!!不要碰它)(是的是的我们移除了之前对静态方法的调用..我戴着面具做那个!!).

如果我有一个接受 3 个参数的方法呢?这个会失败吧?

是的,这肯定会失败。为了克服这一点,我们可以创建一个通用参数方法:

**def logging_decorator(incoming_function):
    def inner_function(*args, **kwargs):
        print('Operation called by', args[0].per_name)
        return incoming_function(*args, **kwargs)
    return inner_function**

有了这个,我们甚至可以有多变量方法:

**@logging_decorator
def add3(self, a, b, c):
    return a + b + c**print(cathy_calculator.add3(4, 5, 8))Output:
Operation called by Cathy
17

下面,我们用一个简单的例子来解释 Python 的基本概念,这些概念非常重要,但人们发现它们很难掌握。这些都是棘手的概念,但并不难。一旦你学会了这一点,你将会在你的 python 生涯中用到很多。我非常喜欢装修工,希望在这之后,你也会喜欢。上面讨论的代码的 GitHub 链接在这里:

感谢阅读!!。对于任何建议,疑问或讨论,在评论区拍摄。如果你喜欢这个代码,请在 Github 上投票。

在 medium 和 GitHub 上关注我,获取即将发布的文章;到那时,快乐的蟒蛇和好运:)

崇高文本 3 中的蟒蛇与崇高的 REPL 和蟒蛇

原文:https://towardsdatascience.com/python-in-sublime-text-3-with-sublime-repl-and-anaconda-744ca0da91e3?source=collection_archive---------41-----------------------

灵活的、以项目为中心的设置

只需一个按键就可以在项目之间切换,而不必担心构建系统的切换。借助这种以项目为中心的设置,加快您的数据科学工作流程。

在本教程中,您将学习如何将 Sublime Text 3 链接到 Anaconda/Miniconda 环境中,并使用 Sublime REPL 执行 Python。当您在 Sublime 中切换项目时,构建系统也会切换到与之相关联的 conda 环境。

您可能已经有了不同虚拟环境的现有项目——不用担心。这种设置可以很容易地应用到您已经拥有的任何项目。**注意:**本指南是为 Windows 10 编写的。

照片由 Pexels 的凯拉·伯顿拍摄

TL;速度三角形定位法(dead reckoning)

看起来可能需要很多步骤,但是如果你手头有一切,你可以在 1 分钟内完成。这是*你需要*去做的:崇高文本 3,包控制,项目经理,Anaconda/Miniconda 和 conda 虚拟环境。比如我这里用 *example_env* 。

  1. 打开崇高文本 3,并确保你有包控制,崇高 REPL 和项目经理。用命令面板安装快捷键:CTRL+SHIFT+p,键入安装 > 包控制:安装包 > 【包名】
  2. 记下 conda env 的名称或创建一个新的名称conda create --name **example_env** python=3.8 -y
  3. C:\Program Files\Sublime Text 3\(或者subl.exe所在的任何地方)添加到你的系统环境变量中,打开项目目录中的cmd,用subl .打开 Sublime
  4. 使用命令面板通过 CTRL+SHIFT+p > Project Manager: Add new project向项目管理器添加一个新项目
  5. CTRL+SHIFT+p 键入browse,选择Preferences: Browse packages。浏览器在C:\Users\[YOURNAME]\AppData\Roaming\Sublime Text 3\Packages\中打开。导航至SublimeREPL/config/Python,复制文件Main.sublime-menu并返回C:\Users\[YOURNAME]\AppData\Roaming\Sublime Text 3\Packages\
  6. 打开User文件夹,创建目录SublimeREPL\config\Python,将Main.sublime-menu粘贴到刚刚创建的目录下(C:\Users\[YOURNAME]\AppData\Roaming\Sublime Text 3\Packages\User\SublimeREPL\config\Python)。将Main.sublime-menu改名为**example_env**.sublime-menu,用 Sublime 打开。
  7. 搜索"id": "repl_python_run",所在的块,找到"cmd": ["python", "-u", "$file_basename"],。我们将"python"替换为我们想要链接到项目的 conda env 的路径,然后保存。比如:"cmd": ["C:\\Users\\philipp\\miniconda3\\envs\\**example_env**\\python.exe", "-u", "$file_basename"],
  8. CTRL+SHIFT+p 查找Project: edit project。插入下面的代码片段来定义项目的构建系统。用你想要的 conda env 替换 example_env
{ 
"build_systems": [ 
{ 
"name": "Conda Python **example_env** REPL", 
"target": "run_existing_window_command", "id": "repl_python_run", "file": "config/Python/**example_env**.sublime-menu", 
} 
], 
... }

厉害!用 CTRL+ ALT + p 在项目之间轻松切换,不用担心 conda 环境。该设置是项目特定的。我希望这能加快你的工作流程。祝你未来的项目愉快!

如果您遇到任何问题,请参考下面更详细的指南。如果你还有问题,有不清楚的地方或者你只是想打个招呼,请告诉我。非常感谢任何形式的反馈。

这篇文章的灵感来自于社区在stack overflow上的努力。我的方法避免了崇高的建筑系统菜单将充满每一个你链接到 REPL 的虚拟环境。此外,这里的解决方案是以项目或环境为中心的,而不是全局设置。

问题

项目越多,创建的虚拟环境就越多,特定于项目的设置就变得越重要。最*,当三个项目并行运行,我不得不频繁地在它们之间切换时,我就经历了这种情况。切换项目和构建系统打乱了我的工作流程。我需要一种快速的方法来切换和改变每个构建系统。我的工作流程依赖于 Sublime Text 3、Anaconda 和 Sublime REPL,所以我需要一个针对这些工具的解决方案。本文提出了一种*稳地集成它们的方法,以实现快速而稳定的项目设置。

对于一个轻量级和快速的 Python 设置,我们需要什么?

  • 使用轻量级文本编辑器快速加载文件→升华文本 3
  • 在项目之间轻松切换→提升项目经理
  • 处理与为每个项目定义的虚拟环境的依赖关系→ Anaconda/Miniconda
  • 使用交互式命令行构建系统→升华 REPL

这里提供的设置允许您使用 CTRL+ALT+p 在项目和它们相关的虚拟环境之间切换。选择另一个项目后,构建系统也会切换到您在项目设置中定义的项目。有几个步骤是必须遵循的,但这是值得的。希望这也对你有用。

开始之前

如果您还没有,请安装以下软件:

  1. 崇高的文本 3:【https://www.sublimetext.com/3
  2. 崇高包控:【https://packagecontrol.io/installation
  3. 项目经理:https://packagecontrol.io/packages/ProjectManager
  4. 崇高的 https://packagecontrol.io/packages/SublimeREPL[REPL](https://packagecontrol.io/packages/SublimeREPL)
  5. anaconda/Miniconda:https://docs.conda.io/en/latest/miniconda.html答:整个教程(conda create --name **example_env** python=3.8 -y)我都用 example_env

本教程假设 Windows 10 ,但应该同样适用于 Linux。我推荐使用 Miniconda,在 Reddit 上看看为什么

解决办法

来自 Pexels 的 Andrea Piacquadio 的照片

我们安装了上面列表中的所有东西,并且有一个 conda 环境。我使用 example_env 作为 conda 环境。第一步是将 Sublime 放在项目根文件夹的中心。这样,所有像用 CTRL+p 跳转到一个文件这样的快捷方式都与项目根./相关。然后我们初始化项目管理器。

让我们像C:\Users\[YOURNAME]\[PATH-TO-PROJECT]\[PROJECTNAME]一样在项目根打开 Sublime 的一个新实例。我已经将 Sublime 添加到我的环境变量中(下面有一个简短的操作方法),并在项目根目录中打开一个终端C:\Users\philipp\projects\Sublime project setup。用 CTRL+l 进入地址栏,键入cmd打开命令,
回车。运行subl .(仅当您将subl.exe添加到您的 Windows 环境变量中时才有效——在下面的附录中找到一个简短的方法)。一扇空荡的窗户打开了。这就对了。

现在我们转向项目经理。使用命令面板通过 CTRL+SHIFT+p > Project Manager: Add new project向项目管理器添加一个新项目。用 CTRL+SHIFT+p > Project Manger: Edit project检出项目设置文件。Sublime 在这里存储您的项目设置,包括项目namepath:

{ 
"folders": [ 
{ "binary_file_patterns": [ ], 
"file_exclude_patterns": [ ], 
"folder_exclude_patterns": [ ], 
"name": "Sublime project setup", 
"path": "C:\\Users\\philipp\\projects\\Sublime project setup" 
} 
] 
}

接下来,我们定义一个构建系统,它采用 conda env 项目的python.exeexample_env 。记下 conda 环境的名称。例如,在本教程中,我用conda create --name **example_env** python=3.8 -y创建了一个环境。所以,我在这里使用 example_env 作为环境。

  1. CTRL+SHIFT+p 键入browse并选择Preferences: Browse packages。浏览器在C:\Users\[YOURNAME]\AppData\Roaming\Sublime Text 3\Packages\中打开。
  2. 从这里,导航到SublimeREPL/config/Python并复制文件Main.sublime-menu
  3. 回到C:\Users\[YOURNAME]\AppData\Roaming\Sublime Text 3\Packages\,打开User,创建以下文件夹:SublimeREPL\config\Python
  4. 现在你在C:\Users\[YOURNAME]\AppData\Roaming\Sublime Text 3\Packages\User\SublimeREPL\config\Python中,将Main.sublime-menu粘贴到目录中。
  5. Main.sublime-menu改名为example_env.sublime-menu,用 Sublime 打开
  6. 重要步骤:搜索"id": "repl_python_run",所在的区块,用"cmd": ["C:\\Users\\[YOURNAME]\\[PATHTOCONDA]\\envs\\**example_env**\\python.exe", "-u", "$file_basename"],替换"cmd": ["python", "-u", "$file_basename"],并保存。*注意:*用任何通向您的 conda 环境的python.exe的路径替换该路径。对我来说,这是C:\\Users\\philipp\\Miniconda3\\envs\\**example_env**\\python.exe
  7. 用 CTRL+SHIFT+p find Project: edit project打开命令面板,选择项目名称。现在我们定义使用我们的 conda 环境的 Python 的构建系统,**example_env**。复制粘贴下面的代码片段,并用您的环境名称替换 **example_env**重要提示:共有 2 处替换粗体
{ "build_systems": [ 
{ "name": "Conda Python **example_env** REPL", 
"target": "run_existing_window_command", 
"id": "repl_python_run", 
"file": "config/Python/**example_env**.sublime-menu", } 
], "folders": 
[ 
{ "binary_file_patterns": [ ], 
"file_exclude_patterns": [ ], 
"folder_exclude_patterns": [ ], 
"name": "example project", 
"path": "C:\\Users\\[YOURNAME]\\Projects\\example_project" } 
] }

为了简单地测试设置,创建一个像test.py一样的新文件并插入

用我们的构建系统运行这个文件(这里:Conda Python example _ env REPL):

我希望它一直工作到现在。如果没有,请给我留言或留下评论!

使用

当您并行处理几个项目时,这种设置显示了它的优势。您可以(I)使用 CTRL+ ALT + p 在项目之间切换,( ii)在您的构建系统之间拥有相关的虚拟环境。

要设置一个新项目,从命令行的subl .开始,重复解决方案部分的所有步骤。这样做两次左右,1 分钟后就会自然了。

我确信还有其他我还没有弄清楚的方法和项目设置。因此,我很欣赏任何关于崇高,REPL 和蟒蛇项目设置的技巧和最佳实践。

如果有人想自动化这个过程,并为 Sublime REPL 实现一个基于项目的构建系统,我将不胜感激!我认为许多其他人将从中受益。它将连接来自崇高的阿纳康达和 REPL 世界的最好的工具。

结论

在本教程中,您学习了如何设置崇高的 REPL 和链接到您的康达环境。通过这种方式,您可以跨项目拥有多个环境,并使用 Sublime 的项目管理器及其快捷键 CTRL+ ALT+p 轻松切换。享受您的新设置,享受您的项目!

如果你觉得这很有帮助,或者在安装过程中遇到了一些问题,请给我留言。非常感谢您的反馈!

进一步寻找中级提示和技巧来开发您的数据科学工作流?敬请关注下一篇关于数据科学工具 2021 的帖子。

附录

如何将 subl.exe 添加到环境变量

Windows 用户的快捷方式:点击 Windows 键,输入env,从搜索结果中选择Edit environment variables for your account

然后点击Path>Edit>New>C:\Program Files\Sublime Text 3\>OK。用找到subl.exe的目录替换C:\Program Files\Sublime Text 3\。另见本教程。

资源

https://stackoverflow.com/questions/38214656/using-different-conda-envs-with-sublime-repl

最初发布于https://philippschmalen . github . io

Python 是完美的——为什么反 Python 开发者不会放弃

原文:https://towardsdatascience.com/python-is-perfect-why-anti-python-developers-wont-give-up-326ae1a09e7f?source=collection_archive---------3-----------------------

编程;编排

如何让讨厌 Python 的人闭嘴?

费尔南多·埃尔南德斯在 Unsplash 上的照片

软件开发长期以来一直有点像雷区。

一般人会觉得用传统的编程语言(如 C 和 JAVA)进入软件领域是很可怕的。

但是一旦 Python 加入游戏,这种情况很快就改变了。Python 因其革命性的编程方法而成为软件开发的一大飞跃。

人们爱上 Python 是因为它的简单性、广泛的库支持和表达能力。就这样,像 C 和 JAVA 这样的语言的统治结束了。

没有什么是完美的。等等,真的吗?

如果你是一名软件开发人员,你可能很熟悉术语权衡。

每当出现关于编程语言的问题时,人们期望得到的传统答案是:“视情况而定。”

普遍认为没有完美的编程语言。等等,是真的吗?

对我来说,不是。我开始意识到 Python 是编程语言的银弹。

到目前为止,我用过 Python C,C++,MATLAB,JAVA。Python 是最后的,也是最好的。到目前为止。

为了测试 Python,我做了一个实验,将我的大部分非 Python 程序转换成 Python。最终的 Python 版本要好得多。

现在,据我所知,Python 在两个主要问题上一直受到严厉批评。

  • 动态打字
  • 速度

虽然我同意这些问题在某种程度上对软件开发有重大影响,但我可以肯定这是在 Python 还处于婴儿期的时候。

然而,由于其坚如磐石的社区,Python 已经发展到了几乎可以与所有编程语言相提并论的地步。动态类型和速度不再对编码实践有重大影响。

Python 兼容所有问题。

如果是这样的话,为什么 Python 还没有被广泛采用呢?

如果我现在回答这个问题,我的话将没有任何争论的分量。因此,有必要解释一下为什么动态类型和性能问题是容易被忽略的小问题。

动态打字

要理解动态类型,最好一直拖着对立面——静态类型。

静态类型语言是 C、JAVA 等。这些语言和它们的动态类型对应物之间的主要区别在于类型检查。静态类型是在编译时执行类型检查,而在动态类型中,类型检查是在运行时执行的。

名词(noun 的缩写)b:这是动态和静态类型的一个*淡无奇的定义。细节和技术细节超出了这篇文章的范围。

由于 Python 的调试问题,以及这种语言如何使跟踪 bug 的过程变得更加复杂,一些开发人员对 Python 怨声载道。

这种抱怨源于这样一个事实,即作为一种动态类型语言,Python 在运行时会标记错误,这使得它比像 JAVA 这样的静态类型语言更容易出错。

从理论上讲,考虑到众所周知的谚语— 人非圣贤,孰能无过,这是一种威胁。但是实际上,我怀疑类型错误就像他们声称的那样严重。

说真的,现在谁会犯打字错误?

静态类型语言远不是一个没有麻烦的调试避风港。除了类型错误之外,大多数静态类型语言都无法捕捉到您可能认为可以检测到的细微错误。这些错误同样发生在静态类型和动态类型中。

此外,我们可以同意,几乎没有代码是在没有单元测试的情况下部署的。单元测试为防止类型错误提供了一个很好的保护层。只有极少数情况下,类型错误会绕过单元测试,从而搞乱代码。

编程语言在调试方面的另一个特性是安全和不安全的类型。众所周知,允许用户重写系统类型的语言具有不安全的类型,例如 C。虽然不安全的类型赋予了用户对类型的控制权,但是它的误用会导致程序任意崩溃。

大多数静态类型语言都是不安全的类型系统。然而,JAVA 是一种安全的类型。Python 和我所知道的其他动态类型编程语言也是如此。

最后,我更愿意用像 Python 这样方便和高级的语言编写一百万次代码,而不是陷入像 JAVA 这样的低级语言的细节中。

速度

可怜的蟒蛇一直微笑着接受性能批评。如果你正在进行编程语言的辩论,我几乎可以保证,一旦你开始吹捧 Python,你的性能就会大打折扣。这是真的。嗯,在某种程度上。

我不会争论原始或标准 Python 比 C 或 JAVA 慢的事实。但是正如我之前所说的,有了像 Python 这样勤奋和创新的社区,不用担心。

在过去的几年里,Python 和它的静态竞争对手之间的性能差距已经大大缩小了。JIT 编译和并行计算的进步让 Python 赶上了它的竞争对手。

为了解决 Python 的性能问题,开发了许多变通方法。例如:

  • 将实现从 CPython 改为 PyPy 显著提高了执行速度。偶尔,PyPy 甚至可以跑赢 C

名词(noun 的缩写)B: CPython 是你从Python.org下载的实现

  • 抑制 GIL 使 Python 能够并行执行序列,从而提高计算速度。
  • JIT 编译器 Numba 。把 Numba 装饰器放在你的函数上,看着它以光速运行就足够了。
  • 很多 Python 库像 Numpy 和 Scipy 都是用 C/C++写的。

如您所见,性能在某种程度上是反对 Python 的过时论点。有了所有这些解决方案,除了称赞 Python 充满活力的社区之外,别无他法。

但是为什么有些人仍然不喜欢 Python,而更喜欢老式的 JAVA 之类的东西呢?

为什么不向 Python 投降?

首先,这里有一个 quora 帖子让我忍俊不禁。

来源: Quora

显然,有些人没有也不会把 Python 当回事。有意思!

无论如何,我个人认为,阻碍 Python 被大规模采用的是人们对待编程的心态。换句话说,问题不在于语言,而在于人。

你看,Python 的出现是为了让编程变得更容易,更重要的是,为了节省时间。

您可能会想,“我用 JAVA 编码很舒服。我为什么需要 Python?”你当然不知道,但是你知道如果你投资 Python,你将能够释放你的全部编码潜力,而不是停留在 JAVA 的低级混乱中。

Python 是开源的、干净的、用户友好的。有了一个好的文本编辑器,你甚至可以让你的代码比英文文本更漂亮。Python 不期待什么回报,只是一点点欣赏。

Python 可能好得令人难以置信。我个人认为是。

而人们仍在使用 C 和 JAVA 的原因是一个古老的信念——努力就会有回报。我不会撒谎说这个信念是错误的,因为它不是。但是,我想传达的是“*努力就会有回报”*只是被放在了错误的属性上。相反,它应该放在 Python 中。

事实上,在一头扎进 Python 之前,JAVA 之类的东西是的一个很好的热身。他们很可能让你在 Python 上获得成功。

此外,在顽固的程序员眼中,向 Python 的过渡并不是一个选项,例如,他们协助推出了 C 语言,并从那以后就陷入了困境。一个真正与语言一起成长并在其起伏中支持它的开发人员不太可能放弃它。

我确信这些值得尊敬的开发人员即使在极端情况下也不会改变主意——例如,发布一种可以自己编写代码的编程语言。

事实是**“适应”说起来容易做起来难。**

最后的想法

软件开发是一个迷人的领域。它总是让你保持警觉。

因此,你应该随时准备好武器来反击任何对你最亲爱的编程语言的批评。否则,你只能看着它被射杀。知识和经验是你最好的武器。

编程曾经是一个很难触及的领域,但是现在由于 Python 的出现,它对每个人都是一个开放的世界。

“Python 可以教给孩子”——Guido Van Rossum

我个人认为 Python 是一种完美的编程语言。Python 在可读性方面胜过所有人。当然,一个糟糕的 Python 程序员会产生杂乱的代码。然而,我希望混乱的代码没有那个家伙用另一种语言写同样的代码那么混乱。

坦率地说,我从未遇到过 Python 与另一种编程语言相比相形见绌的情况。也许将来我会的。如果我知道,我会重新忏悔。

同时,如果你有 Python 失去了你的尊重的经历,欢迎在评论中分享。

如果 Python 不是开源的,人们会指责我为 Python 做广告。

很高兴是这样!

参考

文章灵感来自《计算机的进步》一书和作者的经历。

Python Lambda 函数:三个实际例子(排序、映射和应用)

原文:https://towardsdatascience.com/python-lambda-functions-three-practical-examples-sort-map-and-apply-286593792cb4?source=collection_archive---------21-----------------------

通过真实的例子学习 lambda 函数

照片由托尔加·乌尔坎在 Unsplash 上拍摄

简介—λ函数

一个高级的 Python 概念是 lambda 函数,它是使用lambda关键字定义的匿名函数。Lambda 函数具有以下基本形式:

lambda params: expression
  • 关键字lambda表示您正在定义一个 lambda 函数。
  • params指的是 lambda 函数将使用的参数。参数的数量可以是零到多个。
  • expression表示 lambda 函数将要运行的表达式。

就其核心而言,lambda 函数就像其他 Python 函数一样,可以使用 call 操作符(括号)来调用。下面的代码向您展示了相关的事实。

>>> multiply = lambda x, y: x*y
>>> type(multiply)
<class 'function'>
>>> multiply(5, 3)
15
>>> multiply(8, 5)
40

虽然我们把 lambda 函数赋给了变量multiply,这只是为了向你展示 lambda 是函数的事实。在实际项目中,强烈建议不要这样做,因为正如它的别名所知,lambdas 是匿名函数,应该用在不需要明确定义函数的地方。

在了解了如何定义 lambda 函数之后,让我们探索它的实际用例,以便更彻底地学习这种技术。

实际例子

示例 1:对自定义实例列表进行排序

通常,我们使用列表来保存同类数据——相同类型的对象。但是,这些对象可能不会按照特定需求的期望顺序排列。例如,一个列表可以包含一个论坛应用程序的一堆帖子。我们应该允许用户按照作者或者评论号来排序文章。在这些情况下,我们可以将sort方法与 lambda 函数一起使用。

使用 Lambda 排序

因为我们希望按照作者的姓氏对文章进行排序,所以 lambda 函数的表达式需要提取每次的姓氏。我们利用了 string 的 split 方法,它创建了一个字符串列表。因为姓氏是最后一项,所以我们使用-1 来检索它。

如你所见,这些帖子确实是按照作者的姓氏排序的。顺便提一下,sort 方法对列表中的项目进行就地排序,这意味着它改变了原始列表的顺序。

示例 2:绘制熊猫系列的数据(地图)

pandas 中的一个主要数据类型是Series类型,它表示一维数据,比如数据表中的一行或一列。当我们从一个系列开始时,我们可以使用 map 方法创建另一个系列。尽管可以使用 dictionary 对象来提供映射,但通常可以使用 lambda 函数。考虑下面的例子。

>>> import pandas as pd
>>> str_data = pd.Series(["95.2%", "92.7%", "93.4%"])
>>> str_data
0    95.2%
1    92.7%
2    93.4%
dtype: object
>>> float_data = str_data.map(lambda x: float(x[:-1]))
>>> float_data
0    95.2
1    92.7
2    93.4
dtype: float64

如上所示,我们从一个由多个百分比字符串数据组成的Series开始。但是,我们希望在删除百分号后提取这些字符串的数值。我们可以用一个 lambda 函数轻松完成转换:lambda x: float(x[:-1])。在这个函数中,x指的是Series中的每一项,我们使用float函数将字符串转换成相应的数值。

示例 3:从 Pandas 数据框架创建数据(应用)

pandas 中的另一个基本数据类型是DataFrame类型,它表示类似数据表的二维电子表格。带有DataFrame的 lambda 函数最常见的用例是apply方法,使用它,我们可以从现有的列创建新的数据列。让我们通过下面的例子来探索这种用法。

数据帧应用 Lambda

在示例中,我们使用 lambda 函数创建另一列portion。参数x指的是DataFrame中的每一行(因为我们设置了axis=1,这意味着操作是按行应用的)。如您所见,在操作之后,我们成功地计算出了具有相应百分比值的部分。

结论

在本文中,我们回顾了 lambda 函数的三个常见用例。本质上,lambda 函数应该用于执行不需要定义常规函数的小任务。

我希望你喜欢这篇文章。你可以在这里成为中级会员来支持我,不需要额外付费。

如何在 Python 中实现链表

原文:https://towardsdatascience.com/python-linked-lists-c3622205da81?source=collection_archive---------2-----------------------

探索如何使用 Python 从头开始编写链表和节点对象

照片由 Mael BALLAND 在 Unsplash 上拍摄

介绍

链表是最基本的数据结构之一,表示一系列节点。序列的第一个元素称为链表的头和尾,最后一个元素对应于尾和尾

序列中的每个节点都有一个指向下一个元素的指针,也可以有一个指向上一个元素的指针。在单链表中,每个节点只指向下一个节点。

单链表—来源:作者

另一方面,在双向链表中,每个节点既指向前一个节点,也指向下一个节点。

双向链表——来源:作者

链表在各种场景中都非常有用。在以下情况下,它们通常优于标准阵列

  • 在序列中添加或删除元素时,您需要一个恒定的时间
  • 更有效地管理内存,尤其是当元素的数量未知时(如果是数组,您可能必须不断地缩小或增大它们。注意,填充数组通常比链表占用更少的内存。
  • 您希望更有效地在中间点插入项目

与其他通用语言不同,Python 的标准库中没有内置的链表实现。在今天的文章中,我们将探索如何使用 Python 实现一个用户定义的链表类。

在 Python 中实现用户定义的链接类

首先,让我们为链表中的单个节点创建一个用户定义的类。这个类既适用于单链表,也适用于双向链表。因此,这个类的实例应该能够存储节点的值,下一个和上一个节点的值。

class Node:
    def __init__(self, value, next_node=None, prev_node=None):
        self.value = value
        self.next = next_node
        self.prev = prev_node

    def __str__(self):
        return str(self.value)

请注意,当一个Node的实例将next设置为None时,这意味着它本质上是链表的尾部(单个或两个)。类似地,在双向链表中,当一个节点的prev被设置为None时,这表明该节点是链表的头。

既然我们已经为节点创建了一个类,现在我们可以为链表本身创建类了。如前所述,链表有一个head、一个tail和指向彼此的节点。

class LinkedList:
    def __init__(self, values=None):
        self.head = None
        self.tail = None if values is not None:
            self.add_multiple_nodes(values)

现在,为了将构造函数中提供的值作为节点添加到链表中,我们需要定义两个额外的方法。

第一个方法叫做**add_node**,用于向链表中添加一个节点。

def add_node(self, value):
    if self.head is None:
        self.tail = self.head = Node(value)
    else:
        self.tail.next = Node(value)
        self.tail = self.tail.next
    return self.tail

现在让我们快速浏览一下这个方法的逻辑。如果链表没有头,那么这意味着它是空的,因此要添加的节点将是链表的头和尾。如果头部不为空,那么我们添加新创建的Node作为当前tailnext元素,最后移动尾部指向新创建的Node

第二个方法叫做**add_multiple_nodes**,它在构造函数中被调用,并且简单地调用我们之前定义的add_node方法,以便在链表实例中添加多个值作为节点。

def add_multiple_nodes(self, values):
    for value in values:
        self.add_node(value)

到目前为止,我们的链表类如下所示:

class LinkedList:
    def __init__(self, values=None):
        self.head = None
        self.tail = None if values is not None:
            self.add_multiple_nodes(values) def add_node(self, value):
        if self.head is None:
            self.tail = self.head = Node(value)
        else:
            self.tail.next = Node(value)
            self.tail = self.tail.next
        return self.tail def add_multiple_nodes(self, values):
        for value in values:
            self.add_node(value)

现在让我们创建一个额外的方法,它能够插入一个新元素,但是这次是在链表的开始,也就是作为一个头。

def add_node_as_head(self, value):
    if self.head is None:
        self.tail = self.head = Node(value)
    else:
        self.head = Node(value, self.head)
    return self.head

现在让我们在类中重写一些可能有用的特殊方法。首先,让我们实现__str__方法,以便链表对象的字符串表示是人类可读的。例如,当打印出一个带有节点a, b, c, d的链表时,输出将是a -> b -> c -> d

def __str__(self):
    return ' -> '.join([str(node) for node in self])

其次,让我们也实现__len__方法,它将返回我们的用户定义类的长度,本质上是序列中包含的节点数。我们需要做的就是遍历序列的每个节点,直到到达链表的尾部。

def __len__(self):
    count = 0
    node = self.head
    while node:
        count += 1
        node = node.next
    return count

最后,让我们通过实现__iter__方法来确保LinkedList类是可迭代的。

def __iter__(self):
    current = self.head
    while current:
        yield current
        current = current.next

此外,我们还可以创建一个名为values的属性,这样我们就可以访问序列中所有节点的值。

@property
def values(self):
    return [node.value for node in self]

最终的类如下所示:

class LinkedList:
    def __init__(self, values=None):
        self.head = None
        self.tail = None if values is not None:
            self.add_multiple_nodes(values) def __str__(self):
        return ' -> '.join([str(node) for node in self]) def __len__(self):
        count = 0
        node = self.head
        while node:
            count += 1
            node = node.next
        return count def __iter__(self):
        current = self.head
        while current:
            yield current
            current = current.next @property
    def values(self):
        return [node.value for node in self] def add_node(self, value):
        if self.head is None:
            self.tail = self.head = Node(value)
        else:
            self.tail.next = Node(value)
            self.tail = self.tail.next
        return self.tail def add_multiple_nodes(self, values):
        for value in values:
            self.add_node(value) def add_node_as_head(self, value):
        if self.head is None:
            self.tail = self.head = Node(value)
        else:
            self.head = Node(value, self.head)
        return self.head

现在,即使我们的Node类可以表示包含在单向或双向链表中的节点,我们定义的LinkedList类只能支持单向链表。这是因为在添加节点时,我们没有指定前一个节点。

为了处理双向链表,我们可以简单地创建一个额外的类,它继承自LinkedList类并覆盖add_nodeadd_node_as_head方法:

class DoublyLinkedList(LinkedList):
    def add_node(self, value):
        if self.head is None:
            self.tail = self.head = Node(value)
        else:
            self.tail.next = Node(value, None, self.tail)
            self.tail = self.tail.next
        return self def add_node_as_head(self, value):
        if self.head is None:
            self.tail = self.head = Node(value)
        else:
            current_head = self.head
            self.head = Node(value, current_head)
            current_head.prev = self.head
        return self.head

Python 中用户自定义链表的完整代码

包含我们在今天的教程中创建的三个类的完整代码作为 GitHub 要点在下面给出。

包含 Node、LinkedList 和 DoublyLinkedList Python 类的完整代码——来源:作者

最后的想法

在今天的指南中,我们讨论了最基本的数据结构之一,即链表。鉴于 Python 的标准库不包含这种特定数据结构的任何实现,我们探索了如何从头实现用户定义的链表类。

成为会员 阅读介质上的每一个故事。你的会员费直接支持我和你看的其他作家。你也可以在媒体上看到所有的故事。

https://gmyrianthous.medium.com/membership

你可能也会喜欢

Python 列表被高估了

原文:https://towardsdatascience.com/python-lists-are-overrated-776e87cda3e5?source=collection_archive---------27-----------------------

考虑这些潜在的更好的选择

照片由来自佩克斯的克里斯蒂娜·莫里洛拍摄

如果您使用 Python 已经有一段时间了,那么您应该非常熟悉列表;它们无处不在。

列表可以说是 Python 中最流行的数据结构,很容易理解为什么。它们具有巨大的实用性和多功能性。

不幸的是,随着对列表的严重依赖,忽略了其他有能力的数据结构,这些数据结构可以说比列表执行得更好。

你可能会想:这有什么关系呢?如果列表能完成工作,为什么还要用其他工具呢?

如果您满足于处理少量数据,这种推理是合理的。然而,如果你打算利用你的技能为其他公司服务,你应该明白,你不能对如何存储和处理你的数据如此松懈。

经常被数十亿字节的数据所累的公司强调开发在时间和内存消耗方面优化的高效程序的重要性。

效率和功能一样重要!

您需要理解,即使有多种工具可以完成工作,但有些工具就是比其他工具优越。了解哪种数据结构对于特定场景是理想的,对于提高代码质量来说是一个巨大的进步。

虽然列表确实是一种可靠的数据结构,但它们并不适合所有情况。以下是几个值得考虑的替代方案。

1.元组

元组与列表有许多相似之处。这两种数据结构都存储不同的数据类型,并为它们的元素分配特定的顺序。

两者之间的主要区别在于,与列表不同,元组是不可变的。它们的赋值不能以任何方式改变。

考虑到程序员经常需要修改他们存储的数据,这使得列表看起来更有吸引力。然而,出于这个原因完全省略元组是错误的。

尽管元组不允许你改变它的值,但它仍然比列表有一个主要的优势:内存使用。元组比列表需要更少的空间来存储相同数量的数据。

让我们用一个简单的例子来说明这一点。

这里,我们创建一个存储相同值的列表和元组,并使用 sys 模块来确定两个对象的大小。

由作者创建

代码输出(由作者创建)

如上所示,元组需要更少的内存来存储与列表相同的数据。

创建内存高效的程序是必须的,这就是为什么有必要将这种数据结构保存在您的武库中。

由于元组是不可变的,所以只能用来存储值和查找值。因此,使用元组的理想场景是当您知道元组中的值并且不需要修改它们时。

2.Numpy 数组

与列表不同,numpy 数组不是内置的数据结构。它们来自 numpy 模块,专门进行数学运算。

与列表不同,numpy 数组只存储同质值(即数组的元素必须是同一类型)。

幸运的是,它们弥补了这个缺点,允许您以更少的内存使用和更快的运行时间来执行各种计算。

下面的例子展示了 numpy 数组的快速运行时间。

假设我们希望创建 2 个包含 1,000,000 个值的列表,范围从 0 到 999,999,然后将每个列表的元素相加,形成第三组值。让我们来看看用列表和 numpy 数组实现这一点需要多少时间。

注意:运行时间将通过使用“%%timeit”神奇函数得出

运行时间:148 毫秒(由作者创建)

运行时间:3.8 毫秒(由作者创建)

运行时间的差异证明了 numpy 数组的可用性。

除了运行时间更短之外,numpy 数组的内存效率也更高。让我们使用 sys 模块来比较包含 1,000,000 个值的列表和包含 1,000,000 个值的 numpy 数组的大小。

由作者创建

代码输出(由作者创建)

当存储 1,000,000 个值时,numpy 数组使用的内存不到列表的一半。

总的来说,numpy 数组在运行时间和内存使用上都超过了 lists。虽然使用列表进行简单的计算是完全可以的,但是当涉及到计算密集型的计算时,numpy 数组是您最好的选择。

3.设置

在我看来,集合是 Python 中最容易被忽视的数据结构。

根据定义,集合存储不同值的可变集合。鉴于集合不允许重复,人们可能会选择坚持使用列表,因为他们已经习惯使用后者。

然而,忽略集合意味着抛弃所有伴随它们而来的功能。

在执行搜索时,集合是非凡的。

举个例子,让我们把这些文本语料库(可以通过这个链接访问)拆分成一个单词集合。这些单词将存储在列表和集合中。

由作者创建

在比较在列表和第一个语料库的集合中搜索单词“believe”所花费的时间之前,为了公*比较,我将从列表中删除重复值。

删除重复项(由作者创建)

下面是两种搜索的比较:

运行时间:313ns(由作者创建)

运行时间:53.5ns(由作者创建)

运行时间的差别是白天和黑夜。

此外,集合提供了许多功能,使搜索变得更加容易。

例如,如何找到在两个文本语料库中都存在的所有单词?下面是如何使用列表来实现的。

运行时间:37.3 秒(由作者创建)

这是使用集合时的样子。

运行时间:586ns(由作者创建)

set 不仅用更少的时间完成了任务,还用更少的代码行完成了任务。集合提供了许多有用的功能,如“交集”,使程序员能够从不同的集合中提取所需的数据。

因此,在执行搜索时,设置很容易胜过列表。然而,当使用集合时,有一个必须考虑的速度-内存权衡。虽然集合可以缩短运行时间,但它们也需要更多内存。

由作者创建

代码输出(由作者创建)

是否应该使用集合取决于项目的目标和限制。如果您的首要任务是最小化运行时间,那么集合就是理想的数据结构。

结论

照片由来自佩克斯的普拉蒂克·卡蒂亚尔拍摄

总的来说,列表是非常通用的,但是仅仅依靠它们来存储数据是错误的。为了编写有效的代码,熟悉使用不同数据结构的优点和缺点是很重要的。

如果你限制自己只使用列表(即使它们足够了),你就限制了自己作为程序员的能力,并且限制了代码的效率。

希望您现在已经学会了在执行数据操作时总是考虑可选的数据结构。

我祝你在编码工作中好运!

Python 列表有时比 NumPy 快得多。这是证据。

原文:https://towardsdatascience.com/python-lists-are-sometimes-much-faster-than-numpy-heres-a-proof-4b3dad4653ad?source=collection_archive---------4-----------------------

小心使用什么。

布拉登·科拉姆在 Unsplash 拍摄的照片

我最*在做一个数字图像处理项目。超参数调整花了相当长的时间,我才得到想要的精度。都是因为过度拟合的寄生虫和我没用的低端硬件。

对于每次执行,我的机器花费大约 15-20 分钟。20 分钟处理 20 000 个条目。我想象如果我一直在处理一个 100 万的记录数据集,我将不得不在训练结束之前等待地球完成一次完整的旋转。

我对模型的准确性感到满意。然而,在提交我的代码之前,我想尝试许多其他的卷积神经网络(CNN)架构。因此,我决定在我的代码中寻找优化空间。

因为我使用的是 PyPi 中预先构建的机器学习算法——Scikit-Learn 和 tensor flow——只剩下很少的子例程需要优化。一个选择是在数据结构方面提升我的代码。我将数据存储在列表中,由于 NumPy 非常快,我认为使用它可能是一个可行的选择。

猜猜我的列表代码转换成 NumPy 数组代码后发生了什么?

令我惊讶的是,执行时间没有缩短。相反,它飙升。

也就是说,在这篇文章中,我将带您了解列表最终比 NumPy 数组表现更好的确切情况。

数字和列表

首先,让我们讨论 NumPy 数组和列表之间的区别。

NumPy 是用于 N 维数组操作和计算的事实上的 Python 库。它是开源的,易于使用,内存友好,速度快如闪电。

NumPy 最初被称为“Numeric”,它为许多数据科学库(如 SciPy、Scikit-Learn、Panda 等)设置了框架。

Python 列表存储有序的、可变的数据对象的集合,而 NumPy 数组只存储单一类型的对象。因此,我们可以说 NumPy 数组生活在列表的保护伞下。因此,没有 NumPy 数组做不到的事情。

但是,说到 NumPy 整体。Numpy 不仅包括数组操作,还包括许多其他例程,如二元运算、线性代数、数学函数等等。我相信它涵盖了超过一个人可能需要的。

接下来要考虑的是为什么我们通常使用 NumPy 数组而不是列表。

简而言之,我相信每个阅读这篇文章的人都知道:它更快。

NumPy 确实快得离谱,尽管 Python 速度慢是众所周知的。这是因为 NumPy 是 C 和 Fortran 的包装器。而且不用说这两个有多快。

NumPy 数组比列表快

在我们讨论 NumPy 数组变得像蜗牛一样慢的情况之前,有必要验证 NumPy 数组通常比列表更快的假设。

为此,我们将使用 NumPy 和 lists 来计算一百万个元素数组的*均值。该数组是随机生成的。

以下代码是一个示例:

"""General comparison between NumPy and lists"""import numpy as np
from time import time#Random numpy array
numpy_array = np.random.rand(1000000)
list_conv = list(numpy_array)#Start timing NumPy compuation
start1 = time()
#Compute the mean using NumPy
numpy_mean = np.mean(numpy_array)
print(f"Computing the mean using NumPy: {numpy_mean}")
#End timing
end1 = time()
#Time taken
time1 = end1 - start1
print(f"Computation time: {time1}")#Start timing list computation
start2 = time()
#Compute the mean using lists
list_mean = np.mean(list_conv)
print(f"Computing the mean using lists: {list_mean}")
#End timing
end2 = time()
#Time taken
time2 = end2 - start2
print(f"Computation time: {time2}")#Check results are equal
assert abs(numpy_mean - list_mean) <= 10e-6, "Alert, means are not equal"

我的机器输出如下:

Computing the mean using NumPy: 0.4996098756973947
Computation time: 0.01397562026977539
Computing the mean using lists: 0.4996098756973947
Computation time: 0.17974257469177246

正如预测的那样,我们可以看到 NumPy 数组明显比列表快。相当大的速度差异是显而易见的。

也就是说,我们能概括地说 NumPy 数组总是比列表快吗?

事实证明 NumPy 数组并不总是超过列表。列表也有锦囊妙计,这就把我们带到了下一个环节。

NumPy 数组并不总是比列表快

如果列表与 NumPy 数组相比毫无用处,它们可能已经被 Python 社区抛弃了。

与 NumPy 数组相比,列表更出色的一个例子是append()函数。"append()"将值添加到列表和 NumPy 数组的末尾。这是一个常见且经常使用的功能。

下面的脚本演示了列表的append()和 NumPy 的append()之间的比较。这段代码只是将从 0 到 99 999 的数字添加到一个列表和一个 NumPy 数组的末尾。

"""numpy.append() vs list.append()"""
import numpy as np
from time import timedef numpy_append():
    arr = np.empty((1, 0), int)
    for i in range(100000):
        arr = np.append(arr, np.array(i))
    return arrdef list_append():
    list_1 = []
    for i in range(100000):
        list_1.append(i)
    return list_1def main ():
    #Start timing numpy array
    start1 = time()
    new_np_arr = numpy_append()
    #End timing
    end1 = time()
    #Time taken
    print(f"Computation time of the numpy array : {end1 - start1}") #Start timing numpy array
    start2 = time()
    new_list = list_append()
    #End timing
    end2 = time()
    #Time taken
    print(f"Computation time of the list: {end2 - start2}") #Testing
    assert list(new_np_arr) == new_list, "Arrays tested are not the same"if __name__ == "__main__":
    main()

我的机器产生以下输出:

Computation time of the numpy array : 2.779465675354004
Computation time of the list: 0.010703325271606445

正如我们所看到的,在这个例子中,列表比 NumPy 数组表现得更好。Numpy 表现差到被超过 2000 %的地步。

这个案例表明,无论何时涉及到速度,NumPy 都不应该被认为是“总是去”的选项。相反,需要仔细考虑。

Numpy.append 有什么问题?

为了解开这个谜,我们将访问 NumPy 的源代码。append()函数的 docstring 告知如下内容:

"Append values to the end of an array. Parameters
    ----------
    arr : array_like
        Values are appended to a copy of this array.
    values : array_like
        These values are appended to a copy of `arr`.  It must be of 
        the correct shape (the same shape as `arr`, excluding
        `axis`). If `axis` is not specified, `values` can be any 
        shape and will be flattened before use.
    axis : int, optional
        The axis along which `values` are appended.  If `axis` is 
        not given, both `arr` and `values` are flattened before use. Returns
    -------
    append : ndarray
        A copy of `arr` with `values` appended to `axis`.  Note that
        `append` does not occur in-place: a new array is allocated
        and filled.  If `axis` is None, `out` is a flattened array."

在彻底阅读了 docstring 之后,我们可以看到关于函数返回内容的“注释”。它声明追加过程不发生在同一个数组中。而是创建并填充一个新数组。

然而,在列表中,事情是非常不同的。列表填充过程停留在列表本身中,不会生成新的列表。

总之,我们可以看到numpy.append()的复制-填充过程使它成为一个开销。

外卖食品

编程没有万金油。

我们的发现证明了 NumPy 阵列并不能解决所有性能问题。在考虑所有选择之前,不应该盲目地采取行动。这样做也最大化了产生设计更好的代码的机会。

此外,通过实验探索多种途径可以确保人们最终不会后悔做出了不同的选择。更重要的是,实验会发现错误信息。

最后,作为对我坚持这一观点的奖励,这里有一段 A. Einstein 深刻的引用,总结了这篇文章的观点。

“再多的实验也无法证明我是对的;一个简单的实验就能证明我是错的。”——阿尔伯特·爱因斯坦。

如果你觉得这很有见地,可以考虑成为 高级 会员,每月 5 美元。如果用这个 链接 ,我会得到一个小切。

https://ayarmohammed96.medium.com/membership

享受您的编程日!

Python 列表、Numpy 数组和 Pandas 系列

原文:https://towardsdatascience.com/python-lists-numpy-arrays-and-pandas-series-72c4829242bf?source=collection_archive---------18-----------------------

由 Jyotirmoy Gupta 在 Unsplash 上拍摄的照片

不同数据结构的便利性

假设你有 1 到 20 之间的奇数,你用以下方式存储它们:

# Python list
my_odd_nums = [1, 3, 5, 7, 9, 11, 13, 15, 17, 19]# Numpy array
my_odd_nums = numpy.array([1, 3, 5, 7, 9, 11, 13, 15, 17, 19])# Pandas series
my_series = pandas.Series(my_odd_nums)>>
0     1
1     3
2     5
3     7
4     9
5    11
6    13
7    15
8    17
9    19
dtype: int64

列表、数组和熊猫系列乍一看非常相似,所以人们经常会问——为什么我们需要不同的数据结构?有哪些利弊和用例?这篇简短文章的目的就是要澄清一些困惑。

1。Python 列表

列表是 Python 中存储多项的 4 种内置数据类型之一(另外 3 种数据类型是字典、元组和集合)。一个列表可以同时存储多种数据类型——整数、浮点、字符串。一个列表甚至可以存储其他列表、元组和字典。

使用列表的相关方法如下:append(), extend(), insert(), remove(), pop(), clear(), index(), count(), sort(), reverse(), copy()

例如,append()方法用于将一个条目添加到一个已有的列表中:

mylist = [10, 12, 8, 6, 14]
mylist.append(99)>> [10, 12, 8, 6, 14, 99]

关于如何应用这些方法和函数的更多细节,请查看本文:使用 Python 列表:备忘单。

2.Numpy 数组

另一方面,Numpy 是用于科学计算的核心 Python 库(因此得名“数字 Python”或 Numpy)。该库提供了创建和处理多维对象(称为数组)的方法和函数。数组是值的网格,与 Python 列表不同,它们具有相同的数据类型:

# 1-dimesional array
array([1, 2, 3])# 2-dimensional array
array([[1, 2],
       [3, 4],
       [5, 6]])

常用方法的一个例子是随机数生成器:

# generate random integers
ran = np.random.randint(0,20,5) # low, high, size
print(ran)>> array([1, 3, 5])

由于 Numpy 数组都是数字数据类型,因此可以应用一系列统计操作:

# to add all values in an array
my_array = array([1,2,3])my_array.sum()>> 6

Numpy 是一个强大的库,具有全面的数学函数和线性代数例程,因此它是许多数据科学/机器学习/深度学习库中的基础。

3.熊猫系列

Pandas 系列是一个一维数值列表(可以是混合数据类型——整数、浮点、文本),存储有一个带标签的索引。而如果多个序列组合成一个单一的指标,则称为“数据框”。换句话说,数据帧是具有相同索引的系列的集合。Pandas 是数据科学中最受欢迎的数据辩论库。

可以从现有的 Pythion 列表或 Numpy 数组创建系列:

# index
countries = ["US", "BD", "CA"]
# data
data = [100, 200, 300]
# series
myseries = pd.Series(data, index = countries)>>
US    100
BD    200
CA    300
dtype: int64

回到原来的问题:列表、数组、数列的区别和优势是什么?

一个关键的区别是存储数据所需的空间。假设我们有 0 到 20 之间的数字,存储在 3 个数据结构中。看储物空间的差异。Numpy 数组显然是赢家。

另一个区别是 Numpy 数组在向量和矩阵运算中的显著高性能。

尽管存在一些差异,但每种数据类型在数据科学中都有特定的应用案例——例如,用于存储包括文本数据在内的复杂数据类型的 Python 列表;用于高性能数值计算的 Numpy 阵列:和 Pandas 系列,用于操纵表格数据以进行可视化、统计建模以及过滤和汇总。

希望这是一次有益的讨论,如果你有意见,请随时写在下面,或者通过媒体、推特或 LinkedIn 与我联系。

Python 日志记录

原文:https://towardsdatascience.com/python-logging-from-nothing-to-something-79a7f6a0c1eb?source=collection_archive---------20-----------------------

没事->有事

跟着练习,为你的编程工作装备这种不性感但重要的日志记录技能

只是一个基本的日志功能!

我们通常打印输出来检查我们的逻辑在编码时是否按预期运行。但是,在生产过程中可能会出现运行时问题,我们无法验证打印输出。这时,日志记录可以帮助我们识别运行时问题。我们开始吧!

导入日志模块

创建一个 python 文件log_utility.py并编写第一行代码:

import logging

简单,嗯?日志是软件开发的基本特性之一,所以 Python 拥有这个内置模块来支持严肃的软件开发是很好的。

定义 get_logger()函数并初始化日志记录参数

log_utility.py中,我们定义一个函数并初始化所有参数:

def get_logger(name, log_config = None):
 log_filename = 'C:\\temp\\main_%PID%.txt'.replace('%PID%', str(os.getpid()))
 log_format = "%(asctime)s from %(name)s: %(message)s"
 log_datefmt = "%d %b %Y %H:%M:%S"
 log_level = logging.INFO
 logging.basicConfig(filename = log_filename,
      format = log_format,
      datefmt = log_datefmt,
      level = log_level)
 return logging.getLogger(name)
  • filename:存储日志消息的日志文件的文件名。在这种情况下,输出的一个例子是C:\temp\main_8888.txt。我们可以使用 PID(进程 ID)作为唯一的标识符,作为文件名的一部分。因此,我们需要包含模块os:
import logging, os 
  • format:日志文件中记录的消息格式。输出的一个可能的例子是14 Aug 2021 00:00:00 from __main__: Hello World!,其中asctime表示日期时间,name表示传递给函数的参数name,而message表示要记录的消息。日期时间的格式由下一个参数定义。
  • datefmt:日期时间的格式。这里格式字符串%d %b %Y %H:%M:%S被翻译成14 Aug 2021 00:00:00。有关更多格式化指令,请参考时间—时间访问和转换— Python 3.9.6 文档
  • level:如果消息的级别高于或等于该参数,将被记录。否则,不会。在这里,水*被设置在logging.INFO

最后,我们通过调用logging.getLogger(name)返回一个 Logger 对象

记录器的使用

既然我们已经创建了一个基本的日志功能,我们需要将这个功能合并到我们的主应用程序中。

创建一个main.py并插入如下代码:

from log_utility import get_loggerlogger = get_logger(__name__)def run():
 logger.info('Hello World from main!')if __name__ == '__main__':
 run()

运行main.py,你将得到一个日志文件C:\temp\main_%PID%.txt,其中%PID%是一个反映运行进程 PID 的数字序列。日志文件中一个可能的输出是14 Aug 2021 00:58:46 from __main__: Hello World from main!

就是这样!我们已经成功地实现了一个基本的日志功能,可以在项目的任何地方使用。每当我想到对这个日志记录机制的任何改进,我都会用它来更新这个页面。也欢迎分享你的想法。

Python 循环:综合指南

原文:https://towardsdatascience.com/python-loops-a-comprehensive-guide-825f3c14f2cf?source=collection_archive---------13-----------------------

遍历列表、元组、字典和字符串以及循环控制—中断、继续、传递

普里西拉·杜·普里兹在 Unsplash 上的照片

什么是循环

在编程中,循环意味着以相同的顺序多次重复相同的计算。

想想现实生活中的情况。你是一名野外生物学家,正在测量森林中的树木。你选择一棵树,测量它的直径和高度,记在你的笔记本上,估计它的总体积。

接下来,你选择另一棵树,测量它的直径和高度,记在你的笔记本上,估计它的总体积。

然后,你再选一棵树,测量它的直径和高度,记在你的笔记本上,估计它的总体积。

您不断重复相同的过程,直到样本中的所有树都用完为止。在编程行话中,你在每棵树上循环,以相同的顺序执行相同的任务。

回到编程上来,如果给你一个整数值列表,要求你对每一项求*方,然后加 5,最后报告结果——这就是循环的一个例子。

我们可以循环什么?

那么我们能循环什么呢?基本上,任何可迭代的数据类型。Python 中的可迭代对象是以不同数据格式存储的值序列,例如:

  • 列表(例如[10,12,13,15])
  • 元组(例如(10,12,13,15)
  • 字典(例如{ '姓名':'艾伦','年龄':25})
  • 字符串(例如“数据科学”)

什么是不同种类的循环?

你主要会遇到的两种循环:for 循环和 while 循环。其中,for 循环是数据科学问题中最常见的一种。主要区别在于:

  • for 循环对 iterable 对象中的每个元素迭代有限次
  • 而循环继续进行,直到满足某个条件

在列表上循环

遍历列表非常简单。给你一个值列表,要求你对每一项做些什么。假设您有:

my_list = [1,2,3,4]

并且要求您计算列表中每个值的*方:

for each_value in my_list:
    print(each_value * each_value)Out:
1
4
9
16

类似地,你可以做一些更复杂的循环(例如“嵌套循环”)。例如,给你两个列表,要求你(I)将一个列表的值与另一个相乘,(ii)将它们追加到一个空列表中,以及(iii)打印出新的列表。让我们按顺序来做:

new_list = []list1 = [2, 3, 4]
list2 = [4, 5, 6]for i in list1:
    for j in list2:
        new_list.append(i*j)

print(new_list)Out:
[8, 10, 12, 12, 15, 18, 16, 20, 24]

在元组上循环

根据元组的结构和要完成的任务,在元组上循环可能有点复杂。

让我们将一些元组存储在一个列表中,每个元组代表一个班级中学生的姓名和年龄:

students = [('Allie', 22), ('Monty', 18), ('Rebecca', 19)]

现在的任务是(I)提取所有年龄,( ii)将它们存储在一个列表中,( iii)计算*均年龄:

ages = []for i,j in students:
    ages.append(j)avg = sum(ages)/len(ages)
print(avg)Out: 
19.666666666666668

这里的每个元组包含两项(姓名和年龄)。即使你对名字不感兴趣,通过 ij 你也指定了这两个项目,并要求将项目 j (年龄)添加到新列表中。叫做“元组解包”。

在字典上循环

Python 中的字典是一个键-值对的集合——这意味着字典中的每一项都有一个键和一个关联值。字典的一个例子:

# fruit price dictionary
fruit_prices = {"apple": 2.50, "orange": 4.99, "banana": 0.59}

您可以循环遍历这些字典元素,并执行各种操作。这里有几个例子:

提取字典中的所有键:

for i in fruit_prices.keys():
    print(i)Out:
apple
orange
banana

将所有值存储在列表中:

values = []
for i in fruit_prices.values():
    values.append(i)
print(values)Out:
[2.5, 4.99, 0.59]

挑战:你能在字典中找到所有价格的*均值吗?

在字符串上循环

让我们考虑字符串—“Hello”。它看起来像一个可迭代的对象吗?事实上的确如此。

for i in 'Hello':
    print(i)Out:
H
e
l
l
o

你可以用 for 循环解开字符串中的每个字符,并对它们进行各种操作。

同样,也可以迭代一个句子中的每个单词。然而,在这种情况下,需要额外的步骤来拆分句子。

sent = 'the sky is blue'# splitting the sentence into words
sent_split = sent.split()# extract each word with a loop
for i in sent_split:
    print(i)Out:
the
sky
is
blue

While 循环

与 for 循环一样,while 循环重复执行一段代码——只要条件为真。只有当循环条件为假时,循环才会中断。

while 循环的一般结构如下:

i = 0while i <=5:
    print(i)
    i = i+1 # option to break out of the loop
Out:
0
1
2
3
4
5

在上面的例子中,在每次迭代中,打印出 i 的值,直到它达到 5。此后,while 循环条件变为假(即当 i = 6i ≤ 5 变为假)。

while 循环的一个实际用例是在网站上使用您的登录凭证。当您没有提供正确的用户名或密码时,您无法登录*。*

user_id = 'user101'while True:
    user = input('Enter your user ID: ')

    if user == user_id:
        print("You have entered ", user) 
        break
    else:
        print("Enter a valid user ID ")

循环控制:继续、中断、通过

所谓的循环控制关键词有三种:break、continue、pass。这些语句改变了循环的流程,并允许程序在某个外部条件被触发时退出或跳过部分循环。

破裂

如果循环中存在一个break 语句,当满足一个条件时,它将终止循环。

string = 'hello, there'for i in string:
    if i == ',':
        break
    print(i)Out:
h
e
l
l
o

在上面的代码片段中,我们要求程序在找到字符串中的逗号并执行下一条语句(打印 I)后立即存在。想想开头给出的现实生活中的例子。作为一名野外生物学家,你在反复测量树木。但是你会打破这个循环——如果下雨

继续

continue语句没有跳出循环,而是简单地跳过一次迭代,继续下一次迭代。

让我们执行上面相同的代码,但是使用 continue 关键字。

string = 'hello, there'for i in string:
    if i == ',':
        continue
    print(i)Out:
h
e
l
l
o

t
h
e
r
e

所以在这种情况下,如果循环遇到一个逗号,循环就跳过这个逗号继续。

因此,在树木测量示例中,如果您遇到一棵枯树/断树,您只需跳过它并继续下一个。

及格

不做任何事情,它只是一个还没写完的语句的占位符。

string = 'hello, there'for i in string:
    pass

如果我们没有放一个pass在那里,它将抛出一个错误消息,其余的代码将不会执行。

摘要

本文的目的是给出 Python 中的for 循环和while 循环的直观感受。给出了如何循环遍历列表、元组、字典和字符串等可迭代对象的例子。到最后,循环控制语句的概念——break、continue 和 pass——都包含了例子。

本文旨在给出循环如何工作的初步概述。在以后的文章中,我将介绍一些数据科学家在项目中经常遇到的高级循环挑战。

希望这篇文章是有用的,如果你有意见,请写在下面,或者通过媒体、 Twitter 或 LinkedIn 与我联系。

Python 循环:一些初学者友好的循环挑战

原文:https://towardsdatascience.com/python-loops-some-beginner-friendly-looping-challenge-e112fa606493?source=collection_archive---------3-----------------------

遍历列表、元组、字典和字符串

由博尼瓦尔·塞巴斯蒂安在 Unsplash 上拍摄的照片

在编程中,循环是一种逻辑结构,它重复一系列指令,直到满足某些条件。循环允许对 iterable 对象中的每一项重复相同的任务集,直到所有项都用完或达到循环条件。

循环应用于 iterables,iterables 是以特定数据格式(如字典)存储一系列值的对象。循环的美妙之处在于,你只需编写一次程序,就可以根据需要在任意多的元素上使用它。

本文的目的是实现一些应用于四种 Python 数据类型的中间循环挑战:列表、元组、字典和字符串。选择这些挑战是为了给各种可能的操作提供一个通用的循环结构。

遍历列表

列表是 Python 中的内置数据类型。它们是可变的、无序的元素序列——也称为可迭代对象。它是 Python 循环中使用的最通用的数据类型。所以我们的第一组循环挑战是基于列表的。

挑战#1: 遍历列表中的每个数字,分离出偶数和奇数。还要确定可能的异常值。

my_list = [1, 2, 3, 4, 5, 6, 7, 100, 110, 21, 33, 32, 2, 4]even = []
not_even = []
outlier = []for i in my_list:
    if i > 90:
        outlier.append(i)    
    elif i%2 == 0:
        even.append(i)
    else:
        not_even.append(i)print('Even numbers', even)
print('Odd numbers', not_even)
print('outliers', outlier)Out:
Even numbers [2, 4, 6, 32, 2, 4]
Odd numbers [1, 3, 5, 7, 21, 33]
outliers [100, 110]

**挑战 2:找出列表中所有数字的总和**

num_sum = 0for i in my_list:
    num_sum += iprint('Sum of the elements in the list', num_sum)Out:
Sum of the elements in the list 330

**挑战 3:计算列表中偶数的和**

# step 1: find the even numbers
even = []for i in my_list:
    if i%2 == 0:
        even.append(i)# step 2: add all items in even list
sum_num = 0for i in even:
    sum_num +=iprint('The sum of even numbers', sum_num)Out:
The sum of even numbers 260

**挑战 4:计算一个列表中偶数的个数**

count = 0for i in range(len(my_list)):
    if my_list[i]%2==0:
        count +=1print('The count of even numbers in the list', count)Out:
The count of even numbers in the list 8

挑战 5:计算列表中所有元素的累积和

initial_val = 0
cumsum = []for i in my_list:
    initial_val += i
    cumsum.append(initial_val)print('The cummulative sums of the list', cumsum)Out:
The cummulative sum of the list [1, 3, 6, 10, 15, 21, 28, 128, 238, 259, 292, 324, 326, 330]

挑战#6:遍历两个不同的列表,用 **zip** 函数将它们聚合

state_id = [1,2,3]
state = ['Alabama', 'Virginia', 'Maryland']for i, j in zip(state_id, state):
    print(f'{i} {j}')Out:
1  Alabama
2  Virginia
3  Maryland

遍历元组

元组类似于列表,但关键的区别是,元组是不可变的。与列表不同,没有与元组相关联的方法来添加或移除元组中的项目。由于元组是不可变的,所以它们被广泛用于不变的数据(例如,社会保险号、出生日期等。)

挑战 7:遍历混合数据类型的元组,只提取整数值

my_tup = (1, 2, 'apple', 3, 4, 'orange')for i in my_tup:
    if type(i) == int:
        print(i)Out:
1
2
3
4

挑战 8:元组解包:提取存储在列表中的元组

list_of_tutple = [(1,2), (3, 4), (5, 6)]list1 = []
list2 = []for i,j in list_of_tutple:
    list1.append(i)
    list2.append(j)

print('List of first tuple items', list1)
print('List of second tuple items', list2)Out:
List of first tuple items [1, 3, 5]
List of second tuple items [2, 4, 6]

挑战 9:应用 **enumerate()** 函数提取元组中的元素

my_tup = ('Apple', 'Orange', 'Banana')for i,j in enumerate(my_tup, start=1):
    print(i,j)Out:
1 Apple
2 Orange
3 Banana

循环浏览字典

Python 中的字典是键值对的集合,这意味着字典中的每个条目都有一个键和一个关联值。下面是一个 Python 字典的例子,其中水果是“键”,价格是“值”。[注意:接下来的循环挑战将使用该字典作为输入。]

# Python dictionary
my_dict = {"apple": 2.50, "orange": 4.99, "banana": 0.59}

挑战 10:访问字典中的所有键

for i in my_dict.keys():
    print(i)Out:
apple
orange
banana

挑战 11:访问字典中的所有值

for i in my_dict.values():
    print(i)Out:
2.5
4.99
0.59

挑战 12:从字典中同时访问键和值

for i,j in my_dict.items():
    print('Key: ', i)
    print(f'Value: {j}')Out: 
Key:  apple
Value: 2.5
Key:  orange
Value: 4.99
Key:  banana
Value: 0.59

挑战 13:找出字典中所有值的*均值

for i in my_dict.values():
    values.append(i)

average = sum(values)/len(values)print(f'Average of values: {average}')Out:
Average of values: 2.6933333333333334

挑战 14:条件循环:根据特定条件过滤字典条目

fruits = []for i in my_dict.keys():
    if i in ["apple", "orange", "pear", "watermelon"]:
        fruits.append(i)print(fruits)Out:

['apple', 'orange']

挑战 15:更新值:将所有值减少 25%(在实际应用中,这就像给所有水果打八五折)

for i, j in my_dict.items():
    my_dict.update({i: j*.75})print(my_dict)Out:
{'apple': 1.875, 'orange': 3.7425, 'banana': 0.4425}

在字符串中循环

Python 没有单独的“字符”数据类型,而是由字符串对象表示一系列字符。字符串是不可变的——一旦创建就不能更改——但是每个元素都可以通过循环访问。

挑战 16:通过字符串访问所有元素的简单迭代

my_str = 'Matplotlib'for i in my_str:
    print(i, end=' ')Out:
M a t p l o t l i b

挑战 17:切片:遍历字符串中的替换字符

my_str = 'Matplotlib'for i in my_str[0: len(my_str): 2]:
    print(i, end=' ')Out:

M t l t i

挑战 18:迭代一个句子并打印每个单词

sent = 'It is a dark night'# splitting the sentence into words
sent_split = sent.split()# extract each word with a loop
for i in sent_split:
    print(i, end = ' / ')Out:
It / is / a / dark / night /

一锤定音

本文的目的是提供一些初学者友好的中级循环挑战,这些挑战经常出现在数据科学项目中。当然,有数百种可能的组合可以应用于各种情况和各种数据类型,但是这里给出的例子是更高级循环的基础。

希望这些挑战是有用的,如果你有意见,请随意写在下面,或者通过媒体、推特或 LinkedIn 与我联系。

Python 使电子表格 Excel'lent

原文:https://towardsdatascience.com/python-makes-spreadsheets-excellent-f48ce0c648e3?source=collection_archive---------16-----------------------

使用 Python 和 VBA 实现 Microsoft Excel 工作流的自动化

由卢卡斯·布拉塞克在 Unsplash 拍摄的照片

在这个数据时代,python 已经成为全球许多开发人员最广泛采用的语言。这意味着有许多潜在的图书馆等着被利用。

虽然 python 最*成为了分析数据的首选,并在许多任务中拥有从低到高的广泛应用;Microsoft Excel 有着悠久的历史,并且在大多数情况下仍然是用于分析/管理/可视化数据的不可或缺的工具。

想象一下,如果我们可以利用 python 中的开源库来自动化我们在 Excel 中的工作流程;这会让我们的生活变得超级方便!站在巨大的开源 python 社区的肩膀上,我们可以在 Excel 中利用他们的功能,这个社区有数千个不同用例的包。

我有一个这样的需求,我想在下面讨论一下,在xlwings的帮助下,我能够通过 VBA 将我的 Excel 前端与 python 后端集成在一起,并且只需点击一个按钮,就可以自动完成一个相当繁琐的手动过程!

因此,这篇文章的主要目的是向您展示如何利用这个名为xlwings的强大 python 包通过 VBA 与 Excel 通信,并在此过程中自动完成一些任务。python 代码可能有 VBA 的变通办法,但对于我的特殊用例,我无法规避对 python 的需求,因为 VBA 无法完成一些像nsepy这样的 python 库能够完成的任务。

问题是

我保留了一张表,用来跟踪我的股票以及它们在一天结束时的表现。为此,我需要获取我投资组合中每只股票的收盘价,并每天努力手工输入。任何手动过程都容易出错,我以前就犯过这种错误,我在更新一些股票的收盘价时犯了一个错误,我的计算显示,我的风险比我想象的高得多;但当我反复核对时,情况并非如此,我意识到我在一只股票的十位数字中错误地输入了 9,而不是 6。那些在键盘上使用数字小键盘的人可能会遇到这个问题

现在,我知道了这个名为nsepy的库,它由 Swapnil Jariwala 维护,帮助从 NSE 服务器获取与 NSE 上列出的任何给定股权/期权相关的所有历史数据,但该库完全是用 python 编写的。如果我能在 Excel 中使用 python 的能力,那该多好啊!

这是完全可能的。还有另一个叫做xlwings的 python 库,它专门解决了 excel 与 python 的集成问题,瞧!问题解决了!!

为了给你一些关于这个问题的背景,这里有一张我用来保存我的股票账户的表格的一部分。

图片由 Vinayak 提供

我有脚本/股份/股票的 ISIN 编号及其名称,从同一工作簿的其他工作表中引用的*均买入价格,我过去在一天结束时手动输入的收盘价,以及基于每个脚本的我当前的头寸。

最后,您应该能够实际创建如下内容

图片由 Vinayak 提供

设置 xlwings

像任何其他包一样,您可以简单地用 python 包安装程序安装“xlwings ”,如下所示

pip install xlwings

我建议您为这个项目创建一个虚拟环境,以避免您已经存在的包安装中的依赖冲突。这篇文章不会涉及它,但是你可以看这里如果你想用 pip 安装一个虚拟环境或者看这里如果你像我一样喜欢 conda。

设置管道的一部分,即 python 管道。接下来,您应该安装 excel 与 xlwings 集成所需的部分。为此,保存并关闭您现在从安装 xlwings 的环境中打开的所有 excel 工作簿,只需运行以下命令

xlwings addin install

这应该会无缝地发生,但有时,如果你在 Windows 10 机器上使用 Excel 2016,你可能会遇到如下错误

xlwings 0.17.0[Errno 2] No such file or directory: 'C:\\Users\\nayak\\AppData\\Roaming\\Microsoft\\Excel\\XLSTART\\xlwings.xlam'

这可以通过使用简单的 mkdir 命令创建丢失的目录来解决

mkdir C:\Users\nayak\AppData\Roaming\Microsoft\Excel\XLSTART

成功安装 xlwings 后,下次打开 Excel 时,您会看到在顶部的工具栏中为“xlwings”创建了一个选项卡。该选项卡还包含解释器和 python 路径,如果您有不同的虚拟环境要执行 python 函数,则必须指定这些路径。

图片由 Vinayak 提供

因为我使用 anaconda 来维护我的环境,所以我提供了到我的 conda 的路径和我想要使用的相应环境。默认情况下,xlwings将检测一个环境(主要是您安装了这个库的环境),但是如果没有检测到,您就必须手动给出您的 anaconda 发行版的路径和环境名称。

要找到您的 anaconda 发行版在哪里,您可以从 Windows 开始菜单打开 anaconda 提示符,在该提示符下,首先通过运行命令激活您已经安装了xlwings的环境

conda activate your_env_name

并随后运行

where python

这可能会带来几个结果,但你需要把重点放在第一个;请看下面我的环境“分享”中的第一个结果。

图片由 Vinayak 提供

这里的路径是C:\\Users\nayak\Anaconda3\env\shares\python.exe,所以在康达路径框中是C:\\Users\nayak\Anaconda3,在康达环境框中是shares。这将有助于调用 python 函数的宏理解应该在哪个环境中执行 python 代码。如果您没有使用 anaconda 发行版来管理您的环境,那么您可以使用您的 python 环境做一个等效的练习,您可以给出解释器的路径和 python 的路径,而不是在 conda 中这样做。

接下来,您需要在 Excel 界面中启用xlwings的用户自定义函数(UDF)。打开 Excel 后,使用 Alt + L + H 导航到加载项框,然后您将看到一个带有多个复选框的屏幕,询问您需要为此工作簿启用哪些加载项。点击xlwings旁边的方框,点击确定。

在这之后需要做的最后一步是授权访问所有想要使用 xlwings 等第三方插件的宏。您可以通过导航到文件>选项>信任中心设置>宏设置来完成此操作,并在宏设置中选择启用所有宏,然后在开发者宏设置中选择信任对 VBA 项目对象模型的访问

图片由 Vinayak 提供

至此,我们已经准备好再次转向 python 方面了。

创建项目

通过从 cmd 提示符或终端或 shell 运行以下命令,您可以快速开始一个项目

xlwings quickstart project_name

这将创建一个以 project_name 作为文件夹名称的项目,默认情况下,您将在该文件夹中获得两个文件。

  • project_name.py:这是一个 python 文件,应该包含您的宏可以调用来修改工作表的代码。
  • project_name.xlsm:这是一个启用宏的 excel 文件,为空,包含两张表,分别是Sheet1xlwings.config文件。您可以在此工作簿中创建其他工作表,并向其中添加任意数量的宏。回调的逻辑必须写在上面创建的 python 文件中。

我所做的是我创建了一个项目,并将我的 excel 文件的内容复制到 xlsm 文件中,并根据我的喜好重命名该表。

python 后端逻辑

现在是你自动化你的项目所需要的代码的主要部分。在这种情况下,您可以从上面的“xlsm”工作簿的任何工作表中访问任何单元格。

您可以像在任何其他 python 脚本中一样编写任意数量的自定义函数,并在这里使用它们。我已经在 main 函数中编写了代码,我将用它来更新每日收盘价。为了便于理解,我已经给出了下面的代码。你可以在这里立刻看到完整的代码。

import xlwings as xw
wb = xw.Book.caller()

因为我是从工作簿内部调用的,所以我可以使用上面的语法并获取工作簿对象。如果您想获取对其他工作簿的引用,您可以

wb = xw.book(file_path)

这里的 file_path 指的是保存 excel 文件的路径。然后,要访问任何工作表,基本上可以使用 index 或工作表的名称,要访问工作表中某个范围的值,可以在它上面链接 range 命令。

# Refer by index
DATE = wb.sheets[0].range("B1").value# Refer by name
SHARE_NAME = wb.sheets["sheet_name"].range("C1").value

一旦你得到了一个股票的 ISIN 代码和它的名字,nsepy 就可以帮助你获取在 NSE 上列出的所有股票的 bhavcopy(价格列表),你可以在任何给定的有效 NSE 日期过滤该脚本的收盘价。nsepy 是一个由 Swapnil Jariwala 维护的库,你可以在这里参考它的文档。

from nsepy.history import get_price_list# Get the pricelist/bhavcopy for a given date and only keep the relevant info
price_list = get_price_list(DATE)
price_list = price_list[["SYMBOL", "CLOSE", "ISIN"]]# An inline function to query the close price given a script name
get_close = lambda x: price_list[price_list.ISIN == x]["CLOSE"]# Read the ISIN Code of our script name
ISIN = wb.sheets[sheet_name].range(f"A1").value
ISIN = ISIN.strip()# Use the inline function above to get the close price of our share
close_price = get_close(ISIN)# Copy the share price value to Excel
wb.sheets["sheet_name"].range(f"C1").value = float(close_price)

如果你有多个脚本,你可以循环这个代码来获得所有脚本的收盘价,并根据你的意愿将它们写到 Excel 表中。

这只是实际代码的一部分,可以在我的 github repo 上找到,这里是这个项目的。

Excel 前端逻辑

现在我们已经定义了 python 逻辑,我们需要将它连接到一个宏,并将其分配给主表单中的一个按钮。为此,您需要首先插入一个按钮。你可以去开发者>插入>按钮然后把一个按钮拖到表单中。

然后你可以点击 Alt + F11 调出 VBA 界面来定义一个宏。现在,您可以在放置按钮的同一个工作表中定义一个 sub,如下所示。

Sub CallUpdate()
    mymodule = "Python_File_Name_Here"
    RunPython ("import " & mymodule & ";" & mymodule & ".main()")
End Sub

宏中的这段代码将执行以下操作:

  • 查找名为“Python_File_Name_Here”的文件
  • 导入该文件中的模块
  • 调用指定 python 文件中的 main 函数。

这段代码可以用许多不同的方式进行调整;如果你除了 main 之外还有另一个函数,你想调用它,你可以写

RunPython (“import “ & mymodule & “;” & mymodule & “.other_function_name()”)

这仍然可以正常工作。您可以在 VBA 中进行一些检查,在 python 中进行一些检查,并在需要时让两个接口自由通信,这不是很好吗?

最后,对于您已经创建的按钮,您可以通过右键单击该按钮并转到分配宏选项来分配该宏,并最终选择该宏CallUpdate并单击确定。

现在,每当您单击按钮(我将其命名为 Update Close),计算将在 conda 环境shares中运行,输出将在 excel 表的 c 列中更新。这样,我就不必在网上手动查找每只股票的收盘价,然后将其填入 Excel 中,只需单击一个按钮即可完成!!

你也可以在 python 中定义函数,这些函数可以像 Excel 中的公式一样被调用,还可以做更多的事情,但我会在以后的文章中介绍这些内容…

结论

希望这篇文章能帮助你理解如何借助 python 为你的 excel 工作流插上翅膀:)

参考

  • Github 代码回购帖子中的所有代码
  • xlwings 官方文档

Python 映射、过滤和简化

原文:https://towardsdatascience.com/python-map-filter-and-reduce-9a888545e9fc?source=collection_archive---------12-----------------------

使用 Map、Filter、Reduce 和 lambda 函数编写优雅而高效的代码

雅各布·安德烈森在 Unsplash 上拍摄的照片

在 Python 中,可以使用def来定义函数。另一种编写小功能的方法是使用 lambda。Lambda 函数是内联匿名函数。它只包含一个没有显式 return 语句的表达式。让我们看一些例子。

from time import time_nsdef cubed(x):
        return x**3lambda_cubed = lambda x:x**3start = time_ns()print(f"Cube of 9 is {cubed(9)}")print(f"Time taken for previous method : {(time_ns()-start)//1_000} ms")start = time_ns()print(f"Cube of 9 is {lambda_cubed(9)}")print(f"Time taken for previous lambda : {(time_ns()-start)//1_000} ms")start = time_ns()
print(f"Cube of 9 is {(lambda x:x**3)(9)}")
print(f"Time taken for inline lambda : {(time_ns()-start)//1_000} ms")

在前面的片段中,我们将比较一个函数和一个 lambda 函数计算 9 的立方所需的时间。我们可以给一个 lambda 函数命名。我们可以在片段中看到它们。如果我们运行它,我们会得到这样的结果。

Cube of 9 is 729
Time taken for previous method : 18 ms
Cube of 9 is 729
Time taken for previous lambda : 3 ms
Cube of 9 is 729
Time taken for inline lambda : 2 ms

每次执行代码片段时,这些数字可能会稍有不同。但是很明显,lambda 函数比它们的替代方法要快得多。

我们现在将看到如何使用 lambda 函数来进一步优化我们的代码。要了解关于 Python lambda 函数的更多信息,可以访问这个链接。

地图

我们可以使用 Python map 函数从另一个列表生成一个新列表。我们将比较循环,并映射生成一个包含 1–10 个方块的列表。

start = time_ns()
squares=[]
for i in range(1,11):
        squares.append(i**2)
print(squares)print(f"Time taken for a for loop to generate squares: {(time_ns()-start)//1_000} ms")start = time_ns()squares=list(map(lambda i:i**2,list(range(1,11))))print(squares)
print(f"Time taken for map and lambda function to generate squares : {(time_ns()-start)//1_000} ms")

上面代码片段的输出是…

[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
Time taken for a for loop to generate squares: 12 ms
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
Time taken for map and lambda function to generate squares : 9 ms

过滤器

类似地,我们可以结合使用 filter 方法和 lambda 函数来从列表中过滤出条目。在下一个例子中,我们将使用循环和过滤方法创建一个从 1 到 10 的偶数列表。

start = time_ns()
evens=[]
for i in range(1,11):
        if i%2==0:
                evens.append(i)
print(evens)
print(f"Time taken for a for loop : {(time_ns()-start)//1_000} ms")start = time_ns()squares=filter(lambda i:i%2==0,list(range(1,11)))print(evens)
print(f"Time taken for filter : {(time_ns()-start)//1_000} ms")

如果我们比较每种方法的时间,我们会看到…

[2, 4, 6, 8, 10]
Time taken for a for loop : 6 ms
[2, 4, 6, 8, 10]
Time taken for filter : 4 ms

减少

Reduce 函数的工作方式类似于聚合函数。前面的方法mapfilter是 python 内置的,但是我们需要导入functools来使用 reduce 函数。

from functools import reduceprint(reduce(lambda x,y:x+y, list(range(1,11))))

输出将是55。reduce 的工作方式是首先获取列表的前两个元素,并存储在xy中。对 x 执行 lambda 函数后,y(本例中为 sum) reduce 函数将新值存储在 x 中。然后将下一个元素分配给 y,并将相同的函数应用于 x,y &结果存储在 x 中,依此类推。

没有必要将 lambda 函数与 map、filter 和 reduce 函数一起使用。这里有一个将一列数字作为输入的例子。

a=list(map(int, input().split()))
print(a)

如您所见,它们与预定义的 Python 函数配合得很好。

一旦您熟悉了这些功能,很容易发现这些功能会导致更短和优化的代码。虽然有时使用 for 循环可以使代码更具可读性。

但是这些功能的知识并没有被浪费。下次你准备在 Pandas 中预处理一个数据集时,了解 lambda 函数的概念将会使你受益。

点击 此处 了解更多关于这些功能的信息。

希望你喜欢。敬请关注更多内容。

Python、内存和对象

原文:https://towardsdatascience.com/python-memory-and-objects-e7bec4a2845?source=collection_archive---------4-----------------------

数据科学

面向数据科学家的 Python 内存管理基础

照片由查尔斯·德鲁维奥在 Unsplash 拍摄

作为数据科学家,通常情况下,我们不会关注 Python 和底层操作系统如何为我们的代码处理内存。毕竟,Python 是数据科学家中最受欢迎的语言,部分原因是它自动处理这些细节。只要我们在小数据集上工作,忽略 Python 如何管理内存(即内存分配和释放)不会影响我们的代码性能。但是,一旦我们转向大型数据集(大数据)或繁重的处理项目,关于内存管理的基础知识就变得至关重要。

举个例子,我正在做一个关于索引人类 DNA 的数据科学项目。我使用 python dictionary 对象来跟踪序列(即核苷酸序列),并将它们的位置存储在参考人类 DNA 中。这个过程进行到大约 10%时,dictionary 对象占用了我所有的 RAM,并开始在磁盘和 RAM 之间交换。这使得该过程非常慢(因为磁盘在数据传输中要慢得多)。作为一名数据科学家,如果我了解 Python 和内存管理的基础知识,我就可以防止这种情况,并编写更多内存高效的代码。

在本文和下一篇文章中,我将解释 Python 中内存管理的一些基本概念。在本文结束时,您已经掌握了 Python 如何处理内存分配和释放的基本知识。让我们开始吧…

基础

python 程序是以下内容的集合

  1. 方法
  2. 参考
  3. 目标

方法或操作都很容易。当您将两个数字相加时,基本上是将加法(或求和)方法应用于两个值。参考文献有点难以解释。引用是我们用来访问数据值(即对象)的名称。编程中最著名的引用是变量。定义x = 1时,x是变量或引用,1是其值(更准确地说是整数对象)。除了变量,属性和项是编程中另外两个流行的引用。

现在,让我们更深入地介绍对象。作为一名 Python 程序员,你一定听说过“Python 中的一切都是对象。”整数是一个对象。字符串是一个对象。列表、字典、元组、熊猫数据框、NumPy 数组都是对象。甚至函数也是一个对象。当我们创建一个对象时,它将被存储在内存中。当我们在前一段中定义引用时,我应该已经告诉过你,在 Python 中引用不是指向一个值,而是指向一个对象的内存地址。例如,在我们的简单示例x = 1中,引用 x 指向存储整数对象1的内存地址。

堆栈内存与堆内存

在运行时,计算机内存被分成不同的部分。三个重要的内存部分是:

  1. 密码
  2. 许多

存储器的代码(也称为文本或指令)部分以机器能够理解的形式存储代码指令。机器遵循代码部分的说明。根据指令,Python 解释器将函数和局部变量加载到堆栈存储器(也称为堆栈)中。堆栈内存是静态的和临时的。静态意味着存储在堆栈中的值的大小不能改变。临时意味着,一旦被调用的函数返回它的值,该函数和相关变量将从堆栈中删除。作为一名数据科学家和程序员,你没有访问堆栈内存的权限。Python 解释器和操作系统内存管理一起负责这部分内存。

正如您所了解的,变量(或一般的引用)只存储对象的内存地址。那么,物品在哪里?它们在堆栈内存中吗?不,它们在一个叫做“堆内存”(也叫堆)的不同内存中。为了存储对象,我们需要具有动态内存分配的内存(即,内存和对象的大小可以改变)。Python 解释器主动分配和释放堆上的内存(C/C++程序员应该手动做的事情!!!谢谢 Python!!!).Python 使用垃圾收集算法(称为垃圾收集器)来保持堆内存干净,并删除不再需要的对象。

您不需要弄乱堆,但是最好理解 Python 是如何管理堆的,因为您的大部分数据都存储在内存的这一部分。

让我们在堆上找到变量x指向的内存地址。为了找出答案,我们可以使用一个名为id()的函数。

>>> x = 1
>>> id(x)
140710407579424
>>> hex(id(x))
'0x7ff9b1dc2720'

当我们运行第一行(x = 1)时,Python 将整数对象1存储在我的电脑(与你的电脑不同)的内存地址140710407579424中。在计算机科学中,我们通常用十六进制数表示内存地址;因此,我使用了hex()函数(注:前缀0x用于计算机科学中,表示数字为十六进制)。在堆内存中存储 int 对象1后,Python 告诉引用(或变量)x记住这个地址(1407104075794240x7ff9b1dc2720)作为它的值。

内存优化

看一下这个例子。

>>> x = 1
>>> y = 1
>>> hex(id(x))
'0x7ffdf176a190'
>>> hex(id(y))
'0x7ffdf176a190'

在这里,我定义了两个变量(xy)。我给他们俩分配了一个整数对象(即1)。令人惊讶的是,这两个变量指向的内存地址是相同的。再看一个例子。

>>> str1 = "Python"
>>> str2 = "Python"
>>> hex(id(str1))
'0x1e3adfe2830'
>>> hex(id(str2))
'0x1e3adfe2830'

我定义了两个变量(str1str2),并给它们分配了一个字符串对象(Python)。两个变量指向的内存地址是相同的。如果您对布尔对象进行同样的测试,您将会看到类似的观察结果。为什么?

优化内存分配。Python 做了一个叫做“实习”的过程对于一些对象(将在后面讨论),Python 只在堆内存中存储一个对象,并要求不同的变量在使用这些对象时指向这个内存地址。Python 在其上实习的对象是整数[-5,256]、布尔和一些字符串。实习不适用于其他类型的对象,如大整数、大部分字符串、浮点数、列表、字典、元组。

更高级的数据结构

到目前为止,我们已经展示了简单数据结构的例子,如整数、字符串或布尔值。更复杂的数据结构呢,比如列表或字典。

>>> lst = [1, 2, 3, 257]
>>> hex(id(lst))
'0x236330edf88'
>>> hex(id(lst[0]))
'0x7ffdf176a190'
>>> hex(id(lst[3]))
'0x7ffdf176a1b0'

这个例子清楚地显示了 list 对象的内存地址不同于它的条目。这是有意义的,因为列表是对象的集合,它的每一项都有自己的标识,是一个单独的对象,有不同的内存地址。

如果列表中的每一项都是一个单独的对象,那么实习(上一节)适用于列表中的每一项吗?很容易检查。

>>> a = 1
>>> b = 257
>>> hex(id(a))
‘0x7ffdf176a190’
>>> hex(id(b))
‘0x236330dc450’

如你所见,alst[0]都由于整型强制而指向同一个内存地址。另外,你可以看到,当整数超过 256 时,blst[1]都指向不同的内存地址。

当我们向列表中添加一个新项目时会发生什么。内存地址有变化吗?我们来测试一下。

>>> lst = [1, 2, 3]
>>> hex(id(lst))
'0x23633104888'
>>> lst.append(4)
>>> lst
[1, 2, 3, 4]
>>> hex(id(lst))
'0x23633104888'

有趣的是,列表的内存地址保持不变。原因是列表是一个可变的对象,如果你给它添加了条目,这个对象仍然是多了一个条目的同一个对象。

关于可变对象的另一个重要事实是,如果你从一个对象实例化不同的变量,如果你对对象做任何改变,所有的变量都会改变。让我用一个简单的代码来展示一下。

>>> lst1 = [1, 2, 3]
>>> lst2 = lst1
>>> lst1.append(4)
>>> lst2
[1, 2, 3, 4]
>>> lst2.append(5)
>>> lst1
[1, 2, 3, 4, 5]

在这个例子中,变量lst1lst2都指向同一个可变对象(即[1, 2, 3])。如果这些变量中的任何一个改变了对象(例如,附加一个新项目),另一个变量(它指向同一对象)的值也将改变。获得可变对象的单独副本的唯一方法是使用.copy()方法。

>>> lst1 = [1, 2, 3]
>>> lst2 = lst1.copy()
>>> lst1.append(4)
>>> lst2
[1, 2, 3]
>>> hex(id(lst1))
'0x236330dfe08'
>>> hex(id(lst2))
'0x236330e0c88'

如你所见,使用.copy()方法,我们创建了两个不同内存地址的列表对象,改变其中一个不会改变另一个。

我们所说的关于列表对象的几乎所有内容也适用于字典对象。有些细微的差别超出了本文的范围。例如,在添加新项目后,他们的内存大小增长的方式会有所不同。

要检查两个或多个变量是否指向同一个对象,不需要检查它们的内存地址。您可以使用is检查两个变量是否指向同一个对象。这里有一个例子。

>>> lst1 = [1, 2, 3]
>>> lst2 = lst1
>>> lst3 = lst1.copy()
>>> lst2 is lst1
True
>>> lst3 is lst1
False

记住,is==不一样。is告诉你两个对象是否相同,而==告诉你它们的内容或值是否相同。例如,在前面的代码中,lst3lst1的内容是相同的(即[1, 2, 3],但它们是两个独立的不同对象。下面的代码清楚地展示了这一点。

>>> lst3 == lst1
True
>>> lst3 is lst1
False

摘要

本文向您介绍了 Python(更准确地说是 CPython 实现)如何为对象分配内存的基本知识。在 Python 中处理大数据时,您需要了解这些基本概念,以便编写更节省内存的代码。

在推特上关注我的最新报道:https://twitter.com/TamimiNas

Python 蒙特卡罗模拟:系综被解放

原文:https://towardsdatascience.com/python-monte-carlo-simulations-copulas-unchained-ff29d051c0a0?source=collection_archive---------6-----------------------

SciPy 中带高斯 Copulas 的相关随机变量

从上周开始,我们已经学习了四个教程,一步一步地解释了蒙特卡罗方法。今天,我们将前往蒙特卡洛的下一站。

最新的文章介绍了相关随机变量。我们使用了 MCerp 库,它应用了 Iman-Conover 方法来生成相关性(在 Python 中使用相关随机变量的蒙特卡罗模拟|走向数据科学)。

文章强调,在任何模拟模型中,反映相关结构对于准确的结果至关重要。输入变量可以显示出一前一后移动的趋势。如果不考虑*行运动,模拟模型会对不切实际的结果赋予同等的权重。我们经历了一个例子,其中当忽略相关结构时,模拟输出的标准偏差是两倍。

昨天的文章介绍了在 MCerp 中实现的 Iman-Conover 方法。今天,我们将学习另一种方法,它不需要安装额外的库,只需要 SciPy 和大约十几行代码——高斯连接函数。

几乎所有你能找到的关于高斯连接函数的资料都是用一种“编程”语言编写的,而我们很少有人能完全精通这种语言:数学。对于把数学作为第二或第三语言而非母语的人来说,维基百科的解释不容易理解。

Copula(概率论)—维基百科;右上角的情节:维基百科上的马泰奥·赞迪, CC BY-SA 3.0

几年前,我遇到了它,想知道高斯系词可能有什么用处——在脑海中把“系词”这个词用一个大大的虚拟问号夹起来。我又把它挖了出来,并着手把“数学”翻译成 Python。除了随机波动之外,Python 脚本的结果与 Iman-Conover 方法的结果相匹配。正如我们将在下面看到的,强加的相关结构等于目标。

让我们来演示一下高斯连接函数在我们的工具箱中会起到什么样的作用。

今天的示例模型将与您在昨天的文章中找到的模型相同(Python 中相关随机变量的蒙特卡罗模拟|走向数据科学)。但是我们将应用 SciPy 和 Gaussian copula 方法,而不是 MCerp 库和它的 Iman-Conover 实现。copula 方法比 Iman-Conover 方法需要更少的代码行。

1.属国

我们导入 Seaborn 的图表库,因为它的 jointplot 为我们提供了在宏伟的布局中可视化相关结构的方法。

从 SciPy 中,我们导入了一些分发对象。

  • 多元正态分布对于创建高斯连接函数是不可避免的。
  • 我们将使用 SciPy 的 rv_continuous 和 beta 分布来定义 PERT 分布。PERT 和正态分布将作为所谓的边际分布。我们将在三个输入变量中将它们联系在一起,copula 将在它们之间建立一个相关结构。

2.分 4 步准备高斯连接函数

copula 在 Python 中实现并不困难,这与带有大量数学符号的源代码中出现的情况相反。生成相关随机变量需要四个步骤。

开始时——第 0 步**——我们应该有一个目标矩阵,其中包含我们希望应用于每对输入变量的相关系数。理想情况下,目标相关性可以从历史观察中确定。如果不是,那么计划者应该考虑系数的三点估计。**

步骤#1 中,我们实例化了 SciPy 的多元正态分布。

  • 我们用一个零向量来设置它,这将作为它的每个列向量的*均值。
  • 第二个参数是目标相关矩阵。

在实例化多正态分布之后,我们为它的三个向量绘制 N = 10,000 个随机变量,这些变量将展示目标相关结构。

步骤#2 中,我们将这些随机变量输入到单变量标准正态分布的累积分布函数中。通过这样做,我们获得了列向量——三个输入变量中的每一个都有一个——它保留了多正态分数的相关结构,但被转换为统一分数:它们将位于区间[0;1].

步骤#3 中,我们定义了三个边际分布,此时它们之间仍然没有相关结构。

  • PERT 分布将模拟销售量的不确定性;
  • 反映销售价格的正态分布;
  • 材料单位成本的第二个正态分布。

在步骤#4 中,我们将在这三个输入分布上强加相关结构。

然而,在我们进行最后一步#4 之前,我们想要检查如果我们在没有应用 copula 的情况下绘制随机变量*,它们将具有哪种相关结构。以下两个笔记本单元格不属于创建高斯连接函数的 4 步序列。*

初始相关矩阵显示接*零的系数。我们从 PERT 和两个正态分布中抽取的 10,000 个随机变量的原始向量是独立的,带有一些导致小系数的随机噪声。

当我们想要绘制每个变量对的相关性图表时,我们可以从 seaborn 调用 jointplots 绘制几乎圆形的斑点——上面是销售价格和材料单位成本的例子,它们应该表现出很强的正相关性。如果他们真的展示出来,我们会看到一个从左下角延伸到右上角的椭圆形状:高价格将与高材料单位成本相关联。这里,低价格与低和高的材料单位成本相关联,这就形成了一个没有方向的形状。

在确认原始输入变量缺乏相关性后,我们进入最后一步,**步骤#4,**并实施目标结构:

  • 在步骤#2 中,我们已经生成了从多元正态分布继承了目标相关结构的正态分数
  • 步骤#4 中,我们将这 3x 万个正态分数(数组 rand_U )插入到三个输入分布的百分点函数 ppf() 中。每个 ppf()将返回 N = 10,000 个随机变量。具有随机变量的三个数组将展示目标相关结构,它们通过分数(rand_U)从多元正态分布中继承了目标相关结构。这种继承是“高斯连接函数”术语背后的原因。

这就完成了 4 步过程。

让我们想象一下两两相关。我们将为三个输入参数中的每一对调用一个助手函数, plotcorr() :

Seaborn 的关节图不再呈现圆形或无定形。相反,它们显示出反映它们正相关或负相关的省略号。

  • 销量与售价:负相关——省略号从左上角延伸到右下角。高价格往往伴随着低交易量。

  • 数量与材料单位成本:弱正相关——较高的数量将导致供应商给予企业一些数量折扣。

  • 销售价格与材料单位成本:强正相关-企业将试图通过更高的销售价格来抵消供应商价格的上涨。省略号很紧凑,反映了 0.7 的强相关性,并且从左下到右上是目标:高材料单位成本伴随着高销售价格。

当我们计算三个耦合输入变量的相关矩阵时,我们发现它与目标相关矩阵的偏差很小≤ 0.026,本质上是随机波动。

现在,输入数据的准备工作已经完成。相关随机变量的数组——数量、价格和材料单位成本——可以传递给模拟模型。

3.准备和运行模拟模型

接下来的步骤与我们在之前的文章中讨论的 MC 模拟过程相同。

我们通过定义尽可能多的想要研究的输出变量来建立模型。每个输出变量只是一个或多个输入变量的和、积或商。任何我们串在一起包含一个或多个输入变量的函数都可以作为输出变量。

我们将毛利 GP 定义为我们要跟踪的主要输出变量,此外还有一些次要输出变量,如收入 r。输出变量是数组(列向量),其元素是通过将相应的输入元素组合在一个函数中来计算的。

此时,因为输入向量 v、p 和 m 已经在前面的代码行中填充了它们的相关量,所以输出向量已经可用。当代码到达第 13 行时,模拟已经完成。我们可以开始绘制输出。

一个辅助函数将绘制列向量的直方图。

我们对六个输出和输入数组调用了六次绘图函数,从总利润开始。

下面的输出类似于我们在过去几天的蒙特卡罗文章中讨论的内容。

校准相关结构集中了仿真模型将在其 10,000 次迭代中生成的输出。初始模型还必须评估参数元组,这些参数元组难以置信地将极高的价格与极低的材料单位成本(反之亦然)以及巨大的销售量结合在一起

在非常高和非常低的销售价格下,许多这些不切实际的场景通过强加一个相关结构而被归入离群值的状态。在 10,000 次试验中,异常情况出现的次数较少,因此结果更紧密地围绕€ 48,589 的*均结果。

4.结论

当我们应用 MCerp 的 Iman-Conover 方法时,我们得到了与昨天文章中几乎相同的结果。

高斯连接函数作为一种继承机制。多元正态分布将目标相关结构传递给正态分数数组。我们使用分数作为输入,为不同类型的单变量分布生成几个随机变量向量,即所谓的边际,,它将作为模拟模型*的输入参数。*这些单变量随机变量将保持遗传相关性。

通过将输入值的不可信组合推到背景中,相关结构有助于将模拟输出的标准偏差减半。

  • Jupyter 笔记本可以在 GitHub 上下载: h3ik0th/MC_copulas:用高斯 copulas 和 SciPy(github.com)进行 Python 蒙特卡罗模拟
  • 图片: Copula(概率论)—维基百科截图|右上角情节:维基百科上的马泰奥·赞迪, CC BY-SA 3.0
  • 所有其他图片:作者
  • https://medium.com/subscribe/@h3ik0.th

Python 命名元组:什么、如何以及何时使用

原文:https://towardsdatascience.com/python-named-tuple-what-how-and-when-to-use-4718b0668afd?source=collection_archive---------3-----------------------

图片来自 Pixabay 的 eloiroudaire77

Python 命名 Tuple 的配方以及我们为什么要使用它

Python 中有一种“特殊”类型的元组,叫做“命名元组”。对于 Python 学习者来说,对它感到困惑是很常见的,尤其是我们应该何时以及为什么使用它。

命名元组就是元组,所以它做元组能做的一切。然而,它超出了普通的 Python 元组。它更像是 C++等其他编程语言中的“struct”。它是一个元组的特定子类,是根据您的规范以编程方式创建的,具有命名字段和固定长度。

在本文中,我将介绍名为 tuples 的 Python,并尝试演示如何使用它、何时使用它以及为什么我们应该使用它。

1.创建命名元组

图片来自 Pixabay 的 Dirk Hoenes

在我们开始之前,这里有一个小提示。从 Python 3.1 开始,有一个重要的名为 tuple 的内置常量来表示我们现在使用的 Python 版本。我们可以如下得到。

import sys
sys.version_info

如果我们想在我们的程序中使用它,我们需要从集合模块中导入它。

from collections import namedtuple

集合模块中的namedtuple是一个类工厂。换句话说,它制造类。我们需要提供下面的东西来产生我们想要的类。

  • 我们要使用的类名
  • 我们要分配的字段名序列,按照元组中元素的顺序。
  • 我们需要在代码中将该类赋给一个变量名,这样我们就可以用它来构造实例。

例如,如果我们想要定义一个具有纬度和经度两个属性的Coords类,我们可以如下实现它。

Coords = namedtuple('Coords', ['latitude', 'longitude'])

然后,我们可以使用Coords类实例化一个对象,这将是一个命名元组。

home = Coords(latitude=-37.8871270826, longitude=144.7558373041)

我们可以验证它是一个元组,尽管它有字段名。

isinstance(home, tuple)

此外,为了从类中实例化一个命名元组,我们不必每次都指定字段名,因为命名元组的顺序和长度是固定的。

home = Coords(-37.8871270826, 144.7558373041)

1.1 定义命名元组类的简单方法

我们可以很容易地使用字符串来指定字段,而不是使用列表。例如,我们可以使用 common 来分隔字段名称。

Coords = namedtuple('Coords', 'latitude, longitude')

我们甚至可以使用空格作为分隔符,这完全没问题。

Triangle = namedtuple('Triangle', 'first_side second_side third_side')
t1 = Triangle(5, 5, 5)

1.2 使用类型提示从超类创建

从 Python 3.6 开始,我们还可以更正式地定义一个命名元组类。这也将支持命名字段的类型提示。

from typing import NamedTuple  # 3.6+class Coords(NamedTuple):
    """A coordinate consists latitude and longitude"""
    latitude: float
    longitude: floathome = Coords(-37.8871270826, 144.7558373041)

不同之处在于,我们需要使用类型模块中的NamedTuple超类。

2.字段名称自动重命名

图片来自 Pixabay

当我们定义一个命名的元组类时,虽然我们可能使用字符串作为字段名,但它们将被反映为类属性。因此,这些字段名称会有一些限制。

首先,我们不能使用以下划线开头的字段名。

MyNamedTuple = namedtuple('MyNamedTuple', ['_attr1', 'attr2'])

一些保留的关键词也被禁止,比如def

MyNamedTuple = namedtuple('MyNamedTuple', ['def', 'attr2'])

此外,字段名称不能重复,因为一个类不能有两个同名的属性。

MyNamedTuple = namedtuple('MyNamedTuple', ['attr1', 'attr1'])

然而,从 Python 3.1 开始,我们可以将标志rename设置为 True,这样任何无效的字段名都会被自动重命名,而不会抛出错误。

MyNamedTuple = namedtuple(
    'MyNamedTuple', 
    ['_attr1', 'def', 'attr2', 'attr2'], 
    rename=True
)

上面代码中的命名元组定义违反了这三条规则,但是因为我们将 rename 标志设置为 true,所以它不会出错。

有一个小技巧来检查一个命名元组类的字段名,这个类使用它的私有属性_fields

MyNamedTuple._fields

3.使用命名元组

图片由 Gerd Altmann 来自 Pixabay

为了演示如何使用一个命名元组,让我们重复我们在上面几节中使用的例子。我们可以定义Coords类并实例化一个命名元组home

Coords = namedtuple('Coords', 'latitude, longitude')
home = Coords(-37.8871270826, 144.7558373041)

3.1 访问值

然后,我们可以访问命名元组中的值。非常直观地,我们可以使用字段名来访问它们对应的值。这是使用命名元组的主要好处之一。它还提高了代码的可读性。

print(home.latitude)
print(home.longitude)

另外,不要忘记命名元组就是元组。因此,我们也可以使用索引来获取值。

print(home[0])
print(home[1])

3.2 将命名元组转换成字典

就其表现形式而言,命名元组非常类似于字典。字段名可以看作是字典的键,它们都有相应的值。

事实上,一个命名的元组有一个内置的私有方法来将其自身转换为有序字典。

home._asdict()

这是有道理的。命名元组和字典之间最重要的区别可能是命名元组中字段的顺序很重要。这意味着命名元组和有序字典可以互换。

然而,如果我们不关心键的顺序,而只想要一本字典,那该怎么办呢?我们可以简单地向有序字典添加一个类型转换,如下所示。

dict(home._asdict())

3.3 替换字段的值

由于命名元组是元组,并且元组是不可变的,所以不可能改变字段的值。

home.latitude = 10

在这种情况下,我们必须使用另一个私有方法_replace()来替换字段的值。

home1 = home._replace(latitude=10)

_replace()方法将返回一个新的命名元组。这很重要,因为即使使用这种方法,我们仍然不能修改命名元组。

如果我们检查原始的命名元组,它没有改变。

3.4 具有默认值的命名元组

就像在一个普通的类中我们可以为属性设置默认值一样,一个命名的元组类也可以设置默认值。然而,由于具有默认值的字段必须在任何没有默认值的字段之后,默认值应用于最右边的参数。

例如,让我们再次定义只有一个默认值的Coords类。

Coords = namedtuple('Coords', 'latitude, longitude', defaults=[100])
home = Coords(-37.8871270826)

如果我们实例化只有一个值的命名元组,默认值100将用于经度,这是我们定义中最右边的一个。

如果我们显式地将字段设置为经度,那么默认值将用于纬度吗?

home = Coords(longitude=-37.8871270826)

答案当然是否定的。在一个命名元组中,字段的顺序是非常严格的。为了避免混淆和潜在的问题,默认值必须在最右边,即使我们明确地指定了一些东西。

如果我们给所有字段赋予默认值,这意味着所提供的默认值的数量与字段的数量相同,那么当我们实例化一个命名元组时,我们将不必传入任何值。

Coords = namedtuple('Coords', 'latitude, longitude', defaults=[-50, 100])
home = Coords()

检查命名元组类的默认值的一个技巧是使用它的私有属性_field_defaults

Coords._field_defaults

3.5 将元组转换为命名元组

如果我们已经准备好了一个普通元组和一个命名元组类,我们可以使用私有方法_make()轻松地将元组转换为命名元组。请注意,元组的长度必须与命名元组的长度相同。

t1 = (-37.8871270826, 144.7558373041)
home = Coords._make(t1)

4.为什么以及何时使用命名元组?

图片由 Gerd Altmann 从 Pixabay 拍摄

我们已经介绍了 Python 中关于命名元组的几乎所有内容。Python 为什么会有它,我们应该什么时候使用它?

答案如下:

  • 与普通元组相比,使用命名元组可以改进我们的代码,以表达元组元素的语义。
  • 与 Python 类相比,使用命名元组可以提高代码可读性,并显著减少代码行数。

第一点很容易理解。对于第二个,让我们考虑下面的例子。假设我们需要定义一个Student类。它不一定有任何方法。换句话说,它只是保存一个学生对象的数据。

class Student:
    def __init__(self, student_no, first_name, last_name, birth_year, gender):
        self.student_no = student_no
        self.first_name = first_name
        self.last_name = last_name
        self.birth_year = birth_year
        self.gender = gender

如果我们使用一个命名元组,定义就像下面这样简单。

Student = namedtuple('Student', 'student_no, first_name, last_name, birth_year, gender')

因此,对于这种情况,使用命名元组比使用类要简单和整洁得多。然而,如果我们需要任何类方法,命名元组将不再适用。

使用命名元组的另一个好处是它的可迭代性。也就是说,命名元组是可迭代的,因此它可以用于许多场景,如循环和生成器。

让我们使用Student类来实例化一个命名元组

s1 = Student(123, 'Chris', 'Tao', 1900, 'Male')

然后,我们可以循环它的值。

for attr in s1:
    print(attr)

摘要

图片来自 Pixabay

在本文中,我介绍了 Python 中的命名元组,它是什么,如何创建命名元组类,以及如何将该类实例化为命名元组。然后,我举例说明了什么时候我们应该使用命名元组,什么时候不应该。希望你喜欢阅读!

https://medium.com/@qiuyujx/membership

如果你觉得我的文章有帮助,请考虑加入 Medium 会员来支持我和成千上万的其他作者!(点击上面的链接)

Python 现在支持 Switch 语句——下面是入门方法

原文:https://towardsdatascience.com/python-now-supports-switch-statements-heres-how-to-get-started-fe7da830236a?source=collection_archive---------3-----------------------

了解如何在 Python 3.10 中使用结构化模式匹配实现 Switch 语句

照片由罗伯特·威德曼在 Unsplash 上拍摄

Python 3.10 仍处于 alpha 阶段,但会带来一些令人兴奋的新特性。我们今天将看看其中的一个——switch 语句——正式名称为结构模式匹配

Switch 语句在大多数编程语言中都很常见,它提供了一种更简洁的实现条件逻辑的方式。当有很多条件需要评估时,它们就派上用场了。

今天,我们将看看如何使用它们,并比较与更传统的方法的代码差异。

这篇文章的结构如下:

  • 实现条件运算的旧方法
  • python 3.10 Way-Switch 语句
  • 最后的想法

实现条件运算的旧方法

我们会掩护两个。第一个是标准的 if-elif-else 语句,另一个将使用字典键值映射来完全避免 if 语句。

首先,我们需要一些代码。我们将声明一个名为get_mood(day: str) -> str的函数,它返回一个值取决于输入参数的字符串。随着周末的临*,返回值变得更加令人兴奋。愚蠢的小功能,但将做演示的目的。

让我们用经典的if方法来实现它。代码如下:

没有什么新的或开创性的。代码很容易理解,但是太冗长,尤其是当多个值可以满足一个条件时。

我们可以通过完全避免 if 语句并以键值映射的形式编写函数来“改进”或混合一些东西。这还包括一个用于设置默认返回值的try except块。

下面是代码片段:

如您所见,结果是相同的,但是我们没有使用条件运算符。这两种方法都可以很好地工作,但是 Python 一直缺少的是一个专用的switch语句。

3.10 版本解决了这个问题。

python 3.10 Way-Switch 语句

根据官方文件:

增加了结构模式匹配,形式为带有关联动作的模式的匹配语句case 语句。模式由序列、映射、原始数据类型以及类实例组成。模式匹配使程序能够从复杂的数据类型中提取信息,对数据结构进行分支,并基于不同形式的数据应用特定的操作。

我们今天将坚持基础知识,并在其他时间探索结构模式匹配必须提供的一切。

让我们回到我们的get_mood()函数,用类似于switch语句的语法重写它。与许多其他编程语言不同,Python 使用了match关键字,而不是switchcase关键字是相同的。

下面是代码片段:

概括一下:

  • 使用case关键字评估条件(case ‘Monday’if day == ‘Monday’相同)
  • 使用管道运算符|分隔多个条件,例如,如果两个输入值应该产生相同的返回值
  • 使用下划线运算符— _ —指定默认大小写。

这就是你的结构模式匹配——至少是最基本的。这只是 Python 3.10 即将推出的令人兴奋的新特性之一。

最后的想法

Python 3.10 将带来许多令人兴奋的特性,但它仍处于 alpha 阶段。因为它还不能用于生产,所以将其作为默认 Python 版本安装可能不是一个好主意。

今天我们已经介绍了可能是最激动人心的新特性,但是其他的也将在接下来的文章中介绍。请继续关注博客,了解更多信息。

喜欢这篇文章吗?成为 中等会员 继续无限制学习。如果你使用下面的链接,我会收到你的一部分会员费,不需要你额外付费。

https://medium.com/@radecicdario/membership

了解更多信息

  • 我作为数据科学家卖掉我的 M1 Macbook Pro 的三大理由
  • 如何用 Cron 调度 Python 脚本——你需要的唯一指南
  • Dask 延迟—如何轻松并行化您的 Python 代码
  • 如何用 Python 创建 PDF 报告——基本指南
  • 2021 年即使没有大学文凭也能成为数据科学家

保持联系

  • 在 Medium 上关注我,了解更多类似的故事
  • 注册我的简讯
  • 在 LinkedIn 上连接

Python 还是 R?数据科学家和数据分析师哪个好?

原文:https://towardsdatascience.com/python-or-r-which-one-is-better-for-data-scientists-or-data-analysts-3be038f6c74c?source=collection_archive---------18-----------------------

照片由西格蒙德在 Unsplash 上拍摄

简单明了的推理

这是一个很常见的问题。尤其是对新手来说。从哪里开始?即使对于中级数据科学家来说,这也是一个问题。因为不同的人有不同的选择或者不同的做事风格。有的公司更倾向于 Python,有的公司更倾向于 r,我有朋友先学了开始学 Python 然后有的招聘人员或者有的用人单位说要学 r,现在开始学 r,其实哪个更好呢?

我从 Python 开始。当我在波士顿大学开始攻读硕士学位时,我必须学习 R。因为一些数据分析课程只使用 R。开始的时候很不舒服。现在我很高兴我能学习 R。因为我现在既懂 Python 又懂 R,我想我应该在这里分享我的观点。

我自己的经历

因为我非常了解 Python,所以我可以很快学会 R。这并不太难。特别是,如果您了解 Python 中的数据操作库,您可能会发现许多命令是相似的(而不是相同的)。所以学习并不难。但这仍然需要时间。这需要大量的练习。因为有如此多的库可用于数据操作和分析,所以对于初学者来说,有时很难跟上。但是很快就变得容易了。

但问题是,值得花时间吗?

对我来说,是的!

Python 相当强大。你可以用 Python 管理 R 中的大部分人员,但是了解 R 会给你很大的灵活性。许多库在 R 中的结构比 Python 更好。如果你两个都擅长,你就有选择了。比如我喜欢用 R 做推断统计,而不是 Python。感觉 R 里的库和包比 Python 里的包好。这只是我的看法。我几乎总是用 R 进行统计分析。有些人可能比 Matplotlib 和 Seaborn 更喜欢 ggplot2。同样,如果我需要使用机器学习库,我更喜欢 Python 的 scikit-learn 库,而不是不同的 R 包。

在这一点上,我感觉,对于中级水*的学习者来说,同时学习 Python 和 r 都是不错的,如果你是自由职业者或者求职者,会打开很多门路。

初学者从哪里开始

在我看来,从 Python 入手是好的。如果你是一名有抱负的数据科学家,并且正在学习你的第一语言,那应该是 python。仅仅是因为 python 更受欢迎。此外,我还找到了更多关于 Python 的资源。如果你看一些流行的程序员网站,比如面向极客的极客、教程点或编程,你会发现他们有几种不同语言的解决方案。Python 就是其中之一。但是你在那里找不到休息。所以,学习会轻松很多。同样,如果你遇到困难,你会在 Python 中更快地找到帮助。

如果你是一名数据分析师,Python 或 R 中的任何一个都可以帮助你完成任务。但如果你是一名数据科学家,也想随着时间深入机器学习和人工智能领域,那么你绝对应该选择 Python。因为你可能需要和软件工程师合作。你不会找到很多愿意在 r 工作的软件工程师,而且我到目前为止看到的所有好的在线课程或硕士课程都使用 Python 教授机器学习。

最后一句话

如你所见,我在学习 Python 上强调了很多。但是,如果可能的话,两者都要学。如果想做数据分析师,Python 或者 R 都可以。但是如果你打算做数据科学家,还是推荐 Python。两个都学就更好了!我的建议是,先学好一门。

更多阅读

用于 NLP 的 Python 包——第 1 部分

原文:https://towardsdatascience.com/python-packages-for-nlp-part-1-2d49126749ef?source=collection_archive---------41-----------------------

用于 NLP 操作的 Polyglot- Python 包

米卡·博斯韦尔在 Unsplash 上的照片

自然语言处理旨在操纵人类/自然语言,使其为机器所理解。它涉及文本分析、文本挖掘、情感分析、极性分析等。有不同的 python 包可以使 NLP 操作变得简单而不费力。

所有的 NLP 软件包都有不同的功能和操作,这使得最终用户更容易执行文本分析和各种 NLP 操作。在这一系列文章中,我们将探索 python 的不同 NLP 包及其所有功能。

在本文中,我们将讨论 Polyglot,这是一个开源 python 包,用于操作文本并从中提取有用的信息。它有几个功能使得它比其他基于 NLP 的库更好更容易使用。这里我们将讨论它的不同功能以及如何实现它们。

让我们开始吧。

安装多语言和其他依赖项

为了开始,我们首先需要安装 polyglot 及其所有依赖项。对于本文,我们将使用 Google Colab,下面给出的代码将安装 polyglot 及其依赖项。

!pip3 install polyglot
!pip3 install pyicu
!pip3 install pycld2
!pip3 install morfessor

安装完这些库之后,我们还需要安装 polyglot 的一些功能,这些功能将在本文中用到。

!polyglot download embeddings2.en
!polyglot download pos2.en
!polyglot download ner2.en
!polyglot download morph2.en
!polyglot download sentiment2.en
!polyglot download transliteration2.hi

导入所需的库

下一步是导入所需的 polyglot 库和功能,我们将在本文中探讨。

import polyglot
from polyglot.detect import Detector
from polyglot.text import Text, Word
from polyglot.mapping import Embedding
from polyglot.transliteration import Transliterator

执行 NLP 操作

让我们从探索 polyglot 提供的一些 NLP 功能开始,但是在此之前,让我们输入一些我们将要处理的样本数据。

sample_text = '''Piyush is an Aspiring Data Scientist and is working hard to get there. He stood Kaggle grandmaster 4 year consistently. His goal is to work for Google.'''
  1. 语言检测

Polyglot 的语言检测器可以很容易地识别文字的语言。

#Language detection
detector = Detector(sample_text)
print(detector.language)

语言(来源:作者)

2.**句子和单词**

为了从文本/语料库中提取句子或单词,我们可以使用多语言功能。

#Tokenize
text = Text(sample_text)
text.words

单词(来源:作者)

text.sentences

句子(来源:作者)

3.**位置标记**

词性标注是一项重要的自然语言处理操作,可以帮助我们理解文本及其标注。

#POS tagging
text.pos_tags

词性标注(来源:作者)

4.**命名实体识别**

NER 用于识别语料库/文本数据集中的人、组织和位置(如果有的话)。

#Named entity extraction
text.entities

NER(来源作者)

5.形态分析

#Morphological Analysis
words = ["programming", "parallel", "inevitable", "handsome"]for w in words:
     w = Word(w, language="en")
     print(w, w.morphemes)

形态学(来源:作者)

6.情感分析

我们可以分析一句话的情绪。

#Sentiment analysistext = Text("Himanshu is a good programmer.")
for w in text.words:
   print(w, w.polarity)

极性(来源:作者)

7.翻译

我们可以把文本翻译成不同的语言。

#Transliteration
transliterator = Transliterator(source_lang="en", target_lang="hi")
new_text = ""
for i in "Piyush Ingale".split():
  new_text = new_text + " " + transliterator.transliterate(i)
new_text

翻译(来源:作者)

这就是你可以轻松地探索文本数据集的不同属性的方法。

继续尝试不同的文本数据集,如果你发现任何困难,你可以在回复部分发布。

这篇文章是与皮尤什·英加尔合作的

在你走之前

感谢 的阅读!如果你想与我取得联系,请随时在 hmix13@gmail.com 上联系我或我的 LinkedIn 个人资料 。可以查看我的Github简介针对不同的数据科学项目和包教程。还有,随意探索 我的简介 ,阅读我写过的与数据科学相关的不同文章。

骑着 Python 穿越 Peloton

原文:https://towardsdatascience.com/python-pandas-and-the-peloton-aa024ca74fa5?source=collection_archive---------14-----------------------

实践教程

使用基础数据分析对环法自行车赛进行分析

📸—罗伯·温盖特— Unspalsh

第一阶段:介绍

每年夏天,世界顶尖的自行车选手聚集在法国,进行一场激烈的全国比赛。再过几天,2021 年环法自行车赛将在小港口城市布列斯特开始,并像往常一样,在几周后的巴黎结束。鉴于“大数据”和“分析”的趋势正在触及体育世界的每一个部分,包括自行车,我想使用 Python 中可用的流行库组合来更深入地了解这个年度赛事。这篇文章旨在构建对历史环法自行车赛数据的现有分析,同时展示 Python 在数据分析和可视化基础方面的强大功能。

第二阶段:读入并探索数据

我们分析使用的主要数据集直接来自环法自行车赛官方网站,其中包含了从 1903 年到 2020 年的每场比赛的骑手信息(感谢杰里米·辛格-万的数据是多元的时事通讯来呈现数据集!).由于托马斯·坎米纳迪令人印象深刻的网络搜集——这是一个值得完全独立发布的有用技能组合——这些数据已经被格式化为一个方便的 CSV 文件,详细列出了每个骑手的等级、时间、团队等等。

图:数据来源网站(letour.fr/en)

我们将从 Python 中导入我们分析所需的特定库,然后使用 read_csv 方法从 Pandas 库中读入数据。

图:我们的数据帧的前五行

的。info 方法不仅详细说明了数据集的大小(9,452 行和 18 列),还详细说明了每列的数据类型以及每列缺失的数据量。粗略地看一下,我们发现“奖金”和“积分”变量在我们的数据准备阶段很容易被删除或估算,因为每个变量都有大量的缺失值或空值。

图:我们的列的名称、数据类型和缺失数据计数

最后。describe 方法显示了我们的数字列的快速统计摘要。虽然我们正在处理的数据集不够全面,不足以证明这些步骤,但通过热图或相关矩阵图以及直方图来可视化您的数据,以了解数据分布,可以作为探索您的数据的初始模式或关系的另一种方式。

图:我们的整数类型列的一些统计度量

第三阶段:清理和准备数据

尽管耗时且*庸,数据清理和准备对于任何适当的数据深度挖掘都是必不可少的一步,因为它创建了一个适当的基础数据集来进行未来的分析。我们将删除数据集的第一列。drop 方法,因为它不提供任何值。此外,“奖金”和“积分”列似乎没有太大的帮助,因为缺少大量的值,所以让我们也放弃这些。虽然我们不会在这里触及,但各种数据插补和替换方法可用于解决数据缺失部分。将距离从千米转换成英里可以通过在阵列上运行简单的计算来实现;我们还想用。为了清楚起见,重命名方法。

图:我们的“清理”数据集的前十行

除了列名之外,在查看“team”变量时,我们遇到了一个很好的数据清理机会。特殊团队“Touriste-Routiers”(翻译为“公路游客”)与“Touristes Routiers”分开计算。

图:初始数据集中的前 10 个团队

虽然这实际上可能是两个独立的团队——关于是否存在连字符的争论很常见——让我们继续假设这只是一个错误,我们可以使用。将方法替换为地址。

图:10 大团队,使用。替换方法

阶段 4:分析数据

在这一点上,我们可以开始问——并希望回答——一些关于环法自行车赛的简单的时间序列问题。除了季节性和变化率之外,总体趋势也是时间序列分析的共同兴趣点。例如,如果我们想了解比赛的规模是如何增长的,我们可以使用。按 and 分组。数数每年乘坐的人数。

图:每年乘客总数

我们创建的图表看起来非常简单;我们通常认为随着赛事和运动的流行,比赛的规模会随着时间的推移而增加。然而,1966 年骑手数量的大幅下降有点不寻常。那年的比赛看起来没什么特别的(嗯,除非你考虑到引入兴奋剂检测和参赛者的中途抗议)。巡回赛的统计学家放暑假了吗?数据记录是否被一名当年未获得参赛资格的复仇但精通网络的骑手抹去了?撇开阴谋论不谈,这种奇怪的现象确实值得再研究一下。如果我们好奇哪些车手参加了最多的环法自行车赛,环法自行车赛,环法自行车赛?让我们来看看比赛…

图:数据集中出现频率最高的骑手

看起来法国自行车手西尔万·查瓦内尔以 16 件参赛作品领跑!让我们稍微解开这一行代码…

  • rider_df['rider]从我们的数据帧中选择“Rider”列,生成一个序列
  • 。values_counts()是对这个新系列中的值进行计数的方法;这实际上创建了一个新的序列,其中索引是附加项,值是它们出现的频率(从最频繁开始递减)
  • [:10]给出了这个系列中前 10 个最常出现的骑手;最终创建最终系列,如上所示

如果我们想快速查看每年的所有获奖者,该怎么办?我们只需创建一个列表,并在“Rank ”= 1 时进行过滤。通过过滤选择数据子集是回答此类问题或生成新问题的一个好方法。

图:显示获胜者的数据子集

换挡(不得不偷偷把至少一个糟糕的骑车双关语放到这篇文章中),让我们看看光谱的另一端,按速度排序数据,看看历史上最慢的骑手。这可以通过。sort_values 方法应用于适当的列,在我们的例子中是“PersonalAvgPace”。

图:基于“个人空间”变量的最慢骑手

不出所料,最慢的车手名单经常出现在一些最早记录的比赛中。

阶段 5:将数据与另一个数据集合并

我们的原始数据集是全面的,但当涉及到每个骑手的有趣信息时,就相当有限了。幸运的是,我们在 Github 上看到了一个单独的数据集,列出了每年环法自行车赛的冠军,包括成绩信息以及骑手的身高、体重、年龄、国籍等等。 Python 让我们能够转换现有的骑手数据框架,并将两个数据集合并在一起。

我们的原始数据集已被修改为只显示获胜者,并被重命名为 df2 现在,我们将阅读并检查我们遇到的其他赢家数据集。

图:我们新数据集的初步观察

我们将在这个新数据集上执行一组类似的数据清理和准备活动,就像我们对原始数据集所做的那样。Year 显然是连接两组数据的主键,但是我们需要确保它们的格式相同。让我们用一个快速切片来修剪“Year”列(当我们使用 quick .str.upper 方法时,用同样的方式来格式化 riders)。确保数据类型相同很重要,因此我们将确保“Year”变量是一个带有。a 类型方法。

图:我们的两个数据框,准备合并

类似于 SQL 的 JOIN 子句。Pandas 中的 merge 方法允许在一列或多列上连接两个数据帧。默认的合并类型是内部联接,但是可以使用 how 参数覆盖它。我们知道获胜者/骑手的名字是多余的,因为它包含在两个数据集中;在 Python 中,列上的 _x 和 _y 后缀有助于指示列名的来源(x 是左边的数据集,而 _y 是右边的)。我们将删除其中一个,并在最终的合并数据集中有一个单独的“获胜者”列。

图:我们新合并的数据框架

我们成功合并的数据集为环法自行车赛获胜者开辟了许多新的探索途径。自行车运动中的“团队”概念一直是我感兴趣的,也是我不太了解的东西。一个与团队相关的问题可能是“从一个国家到另一个国家,哪个团队最成功?”为了回答这个问题,我们可以安排我们的数据来看看团队获胜,按国籍分组。

图:我们输出的一个子集,按国家列出了获胜团队的名称

阶段 6:用 Matplotlib、Seaborn 和 Plotly 可视化数据

Python 提供了各种绘图库来帮助你创建清晰、深刻的数据可视化。在这个阶段,我们将展示一些最流行的库的功能,包括 Matplotlib、Plotly 和 Seaborn。让我们用一个简单的线形图来显示获胜者每年的*均速度。我们将添加一些参数,如绘图标题、轴标签和图例。我们还将添加一个注释来记录一段缺失的数据。

图:显示每年获胜者*均速度的线形图

毫不奇怪,随着时间的推移,比赛获胜者的速度越来越快,*年来的增长略有放缓,这可能是由于*年来对兴奋剂的打击以及骑手和自行车不断达到的身体限制的综合作用。如果我们想将这些信息与另一个不同尺度的变量进行比较,比如距离,会怎么样呢?我们只需用另一个轴创建一个图来容纳我们的新变量。我们还将确保合并标签,这样两行都可以考虑。

图:比较比赛距离和冠军配速

条形图是 Python 中可视化数据的常用方式。让我们使用此图表类型按获胜次数来比较获胜者。

我们已经可以看出这个图表不是最直观或最容易阅读的。无意冒犯许多只赢了一场比赛的车手,但让我们看看是否能把它精简到赢了多次的车手。通过对数据和标签的一些调整,我们应该能够创建一个更容易解释的图表。(点击这里查看一篇更详细的帖子,展示了构建更好图表的各个层次)

图:显示多个赢家的改进条形图

也许我们想显示一段时间内环法自行车赛“*均”获胜者的数据。创建一些变量并将它们传递到 print 语句中是一个好的开始。

让我们试着用一种更具视觉吸引力的形式来表现“普通”骑手。直方图是查看数据频率分布的有用方式。的。hist()函数有许多选项来改变数据的计算和相应图表的显示。如果我们假设我们的数据是正态分布的,一些直方图将有助于描绘一幅“*均赢家”的图画。Matplotlib 的 subplot 特性允许我们并排比较这些图表。

图:环法自行车赛获胜者*均值直方图的子图

简单地说,散点图是展示两个变量之间关系的好方法。也许我们想要一个关于获胜者身高和体重之间相关性的视觉表现。

图:身高和体重的散点图

这是一个好的开始,但让我们做一些挖掘来注释上图中的一个异常值,特别是体重最大的骑手。对我们的数据进行一些快速过滤,很快就确定了这个异常值是 1909 年环法自行车赛冠军弗朗索瓦·费伯(Francois Faber)的名字,他被恰当地称为“哥伦布巨人”。让我们对 Francois 在领奖台上穿着 XL 黄色球衣给予应有的认可,并用注释更新我们的散点图。

据其网站,“Seaborn 是一个基于 matplotlib 的 Python 数据可视化库。它提供了一个高级界面,用于绘制有吸引力且信息丰富的统计图形。”虽然 matplotlib 中的一些绘图类型非常令人满意,但 Seaborn 为其他绘图类型提供了轻微的视觉增强,希望能够提高理解能力。我们将使用 boxplots 来展示 Seaborn 的功能,这是一种非常好的异常检测方法。下图显示了 Seaborn 如何对按国籍分组的*均获奖者年龄的箱线图进行细微的视觉改进。

图:使用 matplotlib 的箱线图

图:使用 seaborn 的箱线图

虽然我们的样本量相对较小,但深入挖掘箱线图中显示的意大利获胜者的异常值会很有意思。最后但同样重要的是, Plotly 是一个图形库,允许用 Python 创建交互式图表。Plotly 在多种格式的可读性方面大放异彩,它为各种类型的情节提供了许多有趣的定制功能,但我们将使用下面的代码创建一个带有交互式悬停状态的简单的 winners age 折线图。

图:显示每年获奖者年龄的图表

阶段 7:未来分析

如前所述,这个项目在范围上相当有限,只是触及了可以在这样的数据集上运行的分析的表面。未来分析中涉及的几个有趣的问题或概念包括:

  • 预测:时间序列分析的首要目标往往是预测;观察未来 15 年的冠军配速或比赛距离等因素可能会很有趣。
  • 回归模型:建立多元线性回归模型,根据身高、体重、年龄、国籍等因素预测骑行者的步伐。
  • 异常检测:查看哪些骑手或获胜者在人群中脱颖而出,并找出原因将是扩展我们上面展示的一些异常检测工作的一个好方法。
  • 对比被禁赛车手的表现 : 兴奋剂指控和争议从环法自行车赛开始就一直困扰着它;被指控服用兴奋剂的被取消资格的骑手没有被包括在数据集中,但比较被禁赛的骑手被剥夺冠军头衔的速度和我们数据集中的获胜者的速度等指标会很有趣
  • 雷达图:绘制多个雷达(或蜘蛛)图比较不同国籍的车手在各种不同因素上的表现。

当你加入生物特征数据、天气、海拔和自行车类型等因素时,在未来几年的环法自行车赛中,将会发现大量有趣且有望产生影响的见解。

第八阶段:进一步阅读和总结

如上所述,这不是第一次深入研究环法自行车赛的数据,希望不会是最后一次!如果你对这些感兴趣的话,我在研究过程中发现了更多的资源:

  • 数字环法自行车赛简介
  • 环法自行车赛数据分析,使用 Jupyter 笔记本中的 Strava 数据以及 Python、Pandas 和 Plotly
  • 可视化环法自行车赛
  • #tidytuesday:环法自行车赛冠军

希望您喜欢这个高层次的概述,并发现它对展示 Python 及其相关特性的简单方面如何揭示数据集的独特见解有所帮助。点击查看原始代码。我错过/忘记了什么,或者你会采取什么不同的做法?任何和所有的反馈都欢迎通过下面的评论,或者在 wmc342@gmail.com 给我留言。非常感谢您的阅读,并享受比赛!🚴

Python 熊猫面试数据科学的问题

原文:https://towardsdatascience.com/python-pandas-interview-questions-for-data-science-377dfac4a7a1?source=collection_archive---------23-----------------------

利用熊猫进行 Python 数据科学面试试题

在 Unsplash 上由Christina @ wocintechchat.com拍摄的照片

在上一篇文章 Python Pandas 数据科学面试问题第 1 部分 中,我们研究了如何将数据导入 Pandas 并执行基本计算,例如

  • 排序数据帧
  • 处理重复
  • 聚集
  • 合并数据帧
  • 计算字段

在这一部分中,我们将基于这些知识,用它们来解决更复杂的 Python 熊猫面试问题。我们将关注以下领域

  • 日期时间操作
  • 文本操作
  • 应用函数
  • 高级聚合
  • 偏移数据
  • 使用熊猫的统计

如果你不熟悉熊猫图书馆,请浏览那篇文章。让我们开始吧。

应用函数

在本系列的前一部分中,我们研究了如何创建额外的字段。我们也可以使用 Python 库中的函数或用户定义的函数来操作值。为此,我们使用 apply()方法。apply()方法使用矢量化,因此与遍历序列中的每一行相比,可以更快地计算值。这里有一个来自 AirBnB 数据科学采访的简单问题。


生活设施最多的城市

从 Airbnb 上给定的房产搜索数据集中,找出主人所有房产中设施最多的城市。假设每行代表一个唯一的主机。输出城市名称作为您的解决方案。

截图来自 StrataScratch

你可以在这里解决这个 Python 熊猫面试问题。https://platform . stratascratch . com/coding/9633-拥有最多便利设施的城市?python=1

该问题使用包含这些字段的 airbnb_search_details 数据集

截图来自 StrataScratch

以下是数据的显示方式。

截图来自 StrataScratch

方法和解决方案

让我们把这个问题分解一下。给定酒店的所有便利设施都在便利设施栏中给出。便利设施以逗号分隔的字符串形式列出。我们通过简单地使用逗号分隔符分割字符串并取结果列表的长度来找到便利设施的数量。

# Import your libraries
import pandas as pd

# get the number of number of amenities
airbnb_search_details['num_amenities'] = airbnb_search_details
['amenities'].apply(lambda x : len(x.split(",")))
airbnb_search_details[['city', 'id', 'amenities', 'num_amenities']]

数据集现在看起来像这样。

截图来自 StrataScratch

我们现在可以简单地合计每个城市的设施数量,并输出设施数量最多的城市。我们可以使用方法链接将所有这些方法组合在一行中。

# Summarize by city and output the city with the most amenities
airbnb_search_details.groupby(by = ['city'], as_index = False).agg
({'num_amenities' : 'sum'}).sort_values(by = ['num_amenities'], 
ascending = False).reset_index()['city'][0]

我们不局限于内置函数。我们可以创建自己的函数,也可以动态创建 lambda 函数。让我们在下面的 Python 熊猫采访问题中尝试一下。这个出现在旧金山数据科学采访中。

划分业务类型

将每家企业归类为餐馆、咖啡馆、学校或其他。被归类为餐馆的企业名称中应包含“餐馆”一词。对于咖啡馆,企业名称中应包含“咖啡馆”、“咖啡馆”或“咖啡”。学校会有‘学校’这个词。根据上述规则,如果企业不是餐馆、咖啡馆或学校,则应归类为“其他”

截图来自 StrataScratch

你可以在这里解决这个 python 面试问题。https://platform . stratascratch . com/coding/9726-classify-business-type?python=1

此问题使用具有以下字段的SF _ restaurant _ health _ violations数据集。

截图来自 StrataScratch

数据是这样的。

截图来自 StrataScratch

方法和解决方案

这个 Python Pandas 面试问题的唯一相关字段是 business_name 列。我们编写一个 lambda 函数,并使用 apply 方法来检查企业满足了哪些业务规则。一旦我们有了类别,我们就输出问题中所需的相关字段。

# Import your libraries
import pandas as pd

# Classify as per the rules
sf_restaurant_health_violations['category'] = sf_restaurant_health_violations
['business_name'].apply(lambda x: \
'school' if x.lower().find('school') >= 0 \
else 'restaurant' if x.lower().find('restaurant') >= 0 \
else 'cafe' if (x.lower().find('cafe') >= 0 or x.lower().find('café') >= 0 \
or  x.lower().find('coffee') >= 0) \
else 'other'
    )
# Output relevant fields    
sf_restaurant_health_violations[['business_name', 'category']].drop_duplicates()

正如我们所见,apply 方法是使用用户定义的函数或 Python 库中的函数操作值和创建计算字段的一种非常强大的方法。

高级聚合

在上一篇文章中,我们已经看到了使用 groupby 方法的聚合。Pandas 也支持其他聚合,我们还可以创建一个电子表格风格的数据透视表。让我们看看如何在熊猫身上做到这一点。我们从旧金山市的一个数据科学问题开始。

制作一张数据透视表,找出每位员工每年的最高工资

查找从 2011 年到 2014 年每年每位员工的最高薪酬。以表格的形式输出结果,列中是年份,行中是按字母顺序排序的员工姓名。

截图来自 StrataScratch

你可以在这里解决这个 Python 熊猫面试问题。https://platform . stratascratch . com/coding/10145-make-a-pivot-table-to-find-the-high-payment in-year-per-employee?python=1

该问题使用具有以下字段的 sf_public_salaries 数据集。

截图来自 StrataScratch

数据是这样显示的。

截图来自 StrataScratch

方法和解决方案

虽然这个问题对于 SQL 来说可能有点困难,但是对于 Pandas 来说,这个问题可以通过使用 pivot_table()函数在一行代码中解决。我们只需传递正确的参数,并获得想要的输出。

# Import your libraries
import pandas as pd

# create the pivot table 
pd.pivot_table(data = sf_public_salaries, columns = ['year'], 
index = 'employeename', values = 'totalpay', aggfunc = 
'max', fill_value = 0).reset_index()

pivot_table()方法非常强大,可以帮助快速解决复杂的聚合问题。这是另一个例子。这是来自脸书数据科学的采访。

两个事件之间的时间

报告用户从页面加载到第一次向下滚动的最短时间。输出应该包括用户 id、页面加载时间、第一次向下滚动时间以及两个事件之间的时间间隔(秒)。

截图来自 StrataScratch

你可以在这里解决问题https://platform . stratascratch . com/coding/9784-time-between-two-events?python=1

这个问题使用了包含以下各列的 facebook_web_log 数据集。

数据是这样显示的。

截图来自 StrataScratch

方法和解决方案

同样,这个面试问题在 SQL 中可能有点复杂。然而,在熊猫中,使用数据透视表是相对简单的。我们首先创建一个数据透视表,其中包含每个 user_id 的每个动作的最早实例。因为我们只需要 page_load 和 scroll_down 事件,所以我们只在输出中保留那些列。

import pandas as pd

# Find the first instance of diffrent actions
summ_df = pd.pivot_table(data = facebook_web_log, index = 'user_id', columns = 
'action', aggfunc = 'min', values = 'timestamp').reset_index()[['user_id', 
'page_load', 'scroll_down']]

输出如下所示。

截图来自 StrataScratch

现在问题变得很简单。我们可以通过获取 scroll_down 和 page_load 时间戳之间的差值来直接计算持续时间。然后,我们输出持续时间最短的用户的 user_id 和其他必填字段。

# Caclulate duration
summ_df['duration'] = summ_df['scroll_down'] - summ_df['page_load']
# Output the user details for the user with the lowest duration
summ_df.sort_values(by = ['duration'])[:1]

如您所见,pivot_table()函数允许我们进行多个聚合,而不必像在 SQL 中那样将它们分离和合并。

日期时间操作

日期时间操作是最常被问到的数据科学面试问题之一。datetime 数据集的普遍性和一系列可以通过简单的数据实现的复杂性使其成为一个流行的数据科学测试领域。Pandas 有许多日期时间函数,涵盖了广泛的日期时间用例。一旦以日期时间格式将数据加载到 Pandas 中,就可以通过调用。dt 访问器。这为我们提供了对各种日期时间方法的访问,这些方法可以通过整个熊猫系列来访问。

让我们在一个真实世界的 Python 熊猫面试问题中使用它。这是一个来自 DoorDash 数据科学的采访。

每个工作日和每个小时的*均收入

报告一周中每天每小时的*均收入数。使用 customer_placed_order_datetime 字段计算相关的日期时间值。收益可视为“订单总额”字段的总和。把星期一当作一周的第一天。

截图来自 StrataScratch

你可以在这里解决问题:https://platform . stratascratch . com/coding/2034-avg-earnings-per-weekday-and-hour?python=1

该问题使用了具有以下字段的 doordash_delivery 数据集。

截图来自 StrataScratch

数据集看起来像这样。

截图来自 StrataScratch

方法和解决方案

为了解决这个 Python 熊猫采访问题,我们需要从相关的 datetime 字段中提取星期几和小时。如问题中所述,该字段为“客户 _ 已下单 _ 订单 _ 日期时间”。

为了获得星期几,我们使用. dt.weekday 属性。根据文档,这将为周一返回 0,为周日返回 6。因为周一我们需要从 1 开始,所以我们在结果上加 1。

# Import your libraries
import pandas as pd

# Keep relevant fields
dd_df = doordash_delivery[['customer_placed_order_datetime', 'order_total']]
# Get the day of the week (add 1 to keep have Monday = 1)
dd_df['weekday'] = dd_df['customer_placed_order_datetime'].dt.weekday + 1

同样,我们也可以提取一天中的小时。为此,我们使用 datetime 对象的. dt.hour 属性。

# Hour of the day
dd_df['hour'] = dd_df['customer_placed_order_datetime'].dt.hour

我们现在可以简单地按一周中的某一天和一天中的某个小时进行聚合,并输出相关的列。

dd_df.groupby(by = ['weekday', 'hour'], as_index = False).agg
({'order_total': 'mean'})

让我们尝试一个稍微难一点的 Python 熊猫面试问题。这一个来自 Salesforce Data Science 访谈,使用日期时间操作和数据透视表。

用户增长率

计算每个账户在 2021 年 1 月和 2020 年 12 月的活跃用户增长率。

截图来自 StrataScratch

你可以在这里解决问题。https://platform . stratascratch . com/coding/2052-user-growth-rate?python=1

该问题使用了包含以下各列的 sf_events 数据集。

数据是这样的。

截图来自 StrataScratch

方法和解决方案

我们需要汇总每个帐户在两个不同时间段的用户数量。这是数据透视表的一个完美案例。但是在我们这样做之前,我们首先从日期中提取年和月,因为我们必须对几个月进行汇总。为此,我们使用。strftime()方法。这扩展了 Python 日期时间库中可用的 strftime()方法。这非常类似于我们在 SQL Datetime 文章中讨论过的 SQL 中的 TO_CHAR()函数。​

# Import your libraries
import pandas as pd

# Create the Year - Month indicator
sf_events['month'] = sf_events['date'].dt.strftime('%Y-%m')

我们现在可以使用数据透视表对 2020 年 12 月和 2021 年 1 月进行汇总,计算增长率并输出相关列。

# Aggregate relevant months
summ_df = sf_events[sf_events['month'].isin(['2020-12', '2021-01'])].
pivot_table(
    index = 'account_id', columns = 'month', values = 'user_id', aggfunc = 
    'nunique').reset_index()
# Calculate growth rate and output relevant columns
summ_df['growth_rate'] = summ_df['2021-01'] / summ_df['2020-12']
summ_df[['account_id', 'growth_rate']]

文本操作

与 datetime 函数一样,Pandas 提供了一系列字符串函数。就像。对于日期时间函数,我们可以使用。str 访问器在整个系列中使用标准字符串函数。除了标准的字符串库之外,还有一些额外的函数可以派上用场。让我们来看几个 Python 熊猫面试问题的例子。第一个是来自洛杉矶一个城市的数据科学采访。

“面包店”自有设施

截图来自 StrataScratch

你可以在这里解决问题https://platform . stratascratch . com/coding/9697-bakery-owned-facilities?python=1

该问题使用具有以下字段的洛杉矶餐厅健康检查数据集

截图来自 StrataScratch

数据如下所示。

截图来自 StrataScratch

方法和解决方案

虽然数据集中有许多列,但相关的是 owner_name 和 pe_description。我们首先只保留数据集中的相关列,删除重复的列(如果有的话)。

然后,我们在 owner_name 字段中搜索文本 BAKERY,在 pe_description 字段中搜索低风险。为此,我们使用 str.lower()方法将所有值转换为小写,并使用. str.find()方法查找相关文本的实例。 .str.find() 是 Python 内置方法 find()对字符串类型变量的扩展。

然后,我们使用布尔掩码输出满足这两个标准的行。

# Import your libraries
import pandas as pd

# Keep relevant fields
rel_df = los_angeles_restaurant_health_inspections
[['owner_name', 'pe_description']].drop_duplicates()
# Find the relevant text in the two fields. 
rel_df[(rel_df['owner_name'].str.lower().str.find('bakery') >= 0) &( rel_df
['pe_description'].str.lower().str.find('low risk') >=0)]

除了通常的字符串方法之外。str 访问器还有一些额外的方法。一种这样方法是 explode()。

顾名思义,该方法拆分数据帧中的系列或特定列。其他值(如果是数据帧)和索引是重复的。让我们看看这在实践中是如何使用的。我们在本文前面解决的 AirBnB 数据科学面试问题中使用了这一点。

生活设施最多的城市

从 Airbnb 上给定的房产搜索数据集中,找出主人所有房产中设施最多的城市。假设每行代表一个唯一的主机。输出城市名称作为您的解决方案。

截图来自 StrataScratch

airbnb_search_details 数据集中的相关字段是便利设施和城市

截图来自 StrataScratch

方法和解决方案

我们从保留数据集中的相关字段开始(这在实际解决方案中是不需要的。我们这样做是为了使解决方案更容易理解)。

# Import your libraries
import pandas as pd

# Keep Relevant fields
rel_df = airbnb_search_details[['amenities', 'city']]

我们通过调用 split 方法将便利设施字符串拆分成一个列表。

# Split the amenities string
rel_df['amenities'] = rel_df['amenities'].str.split(",")

现在我们调用便利设施列上的 explode()方法。

rel_df = rel_df.explode('amenities')

截图来自 StrataScratch

可以看到,explode 方法为一个 iterable 内部的每个对象创建了一个单独的行,比如 list、set、tuple 等。现在,我们可以聚合城市中的便利设施,并像前面一样输出结果。

# Summaroze by city
rel_df.groupby(by = ['city'], as_index = False).agg({'amenities' : 'count'}).
sort_values(by = ['amenities'], ascending = False).reset_index()['city'][0]

explode 是一个非常强大的函数,对于基于文本操作的问题来说非常方便。

使用熊猫的统计

考虑到处理表格数据的能力,Pandas 也是统计操作的自然选择。虽然 NumPy 被认为是统计操作的首选库,但由于 Pandas 是基于 NumPy 构建的,它继承了相当多的统计度量,可以很容易地调用这些度量来计算这些度量。让我们看几个例子。第一个是来自洛杉矶一个城市的数据科学面试问题。

求 A 级分数的方差和标准差

截图来自 StrataScratch

你可以在这里解决这个 Python 熊猫面试问题。https://platform . stratascratch . com/coding/9708-find-the-variance-and-the-standard-deviation-of-scores-that-have a grade?python=1

这个问题使用了我们之前看到的洛杉矶餐厅健康检查数据集。该数据集具有以下字段。

截图来自 StrataScratch

数据是这样显示的。

截图来自 StrataScratch

方法和解决方案

让我们自己通过计算来解决这个问题。然后我们将通过调用内置的 Pandas 方法来解决这个问题。我们首先对相关字段进行子集划分,只保留那些与 a 级相对应的分数。

# Import your libraries
import pandas as pd

# Subset relevant rows
la_df = los_angeles_restaurant_health_inspections[los_angeles_restaurant_health_
inspections['grade'] == 'A'][['grade', 'score']]
la_df

截图来自 StrataScratch

方差是*均值的均方差。人口方差被定义为

标准差是方差的*方根

我们可以通过计算等级*均值的*方差的*均值来计算方差。我们可以通过使用向量化操作一步完成。根据方差,我们可以很容易地计算出标准差。最后以期望的格式输出结果。

variance = ((la_df['score'] - la_df['score'].mean())**2).mean()
stdev = variance ** 0.5
output_df = pd.DataFrame({'variance' : [variance], 'stdev' : [stdev]})

除了从公式中计算方差,我们还可以调用内置的 Pandas 统计方法。由于我们正在计算人口方差和标准差,我们需要指定熊猫不使用贝塞尔校正。我们可以通过在方差和标准差计算中将 ddof 参数设置为 0 来做到这一点。

variance = la_df['score'].var(ddof = 0)
stdev = la_df['score'].std(ddof = 0)
output_df = pd.DataFrame({'variance' : [variance], 'stdev' : [stdev]})

让我们试试稍微复杂一点的。这是来自谷歌数据科学的采访。

邮件与活动时间的关联

找出一个用户收到的邮件数量和每天总运动量之间的相关性。每天的总运动量就是每天的用户会话数。

截图来自 StrataScratch

你可以在这里解决问题https://platform . stratascratch . com/coding/10069-correlation-between-e-mail-and-activity-time?python=1

这个问题使用了两个数据集

截图来自 StrataScratch

数据显示如下:

google_gmail_emails

截图来自 StrataScratch

google_fit_location

截图来自 StrataScratch

方法和解决方案

我们首先计算每天发送给每个用户的电子邮件数量。我们通过按用户 id 和日期合计电子邮件数量来做到这一点

# Import your libraries
import pandas as pd

# Get the number of emails per day
mail_df = google_gmail_emails.groupby(by = ['to_user', 'day'], 
as_index = False).agg({'id' : 'count'}).fillna(0)
mail_df

我们得到下面的数据集。

截图来自 StrataScratch

我们对其他数据集做同样的事情,计算每天的用户会话数。注意:我们只需要对每个用户会话计数一次。

exer_df = google_fit_location.groupby(by = ['user_id', 'day'], as_index = 
False).agg({'session_id' : 'nunique'}).rename(columns = 
{'user_id': 'to_user'}).fillna(0)
exer_df

给了我们

截图来自 StrataScratch

我们现在合并用户和日期的两个数据集

merged_df = pd.merge(mail_df, exer_df, on = ['to_user', 'day'], how = 'inner')
merged_df

截图来自 StrataScratch

我们现在可以使用内置的协方差函数来计算协方差。协方差输出将提供两个变量的协方差。例如,两个变量 x 和 y 的协方差输出将包含与此类似的内容。

我们可能需要对角线上的值(用绿色突出显示)。因此,我们对相关字段进行子集划分。

merged_df[['id', 'session_id']].corr()[:1]['session_id']

偏移数据

另一个常见的商业案例,尤其是时间序列数据,是找出它们的前一个或后一个值。Pandas 有能力支持这些 SQL 风格的滞后和超前操作。让我们在实践中使用这些方法,解决优步数据科学采访中的一个问题。

逐年流失

计算每年的司机流失率,并报告与前一年相比,人数是增加了还是减少了。

截图来自 StrataScratch

你可以在这里解决所有的问题。https://platform . stratascratch . com/coding/10017-同比-流失?python=1

该问题使用了具有以下字段的 lyft_drivers 数据集。

数据集看起来像这样。

截图来自 StrataScratch

方法和解决方案

我们从计算每年的流失率开始。为此,我们首先从 end_date 字段计算客户流失的年份,然后计算每年的客户流失数量。

# Import your libraries
import pandas as pd

# Get the year from exit date
lyft_drivers['year'] = lyft_drivers['end_date'].dt.year

# Get the number of the drivers churned for each year
summ_df = lyft_drivers.groupby(by = ['year'], as_index = False).agg
({'index' : 'count'}).sort_values(by = ['year']).rename
(columns = {'index' : 'churn'}).dropna()

这为我们提供了以下汇总数据。

截图来自 StrataScratch

检查与前一年相比,该数字是增加了还是减少了。为此,我们需要偏移或下移客户流失数量的值。我们可以通过使用 shift()方法来实现这一点。顾名思义,shift 方法将数据偏移 n 行。也可以传递负数,以便向上移动数值。

# Fetch the prev year's churn numbers
summ_df['prev_churn'] = summ_df['churn'].shift(1).fillna(0)
summ_df

截图来自 StrataScratch

我们可以将该值与前一项进行比较,并确定数字是增加了还是减少了。

# Compare the two churn numbers and output the change
summ_df['change'] = (summ_df['churn'] > summ_df['prev_churn']).apply
(lambda x : 'increase' if x == True else 'decrease')
summ_df

额外的 Python 熊猫面试问题

作者在 Canva 上创建的图像

我们最后解决了几个问题,这些问题结合了我们所学的所有知识。第一种使用窗口函数。这是来自亚马逊数据科学的采访。

收入随时间变化

求每个月的三个月移动*均线。

截图来自 StrataScratch

你可以在这里解决这个 Python 熊猫面试问题https://platform . stratascratch . com/coding/10314-收入-时间?python=1

该问题使用了具有以下字段的 amazon_purchases 数据集。

数据是这样的。

截图来自 StrataScratch

方法和解决方案

我们从相关事务的数据子集开始。我们删除问题中描述的退款交易。我们通过调用 created_date 字段上的 strftime 方法找到所需的月份指示器。我们进一步汇总每个月的购买交易。

# Import your libraries
import pandas as pd

# Remove refund transactions
pos_df = amazon_purchases[amazon_purchases['purchase_amt'] > 0]
# Create Month indicator
pos_df['month'] = pos_df['created_at'].dt.strftime("%Y-%m")
# Aggregate the purchases by month
summ_df = pos_df.groupby(by = ['month'], as_index = False).sum()
[['month', 'purchase_amt']].sort_values(by = ['month'])

截图来自 StrataScratch

为了找到移动*均线,我们使用滚动函数。滚动函数创建 n 行的移动窗口。我们可以改变参数来得到我们想要的输出。因为我们不希望前两个观察返回空值,所以我们将 min_periods 参数设置为 1。最后,我们调用 mean()方法来计算三个月的*均值,并返回相关字段。

# Calculate the rolling average, ensure that the value is calculated 
  even for the first two months
summ_df['roll_avg'] = summ_df['purchase_amt'].rolling
(3, min_periods = 1).mean()
# Output relevant fields
summ_df[['month', 'roll_avg']]

下一个来自亚马逊数据科学访谈,以创新的方式使用了 apply 方法。

连胜最久的选手

连胜是特定玩家赢得的一系列连续比赛。当一名球员输掉下一场比赛时,连胜就结束了。输出具有最长连胜的玩家的 ID 和连胜的长度。

截图来自 StrataScratch

你可以在这里解决这个 Python 熊猫面试问题。https://platform . stratascratch . com/coding/2059-连胜最长的球员?python=1

这个熊猫面试问题使用带有以下字段的玩家 _ 结果数据集。

数据是这样显示的。

截图来自 StrataScratch

方法和解决方案

为了解决这个问题,我们需要得到一个玩家的结果。假设一个玩家的结果序列是

WWLWLWWWLWWWWWWWLLLWLW

我们可以简单地通过使用字母“L”作为分隔符来拆分字符串。这将把字符串分成几个列表。类似这样的。

[WW] [W] [WWW] [WWWWWWW] [] [] [] [W] [W].

最后,我们找到最长列表的长度,我们将能够确定玩家的连胜。为了在熊猫身上做到这一点,我们需要连接结果。为此,我们只需应用 sum()方法。

当传递数字数据时,sum()方法应该给出值的总数。但是当字符串类型数据被传递时,就会执行加法运算。Python 中的加法运算符将连接字符串,这正是我们需要的!!。

我们从连接结果开始。

# Import your libraries
import pandas as pd

# Create the sequence of results
streak_df = players_results.groupby(by = ['player_id'], as_index = False).agg
({'match_result': 'sum'})

截图来自 StrataScratch

然后,我们继续使用字母 L 作为分隔符来拆分 match_result 字符串。我们还分解了结果,这样每个列表都是单独的一行。

# Split the sequence using 'L' as the separator and explode
streak_df['streak'] = streak_df['match_result'].str.split('L')
streak_df = streak_df.explode(column = 'streak')

截图来自 StrataScratch

现在剩下的问题就简单了。我们只需要合计条纹字符串的最大长度,并输出相关字段。

# Find the length of the streak
streak_df['streak_len'] = streak_df['streak'].apply(len)
# Aggregate
streaks_df = streak_df.groupby(by = ['player_id'], as_index = False).agg
({'streak_len' : 'max'})
# Output relevant fields
streaks_df['rank'] = streaks_df['streak_len'].rank(method = 
'dense', ascending = False)
streaks_df[streaks_df['rank'] == 1].drop(columns = ['rank'])

结论

在这一系列文章中,我们看了如何使用熊猫和如何解决 Python 熊猫面试问题。如果一个人真的想从事以 Python 为主要工具的数据科学领域的工作,那么他应该精通熊猫。使用 Pandas 就像使用 MS-Excel、Google Sheets、Numbers 或 LibreOffice Calc 等电子表格软件一样简单。一个人要精通熊猫只需要一点时间和好奇心。我们在 StrataScratch *台上有超过 700 个与数据科学面试相关的编码和非编码问题。这些问题来源于优步、网飞、Noom、微软、脸书等顶级公司的实际数据科学面试。查看我们最*关于 前 30 名 Python 面试问题和答案 的帖子。在 StrataScratch 上,您将有机会加入一个由 20,000 多名志同道合的数据科学爱好者组成的团体,获得协作学习体验。今天就在 StrataScratch 上注册,为全球大型科技公司和初创公司最受欢迎的职位做好准备。

原载于https://www.stratascratch.com

Python 熊猫阅读 CSV

原文:https://towardsdatascience.com/python-pandas-reading-a-csv-7865a11939fd?source=collection_archive---------19-----------------------

学习如何阅读 CSV 文件并创建熊猫数据帧

照片由米卡·鲍梅斯特在 Unsplash 上拍摄

介绍

作为数据分析师或数据科学家,您将经常需要组合和分析来自各种数据源的数据。我经常被要求分析的一种数据类型是 CSV 文件。CSV 文件在企业界很受欢迎,因为它们可以处理强大的计算,易于使用,并且通常是企业系统的输出类型。今天我们将演示如何使用 Python 和 Pandas 打开并读取本地机器上的 CSV 文件。

入门指南

你可以通过 pip 从 PyPI 安装 Panda。如果这是你第一次安装 Python 包,请参考 Pandas 系列&数据帧讲解或 Python Pandas 迭代数据帧。这两篇文章都将为您提供安装说明和今天文章的背景知识。

句法

在学习熊猫时,对我来说最具挑战性的部分是关于熊猫功能的大量教程,比如.read_csv()。然而,教程往往忽略了处理真实世界数据时所需的复杂性。一开始,我经常发现自己不得不在 StackOverflow 上发布问题,以学习如何应用特定的参数。下面我们包括了所有的参数,以及概念上更复杂的例子。

上面的 python 片段显示了 Pandas read csv 函数的语法。

上面的语法乍一看似乎很复杂;然而,我们将只设置少数几个参数,因为大多数参数都被赋予了默认值。然而,这些参数预示着熊猫强大而灵活的天性。

因素

  • filepath_or_buffer:您可以传入一个字符串或路径对象,该对象引用您想要读取的 CSV 文件。该参数还接受指向远程服务器上某个位置的 URL。

上面的 Python 片段展示了如何通过向 filepath_or_buffer 参数提供文件路径来读取 CSV。

  • sep&delimiter:delimiter参数是sep的别名。你可以使用sep来告诉熊猫使用什么作为分隔符,默认情况下这是,。但是,您可以为制表符分隔的数据传入 regex,如\t
  • header:该参数允许您传递一个整数,该整数捕获 CSV 头名称所在的行。默认情况下,header被设置为infer,这意味着 Pandas 将从第 0 行获取标题。如果您打算重命名默认标题,则将header设置为0
  • name:这里您有机会覆盖默认的列标题。为此,首先设置header=0,然后传入一个数组,该数组包含您想要使用的新列名。

上面的 Python 片段重命名了原始 CSV 的头。我们在上面的例子中使用的 CSV 的副本可以在这里找到。

  • index_col:对于不熟悉 DataFrame 对象的人来说,DataFrame 的每一行都有一个标签,称为索引。如果 CSV 文件包含表示索引的列,则可以传递列名或整数。或者,您可以通过False告诉 Pandas 不要使用您的文件中的索引。如果False通过,Pandas 将使用一系列递增的整数创建一个索引。
  • usecols:您可以使用该参数返回文件中所有列的子集。默认情况下,usecols被设置为None,这将导致 Pandas 返回 DataFrame 中的所有列。当您只对处理某些列感兴趣时,这很方便。

在上面的 Python 片段中,我们告诉 Pandas 我们只希望读取第 1 和第 2 列。您可以使用这个 CSV 来测试上面的代码片段。

  • squeeze:当处理一个单列 CSV 文件时,您可以将该参数设置为True,这将告诉 Pandas 返回一个系列,而不是一个数据帧。如果对熊猫系列不熟悉,可以参考熊猫系列& DataFrame 讲解进行概述。
  • prefix:如果你还没有指定要使用的列标签前缀,你可以在这里设置。当没有指定列时,默认行为是使用整数序列来标记它们。使用该参数,您可以将列012设置为column_0column_1column_2

在上面的 Python 片段中,我们试图读取一个没有头文件的 CSV 文件。默认情况下,熊猫会添加数字列标签。在上面,我们已经用“column_”作为列的前缀。我们在这个例子中使用的 CSV 可以在这里找到。

  • mangle_dupe_cols:如果您正在阅读的 CSV 文件包含同名的列,Pandas 将为每个重复的列添加一个整数后缀。将来mangle_dupe_cols将接受False,这将导致重复的列相互覆盖。
  • dtype:您可以使用这个参数来传递一个字典,这个字典将列名作为键,数据类型作为它们的值。当您有一个以零填充的整数开头的 CSV 时,我发现这很方便。为每一列设置正确的数据类型也将提高操作数据帧时的整体效率。

上面我们已经确保了 employee_no 列将被转换为一个字符串。这还演示了保留前导零的能力,因为您会注意到数据集中的 job_no 没有被转换,因此丢失了前导零。数据集可以在这里找到。

  • engine:目前熊猫接受cpython作为解析引擎。
  • converters:这遵循与dtype相似的逻辑,但是,除了传递数据类型,您还可以传递在读取时操作特定列中的值的函数。

上面的 Python 代码片段将应用 double_number()函数,我们将它定义为第 1 列的转换器。我们的示例 CSV 文件可以在这里找到。

  • true_values & false_values:这个参数挺俏皮的。例如,在您的 CSV 中,您有一个包含yesno的列,您可以将这些值映射到TrueFalse。这样做将允许您在将文件读入 Pandas 时清除一些数据。

上面的 Python 片段演示了如何定义 True & False 值。这里我们将 yes & maybe 设为 True,no 设为 False。此处可找到一个 CSV 示例。

  • skipinitialspace:您可以将此参数设置为True,告诉熊猫分隔符后可能有前导空格的行。然后,Pandas 将删除分隔符之后和非分隔符之前的任何前导空格。
  • skiprows:在处理系统生成的 CSV 文件时,有时文件的开头会包含参数行。通常我们不想处理这些行,而是跳过它们。您可以将skiprows设置为一个整数,表示在开始读取之前要跳过的行数。或者,您可以提供一个 callable,当函数计算结果为True时,它将导致 Pandas 跳过一行。
  • skipfooter:类似于skiprows这个参数允许你告诉熊猫在文件的末尾要跳过多少行。同样,如果报告参数在 CSV 文件的末尾,这也很方便。
  • nrows:您可以使用它来设置从 CSV 文件中收集的行数的限制。我发现在探索阶段,当试图对数据有所感觉时,这很方便。这意味着您可以测试您的逻辑,而不必将大文件加载到内存中。
  • na_values:默认情况下,Pandas 有大量的值被映射到NaN(不是一个数字)。如果您有需要清理和映射的特定于应用程序的值,可以将它们传递给此参数。使用这个参数意味着您可以捕获所有的值,这些值都可以映射到一个默认的预处理。
  • keep_default_na:该参数可以设置为TrueFalse。如果False和 CSV 包含默认的NaN值,那么 Pandas 将保留原来的NaN值。如果True Pandas 将解析NaN值并在数据帧中用NaN屏蔽。
  • na_filter:当您希望 Pandas 解释您的数据中缺失的值时,您可以将此设置为True。提示一下,当读取您知道没有任何丢失值的大文件时,将此参数设置为False
  • verbose:默认设置为False。将verbose设置为True将向控制台输出额外的数据,如NaN值的数量或特定过程花费的时间。
  • 有时,我们收到的数据可能包含空行。通过将skip_blank_lines设置为True,Pandas 将跳过这些行,而不是将它们计为NaN值。
  • parse_dates:使用这个参数告诉 Pandas 你希望 CSV 文件中的日期如何被解释。你可以通过True,这会让熊猫把索引解析成日期。或者,您可以传递 Pandas 将用来创建日期的列名或列列表。

上面的 Python 代码片段将第 0 列转换成索引,并将其解析为日期。用于本例的 CSV 文件可以在这里找到。运行上述脚本时,请注意针对日期时间格式对列 0 所做的更改。

  • infer_datetime_format:您可以将此参数设置为True,它将告诉熊猫推断日期时间格式。当与parse_dates结合时,这样做将导致更大的处理速度。
  • keep_date_col:如果您已经为parse_dates设置了一个值,您可以使用该参数来保留创建数据的列。默认行为是将这些列放到适当的位置。如果您不希望这种情况发生,请将keep_date_col设置为True

上面的 Python 脚本将通过尝试解析列 0、1 和 2 来创建一个日期。此外,keep_date_col 已被设置为 True,这将导致保留列 0、1 和 2。我们的示例 CSV 可以在这里找到。

  • date_parser:如果您已经知道 CSV 中的日期格式,您可以传递一个函数给date_parser来有效地格式化日期时间,而不是推断格式。
  • dayfirst:如果你的日期时间格式是DD/MM,则通过True
  • cache_dates:默认设置为True。Pandas 将创建一组独特的日期-时间字符串转换,以加快重复字符串的转换。
  • iterator:将该参数设置为True将允许您调用 Pandas 函数.get_chunk(),该函数将返回要处理的记录数。
  • chunksize:这将允许您设置数据帧内块的大小。这样做很方便,因为您可以循环数据帧的一部分,而不是将整个数据帧延迟加载到内存中。
  • compression:如果您正在读取的数据在磁盘上被压缩,那么您可以设置动态解压缩的压缩类型。
  • thousands:这是千位单位的分隔符。在 CSV 文件中,你有时可以看到用1_000_000表示的一百万,因为,被用作分隔符。将千设置为_将导致1_000_000反映为1000000
  • decimal:如果偏离.,您可以在 CSV 文件中提供代表小数的字符。
  • lineterminator:如果你已经将engine设置为c,你可以用这个参数告诉熊猫你希望这些行以什么字符结束。
  • quotechar:这是在整个 CSV 文件中使用的字符,表示引用元素的开始和结束。
  • quoting:在这里你可以设置你想要应用到你的元素的报价级别。默认情况下,这是 0,将报价设置为最小;您也可以将其设置为 1-全部引用、2-引用非数字或 3-不引用。
  • doublequote:当两个引号字符出现在一个引号元素中时,您可以使用这个参数告诉 Pandas 该做什么。当True通过时,双引号字符将变成单引号字符。
  • escapechar:长度为一的字符串,熊猫将使用它来转义其他字符。
  • comment:您可以使用该参数来表示您不想处理该行的剩余部分。例如,如果comment设置为#并且#出现在当前行中,熊猫到达#后将移动到下一行。
  • encoding:如果您正在使用非英语的数据,请将此值设置为特定的字符编码,以便可以正确读取数据。
  • dialect:CSV 方言是一组告诉 CSV 解析器如何读取 CSV 文件的参数。常见的方言有excelexcel-tabunix另外,你可以自己创作,传给熊猫。
  • error_bad_lines:如果 Pandas 遇到一个具有两个以上属性的行,通常会引发一个异常,Python 会暂停执行。如果将False传递给error_bad_lines,那么任何通常会引发此类异常的行将从数据帧中删除。
  • warn_bad_lines:如果您已经将error_bad_lines设置为False,那么您可以将warn_bad_lines设置为True,这将输出每一行会引发异常的代码。
  • delim_whitespace:这个参数与delimiter相似,但是它只针对空白。如果你想用空格作为分隔符,那么你可以将delimiter设置为\s+,或者将delim_whitespace设置为True
  • low_memory:默认情况下,Pandas 将此设置为True,这将导致分块处理,然而,存在不匹配类型推理的风险。通过确保设置了dtype参数,可以避免可能的类型不匹配。
  • memory_map:如果你已经传递了一个文件给filepath_or_buffer Pandas 在内存中映射文件对象,以提高处理较大文件的效率。
  • float_precision:您可以在这里为浮动元素的c引擎设置合适的转换器。
  • storage_options:从远程位置读取 CSV 文件时,可以使用该参数传递特定选项。

接下来去哪里

既然您已经了解了如何使用 Pandas .read_csv(),我们的建议是通过 Pandas 系列& DataFrame 解释来学习更多关于 Pandas 数据结构的知识,或者在 Python Pandas 迭代 DataFrame 中学习如何导航数据帧。如果你已经掌握了这些概念,你的下一步应该是阅读旋转熊猫数据框架或如何组合 Python、熊猫& XlsxWriter 。

摘要

作为一名数据分析师,学习如何使用 Pandas .read_csv()是一项至关重要的技能,可以将各种数据源结合起来。正如你在上面看到的,.read_csv()是一个非常强大和灵活的工具,你可以适应各种现实世界的情况来开始你的数据收集和分析。

感谢您花时间阅读我们的故事,我们希望您发现它很有价值。

Python 熊猫 vs. R Dplyr

原文:https://towardsdatascience.com/python-pandas-vs-r-dplyr-5b5081945ccb?source=collection_archive---------4-----------------------

完整的备忘单

图片由作者根据https://allisonhorst.github.io/的 Dplyr 标志和https://pandas.pydata.org/about/citing.html的熊猫标志制作

期待什么

对于许多数据科学家来说, Pandas for Python 和 Dplyr for R 是两个最流行的处理表格/结构化数据的库。关于哪个框架更好,总会有一些激烈的讨论。老实说,这真的重要吗?最后,这是关于完成工作,熊猫和 dplyr 都提供了很好的数据争论工具。**不用担心,这篇文章并不是另一个试图证明任何一个库的观点的比较!**因此,本条的目的是:

  • 帮助其他人从一种语言/框架过渡到另一种语言/框架
  • 探索可以作为数据科学家增加技能的新工具
  • 创建一个参考备忘单,以防您需要查找两种语言中最常用的数据争论函数

数据

在本教程中,我们将使用 iris 数据集,它是 Pythons sklearn 和 base R 的一部分。

Sepal_length  Sepal_width  Petal_length     Petal_width  Species
          5.1         3.5           1.4             0.2   setosa
          4.9         3.0           1.4             0.2   setosa
          4.7         3.2           1.3             0.2   setosa
          4.6         3.1           1.5             0.2   setosa
          5.0         3.6           1.4             0.2   setosa

小抄

免责声明:长阅读

选择列

这里已经变得令人困惑了。首先,在每个框架中,如何从数据框架中选择列有多种方法。在 Pandas 中,您可以简单地传递带有列名的列表,或者使用 filter() 方法。这令人困惑,因为 dplyr 中的 filter() 函数用于根据条件而不是根据列对行进行子集化!在 dplyr 中,我们使用 select() 函数来代替:

熊猫

#Pass columns as list
dataframe[[“Sepal_width”, “Petal_width”]] #Use Filter Function
dataframe.filter(items=['Sepal_width', 'Petal_width'])

Dplyr

dataframe %>% select(Sepal_width, Petal_width)

基于条件的过滤

同样,有多种方法可以根据一列或多列的条件过滤数据帧中的记录。

熊猫

在 Pandas 中,你可以使用索引方法或者尝试方便的查询 API,这是我个人更喜欢的。

#indexing
dataframe[(dataframe["Sepal_width"] > 3.5) & (dataframe["Petal_width"] < 0.3)] #query API
dataframe.query("Sepal_width > 3.5 & Petal_width < 0.3")

Dplyr

dplyr 中过滤记录的标准方式是通过 filter 函数()

dataframe %>% filter(Sepal_width > 3.5 & Petal_width < 0.3)

重命名单列

重命名听起来像是一个简单的任务,但是要小心,注意这里的细微差别。如果我们想在熊猫中将我们的列从物种重命名为类,我们提供一个字典,上面写着**{‘物种’:‘类’},而在 Dplyr 中正好相反类=物种**:

熊猫

dataframe.rename(columns = {'Species': 'Class'}, inplace = True)

Dplyr

dataframe <- dataframe %>% rename(Class=Species)

根据条件重命名多个列

假设我们想根据一个条件一次重命名多个列。例如,将我们所有的特征列(萼片长度、萼片宽度、花瓣长度、花瓣宽度)转换为大写。在 Python 中,这实际上相当复杂,您需要首先导入另一个库,并手动迭代每一列。在 Dplyr 中,如果您想根据条件访问/更改多个列,有一个更简洁的界面。

熊猫

import re#prepare pattern that columns have to match to be converted to upper case
pattern = re.compile(r".*(length|width)")#iterate over columns and covert to upper case if pattern matches.
for col in dataframe.columns:
  if bool((pattern.match(col))):
    dataframe.rename(columns = {col: col.upper()}, inplace = True)

Dplyr

dataframe <- dataframe %>% rename_with(toupper, matches("length|width"))

结果

请注意大写特征列名:

SEPAL_LENGTH  SEPAL_WIDTH  PETAL_LENGTH     PETAL_WIDTH  Species
          5.1         3.5           1.4             0.2   setosa
          4.9         3.0           1.4             0.2   setosa

根据条件改变单元格值

假设我们想要基于条件重新编码/改变单元格值:在我们的示例中,我们将尝试将物种字符串“setosa,versicolor 和 virginica”重新编码为从 0 到 2 的整数:

熊猫

dataframe.loc[dataframe['Species'] == 'setosa', "Species"] = 0
dataframe.loc[dataframe['Species'] == 'versicolor', "Species"] = 1
dataframe.loc[dataframe['Species'] == 'virginica', "Species"] = 2

Dplyr

dataframe <- dataframe %>%
  mutate(Species = case_when(Species == 'setosa' ~ 0,
                             Species == 'versicolor' ~ 1,
                             Species == 'virginica' ~ 2))

每列的不同值

有时我们想看看一列中有哪些不同的/唯一的值。请注意这两个框架中的函数调用有多么不同:Pandas 使用 unique() 方法,dplyr()使用 distinct() 函数来获得相同的结果:

熊猫

dataframe.Species.unique()#array(['setosa', 'versicolor', 'virginica'], dtype=object)

Dplyr

dataframe %>% select(Species) %>% distinct()# Species   
# setosa    
# versicolor
# virginica

记录计数(每组)

如果您想计算数据帧中总共有多少个条目,或者获取某个组的计数,您可以执行以下操作:

熊猫

# Total number of records in dataframe
len(dataframe)#150 # Number of records per Group
dataframe.value_counts('Species')#Species
#virginica     50
#versicolor    50
#setosa        50# Note that you can also use the .groupby() method followed by size()
dataframe.groupby(['Species']).size() 

Dplyr

# Total number of records in dataframe
dataframe %>% nrow()#[1] 150 # Number of records per Group (count and tally are interchangeable)
dataframe %>% group_by(Species) %>% count()
dataframe %>% group_by(Species) %>% tally()#  Species        n
#  <fct>      <int>
#1 setosa        50
#2 versicolor    50
#3 virginica     50

对整个列进行汇总/聚合

如果要为数据框中的一列或多列创建描述性统计数据,可执行以下操作:

熊猫

#get mean and min for each column
dataframe.agg(['mean', 'min'])#      Sepal_length  Sepal_width  Petal_length  Petal_width Species
#mean      5.843333     3.057333         3.758     1.199333     NaN
#min       4.300000     2.000000         1.000     0.100000  setosa

Dplyr

不幸的是,我没有找到如何一次在多个列上使用多个聚合函数的方法。这就是为什么您需要多次调用 summarise 函数来获得相同的结果:

#first aggregation over all columns using mean
dataframe %>% summarise(across(everything(), mean))#  Sepal_length Sepal_width Petal_length Petal_width Species
#         5.84        3.06         3.76        1.20      NA#second aggregation over all columns using min
dataframe %>% summarise(across(everything(), min))#Sepal_length Sepal_width Petal_length Petal_width Species
#        5.84        3.06         3.76        1.20      NA

按组汇总/汇总

如果想要在数据集中按组聚集统计数据,必须使用 Pandas 中的 groupby() 方法和 Dplyr 中的 group_by() 函数。您可以对所有列或特定列执行此操作:

熊猫

请注意 Pandas 如何使用多级索引来清晰地显示结果:

# aggregation by group for all columns
dataframe.groupby(['Species']).agg(['mean', 'min'])#                 Sepal_length      Sepal_width       ...
#                   mean  min        mean min         ...
#Species                                                         
#setosa            5.01  4.3       3.43             ...
#versicolor        5.94  4.9       2.77             ...
#virginica         6.59  4.9       2.97             ... # aggregation by group for a specific column
dataframe.groupby(['Species']).agg({'Sepal_length':['mean']})#   Sepal_length
#                   mean
#Species                
#setosa            5.01
#versicolor        5.94
#virginica         6.59

Dplyr

由于 Dplyr 不支持多级索引,所以第一次调用的输出与 Pandas 相比看起来有点乱。在此输出中,显示了第一个函数的统计数据(mean-fn1),然后是第二个函数的统计数据(min-fn2)。

# aggregation by group for all columns
dataframe %>% group_by(Species) %>% summarise_all(list(mean,min))Species    Sepal_length_fn1  Sepal_width_fn1         ...
setosa                 5.01            3.43          ...
versicolor             5.94            2.77          ...         
virginica              6.59            2.97          ... # aggregation by group for a specific column
dataframe %>% group_by(Species) %>% summarise(mean=mean(Sepal_length))#Species     mean
# setosa      5.01
# versicolor  5.94
# virginica   6.59

列数学/添加新列

有时,您希望创建一个新列,并通过某种数学运算将两个或多个现有列的值组合起来。以下是如何在熊猫和 Dplyr 中做到这一点:

熊猫

dataframe["New_feature"] = dataframe["Petal_width"]* dataframe["Petal_length"] / 2

Dplyr

dataframe <- dataframe %>% mutate(New_feature= Petal_width*Petal_length/2)

删除列

为了清理数据帧,删除列有时会非常方便:

熊猫

在 Pandas 中,你可以用 drop() 删除一列。您也可以使用 inplace=True 来覆盖当前数据帧。

dataframe.drop("New_feature", axis=1, inplace=True)

Dplyr

在 Dplyr 中,您可以在 select()函数中使用前导的减号来指定要删除的列名。

dataframe <- dataframe %>% select(-New_feature)

按值对记录排序

要对值进行排序,您可以在 Pandas 中使用 sort_values() ,在 Dplyr 中使用 arrange() 。两者的默认排序都是升序。请注意每个函数调用在降序排序方面的差异:

熊猫

dataframe.sort_values('Petal_width', ascending=0)

Dplyr

dataframe %>% arrange(desc(Petal_width))

重命名单列

重命名听起来像是一个简单的任务,但是要小心,注意这里的细微差别。如果我们想在 Pandas 中将我们的列从 Species 重命名为 Class ,我们提供一个字典,上面写着 {'Species': 'Class'} ,而在 Dplyr 中,情况正好相反 Class=Species :

熊猫

dataframe.rename(columns = {'Species': 'Class'}, inplace = True)

Dplyr

dataframe %>% relocate(Species)
dataframe %>% relocate(Species, .before=Sepal_width)

更改列的顺序

我不经常使用这个功能,但是如果我想为一个演示创建一个表格,并且列的排序没有逻辑意义,这个功能有时会很方便。以下是如何移动列:

熊猫

在 Python Pandas 中,您需要通过使用列表来重新索引您的列。假设我们想将列物种移到前面。

#change order of columns
dataframe.reindex(['Species','Petal_length','Sepal_length','Sepal_width','Petal_Width'], axis=1)

Dplyr

在 Dplyr 中,您可以使用方便的 relocate() 函数。同样,假设我们想将列物种移到前面。

dataframe %>% relocate(Species)#Note that you can use .before or .after to place a columne before or after another specified column - very handy!
dataframe %>% relocate(Species, .before=SEPAL_WIDTH)

限幅

切片本身就是一个完整的主题,有很多方法可以实现。下面让我们来看一下最常用的切片操作:

按行切片

有时您知道想要提取的确切行号。虽然 Dplyr 和 Pandas 中的过程非常相似**,但是请注意 Python 中的索引从 0 开始,而 R 中的索引从 1 开始。**

熊猫

dataframe.iloc[[49,50]]#  Sepal_length  Sepal_width  Petal_length  Petal_width    Species
#          5.0          3.3           1.4          0.2      setosa
#          7.0          3.2           4.7          1.4  versicolor

Dplyr

dataframe %>% slice(50,51)#  Sepal_length Sepal_width Petal_length Petal_width Species     
#1            5         3.3          1.4         0.2 setosa    
#2            7         3.2          4.7         1.4 versicolor

分割第一个和最后一个记录(头/尾)

有时我们希望看到数据帧中的第一条或最后一条记录。这可以通过提供一个固定数量 n 或一个比例 prop 值来实现。

熊猫

在 Pandas 中,您可以使用 head()tail() 方法来获取固定数量的记录。如果你想提取一个比例,你必须自己计算一下:

#returns the first 5 records
dataframe.head(n=5)#returns the last 10% of total records
dataframe.tail(n=len(dataframe)*0.1)

Dplyr

在 Dplyr 中,有两个为这个用例指定的函数: slice_head()slice_tail() 。请注意如何指定固定数量或比例:

#returns the first 5 records
dataframe %>% slice_head(n=5)#returns the last 10% of total records
dataframe %>% slice_tail(prop=0.1)

按值对第一条和最后一条记录进行切片

有时,选择每列具有最高或最低值的记录很有用。同样,这可以通过提供固定的数量或比例来实现。

熊猫

对于熊猫来说,这比 Dplyr 更棘手。例如,假设您想要 20 个具有最长“花瓣长度”的记录,或者 10%的总记录具有最短的“花瓣长度”。要在 Python 中进行第二个操作,我们必须做一些数学计算,首先对我们的值进行排序:

#returns 20 records with the longest Petal_length (for returning the shortest you can use the function nsmallest)
dataframe.nlargest(20, 'Petal_length')#returns 10% of total records with the shortest Petal_length
prop = 0.1 
dataframe.sort_values('Petal_length', ascending=1).head(int(len(dataframe)*prop))

Dplyr

在 Dplyr 中,这要简单得多,因为此用例有指定的函数:

#returns 20 records with the longest Petal_length
dataframe %>% slice_max(Petal_length, n = 20)#returns 10% of total records with the shortest Petal_lengthdataframe %>% slice_min(Petal_length, prop = 0.1)

按值和组对第一条和最后一条记录进行切片

有时,选择每列具有最高或最低值的记录**,但由组**分隔,这很有用。同样,这可以通过提供固定的数量或比例来实现。想象一下,例如我们想要 3 个每个物种花瓣长度最短的记录。

熊猫

对熊猫来说,这也比 Dplyr 更棘手。我们首先按照物种对数据帧进行分组,然后应用一个 lambda 函数,该函数利用了上述的 nsmallest()nlargest() 函数:

#returns 3 records with the shortest Petal_length per Species
(dataframe.groupby('Species',group_keys=False)
        .apply(lambda x: x.nsmallest(3, 'Petal_length')))#Sepal_length  Sepal_width  Petal_length  Petal_width     Species
#        4.6          3.6           1.0          0.2      setosa
#        4.3          3.0           1.1          0.1      setosa
#        5.8          4.0           1.2          0.2      setosa
#        5.1          2.5           3.0          1.1  versicolor
#        4.9          2.4           3.3          1.0  versicolor
#        5.0          2.3           3.3          1.0  versicolor
#        4.9          2.5           4.5          1.7   virginica
#        6.2          2.8           4.8          1.8   virginica
#        6.0          3.0           4.8          1.8   virginica#returns 5% of total records with the longest Petal_length per Species
prop = 0.05
(dataframe.groupby('Species',group_keys=False)
        .apply(lambda x: x.nlargest(int(len(x) * prop), 'Petal_length')))#Sepal_length  Sepal_width  Petal_length  Petal_width     Species
#         4.8          3.4           1.9          0.2      setosa
#         5.1          3.8           1.9          0.4      setosa
#         6.0          2.7           5.1          1.6  versicolor
#         6.7          3.0           5.0          1.7  versicolor
#         7.7          2.6           6.9          2.3   virginica
#         7.7          3.8           6.7          2.2   virginica

Dplyr

在 Dplyr 中,这要简单得多,因为这个用例有指定的函数。注意如何提供 with_ties=FALSE 来指定是否应该返回 ties(具有相等值的记录)。

#returns 3 records with the shortest Petal_length per Species
dataframe %>% group_by(Species) %>% slice_min(Petal_length, n = 3, with_ties = FALSE)#returns 5% of total records with the longest Petal_length per Species
dataframe %>% group_by(Species) %>% slice_max(Petal_length, prop = 0.05, with_ties=FALSE)

切片随机记录(每组)—抽样

对随机记录进行切片也可以称为抽样。这也可以通过证明一个固定的数字或比例来实现。此外,这可以在整个数据集上进行,也可以基于组*均分布。因为这是一个相当频繁的用例,所以在两个框架中都有这样的函数:

熊猫

在 Pandas 中,您可以使用 sample() 函数,或者指定 n 为固定数量的记录,或者指定 frac 为一定比例的记录。此外,您可以指定替换来允许或不允许对同一行进行多次采样。

#returns 20 random samples
dataframe.sample(n=20)#return 20% of total records
dataframe.sample(frac=0.2, replace=True)#returns 10% of total records split by group
dataframe.groupby('Species').sample(frac=0.1)

Dplyr

Dplyr 中的界面非常相似。您可以使用 slice_sample() 函数,或者为固定数量的记录指定 n ,或者为一定比例的记录指定 prop 。此外,您可以指定替换以允许或不允许对同一行进行多次采样。

#returns 20 random samples
dataframe %>% slice_sample(n=20)#return 20% of total records
dataframe %>% slice_sample(prop=0.2, replace=True)#returns 10% of total records split by group
dataframe %>% group_by(Species) %>% slice_sample(prop=0.1)

连接

连接数据框架也是一个常见的用例。(join 操作范围很广,但我不打算在此详述)

但是,随后您将学习如何在 Pandas 和 Dplyr 中执行完全(外部)连接。

图像您有两个共享一个公共变量“key”的数据帧:

#Python Pandas
A = dataframe[[“Species”, “Sepal_width”]]
B = dataframe[[“Species”, “Sepal_length”]]#R Dplyr:
A <- dataframe %>% select(Species, Sepal_width)
B <- dataframe %>% select(Species, Sepal_length)

熊猫

对于所有的加入操作,你可以使用 Pandas 中的“合并”功能,并指定你想加入什么,如何加入(外部,内部,左侧,右侧,..)您想加入哪个键:

#Join dataframe A and B (WHAT), with a full join (HOW) by making use of the key "Species" (ON) 
pd.merge(A, B, how="outer", on="Species")

Dplyr

在 Dplyr 中,语法非常相似,但是,对于每种连接类型,都有单独的函数。在本例中,我们将再次使用 full_join() 函数执行完全连接:

#Join dataframe A and B (WHAT), with a full join (HOW) by making use of the key "Species" (ON)
A %>% full_join(B, by="Species")

连接/绑定行和列

有时我们不想连接我们的数据帧,而只是通过行或列附加两个现有的数据帧。熊猫和 Dplyr 都有一个很好的界面来实现这一点:

熊猫

在 Pandas 中,可以用 concat() 方法连接两个数据帧。默认值按行连接数据帧。通过指定轴(例如轴= 1),可以通过列连接两个数据帧。

请注意,如果某个值没有出现在其中一个数据帧中,它会自动填充 NA。

#Concatenate by rows
pd.concat([A,B])#       Species  Sepal_width  Sepal_length
#0       setosa          3.5           NaN
#1       setosa          3.0           NaN
#2       setosa          3.2           NaN
#3       setosa          3.1           NaN
# ... #Concatenate by columns 
pd.concat([A,B], axis=1)#       Species  Sepal_width    Species  Sepal_length
#0       setosa          3.5     setosa           5.1
#1       setosa          3.0     setosa           4.9
#2       setosa          3.2     setosa           4.7
#3       setosa          3.1     setosa           4.6
# ...

Dplyr

在 Dplyr 中,有两个独立的函数用于绑定数据帧: bind_rows()bind_columns()。

请注意,如果某个值没有出现在其中一个数据帧中,那么在应用 bind_rows()时,它会自动填充 NA。另外,请注意 R 如何自动更改列名(以避免重复)。使用可以改变这种行为。name_repair 参数。

#Bind by rows
A %>% bind_rows(B)#   Species Sepal_width Sepal_length
# 1 setosa          3.5           NA
# 2 setosa          3             NA
# 3 setosa          3.2           NA
# 4 setosa          3.1           NA
# ...#Bind by columns
A %>% bind_cols(B)#  Species...1 Sepal_width Species...3 Sepal_length
# 1 setosa              3.5 setosa               5.1
# 2 setosa              3   setosa               4.9
# 3 setosa              3.2 setosa               4.7

Pfew!恭喜你!你可能是第一个读到这篇文章/备忘单结尾的人。您可以通过给我鼓掌或在下面给我留言来获得奖励:)

图片来自 Giphy

Python 并行性:几分钟内加速 Python 代码的基本指南

原文:https://towardsdatascience.com/python-parallelism-essential-guide-to-speeding-up-your-python-code-in-minutes-5ec71cbd88e1?source=collection_archive---------18-----------------------

Python 多重处理基本指南。

照片由 Wexor Tmg 在 Unsplash 拍摄

按顺序执行任务可能不是一个好主意。如果第二个任务的输入不是第一个任务的输出,那么你就在浪费时间和 CPU。

你可能知道,Python 的 全局解释器锁 (GIL)机制一次只允许一个线程执行 Python 字节码。这是一个严重的限制,可以通过更改 Python 解释器或实现基于进程的并行技术来避免。

今天,您将学习如何使用 Python 和concurrent.futures库并行执行任务。您将通过一个实际例子理解这个概念——从多个 API 端点获取数据。

这篇文章的结构如下:

  • 问题描述
  • 测试:按顺序运行任务
  • 测试:并行运行任务
  • 结论

你可以在这里下载这篇文章的源代码。

问题描述

目标是连接到jsonplaceholder.typicode.com——一个免费的假 REST API。

您将连接到几个端点并获取 JSON 格式的数据。总共会有六个端点。不是很多,Python 很可能在一秒钟左右完成任务。对于演示多处理能力来说不太好,所以我们将增加一些趣味。

除了获取 API 数据,程序还会在发出请求之间休眠一秒钟。由于有六个端点,程序应该在六秒钟内什么都不做——但是只有当调用按顺序执行时。

我们先测试一下没有并行的执行时间。

测试:按顺序运行任务

让我们看一下整个脚本,并对其进行分解:

URLS变量中存储了一个 API 端点列表。你将从那里获取数据。在它下面,你会发现fetch_single()功能。它向一个特定的 URL 发出 GET 请求,然后休眠一秒钟。当获取开始和完成时,它也打印。

该脚本记下开始和结束时间,并将它们相减以获得总执行时间。在来自URLS变量的每个 URL 上调用fetch_single()函数。

运行该脚本后,您将在控制台中获得以下输出:

图 1-没有多重处理脚本输出(作者提供的图片)

简而言之,这就是顺序执行。这个脚本在我的机器上花了大约 7 秒钟完成。你可能会得到几乎相同的结果。

接下来让我们看看如何使用并行性来减少执行时间。

测试:并行运行任务

让我们看一下脚本,看看有什么变化:

concurrent.futures库用于实现基于进程的并行性。URLSfetch_single()都是一样的,所以没有必要再重复一遍。

下面才是有趣的地方。你必须使用ProcessPoolExecutor类。根据文档,它是一个使用进程池异步执行调用的类[1]。

这里的with语句是为了确保在任务完成后所有的东西都被清理干净。

可以使用submit()函数来传递想要并行执行的任务。第一个参数是函数名(确保不要调用它),第二个参数是 URL 参数。

运行该脚本后,您将在控制台中获得以下输出:

2 —多重处理脚本输出(图片由作者提供)

执行时间仅用了 1.68 秒,比之前有了显著的提高。这是任务并行运行的具体证明,因为顺序执行无法在 6 秒内完成(睡眠调用)。

结论

这就是关于 Python 基于流程的并行性的最基本指南。还有其他基于并发性的方法来加速您的脚本,这些将在下面的文章中介绍。

如果你想看更多高级并行教程,请告诉我。这些将涵盖数据科学和机器学习中的真实用例。

感谢阅读。

喜欢这篇文章吗?成为 中等会员 继续无限制学习。如果你使用下面的链接,我会收到你的一部分会员费,不需要你额外付费。

https://medium.com/@radecicdario/membership

了解更多信息

  • 每个数据科学家必读的 3 本编程书籍
  • 如何让 Python 静态类型化——基本指南
  • 用 Python 进行面向对象编程——你需要知道的一切
  • Python 字典:你需要知道的一切
  • 介绍 f 字符串 Python 中字符串格式化的最佳选项

参考

[1]https://docs.python.org/3/library/concurrent.futures.html

原载于 2021 年 1 月 16 日 https://betterdatascience.comhttps://betterdatascience.com/python-concurrency/

Python 解析库:反转 F 字符串的简单方法

原文:https://towardsdatascience.com/python-parse-library-simple-way-for-reversing-f-strings-72ad4d59e7c4?source=collection_archive---------10-----------------------

我们有时需要反过来。

马修·斯特恩在 Unsplash 上的照片

字符串插值是使用占位符修改字符串的过程。结果字符串包括占位符的值。在 Python 中,format 方法和 f 字符串是两种常用的字符串插值方法。

在跳到解析库之前,做几个例子来演示什么是字符串插值是有帮助的。

folder = "notebook"
subfolder = "parse"file_path = f"documents/{folder}/{subfolder}"print(file_path)
documents/notebook/parse

占位符用花括号表示,它们的值包含在输出中。

这是另一个例子。

name = "John"
age = "23"print(f"{name} is {age} years old.")
John is 23 years old.

我们现在更熟悉 f 弦和一般意义上的弦插值。解析库所做的是字符串插值过程的逆过程。

可以使用解析库提取字符串中的值。我们将做几个例子来解释清楚这个过程。

解析库可以很容易地与 pip 一起安装。如果你用的是 jupyter 笔记本,就加“!”在匹普之前。

!pip install parse

从语法上分析

在第一个示例中,我们使用预定义的文件夹和子文件夹名称创建了一个文件路径。让我们使用解析库从路径中获取文件夹名。我们首先需要从解析库中导入解析函数。

from parse import parsefile_path = "documents/notebook/pandas"parse("documents/{folder}/{subfolder}", file_path)
<Result () {'folder': 'notebook', 'subfolder': 'pandas'}>

我们传递文件路径以及表示占位符的字符串。parse 函数返回一个结果对象,但是我们可以通过添加命名方法使它看起来更漂亮。

parse("documents/{folder}/{subfolder}", file_path).named
{'folder': 'notebook', 'subfolder': 'pandas'}

假设我们有一个格式相同的路径列表,我们需要提取文件夹和子文件夹的名称。这个任务可以通过解析器功能和列表理解的完美结合来轻松完成。

file_paths = [
    "documents/notebook/pandas",
    "documents/notebook/parse",
    "documents/notes/python"
][parse("documents/{folder}/{subfolder}", path).named for path in file_paths][{'folder': 'notebook', 'subfolder': 'pandas'},
 {'folder': 'notebook', 'subfolder': 'parse'},
 {'folder': 'notes', 'subfolder': 'python'}]

搜索

解析库还提供了一些其他函数,在特殊情况下会很方便。例如, search 函数在字符串中查找某种格式。因此,我们不必提供整个字符串的确切格式。

from parse import searchtxt = "Name: Jane, Department: Engineering, Age: 22"search("Name: {Name},", txt).named
{'Name': 'Jane'}

当我们不确定确切的字符串时,也可以使用搜索功能。因此,它提供了额外的灵活性。

考虑以下字符串:

txt = "The department of civil engineering has 23 employees"
txt2 = "The civil engineering department has 23 employees"

我们需要找到文本中雇员的数量。我们可以用同样的子模式找到它。

search("has {number_of_employees} employees", txt).named
{'number_of_employees': '23'}search("has {number_of_employees} employees", txt2).named
{'number_of_employees': '23'}

芬达尔

解析库中另一个有用的函数是 findall 函数。如果有多个我们感兴趣的相同模式的部分,我们可以使用 findall 而不是 parse。

以下示例显示了 findall 函数的一种可能用法。

from parse import findallpaths = "documents/notebook/pandas/ documents/notebook/parse/ documents/notes/python/"findall("documents/{folder}/{subfolder}/", paths)
<parse.ResultIterator at 0x2056556ab80>

我们有一个很长的字符串,其中包含多个格式相同的文件路径。findall 函数,顾名思义,查找给定路径结构的所有文件夹和子文件夹名称。

默认情况下,它返回一个迭代器,但我们可以很容易地将其转换成一个列表。

list(findall("documents/{folder}/{subfolder}/", paths))[<Result () {'folder': 'notebook', 'subfolder': 'pandas'}>,
 <Result () {'folder': 'notebook', 'subfolder': 'parse'}>,
 <Result () {'folder': 'notes', 'subfolder': 'python'}>]

该列表包含 3 个结果对象。我们可以将命名方法分别应用于这些对象。

a = list(findall("documents/{folder}/{subfolder}/", paths))a[0].named
{'folder': 'notebook', 'subfolder': 'pandas'}

结论

Parse 是一个非常实用的函数库。正如我们在示例中看到的,它提供了在字符串中查找模式和值的简单方法。某种意义上是字符串插值的逆向运算。

还有其他方法来执行相同的任务。例如,本文中的例子也可以用 regex 来完成。但是,对于某些操作来说,正则表达式可能太复杂了。我觉得解析库提供了更简单的解决方案。

如果你想了解更多关于解析库的知识,请随意访问官方文档。

感谢您的阅读。如果您有任何反馈,请告诉我。

带寄存器的 Python 多态性| Python 模式

原文:https://towardsdatascience.com/python-polymorphism-with-class-discovery-28908ac6456f?source=collection_archive---------4-----------------------

学习一种模式来隔离包,同时扩展 Python 代码的功能。

照片由 N. 在 Unsplash 上拍摄

动态多态是面向对象编程最强大的特性,它允许我们针对那些实际行为只在运行时定义的抽象进行编程。根据 Robert C. Martin 的说法,这也是真正定义 OOP 的唯一特性。

多态性由两部分组成:具有类型相关实现的方法和类型不可知的方法调用。在 python 代码中:

class TypeA:
  def speak(self):
    print("Hello, this is an object of type TypeA")class TypeB:
  def speak(self):
    print("Greetings from type TypeB")def agnostic_speak(speaker):
  speaker.speak()agnostic_speak(TypeA())
agnostic_speak(TypeB())>> Hello, this is an object of type TypeA
   Greetings from type TypeB

这个简单的例子展示了多态方法 speak()和调用它的通用函数,而不知道调用该方法的对象的实际类型。

对于这样一个简单的例子,一个基于参数类型的条件语句(if/else)可以完成这个任务,但是它也有缺点。首先,条件代码使得函数更难阅读。在这个版本中,我们立即看到它做了什么,它让一个对象说话,而对于条件代码,我们需要首先理解不同的分支做什么。此外,如果将来我们想要添加更多的说话方式,我们将需要返回到这个函数并修改它以添加新的分支。反过来,新的分支将使代码越来越不可读。

假设我们有一个文本分类应用程序,它在输入中接收一行文本,并输出一个代表其主题的标签。像任何好的机器学习项目一样,我们在不同的模型上执行许多迭代,我们希望我们的代码使用它们。当然,我们不希望一个大的 switch 语句(if-elif-elif-…-else)带有运行任何单一模型的逻辑!

出于示例的原因,所有模型共享相同的输入/输出词汇表,即相同的映射 word->input index 和 output index->label。唯一的区别在于底层模型所执行的操作。

在我们代码的第一个版本中,我们只有两个模型架构:一个前馈网络和一个 LSTM 网络,它们提供多态的 forward() 方法来封装每个模型的逻辑:

# main.py
from models import FfModel, LstmModeldef main(args):
  model_type = args.model_type
  model_path = args.model_path
  if model_type == "ff":
    model = FfModel(model_path)
  elif model_type == "lstm":
    model = LstmModel(model_path)
  else:
    raise NotImplementedError("Unrecognizer type %s" % model_type) outputs = [] with open(args.input_text, 'r') as fid:
    for line in fid:
      word_ids = convert_to_id(tokenize(line)) 
      output = model.forward(word_ids)
      outputs.append(convert_to_labels(output)) show_results(outputs)

该代码包含许多未定义的函数,以便让我们专注于本文的相关部分。在这个片段中,我们可以看到模型类型是由用户提供的参数。它用于选择哪种模型类型,由一个类表示,应该用来加载和使用我们保存的模型。注意,这两个类都被导入到主文件中。在代码片段的后半部分,我们有对 forward 的多态调用,它根据深度学习模型的类型正确地执行深度学习模型操作。最后,我们有在文本和不依赖于模型的神经网络格式之间转换输入和输出的函数。

这段代码片段成功地提供了多态行为,但它显然打破了软件设计的坚实原则之一的开/闭原则。这个原则规定我们的程序应该对扩展开放,但对修改关闭。

在实践中,这意味着每当我们想在程序中添加一个新的模型类型时,我们必须为该类型创建一个新的类,然后将其导入 main.py,最后在 if/else 语句中为其添加一个新的分支。我们的程序对扩展开放,但也对修改开放。如果我们想将允许的类型添加到我们的软件帮助中,这样用户就可以很容易地发现它们。每当我们添加一个新的类型时,我们也应该把它的名字添加到允许的类型列表中,否则会给用户带来很大的困扰。

改进上述代码的第一种方法是用一个函数替换条件分支,该函数将模型类型及其路径作为输入,并返回构建的对象(类似于工厂方法)。上面的代码将变成:

# main.py
from models import model_factorydef main(args):
  model_type = args.model_type
  model_path = args.model_path model = model_factory(model_type, model_path)

  outputs = [] with open(args.input_text, 'r') as fid:
    for line in fid:
      word_ids = convert_to_id(tokenize(line)) 
      output = model.forward(word_ids)
      outputs.append(convert_to_labels(output)) show_results(outputs)

现在模型选择被移动到 init。模型包的 py 文件,在这里我们还用对字典的调用替换了条件代码:

# models.__init__.py
__all__ = ["factory_method"]from .ff_model import FfModel
from .lstm_model import LstmModel__MODEL_DICT__ = {
  "ff": FfModel,
  "lstm": LstmModel
}def factory_method(model_type, model_path):
  return __MODEL_DICT__[model_type](model_path)

这里我们只导出 factory_method,它从字典中选择正确的类型名(在 python 中是用于创建该类型对象的工厂),构建一个对象并将其返回给调用者。字典是将条件代码简化为线性流的简单有效的方法。

有了这段代码,我们的主文件就可以忽略现有的模型类型,我们可以添加或删除类,而根本不用修改主文件。然而,我们只是将开放修改代码从 main.py 移到了这个新的 __init.py 文件中。我们通过将主文件与可能的模型修改隔离开来,实现了改进,但是 init 中的代码。出于同样的原因,py 仍然可以修改。

我们能做得比这更好吗?当我们添加一个新的模型类型时,我们可以用这样的方式编写代码吗?我们只需添加新的类和一个名称来标识它,这样就足以将它添加到 MODEL_DICT 字典中了。

事实证明,这不仅在 python 中是可能的,而且只需要一堆代码就可以实现。该机制由两部分组成:

  1. **类发现:**一种无需为类编写显式导入语句即可导入类的算法。
  2. **注册:**被发现的类自动调用的函数,被添加到字典中,如 MODEL_DICT

让我们轮流看他们。

自动发现

我们如何导入类而不为它们或者甚至包含它们的文件(模块)写一个显式的导入呢?或者更根本地说,在给定这些约束的情况下,我们如何让 Python 解释器在运行时知道它们?

这个解决方案是通过 importlib 包中的 import_module 函数实现的。来自 importlib 官方文档:

[…]实现[import](https://docs.python.org/3/reference/simple_stmts.html#import)的组件在这个包中公开,使得用户更容易创建他们自己的定制对象(一般称为导入器)来参与导入过程。

正是我们需要的!让我们看看可以添加到 init 中的代码。py 使它成为我们模型类的自定义导入器:

# Iterate all files in the same directory
for file in os.listdir(os.path.dirname(__file__)):                                 
  # Exclude __init__.py and other non-python files
  if file.endswith('.py') and not file.startswith('_'):
    # Remove the .py extension  
    module_name = file[:-len('.py')]     
    # Assume src to be the name of the source root directory                              
    importlib.import_module('models.' + module_name)

通过这四行代码,我们的软件自动导入模型包中的所有模块。当一个模块被导入时,它的代码被执行,所以我们的下一步是实现一个机制,让我们的类把它们自己添加到我们的 MODEL_DICT

子类注册

第一种方法修改了初始化子类的 Python 机制。幸运的是,Python 公开了在第一次执行类定义时调用的方法 init_sub_class。每个类都自动调用父类的 init_sub_class 并将其类型作为参数。因此,我们可以在父类中集中注册新类的逻辑:

# models.model.py__MODEL_DICT__ = dict()class Model:
  def __init_sub_class__(cls, **kwargs):
    assert "name" in kwargs
    super().__init_sub_class__()
    if kwargs["name"] in __MODEL_DICT__:
      raise ValueError("Name %s already registered!" % name)
    __MODEL_DICT__[kwargs["name"]] = cls

init_sub_class 接受一个名为 cls 的参数作为输入,这是类类型参数的 Python 约定,以及存储在 kwargs 附加库中的任意数量的键值对。我们首先检查 "name" 是否作为一个键存在于附加参数中,因为如果我们不知道用哪个名字注册一个类,我们就不能在字典中注册它。然后,如果以前没有注册过具有该名称的类,只需将它添加到字典中,否则程序会退出并显示一个错误,通知用户存在重复。

现在,我们只需要每个子类将其名称作为附加参数传递给 init_sub_class。这不是通过直接调用函数来完成的,而是在定义父类时完成的:

# models.ff_model.py
from .model import Modelclass FfModel(Model, name="ff"):
  def __init__(self, *args, **kwargs):
    # Construct object def forward(self, x):
    # Operations for computing output probabilities of x

类实现不知道注册过程,它只通过父类名旁边的赋值 name="ff "出现。该赋值以及您可能愿意添加的任何其他赋值将构成模型 init_sub_class 的**kwargs。

同样,对于 LstmModel:

# models.lstm_model.py
from .model import Modelclass LstmModel(Model, name="lstm"):
  def __init__(self, *args, **kwargs):
    # Construct objectdef forward(self, x):
    # Operations for computing output probabilities of x

init 时。py 我们需要执行发现代码并公开一个工厂方法来访问字典

# __init__.py
__all__ == ["factory_method"] import os
import importlibfrom models.model import Model, __MODEL_DICT__ def factory_method(name, path):
    return __MODEL_DICT__[name](path)

for file in os.listdir(os.path.dirname(__file__)):
    if file.endswith('.py') and not file.startswith('_'):
        module_name = file[:file.find('.py')]
        module = importlib.import_module('models.' + module_name)

寄存器功能

第二种方法是定义一个包装我们的类的“注册”函数。register 函数的内部工作有点复杂,所以请原谅我。当包含由我们的注册函数包装的类的模块第一次被执行时,它用它的参数运行包装函数。包装函数定义了一个内部函数,该函数将一个类类型作为输入,并对其进行处理。此外,内部函数可以访问包装函数中的变量,因为它是一个闭包。register 函数简单地返回它的内部函数,这个内部函数又以包装的类类型作为它的输入被立即执行。内部函数是实际注册类的函数,然后返回将由 python 解释器添加到当前名称空间的类类型。

这个过程在 init 之间再次拆分。py 文件和包含实际类的模块文件。

# models.__init__.py
__all__ = ["factory_method"]

import os
import importlib

from .model import Model

def factory_method(name):
    return __MODEL_DICT__[name]

__MODEL_DICT__ = dict()

def register_function(name):

    def register_function_fn(cls):
        if name in __MODEL_DICT__:
            raise ValueError("Name %s already registered!" % name)
        if not issubclass(cls, Model):
            raise ValueError("Class %s is not a subclass of %s" % (cls, Model))
        __MODEL_DICT__[name] = cls
        return cls

    return register_function_fn

for file in os.listdir(os.path.dirname(__file__)):
    if file.endswith('.py') and not file.startswith('_'):
        module_name = file[:file.find('.py')]
        module = importlib.import_module('models.' + module_name)

同样, factory_method 是我们的 main 调用的函数,它基本上是我们字典的包装器。请注意,在我们的内部 register_function_fn 中,我们可以执行我们通常可以在任何函数中执行的任何操作,因此我们额外检查了给定的类是 Model 的子类。这是保持基于层次结构的多态性所必需的,因为我们不能保证 register 类确实是一个模型。

另一种可能的检查可以基于鸭类型,并且将检查类中是否存在转发方法,而不是检查其类型。python 的动态性也允许我们不执行任何检查,但是当我们使用 buggy 模型时,这种勇敢会导致令人讨厌的错误出现。相比之下,在我们的注册机制中插入检查将在每次运行程序时检查所有模型的一致性。在静态类型语言中,这种检查是由类型检查器执行的,而 Python 为我们提供了更多的动态性,但也为我们清理自己的混乱提供了更多的责任。

现在,我们只需要使用 register_function 作为我们的类的装饰器,以使它的功能如上所述:

# models.ff_model.py
from models.model import Model @register_function("ff")
class FfModel(Model):
  def __init__(self, *args, **kwargs):
    # Construct object def forward(self, x):
    # Operations for computing output probabilities of x

通过使用 register_function 作为装饰器,注册机制在类的外部,因此它可以完成它的工作,然后模型类不需要任何关于注册的代码。

该示例的工作方式如下:

  • register_function 以*“ff”*作为唯一参数被调用
  • 它返回 register_function_fn 封闭绑定 name="ff"
  • cls=FfModel 调用寄存器 _ 函数 _fn
  • register_function_fn 在我们的 MODEL_DICT 中注册键值对*“ff”= FfModel*,并将 ff MODEL 返回给解释器
  • 现在可以通过函数 factory_method() 在应用程序中使用 FfModel,并且不需要显式导出。

第一次很难掌握像这样的注册功能的工作原理,主要是因为所有的操作都是在启动阶段执行的。然而,如果你能遵循上面的解释,你将最终掌握一个强大的工具,它的应用远远超出了这个例子。

与前面的解决方案相比,register 函数的难度为我们带来了更多的灵活性,因为它没有将我们限制在类的层次结构中,也可以用于函数而不是类。此外,从单一责任原则 (SRP)的角度来看,这段代码更好,因为注册子类的责任被交给了一个只负责注册子类的函数,而不是父模型类。

结论

类发现是一个强大的工具,它通过 Python 项目的两个组件之间的抽象接口提供多态行为。

最终的设计在不同的包之间有更少的显式依赖,而在同一个包内有更多的依赖。然后,它强制不同包及其内部内聚性的解耦。

此外,我们还看到了这种模式是如何产生更加尊重可靠原则的代码的。

当一个包主要由相同层次结构中的类组成时,我推荐使用它,这样就可以用不同的实现达到相同的目的。只有在定义代码的地方才需要修改代码,这让我们在开发的时候心情愉快。

承认

我从脸书的 FAIRseq 开源项目中学会了如何使用注册函数。你可以在这里找到他们当前的实现或者从我的老叉找到一个更简单的版本。

init_sub_class 方法的一个很好的解释可以在堆栈溢出中找到。

Python 支持的蒙特卡罗模拟

原文:https://towardsdatascience.com/python-powered-monte-carlo-simulations-fc3c71b5b83f?source=collection_archive---------6-----------------------

用 SciPy 的概率分布进行情景分析

蒙特卡洛,图片由卢卡·阮来自皮克斯拜,免费用于商业用途

本教程将演示如何在 Python 中建立蒙特卡罗模拟模型。我们将:

  • 使用 SciPy 的内置分布,具体来说:正态、贝塔、威布尔;
  • 为 beta-PERT 分布添加新的分布子类;
  • 用拉丁超立方体抽样抽取随机数;
  • 并建立三个蒙特卡罗仿真模型。

0.属国

除了我们的全天候包 pandas 和 numpy 之外,该脚本还导入了 SciPy 的 stats 库。

蒙特卡罗模拟的概念

图片由汉斯·布拉克斯米尔从皮克斯拜获得,免费用于商业用途

蒙特卡罗(MC)方法是一种模拟技术,为模型的输出变量构建概率分布,其中一些输入参数是随机变量。MC 方法有时被称为多概率模拟技术,因为它整合了多个随机变量,而这些变量的综合效应很难用封闭形式的方程来描述。

20 世纪 40 年代末,约翰·冯·诺依曼和斯坦尼斯劳·乌姆在洛斯阿拉莫斯实验室工作时发明了 MC 方法。当他们试图应用确定性方法计算中子碰撞时,他们走进了死胡同。乌姆的想法是使用随机抽样,由早期的“超级计算机”ENIAC 支持,以获得*似解。政府保密条例规定了一个代号;冯·诺伊曼和乌姆的一位同事建议把这个地方命名为蒙特卡洛,乌姆的叔叔在二战前经常去那里的赌场:他“只是必须去蒙特卡洛”赌博。( U 日上午)

MC 模拟问题的一个例子:一个企业想要模拟一个新产品的未来成功,并建立如下的模拟模型:

  • 预期销售量,v,作为一个随机变量,遵循 beta-PERT 分布,从 3 点估计值得出;
  • 产品的价格 p 将通过谈判确定——一些客户可能会要求批量折扣或提前付款折扣;计划者决定将公司将实现的*均价格的不确定性建模为*均值为 20、标准差为 2 的正态随机变量;
  • 产品的原材料成本 m 将受到即将到来的供应商价格上涨的影响,由另一个正态分布来估计;
  • 该模型应该提供多种结果:收入,利润,总成本。

一般来说,模拟模型采用被定义为随机变量的输入参数;然后,它返回每个输出变量的概率分布,每个变量都是其输入参数的函数。该函数可以是简单的和或积、输入的指数运算,或者——我们将在示例中看到——包含嵌套随机变量的高阶函数。目标变量将显示一个密度函数,描述输入变量的分布如何相互作用。

仿真模型通过从输入分布中采样来导出输出分布。1,000 或 100,000 次迭代提供了描述每个输入变量行为的数据点。输出变量的等式将这些输入转换为输出变量的尽可能多的样本。一旦我们获得了这个样本,我们就可以像任何内置的 SciPy 分布一样检查它的模式——它的*均值、分位数和离群值倾向。

2.拉丁超立方体采样

NumPy 的 random 库提供了用统一随机数填充数组的方法。SciPy 的每个分发对象都带有生成随机变量的 rvs() 方法。然而,蒙特卡罗模拟需要大量的随机数,对于这种情况,分层抽样方法,如拉丁超立方体抽样(LHS) 是更好的方法。

LHS 将样本空间分成不重叠的单元,每个单元具有相等的概率。LHS 确保从样本空间的完整范围内更均匀地抽取随机数。它防止采样随机数的聚集,这会扭曲频率曲线。LHS 样本将导致更精确的模拟结果。这将导致更低的标准误差,更少的迭代。

出于演示的目的,我们使用 SciPy 的 LatinHyperCube 采样器创建了一个由 10 个随机数组成的小数组。

制服(0;1)随机数在生成之后,可以被重新缩放以填充更大的范围,例如在 0 和 100 之间。然而,对于我们的目的,0 到 1 之间的标准随机数满足要求。

在下一章中,我们将使用 LHS 样本来生成特定分布的随机变量:PERT、正态和威布尔函数。

3.模拟中模拟不确定性的概率分布

3.1 用于专家评估建模的 PERT 分布

我的上一篇文章“用 beta-PERT 分布对专家评估建模”是一篇教程,解释了 PERT 分布 ( Python 场景分析:用 beta-PERT 分布对专家评估建模|走向数据科学)。

如果一个领域、过程或行业专家可以为一个随机过程提供所谓的三点估计(三点估计),包括最坏情况、可能情况和最好情况的结果,那么 pert 函数就可以将这些点连接起来,这是非常确切的。它会将这些点转化为概率分布,我们可以将它分配给模拟模型中的随机变量。

SciPy 的 123 个分布目录不包含 PERT 函数。因此,我们将其创建为继承自 SciPy 的 rv_continuous 父类的新子类。

对于我们的例子,我们用四个参数来实例化 PERT 子类,这四个参数预测新产品的销售量。

我们选择这个 PERT 分布来模拟预期的销售数量,最有可能的值是 12,000 个单位,最小为 8,000 个单位,最大为 18,000 个单位。

其*均预期值将为 12,333 个销售单位。它有一个小的正偏度,仍然类似于正态分布;和-0.62 的中度负过度峰度,这使其成为一个宽峰或大体积分布,其尾部的异常值比正态分布少。

第 3 行抽取 N=10,000 个均匀随机变量的 LHS 样本。然后第 4 行将该数组作为其输入参数。*百分点* *函数 ppf()* 将 10,000 个标准统一数字解释为概率,并计算 10,000 个 PERT 分位数,用这些分位数填充数组 randP。

直方图显示了模拟的 PERT 分布的形状。

3.2 正态分布

我们应用相同的方法(减去创建新的分布子类的需要)从正态分布中抽取 10,000 个 LHS 样本,我们期望这些样本反映销售价格和原材料单位成本中固有的不确定性。*均售价将为€ 20,标准差为€ 2。

第二个正态分布模拟原材料单位成本,*均值为€ 13,标准偏差为€ 1.40。

3.3 模拟 1-模拟随机变量的总和与乘积

我们创建了三个随机变量,代表三个因素的不确定性,这三个因素将决定新产品产生利润的机会或损失的风险:

  • 体积 v 的 1x PERT
  • 2x 正常——对于销售价格 p 和原材料单位成本 m;

我们假设其他成本,那些与供应商价格无关的成本,可以通过一个确定性变量 o 来反映。

为了计算模拟模型的目标变量——产品的毛利 GP,我们将随机变量联系在一起,如下所示:

  • v =数量,p =价格,m =原材料单位成本,o =其他单位成本
  • GP = v * (p — m — o)

作为第二个产出变量,我们可以模拟与 GP *行的收入 R。一般来说,一旦我们定义了输入随机变量,我们就可以用任意多的输出变量建立模拟模型。每个输出变量都可以由一个或多个输入随机变量组合而成,并由它自己的 10,000 个变量的数组来描述。

  • R = v * p

这就建立了我们简单的模拟模型。

为了查看返回毛利随机变量 GP 的分布的数组的属性,我们编写了一个函数 dist_properties() ,该函数将读取数组并返回其矩和选定的分位数。

对于偏度和峰度,我们使用 SciPy 的 skew() 和峰度 () 函数,其结果需要借助 numpy 的 asscalar() 转换方法从数组转换成*面数。我们在字典 dict_moments 中收集它们的值,并在其中添加度量的名称。第 14 行中的 list comprehension 逐行打印字典的内容。

然后我们计算第 17ff 行中的分位数,将它们收集到另一个字典中, dict_quantiles ,并使用第 27 行中的 list comprehension 将其打印出来。

我们合并两个字典,矩和分位数,形成一个综合字典, dict_metrics

*均利润将达到€ 49,250,接*中位数。

偏度和峰度都相当适中,这意味着与正态分布相比,有适度的离群值倾向。

如果销售价格和原材料单位成本同时趋向于最坏的结果,列表中的分位数揭示了新产品将产生损失的 5%的风险。利润将以 90%的概率超过€ 10,731(10%的分位数)。

在下一章中,我们将更进一步,建立一个高阶仿真模型。虽然第一个模型仅从随机变量的和与积中组装输出变量,但我们现在将开发一个包括嵌套随机变量的模拟模型。

3.5 模拟 2 —嵌套分布

如果你读过我以前的文章(概率分布介绍和用 Python 的 SciPy 拟合分布),你会记得我们已经应用了威布尔分布来解决故障前时间部件寿命问题。一个部件的寿命通常可以由一个威布尔分布来模拟,其形状反映了故障率和一个设定所谓的特征寿命的比例参数。

第一艘前往火星的宇宙飞船将配备电子电路板,其典型寿命可能为 5 万个工作小时。为了估计船舶设计中需要的冗余度,我们将模拟多少小时后所有电路的哪一部分会烧坏。

让我们假设,非常符合现实,威布尔分布的两个参数——形状和特征寿命——是不确定的。因此,我们将这些分布参数估计为它们自己的随机变量。威布尔形状及其标度可以在由它们的分布模式设定的边界内波动。

  • 对于形状参数,工程团队估计它应该正态分布在*均值 1.5 左右,标准偏差为 0.1。
  • 对于特征寿命,我们采用工程团队在 45,000、50,000 和 60,000 小时运行时提供的最大、可能和最小值的三点估算值。然后我们推导出一个 PERT 分布来反映不确定性的范围。

两个辅助函数通过拉丁超立方体采样抽取 10,000 个随机变量。

  • wei_shp() 返回正态分布的形状参数。
  • wei_charlife() 返回特征寿命的 PERT 变量数组。
  • 我们将 Weibull 位置参数(也称为等待时间)设置为 0,这意味着产品故障会在生产完成和质量测试开始后立即出现。

在第 7 行中,我们将随机变量组合成一个威布尔输出变量,表示故障前的时间。威布尔函数调用辅助函数来获取其形状和比例参数。目标变量 rand_CL 将保存一个由 10,000 个模拟输出组成的数组,这些输出以小时为单位表示组件的寿命。

有三种嵌套分布的结果:PERT 和正态随机变量充当威布尔分布的参数。我们可以将结果标记为灵活的或随机的威布尔分布,而不是参数为点值的固定分布。

对于如何查看结果的属性,我们可以在两个选项之间进行选择:

  • 将它作为一个数组进行分析,就像我们对之前的模拟结果所做的那样
  • 应用 SciPy 的 rv_histogram 类,它将输出数组打包成一个直方图,并将其转换成一个“真实的”SciPy 概率分布,为此我们可以调用像 pdf 和 ppf 这样的分布函数。

让我们来看看直方图类。

图表用蓝色显示了我们在数组 rand_CL 中模拟的装箱寿命。在 orange 中,它绘制了概率密度函数,这个函数是由 rv_histogram 类从数组中得到的。

函数 histdist_properties() 计算 histdist 概率分布的属性。这个函数在某些方面与我们之前写的函数*distribution _ properties()*不同。 rv_histogram 类并不是在所有情况下都提供相同的分布属性。例如,最小值和最大值必须从。*支持()*方法。

我们调用 histdist_properties() 函数并获得列表度量。数据帧不仅包含累积分布函数和分位数,还包含一列随机变量。采样功能*。rvs()* 使我们能够从我们的输出分布中抽取一些随机数——或者如果需要的话抽取几千个,尽管没有 LHS 方法的分层抽样。

上面,我们分析了 rv_histogram 从数组中导出的连续分布。如图所示,分布曲线与阵列数据并不完全相同。

下面,我们对 10,000 个输出数字的原始数组调用函数 dist_properties() ,并解释矩和分位数。

  • 部件的*均寿命将达到 45,647 小时;
  • 寿命将是右偏的,自然地,异常值将持续超过 100,000 小时;
  • 峰度是独特的——分布是薄峰的,其尾部有长期存活成分的倾向;
  • 11,076 小时后,10%的部件将会烧坏。

任务控制中心告诉我们 11,000 小时的操作将标志着关键的阈值。在完成这些使用时间后,电路将不再是关键任务。分位数意味着,就电路板而言,飞船在设计时应该考虑到至少 10%的冗余度:增加备用电路板,这些电路板将自动打开,作为那些开始出现故障的电路板的替代品。

所以我们的太空船几乎准备好发射了。

图片作者彼得 H 来自皮克斯贝,免费用于商业用途

3.6 模型 3:固定参数模拟

作为最后一项练习,让我们检查威布尔模型对其形状和特征寿命的不确定性有多敏感。

我们将使用“固定形式”的威布尔分布进行蒙特卡罗模拟。

  • 形状参数将固定为*均值 1.5;
  • 特征寿命:PERT 分布的*均值:50,500 小时。

我们再次调用 dist_properties() 函数,并将其指标插入到数据帧中,这样我们就可以并排比较固定威布尔和之前嵌套的“灵活”威布尔。

正如预期的那样,我们看到固定威布尔变量的标准偏差较低,这是在形状和比例参数没有不确定性的情况下产生的。但是差别很小。显然,柔性威布尔模型的形状和尺度参数的不确定性非常接*于我们在固定威布尔模型中使用的*均值。较低的偏度和峰度显示出异常值和不对称性的倾向有所降低。

参数具有不同值或不同分布的模拟可能会显示由增加的不确定性引起的较大差异。

4.结论

今天的教程到此结束。

我们浏览了三个蒙特卡罗模拟的例子,它们是用 SciPy 库提供的工具箱创建的。

  • 第一个模型——利润模拟——展示了随机变量的简单求和与乘积。
  • 第二个模拟——威布尔失效时间——解释了嵌套随机变量的概念,即概率分布的参数本身就是随机变量。
  • 第三次模拟大胆假设分布的参数是精确已知的。如果我们在模拟模型中忽略不确定性的来源,我们可能会低估可能结果的传播,除非不确定性浓缩为参数的*均值。

Jupyter 笔记本可以从 GitHub 下载:h3ik0th/montecallosim:用 SciPy(github.com)进行蒙特卡洛模拟

  • 蒙特卡洛,图片由卢卡·阮来自皮克斯拜,免费用于商业用途
  • 蒙特卡洛赌场,图片由来自 Pixabay 的 Hans Braxmeier 提供,免费用于商业用途
  • 由彼得·H从皮克斯拜工厂,免费用于商业用途
  • 所有其他图片:作者

Python 轻松打印彩色文本

原文:https://towardsdatascience.com/python-printing-colorful-outputs-with-ease-b4e2a183db7c?source=collection_archive---------14-----------------------

如何在 Python 中使用 Console.log、console.warn 和 console.error

让我们用一些颜色来增加那些无聊的控制台输出的趣味吧!(图片由作者提供)

上图两张截图哪个更好看?我肯定会选择正确的!这些颜色会立即将你的注意力吸引到重要的事情上,并给你额外的信息,让你一目了然。

本文涉及两个主题:首先,我们将了解如何在终端上打印颜色

  • 终端中打印颜色的工作原理
  • py-控制台;一个 Python 包,允许您轻松打印彩色输出

在这篇文章的结尾,你将能够打印彩色输出,那么我们还在等什么呢?我们来编码吧!

1.了解打印

首先,我们将检查终端需要什么来打印彩色文本。这将向您展示如何在最底层打印彩色文本。在接下来的部分中,我们将用更容易使用的方法来代替这种方法。

在 Python 中执行print功能时,文本出现在 终端 中。我们的目标是给这个值添加一些数据,以便我们的终端知道在显示文本时添加一些颜色。这些额外数据是ANSI escape character sequences,用于控制光标位置、颜色和字体样式,例如在终端中。终端将这些序列解释为命令,而不是仅仅显示的文本。

在下面的例子中,我们使用 Python 告诉终端打印文本“这是我们的消息”,文本为白色,背景为黄色:

print('\x1b[0;39;43m' + 'This is our message' + '\x1b[0m')

我们控制台中的输出(图片由作者提供)

ANSI 代码的第一段(\x1b[0;39;43m)是终端根据指定参数给所有后续文本着色的命令。我们可以在0;39;43部分用整数指定样式、文本颜色和背景颜色。结尾的第二段 ANSI 代码(\x1b[0m)告诉终端重新设置颜色选项,这样以后的文本就不再是白色和黄色了。

这种方法的问题是,这种方法在每个*台上都不统一;ANSI 代码可以在 Unix 和 MAC 上运行,但在 Windows 上不受支持。我们也不想打出这些具体的、难以理解的代码。让我们找到一种更简单的跨*台方法!

2.py-控制台;用 Python 轻松打印颜色

Py-console是我创建的一个软件包,它使打印颜色变得超级简单。它模仿 JavaScripts 众所周知的方法,如console.logconsole.warnconsole.error等。它还增加了一些更多的功能。

想创建自己的 Python 包吗?→查看 本文 获取说明。也可以查看 这篇文章 来创建你自己的私有包)

装置

首先我们将安装 py-console:

pip install py-console

记录、警告、错误、成功、信息

安装后,我们可以简单地调用 5 个函数之一,以指定的颜色打印文本:

from py_console import consoleconsole.log("log")
console.warn("warn")
console.error("error")
console.success("success")
console.info("info")

这会产生:

py-console 简单功能的输出(图片由作者提供)

切换时间和设置时间戳

我们还可以通过为控制台或 per 方法指定时间戳来为输出添加时间戳:

console.setShowTimeDefault(True)
or
console.log('log', showTime=True)

这将在您要打印的字符串前添加时间:

带时间戳的日志记录(图片由作者提供)

此外,您还可以设置时间格式。点击查看关于此的更多信息。下面的示例将时间格式设置为包括年、月和日。****

console.setTimeFormat('%y-%m-%d %H:%M:%s')

更严重的警告

在方法中,我们可以指定一个“severe”标志,以便我们有更清晰的输出,反转文本颜色和背景颜色:

from py_console import consoleconsole.log("log", severe=True)
console.warn("warn", severe=True)
console.error("error, severe=True")
console.success("success, severe=True")
console.info("info, severe=True")

这将产生以下输出:

这些消息有点严重(图片由作者提供)

突出

最后,我们还可以突出显示字符串的某些部分。看看这个我们使用 console.warn 函数的例子:

在同一个控制台中使用不同的文本颜色和背景颜色

结论

在本文中,我们主要讨论了终端如何打印彩色文本来更清晰地显示重要信息。然后我们看了一下的 py-console** 以及它如何让彩色输出变得非常容易。**

我希望我已经阐明了如何指示终端打印颜色,并且我希望我已经为您留下了一个创建漂亮的控制台输出的不错的包。如果你有建议/澄清,请评论,以便我可以改进这篇文章。

同时,请查看我的其他关于各种编程相关主题的文章:

  • 用 FastAPI 用 5 行代码创建一个快速自动记录、可维护且易于使用的 Python API
  • 从 Python 到 SQL——安全、轻松、快速地升级
  • 创建并发布你自己的 Python 包
  • 创建您的定制私有 Python 包,您可以从您的 Git 库 PIP 安装该包
  • 面向绝对初学者的虚拟环境——什么是虚拟环境以及如何创建虚拟环境(+示例)
  • 通过简单升级,显著提高数据库插入速度
  • 在这里阅读更多关于py-console和更多关于 colorama 这里

编码快乐!

—迈克

页(page 的缩写)学生:比如我正在做的事情?跟我来!

让您的 Python 项目结构更优雅的 7 种方法

原文:https://towardsdatascience.com/python-project-structure-best-practices-d9d0b174ad5d?source=collection_archive---------23-----------------------

以下是易于管理、可伸缩且易于理解的 python 项目结构的最佳实践

完美 Python 项目结构的蓝图。——照片由来自 Pexels 的设计生态学家拍摄

伟大的项目从一个单一的文件脚本开始,并演变成一个社区维护的框架。但是很少有项目能达到这个水*。大多数,不管它们对其他人是否有用,最终都不会被任何人使用。

让你的项目对其他人来说方便(或者痛苦)的关键因素是它的结构。

什么是运行良好的完美 Python 项目结构?

  • 伟大的项目总是<#5269>****。使用 git(或 mercurial。)
  • 他们应该有一个 依赖管理 系统。Virtualenv(或康达)不是一个。
  • 他们有自动化的 干净的代码实践 。让丑陋的编码对你的团队成员来说是不可能的。
  • 伟大的项目总会有一个 自述 并给出更多的脉络。
  • 应该有一个 配置文件 (一个 YAML 或者一个 TOML 文件)。将软件参数与硬编码分开。
  • 秘密应该在一个的环境里(。env)文件。你不想让世界知道你的秘密。
  • 他们应该有整洁的 文档 (可选。)

本文将介绍所有这些,并设置您的 python 项目以获得最大的可维护性。如果这看起来很难,你可以直接使用我为你创建的 蓝图

让每个项目都成为 git 仓库。

您可以通过一行命令将项目变成 git 存储库。做这个不会花一分钟。

git init

但是我们不常这样做。在大多数情况下,我们倾向于认为我们为一个简单的问题写了一个简单的脚本。没关系。

对你来说简单的对别人来说就复杂了。地球另一端的某人正在解决一个问题,您的简单脚本就是解决方案。

此外,你的简单问题可能会吸收其他简单问题,并变得可怕。

随着它的增长,维护您的代码变得越来越困难。你不知道你做了什么改变,为什么要做这些改变。如果你是一个团队,那么接下来的问题是谁和什么时候做了这些改变。

突然,你开始担心你最初写的剧本,而不是为它感到自豪。

开始每一行代码,就好像它是下一个脸书的开始。你需要一个版本控制系统来做这件事。

作为第一步,让您的项目成为一个 git 存储库,并包含一个. gitignore 文件。你可以使用在线工具 gitignore.io 生成一个忽略文件。

.
├── .git
│   ├── <Git managed files>
│  
├── .gitignore

随着您的进展,请确保使用说明性消息提交您的更改。提交是不同时间的检查点。它们确实是你的软件的版本,你可以在任何时候检验。

如何写好提交消息?

一个好的提交消息应该完成句子,“如果应用,这个提交将……”它们应该是句子的大小写,但是没有尾随的句号。提交消息的最佳长度约为 50 个字符。

以下是使用 git CLI 的提交消息示例。

git commit -am 'Print a hello world message'

你也可以创建更多的细节。您可以在没有提交消息的情况下运行git commit

此操作将打开一个编辑器,您可以在其中添加多行提交消息。然而,使用上述约定来创建提交消息的标题。您可以使用空行来分隔邮件的标题和正文。

Print a hello <user> message

Print a hello world message and a hello <user> message

The main function was hardcoded with 'hello world' message.
But we need a dynamic message that takes the an argument and greet.

Amend the main function to take an argument and string formating to
print hello <user> message

这些提交消息约定将使浏览 git 日志中您所做的所有更改变得容易。

用干净的提交消息清理 git 历史——来自作者的博客

使用依赖管理工具。

大多数开发人员,尤其是新开发人员,没有对项目依赖给予足够的重视。首先什么是依赖管理?

您开发的软件可能依赖于其他开发人员创建的包。反过来,它们可能依赖于几个不同的包。这种模块化的方法有助于快速创建软件产品,而不需要一直重新发明轮子。

即使在同一个项目中,依赖关系在不同的环境中也会有所不同。您的开发团队可能有一组不属于生产系统的依赖项。

一个健全的依赖关系管理系统应该能够区分这些集合。

Python 开发人员使用虚拟(或 conda)环境来安装项目依赖项。但是 Virtualenv 不是一个依赖管理工具。它没有上面讨论的好处。这只会有助于将项目环境与您的系统隔离开来。

对于 Python 项目来说,诗歌是一个完美的依赖管理工具。它允许你,

  • 分离开发和生产依赖关系;
  • 分别为每个项目设置 Python 版本;
  • 创建软件的入口点;
  • 帮助您将其打包并发布到诸如 PyPI 之类的存储库中。

诗歌不是虚拟的替代品。它使用方便的实用程序命令创建和管理虚拟 env。

如果你喜欢这个想法,我发表了一篇关于如何使用诗歌有效管理项目依赖的完整教程。

在您的 Python 项目中自动化干净的代码实践。

Python 是最简单的编程语言。它接*自然语言,但在应用上却很强大。

但这并不意味着你的代码总是可读的。您可能最终会写出太长的代码,并且其风格对于其他人来说太难理解了。为了解决这个问题,Python 引入了一个叫做 PEP 8 的通用标准。

PEP 8 是 python 程序员简洁一致地编码的一套指南。它讲述了,

  • Python 类、函数和变量的命名约定;
  • 正确使用空白;
  • 代码布局,如最佳线路长度,以及;
  • 关于评论的约定;

尽管这一准则为 Python 程序员解决了一个大问题,但在大型项目中手动维护这一点很有挑战性。

幸运的是,像 black 和 autopep8 这样的软件包可以很容易地通过一行命令来完成。这一行格式化了 blueprint 文件夹中的每个文件。

black blueprint

Autoflake 是另一个帮助你去除脚本中无用变量的工具。我们声明但不使用的变量给阅读代码带来了不便。下面一行很神奇。

autoflake --in-place --remove-unused-variables blueprint/main.py

最后,我想提一下 isort ,这是一个优化导入的 Python 包。

isort blueprint/main.py

所有这些包在一行中清理你的代码。但是,即使这样,每次修改脚本时运行它也比我们想象的更有挑战性。

这就是为什么我更喜欢 Git 预提交钩子。

使用预提交挂钩,您可以配置为在每次提交更改时运行 black、autoflake 和 isort 来格式化您的代码库。

如何配置预提交钩子自动格式化 Python 代码?

您可以使用 poems add 或 pip 安装预提交包。您的项目根目录中应该有一个.pre-commit-config.yaml文件。您可以配置在每次提交之前运行哪些钩子。然后,您必须将预提交安装到 git 存储库中。可以通过从项目根目录运行预提交安装命令来实现。

poetry add pre-commit
# Create the .pre-commit-config.yaml file
poetry run pre-commit install

下面是您的.pre-commit-config.yaml文件应该是什么样子。

repos:
  - repo: local
    hooks:
      - id: autoflake
        name: Remove unused variables and imports
        entry: bash -c 'autoflake "$@"; git add -u' --
        language: python
        args:
          [
            "--in-place",
            "--remove-all-unused-imports",
            "--remove-unused-variables",
            "--expand-star-imports",
            "--ignore-init-module-imports",
          ]
        files: \.py$
      - id: isort
        name: Sorting import statements
        entry: bash -c 'isort "$@"; git add -u' --
        language: python
        args: ["--filter-files"]
        files: \.py$
      - id: black
        name: Black Python code formatting
        entry: bash -c 'black "$@"; git add -u' --
        language: python
        types: [python]
        args: ["--line-length=120"]

就是这样。现在试着对你的代码做一些修改并提交它。你会惊奇地发现它是如何自动纠正你的编码风格问题的。

使用配置文件来分隔项目参数。

配置文件就像应用程序的中央控制面板。您的代码的新用户只需更改配置文件就可以运行它。

配置文件中包含什么?

我们知道硬编码静态变量是一种不好的做法。例如,如果你需要设置一个服务器 URL,你不应该直接把它放在代码中。相反,最合适的方法是将它放在一个单独的文件中并从中读取。如果你或者其他人想改变它,他们只需要做一次,他们知道在哪里做。

在早期,我们习惯于从文本文件中读取配置。我甚至还用过 JSONs 和 CSV。但是我们有更先进的替代方法来管理配置。

一个完美的配置文件应该易于理解并允许注释。我发现 TOML 文件对这件事来说是不可思议的。poems 已经创建了一个 TOML 文件来管理它的配置。因此,我不必创建一个新的。

你可以用toml python 包读取一个 TOML 文件。只需要一行命令就可以将配置文件转换成字典。

下面是如何读取一个 TOML 配置文件。

  1. 安装toml包。如果你使用诗歌来管理依赖关系,你可以使用add命令来安装它。如果不是*原老pip作品。
poetry add toml
# If you're still using pip and virtualenv,
# pip install toml

2.创建(或编辑,如果已经存在的话)一个 TOML 文件。你可以用任何名字。如果你使用诗歌,它会创建一个叫做pyproject.toml的。

[app]
name='blueprint'

3.将其加载到您的 python 项目中。

import toml
app_config = toml.load('pyproject.toml')# now you can access the configuration parameters
print(app_config)# {'app': {'name': 'blueprint'}}

将机密存储在环境文件中

一组机密项目不应该放在您的代码库中。常见的做法是将它们放在一个. env 文件中,并在运行时读取它们。您也不需要将. env 文件提交到代码库中。

您可以使用这个文件来存储 API 密钥和数据库凭证等信息。

如果您没有将它提交到您的存储库,那么使用您的配置文件来存储机密是非常好的。此外,您还可以使用。如果没有太多复杂的配置,可以将 env 文件作为项目配置文件。

环境文件和配置文件之间的一个关键区别是如何读取它们的值。您可以使用 Python 内置的os模块在项目的任何地方访问环境变量。但是配置文件值并不是对项目的每个模块都可见的。你必须要么读取每个模块上的文件,要么只读取一次,然后将它和函数参数一起传递。

但是我强烈建议使用两个独立的文件,因为它们有不同的用途。配置文件可以方便地配置您的项目,不需要任何硬编码。env 文件存储机密。

您可以使用。gitignore 来阻止您的 env 文件意外地潜入您的存储库。这是你在第 0 天应该做的事情之一。

下面是如何用 Python 创建和读取环境文件。

  1. 在项目根目录下创建一个. env 文件
SECRET_KEY='R9p9BRDshkwzpsooPEmZS86OWjWxQvn7aPunVexFoDw'

2.安装 python-dotenv。

poetry add python-dotenv
# pip install python-dotenv

3.将 env 文件加载到项目中。

from dotenv import load_dotenvload_dotenv()
# If your env file is different from .env you can specify that too,
# load_dotenv('.secrets/.environ')

4.从项目中的任何位置访问环境变量。

import osprint(os.getenv('SECRET_KEY'))
# R9p9BRDshkwzpsooPEmZS86OWjWxQvn7aPunVexFoDw

环境文件是一个古老的约定。因此,大多数技术都预先支持它们。此外,您可以直接在操作系统上设置环境变量。

使用自述文件,并给出额外的背景。

如果你总是给阅读你的代码的人一些上下文,这将会有所帮助。它是关于什么的,你为什么要写它?

自述文件是项目的简短文档。它应该包括让另一个人在没有你的帮助下在他们的系统上设置你的项目的说明。

自述文件通常是降价文件。GitHub、Bit Bucket 和 GitLab 正在使用它在项目存储库上呈现样式化的文档。

Markdown 添加了一些约定,使普通文本显得特别。例如,你可以在一行前面加一个#标记作为标题,加一个##标记作为副标题。这里有一个备忘单,可以了解更多关于减价的信息。

记下备忘单,写出更好的自述文件——来自作者的博客。

用附带文档帮助读者(可选。)

你不必有一个多页的 web 应用程序来记录每个项目。但是有一个是个好主意。因此,我将它设为可选。

自述文件用于保存应用程序的基本信息。文档有助于给出项目的具体细节。

例如,您可以在自述文件中讨论软件包的安装和配置。但是不推荐谈论应用程序中的 101 API 端点。您可能需要在 HTML 文档中更好地组织它,并单独存放它。

Python 有几个创建 HTML 文档的优秀工具。Mkdocs(和它的材料主题)斯芬克斯和 Swagger 是最受欢迎的选择。你如何选择合适的文档工具?

如果您的应用程序涉及许多 API 端点,Swagger 是生成文档的最佳方式。如果没有,Mkdocs 工作良好。使用 Mkdocs,您可以创建自定义页面。您也可以使用mkdocstring扩展名来转换您的文档字符串。但是在我看来,你很少需要为你的代码创建文档。你的代码本身就是一个文档!

下面是如何在 Python 项目中使用 Mkdocs

1.您可以将 mkdocs 安装为一个开发依赖项。在生产系统中你不需要它。

poetry add -D mkdocs-material

2.编辑 index.md 文件(这也是一个降价文件。)

mkdocs new docs
cd docs

3.启动 mkdocs 服务器,在浏览器上查看它。

mkdos serve

你知道你可以用 GitHub 页面免费托管你的文档吗?

如果您有 GitHub 帐户,您也可以托管您的文档,并将您的主分支推送到 GitHub 存储库。它需要一个(并且只有一个)命令和几秒钟。

mkdocs gh-deploy

上述命令将构建文档的静态版本,并将其托管在 GitHub 页面上。托管的 URL 通常类似于

https://<Your username>.github.io/<Your repository name/

你也可以让它在你的主站点的自定义域或子域上运行。这里有一个来自 GitHub 的自定义域名的指南。

你可以在这里看到项目蓝图的托管文档。

如何使用 Python 项目蓝图?

我创建了几个 GitHub 库,您可以将它们作为 Python 项目的起点。第一个是一般 Python 项目的蓝图,另一个是专门针对 Django 应用程序的。

Python 项目蓝图

Django 项目蓝图

您可以克隆存储库并开始工作。每当你认为你应该把它放在远程 GitHub(或者 Bitbucket,Gitlab)上的时候,你可以创建一个远程存储库,并把它和你的本地存储库连接起来。下面是怎么做的。

git clone git@github.com:thuwarakeshm/blueprint.git git remote set-url <Your New Repository>

但是有更好的方法。

转到我上面给出的 GitHub 链接,你会在右上角看到一个按钮,上面写着“fork”分叉允许您创建您可以拥有的存储库的副本。您可以在完全不影响原始源的情况下进行更改。

因此,一定要派生存储库并将新的存储库克隆到您的本地计算机上。

这些存储库是用我上面讨论的所有最佳实践构建的。但是你可以随意把它变成你的。你可以改变任何你想改变的东西和你想改变的方式。

最后的想法,

Python 是一种优雅的语言。但是我们需要更多的训练来完成优秀的项目。如果我们把大部分都自动化了呢?

那是我们刚刚讨论完的。

我们讨论了拥有一个 git 存储库对于所有项目的重要性和原因。然后我们讨论了用诗歌管理依赖性。我们还学习了如何使用自述文件、创建文档、管理配置和环境文件。

上面提到的每一个练习本身都值得一个 30 天的课程。但是我相信这篇文章已经给出了关于这些技术以及我们为什么使用它们的想法。

一旦您意识到这些实践的好处,您就会想要创建支持它们的项目。我已经为你描绘了一幅蓝图,让你不必从头开始。

这个指南是基于我的理解和经验。如果你有什么要补充或改正的,我很乐意和你讨论。

感谢阅读,在 LinkedInTwitterMedium

还不是中等会员?请使用此链接 成为 的会员,因为,不需要你额外付费,我为你引荐赚取一小笔佣金。

Python 项目的结构 P2C

原文:https://towardsdatascience.com/python-projets-structure-p2c-b2f92ab15c55?source=collection_archive---------31-----------------------

在 Unsplash 上由 Hitesh Choudhary 拍摄的照片

从管道到代码

作为一名程序员,我发现编码是一门艺术。当从事一个复杂的项目时,为了开发一个连贯、可靠和可持续的代码,需要遵循许多步骤,这些代码可以被其他贡献者阅读和恢复:

作者图片

  • 首先,为了满足正确的需求,理解问题是至关重要的
  • 这个项目可以被分割成多个子项目,这些子项目有助于完成任务,尤其是协作
  • 您的管道是您的子项目的直接结果
  • 代码的结构应该遵循相同的分段,这样您就可以拥有相同的管道和技术逻辑
  • 为了有效地开发您的代码,可以在每个片段中遵循循序渐进的步骤

在这篇文章中,我们将看到如何从一个业务需求到一个简单易懂的全功能 python 代码。

目录

摘要如下:

  1. 项目示例
  2. 设计机构
  3. Python 中的导入

项目示例

为了便于说明,我们将考虑以下业务需求:

作为一名高速公路经理,我希望使用给定的路线对车辆进行每日计数。为了回答这一需求,一个数据科学团队被投入到该项目中,并决定使用一个固定的摄像头,并计数车牌的唯一编号。** (其中一个建议)**

这个想法可以看作是几个步骤的顺序*(其中一个简化的分解)*:

  • 车辆检测
  • 车牌检测
  • 车牌的光学字符识别

因此,下面的管道:

作者图片

项目的组织

给定上面的管道,可以如下组织代码:

|--**data/** *#useful to store temporary files for instance*
|--**Tests/** *#hosts the functional unit testings of your code/api*
|--**notebooks/** *#helpful for testing and developping and debugging*
|--|--develop.ipynb
|--**weights/** #weights are kept a part for easier manipulation|--**counthighwaypy/** *#folder hosting your entire code* |--|--***detectvehicle/*** *#1st brick of your pipeline*
|--|--|--detect_vehicle_main.py
|--|--|--detect_vehicle_utils.py
|--|--|--detect_vehicle_conf.py
|--|--|--Tests/ #independant unit testings relative to 1st brick
|--|--***detectlicenseplate/*** *#2nd brick of your pipeline*
|--|--|--licence_plate_main.py
|--|--|--licence_plate_utils.py
|--|--|--licence_plate_conf.py
|--|--|--Tests/ #independant unit testings relative to 1st brick
|--|--***ocrlicenseplate/*** *#3rd brick of your pipeline*
|--|--|--ocr_license_main.py
|--|--|--ocr_license_utils.py
|--|--|--ocr_license_conf.py
|--|--|--Tests/ #independant unit testings relative to 1st brick

|--|--utils.py
|--|--conf.py *#! very  important file (see below)*
|--|--main.py *#* orchestrator of the different bricks
|--|--Tests/ *#E2E technical unit testings*+--README.md
+--app.py *#hosts your API and calls the main.py*
+--packages.txt *#python environment*
+--launch_tests.sh *#functional unit testings*
+--pytest.ini
+--Dockerfile

如前一节所述,存储库的结构遵循管道中相同的逻辑。

  • 每个brickhosts:
    +utils文件:包含您的 brick
    + conf 文件的所有辅助函数:包含所有常量参数(变量名称、目录、超参数值……)
    ++main 文件:通常包含一个函数,该函数集合了 utils 文件
    + Tests 文件夹:包含单元测试这是实现更快、更有效调试的基本原则。
  • 当处理机器学习算法时,最好将潜在的权重存储在项目的根,因为在开发过程中它们可能会经常被替换。
  • 使用**笔记本可以轻松尝试新功能。**根据给定的结构,每个都应该包含以下 python 代码,以便能够“看到”和导入模块 counthighwaypy:
import os
import sys
sys.path.insert(0, os.path.abspath("../")) #visible parents folders
from counthighwaypy import ...### your code
  • 在模块 counthighwaypy 中,重要的是要有一个 utils 文件、 confmain 文件,在不忘记 E2E 测试的情况下协调不同的砖块
  • conf 文件非常重要,因为它设置了项目的根目录及其不同的子模块目录**。可以这样写:**
import os
PROJECT_ROOT = os.path.realpath(os.path.join(os.path.realpath(__file__), "../.."))##### directories
DATA = os.path.join(PROJECT_ROOT, "data/")
NOTEBOOK= os.path.join(PROJECT_ROOT, "notebooks")SRC = os.path.join(PROJECT_ROOT, "counthighwaypy/")
WEIGHTS = os.path.join(PROJECT_ROOT, "weights/")MODULE_DETECT_VEHICLE = os.path.join(SRC, "detectvehicle/")
MODULE_DETECT_LICENCE_PLATE = os.path.join(SRC, "detectlicenseplate/")
MODULE_OCR_LICENSE_PLATE = os.path.join(SRC, "ocrlicenseplate/")
  • app 文件将您的项目封装到一个 API 中,该 API 可供其他用户和服务使用
  • 附加文件如 packages.txt 、 **pytest.ini、**和 Dockerfile 放在项目的根目录下

一旦代码的结构设置好了,最好以独立于其他模块的独立格式开发每个模块。也就是说,这里有一些你可以遵循的指导方针:

  • 设置格式输入&输出每块砖,其中输出的砖I是输入的砖I+1****
  • 用简单的方式编写代码的画布(空函数)当你阅读它的时候,你会立刻明白这个脚本是做什么的
  • 不要忘记的签名的评论
  • 使用 code v 版本工具, git 进行更有效的协作
  • 使用 代码格式器代码转换器 保持代码整洁
  • 对于跨团队协作,将您的代码/包公开为可消费的 API

Python 中的导入

从 Python 3.3 开始,文件夹 folderame 被认为是一个模块(,不需要****_ _ init _ _。py 文件)并且可以简单地导入到 python 文件中,只要它是可见的,即在相同的树级上,通过使用:

import foldername

假设我们有以下结构:

|--**FOLDER1/** 
|--|--file1.py
|--**FOLDER2/** 
|--|--file2.py
|--main.py
  • 在 main.py 中,我们可以
import FOLDER1.file1
import FOLDER2.file2
  • 要在文件 1 中导入文件 2:
import os
import sys#make FOLDER2 visible to file1 (one step up in the tree)
sys.path.insert(0, os.path.abspath("../"))from FOLDER2 import file2

在一个复杂的 python 项目中,为了保持你的导入 的一致性,建议从你代码的源开始所有的**。在我们的例子中,在任何。包含以下内容的 py 文件:**

from counthighwaypy.xxx.xxx import xxx

结论

还有其他方法来构建您的 python 项目,但是我发现本文中描述的方法简单易懂,易于掌握。也可以应用于 Python 以外的语言。

我希望你喜欢阅读这篇文章,它将帮助你在未来更好地组织你的工作。欢迎所有的评论和建议!

Python 场景分析:用 beta-PERT 分布模拟专家评估

原文:https://towardsdatascience.com/python-scenario-analysis-modeling-expert-estimates-with-the-beta-pert-distribution-22a5e90cfa79?source=collection_archive---------11-----------------------

实践教程

以及如何向 SciPy 的目录中添加一个新的、用户自定义的发行版

【https://pixabay.com/images/id-1872665/】由奇莫诺、皮沙贝

关于以下内容的教程:

  • 如何在情景分析中使用概率分布;
  • 如何对专家评估建模——如果观察数据很难获得,我们需要从专家那里收集评估,这些专家可以提供关于行业、业务流程或领域的领域知识;或者,如果历史数据可用,但预计不会反映未来的发展,特别是趋势突变,那么我们也需要开发一个基于专家估计的模型;
  • 如何在情景分析中使用 PERT 分布;并添加 PERT(或其他用户定义的分布)作为一个新的子类来完善 SciPy 库;
  • 如何重新参数化 PERT 或其他分布

我的上一篇文章介绍了 SciPy 的概率分布,以及它们如何应用于随机过程的建模。(用 Python 的 SciPy 介绍概率分布| 2021 年 10 月|走向数据科学)

今天的文章将展示我们如何使用概率分布进行场景分析

情景规划——在商业、工程或金融领域——通常涉及所谓的三点评估 : 最佳情况、可能情况、最坏情况。 ( 三分法估算——维基百科)

三点估计技术有助于避免单点估计的一些缺陷——其中之一是,如果计划者甚至在不知情的情况下过度乐观或悲观,决策者可能会发现相当大的偏差倾向。“偏向是一种不成比例的权重偏向反对一个想法或事情”(偏向——维基百科)。如果一个企业或任何组织的团队成员感到有压力要为成功做计划,但不考虑哪怕是暂时的挫折,因为未来总是看起来更光明,那么他们的计划可能会表现出隐藏的或不太隐藏的对难以置信的好结果的偏好。

三点估计技术试图通过明确询问最坏情况的结果来揭示隐藏的偏差。

  • 报告的最差情况可能仍然有正偏差,但它可以作为讨论风险的起点。
  • 对最佳情况的隔离表明,所谓的可能情况并没有被置于过于乐观的假设的边缘。

然后可以启动一个调整过程,例如德尔菲法(也称为估计-讨论-估计),以达成一致的场景。(德尔菲法—维基百科)

德尔菲法,维基百科,Czkassa(后 Maurizio Bolognini,demo copicnia elettronica,2001),也可免费用于商业用途

如果没有数据科学家的参与,他们可以提供用概率分布模拟不确定性的专业知识,许多组织倾向于只准备三种情况:预期*均结果、悲观情况和乐观情况。然后,这三个场景将留给决策者,让他们尝试直观地将这些点联系起来。仅仅三个点,运用个人直觉。会出什么问题呢?

数据科学家可以通过概率分布来连接这些点,该概率分布在整个范围内对涉及的风险、机会和不确定性进行建模。该模型不仅会报告两个极端值(根据定义,最好和最坏的情况必须相对不太可能,有时甚至极不可能发生),而且科学家还可以报告分位数,这些分位数描述了结果的连续谱及其相关概率。

当我们想要将 3 点情景规划过程建立在更坚实的基础上时,我们应该考虑哪些模型?

正态分布,由于其围绕均值和中值的不变对称性,并不总是显示尾部风险倾向的随机过程的最佳模型。

一个风险因素可能会导致巨大的成本或超过技术安全限制,但它是不对称的:如果风险没有实现,就不会有同等的成本降低或超过基线的安全改进。正态分布不能解释许多不对称风险。

我们想要建模的真实世界随机过程也可能表现出过度的峰度:异常值在其两个尾部出现的频率高于正态分布的情况-具有高峰度的真实世界随机过程的中心包含不到 99.7%的在*均值周围三个标准偏差内的所有事件。

如果我们选择正态分布,我们不仅大胆地假设我们模拟了一个既不偏斜也不重尾或轻尾的随机过程。我们还使用了一个双向无界的模型;它的结果可以沿着 x 轴向负无穷大和正无穷大延伸。

我们的结论是,除了正态分布,我们还应该看看其他模型。

三角形分布在三个点之间画出直线。由此产生的概率密度函数是字面上连接三个点并获得简单随机过程模型的最直接的方法。

我们还将回顾贝塔-珀特分布,它可以展示出类似正态分布和三角形分布的混合形状。三点估算技术将三角形和 PERT 分布应用于建模。 PERT 分布—维基百科

我们通过详细阐述 3 点估计的分布来建模的随机过程可以代表受不确定性影响的任何情况。最初的 3 点估计提供了可能结果边缘的两个极端点,根据定义,这两个极端点发生的概率较低。概率分布将采用这些点,然后显示结果的完整范围及其模式,例如:

  • 新产品或现有产品在新市场或现有市场的销售量
  • 顾客接受新产品的价格,相对于竞争对手的产品价格进行估计
  • 供应商价格,3 个月后
  • 新开发技术部件的典型寿命
  • 30 天后的外汇汇率
  • 拟建海上风力发电场位置的风力

然后,分布特性将使我们能够量化预期结果:

  • 新部件的*均或中位故障时间,它告诉我们其未来的保修率、成本和关键安全阈值;
  • 分位数,确认某个位置的风力不会超过蒲福风 6 或 27 节,概率为 99%,但它会在至少 80%的时间内以 11 节的速度驱动计划的风力涡轮机,这告诉我们涡轮机需要有多坚固才能承受大风;以及它们预期发电千瓦数的规模,以使投资物有所值;
  • 投资组合的风险价值,具有预期的未来回报和估计的波动性。

0.属国

从 SciPy 中,我导入了包含 123 个发行版的庞大目录的 stats 库。具体来说,我们将在教程中使用正态、三角和贝塔分布。

我还导入了 SciPy 的优化模块,并将演示我们如何求解一个非线性方程组,在这种情况下是为了确定一些分布属性。

1.模拟专家评估的三角分布

三角形分布的自变量是最坏情况(最小值或位置参数)、可能情况(模式)和最佳情况(比例参数=最大值-最小值)。

三角形分布是建模专家意见的常用选择。它的三角形直观上很容易理解。

但是除了简单性之外,我不认为它是最佳选择。曲线的形状有着尖锐、笔直的边缘,看起来不像真实世界中的随机过程。当我们将其与下面的 beta-PERT 分布进行比较时,我们还会注意到三角形的峰度(异常值倾向的指标)是有问题的。

“[三角分布]公式同样重视通常不如最可能值为人所知的极端值,因此可能受到对极端值估计不足的不当影响。三角形分布也有一个角形,与代表主观知识的更*滑的形状不匹配。” PERT 分布—维基百科

2.贝塔-珀特分布

2.1 PERT 概念

PERT 代表“Pprogram(或Pproject)Eevaluation 和RreviewTtechnique”PERT 分布是支持这种项目管理综合方法的工具之一。PERT 最初是由美国海军在 20 世纪 50 年代末开发的,用于管理北极星核潜艇项目。该方法很快被许多其他组织和行业采用(项目评估和审查技术——维基百科)。

对于我们今天的目的——建模不确定性——我们可以把重点放在概率分布上,但我们将跳过 PERT 的组织和方法方面,以及它如何处理关键路径分析。

原始 PERT 分布采用与三角分布相同的三个参数:

  • 最坏情况或最小情况;
  • 可能的情况或模式;
  • 最好的情况或最大值。

PERT 概率密度函数的魅力在于它可以*似正态分布的钟形曲线。或者,如果我们将它的 lambda 权重校准为第四个超参数,它也可以模拟一个更灵活的分布形状,表示偏度和更高或更低的尾部风险。上图显示了四条可供选择的 PERT 曲线。所有这些都是基于专家估计的相同大小的最小值、可能值和最大值,但是我们应用了不同的λ因子来模拟它们的尾部。

下图比较了 PERT(1,3,8,8)和正态(3,1)分布。这些曲线彼此相似,但 PERT 并不完全对称。它是右尾的,具有 0.48 的中等正偏斜。与正态变量相比,它的右尾有更多的异常值,但左侧的异常值较少。

2.2 PERT 与三角分布

一些资料来源认为,如果缺少可以推导出λ重量的历史数据,三角形分布在 3 点方案中更可取。

三角形的*均值就是最小值、众数和最大值之和除以 3。PERT 均值也采用这个和,但是用一个因子对模式进行加权,在尝试其他权重之前,该因子通常设置为 4。如果λ保持固定为 4,则分布代表传统的 PERT 变量——而可由分析师设置的可变λ将其转变为所谓的修正 PERT 分布。

然而,对权重因子的关注并不是一个合理的论点,因为我们可以选择将 lambda 设置为 1.0 的中性值,这不会相对于最好和最坏的情况而言过重可能的结果。

下面,我将以强调 PERT 的*均值只包括一个选定的权重因子 lambda 的方式编写*均值的等式,该权重因子可以设置为 1.0,类似于三角形分布。

由于专家已经提供了最坏、可能和最好结果的估计,没有理由不向他们询问分布曲线的“尾部”:他们是否预见到分布是重尾的,异常值比正态分布的钟形曲线更经常出现,这种曲线逐渐消失在*均值的三个标准偏差之外?

在专家估计了最佳、最差和可能结果的幅度之后,Python 脚本可以立即用备选λ权重绘制 PERT 的曲线,以便专家可以选择反映他们对可能结果的真实可能性的直觉的形状;或者反过来,他们可以选择一个图形来显示尾部风险出现的频率。

该图表表明,对于可能的结果,随着λ权重的增加,PERT 曲线开始类似于正态分布——直到非常高的λ值会使其变成尖峰分布。红色曲线显示了一个 PERT 形状,其 lambda 默认值为 4.0。

任何三角形变量的过度峰度都是负的常数,-0.6,这意味着它比正态分布表现出的异常值倾向低得多。我们知道,正态分布经常低估现实世界随机过程中极端结果的尾部风险,例如在金融市场中。具有负过度峰度的三角形分布是扁峰度,或围绕中心的大体积,这意味着它假设尾部的异常值出现的频率更低,并且比正态分布预测的幅度更低。这可能是一个危险的提议。

相比之下,PERT 分布使我们能够通过修改 lambda 权重将尾部风险倾向校准到一个选定的值,例如,如果我们想要达到比正态分布高一个等级的峰度。

一个技术说明:SciPy 在单位区间[0;1].我们可以尝试用位置和比例值来定义它,以将它移动到单位间隔之外,但我的尝试导致了 NaN 值。显然,这是一个不影响除了 triang 子类之外的其他发行版的 SciPy bug。

2.3 PERT 作为转换后的 Beta 分布

SciPy 的两个类及其 123 个发行版不包含 PERT 子类。不过,我们不需要从头开始编写 PERT。SciPy 为我们提供了可以用来定义 PERT 的构件。

  • beta-PERT 分布可以表示为标准 Beta 分布的变换:

  • 我们需要对随机变量 X 进行移位和重标度,然后插入变换后的变量 Z,它符合单位区间[0;1],转化为β变量:

  • 我们通过转换四个 PERT 参数的两个方程推导出贝塔分布的两个形状参数α和β:

  • 由此产生的*β(z;α,β)分布将等于PERT(x;最小,模式,最大,λ)*变量。

3.在 SciPy 的目录中增加一个新的分布:修改的 PERT

我们转换 SciPy 的 Beta 分布,以便在需要时为我们提供 PERT 结果。但是为了避免在我们向我们的专家询问更新的评估时走弯路,我们将把 PERT 本身作为一个新的类别添加到我们导入的 SciPy 分布目录中。

为了准备测试新类,我们选择一个参数元组:

  • 最小值= 10
  • 模式,最频繁或最可能的值= 20
  • 最大值= 40
  • λ= 4.0

这个新类将继承自连续分布的 SciPy 父类,RV _ continuous

这将允许我们依赖与 SciPy 的内置发行版相同的语法。

继承带来了另一个特性。我们可以创建一个新的子类,只需要一个方法来定义它的概率密度函数 pdf 或累积分布函数 cdf。由于我们将它设置为子类,父类 rv_continuous 将自动填补我们在子类中留下的任何空白:父类将在运行时计算缺失属性的数值*似值。示例:如果我们没有明确定义一个方法来计算*均值,当我们应用时,SciPy 将会解决这个问题。mean() 函数在我们的代码中。然而,从 rv_continuous 继承而来的间隙填充机制是有代价的。也可以用公式表示的度量的数值*似值将花费相当多的处理时间。

我们建立了新的 PERT 生成器类,并包含了所有属性的显式方法,我们可以为这些属性写下封闭形式的表达式。

出于实验的原因,我复制了新的详细类,并从我标记为 pertm_gen_less 的副本中删除了大部分方法,除了它的累积分布函数。然后我在长班和短班之间跑了一场赛马。在实例化这两个类之后,我们将在下面看到结果。

我们编写一个函数 pertm_results() ,每当 PERT 实例调用该方法时,它将计算一组属性。该函数将返回累积分布函数、概率密度、百分点(即分位数函数 ppf)和前四个矩。

在下面的第 4 行,我们创建了一个生成器类 *pertm_gen* 的实例。第 6 行使用我们之前设置的参数 *min,mode,max,*和 *lambda* 对其进行参数化,并将实例分配给变量*RV:*PERT 随机变量。

第 8 行调用 pertm_results() 函数来报告分布的主要属性。

为了找出一个只包含 cdf 函数的类是否会有较弱的性能,我们还实例化了“不太完整”的类 pertm_gen_less 。它包含相同的累积分布函数,但它的所有其他属性必须在运行时由 SciPy 的内部解算器*似计算。我们观察到,它对同一个 pertm_results() 函数的调用花费的处理时间是我们为更详细的生成器类所测量的两倍多。因此,如果我们知道如何将一个分布属性表达为一个代数公式,我们应该将它作为一个方法添加到类定义中。

现在我们可以绘制分布函数,就像上一篇概述 SciPy 内置随机变量的教程文章一样(Python SciPy 的概率分布简介)。

interval() 函数返回一种置信区间。它告诉我们,对于给定的概率,随机数将从中位数附*的报告区间中抽取。这种见解与我们对正态分布的了解相当:99.7%的观察值都在中位数的三个标准差范围内。对于其他非对称分布,我们事先不知道这个事实,但我们可以参考 interval() 函数。

我们将 x 轴上的选定点输入 PERT 的累积分布函数和概率密度函数。

4.使用分位数的专家估计:转换的 pert

PERT 和三角分布作为参数所期望的最大值和最小值提出了另一个潜在的问题:对于包括我们在内的规划者来说,确定一个场景的极端情况是众所周知的困难。对于长尾随机过程,即使出现的概率在三到五个标准差后会缩小到接*零,x 轴也可以被拉至接*无穷大。

为了防止对极端结果的有偏见的预测,我们不应该请领域专家来估计“真正的”最小值和最大值,而是考虑 x 轴两端的两个更现实、不太极端的点:“估计随机过程只会以 10%的概率超过哪个大值(也就是 90%的分位数)。并估计阈值,低于该阈值时,更糟糕的结果只会以 10%的概率发生(也就是 10%的分位数)。”这个(10,90)分位数对将代表更现实的最坏和最好情况,在大多数情况下可以更容易地进行估计。

我们将定义一个 PERT 分布,它可以使用分位数作为参数。

为了检查一致性,我们希望转换后的 PERT 分布与我们在第 4 章中分析的原始分布相匹配。因此,我们将 10%和 90%分位数设置为我们已经导出的值,作为修改后的 PERT 分布的属性(第 4 行到第 6 行)。

我们需要将分位数转换成参数,然后传递给(大)母分布:标准 Beta 随机变量。为此,我们将打开 SciPy 的工具箱,使用它的非线性方程系统的解算器。

在第 5 行和第 9 行之间的函数 *f,*中,我们建立了方程组。两个百分点或分位数函数 ppf() 必须进行校准,以等于专家估计的给定分位数值 q10 (14.59)和 q90 (29.33):

  • 0 = ppf(0.10) — q10
  • 0 = ppf(0.90) — q90

第 13 行中 SciPy 的 fsolve() 方法将搜索两个值 minmax ,这两个值将校准百分点函数以匹配它们的目标值 q10 和 q90。

我们的专家估计的模式不需要转变。我们还保留 lambda 的默认值 4.0。

第 12 行提供了一个初始的猜测值,这对于 fsolve()是必需的。we用 100 乘以 90%的分位数来猜测最大值;并将 10%的分位数乘以 0.01 以接*最小值。x 轴上 10%分位数的最左侧和 90%分位数的最右侧的结果起点似乎分别非常接*最小值和最大值,求解程序可以确定这两个极值的*似值。在测试过程中,解算器没有陷入死胡同解,尽管最小值和最大值有时很难在 x 轴的极端边缘找到。

元组 params 接收两个方程组的解:解算器确定的最小值和最大值。

在下一步中,我们将元组转换为可变列表,为 lambda 添加默认值 4.0,并在 min 和 max 之间插入模式。然后,字典将这四个参数值与它们的名称配对。第 10 行中的列表 comprehension 打印了这个参数字典。

这些是我们现在可以提供给 PERT 子类的输入参数。

在第 15 行和第 16 行,我们选择要计算分位数的概率。第 17 行组合了另一个字典中的概率及其相应的分位数,第 19 行中的 list comprehension 逐行打印。我们在下面的截图中看到了分布参数和分位数。

5.PERT-Q 分布

在第四章中,我们编写了代码来翻译分位数参数,以便用传统的参数化(min,mode,max,lambda)使它们易于被 PERT 理解。这需要输入和输出之间的手动步骤。

我们可以通过定义一个新的、重新参数化的 PERT-Q 分布类来简化转换步骤,该类立即接受分位数作为其参数。最终用户甚至可以自己输入估计的分位数。

新的生成器子类 pertQ_gen 继承自其父类,即修改后的 PERT 生成器 pertm_gen 。我们在第 4 章中开发的代码可以重复使用来建立这个新类,只需很少的修改。

除了模式和λ之外,累积分布函数 _cdf()还将 10%和 90%的分位数作为其参数。在第 12 行和第 16 行之间的函数 *f,*中,我们建立了非线性方程组。带有最小和最大参数的两个百分点函数 *ppf()、*被设置为等于 q10 和 q90 中的分位数值:

  • 0 = ppf(0.10) — q10
  • 0 = ppf(0.90) — q90

第 20 行中的 _minmax() 函数应用了 SciPy 的 fsolve() 方法。然后,行 40 导出形状参数α和β,如前几章所述。

辅助函数 _minmax_shape 返回的转换结果被输入到反映 PERT 分布的标准 Beta 分布中。

为了查看结果,我们给变量 *pertQ 分配一个新的 PERT-Q 分布类的实例。*我们用之前选择的参数元组(q10,mode,q90,4.0)来设置它,并通过将它映射到变量 rvQ 来冻结实例。

现在,我们可以绘制 PERT-Q 分布的 pdf,当然,我们会发现它的曲线与我们之前准备的修正 PERT 的曲线是相同的。请记住,我们将专家估计值设置为与修改后的 pert 提供的值相同。因此,两个参数化必须导致相同的分布。

当我们计算 PERT-Q 的矩时,应用。stats()方法,我们发现它们与修改后的 PERT 分布的矩相匹配。

通过计算 x 轴上相同点的 cdf 和 pdf,我们可以进一步确认 PERT-Q 和修正的 PERT 是相同的。

在第 7 行和第 8 行中,我们通过压缩合并了它们的值。在左边,代码列出了 x 值;中间的两列比较它们的 cdf 值,它们没有显示差异;右边的两列显示 pdf 值也没有不同。

6.结论

我们模拟的随机过程可以代表任何受未来结果不确定性影响的变量:汇率;销量;价格;通货膨胀率;药物的代谢吸收率;2022 年新增 Medium.com 用户数;预计海上风电场位置的风力。

通常(但还不够经常),我们可以依靠历史观察数据来准备预测模型。但是很多时候,我们面临着原始数据的缺乏。

我们还必须面对这样一个事实,即每个数学模型只能预测过去观察到的模式。开发能够应对趋势突变的情景——人们担心这是黑天鹅事件;或者希望如果企业想要推出新产品,那么我们需要将场景规划添加到我们的工具箱中。

当新冠肺炎在 2020 年 1 月浮出水面时,我们可以预见它会扰乱公共生活和经济。虽然我们没有数据序列,我们可以从这些数据序列中自动得出单个企业或行业的时间序列预测。上一次如此规模的全球疫情造成浩劫是在 1918 年。2020 年被破坏的供应链、市场和行业在一个世纪前并不存在,因此没有时间序列分析可以提供可靠的模型。

许多其他例子之一是电动汽车制造商特斯拉的崛起。几年前,当特斯拉开始(重新)发明电动汽车时,没有时间序列数据可以表明,当时规模相对较小的初创公司会扰乱汽车行业及其企业巨头。

在许多情况下,我们发现我们需要从领域专家那里收集评估,他们应该为我们提供对预期结果的 3 点评估。然后,我们可以启动我们的数据科学引擎,通过进入 SciPy 的概率分布工具箱,对场景中的不确定性进行建模。

下一篇文章将讨论场景规划的下一个阶段:Python 中的蒙特卡罗模拟。

Jupyter 笔记本可以在 GitHub 上下载:h3ik0th/PERT:PERT distribution SciPy(github.com)

  • 标题图片:奇莫诺,pixabay 上免费
  • 德尔菲法:维基百科,Czkassa(源自 Maurizio Bolognini,democornia elettronica,2001),免费用于商业用途
  • 所有其他图片:作者

Python 集合运算:完整指南-数据结构

原文:https://towardsdatascience.com/python-set-operations-complete-guide-data-structures-98fcd0532296?source=collection_archive---------21-----------------------

在本文中,我们将重点介绍 Python 集合操作的完整过程。

马丁·桑切斯在 Unsplash 上的照片

目录

  • 介绍
  • Python 集合并集
  • Python 集合交集
  • Python 集合差异
  • Python 集对称差
  • 结论

介绍

此时,读者应该熟悉 Python 集合。如果你想复习,或者对 set 不熟悉,请查看 Python sets 初学者指南。

在本教程中,让我们从数学的角度考虑集合,并考虑集合论的部分内容。集合只是不同元素(或数学对象)的集合。

出于本教程的目的,我们将使用数字集合。类似于: A = {1,2,3,5,7}和 B = {1,2,4,8,9}。

Python 集合并集

在集合论中,集合并是集合的集合。这意味着,如果我们有两个集合, AB ,我们可以将它们组合起来,只取不同的元素(意味着重复的元素将被丢弃)。

但是这在数学上看起来如何呢?

这是:

作者图片

一个数值例子有助于理解它。考虑两组:

  • A = {1,2,3,5,7}
  • B = {1,2,4,8,9}

或者视觉上:

作者图片

在这种情况下:

作者图片

元素 1AB 都存在。当联合两个集合时,我们只考虑每个重复的元素一次。

下图中的黄色区域显示了集合 A 与集合 B 的联合:

作者图片

现在让我们看看如何使用 Python 来完成同样的操作。我们将首先创建两个集合, AB 。然后使用**。**Python 集合的 union()【方法】执行 union 操作:

您应该得到:

{1, 2, 3, 4, 5, 7, 8, 9}

这是完全相同的集合,在上面信息图中的黄色区域有相同的元素。

Python 集合交集

在集合论中,集合交是集合的互元素的集合。这意味着,如果我们有两个集合, AB ,集合交集将包含两个集合中出现的所有元素。

从数学角度来说:

作者图片

一个数值例子有助于理解它。考虑两组:

  • = {1,2,3,5,7}
  • B = {1,2,4,8,9}

或者视觉上:

作者图片

在这种情况下:

作者图片

下图中的黄色区域显示了集合 A 与集合 B 的交集:

作者图片

现在让我们看看如何使用 Python 来完成同样的操作。我们将首先创建两个集合, AB 。然后使用**。**Python 集合的交集()方法执行交集操作:

您应该得到:

{1, 2}

Python 集合差异

在集合论中,集合 B 和集合 A 之间的集合差,也称为集合 B 中集合 A 的相对补。它被定义为存在于集合 B 中但不存在于集合 A 中的元素集合。

或者,集合 A 和集合 B 之间的集合差异是集合 A 中存在但集合 B 中不存在的元素集合。

数学上:

作者图片

一个数值例子有助于理解它。考虑两组:

  • A = {1,2,3,5,7}
  • B = {1,2,4,8,9}

或者视觉上:

作者图片

在这种情况下:

作者图片

元素 1AB 是两组中唯一一起出现的。但是元素 489 只存在于集合 B 中。因此,集合 B 和集合 A 之间的集合差正是这些元素:{4,8,9}。

下面信息图中的黄色区域显示了器械包 B 和器械包 A 之间的器械包差异:

作者图片

现在让我们看看如何使用 Python 来完成同样的操作。我们将首先创建两个集合, AB 。然后使用**。**Python 集合的 difference()【方法】执行差分运算:

您应该得到:

{8, 9, 4}

Python 集对称差

在集合论中,两个集合( AB )之间的对称差也称为析取连接。它被定义为存在于集合 A 或集合 B 中,但不同时存在于两者中的元素集合(不在集合 A 和集合 B 的交集中)。

数学上,它被计算为两个差的并集:

一个数值例子有助于理解它。考虑两组:

  • A = {1,2,3,5,7}
  • B = {1,2,4,8,9}

或者视觉上:

作者图片

这里有三个步骤来寻找对称差:

  1. 找出集合 A 和集合 B 之间的集合差:A \ B = {3,7,5},因为这些元素出现在集合 A 中,而不出现在集合 B 中。
  2. 找出集合 B 与集合 A 之间的集合差异:B \ A = {4,8,9},因为这些元素出现在集合 B 中,而不出现在集合 A 中。
  3. 在步骤 1 和步骤 2 中找到的两个集合差的并集:(A \ B)∩(B \ A)= { 3,7,5,4,8,9}

在这种情况下:

作者图片

下图中的黄色区域显示了 set A 和 set B 的对称差异:

作者图片

现在让我们看看如何使用 Python 来完成同样的操作。我们将首先创建两个集合, AB 。然后使用**。**Python 集合的 symmetric_difference()【方法】执行对称差分运算:

您应该得到:

{3, 4, 5, 7, 8, 9}

结论

这篇文章是关于 Python 集合操作的完整指南,有详细的公式解释和例子。

如果你有任何问题或对一些编辑有建议,请随时在下面留下评论,并查看更多我的数据结构文章。

最初发表于 2021 年 12 月 10 日https://pyshark.com

面向初学者的 Python 统计

原文:https://towardsdatascience.com/python-statistics-for-beginners-pearson-correlation-coefficient-69c9b1ef17f7?source=collection_archive---------11-----------------------

皮尔逊相关系数

什么是相关性?

我们都听过“相关性”这个词。虽然我们可能不确定它的确切含义,但我们知道它是两个变量相互关联程度的某种指标。你知道吗?这非常接*事实。

相关性本身是一种数学技术,用于检查两个定量变量之间的关系,例如汽车的价格和发动机的大小。

相关类型

那么有哪些类型的相关性呢?让我们画一些图表来更好地理解:

# Import working libraries
import pandas as pd
import numpy as np# Positive correlation
x = np.arange(start=0, stop=25, step=1)
plt.plot(x, 'o')# Negative correlation
x = np.arange(start=25, stop=0, step=-1)
plt.plot(x, 'o')# No correlation
x = np.random.rand(25)
plt.plot(x, 'o')

正相关表示两个变量会向同一个方向运动。换句话说,如果一个变量增加,另一个也会增加,如果一个变量减少,另一个也会等量减少。正相关关系可以说明如下:

正相关。作者创建的图像。

负相关是两个变量之间的关系,其中一个变量的增加导致另一个变量的减少。负相关的一个很好的例子是氧气含量与海拔的关系。随着海拔的升高,空气中的含氧量会降低(极限登山运动员的通病)。负相关看起来像这样:

负相关。作者创建的图像。

最后但同样重要的是,第三种形式:**没有关联。**在这种情况下,数据图是完全随机的,没有显示任何相关的迹象。没有相关性可以这样来说明:

没有关联。作者创建的图像。

相关形式

关联有不同的形式。可以是线性非线性、单调。让我们再画一些图来说明不同之处 :

# Import additional working libraries
import seaborn as sns # Linear correlation plot
data1 = 20 * np.random.random(100) + 100
data2 = data1 + (10 * np.random.random(100) + 50)
sns.regplot(x=data1, y=data2) # Non-linear correlation plot
x = np.arange(0, 10, 0.1)
ytrue = np.exp(-x / 10000) + 2 * np.sin(x / 3)
y = ytrue + np.random.normal(size=len(x))
sns.regplot(x, y, lowess=True) # Monotonic non-linear correlation plot
def f(x, a, b, c, d):
    return a / (1\. + np.exp(-c * (x - d))) + ba, c = np.random.exponential(size=2)
b, d = np.random.randn(2)n = 100
x = np.linspace(-10., 10., n)
y_model = f(x, a, b, c, d)
y = y_model + a * .2 * np.random.randn(n)sns.scatterplot(x, y)
sns.lineplot(x, y_model)

线性相关性是数据中两个变量以恒定速率变化的趋势。例如,假设一个汽车经销商想要估计燃料消耗对汽车价格的影响。他们发现每多消耗一升燃料,汽车价格就会下降 1000 美元。这描述了燃料消耗和汽车价格之间的线性关系,其中汽车价格以 1000 美元的恒定比率受到燃料消耗的影响。线性相关性可以用一条直线来表示:

线性相关。作者创建的图像。

当两个变量不以恒定速率变化时,相关性为非线性**。**结果,变量之间的关系并没有用直线来表示,而是在数据中造成了某种程度的曲线模式。下图展示了这种情况:

非线性相关。作者创建的图像。

类似于线性关系,单调关系中的两个变量将沿同一方向移动。然而,变量不一定要以恒定的速率移动。下一张图显示了两个变量同时增加但速率不同的情况。

单调非线性相关。作者创建的图像。

皮尔逊相关系数

绘制数据及其关系的图表有助于我们确定我们面对的是哪种类型的相关性以及它的形式。然而,它并没有告诉我们实际上我们看到的相关性有多强。为了量化这两个变量之间的关系,我们需要计算相关系数

相关系数是量化两个变量之间关系的统计度量。该系数的值介于-1.0 和 1.0 之间,而大于 1.0 的计算值表示函数中有错误。系数-1.0 表示完美的负相关和 1.0 表示完美的正相关。另一方面,系数 0.0 意味着两个变量之间没有关系。****

有许多不同的方法来计算两个变量的相关系数。最常见的就是所谓的皮尔逊相关系数 (r)** 。这是一种测试,用于测量两个正态分布变量之间的线性关系的强度。如果数据不是正态分布,可以使用 Kendall 和 Spearman 检验。**

由于 (r) 的取值范围在-1.0 到 1.0 之间,所以当接*零时,它的解释会变得困难。关于如何解释 (r) 的不同值,有许多经验法则,但最常见的一个可能是 2003 年由 Dennis E. Hinkle 和他的合著者在他们的《行为科学应用统计学》的介绍性文本中发表的。它旨在教导学生,并为解释相关系数的大小提供一个经验法则:

辛克尔德,威尔斯马 W,Jurs SG。行为科学应用统计学。第五版。波士顿:霍顿·米夫林;2003

意义

通过测量 (r) 值,我们量化了两个变量之间的关系有多强。但这只能告诉我们故事的一半,对吗?原因是我们计算的相关系数只代表一个样本,而不是整个群体。因此,虽然我们知道样本中的相关性,但我们不能确定我们量化的相关性是否代表整个总体。因此,我们现在要进行一个统计显著性检验**,它可以告诉我们,我们的观察结果是否可以预期在整个人口中是真实的。让我们一步一步地设置我们的测试:**

**步骤 1:制定一个假设**

在假设检验中,我们总是要提出两个假设。一个叫做零假设**,另一个叫做备择假设。他们通常陈述相反的结果:**

零假设 (Ho) 是我们试图反驳的假设。在我们的例子中,假设在给定的总体中,两个变量之间没有显著的线性相关性。

备选假设 (Ha) 是我们试图为之提供证据的假设。在这个例子中,我们将试图证明总体中存在线性相关。

**步骤 2:进行 T 检验**

不需要深入复杂的数学,T-检验(也称为学生 T-检验)允许我们在整个人口中检验一个假设。在我们的案例中,它将帮助我们发现我们在样本中观察到的相关性是否可以在整个人群中重现。

T 检验会给我们一个数字*【T】,我们必须依次解释这个数字。*【t】越高,相关性在群体内可重复的可能性越高。但这是为什么呢?

简单地说,T 检验计算两组之间的差异。在我们的例子中,我们使用 T-test 来比较一个没有相关性的组和一个有确定关系的组。如果我们观察到的相关性是由于巧合而发生的,(t)将等于 0 左右,这表明这两个群体是相同的*。*******

然而,如果两组显著不同*,则 t 值将高得多,并位于群体的极端。这表明我们观察到的相关性不是由于巧合,也不能在没有相关性的人群中找到:***

*# Creating a bell curve for illustration purposes
from scipy.stats import normx = np.arange(-4, 4, 0.001)
y = norm.pdf(x,0,1)fig, ax = plt.subplots(figsize=(9,6))
ax.plot(x,y)
plt.show()*

假设没有相关性的 Ho 的说明性总体。红色方块表示 Ha 和 a (t)值> 3.2 的交叉点,表明两组之间存在显著差异。作者创建的图像。

*****步骤 3:解释 p 值*****

由于 (t) 是一个依赖于总体的理论检验统计量,我们不知道我们观察到的 t 值是否很高。换句话说,范围从 0 到 1,000 的总体将具有不同于范围从 0 到 1 的总体的其他 t 值。为了克服这个小问题,每次 T-Test 也计算所谓的 p-value (p)

p 值向我们展示了我们可以在 Ho 人群中观察到该 t 值的概率。你也可以把它想象成一场飞镖游戏。 (p) 越高,你的飞镖落在上图中 0 附*的几率就越高。 (p) 越低,你打中中路的几率越低。

因此(p)越低,我们的 t 值越有可能位于其中一个极端,因此“足够高”来表明我们两个群体之间的显著差异。或者换句话说:p 越低,我们观察到的相关性由于巧合而没有发生的概率就越高。

第四步:决定假设

让我们进入最后一部分,看看最后一个问题——(p)需要多低?****

还是那句话,看情况。这取决于你为你的实验设定的显著水* (a)* 。显著性水*是 p 值的阈值。通常, (a) 被设置为 0.05 或 5% ,意味着大于该值的 p 值将导致接受零假设。或者换句话说:如果你不能 95%确定两组有显著差异,你不能拒绝零假设。***

假设当我们将 (a) 设置为 0.05 时,我们已经接收到 p 值 0.002。在这种情况下(p)低于我们的显著性水*,我们可以得出以下结论:

我们可以拒绝而支持因为我们发现我们在样本中观察到的相关性可以在更大的人群中重复,并且不是巧合。

Python 示例

但是现在理论已经讲得够多了——让我们看一个现实生活中的例子,以及如何用 Python 完成所有这些!

你需要什么?

要完成本教程,您需要具备以下条件:

我们要做什么?

在本教程中,我们将看看 UCI 机器学习库提供的汽车数据集*。该数据集包括 1985 年以来汽车的技术价格数据以及保险信息。数据集是开源的,通常用于训练/测试回归模型。***

我们将对这个数据集进行一些格式化,使其为我们所用,然后探索几个变量与变量价格相关性*。***

***步骤 1:导入工作库***

首先,我们需要导入执行分析所需的库。如果您还没有安装它们,请在执行下面的代码之前检查相应的文档。

*# Import working librariesimport pandas as pd
import numpy as np
import seaborn as sns
from scipy import stats*

***步骤 2:加载数据***

如果您还没有完成,请从 UCI 网站下载“ imports-85.data ”并保存在您的工作目录中。请注意,你必须改变”。数据“到”。csv”来执行以下代码。

*# Load data and show first 5 rowsdf = pd.read_csv("imports-85.csv")
df.head(5)*

数据框。作者创建的图像。

***步骤 3:格式化数据***

你有没有注意到——我们的数据集没有标题,而且有些值显示一个问号?这是我们在运行相关性分析之前应该解决的问题,否则我们的代码可能无法工作,得到的结果可能会失真。****

让我们在第一步中添加一些列名*,以便我们更容易理解数据集并使用它。***

*# Add headers to df and display first 5 rowsheaders = ["symboling", "normalized-losses", "make", "fuel-type", "aspiration", "num-of-doors", "body-style", "drive-wheels", "engine-location", "wheel-base", "length", "width", "height", "curb-weight", "engine-type", "num-of-cylinders", "engine-size","fuel-system", "bore", "stroke", "compression-ratio", "horsepower", "peak-rpm", "city-mpg", "highway-mpg", "price"]df.columns = headers
df.head(5)*

带标题的数据帧。作者创建的图像。

由于这看起来效果不错,我们现在应该看看归一化损失一栏中的这些问号。我们不能用它们来做进一步的分析,所以我们将用 NaN 值来替换值。注意,我们将在整个数据帧上调用 pandas.replace 函数,而不仅仅是在一列上。我们这样做是为了确保我们以后不会在数据集中的其他地方遇到另一个问号*。*******

*# Replace ? with NaN and show first 5 rows of dfdf = df.replace('?',np.NaN)
df.head(5)*

没有问号的数据框。作者创建的图像。

因为我们没有没有的概述,现在中缺少值,我们应该运行一个快速分析来找出 NaN 值。我们可以通过首先检查NaN 值的数据帧,然后使用 for 循环打印出我们的分析。

*# Check df for missing valuesmissing_data = df.isnull()
missing_data.head(5)*

使用 bolean 检查缺失值的数据框。作者创建的图像。

*# Run for loop to check for missing valuesfor column in missing_data.columns.values.tolist():
    print(column)
    print (missing_data[column].value_counts())
    print("")*

首先分析 for 循环的列。作者创建的图像。

如果您滚动分析,您会发现在下面的中有个缺失值(=True)* 😗**

  • 标准化损失
  • 门的数量
  • 钻孔
  • 中风
  • 马力
  • 峰值转速
  • 价格

因为我们需要一个有效的数据集,所以我们必须找到一种方法来处理缺失的数据。一般来说,有两种选择。要么我们丢弃带有丢失数据的列或行,要么我们替换丢失的数据。

由于我们的数据集不够大,不能简单地删除所有带有缺失值的行/列,我们将使用剩余数据的手段替换缺失值。

唯一的例外是门的数量一栏,因为这个数据是而不是的数字。这里我们将用最常见的变体替换缺失数据。在这种情况下,那就是“四”。

*# Replacing NaNs with mean / most common variantavg_norm_loss = df["normalized-losses"].astype("float").mean(axis=0)
df["normalized-losses"].replace(np.nan, avg_norm_loss, inplace=True)avg_bore = df['bore'].astype('float').mean(axis=0)
df["bore"].replace(np.nan, avg_bore, inplace=True)avg_stroke = df['stroke'].astype('float').mean(axis=0)
df["stroke"].replace(np.nan, avg_stroke, inplace=True)avg_horsepower = df['horsepower'].astype('float').mean(axis=0)
df['horsepower'].replace(np.nan, avg_horsepower, inplace=True)avg_peakrpm = df['peak-rpm'].astype('float').mean(axis=0)
df['peak-rpm'].replace(np.nan, avg_peakrpm, inplace=True)avg_price = df['price'].astype('float').mean(axis=0)
df['price'].replace(np.nan, avg_price, inplace=True)df["num-of-doors"].replace(np.nan, "four", inplace=True)*

还剩下什么?对!检查数据表格*。我们可以通过执行以下代码轻松做到这一点:***

*# Checking df typesdf.dtypes*

数据框类型。作者创建的图像。

如你所见,一些列仍然有错误的数据类型(例如,bore 应该有整数)。所以让我们快速纠正一下:

*# Format data typesdf[["bore", "stroke"]] = df[["bore", "stroke"]].astype("float")
df[["normalized-losses"]] = df[["normalized-losses"]].astype("int")
df[["price"]] = df[["price"]].astype("float")
df[["peak-rpm"]] = df[["peak-rpm"]].astype("float")*

在数据标准化规范化方面,我们还可以做得更多,但我们在本教程中不需要这些,所以让我们继续。

第四步:浏览数据

让我们快速回顾一下我们想要做的事情:我们想要寻找与汽车的价格有某种关联变量,对吗?所以我们需要首先检查类型形式这两个变量之间的关系——最简单的方法就是多画一些图!****

为此,我们将使用 seaborn 库及其 regplot 函数。该功能将在 2D 图上为我们选择的变量绘制出数据点线性* 回归线。虽然这并不能帮助我们量化相关性,但它帮助我们确定****两个变量之间的关系类型和形式。***

但事不宜迟,让我们开始看看价格与以下因素的关系:

发动机尺寸 看看下图。我们可以观察到线性回归线绘制的数据非常吻合。我们还可以观察到回归线有一个陡峭向上的梯度。但是考虑到相关性的类型和形式,这告诉我们什么呢?****

虽然回归线的递增梯度向我们表明,我们可以预期发动机尺寸和价格之间存在正相关性,但回归线的良好拟合表明变量之间存在线性相关性****

***# Create a plot for engine-size and pricesns.regplot(x="engine-size", y="price", data=df)***

发动机尺寸和价格的回归图。作者创建的图像。

下图向我们展示了公路每加仑英里数(mpg)与汽车价格的回归曲线。这里我们可以观察到回归线是递减,表示一个负相关。然而,我们可以看到回归线没有覆盖 x 轴上 0 到 25 之间的异常值。这表明线性回归不是一个很好的拟合,并表明数据可能非线性的****

***# Create a plot for highway-mpg and pricesns.regplot(x="highway-mpg", y="price", data=df)
plt.ylim(0,)***

公路回归图-mpg 和价格。作者创建的图像。

峰值转速 峰值转速和价格的图形看起来又不一样了。我们看到数据点有很大的变化,回归线几乎水*。基于这一观察,我们无法确定我们可以预期的相关形式,但是假设存在线性关系,则*乎*坦的回归线表明我们可以预期在 0 附*的相关系数。因此,我们可以得出结论,峰值转速和价格之间没有相关性。****

***# Create a plot for peak-rpm and pricesns.regplot(x="peak-rpm", y="price", data=df)
plt.ylim(0,)***

峰值转速和价格的回归图。作者创建的图像。

由于逐个查看每个变量对需要花费很长时间,我们可以使用 panda 的 corr() 函数来缩短这个过程:

***# Using pandas corr() functiondf.corr()***

熊猫的 Corr()函数。作者创建的图像。

如您所见,pandas 的 corr() 函数创建了一个相关矩阵,其中包含了我们数据集中的所有变量。你可能会问——为什么我们不从一开始就这样做呢?****

答案比较简单。虽然 corr() 函数默认计算皮尔逊相关系数,但它不会给出任何关于形式相关的信息。因此,它可以作为一个很好的起点来识别潜在的高相关性,但是单独检查变量对仍然是必要的。****

步骤 5:计算相关性和(p)值

在我们继续之前,让我们快速总结一下上述分析的结果:

  • 我们可以预期发动机尺寸和价格之间存在正的线性关系
  • 我们已经观察到公路里程数和价格之间的负相关性,这似乎是非线性的 T21。
  • 我们假设 T22 峰值转速和价格之间没有相关性,并且我们没有相关形式的可靠信息 T24。

因此,我们可以得出这样的结论:变量 highway-mpg 和 peak-rpm 似乎不适合使用皮尔逊相关系数进行进一步分析。****

为了进一步了解引擎大小,我们可以借助 scipy.stats 库来计算皮尔逊相关系数以及 p 值。

***# Calculate pearson coefficient and p-valuepearson_coef, p_value = stats.pearsonr(df['wheel-base'], df['price'])print("The Pearson Correlation Coefficient is", pearson_coef, " with a P-value of P =", p_value)***

打印报表。作者创建的图像。

结果,我们得到的皮尔逊相关系数为 ~ 0.87 ,p 值几乎为 0

由于我们的系数> 0.7,但是<0.9, we conclude that we are observing a 发动机尺寸和价格之间的正相关性很高。

最后,我们可以观察到我们的 p 值肯定是低于我们的显著性水*(a)0.05。因此,我们可以得出结论,我们上面计算的相关性不是巧合,因此是重要的****

最后的话

如果我不说出下面这句著名的话,这就不是一个好的教程:

相关性并不意味着因果关系。

我们已经知道相关性是描述两个变量之间关系程度的一种度量。因果关系另一方面是两个变量之间的因果关系。****

即使我们观察到了相关性,我们也不能断定一个变量会引起另一个变量的变化。除非我们已经能够考虑所有不同的变量,否则我们不得不假设仍然有机会巧合或者第三个因素可能导致我们两个变量都发生变化。****

因此,在得出关于相关数字的最终结论之前,进行更全面的实验总是很重要的。****

2021 年数据科学家的 Python 字符串操作

原文:https://towardsdatascience.com/python-string-manipulation-for-data-scientists-in-2021-c5b9526347f4?source=collection_archive---------27-----------------------

破解数据科学面试

使用不同的重量训练你的 Python 编码肌肉

照片由pix poeties在 Unsplash 上拍摄

数组和字符串操作是数据科学和软件工程面试中测试最多的话题之一。这是测试候选人程序性思维能力和编码流畅性的最佳面试题型。要想表现出色,我们必须熟悉数组/字符串、矩阵及其行/列结构的基本操作,以及 Python 语法。

在两篇类似的博客文章中,我已经触及了一些基本问题,并对几个真实的面试问题进行了现场编码。

</6-python-questions-you-should-practice-before-coding-interviews-f958af55ad13>

在今天的帖子中,让我们尝试一些不同的东西。正如Emma Ding(Airbnb 的数据科学家)和Rob Wang(robin hood 的数据科学家)的帖子所建议的那样,我们可以通过对同一个问题提出多种解决方案,同时比较和对比它们的优缺点,来显著提高我们对各种算法的理解。

这正是我们在这篇文章中要做的!

我每天都在用 Python 写代码,并在这里记录了我的进步。如果您是编程新手,或者正在积极准备数据科学和软件工程面试,让我们组队一起编写代码。

问题 1:在一个排序矩阵中计算负数,亚马逊和苹果

-给定一个 m x n 矩阵网格,该网格按行和列的非递增顺序排序,返回网格中负数的个数。
-
https://leet code . com/problems/count-负数-in-a-sorted-matrix/

走过我的思考

亚马逊和苹果都问这个问题。作为热身,我们可以使用野蛮武力,直接迭代矩阵:检查一个元素是否为负数。

在 Python 中,我们可以做如下事情:

解决方案 1:嵌套 For 循环

8

3 行:使用 for 循环遍历行

4 行:使用 for 循环遍历列

这里,我们使用一个嵌套的 for 循环来遍历矩阵并检查单个元素:如果元素是负的,count 就加 1。这是计算计数值的典型算法。

但是,缺点是计算效率低。它的时间复杂度 O(N)在运行时间上被认为是“恐怖的”。双 for 循环方法不适用于大型矩阵(例如,一百万行/列的矩阵)。

顺便提一下,如果你不熟悉数据结构和算法(DSA) ,你可能会发现下面的各种时间复杂度的比较很方便。在绿色和黄色区域,我们有 O(1)、O(log N)和 O(N)。在红色区域,我们有 O(N!)、O(2^N)和 O(N)。

【https://www.bigocheatsheet.com 号

O(N)是第三差的算法,仅比 O(N!)和 O(2^N).在真实的面试环境中,如果答案的时间复杂度为 O(N ),你的面试官总是会要求改进。

我从阅读以下关于 DSA 的文章中受益匪浅:Python 中的数据结构&算法 ,作者Papa Moryba kou ate 数据科学家的数据结构指南&算法,第 1 部分&*第 2 部分*

解决方案 2:导入包

8

如果我们被允许导入一个包,我们可以把矩阵变成一个 Numpy 数组,然后把它展*。然后,我们使用 for 循环来计算负值的数量。这种方法既快速又简单。它具有线性时间复杂度 O(N),比 O(N)好得多。

如果你是 Python Numpy 和矩阵运算的新手,请查看我关于这个主题的另一篇文章:

然而,面试官可能会禁止外包,并希望增加一些趣味。

解决方案 3:二分搜索法

重新检查原问题后,有一个隐藏的条件,即按行和列的非递增顺序排序 ,这让我们想起了一个二分搜索法。

这是算法的基本逻辑。

我们创建一个由三个值限定的搜索空间——低(左)、中和高(右)——并找到一行中第一个负元素的位置,即-1。

如果选择的值是正数,我们向右移动并搜索更小的值,因为数组是按非递增顺序排序的。如果选择的值是负的,我们向左移动并搜索一个更大的值。

这是一个相当简单的算法总结,请查看我关于这个主题的另一篇文章:

8

与经典的二分搜索法算法(O(log(N))不同,我们将其嵌套在 for 循环(O(N))中。总的来说,其时间复杂度为 O(N*log(N)) 。就运行时间而言,它介于第一种方法 O(N)和第二种方法 O(N)之间。

问题 2:用右边最大的元素替换亚马逊的元素

-给定一个数组 arr,用它右边元素中最大的元素替换数组中的每个元素,用-1 替换最后一个元素。
-完成后,返回数组。
-
https://leet code . com/problems/replace-elements-with-great-element-on-right-side/

走过我的思考

亚马逊包括这个问题。对于一个数组,我们必须用它右边最大的元素替换每个元素。我们可以使用野蛮的武力,如下。

解决方案 1:暴力

[18, 6, 6, 6, 1, -1]

唯一的警告是我们必须保存最后一个元素并用-1 替换它。我们省略了第 5 行的最后一个元素,并将-1 加回第 8 行的数组。

解决方案 2:反转和替换

[18, 6, 6, 6, 1, -1]

第二种解决方案是向后迭代元素,首先读取最后一个元素:如果它大于右边的元素,则更新值。虽然这两种解决方案的运行时间相同,但是第二种方法最酷的地方在于它对最后(最右边)位置的处理。很多时候,我们会错过这些特例。

#问题三:三连冠赔率,由大疆

-给定一个整数数组 arr,如果数组中有三个连续的奇数,则返回 true。否则,返回 false。
-
https://leetcode.com/problems/three-consecutive-odds/

走过我的思考

大疆问这个问题。我的首选算法是采用野蛮力法,检查三个连号是否都是奇数,如下:

解决方案 1

False

上述解决方案的唯一问题是 else 语句的缩进:它应该与 for 语句*行,而不是在 for 语句内部。另外,请不要忘记在第 2 行指定搜索边界。

解决方案 2

一种类似但更有技巧的方法是在 for 循环中设置一个计数器:如果计数达到 3,则返回 True。如果整个 for 循环不包含任何 3 个连续元素,则函数返回 False。因此,else 语句应该在 for 循环的外部,而不是内部。你的面试官可能会问这个缩进问题。

False

老实说,我只提出了第一个解决方案,并不得不检查其他人关于 Leetcode 的第二个解决方案,这是更 Pythonic 式的思维。

完整的 Python 代码在我的 Github 上有。

外卖食品

  • 熟能生巧!坚持每天阅读和练习可以显著提高每个人的编码技能。
  • 提高编程最有效的方法是掌握基础知识,并将它们融入到你的日常工作中。
  • 与其他 Python 问题相比,数组和字符串操作更简单,并且测试考生的基础知识。破解这类面试题需要时间和练习。

Medium 最*进化出了它的 作家伙伴计划 ,支持像我这样的普通作家。如果你还不是订户,通过下面的链接注册,我会收到一部分会员费。

https://leihua-ye.medium.com/membership

我的数据科学面试序列

</5-python-coding-questions-asked-at-faang-59e6cf5ba2a0>

喜欢读这本书吗?

请在 LinkedIn 和 Youtube 找到我。

还有,看看我其他关于人工智能和机器学习的帖子。

Python 结构化模式匹配——帮助您入门的 3 大用例

原文:https://towardsdatascience.com/python-structural-pattern-matching-top-3-use-cases-to-get-you-started-262160007fa0?source=collection_archive---------12-----------------------

Python 3.10 增加了对 switch 语句的支持——这里有 3 个实用的例子来帮助您入门

照片由 Grzegorz Walczak 在 Unsplash 上拍摄

Python 的等价语句switch终于在 Python 3.10 中出现了。它隐藏在一个有趣的名字结构模式匹配的后面,做你所期望的和更多的事情。它支持处理原始数据类型、序列,甚至类和枚举。

今天我将向你展示如何开始,并讨论在什么情况下你应该使用它。我们将从创建一个基于 Python 3.10 的新环境开始。

不想看书?请观看我的视频:

如何配置 Python 3.10 环境

我们将使用 Anaconda 创建一个新的虚拟环境。如果您使用其他工具来管理 Python 工作流,请在线搜索等效的命令。

从终端逐行执行以下命令,创建一个名为py310的新 Python 3.10 虚拟环境,安装并启动 Jupyter Lab:

conda create — name py310 python=3.10
conda activate py310
conda install jupyter jupyterlab
jupyter lab

现在,您可以使用以下命令来验证您使用的是 Python 3.10(假设您使用的是 Jupyter 或 Python shell):

你应该看到3.10.0打印出来,这意味着你可以走了。

Python 结构化模式匹配-基本语法

在深入到用例之前,让我们先了解一下结构模式匹配背后的基本语法。整个事情基于两个关键词:

  • match —您想要评估条件的受试者。它可能是 API 请求返回的状态代码。
  • case —评估个体条件以查看是否确认匹配。

下面是来自官方文档的模式匹配的一般语法:

如您所见,Python 中的结构化模式匹配不需要在每个案例的结尾使用break 语句。如果在其他案例中没有找到精确匹配,将使用最后一个案例(_)。

您现在已经了解了基础知识,所以让我们深入到示例中。

Python 结构化模式匹配-匹配简单值

先说一个简单的。我们将编写一个名为 http_error()的函数,它接受一个状态代码。然后,我们将使用状态代码作为匹配语句的主题,并针对不同的条件编写几个案例:

仅供参考,您可以使用|在一个模式中组合多个条件。例如,状态代码 401、403 和 404 返回相同的结果,因此编写三个不同的案例没有意义。

现在让我们用不同的状态代码调用 http_error()函数来验证它是否正常工作:

图片 Python 中的基本结构模式匹配(图片由作者提供)

如你所见,即使你传入一个字符串,这个函数也不会崩溃。接下来,我们将深入更高级的用例。

Python 结构化模式匹配-匹配复杂模式

有时候匹配一个变量是不够的。好消息是——您可以在 Python 的匹配语句中加入几乎任何可以想象的东西。

看看下面的代码片段——它声明了一个名为get_service_level()的函数,该函数将用户数据作为字典接受。目标是基于用户订阅类型(免费,高级)和消息类型(信息,错误),返回不同的服务级别。在 info 消息的情况下,不管订阅类型如何,服务级别都是 0。在有错误消息的自由用户的情况下,服务级别是 1。最后,在高级用户出现错误消息的情况下,服务级别是 2:

在一个关键词后面写一本字典有点不自然,但这是你必须习惯的事情。无论如何,以下是对所有四种可能情况的测试:

*图 2 —匹配 Python 中的复杂模式(图片由作者提供)*

您可以通过添加默认案例来进一步改进该功能,但是您已经知道如何做了。

Python 结构化模式匹配-匹配类中的模式

可以对 Python 的类和数据类使用结构模式匹配。文档向您展示了如何使用数据类,所以我将在这里介绍常规的数据类。简而言之,您可以使用一个类名,后跟一个作为 case 模式传递给构造函数的参数列表。

以下示例向您展示了如何在 Python 类中实现相同的服务级别逻辑。向构造函数传递了两个参数—订阅类型和消息类型。对于任何信息消息,级别为 0。在自由用户出错的情况下,级别为 1。最后,在高级用户出错的情况下,级别为 2。我们还将添加一个默认案例,只是为了混淆一下:

语法与第二个例子中的不同,但仍然可以理解。我认为它也更容易阅读。假设您有两个以上的参数——将它们作为字典传递会很快变得混乱。

让我们针对四种可能的场景和一种默认情况进行测试:

图片 Python 类中的结构模式匹配(图片由作者提供)

很管用。让我们在这里停止使用实际的例子,把焦点转移到一个重要的问题上——什么时候应该使用结构模式匹配?

最后的想法

Python 中早就应该有一个专用的switch语句了。结构模式匹配打包了 switch 语句所做的一切,甚至更多。这是一种书写和评估条件的新方法。但是正如科里·泰勒曾经说过的那样——新并不意味着最好

如果你所做的只是测试简单的条件,那么在结构模式匹配上费心是没有意义的。此外,结构模式匹配仅在 Python 3.10 中可用。如果您尝试使用旧的 Python 版本,将会得到一个错误。值得升级吗?

当您有更复杂的模式要评估时,主要使用结构化模式匹配,就像今天讨论的那些。

*你们觉得结构模式匹配怎么样?你是否用它替换了你的一些旧代码,为什么?*请在下面的评论区告诉我。

喜欢这篇文章吗?成为 中等会员 继续无限制学习。如果你使用下面的链接,我会收到你的一部分会员费,不需要你额外付费。

https://medium.com/@radecicdario/membership

保持联系

  • 注册我的简讯
  • 在 YouTube上订阅
  • 在 LinkedIn 上连接

Python 模板字符串格式化方法

原文:https://towardsdatascience.com/python-template-string-formatting-method-df282510a87a?source=collection_archive---------11-----------------------

关于模板字符串的详细信息,这是 python 中格式化字符串的一个选项。当使用正则表达式时,它可能更合适

照片由来自佩克斯的克里斯蒂娜·莫里洛拍摄

模板字符串是 Python 中用来格式化字符串的另一种方法。与%运算符相比,。format()和 f 字符串,它有一个(可以说)更简单的语法和功能。对于国际化(i18n) 来说,这是的理想选择,并且有一些细微差别您可能会发现是有利的——尤其是在使用正则表达式( regex )时。

我们先看看其他三个的语法,这样就可以比较了。

%运算符

>>> name = "Alfredo"
>>> age = 40
>>> "Hello, %s. You are %s." % (name, age)
'Hello, Alfredo. You are 40.'

。格式()

>>> print('The {} {} {}'.format('car','yellow','fast')) **#empty braces** The car yellow fast>>> print('The {2} {1} {0}'.format('car','yellow','fast')) **#index** The fast yellow car>>> print('The {f} {y} {c}'.format(c='car',y='yellow',f='fast')) **#keywords** The fast yellow car

f 弦

>>> name = "Peter"
>>> print(f'Nice to meet you, {name}')

模板字符串

它使用字符串模块中的模板类。它的语法有点类似于。format(),但是它没有使用花括号来定义占位符,而是使用了美元符号(\()。\)也是有效的,当占位符后出现有效字符串时,它应该出现。

参见各种情况的语法。我将开始解释 safe_substitute 和 regex 的用法。

安全替换和正则表达式

假设您想要替换包含、%和$的字符串中的某个内容。使用正则表达式时会发生这种情况。

考虑一个输入字段,该字段接受公司股票代码加上百分比形式的正(+)或负(-)值,之后不接受任何内容。并且符号加号+被动态替换。比如:AAPL: +20%或者 TSLA: -5%(我知道这可能是一个愚蠢的用例!但是请忽略。只是一个例子)。

regex:([symbol:+或-][0–9]{ 1,4}[%])$)

对于%运算符,。format()和 f 字符串—不起作用。%运算符和。format()会引发错误,f-strings 会移除{m,n}。见下文

**#% operator**
>>> print('(([%s][0-9]{1,4}[%])$)' % "AAPL: -")
*TypeError: not enough arguments for format string***#.format()** >>> print('(([{symbol_pos_neg}][0-9]{1,4}[%])$)'.format(symbol_pos_neg = 'AAPL: +'))
*KeyError: '1,4'***#f-strings** >>> symbol_pos_neg = "AAPL: +"
>>> print(f'(([{symbol_pos_neg}][0-9]{1,4}[%])$)')
(([AAPL: +][0-9]*(1, 4)*[%])$)

虽然通过对字符进行转义(将花括号或百分号加倍)可以很容易地解决这个问题,但我觉得这很不方便。使用模板字符串,您不需要这样做。和%不用于定义占位符,并且通过使用 **safe_substitute,**您不必替换所有的$。

见下文,正则表达式不变,$symbol_pos_neg 被正确替换。

>>> print(Template('(([$symbol_pos_neg][0-9]{1,4}[%])$)').**safe_substitute**(symbol_pos_neg='AAPL: -'))(([AAPL: -][0-9]{1,4}[%])$)

带关键字的常规案例

>>> from string import Template
>>> Template('$obj is $colour').substitute(obj='Car',colour='red')
'Car is red'

带字典的普通箱子

**>>>** d = dict(obj='Car')
**>>>** Template('$obj is red').substitute(d)
'Car is red'

占位符后跟无效字符

如果占位符后有无效字符串,则只考虑占位符。示例,请参见“.”$who 后面的点号。正常打印。

>>> from string import Template
>>> Template('$obj**.** is $colour').substitute(obj='Car',colour='red')
'Car. is red'

占位符后跟有效字符

如果占位符后有有效字符,则必须用花括号括起来。

>>> from string import Template
>>> Template('${noun}ification').substitute(noun='Ident')
'Identification'

请注意,下划线(_)等字符也被认为是有效的。因此,使用$的规则同样适用。

多个$$$符号

替换照常进行,并打印出一个额外的$值。

>>> Template(’$obj. is $$$colour’).substitute(obj=’Car’,colour=’red’)
'Car. is $red'

最后的想法

我仍然是 Python 的初学者,比较字符串格式化方法教会了我很多。我希望我已经清楚地向你提供了细节。尽管模板字符串的功能不如:

viniciusmonteiro$ python3 -m timeit -s "x = 'f'; y = 'z'" "f'{x} {y}'"5000000 loops, **best of 5: 82.5 nsec per loop**viniciusmonteiro$ python3 -m timeit -s "from string import Template; x = 'f'; y = 'z'" "Template('$x $y').substitute(x=x, y=y)"  # template string500000 loops, **best of 5: 752 nsec per loop**

我认为在某些情况下,它在简单和方便方面带来了一些好处。虽然我知道这些可能是主观的。

参考

[1] [string](https://docs.python.org/3/library/string.html#module-string) —常见字符串操作https://docs . python . org/3/library/string . html # template-strings

[2] Python 3 的 f-Strings:改进的字符串格式化语法(指南)https://realpython.com/python-f-strings/

[3]Python 中不同字符串连接方法的性能——为什么 f 字符串如此出色https://grski.pl/fstrings-performance.html

Python:知道和理解它的区别

原文:https://towardsdatascience.com/python-the-difference-between-knowing-and-understanding-it-3b2ebd4e2317?source=collection_archive---------33-----------------------

你知道 Python 中列表的调整因子是什么吗?

Artem Sapegin 在 Unsplash 上拍摄的照片

我只是在开玩笑。你不需要知道 Python 的列表大小调整因子就能很好地理解它。但是如果你想了解更多,请阅读这里的!

我来自一个非计算机科学(CS)和非技术背景的人,当我刚开始机器学习(ML)的旅程和职业生涯时,我遭受了 CS 基础知识的缺乏。这包括理解渐*运行时间、内存使用、数据结构、算法等等。更糟糕的是,我在没有工程基础的情况下直接进入了 ML——我甚至不知道类中的 self 是什么意思(在 Python 上下文中)。我是*。*

足够幸运的是,有了足够的 ML 概念但缺乏工程知识,我在 ML 中获得了立足点,并开始了我的旅程学习和实践 ML。在这段旅程中,我很快意识到工程在 ML 工程中扮演着重要的角色。然而,事实上,知道如何简单地使用一种编程语言和很好地理解它之间有着明显的区别。

在你说你真正理解 Python 之前,你应该知道以下一些事情。本文分为几个部分:

  1. Python 中的一切都是对象
  2. 内置 Python 对象不包含 dict 属性
  3. 指针在 Python 中本来就不存在
  4. Python 的可变默认值是一切邪恶的根源
  5. 复制 Python objects

1.Python 中的一切都是对象

好吧,我们从简单的开始。我相信大多数读者都知道 Python 是一种面向对象的编程语言。但是这到底意味着什么呢?

当我说一切时,我指的是一切*。不仅用户定义的函数和类是对象,内置类型也是对象!本质上——一切 。*

正如您将在后面的第(3)点中看到的,Python 中的对象包含元数据(也称为属性*)以及一些功能(也称为方法)。要查看与对象相关的所有关联元数据和功能,可以使用内置的dir()函数。*

*# Look at the metadata of an integer object
x = 1
dir(x)
>>> **['__abs__', '__add__', '__and__', '__bool__', '__ceil__', '__class__', '__delattr__', '__dir__', '__divmod__', '__doc__', '__eq__', '__float__', '__floor__', '__floordiv__', '__format__', '__ge__', '__getattribute__', '__getnewargs__', '__gt__', '__hash__', '__index__', '__init__', '__init_subclass__', '__int__', '__invert__', '__le__', '__lshift__', '__lt__', '__mod__', '__mul__', '__ne__', '__neg__', '__new__', '__or__', '__pos__', '__pow__', '__radd__', '__rand__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rlshift__', '__rmod__', '__rmul__', '__ror__', '__round__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__trunc__', '__xor__', 'as_integer_ratio', 'bit_length', 'conjugate', 'denominator', 'from_bytes', 'imag', 'numerator', 'real', 'to_bytes']***

我们可以观察到,即使整数类型本身也是具有内置属性和方法的对象。例如,我们可以通过realimag属性获得一个复数的实部和虚部。

*real_x, imag_x = x.real, x.imag
print(f"Complex number of {x} is: {real_x} + {imag_x}i")
>>> **Complex number of 1 is: 1 + 0i***

还有可以调用的方法包括as_integer_ratio()bit_length()等等。

*x.as_integer_ratio()
>>> **(1, 1)**x.bit_length()
>>> **1***

此外,这些对象具有这些内置的 dunder 方法的事实允许它们执行某些操作,如加法(__add__)、转换为浮点(__float__),甚至获得对象的可打印表示(__repr__)。

随意探索其他内置数据结构的内置属性和方法,如列表、元组、字典等等!

2.内置 Python 对象不包含 dict 属性

如果你还不知道,我们可以使用内置的vars函数返回一个对象的__dict__属性,如果它存在的话。让我们看看如果在内置 Python 对象上使用这个函数会发生什么。

*x, y, z = 1, "hello", {"hello": 1}vars(x)
>>> **TypeError**: **vars() argument must have __dict__ attribute**vars(y)
>>> **TypeError**: **vars() argument must have __dict__ attribute**vars(z)
>>> **TypeError**: **vars() argument must have __dict__ attribute***

因此,我们无法通过setattr()函数为内置 Python 对象设置新的属性和方法,也无法覆盖现有的属性和方法。之所以如此,是因为使用 C 进行了优化,防止添加额外的属性,从而允许更多的“固定”内存分配。

*setattr(x, "some_new_attr", 123)
>>> **AttributeError: 'int' object has no attribute 'some_new_attr'***

然而,对于定制对象,我们可以自由地创建新的属性,因为这些对象通常有一个__dict__属性。这是因为__dict__属性是一个映射对象,用于存储对象的可写属性。

注意:在类上调用vars时要小心,因为在类本身上调用vars和在该类的实例上调用vars是有区别的。

*vars(Animal)
>>> **mappingproxy({{'__module__': '__main__',
                   '__annotations__': {'has_eyes': bool},
                   'mammal': True, ... }})***

3.指针在 Python 中并不存在

你可能会好奇——列表集合字典甚至数据帧都可以修改,它们的内存地址保持不变。什么叫指针不存在?!

: *id()*用于获取对象的内存地址

*# Example using a list (or any other mutable data structure)
x = [1, 5, 10]
id(x)
>>> **140298465662784**x.pop()
id(x)
>>> **140298465662784***

上述数据结构作为可变类型存在的事实模仿了Python 中指针的功能。实际上,你已经为其分配了变量的可变对象并不真正拥有那个内存地址。

那么 Python 是如何跟踪对象的呢?

在 Python 中,对象被存储为 PyObject s,这实质上是所有具有 CPython 结构的 Python 对象的基础结构。当我们检查两个对象是否相等时,即a is b,我们实际上是在检查这两个对象的内存地址是否相同。

创建任何对象时,都会创建一个 PyObject,它跟踪数据类型、值以及最重要的引用计数*(即指向它的名称/变量的数量)。因此,更恰当的说法是 Python 中的每个名称/变量都是一个对 PyObject 的 引用 。*

*# As seen below, x and y point to the same PyObject
# Each of them do not own the memory addressx = "hello"
y = "hello"id(x) == id(y)
>>> **True**# The PyObject looks something like this
# Type: str; Value: "hello"; Reference Count: 2*

如上所述,像 intstr元组这样的不可变数据结构并不拥有自己的内存地址,而是将名称/变量绑定到引用(p objects)。

:上面这个用字符串(“hello”)的例子是字符串实习的一个特例。通常情况下,不可变对象将具有不同的内存地址,其他例外情况有整数缓存空不可变对象发生或发生。为了更清楚地理解 Python 中可变和不可变数据结构的内存管理,我强烈推荐这篇文章!

那么,可变数据结构的行为如何像指针呢?

可变数据结构是不同的,因为我们能够修改它的值而不需要修改它的内存地址。这样,包含相同值的相同列表(或任何其他可变数据结构)将而不是具有相同的内存地址。

*x = [1, 5, 10]
y = [1, 5, 10]
id(x) == id(y)
>>> **False***

RealPython 的这篇文章对于理解 Python 中的“指针”非常有用。

你可能会问,这有什么关系?嗯,这就引出了我的下一个观点。

4.Python 可变缺省值是所有罪恶的根源

因为可变对象模拟了指针的功能,所以当用作函数或方法中的默认参数时,它也可能是万恶之源。

***常见用例:*列表(或其他可变数据结构)作为默认参数

正如你在下面的例子中看到的,这个在arr中使用List作为默认参数的increment_list函数可能会导致很多问题,因为在这个函数的return之后赋值的任何变量都拥有相同的内存地址(或对象*)。这实质上意味着这个可变对象的值甚至可以在函数之外修改。*

那么,可变数据结构应该如何被用作默认参数呢?

使用None作为默认值,并在函数中检查它。这确保了在创建新列表时,总是为这个List对象创建一个新的内存地址。

如何创建可变对象的副本?

我们可以在这些数据结构中使用.copy()方法来获得对象的副本。但是请注意,复制一个对象的结果是复制它,因此会消耗额外的内存。在进行复制时请记住这一点,尤其是在复制较大的对象(在内存中)时。这就把我带到了文章的最后一点— 复制 Python 对象

*a = {1: 'hello'}
b = a.copy()
id(a) == id(b)
>>> **False***

5.复制 Python 对象

正如我们在上面看到的,我们使用了字典数据结构的内置copy方法。这实际上是创建了对象的浅拷贝,而不是深拷贝。有什么区别?

  • 一个浅拷贝意味着构造一个新的集合对象,然后用在原始对象中找到的子对象的引用填充它。本质上,浅抄只是深一级*。复制过程不会递归,因此不会创建子对象本身的副本。[4]*
  • 一个深度复制使复制过程递归。它意味着首先构造一个新的集合对象,然后用原始集合中找到的子对象的副本递归地填充它。以这种方式复制对象会遍历整个对象树,从而创建原始对象及其所有子对象的完全独立的克隆。[4]

以上到底是什么意思?让我们来看一些例子。

好吧,这里刚刚发生了什么?在上面的例子中,我们看到list().copy()都创建了原始列表的浅层副本,而来自副本模块的deepcopy()创建了深层副本。

最初检查时,我们确实希望所有副本都有自己的内存地址,因为它们是可变对象。然而,再深入一层*(对象中的对象),我们观察到第一个索引中的第一个列表与浅拷贝的原始列表具有相同的内存地址!这也适用于原始列表中的其他子对象(列表)。对于深层副本,这是另外一种情况,因为我们已经递归地创建了包含这个副本的列表。*

如果我们修改原始或复制的对象会发生什么?

从上面可以看出,修改原始或浅复制对象的子对象将导致原始和浅复制对象的改变。深层复制的对象不受影响。

如果我们修改一个自定义类会发生什么?

我们不仅可以从内置对象中看到这种行为,还可以从自定义对象(如自定义类)中看到这种行为。下面,我们创建一个包含三个像素值作为属性的RGB类和一个接受两种颜色的复合TwoColors类,特别是RGB类的两个不同实例。

***问题:*如果我们修改pixel的属性,你认为会发生什么?

*pixel.red, pixel.blue, pixel.green = 100, 100, 100pixel
>>> **RGB(100,100,100)**pixel1
>>> **RGB(255, 255, 255)**pixel2
>>> **RGB(255, 255, 255)***

希望你没弄错——是的,复制的对象没有**变化,因为这是一个单独的对象。**

问题:如果我们修改复合对象colours中的子属性的属性会怎么样?

*colours.colour_one.red, colours.colour_two.blue = 111, 77colours
>>> **TwoColours(RGB(111, 0, 0), RGB(255, 255, 77)**colours1
>>> **TwoColours(RGB(111, 0, 0), RGB(255, 255, 77)**colours2
>>> **TwoColours(RGB(0, 0, 0), RGB(255, 255, 255)***

如您所料(希望如此),深度复制的对象不会修改其值,因为它是原始对象的完整副本。

结束语

至此,我希望您从这篇文章中学到了一些东西,并巩固了您的 Python 知识。感谢您的阅读!

如果你喜欢我的内容,并且没有订阅 Medium,请考虑支持我并通过我的推荐链接订阅这里 ( 注:你的一部分会员费将作为推荐费分摊给我)。

参考

Python,系统路径以及 conda 和 pyenv 如何操纵它

原文:https://towardsdatascience.com/python-the-system-path-and-how-conda-and-pyenv-manipulate-it-234f8e8bbc3e?source=collection_archive---------5-----------------------

深入探究当您键入“python”时会发生什么,以及流行的工具是如何操作的

都铎·巴休在 Unsplash 上的照片

系统路径(在 Mac/Linux 上的echo $PATHecho -e ${PATH//:/\\n}版本稍微漂亮一点)不仅仅是 python 的事情,它对 python 的功能也非常重要。如果你打开一个命令行,输入一个“命令”,计算机(或“操作系统”)需要知道在哪里寻找这个命令,找到它的底层代码,然后“执行”它。这些“命令文件”通常被称为可执行文件,当你键入与它们的文件名匹配的单词时,它们就会运行。

让我们以echo为例。当我们键入echo $PATH时,我们的计算机需要知道在哪里找到命令echo的代码,并通过传入一个参数$PATH来执行它。我们可以通过下面的代码看到代码在哪里:

> whereis echo
/bin/echo

其中whereis是我们的计算机知道如何定位的另一个可执行文件:

> whereis whereis
/usr/bin/whereis

有意思。他们坐在两个不同的位置。如果我们在这两个地方有相同的可执行文件名称会怎么样?我们会执行哪一个?

系统路径排序

$PATH变量决定了这一点,幸运的是,操作系统做了简单的事情,从左到右/从上到下地完成了它。作为一个例子,让我们看看我的系统变量$PATH的如下输出:

> echo -e ${PATH//:/\\n}
/usr/local/bin
/usr/bin
/bin
/usr/sbin
/sbin

假设我们去寻找一个名为myspecialexec的可执行文件。我们的系统将从顶层开始。在这种情况下,它将在目录/usr/local/bin中查找名为myspecialexec的可执行文件。如果找到了,就执行它。如果没有找到,它就移动到路径中指定的下一个目录——在这种情况下是/usr/bin,如果没有找到,就移动到/bin,依此类推。

这和 Python 有什么关系?

Python 是可执行的。当你运行brew install python或前往这里并运行 python 安装程序时,这就是你正在下载的东西(连同标准库和一些其他东西)。您正在执行以下操作之一:

  • 获取 python 可执行文件,并将其粘贴到$PATH中的上述目录之一
  • 获取 python 可执行文件并粘贴到其他地方,然后确保目录名现在在$PATH

因此,当您运行命令python时,您的操作系统开始向下滑动$PATH列表,寻找它能找到的第一个匹配的名字。如果您有多个版本,那么它将使用最先找到的版本。**如果你有一个你想让它找到的版本,那么你应该把那个 python 版本的位置放在路径的顶部。**最后一点是理解(和简化)所有这些工具“管理环境”和“管理 python 版本”的关键。一旦我们理解了我们的计算机如何决定使用哪个版本的 python,像包管理这样的其他事情就非常简单了。

例 1:conda 是如何做到这一点的,所以我们使用基于 conda 环境的正确版本的 python?

Conda 正如它自己描述的那样*“一个运行在 Windows、macOS 和 Linux 上的开源包管理系统和环境管理系统”*。这是我学习的第一个试图创建和管理稳定的 python 环境的系统,它因其与 python 中机器学习和统计编程的兴起(matplotlib、numpy、scipy、pandas、sklearn 等)的原始联系而广受欢迎。

它的“较轻”版本, miniconda ,是大多数人在谈论“康达”时谈论的内容。如果您要前往这里并安装它,您现在就会这样做,就像使用 python 一样:

  • 在你电脑的某个地方有一个conda可执行文件
  • 通过位于$PATH变量中的某个位置,该可执行文件将对您的 shell/终端可见

这意味着当你在终端中输入conda时,它会知道如何执行 conda 附带的功能。

那么 conda 如何确保你使用的是想要的 python 版本呢?

通过操作$PATH 变量,特别是利用它自顶向下的特性。为了建立一个例子,让我们做以下事情。假设安装了 conda,并且您打开了一个新的终端/外壳,您将看到:

(base) >

即,您将被定向到为您自动创建的默认基础 conda 环境。这里的目的不是谈论环境创建,而是说 conda 已经在某个地方创建了一个“基础”目录。在该目录中,它放置了:

  • 该环境附加到的 python 版本(因此,每次在基本环境中键入python时,它都会运行相同版本的 python)
  • 您可以为该环境下载的所有其他软件包依赖项

这个目录到底在哪里?我们可以通过使用命令which python看到:

(base) > which python
/Users/jamisonm/opt/miniconda3/bin/python

好极了。所以 conda 在某个地方创建了一个目录,并在其中放了一个我们可以调用的 python 版本。但是当我们在这个环境中的时候,我们的计算机怎么知道调用这个版本的 python 呢?因为它将该目录放在了$PATH 的顶部。我们可以证实这一点:

(base) > echo -e ${PATH//:/\\n}
/Users/jamisonm/opt/miniconda3/bin
/usr/local/bin
/usr/bin
/bin
/usr/sbin
/sbin

现在,当我停用环境,即我不想再使用这个环境和相应的 python 版本时,会发生什么?

(base) > conda deactivate

> echo -e ${PATH//:/\\n} # environment disabled so lose the (base)
/usr/local/bin
/usr/bin
/bin
/usr/sbin
/sbin

> which python
/usr/bin/python

所以conda deactivate已经从系统路径的顶部移除了/Users/jamisonm/opt/miniconda3/bin,所以我们默认使用之前的自顶向下搜索,并最终在/usr/bin/python中找到预装的 python。

创造一个新的环境怎么样?让我们创建一个名为conda-demo的新环境。

> conda create --name conda-demo
> conda activate conda-demo
(conda-demo) > echo -e ${PATH//:/\\n}
/Users/jamisonm/opt/miniconda3/envs/conda-demo/bin
/usr/local/bin
/usr/bin
/bin
/usr/sbin
/sbin

因此,康达又一次做到了以下几点:

  • 在某个地方创建了一个文件夹(/Users/jamisonm/opt/miniconda3/envs/conda-demo/bin)来存放附加到这个环境中的 python 版本(以及其他潜在的下载包)
  • 将该目录放在$PATH 的顶部,这样当我们键入python时,它会执行存在于该目录中的 python,而不是位于$PATH变量下方的目录

示例 pyenv 是如何做到这一点的,以便我们根据特定的环境使用正确的 python 版本?

与 conda 不同, pyenv (最初)并不是一个完全成熟的环境管理器,但更倾向于解决以下问题:

“我如何在同一台计算机上处理使用不同版本 python 的多个项目,并确保当我键入 *python* 时,我使用的是我想要使用的 python 版本?”

这是一个“python 版本管理”工具,关键是不涉及任何 Python。为了安装和管理不同的 python 版本,重要的是它本身不依赖于 python,而是主要是 bash 脚本的负载。

正如您可能已经猜到的,它通过操纵$PATH变量来完成 python 版本管理。事情比康达复杂一点,我们来看一个例子。假设您已经通过类似brew install pyenv的东西安装了,当您启动一个新的终端/shell 时,您应该准备好了(因为语句(或类似的东西)eval "$(pyenv init --path)"将被添加到您的.zshrc / .zprofile / .bash_profile / .bashrc的末尾,pyenv 命令将被加载到您的 shell 中。

树立榜样

在开始之前,让我们使用 pyenv 来建立一个示例,这样我们就可以准确地演示它是如何工作的。安装 pyenv 后,让:

**安装 2 个不同版本的 python,并将 3.9.7 设为全局版本**

在新的终端中,键入以下内容以安装 python 3.8.12 和 python 3.9.7,并将 python 3.9.7 设置为要使用的首选“全局”python 版本:

> pyenv install 3.8.12
> pyenv install 3.9.7
> pyenv global 3.9.7

然后,我们可以通过检查以下内容来检查这是否成功:

> pyenv versions
   system
   3.8.12
 * 3.9.7 (set by /Users/jamisonm/.pyenv/version)

新建一个项目,将 python 的本地版本设置为 3.8.12

为了理解 pyenv 如何同时管理多个 python 版本,我们需要创建一个“新项目”,并告诉它我们不想使用我们已经设置的“全局”python 版本——3 . 9 . 7。为此,您可以创建任何新目录并运行以下命令:

> mkdir ~/pyenv-demo    # make a new directory called pyenv-demo in my home directory
> cd ~/pyenv-demo       # cd into it
> pyenv local 3.8.12    # set 3.8.12 as the python version to be used in this directory

这做了什么?这就创建了一个只包含以下文本的.python-version文件:3.8.12。如果一切按计划进行,那么:

  • 当我们在这个目录(现在是一个简单类型的“环境”)中时,我们将使用 python 3.8.12
  • 当我们在它之外时,我们将使用默认的 3.9.7

现在我们已经准备好了,我们可以检查 pyenv 如何确保我们使用期望的 python 版本。

当我键入python时,pyenv 如何知道运行 python 的“本地”版本?

首先,让我们通过使用cd ~/pyenv-demo将自己转移到新目录。让我们看看当我们键入python时我们指向哪里,并且根据我们的路径,这是否有意义:

> which python
/Users/jamisonm/.pyenv/shims/python
> echo -e ${PATH//:/\\n}
/Users/jamisonm/.pyenv/shims
/usr/local/bin
/usr/bin
/bin
/usr/sbin
/sbin

因此,我们似乎在/Users/jamisonm/.pyenv/shims中使用了 python 可执行文件,正如 conda 的情况一样,这是因为 pyenv 在我们的路径顶部放置了一个名为/Users/jamisonm/.pyenv/shims的目录(因此首先搜索这个目录)。如果我们检查指定的 python 可执行文件,我们会发现它实际上不是真正的 python 可执行文件,而是一个名为“python”的 bash 脚本。这就是“填补程序”的含义——我们欺骗我们的操作系统运行这个可执行文件(它被称为 python,但实际上并不是python),而不是进一步搜索$PATH并找到一个真正的 python 可执行文件。

那么这个“填充”python 脚本是做什么的呢?

让我们来看看:

> less /Users/jamisonm/.pyenv/shims/python

#!/usr/bin/env bash
set -e
[ -n "$PYENV_DEBUG" ] && set -x

program="${0##*/}"

export PYENV_ROOT="/Users/jamisonm/.pyenv"
exec "/usr/local/opt/pyenv/bin/pyenv" exec "$program" "$@"

简单地说,它检查一些调试标准,设置一个变量PYENV_ROOT,然后调用另一个可执行文件:/usr/local/opt/pyenv/bin/pyenv。因此,到目前为止,它所做的不是调用 python,而是拦截对 python 的调用(通过操纵$PATH),然后调用自己。

我们可以检查这个可执行文件(/usr/local/opt/pyenv/bin/pyenv),但是它只有大约 150 行 bash 脚本。相反,假设一切顺利,我们可以把注意力集中在底部,它有:

command="$1"
case "$command" in
"" )
  { pyenv---version
    pyenv-help
  } | abort
  ;;
-v | --version )
  exec pyenv---version
  ;;
-h | --help )
  exec pyenv-help
  ;;
* )
  **command_path="$(command -v "pyenv-$command" || true)"**
  if [ -z "$command_path" ]; then
    if [ "$command" == "shell" ]; then
      abort "shell integration not enabled. Run \`pyenv init' for instructions."
    else
      abort "no such command \`$command'"
    fi
  fi

  shift 1
  if [ "$1" = --help ]; then
    if [[ "$command" == "sh-"* ]]; then
      echo "pyenv help \"$command\""
    else
      exec pyenv-help "$command"
    fi
  else
    **exec "$command_path" "$@"**
  fi
  ;;
esac

您可以自己检查脚本,或者相信我的话,它会为您指出:

  • command_path = /usr/local/Cellar/pyenv/2.2.0/libexec/pyenv-exec(这是因为我用 brew 安装了 pyenv,brew 把这个可执行文件放在了‘Cellar’目录中)
  • "$@" = python

这意味着现在我们已经被引导到另一个可执行文件上,即带有参数python/usr/local/Cellar/pyenv/2.2.0/libexec/pyenv-exec

那么接下来会发生什么?

现在这个脚本有点短了,所以我们可以把它全部打印出来,然后用文字浏览一遍:

set -e
[ -n "$PYENV_DEBUG" ] && set -x

# Provide pyenv completions
if [ "$1" = "--complete" ]; then
  exec pyenv-shims --short
fi

**PYENV_VERSION="$(pyenv-version-name)"**
PYENV_COMMAND="$1"

if [ -z "$PYENV_COMMAND" ]; then
  pyenv-help --usage exec >&2
  exit 1
fi

export PYENV_VERSION
PYENV_COMMAND_PATH="$(pyenv-which "$PYENV_COMMAND")"
PYENV_BIN_PATH="${PYENV_COMMAND_PATH%/*}"

OLDIFS="$IFS"
IFS=$'\n' scripts=(`pyenv-hooks exec`)
IFS="$OLDIFS"
for script in "${scripts[@]}"; do
  source "$script"
done

shift 1
if [ "${PYENV_BIN_PATH#${PYENV_ROOT}}" != "${PYENV_BIN_PATH}" ]; then
  # Only add to $PATH for non-system version.
  export PATH="${PYENV_BIN_PATH}:${PATH}"
fi
exec "$PYENV_COMMAND_PATH" "$@"

重要的一点是我强调的那一点。这个位在当前目录中检查一个.python-version文件,如果它存在,它就使用它。否则,它会遍历到该目录的所有路径,寻找其他的.python-version文件,直到找到一个。否则,它将默认使用我们设置的全局 python 版本。在这种情况下,它:

  • 在我们的 pyenv-demo 目录中找到.python-version文件
  • 3.8.12读取
  • 转到/Users/jamisonm/.pyenv/versions,pyenv python 可执行文件实际上存储在那里
  • 找到我们想要的,并将我们的可执行文件设置为/Users/jamisonm/.pyenv/versions/3.8.12/bin/python

上述过程在他们的 git 上有描述。

对于它正在实现的目标来说,这似乎相当复杂

这相当复杂——尤其是来自编程 python 和尝试通读一堆 bash 脚本。然而,最终所有这些都隐藏在对pyenv的简单调用之下。有时候,确切地了解正在发生的事情是很好的,但是日常生活中没有必要知道这些。然而,要点仍然存在——理解不同的环境管理程序和 python 版本化有时看起来很复杂,但归根结底只有一件事:以一致的方式操作$PATH变量,以便您想要的 python 版本就是您得到的 python 版本。

结论

希望通过这次经历,事情开始变得简单。“环境管理”和“python 版本控制”实际上可以归结为以下几点:

  • 环境管理:在某个地方创建一个目录,并将我们希望用于该环境的 python 版本放在那里
  • Python 版本控制:确保当我们在特定的目录/环境中时,在浏览$PATH时,首先找到附加的 python 可执行文件的路径
  • 包管理:将所需的包下载到我们想要的 python 可执行文件所在的“相同位置”,这样就可以用它们来代替其他环境中的包

最后一点我们还没有真正经历过,但是一旦我们理解了当我们键入python时,我们的计算机如何知道使用哪个版本的 python,它真的一点也不复杂。然而,要理解它,我们需要理解模块搜索路径和,正如我们将在下一篇文章中看到的,这实际上只不过是在 python 启动时对$PATH进行一些小的标准操作。

https://markjamison03.medium.com/python-and-the-module-search-path-e71ae7a7e65f [## Python 和模块搜索路径

markjamison03.medium.com](https://markjamison03.medium.com/python-and-the-module-search-path-e71ae7a7e65f)

Python 到 SQL —安全、轻松、快速地向上插入

原文:https://towardsdatascience.com/python-to-sql-upsert-safely-easily-and-fast-17a854d4ec5a?source=collection_archive---------2-----------------------

使用 Python 进行闪电般的插入和/或更新

是时候更新我们的存储空间并插入一些新东西了(图片由斯蒂夫·约翰森在 Unsplash 上提供)

当您将数据向上插入到表中时,您会更新已存在的记录并插入新的记录。阅读完本文后,您将能够将 Python 应用程序连接到数据库,并以闪电般的速度更新数据。我们将通过几个实际例子来演示实现这一点的各种方法。它们都适合处理大型数据集;关注高速度和数据有效性。我们来编码吧!

另外,我们将在本例中使用 SQL Server,但所使用的技术适用于各种数据库。

1.设置和准备

我们有一个出售各种音乐文章的网站。每小时我们都会收到一份来自所有供应商的文件,上面有他们当前的库存;他们出售的所有商品以及股票和价格信息。

在本例中,我们将关注一个供应商的文件。我们的目标是增加销量;插入所有新文章并更新已经存在的文章。也许他们有新的股票价值或不同的价格。作为奖励,我们将了解如何删除缺货的商品。

1.1 数据库准备

让我们首先在数据库中创建一个可以插入的表。

我们的音乐物品表

如你所见,我们的小网店卖不了多少东西。没关系,虽然,它会保持这篇文章漂亮和简洁。最值得注意的是 SKU 专栏。这是库存单位;每个产品都有一个独一无二的编号。

1.2 Python 加载数据

假设我们的供应商给我们发送了新的 CSV 文件和他们的文章更新。我们已经编写了一个 Python 程序,它一检测到新文件就加载该文件。现在的目标是加载 CSV,然后将其插入数据库。请记住,不需要从 CSV 中读取您的数据;任何数据结构都可以。首先,我们将加载 CSV:

将新产品从 csv 加载到字典列表中

我选择使用熊猫来阅读我的 CSV,但这不是必需的。你可以用任何你喜欢的方法。这会产生以下数据帧:

1.3 Python 创建数据库连接

我们将使用 SQLAlchemy 创建一个数据库连接,连接到在我的本地主机上运行的 SQL Server 数据库。

通知fast_executemany=True。这是一个专门针对 SQL Server 的功能,它可以确保以闪电般的速度插入数据。更多信息请见本文

准备工作做好了,我们来插入一些数据吧!(图片由将提升到像素上)

2.向上插入

加载我们的 CSV,准备好我们的表,建立数据库连接;让我们插上!

这个计划

我们首先将所有数据插入到一个临时表中。然后,如果这成功了,我们将合并我们的表。该操作允许您在一条语句中插入、更新删除!有关合并的更多信息,请查看本文

执行

我们将遍历下面的代码。

我们分 4 个步骤执行升级:

  1. 创建一个名为#NewProducts 的临时表。这里没什么太令人兴奋的
  2. 将新数据插入临时表。
    我们使用一些好的参数将我们的新值批量插入到临时表中。在插入到 SQL Server 时,此插入操作还使用 fast_executemany 选项;这是超快的。
  3. 将临时表与目标表合并(MusicArticles)
    这就是神奇的地方:合并!我们拿 SKU 来比较产品。
    -如果它们不在目标(我们的音乐物品表)中,那么我们将简单地插入新的乐器。
    -两个表中都有 SKU 的产品得到更新。我们还更新了修改后的列。
    -在 MusicalArticles 中但不在 temp 表中的产品从 MusicalArticles 中删除。所以实际上这个语句不仅插入和更新,它还删除!您可以选择删除这一部分,以保持我们的声明是一个纯粹的 upsert。
  4. 删除我们的临时表

安全的

还要注意,我们在起始于with dbEngine.begin() as con的代码块中执行这两个步骤。这将启动一个新的事务,确保块中的所有内容要么成功,要么回滚。这意味着如果合并失败,插入数据和创建临时表的操作将被撤消。更多交易信息请参见 本文

如果有更多的步骤,例如在插入或与许多表格合并后清理数据,这将非常方便。或者,您可以将 insert 语句放在不同的块中,这样大型插入就不会回滚,而只是将数据存储在临时表中。

结果

当然,这是一些不错的代码,但让我们看看一些结果!这是我们执行这个 python 脚本前后的 MusicalArticles 表。

在插入之前(红色将被删除,橙色将被修改)

向上插入后(橙色被修改,绿色被新插入)

你会注意到 SKU 123,24,33 和 34 被修改了。不是价格变了就是库存变了。此外,我们还有两款新仪器 SKU 35 和 111。还有,SKU 5577 不见了!肯定卖光了。对于 SKU 1233,一切都没有改变。

结论

通过这篇文章,我希望对如何安全、方便、快速地插入数据有所启发。如果你有建议/澄清,请评论,以便我可以改进这篇文章。与此同时,请查看我的其他关于各种编程相关主题的文章,比如:

  • 删除进入另一个表
  • 更新进入另一个标签页 le
  • 在一条语句中插入、删除和更新
  • 更新选择一批记录

编码快乐!

—迈克

页(page 的缩写)学生:比如我正在做的事情?跟我来!

Python 技巧:对照单个值检查多个变量

原文:https://towardsdatascience.com/python-tricks-check-multiple-variables-against-single-value-18a4d98d79f4?source=collection_archive---------13-----------------------

照片由克里斯里德在 Unsplash 上拍摄

如何一次用一个值比较多个变量?

欢迎阅读一系列短文,每篇短文都有方便的 Python 技巧,可以帮助你成为更好的 Python 程序员。在这篇博客中,我们将研究变量比较。

情况

你有变量x, y, z,你也有一个常量c,你想检查它是否存在于三个变量中。我们可以按照下面的逻辑用蛮力的方法来做,但是有更好的解决方案吗?

if (x == c) or (y == c) or (z == c):
    print("It exists!")
else:
    print("It does not exist!")

可能的解决方案:迭代器

if any(i == c for i in (x, y, z)):
    print("It exists!")
else:
    print("It does not exist!")

如果您想检查是否所有的x, y, z都有价值c,您也可以将any改为all。这两个函数检查所提供的迭代器中是否有一个/全部被评估为True。这意味着如果我们有一个元组t = (0, 1, 2),那么all(t)将返回False,因为第一个元素0将被计算为False。这也提供了很大的灵活性,因为像0None[]()""{}这样的公共值都将被评估为False

注意x, y, z已经被合并到一个元组中,而不是一个列表中,以获得更好的内存性能。如果您想了解更多关于数据结构的内存使用情况,我以前写过一篇文章:

更好的解决方案:使用 Iterable 的成员测试

除了创建一个 tuple 并遍历它,我们还可以通过成员测试来完成它,而不需要使用any

# Membership test with list
if c in [x, y, z]:
    print("It exists!")
else:
    print("It does not exist!")# Membership test with tuple
if c in (x, y, z):
    print("It exists!")
else:
    print("It does not exist!")# Membership test with set
if c in {x, y, z}:
    print("It exists!")
else:
    print("It does not exist!")

虽然listtupleset都支持成员测试,因为它们都是 Python 中的可迭代对象,但是在选择使用哪一个时还是有细微的差别。例如,假设元组中值的唯一性很高,使用tuple将占用三者中最少的内存。另一方面,set的实现允许常数成本成员测试,这意味着它在三者中具有最小的计算复杂度,尽管构建一个集合可能会超过它的好处。而对于list,在这种情况下使用变长数组没有任何优势,所以忘了它吧。

这篇博文就讲到这里吧!我希望你已经发现这是有用的。如果你对其他 Python 技巧感兴趣,我为你整理了一份简短博客列表:

  • Python 技巧:拉*列表
  • Python 技巧:简化 If 语句&布尔求值
  • Python 技巧:如何检查与熊猫的表格合并

如果你想了解更多关于 Python、数据科学或机器学习的知识,你可能想看看这些帖子:

  • 改进数据科学工作流程的 7 种简单方法
  • 熊猫数据帧上的高效条件逻辑
  • 常见 Python 数据结构的内存效率
  • Python 的并行性
  • 为数据科学建立必要的 Jupyter 扩展
  • Python 中高效的根搜索算法

如果你想了解更多关于如何将机器学习应用于交易和投资的信息,这里有一些你可能感兴趣的帖子:

  • Python 中用于交易策略优化的遗传算法
  • 遗传算法——停止过度拟合交易策略
  • 人工神经网络选股推荐系统

https://www.linkedin.com/in/louis-chan-b55b9287

特别感谢 Sander Koelstra 指出了一些误导性的陈述。

Python 技巧:扁*化列表

原文:https://towardsdatascience.com/python-tricks-flattening-lists-75aeb1102337?source=collection_archive---------28-----------------------

由 Max Duzij 在 Unsplash 上拍摄的照片

你还在遍历 for 循环吗?

欢迎阅读一系列短文,每篇短文都有方便的 Python 技巧,可以帮助你成为更好的 Python 程序员。在这篇博客中,我们将探讨如何扁*化列表。

情况

我们都处理过列表的列表,甚至更糟:嵌套列表的列表。

理论上,我们可以像剥洋葱一样一层一层地剥列表:

l = [0, 1, 2, [3, 4, 5, [6, 7, 8]]]from collections import Iterabledef unwrap(l):
    flat_list = []
    for item in l:
        if isinstance(item, Iterable):
            flat_list.extend(item)
        else:
            flat_list.append(item)
    return flat_listl = unwrap(l)
# [0, 1, 2, 3, 4, 5, [6, 7, 8]]l = unwrap(l)
# [0, 1, 2, 3, 4, 5, 6, 7, 8]

这也意味着无论有多少嵌套层,我们都需要运行unwrap

有没有更有效的方法来展*嵌套列表?

候选解决方案:itertools

itertools是 Python 的标准库之一,它提供了创建迭代器以实现高效循环的便捷函数。其中有一个名为chain的函数,它创建了一个链式对象来连接一个可迭代列表。链对象可以理解为遍历每个可迭代对象的迭代器。因此,如果你需要的不仅仅是一个迭代器,你就需要把它显式地转换成一个链表。

from itertools import chainl = [[0, 1], [2, 3]]
list(chain(*l))# [0, 1, 2, 3]list(chain.from_iterable(l))# [0, 1, 2, 3]

然而,chain也有一些限制:它连接了一个可迭代的列表,但是没有进一步展开,并且它还要求所有元素都是可迭代的:

from itertools import chainl = [[0, 1], [2, 3, [4, 5]]]
list(chain(*l))# [0, 1, 2, 3, [4, 5]]l = [0, 1, [2, 3, [4, 5]]]
list(chain(*l))# TypeError: 'int' object is not iterable

候选解决方案:熊猫

作为itertools.chain的替代,熊猫在pandas.core.common下有一个flatten功能。与itertools.chain类似,flatten返回一个生成器而不是一个列表。生成器和迭代器在实现和好处上有一些不同。虽然我们将在以后介绍这一点,但它们之间的一个简单的区别是,生成器是使用 yield 语句的迭代器,并且只能读取一次。

from pandas.core.common import flattenl = [0, 1, [2, 3, [4, 5]]]
list(flatten(l))# [0, 1, 2, 3, 4, 5]

从上面的例子中,我们可以看到flatten也像chain一样处理嵌套列表!

结束语

那么我们应该用哪一个呢?

看情况。

如果你有一个嵌套列表的列表,要么重构代码以避免创建那个怪物,要么使用flatten一次完全展*它。

如果你有一个列表列表,那么chain就足够了。毕竟chain也比flatten

l = [[0, 1], [2, 3, 4], [5, 6, 7, 8]]%timeit list(chain(l))
# 236 ns ± 7.68 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)%timeit list(flatten(l))
# 5.13 µs ± 164 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

这篇博文就讲到这里吧!我希望你已经发现这是有用的。如果你对其他 Python 技巧感兴趣,我为你整理了一份简短博客列表:

  • Python 技巧:如何检查与熊猫的表格合并
  • Python 技巧:简化 If 语句&布尔求值
  • Python 技巧:对照单个值检查多个变量

如果你想了解更多关于 Python、数据科学或机器学习的知识,你可能想看看这些帖子:

  • 改进数据科学工作流程的 7 种简单方法
  • 熊猫数据帧上的高效条件逻辑
  • 常见 Python 数据结构的内存效率
  • 与 Python 并行
  • 数据科学的基本 Jupyter 扩展设置
  • Python 中高效的根搜索算法

如果你想了解更多关于如何将机器学习应用于交易和投资的信息,这里有一些你可能感兴趣的帖子:

  • Python 中交易策略优化的遗传算法
  • 遗传算法——停止过度拟合交易策略
  • 人工神经网络选股推荐系统

https://www.linkedin.com/in/louis-chan-b55b9287

Python 技巧:生成器解释

原文:https://towardsdatascience.com/python-tricks-generators-explained-4b4ba00402e4?source=collection_archive---------29-----------------------

照片由大卫·卡尔波尼在 Unsplash 拍摄

Python 中的生成器是如何工作的?

欢迎阅读一系列短文,每篇短文都有方便的 Python 技巧,可以帮助你成为更好的 Python 程序员。在这篇博客中,我们将研究发电机。

介绍

生成器是 iterable 的子类。

为了理解生成器是如何工作的,我们需要首先修改 iterables 是如何工作的。

可重复的

iterable 是可以迭代的对象。

当我们写 for 循环时,它以一个for x in y:语句开始。for语句正在迭代表达式y。因此,y是可迭代的。

一些非常常见的可重复项有:列表、元组、字符串等

for i in [0, 1, 2]:
    print(i)for i in (0, 1, 2):
    print(i)for i in "012":
    print(i)

Iterables 将值存储在内存中,并允许使用__iter____next__方法进行迭代。以上面的例子为例,在使用__iter__进行迭代之前,首先创建一个值为"012"的字符串并存储在内存中。因此,iterables 的一个主要优点是它们可以被访问和迭代任意多次。同时,作为将它存储在内存中的折衷,当你的 iterables 变长时,它将占用更多的内存。

发电机

生成器是只能迭代一次的 iterable 的子类。

本质上,生成器生成(产出)一个值,忘记它,并生成下一个值,直到满足结束条件。

在选择使用生成器还是迭代器时,有几个关键因素值得考虑:

  1. 你需要内存中的全部结果吗?
  2. 您需要按原样重用原始结果吗?
  3. 你的结果是否足够小,可以放入内存?
  4. 是否要在获得所有结果后处理结果?

如果以上都是肯定的,那么迭代器就足够了。否则,您可能想考虑使用一个生成器来从延迟执行和动态让步中获益。

您可以通过两种方式创建发生器:

**# 1\. list comprehension but with parenthesis**g = (i ** 2 for i in range(10))
type(g)# generator**# 2\. use yield**def create_generator(i):
    for i in range(i):
        yield i ** 2type(create_generator(10))
# generator

假设你有一个生成器,你想把值存储在一个列表中。由于生成器会动态地生成值,因此需要对其进行迭代,以获取用于创建列表的值。由于生成器只能迭代一次,因此之后生成器将为空。如果我们迭代并中途停止,情况也是如此:

g = (i for i in range(5))**# Cast the generator to a list for the first time**
l = list(g)
l# [0, 1, 2, 3, 4]**# Cast the generator to a list for the second time**
l = list(g)
l# []g = (i for i in range(5))**# Iterate the generator and stop halfway through**
for i in g:
    if i == 2:
        breaklist(g)# [3, 4]

收益陈述&发电机是如何工作的?

现在我们已经介绍了生成器的基本机制,让我们通过下面的例子来看看生成器实际上是如何工作的:

import numpy as np
import timedef create_generator(n):
    """ A generator that generate n arrays of 10^n random number
    between 0 and 1\. """ length = 10 ** n
    print(f"length: {length}") for _ in range(n):
        time.sleep(_)
        print(_)
        yield np.random.rand(length)
        print("After yielding") print("End of generator")

收益率报表是更现实的生成器的核心。

一个普通的函数在被调用时将被执行,并使用一个return语句返回结果(如果没有语句,则返回None)。

一个生成器函数在被调用时不会被执行,而是只会返回一个生成器对象。

好吧,但那是什么意思?

假设我们有一个使用生成器的 for 循环:

g = create_generator(3)
print(type(g))
print("Before For Loop")for i in g:
    print("Got something!")print("After For Loop")

我们将从片段中得到的是:

<class 'generator'>
Before For Loop
length: 1000
0
Got something!
After yielding
1
Got something!
After yielding
2
Got something!
After yielding
End of generator
After For Loop

发电机的工作方式是:

  1. 代码在创建时不会被执行,而是返回一个生成器对象
  2. for语句第一次使用生成器时,生成器代码将一直执行到yield语句
  3. 发电机的任何后续使用将恢复进度并运行,直到它再次到达yield语句
  4. 一旦生成器到达末尾,它将运行剩余的代码

这篇博文就讲到这里吧!我希望你已经发现这是有用的。如果你对其他 Python 技巧感兴趣,我为你整理了一份简短博客列表:

  • Python 技巧:扁*化列表
  • Python 技巧:如何检查与熊猫的表格合并
  • Python 技巧:简化 If 语句&布尔求值
  • Python 技巧:对照单个值检查多个变量

如果你想了解更多关于 Python、数据科学或机器学习的知识,你可能想看看这些帖子:

  • 改进数据科学工作流程的 7 种简单方法
  • 熊猫数据帧上的高效条件逻辑
  • 常见 Python 数据结构的内存效率
  • 与 Python 并行
  • 为数据科学设置必要的 Jupyter 扩展
  • Python 中高效的根搜索算法

如果你想了解更多关于如何将机器学习应用于交易和投资的信息,这里有一些你可能感兴趣的帖子:

  • Python 中交易策略优化的遗传算法
  • 遗传算法——停止过度拟合交易策略
  • 人工神经网络选股推荐系统

https://www.linkedin.com/in/louis-chan-b55b9287

Python 技巧:如何检查与熊猫的表合并

原文:https://towardsdatascience.com/python-tricks-how-to-check-table-merging-with-pandas-cae6b9b1d540?source=collection_archive---------13-----------------------

照片由米卡·鲍梅斯特在 Unsplash 上拍摄

一个让你免受挫折的关键词

欢迎阅读一系列短文,每篇短文都有方便的 Python 技巧,可以帮助你成为更好的 Python 程序员。在这篇博客中,我们将探讨检查熊猫的连接。

情况

当我们在 Python 中处理数据集集合时,知道如何将表连接在一起是至关重要的。我们都知道 SQL 中的INNER JOINLEFT JOINRIGHT JOINFULL OUTER JOIN以及在熊猫中是如何做到的,但你知道还有一个不太为人知的关键字参数叫indicator吗?

出于说明的目的,我们将使用下面的表格,其中ID将被用作本博客剩余部分的表格之间的公共键:

import pandas as pddf_left = pd.DataFrame({
    "ID": [0, 1, 2],
    "first_name": ["Dave", "Henry", "Alex"],
    "last_name": ["Smith", "Adams", "Brown"],
})df_right = pd.DataFrame({
    "ID": [1, 2, 3],
    "location": ["London", "Berlin", "Paris"],
    "age": [25, 34, 18],
})

说明性虚拟表格—作者图片

Pandas 中类似 SQL 的连接操作的快速修改

**# Inner Join**
df_results = df_left.merge(
    df_right, 
    on=["ID"], 
    how="left"
)**# Left Join**
df_results = df_left.merge(
    df_right, 
    on=["ID"], 
    how="left"
)**# Right Join**
df_results = df_left.merge(
    df_right, 
    on=["ID"], 
    how="right"
)**# Outer Join**
df_results = df_left.merge(
    df_right, 
    on=["ID"], 
    how="outer"
)

加入插图-作者图片

注意,我们使用了一个列表作为关键字参数on的输入,以展示放入多个键列是可能的。如果ID是唯一的键列,请随意使用"ID"而不是["ID"]

检查合并结果

每当连接两个表时,检查结果表。无数个夜晚,我试图合并表,并认为连接是正确的(双关语😉)意识到它应该被留下。您最不想做的事情就是重新访问您几个月前完成的连接,这就是为什么当您进行合并时,传递indicator=True以获得有关合并结果的更多信息:

**# Outer Join**
df_results = df_left.merge(
    df_right, 
    on=["ID"], 
    how="outer",
    indicator=True,
) ID first_name last_name location   age      _merge
0   0       Dave     Smith      NaN   NaN   left_only
1   1      Henry     Adams   London  25.0        both
2   2       Alex     Brown   Berlin  34.0        both
3   3        NaN       NaN    Paris  18.0  right_only

使用_merge列,我们不仅可以检查任意一个表中存在的行数,还可以提取精确的行以进行进一步的质量检查。这在很多情况下有助于理解数据处理流程中的问题。

排除合并/反合并

使用_merge列,我们还可以很容易地实现所谓的左/右合并,不包括连接和反合并。left excluding join 生成的表只包含第一个表中的记录,这些记录在第二个表中没有匹配项;右排除连接是它的镜像。反合并本质上是排除连接的左和右的组合。

**# LEFT EXCLUDING JOIN**
df_results = df_left.merge(
    df_right, 
    on=["ID"], 
    how="left",
    indicator=True,
)
df_results = df_results[df_results["_merge"] == "left_only"] ID first_name last_name location  age     _merge
0   0       Dave     Smith      NaN  NaN  left_only

不用在选择_merge == "left_only"之前分配中间结果,我们实际上可以使用 Pandas DataFrame 的query方法来实现,然后删除_merge列:

**# LEFT EXCLUDING JOIN**
df_results = (df_left.merge(df_right, 
                            on=["ID"], 
                            how="left",
                            indicator=True)
                     .query("_merge != 'both'")
                     .drop("_merge", 1)) ID first_name last_name location  age
0   0       Dave     Smith      NaN  NaN**# RIGHT EXCLUDING JOIN**
df_results = (df_left.merge(df_right, 
                            on="ID", 
                            how="right",
                            indicator=True)
                     .query("_merge != 'both'")
                     .drop("merge", 1)) ID first_name last_name location  age
2   3        NaN       NaN    Paris   18**# ANTI MERGE**
df_results = (df_left.merge(df_right, 
                            on=["ID"], 
                            how="outer",
                            indicator=True)
                     .query("_merge != 'both'")
                     .drop("_merge", 1)) ID first_name last_name location   age
0   0       Dave     Smith      NaN   NaN
3   3        NaN       NaN    Paris  18.0

加入插图-作者图片

这篇博文就讲到这里吧!我希望你已经发现这是有用的。如果你对其他 Python 技巧感兴趣,我为你整理了一份简短博客列表:

  • Python 技巧:拉*列表
  • Python 技巧:简化 If 语句&布尔求值
  • Python 技巧:对照单个值检查多个变量

如果你想了解更多关于 Python、数据科学或机器学习的知识,你可能想看看这些帖子:

  • 改进数据科学工作流程的 7 种简单方法
  • 熊猫数据帧上的高效条件逻辑
  • 常见 Python 数据结构的内存效率
  • 与 Python 并行
  • 数据科学的基本 Jupyter 扩展设置
  • Python 中高效的根搜索算法

如果你想了解更多关于如何将机器学习应用于交易和投资的信息,这里有一些你可能感兴趣的帖子:

  • 用 Python 实现交易策略优化的遗传算法
  • 遗传算法——停止过度拟合交易策略
  • 人工神经网络选股推荐系统

https://www.linkedin.com/in/louis-chan-b55b9287

Python 招数:“是”还是“==”

原文:https://towardsdatascience.com/python-tricks-is-or-1033eddafa4?source=collection_archive---------45-----------------------

一个存在性问题背后的答案,用 python 写的!

作者图片

你有没有收到过 Pycharm 的警告说:

使用相等运算符执行与 None 的比较

想知道为什么?

好了,今天我们要彻底揭开这个秘密!

做好准备,这实际上是一个相当深刻的问题,可能会让你质疑生命的意义。

是 Python 吗?

是的是的是的。

众所周知,Python 有两个操作符:“is”和“==”,在许多常规情况下,它们会产生相同的结果。但是,如果不了解它们之间的区别,您可能会遇到一些令人费解的错误,这些错误会让您绞尽脑汁几个小时。

这里有一个例子:

from copy import copy

def compare(a, b):

    print("--- compare a and b")
    print(f"id(a): {id(a)}")
    print(f"id(b): {id(b)}")

    print(f"a == a: {a == a}")
    print(f"a == b: {a == b}")
    print(f"a is a: {a is a}")
    print(f"a is b: {a is b}")

class Python:
    def __init__(self, size):
        self.size = size

    def __eq__(self, other):
        return self.size == other.size

print('test 1')
a = Python(5)
b = Python(6)
compare(a, b)

print('test 2')
a = Python(5)
b = Python(5)
compare(a, b)

print('test 3')
a = Python(5)
b = copy(a)
compare(a, b)

print('test 4')
a = Python(5)
b = a
compare(a, b)

我们有一个非常简单的类,叫做 Python,它有一个“eq”操作符,如果要比较的另一个对象有相同的大小,它返回 True。

测试 1:实例化两条大小分别为 5、6 的 Pythons。

测试 2:实例化两条大小为 5 的蟒蛇。

测试 3:为 a 实例化一个 Python,为 b 制作一个副本。

测试 4,为 a 实例化一个 Python,然后赋给 b。

以下是结果:

test 1
--- compare a and b
id(a): 2810649485760
id(b): 2810649485664
a == a: True
**a == b: False** # different sizes
a is a: True
a is b: False
test 2
--- compare a and b
id(a): 2810648609840
id(b): 2810648608832
a == a: True
**a == b: True**
a is a: True
a is b: False
test 3
--- compare a and b
**id(a): 2810649485760
id(b): 2810649485808 # different ids**
a == a: True
a == b: True
a is a: True
**a is b: False**
test 4
--- compare a and b
**id(a): 2810648609840
id(b): 2810648609840** # same ids
a == a: True
a == b: True
a is a: True
**a is b: True**

查看测试 1 和测试 2 的结果,我们注意到“a == b”对于测试 1 为假,而对于测试 2 为真。这是可以理解的,因为 python a 和 b 对于 test 1 有不同的大小。

查看测试 3 和测试 4 的结果,我们看到“a 是 b”对于测试 3 为假,但是对于测试 4 为真。这很奇怪,因为在测试 3 和测试 4 中,a 和 b 的大小都是 5。但是看一下 a 和 b 的 id,在测试 3 中,id 是不同的,而在测试 4 中它们是相同的。

从这些测试中,我们可以得出结论:

“= =”运算符调用“eq”函数确定相等,而“is”运算符比较对象 id 确定相等。

Python 是龙吗?

依赖

蟒蛇是龙吗?(图片由作者提供)

让我们定义另一个类 Dragon,并与 python 进行比较:

class Dragon:
    def __init__(self, size):
        self.size = size

print('test 5')
a = Python(5)
b = Dragon(5)
compare(a, b)

print('test 6')
a = Dragon(5)
b = Python(5)
compare(a, b)

print('test 7')
a = Dragon(5)
b = Dragon(5)
compare(a, b)

结果:

test 5
--- compare a and b
id(a): 2810649489264
id(b): 2810649488544
a == a: True
**a == b: True**
a is a: True
a is b: False
test 6
--- compare a and b
id(a): 2810648806784
id(b): 2810648806112
a == a: True
**a == b: True**
a is a: True
a is b: False
test 7
--- compare a and b
id(a): 2810649489264
id(b): 2810649488544
a == a: True
**a == b: False**
a is a: True
a is b: False

“is”的结果和预期的一样,它对三种情况都是假的,因为每种情况下的 id 都不同。

“==”的结果有点奇怪,在所有三种情况下,dragon 和 python 的大小都是 5,但是当我们使用“python == dragon”和“dragon == python”时,结果是真的,而“dragon == dragon”是假的。

这实际上是由于 Python 和 Dragon 类是如何定义的。仔细看,我们看到 Dragon 没有定义“eq”操作符。这意味着当我们测试“python == dragon”和“dragon == python”时,调用了 python 的“eq”运算符,但是当我们测试“dragon == dragon”时,由于两边都没有“eq”运算符,所以比较默认为“is”,它比较两条龙的 id,结果为 false。

龙,双倍大(图片由作者提供)

如果我们为 Dragon 定义一个“eq”操作符,情况会变得更加有趣:

class Dragon2:
    def __init__(self, size):
        self.size = size

    def __eq__(self, other):
        return self.size * 2 == other.size

print('test 8')
a = Python(5)
b = Dragon2(5)
compare(a, b)

print('test 9')
a = Dragon2(5)
b = Python(5)
compare(a, b)

龙是一个自负的物种,当他们和别人比较时,他们想象自己是两倍大:

test 8
--- compare a and b
id(a): 2810649486096
id(b): 2810649487920
a == a: True
**a == b: True**
a is a: True
a is b: False
test 9
--- compare a and b
id(a): 2810628730688
id(b): 2810649488544
a == a: False
**a == b: False**
a is a: True
a is b: False

所以在这种情况下“Python(5) == Dragon2(5)”产生 true,而“Dragon2(5) == Python(5)”产生 false!

0 等于 0 吗?

总是

既然我们已经了解了“是”和“==”是如何工作的,那么让我们来测试一个重要的默认类型,整数:

print('test 10')
a = int(0)
b = int(0)
compare(a, b)print('test 11')
a = int(0)
b = a
compare(a, b)print('test 12')
a = int(0)
b = copy(a)
compare(a, b)

结果:

test 10
--- compare a and b
id(a): 2810573515184
id(b): 2810573515184
a == a: True
a == b: True
a is a: True
a is b: True
test 11
--- compare a and b
id(a): 2810573515184
id(b): 2810573515184
a == a: True
a == b: True
a is a: True
a is b: True
test 12
--- compare a and b
id(a): 2810573515184
id(b): 2810573515184
a == a: True
a == b: True
a is a: True
a is b: True

你看,python 的创造者们一直在努力向你隐藏“是”和“”之间的区别。F **或整数,只要值相同,使用“is”和“”结果没有区别。**

是没有还是等于没有?

让我们不要迷惑自己

Pycharm 警告无比较(图片由作者提供)

让我们来看看 None,那么为什么 Pycharm 警告你在与 None 比较时不要使用“== ”?

**真的是为了保护你不犯粗心的错误。**因为 None 是一个常量,它总是只有一个实例,所以没有理由使用“==”操作符,它试图调用“eq”操作符,除非你真的想做一些奇怪的事情,如下所示:

class EndOfWorld:
    def __eq__(self, other):
        return other is None

a = EndOfWorld()
a == None
# Out[58]: True

忒修斯之船

让我们谈谈哲学

所以,python 的创造者们似乎做出了一个决定,每个对象都有一个 id,如果 id 相同,那么两个对象就是彼此。这与“忒修斯之船”的思想实验有着有趣的相似之处,在这个实验中,身份的问题是通过询问一艘所有组件都被替换的船是否仍然是同一艘船而提出的。

参见维基上的忒修斯的船。

事实证明,你只需要一张身份证!

(我的 id 是什么?)

结论

我希望我的读者在学习 python 对象标识比较的细微差别时度过了一段美好的时光。这些概念不是 python 独有的,它们以引用和指针的形式存在于 C 和 C++中,类似地,在许多其他语言中也存在,所以值得花时间去理解细节以避免错误。

尽管在编程语言的抽象世界中给每样东西分配一个 id 是相当容易的,但是在现实生活中给对象分配一个 id 却不那么容易,你认为呢?

Python 技巧:简化 If 语句和布尔求值

原文:https://towardsdatascience.com/python-tricks-simplifying-if-statements-boolean-evaluation-4e10cc7c1e71?source=collection_archive---------5-----------------------

照片由 Hitesh Choudhary 在 Unsplash 上拍摄

布尔求值如何帮助缩短 if 语句?

欢迎阅读一系列短文,每篇短文都有方便的 Python 技巧,可以帮助你成为更好的 Python 程序员。在这篇博客中,我们将研究布尔运算。

情况

假设我们有一个变量x,如果满足某个条件,我们想做点什么。基本方法如下:

if x == condition:
    print("Conditions met!")
else:
    print("Conditions not met!")

使用运算符==,我们正在对x是否等于condition进行布尔运算。但是你知道吗,我们并不总是需要显式地调用一个布尔求值来使 if 语句工作。

数字类型的布尔计算

我们都知道 Python 中的TrueFalse可以分别用整数表示为10。这意味着不用检查x是否是1,我们可以简单地做以下事情:

x = 1# This will return True
if x:
    print("True")
else:
    print("False")

事实上,除了00.0之外的所有整数和浮点数都将返回True作为布尔值。(是的,甚至np.infnp.nan也被评估为True)

for x in range(-10, 11):
    print(f"{x}: {bool(x)}") # Only 0 will be Falseimport numpy as np
for x in np.arange(-10, 11, .5):
    print(f"{x}: {bool(x)}") # Only 0.0 will be Falseprint(bool(np.inf)) # True
print(bool(np.nan)) # True

无的布尔评估

从输入中提取信息的函数如果提取不出任何信息,返回None是很常见的。这方面的一个例子是re.match:

import rematch = re.match(r"Goodbye", "Hello, World!")
if match is None:
    print("It doesn't match.")
else:
    print("It matches.")

然而,当你有一堆if x is Noneif x is not None等的时候,这可能会令人困惑。这就是使用布尔评估和坚持一致的方法可以有所帮助的地方:

import rematch = re.match(r"Goodbye", "Hello, World!")
if match:
    # this block will run if match is **not** **None**
    print("It match.")
else:
    # this block will run if match is **None**
    print("It doesn't match.")

根据你的喜好,你可以把if match或者if not match作为你的 if 语句。这里的关键是在同一个代码库中坚持使用而不是混合使用这两种代码。此外,如果您使用的是 Python 3.9 或更高版本,还可以使用 Walrus 运算符来进一步简化上面的代码:

import reif match := re.match(r"Goodbye", "Hello, World!"):
    # this block will run if match is **not** **None**
    print("It match.")
else:
    # this block will run if match is **None**
    print("It doesn't match.")

可重复项的布尔求值

除了intfloatNone之外,还可以将dictsetlisttuplestr评价为布尔(注意namedtuple不在其中)。布尔求值在这些可迭代对象上工作的方式是使用它们内置的__len__方法。也就是说,iterable 的长度将被用作布尔值。对数字类型重新应用布尔求值逻辑,我们现在也知道了下面的特殊情况,其中 iterables 被求值为False:

print(bool(tuple())) # Falseprint(bool(dict()))  # Falseprint(bool(set()))   # Falseprint(bool(list()))  # Falseprint(bool(""))      # False

这意味着不做:

x = [0, 1, 2, 3]if len(x) > 0:
    print("The list is not empty")
else:
    print("The list is empty")

我们可以这样做:

x = [0, 1, 2, 3]if x:
    print("The list is not empty")
else:
    print("The list is empty")

类的布尔求值

现在我们知道了更复杂的数据结构的布尔求值的魔力依赖于内置方法__len__,我们可以将这一知识扩展到其他类。假设我们有一门课:

class Dummy:
    def __init__(self):
        self.__state = False

    def toggle(self):
        self.__state = not self.__state

    def __len__(self):
        return self.__state

如果我们运行下面的脚本,我们将看到类Dummy的布尔求值是如何变化的:

d = Dummy()bool(d) # Falsed.toggle()
bool(d) # Trued.toggle()
bool(d) # False

请注意,在更改self.__state后调用__len__将会抛出一个TypeError,因为None不能被转换为int类型,而int__len__应该返回的类型。

结论

这篇博客讨论了布尔求值是如何工作的,以及它如何帮助简化 if 语句,并使代码库更加一致地可维护。概括一下,下面是 Python 中一些非布尔类型的布尔求值逻辑列表:

  • int : False如果0,否则True
  • float : False如果0.0,否则True
  • dictsetlisttuplestr:如果长度为0,则为False,否则为True
  • 自定义类:False如果__len__返回0,否则True

这篇博文就讲到这里吧!我希望你已经发现这是有用的。如果你对其他 Python 技巧感兴趣,我为你整理了一份简短博客列表:

  • Python 技巧:扁*化列表
  • Python 技巧:对照单个值检查多个变量
  • Python 技巧:如何检查与熊猫合并的表格

如果你想了解更多关于 Python、数据科学或机器学习的知识,你可能想看看这些帖子:

  • 改进数据科学工作流程的 7 种简单方法
  • 熊猫数据帧上的高效条件逻辑
  • 常见 Python 数据结构的内存效率
  • 与 Python 并行
  • 数据科学的基本 Jupyter 扩展设置
  • Python 中高效的根搜索算法

如果你想了解更多关于如何将机器学习应用于交易和投资的信息,这里有一些你可能感兴趣的帖子:

  • Python 中交易策略优化的遗传算法
  • 遗传算法——停止过度拟合交易策略
  • 人工神经网络选股推荐系统

https://www.linkedin.com/in/louis-chan-b55b9287

Python 类型提示和文档字符串

原文:https://towardsdatascience.com/python-type-hints-docstrings-7ec7f6d3416b?source=collection_archive---------7-----------------------

将 Python 的类型提示(3.5+)中的变量类型自动插入到 Google 风格的文档字符串中

从类型提示到文档字符串,作者的图像。

在这个故事中,您将跟随我从 Python 类型提示自动生成 Google 风格的文档字符串。我们将关注以下要素。

  • Python 类型提示
  • 将类型提示插入函数文档字符串
  • 通过预提交 Git 挂钩实现自动化

Python 类型提示

从 Python 3.5+开始,我们已经看到了下一代代码文档:在函数/类参数和返回语句中提示哪些变量的类型。这使得格式化程序、linters 和 IDE 能够为类型检查提供运行时支持。

Python 类型提示示例

为什么是类型提示

简单地说,类型提示增加了源代码文档和可读性。它们是 Python 的增强协议(PEP)的一部分,PEP 是一个用于增加 Python 代码的清晰度和逻辑性的进化框架。

如何使用类型提示

类型提示可以从typing模块导入。格式为<variable name> : <variable type>,可以直接插入函数参数中。您可以用-> <return variable>包含返回声明。

类型检查

请注意,类型提示是可选的,因此 Python 解释器会完全忽略它们(带有错误格式的类型提示的 Python 代码仍然会运行)。然而,像 Pycharm 这样的 ide 已经集成了类型检查,并且像mypy这样的静态类型检查工具将类型错误作为 bug 挖掘出来。

PyCharm 中的类型检查

将类型提示插入函数文档字符串

为了从函数定义中提取参数及其类型提示,我们将

  1. 使用abstract syntax trees解析 Python 脚本并读取函数及其参数
  2. 使用库从我们的函数参数中获取变量类型提示
  3. 使用regular expressions将变量类型映射到 Google 风格的 docstring 格式

Google 风格的文档字符串的自动变量类型添加

抽象语法树

[ast](https://docs.python.org/3/library/ast.html#module-ast)模块帮助 Python 应用程序处理 Python 抽象语法的树。我们现在可以通过ast nodes以编程方式读取语法,而不是将 Python 脚本作为文本文件读取。打印和理解这些节点的一个有用的包是astpretty。我们使用ast模块来获取脚本中的函数或类名。

pathlib.Path模块创建了一个面向对象的文件系统路径,您可以查询这个路径的属性,比如最后一个路径组件.stem,没有它的后缀。我们可以用importlib.import_module将函数或类作为字符串导入。

getattr从一个对象中提取命名属性,在本例中,是我们的 Python 模块中的函数,方式与我们做from module import attribute的方式相同。

打字

来自typing模块的方法get_type_hints()返回包含函数、方法、模块或类对象的类型提示的字典。get_type_hints 不支持字符串,因此我们使用ast库来解析模块中的方法。

本词典中的最后一个key是“return ”,包含返回变量 type-hint(如果有),否则没有。我们可以用.pop方法从字典中排除这个键。

正则表达式

我们使用下面的正则表达式来理解函数参数在我们的 Google 风格的文档字符串中的定义位置。

首先,我们从函数节点get_docstring开始,寻找三个属性ArgsExample[s]Return[s](第 8 行)。然后我们迭代每个函数参数(第 31 行),用它选择我们的type_hints字典中的值(第 38 行),删除打印的 type_hint 的<class ''>部分(第 39 行),并将其插入括号中(第 43 行)。

请在这里找到完整的脚本,看看我们如何对函数返回参数的类型提示做同样的事情,并用新格式化的 docstring 文本覆盖我们的 Python 脚本。

通过预提交 Git 挂钩实现自动化

预提交 Git 管道

通过 Git 钩子,我们可以在每次提交和推送到我们的库之前运行林挺和格式化工具,比如mypyautoflakeflake8isortblack。这使我们能够在 Python 项目中自动创建“编码标准”。这些 Git 挂钩是由pre-commit包提供的,它使用一个pre-commit-config.yaml文件来指定在预提交管道中包含哪些包。

请在下面的故事中找到更多关于预提交 Git 挂钩的有用提示👇

https://betterprogramming.pub/4-tips-to-automate-clean-code-in-python-527f59b5fe4e

除了林挺和格式化工具,您还可以通过 bash 命令在这些 Git 挂钩中包含 Python 脚本的执行,使我们能够自动化docstring_from_type_hints()功能。这样,在每次提交时,类型提示都会被插入到我们的 docstring 中。

MkDocs 中自动生成的 Google 风格的 docstring 示例

更进一步,我们可以用 Mkdocs 中的 Python 文档自动创建一个静态网站。以下是更多相关信息👇

Python:用 Delorean 和熊猫来计算你的下一次飞行时间

原文:https://towardsdatascience.com/python-use-delorean-and-pandas-to-calculate-your-next-flight-time-76ecf87fea8a?source=collection_archive---------26-----------------------

操纵时区感知天真的datetime物体

Ramon Kagie 在 Unsplash 上拍摄的照片

范围

机票上注明了出发和到达的日期和时间,但却标注了各自的当地时区,这使得计算飞行时间有点棘手。在本文中,引入了Delorean库,并与pandasdatetime一起操作带有时区信息的对象。作为一个例子,计算了英国伦敦希思罗机场马来西亚吉隆坡之间的飞行时间。

图片由作者提供|完整的归属细节见下文

介绍

如果你到了某个年龄,那么始于 20 世纪 80 年代的 【回到未来】 系列电影是迄今为止最好的时间旅行系列。对于其他人来说,这是一个关于时间旅行的托尼·斯塔克引用自 复仇者联盟 4:终局之战 。这是一个啰嗦的说法,图书馆 Delorean 是以九十年代 回到未来 电影和高飞电视动画片中的汽车命名的。

[Delorean](https://delorean.readthedocs.io/en/latest/index.html)库提供了另一个接口来管理基于[dateutil](https://dateutil.readthedocs.io/en/stable/)[pytz](https://pythonhosted.org/pytz/)的时区感知对象。在本文中,我将介绍 Delorean ,创建并操作datetime对象,并应用该功能来确定从伦敦希思罗机场*(LHR)英国吉隆坡 (KUL)的飞行时间。*

Python 环境

德罗林装置

第一步是建立一个适当的环境;下面的 gifcast 展示了在使用pip安装delorean之前,用conda克隆一个环境的过程。

康达设置要点|作者

关键步骤是检查和克隆现有的conda环境(ds01)。Delorean似乎无法通过conda-forge获得,因此需要进行pip安装。虽然conda包不应该和pip包混在一起,但是如果pip安装是最后一项,它们是兼容的。

康达环境设置|由作者铸造

Jupyter 笔记本设置

delorean环境已经预装了jupyter、熊猫和相关的软件包。为了提供可再现的分析,列出了包的版本:

Jupyter 笔记本进口|作者

德罗林

介绍

作者简介 Delorean

初始化一个delorean对象相当于在世界协调时间 (UTC)时区为当前日期时间生成一个datetime对象。因此,世界上任何地方的两个用户同时运行相同的命令都会得到相同的结果。第二个命令Delorean.now()datetime对象定位到用户的本地时区,对我来说就是Europe/London

时区转换

笔记本片段|作者

shift命令用于将utc变量的时区从UTC更改为Europe/London;然而,它似乎也改变了原来的utc变量,这是意料之外的。为了解决这个问题,必须重新初始化utc变量:

笔记本片段|作者

笔记本片段再次显示了utc变量的创建,但也显示了一个单独的london变量,该变量获取初始化的Delorean对象并将其转移到伦敦时区。通过将timezone=”Europe/London”参数传递给Delorean对象可以达到同样的目的。

两个变量(londonutc)之间的差异在微秒级,因为两个区域当前是相同的。存在微小的差异是因为两个变量是分别初始化的;为了使用一个公共时基,Delorean类接受一个时区 naive datetime对象:

笔记本片段|作者

由于两个变量现在使用相同的时间,但在不同的时区,它们之间的差异当然是零。

马来西亚吉隆坡

为了确定马来西亚吉隆坡的时区,可以使用pytz.common_timezones列出最常见的时区:

笔记本片段|作者

公共时区列表可以用 list comprehension 来解析,查找关键字“Kuala”,并显示合适的时区是Asia/Kuala_Lumpur。然后用变量utc初始化一个新的变量kuala_lumpur和城市的时区。不出所料,伦敦和吉隆坡的时差是零——两个地理位置的时间当然是一样的!

熊猫日期时间

以下笔记本显示了与上述类似的步骤,但使用了pandas:

笔记本片段|作者

使用pd.to_datetime(‘today’)生成当前日期和时间(无时区信息),然后使用.tz_localize将其本地化为Europe/London并存储为pd_london。然后可以使用astimezone(‘Asia/Kuala_Lumpur’)功能创建吉隆坡马来西亚的日期和时间。注意显示时差和时区的两个变量的输出。不幸的是,与Delorean不同,没有简单的方法来减去两个时区感知变量之间的差异,因此出现了错误消息。

笔记本片段|作者

为了减少误差,可以使用.to_pydatetime()pandas表示转换为datetime对象,这当然也显示了零时差。值得注意的是,这也是意料之中的事,两个不同地理位置的同一时间的时差为零。

为了计算两个时区之间的差异*,需要使用.tz_localize(None)将时区感知表示转换为时区原始表示,正如预期的那样,显示了 8 小时的差异。*

飞行时间计算

照片由 Nysa Zainal 在 Unsplash 上拍摄

熊猫来了

理解了使用Deloreanpandas进行时区操作的基础知识后,我们可以将它应用到实际应用中。我们将确定伦敦希思罗机场英国吉隆坡马来西亚之间最后两个航班的飞行时间。数据来自 飞行雷达 24 ,针对马航MH3/MAS3 航班:

笔记本片段|作者

笔记本片段显示了使用基于飞行雷达 24 数据的列表字典创建一个DataFrame ( df)。它包括出发和到达时间以及时区信息。

笔记本片段|作者

将日期和时间信息转换成原生的pandas datetime对象可能很有诱惑力。请注意,这些是时区的简单表示。两个时间简单相减,显示飞行时间为 19 小时 40 分,与实际飞行时间不符。原因是时间表示没有考虑到两个不同的时区。

笔记本片段|作者

上面的代码首先将出发和到达时间定位到各自的时区(使用.tz_localize)。注意datatypes信息是如何进行时区编码的。为了计算飞行时间,两列相减是不够的,因为 pandas 不支持两个不同时区的这种操作。必须将其中一列转换(使用.dt.tz_convert)为通用时区。结果显示正确的飞行时间约为 11 小时 40 分钟。简单的心算还应该证明,根据时区之间的 8 小时时差调整后,之前的 19 小时 40 分钟产生了大约 11 小时 40 分钟。

用 Delorean 进行理智检查

作为最后一项检查,使用datetime.datetime.strptime创建出发和到达时区简单的 datetime对象——其中strptime使用一个字符串和一种格式生成一个datetime对象。然后用两个各自的时区初始化一个Delorean对象。两个对象之间的差异被表示为一个timedelta对象。timedelta格式的一个限制是差异用daysseconds的组合来表示,而不是更合适的单位,如小时:

笔记本片段|作者

健全性检查显示,两个时间之间的差异与之前使用pandas进行的计算一致。

结束语

我们已经讨论了时区幼稚感知 datetime对象,并介绍了Delorean作为时间操作的替代接口。我们还研究了pandas如何执行类似的计算,并将其应用于计算飞行时间的真实用例。

用过Delorean,虽然有.humanize()等一些好看的功能,但是从 2018 年开始就没有更新过,可以说很笨重。因此,我会继续推荐使用pandas来代替。

属性

所有gists,笔记本,终端演员表均为作者。所有作品都是基于拥有 CC0 或公共领域许可或SIL of的资产,因此不侵权。

使用的 Python 标志Python 软件基金会指南一致。

主题灵感来自并基于我最喜欢的vim主题:grubbox。

Python 虚拟环境

原文:https://towardsdatascience.com/python-virtual-environments-22f2362c22ec?source=collection_archive---------33-----------------------

通过将 Python 虚拟环境与 Anaconda 结合使用,避免未来的麻烦

几个月来,我一直在 Anaconda (conda)虚拟环境中使用 Jupyter 笔记本电脑中的数据,一个接一个地安装 Python 包,天真地不知道我的计算机不断增长的 PyPI 库怎么可能会出错——直到出现一些冲突,我使用的代码表明它依赖于我已经安装的包的早期版本。

如果您正开始使用 Python 处理数据,并且正在疯狂地学习伴随 Python 知识而来的不断增长的软件包库,那么我给你的建议是:通过从一开始就知道如何使用 Python 虚拟环境,您将节省数小时的错误修复和回溯时间。如果你还没有,从下载 Anaconda 开始。这可能需要一点时间,所以去给自己冲一点咖啡(或茶!)下载,然后你可以在大约 5 分钟内完成这篇博客的其余部分。

照片由内容小精灵在 Unsplash 上拍摄

简单地说,虚拟环境是一种隔离每个项目的依赖关系的方式。

通过建立单独的虚拟环境,你将把自己(和其他想要添加或复制你的代码的人)从无尽的挫折中解救出来。如果您曾经因为不同的包版本需求或任何种类的过时(大声说出这个词)而经历过代码中断,Python 虚拟环境就是您的解决方案。有了虚拟环境,您将能够直接从命令行(如果使用 Windows,则从 Anaconda 提示符窗口)为所有独特的项目设置和导航专门的工作环境。

现在,您已经对虚拟环境有了概念性的了解,并且已经下载了 conda,我们将介绍如何设置、导航、更新和删除您的虚拟环境的一些基础知识,您将很快就能进行完美的生产。

我从康达网站上了解到所有这些,所以如果你想更深入地了解,这是我推荐的起点。

步骤 1:创建和激活新的虚拟环境

现在安装了 conda,只要我们打开它,我们就已经在 conda(基础)虚拟环境中了。我们不想向我们的基础环境添加任何包,因为这将是我们的白板,我们的家“基础”相反,我们将为任何我们想要开始工作的项目创造一个新的环境。打开一个终端窗口,键入下面的命令。您可以将 my_env 替换为虚拟环境中您想要的任何名称,并在您的环境中选择您想要的 Python 版本(默认情况下,它将是您在安装 conda 时拥有的 Python 版本):

conda create --name my_env python=3.8.3

一旦终端提示您是否要继续,您的新环境就创建好了([y]/n)?键入 y 并按回车键。现在它会问你是否要激活环境。要激活它,请遵循命令行提示并键入:

conda activate my_env

您将知道您的环境已被激活,因为您给出的名称将显示在终端左侧的括号中。

作者图片

步骤 2:更新您的虚拟环境

在我们新的虚拟环境中,我们可以像任何其他项目一样使用 pip install 添加软件包,该环境的优点是任何新的安装都不会影响您计算机的其他部分,只会影响我们已经设置的虚拟环境。我现在正在做一个自然语言处理项目,所以我将把自然语言工具包( nltk )安装到我的环境中,如下所示:

pip install nltk

现在这个包已经安装在我为我的 NLP 项目设置的环境中,不会在其他地方或以后对我造成任何冲突。

步骤 3:导航多个虚拟环境

在不同环境间导航很容易。要停用一个环境,您只需输入命令conda deactivate。这将带您回到基本环境。切换目录的一种更快速的方法是只激活您想要切换到的新目录,例如:

作者图片

正如您在上面看到的,我从 my_env 环境开始,然后通过使用conda activate命令激活它,快速切换到另一个名为 nlp_env 的环境。在下一行中,我使用了这个命令

conda info --envs

这将弹出我的所有环境的列表,星号表示哪个环境是活动的。

步骤 4:删除虚拟环境

删除或移除环境也可以通过一行代码来完成。确保您已经停用了想要删除的环境,然后运行这行代码

conda remove --name my_env --all

结论

我再怎么强调这对你前进的帮助也不为过。Anaconda 应用程序有更多的功能和有用的 GUI,但是如果您在数据科学相关的项目中较少使用 Python,并且没有发现自己在使用 Anaconda 作为资源,我建议安装 PyPI 的 pipenv 来为您的任何编程项目建立相同的独立虚拟环境框架。在使用虚拟环境时,实际的 Python 文档有点过时,所以我建议坚持使用 conda 和 pipenv。希望这有所帮助,并可能让你很多头痛!

Python 虚拟环境& Jupyter 笔记本

原文:https://towardsdatascience.com/python-virtual-environments-jupyter-notebook-bb5820d11da8?source=collection_archive---------4-----------------------

用 conda 在 Jupyter 中创建虚拟环境和相应的内核

TL;博士

conda create -n myenv python=3.8
conda activate myenv
conda install jupyter
conda install -n base nb_conda_kernels
jupyter notebook

注意,我在 Windows 上运行 Python 3.8(与 Anaconda 一起安装)

由麦克斯韦·纳尔逊在 Unsplash 上拍摄的照片

完整的操作方法

我最*决定清理我的 Python 安装,因为我的已经变得一团糟。我用 Anaconda 卸载/重新安装了 Python,并选择在开始新项目时开始创建虚拟环境,以保持 Python 的基本安装整洁。虚拟环境将允许我安装给定项目所需的任何包的任何版本,而不会干扰我的基本安装。这样,我可以拥有我需要的所有软件包的所有版本,并且不会出现我不知道的恼人的不兼容错误。

第一步。

要创建一个新的虚拟环境,打开 Anaconda 提示符并运行以下命令*(用您选择的虚拟环境名替换“myenv”):*

conda create -n myenv python=3.8
conda activate myenv

请注意,您必须指定 Python 版本,否则您的虚拟环境将被创建为一个完全空的文件夹,没有预先安装 Python 和一些像 pip 这样的基本包。你可以通过运行 conda list 来查看哪些包已经在那里(提示:应该有> 0),以确保你已经正确安装了它。

此时,您可以使用 condapip 在新激活的虚拟环境中安装您需要的软件包。

第二步。

当您在 Jupyter Notebook 中运行项目时,您需要一种方法来引用这个新的虚拟环境,而不是您的基础环境。在 Anaconda 提示符下(在虚拟环境仍然激活的情况下),运行以下命令。请记住,您必须在您创建的每个虚拟环境中运行此操作。

conda install jupyter

第三步。

然后,在您的基础环境中,您需要安装 nb_conda_kernels (只需一次),它会为您创建的每个虚拟环境自动创建一个新的内核,前提是您已经在其中安装了 jupyter。

conda install -n base nb_conda_kernels

第四步。

您的 Jupyter 笔记本现在可以在任何一个内核(base 或 myenv)上运行,因此可以根据手头的项目获得正确的包/版本。您可以使用下面的命令从任何激活的环境中启动 Jupyter Notebook,它将打开您的笔记本位置(我的位置在 C:/Users/myusername 中,我的 Anaconda3 也安装在那里)。

jupyter notebook

打开一个新的 Jupyter 笔记本,点击内核 > 改变内核 >,选择你需要的虚拟环境。

我希望这有所帮助!

Python 与 Julia——线性回归

原文:https://towardsdatascience.com/python-vs-julia-linear-regression-49738b5de383?source=collection_archive---------19-----------------------

意见

Python 程序员的视角

王力普在 Unsplash 上拍照

我的数据科学之旅始于我在贝尔维尤大学(Bellevue University)攻读数据科学硕士学位期间,在那里我学习了 R 和 Python。虽然我是 Python 的忠实拥护者,但它并不是我用过的最快的编程语言(也不是最慢的)。但是我听说了很多关于 Julia 的速度,所以我开始试验它。我甚至写了一篇 Medium.com 的文章,介绍如何与茱莉亚开始交往。我必须承认,我离开那篇文章时并没有对朱莉娅留下什么真正的印象,因为我没有对它做任何有用的事情。

但是正如我在以前的文章中所说,我喜欢竞争。那个激励了我。让游戏从我能理解的事情开始——建立一个线性回归模型,看看哪个更快、更好、更容易。(注意这种偏见的健康剂量,因为我已经每天使用 Python。)

导入包

在 Julia 中导入包非常类似于 R——如果包不在那里,我也可以添加包,如果在那里,就使用它。Pkg包允许我添加包,就像 Python 中的pip一样。

第一印象——朱莉娅

Julia 和 R 对于 imports(更确切地说,using)的相似性立刻让我开始怀疑 Julia 是不是 Python 和 R 的混血儿(如果是,我想我明白为什么人们会挖掘 Julia 了)。

但在这一点上,我没有真正显著的印象,只是怀疑我会喜欢上 Julia,因为比起 Python,我真的更喜欢 R 的直觉。

装载 CSV

我为此使用的数据来自 R GitHub 的机器学习数据,可以在这里找到。Julia 和 Python 之间有一点是相同的,那就是目录路径在 Visual Studio 代码中并不完全直接,所以在我指定数据文件的路径之前,我必须弄清楚解释器在什么目录中。如果你想知道我是如何让 Julia 使用 Visual Studio 代码的,请查看我之前提到的入门文章。**

为了找出 Julia 中当前的工作目录,我在终端中使用julia命令启动了 REPL(它是解释器,相当于键入python)。然后我输入了pwd(),和 Python 一模一样的命令。(命令行开始的图形是如此的 20 世纪 80 年代-爱死它了。)

在 Julia 中使用 pwd()。图片作者。

在 Julia 中加载一个. csv 文件非常简单,然后在一个数据类型中使用它就更容易了。事实上,在 Julia 中有一个 DataFrames 包,有点像熊猫。

描述数据

朱莉娅describe()函数就像熊猫数据帧中的describe()info的组合。看起来没有任何东西和熊猫info()完全一样,但是考虑到茱莉亚的describe()输出,这并不是必需的。

Julia describe()函数输出—作者图片

我并不讨厌describe()输出,但我认为我更喜欢 Python 中的info()describe()分离——可能只是按行读取数据。(也可能只是熟悉度偏差。)

在 Julia 和 Python 之间,显示第一个行数也是相似的。不过,Julia 的语法有点不同。

**println(first(insurance_df,5))**

Julia first()函数输出-作者图片

我真的很喜欢这种方式,它为每一列指定了行和数据类型,格式也很好。等等。哦,不。我以为我的好日子已经一去不复返了。五个字母就够了,为什么还要用七个字母呢?哦,好吧——印刷体在朱莉娅中是println

在我忘记之前,如果你对在 Visual Studio Code Jupyter 笔记本中使用 Julia 感兴趣,请确保获得Visual Studio Code-Insiders。(蛮甜的。)

特征工程(轻型)

在这一点上,我不打算用 Julia 太深入地研究探索性数据分析,因为这会淡化 Julia 和 Python 在回归方面的比较。(我打算以后再深入探讨这个问题。)

然而,我必须对分类特征进行一次性编码。对于 Python,这意味着性别、吸烟者和地区。然而,Julia 可以处理二进制字符串,如 yes/no 男性/女性,true/false,而无需工程。(外加一个朱丽娅。)这仅留下了用于独热编码的区域特征。

为此,我将使用Lathe包的OneHotEncoder函数。Lathe 是 Julia 的 ML / stats 包(老实说,这是我所知道的全部)。

一键编码

Julia 和 Python 之间的独热编码非常相似(如果我把它比作熊猫get_dummies()——如果我把它比作 scikit,Julia 更简短——学习 OneHotEncoder)。

即使与get_dummies().相比,输出也明显不同

朱莉娅·OHE 输出—作者图片

注意,所有二进制特性的字符串都保持不变——包括编码的region特性。我更喜欢这样,因为读起来更直观。

建模阶段

和 Python 一样,我仍然需要分割数据用于训练和测试。车床有一个 TestTrainSplit 对象,它是先前通过using命令添加的。

**train, test = TrainTestSplit(insurance_df,.75)**

这里有几件事是不同的:

  1. 百分比是相反的——使用 scikit-learn train_test_split函数,测试集的大小被指定。没什么大不了的,但是要记住
  2. x 和 y 不区分(特征和目标)。其原因将很快变得显而易见
  3. 运行这一行代码有一个输出(与 scikit-learn 不同)——这个输出是必不可少的,就像对训练和测试数据运行describe()一样

Julia TrainTestSplit 输出-作者图片

拟合和测试模型是 Julia 的类 R 本质真正显现出来的地方。

我不是 R "@ "函数语法的粉丝(当我回忆起我作为 Lotus Notes 开发人员的日子时,我的眼睛在抽搐)。然而,它足够简单,也同样容易预测(但我更喜欢像 Python 中那样显式调用fitpredict)。

在拟合和测试模型时,有些东西是不同的——有自动输出,包括函数和系数。(我很喜欢那部分——也很像 R。)

Julia 在训练和拟合线性回归模型时自动输出-图片由作者提供

模型评估

对于这个简单的(多重)线性回归,我用 R2 评估了模型,只用了一行代码,就像用 Python 一样简单。

**println(“\nR2 for the linear regressor: “, r2(linear_regressor))**

输出是:

R2 for the linear regressor: 0.7514530856688412

有趣的是,用 Python 创建的同一个模型的输出是:

R2 for the linear regressor: 0.7424

我的猜测是,小的 R2 差异是因为有更少的独热编码,因此在朱莉娅数据中用于训练的维度更少。

正误表

一些不属于数据科学过程本身的值得一提的事情:

  1. 我没有注意到任何性能差异,但数据足够小,这不是决定性的
  2. 我并不认为其中一个比另一个更好——但我仍然有动力继续前进(我现在更加好奇)
  3. 类似 R 的语法有好有坏(或者说我是真正面向 Python 的)
  4. 这迫使我挑选 Visual Studio 代码内部人员——光是入场费就足够了

我希望你和我一样喜欢这一半。

参考

[1]辛格,卡比尔。(N.D.) 朱莉娅中的线性回归。https://www . machine learning plus . com/linear-regression-in-Julia/

Python Web 应用对于分析项目来说是一个糟糕的想法。

原文:https://towardsdatascience.com/python-web-apps-are-a-terrible-idea-for-analytics-projects-36f1bd33e84b?source=collection_archive---------2-----------------------

纯 Python 栈忽略了严重的缺点。以下是如何弥补的方法。

蒂姆·高在 Unsplash 上拍摄的照片

这是本能。我们数据科学家热爱 Python。因此,我们在每个应用程序中都倾向于 Python 框架。万能的语言似乎在大多数情况下也能很好地工作。其中一个场景是为您的分析项目构建 web 应用程序。

其中的关键词是“大多数情况”Python 是一门漂亮的语言,几乎可以用在任何问题上。然而,仔细观察可能会发现一些细微的差别,这些差别可能会使 Python 在某些情况下变得无关紧要。

多年来,我一直是姜戈的粉丝。它是构建 web 应用程序最流行的 python 框架。Django 提供了一个典型的 web 框架所需要的一切——认证、数据库迁移、管理界面等等。集成一个机器学习算法是毫不费力的,因为两者都在同一种语言中。

然而,在发现关于这个框架的可怕事实后,我不得不改变我的想法。更具体地说,它在数据科学项目中的使用。但这不是姜戈的事;是 Python。即使您使用其他 Python web 框架,如 Flask,也必须面对同样的问题。

但是在匆忙抛弃 Django/Flask 之前,我不得不说这不是一个死胡同。我们可以让 Python 再次变得伟大;作为一个网络框架。

在本文中,我们将讨论

  • Flask 与 ExpressJS 执行长时间运行任务的比较;
  • 调查 python web 应用在分析项目中失败的原因,以及;
  • 使 Python web 应用程序更好地满足请求的变通方法。

Python 和 JavaScript web 应用程序。

与 JavaScript 相比,我们需要一个演示来理解 Python 的问题。

因此,让我们在每种语言中使用两种流行的框架来完成相同的任务——计算第 30 个斐波那契数。下面是我们如何使用 Flask (Python。)

Python (Flask)服务于第 30 个斐波那契数——作者。

要运行上述任务,您可以在终端中使用以下命令:

pip install flask # if Flask is not installed yet.
flask app.py

现在让我们用 Express JS (JavaScript)做同样的事情:

JavaScript (ExpressJS)服务于第 30 个斐波那契数——作者。

以下是使用 node 启动服务器的方法:

npm install express --save # if express is not installed already
node app.js

如你所见,两者在各方面都很相似。它在请求中不接受任何参数,计算第 30 个斐波那契数,并返回相应的值。

我们在单个线程中以开发模式启动这两个应用程序。这正是我们所需要的,因为我们在单个线程中测量它们的性能。

最后,让我们通过向服务器发出多个请求并记录时间流逝来模拟真实世界。下面的代码在 10 个并行线程中向我们的服务器发送 1000 个请求。该计划还将打印*均耗时毫秒。

使用 10 个线程计算 1000 个请求的*均响应时间。

让我们比较一下结果。以下是节点服务器服务时的结果:

使用 JavaScript 服务器计算第 30 个斐波那契数 1000 次所用的*均时间截图——来自作者。

当 Flask 为请求提供服务时,情况也是如此:

使用 Python 服务器计算第 30 个斐波那契数 1000 次所用的*均时间截图——来自作者。

多次做这个实验会得到几乎相似的结果。Express (JavaScript)处理请求的速度比 Flask (Python)快*四倍。)

计算第 30 个斐波那契数并不是一个长期运行的任务。但这足以帮助我们理解这个问题的重要性。

是什么让 Python 服务器变慢了?

你去一家名为“Sync”的餐厅,那里的服务员叫 Python。一个陌生人先点他的食物。Python 去了厨房,二十分钟后回来,给陌生人上菜。然后他走过来问你:“先生,你要点什么?”

如果你现在点菜,你将不得不再等二十分钟。

出于无奈,你离开了那家餐厅,去了另一家名为“Async”的餐厅。在那里,水的名字是 JavaScript。同样,一个陌生人先点他的食物。JavaScript 去了厨房,一会儿就回来。他对陌生人叽叽喳喳地说了些什么,然后沿着你的路走过来,问道:“先生,你要点什么?”

你点了你的食物;陌生人在接下来的 18 分钟内得到了他的,而你在 20 分钟内得到了你的。

这就是 Python 和 JavaScript 的工作方式。Python 以同步的方式一次处理一个任务。然而,当已经有一个请求正在处理时,JavaScript 接受了另一个请求。这种异步行为使得基于 JavaScript 的 web 应用程序速度更快。

如何让 Python web 应用更好地执行分析?

只要您只有短暂的请求或长期运行的任务,并且只有少量来自用户的预期请求,那么您就做得很好。像 Flask 和 Django 这样的 Python 框架是理想的,因为你可以用一种语言——Python 来保存所有的东西。

当您有大量需求的长时间运行的任务时,复杂性就会增加。为了保持服务器正常运行,您可能需要部署多个实例并很好地*衡负载。这种变通方法在大多数情况下是不可持续的。它把成本飙升到一个疯狂的高度。

数据科学项目经常伴随着这种长时间运行的任务。没有异步行为的繁重计算可能需要更多的计算能力。然而,在转移到不同的框架之前,您可以尝试一些变通方法。

将计算从请求-响应周期中分离出来。

无论如何,将高性能计算与 web 服务器结合在一起是一个糟糕的想法。即使是异步服务器也不应该在请求-响应周期内为它们服务。如果是这样,Instagram 和 Spotify 这样的巨型*台是如何处理海量计算的?这里有一篇文章可以回答这个问题。

这个想法是向运行在独立线程上的计算引擎发送消息。web 服务器不需要等到计算完成才发送响应。相反,计算引擎更新数据库。web 服务器可以使用单独的端点随时读取其值。

尝试优化您的代码。

不管您将使用哪种框架,如果您总是试图优化您的代码,那将是最好的。最*的一个库似乎在优化 Python 程序方面非常有效。

Tuplex 将您的 Python 代码转换为本地 LLVM,并并行运行它。该库背后的团队在一篇研究文章中显示,它的速度大约快了 91 倍。下面是我之前写的一篇关于这个话题的详细文章:

引入异步行为。

Flask 和 Django 都有引入异步行为的方法。对于潜在的耗时任务,这种变通方法是一个很好的选择。

然而,这两个框架在它们的文档中都提到它们有几个缺点。因此,在所有的 web 请求中使用它们是不明智的。

标准 Python 库 asyncio 帮助您异步转换一些函数。尽可能使用它。

因为 Python 本身不是异步的,所以所有这些解决方法都依赖于运行一个无限事件循环。尽管它们在许多方面并不完美,但在迁移之前考虑它们是值得。

最后的想法

Python 是一种非常棒的数据科学编程语言。因为它是一种通用语言,Python 的应用并不局限于数据科学。但这并不意味着它是所有用例的完美语言。

因为大多数数据科学家已经爱上了 Python,所以我们选择 Flask 和 Django 这样的框架来构建 web 应用。然而,其同步行为可能导致生产中严重的成本劣势。

JavaScript 本质上是异步的,对于长时间运行的任务表现更好。但是,python 框架也有一些变通方法来提高它们的性能。因此,在决定迁移之前考虑它们是值得的。

如果你仍然喜欢 Python web 应用程序而不是你的数据科学项目,那么试试 streamlit。

谢谢你的阅读,朋友。看来你和我有许多共同的兴趣。一定要看看我的个人博客。

向我问好 上LinkedInTwitter中。我会为你打破僵局。

还不是中等会员?请使用此链接 成为 会员。你可以享受成千上万的有见地的文章,并支持我,因为我赚了一点佣金介绍你。

Pythonic 计数:概述

原文:https://towardsdatascience.com/pythonic-counting-an-overview-4266edbb0001?source=collection_archive---------27-----------------------

计算元素非常重要!学会如何雄辩地做到!

克里斯·贾维斯在 Unsplash 上的照片

在 Python 或任何其他编程语言中,当试图解决问题和/或操作数据集时,计算具有给定属性的元素的数量是一项经常发生的任务。在数据科学项目中,通过循环操作数据通常需要了解正在处理多少项(即计数)。因此,了解一种简单易行的方法来实现这一点非常重要。Python 有许多不同的方法来实现这一点,在本文中,我们将介绍一种基本的方法,以及一些更有说服力的方法来进行计数操作。

由伊莎贝拉和 Zsa Fischer 在 Unsplash 拍摄的照片

循环内计数:

在计数操作的核心,分配一个变量来表示计数,通常是整数数据类型。如上所述,在循环 a 中,如果满足特定标准,计数器变量将被要求增加 1。下面的代码块给出了一个简单的例子:

**# Option #1**
list = ['foo', 'bar', 'foo', 'foo', 'bar', 'foobar']
counter = {}

for item in list:
    if item not in counter:
        counter[item] = 0
    counter[item] += 1

In[1]: counter
Out[1]: {'foo': 3, 'bar': 2, 'foobar': 1}

在上面的例子中,没有将计数器设置为一个普通的整数变量,而是使用了一个字典来生成被计数项的键值对及其各自的计数。这是一个很好的将它加入字典的附加功能,但是下面有两个例子来说明如何做同样的事情,但是更简洁一些。

**# Option #2**
list = ['foo', 'bar', 'foo', 'foo', 'bar', 'foobar']
counter = {}
for item in list:
    counter[item] = counter.get(item,0) + 1In[2]: counter
Out[2]: {'foo': 3, 'bar': 2, 'foobar': 1}**# Option #3**
from collections import defaultdict

list = ['foo', 'bar', 'foo', 'foo', 'bar', 'foobar']
counter = defaultdict(int)
for item in list:
    counter[item] += 1In[3]: counter
Out[3]: defaultdict(int, {'foo': 3, 'bar': 2, 'foobar': 1})

因此,上面的选项 2 和 3 都提供了循环内计数的一些干净的替代方法。第二个选项使用 dict.get()方法,并将初始值指定为 0。通过循环,当遇到给定的键时,会添加“+ 1”部分,使值增加 1。仔细看看第三个选项,我们可以看到 defaultdict 项是从 collections 库中导入的,它被初始化为一个整数数据类型的对象。这个循环很简单,当 deafultdict 遇到列表中的条目时,条目(dict 中的键)的值增加 1。因为它是一个 deafultdict,所以任何新项(或键)的初始值默认为 0。所以这里我们有 3 种方法来利用计数操作和变量循环,后一个例子稍微简化了代码。

亚历克斯·丘马克在 Unsplash 上的照片

Python 的计数器子类

幸运的是,由于计数在项目中是如此重要的一个过程,Python 在 dict 对象中构建了一个 Counter 子类。它的特别之处在于,对于可散列对象的序列,它会自动构建并输出一个元素及其计数的字典。我们可以在下面的例子中看到,Counter 子类进一步简化了事情。

from collections import Counter

list = ['foo', 'bar', 'foo', 'foo', 'bar', 'foobar']
count = Counter(list)In[4]: count
Out[4]: Counter({'foo': 3, 'bar': 2, 'foobar': 1})

不需要遍历列表,因为这是计数器固有的功能。有趣的是,在列表中,您可以在计数器中为项目赋值,以获得 0 值甚至负值(这将取决于您的约定或过程,如出库清单)。

计数器的一个非常好的特性是,一旦你指定了一个变量来计算一些东西,如果有状态变化,你想更新所有东西的计数,有一个更新方法。更新允许已经执行的计数被新的字典或计数对象更新。下面的例子说明了如何应用这种方法。

from collections import Counter

list = {'foo': 4, 'bar': 3, 'foobar': 1}
count = Counter(list)

new_list = {'foo': 3, 'bar': 1, 'foobar': 3}
count.update(new_list)

In[5]: count
Out[5]: Counter({'foo': 7, 'bar': 4, 'foobar': 4})

注意,最后 count 的输出是通过 update 方法得到的列表和新列表的总和。

由拉克伦·唐纳德在 Unsplash 上拍摄的照片

有用的计数器功能:

可以从计数器中提取的另外两个东西是获取唯一项的计数,以及获取列表中前“n”项的排序列表。要获得一个唯一项的计数,只需使用一个括号,括号内的项的名称跟在分配给计数器的变量后面。most_common 方法也可以与内的参数一起应用,将输出限制为列表中的前“n”项。下面可以看到这两个东西的例子。

# Count of Named Element
In[6]: count['foo']
Out[6]: 7# Example of Top 'n' items
In[7]: count.most_common(2)
Out[7]: [('foo', 7), ('bar', 4)]

请注意,most_common 方法将按降序对列表进行排序,并将计数限制为用作参数的数字。

活动创建者在 Unsplash 上拍摄的照片

直方图和曲线图:

通过了解如何构建自定义计数方法,您可以通过可视化数据将数据带到下一个级别。在下面的简单示例中,我们采用了我们一直在使用的相同数据,并对数据应用了一个简单的条形图。我稍微修改了一下示例,使语法更加清晰,并且在参数中不使用字符串,但是结果是一样的。下面的代码显示了我如何获取从计数器创建的字典,并将它们分配给变量,然后我可以使用这些变量来绘图。

from collections import Counter
import matplotlib.pyplot as plt

count = Counter(foo=4, bar=3, foobar=1)

new_list = Counter(foo=3, bar=1, foobar=3)
count.update(new_list)

x = count.keys()
y = count.values()

plt.bar(x,y)
plt.show()

计数示例的简单直方图

请注意,还可以增加更多的复杂性,可视化的类型也可以增加。

总结:

我只是想以展示计数是一个简单的概念性问题来结束,我们可能需要这样做来检查和可视化我们的数据集,并且在 Python 中有多种方法来为猫剥皮,以及在核心 Python 中有许多很酷的内置功能。

Pythonic 提示和技巧—频率提取

原文:https://towardsdatascience.com/pythonic-tip-tricks-frequency-extraction-5ecc23b73c16?source=collection_archive---------53-----------------------

毒蛇之路

如何从列表中构造数据帧

照片由 Unsplash 上的 Stijn Swinnen 拍摄

Python 是一门优秀的语言,适合新程序员学习。它的直观性质使大多数人很容易快速理解代码和算法实际上在做什么。在这篇文章和以后的许多文章中,我们将讨论使用 Python 解决初学者练习中更复杂问题的许多方法。请注意,对于所有这些文章,我将使用 Jupyter 笔记本来显示代码的结果。

我们开始吧!

因此,让我们想象一下,给你一个单词列表,你必须从中提取特定的指标。你会怎么做?

文字

wordy_list = ['dog cat bear tiger tiger tiger dog dog cat cat bear   
               tiger tiger panda panda panda iguana iguana']

你可能注意到的第一件事是上面的列表不太适合数据分析。一个关键问题是数据被保存为单个字符串。然后,我们的第一步是将字符串分解成单词列表。为此,我们使用 Python 中可靠的 split() 函数。

list_of_words = wordy_list.split()

属性错误

哦,不,似乎我们有一个错误。原因很简单。如果我们看这个变量,它在技术上是一个列表,然而它是一个只包含一个字符串作为元素的列表。这种问题在数据科学领域非常普遍,因此我们必须保持警惕,尽早发现这种问题。为了避免这个错误,我们可以简单地将 split()函数与索引结合使用。

list_of_words = wordy_list[0].split()

构建的单词列表

很好,这个列表非常适合数据分析。所以让我们从一些基本任务开始。

计算每个单词出现的次数

在您的数据科学之旅中,您会多次遇到一个相当简单的任务,那就是频率分析。下面的函数将返回一个列表的所有元素,作为一组带有 (element,element frequency) 的元组:

def word_counter(word_list):
    new_list = []
    for w in word_list:
        number = word_list.count(w)
        datapoint = (w, number)
        new_list.append(datapoint)
    return set(new_list)counted_list = word_counter(list_of_words)

功能输出

太好了,任务现在完成了!

但假设你想给你的老板和学校留下深刻印象。你不应该给他们这样的代码。虽然代码实现了它的功能,但是有更多美观的方式来编写它。下面是一个代码示例,它执行完全相同的任务,但是写得非常干净。

def word_counter(word_list):
    return set([(element, word_list.count(element)) 
               for element in word_list])counted_list = word_counter(list_of_words)

完全相同的输出

我们可以看到,这个函数现在已经简化为一行代码。代码不仅保留了所有的功能,而且看起来简单而优雅。

计算每个单词出现的次数,并从最频繁到最不频繁排列输出

现在,您的老板希望您构建频率计数,但也希望您返回从最频繁出现的单词到最不频繁出现的单词排列的数据。当然,我们可以在返回数据后简单地处理它,但是为了节省时间,让我们将这个特性构建到函数中。为此,让我们利用 sorted() 函数以及 lambda 函数

def word_counter(word_list):
    new_list = [(element, word_list.count(element)) 
                for element in set(word_list)]
    return sorted(new_list, key = lambda j: j[1], reverse  = True)counted_list = word_counter(list_of_words)

排列列表

太好了,我们现在已经完成了任务。

最后一分钟改变!!!

假设你的老板早上 7 点打电话给你,迫切需要你修改你的代码。

“有什么问题?”你可能会问。

“我们需要将数据从最频繁到最不频繁排列,然后按字母顺序排列,我们还需要将它存储为一个数据框”你的老板说。

相信我,这种情况在数据科学领域非常普遍。让我们答应这个要求吧。下面的代码确保输出按最频繁到最不频繁的元素排列,然后按字母顺序对元素排序。此外,我们将数据加载到 pandas 数据框架中。注意,我们必须导入 pandas 模块。

import pandas as pddef word_counter(word_list):
    new_list = [(element, word_list.count(element)) 
                for element in set(word_list)]
    return pd.DataFrame(sorted(new_list, 
                               key=lambda x:(-x[1], x[0])),
                        columns = ['element', 'frequency_count'])df_counted = word_counter(list_of_words)

频率数据帧

太好了,这应该会让我们的老板和其他股东满意。

总之

我们已经看到了 Python 可以帮助简化数据分析的一些方式。对于本文,我使用的样本列表非常短,但是实际上这样的任务会涉及到成千上万的非结构化数据点。在未来的课程中,我们将回顾使用 Python 来帮助我们进行数据分析的其他方法,但现在,我希望我能够给你一个关于如何将 Python 应用于简单任务的想法。

Pythonic 提示和技巧—基本密码学

原文:https://towardsdatascience.com/pythonic-tips-tricks-basic-cryptography-361d8cd39071?source=collection_archive---------38-----------------------

地下室的故事

使用 Python 解密编码信息

阿吉特在 Unsplash 上拍摄的照片

成为数据科学家的挑战之一是解决让大多数人摸不着头脑的独特问题。这些范围从看似无害的教科书练习到多年未解的复杂谜题。在本文中,我们将讨论一些中间问题,这将有助于您更好地理解如何解密加密消息。然后,我们将应用我们的知识来解决一个基本的破译练习。

获取整数中所有数字的总和

假设这个问题是:

"得到一个整数中所有数字的总和,使解决方案足够一般化,以适用于任何整数"

假设给我们的数字是一个整数,我们可以简单地将它转换成一个字符串,然后简单地使用 split 函数得到一个数字列表。我们所要做的就是使用 sum 函数。

def digit_sum_v1(number):
    number = [c for c in str(number)]
    return sum(number)

digit_sum_v1(369)

函数输出

看来我们遇到了一个错误。幸运的是,这个问题很容易解决。在求和之前,我们只需要看看函数的输出。

函数输出

我们可以看到列表中的所有元素实际上都是字符串。这意味着,要修复我们的函数,我们只需在执行 sum 函数之前,将列表中的每个元素转换成一个整数。

def digit_sum_v1(number):
    number = [int(c) for c in str(number)]
    return sum(number)digit_sum_v1(369)

校正输出

太好了,我们的函数做了我们想要它做的事情。让我们做一个理智检查。

对函数进行健全性检查

一切似乎都很好。这是一个相当简单的练习,现在让我们把这个练习变得复杂一点。

合并 If-Else 逻辑时,获取整数中所有数字的总和

假设这个问题是:

“得到整数中所有偶数数字的和所有奇数数字乘积。如果整数大于 5000,取所有偶数乘积和所有奇数和。使解决方案足够一般化,可以处理任何整数”

咻,那肯定变得复杂多了。实际上,这比简单的数字求和函数更接*数据科学家的要求。因此,让我们来解决这个挑战。

from numpy import prod
def digit_sum_v2(number):
    digit_even = [int(c) for c in str(number) if (int(c)%2 == 0)]
    digit_odd = [int(c) for c in str(number) if (int(c)%2 != 0)]

    if number > 5000:
        even = prod(digit_even)
        odd = sum(digit_odd)
    else:
        even = sum(digit_even)
        odd = prod(digit_odd)
    return even, odd

注意,我们必须利用 NumPy 中的 prod 函数。本质上,我们正在做的是创建两个不同的列表,一个由所有偶数数字填充,另一个由所有奇数数字填充。然后,我们根据整数的大小执行 sum 和 prod 函数。

让我们通过几个整数运行我们的函数,看看它是否给出我们想要的。

print(digit_sum_v2(1212))
print(digit_sum_v2(1313))
print(digit_sum_v2(3131))
print(digit_sum_v2(1515))
print(digit_sum_v2(5151))

函数的输出

很好,我们的功能正常工作。请注意,我们函数的一个特性是它不知道数字的位置。这意味着数字完全相同的数字应该产生完全相同的结果,除非其中一个数字大于 5000。

现在你可能会问自己,这和编码信息有什么关系?让我们再一次提高这个最后问题的复杂性。

“得到一个整数中所有小于等于 5 的数字的和所有大于 5 的数字的积。如果大于 5000,取所有小于等于 5 位的和大于 5 位的之和的乘积。****

按照以下逻辑将结果映射到字母数据帧:

5 输出下方的索引数据帧行****

5 输出上方的索引数据帧列****

通过数据帧循环的两个输出索引

你应该只使用一个单一函数,它将把一个整数列表字母数据帧作为输入

import numpy as np
import pandas as pd
alphabet_matrix = pd.DataFrame(np.array(list('abcdefghijklmnñopqrstuvwxyz_'))
             .reshape(4,7))
alphabet_matrix

字母数据帧

请注意,在本练习中,下划线代表空格。好吧,让我们来解决这个问题。

def decoder(list_of_ints, df_reference):
    references = []
    for i in list_of_ints:
        below_5 = [int(c) for c in str(i) if (int(c) <= 5)]
        above_5 = [int(c) for c in str(i) if (int(c) > 5)]
        if i > 5000:
            bel_5 = prod(below_5)
            abv_5 = sum(above_5)
        else:
            bel_5 = sum(below_5)
            abv_5 = prod(above_5)
        references.append((bel_5, abv_5))

    final_message = [df_reference
                    [r[1]%(df_reference.shape[1])]
                    [r[0]%(df_reference.shape[0])]
                    for r in references]

    return ' '.join(final_message)

让我们运行这个函数,看看隐藏的消息是什么。

message = decoder([1080, 1116, 1099, 1108, 1280, 9904, 1611, 119, 2199, 561, 17, 181, 61], alphabet_matrix)
print(message)

多甜蜜啊

总之

我们构造了一些有趣的函数来帮助我们破译加密信息。可以想象,大多数加密的信息不会这么容易被解码。尽管如此,我相信这篇文章足以让您对如何着手研究密码学领域有一个基本的了解。在以后的文章中,我们将讨论更复杂的函数,并应对更有趣的挑战。

Pythonic 提示和技巧—识别和索引重复项

原文:https://towardsdatascience.com/pythonic-tips-tricks-checking-and-indexing-duplicates-d62fdfea9eec?source=collection_archive---------35-----------------------

毒蛇之路

如何识别和索引列表中的重复项

Wolfgang Hasselmann 在 Unsplash 上拍摄的照片

重复检测和索引是每个数据科学家都应该具备的基本技能。在处理任何数据集时,识别和定位相同的值非常重要。在这篇文章中,我们将看看你可以使用的几种技术。

我们开始吧!

假设你收到了下面的问题。

"创建一个函数来检查列表中是否有重复值,如果有重复值,将它们全部列出。如果列表没有重复项,返回“没有重复项”。

相当简单的问题,让我们看看如何编码。

def duplicates(example_list):
    return len(example_list) != len(set(example_list))result_1 = duplicates(['1','2','3','5','5','6','6'])
result_2 = duplicates(['1','2','3','5','6'])
print(result_1)
print(result_2)

产出 1

上面的代码能够检查输入的列表是否包含任何重复项。如果列表确实有重复项,它将返回“真”,如果没有,则返回“假”。然而,这仅仅回答了我们问题的一半。

让我们想办法让它返回重复的值。要做到这一点,我们可以利用 If-Else 语句结合一些列表理解。

def duplicates_1(example_list):
    if (len(example_list) != len(set(example_list))) is False:
        return 'No Duplicates'
    else:
        return set([i for i in example_list 
                    if example_list.count(i) > 1])

result_1 = duplicates_1(['1','2','3','5','5','6','6'])
result_2 = duplicates_1(['1','2','3','5','6'])
print(result_1)
print(result_2)

产出 2

太好了,我们的函数正是问题所要求的。但是如果我们想从列表中提取更多的数据呢?

创建一个函数来检查列表中是否有重复的值,如果有重复的值,列出它们以及它们对应的频率。以元组列表的形式返回结果,从最频繁到最不频繁排列。如果列表没有重复项,返回“没有重复项”。

这是一个比你想象的稍微复杂的问题。下面的代码让我们完成了一半,但是有一个明显的缺陷。

def duplicates_2(example_list):
    if (len(example_list) != len(set(example_list))) is False:
        return 'No Duplicates'
    else:
        return sorted([(i, example_list.count(i)) 
                           for i in example_list if 
                           example_list.count(i) > 1], 
                      key = lambda x:x[1], reverse = True)

result_1 = duplicates_2(['1','2','3','5',
'5','5','5','6','6','6','7','7','7','7','7'])
result_2 = duplicates_2(['1','2','3','5','6'])
print(result_1)
print(result_2)

产出 3

我们可以看到,该函数返回重复项的所有实例,而实际上我们只需要一个元组。让我们尝试通过使用将列表转换为集合来解决这个问题。

产出 4

我们可以看到,虽然我们能够生成集合,但顺序已经改变。为了解决这个问题,我们将函数重新编码如下。

def duplicates_2(example_list):
    if (len(example_list) != len(set(example_list))) is False:
        return 'No Duplicates'
    else:
        return sorted(list(set([(i, example_list.count(i)) 
                           for i in example_list if 
                           example_list.count(i) > 1])),
                      key = lambda x:x[1], reverse = True)

result_1 = duplicates_2(['1','2','3','5','5','5','5','6','6','6','7','7','7','7','7'])
result_2 = duplicates_2(['1','2','3','5','6'])
print(result_1)
print(result_2)

产出 5

我们看到,通过简单地改变操作的顺序,我们可以得到我们想要的列表。让我们再一次尝试增加函数的复杂度。

创建一个函数来检查一个列表是否有重复值,如果有重复值,列出所有重复值及其对应的频率和每个重复值的索引。

将结果作为元组的列表返回,从最频繁到最不频繁排列。如果列表没有重复项,返回“没有重复项”。

同样,这比你想象的要稍微困难一些。

def duplicates_3(example_list):
    if (len(example_list) != len(set(example_list))) is False:
        return 'No Duplicates'
    else:
        final_list = sorted(list([(i, example_list.count(i), 
                    [n for n, j in enumerate(sample) if j == i])
                           for i in example_list if 
                           example_list.count(i) > 1]),
                      key = lambda x:x[1], reverse = True)

        return final_list
result_1 = duplicates_3(['1','2','3','5','5','5','5',
'6','6','6','7','7','7','7','7'])
result_2 = duplicates_3(['1','2','3','5','6'])
print(result_1)
print(result_2)

产出 6

我们再次看到这个函数做了我们想要的,唯一的问题是它返回了所有重复的值,而实际上我们只需要其中的一个。直观的解决方法是简单地将输出转换成一个集合。然而,我们遇到了这个问题。

产出 7

鉴于错误的性质,我们必须找到另一种方法来创建函数。下面的代码对函数进行了重大修改。

def duplicates_3(example_list):
    if (len(example_list) != len(set(example_list))) is False:
        return 'No Duplicates'
    else:
        filtered_elements  = set([i for i in example_list if 
                                  example_list.count(i) > 1])
        filtered_counts = [example_list.count(i) for i in 
                           filtered_elements]
        filtered_indexes = []
        for i in filtered_elements:
            a = [n for n, j in enumerate(example_list) if j == i]
            filtered_indexes.append(a)
        final_list = list(zip(filtered_elements, filtered_counts,
                          filtered_indexes))
        return sorted(final_list, key = lambda x:x[1], 
                      reverse = True)

result_1 = duplicates_3(['1','2','3','5','5','5','5',
'6','6','6','7','7','7','7','7'])
result_2 = duplicates_3(['1','2','3','5','5','6','6','6','7','7','7','7','7'])
result_3 = duplicates_3(['1','2','3','5','6'])
print(result_1)
print(result_2)
print(result_3)

产出 8

很好,我们的函数现在返回我们需要的数据。

总之

虽然看起来很简单,但是在一个列表中定位和索引重复项是非常棘手的。我们已经看到,如果我们没有正确地构造函数,就会出现意外错误。我希望这篇文章能够帮助您更好地了解如何最好地解决需要重复标识的问题。尽管我们在本文中使用的示例看起来微不足道,但它们确实展示了您在数据科学之旅中将会遇到的挑战。在以后的文章中,我们将学习如何将这些函数应用到实际数据中。

Pythonic 提示和技巧—寻找 GCD 和 LCM

原文:https://towardsdatascience.com/pythonic-tips-tricks-finding-the-gcd-and-lcm-34b08b6e232b?source=collection_archive---------34-----------------------

毒蛇之路

如何使用 Python 获得最大公分母和最小公倍数

Photo by 浮萍 闪电 on Unsplash

对于我们大多数人来说,寻找数字之间的最大公分母是小学数学中的一个常见练习。然而,在现实世界中,寻找 gcd 可以成为我们算法和分析的一个组成部分。在本文中,我们将讨论如何在不同的场景下获得 gcd。

我们开始吧!

"创建一个函数,将两个整数作为输入,并找到它们的最大公分母."

听起来很简单,让我们看看一个典型的算法,找到两个整数的 GCD。一种常见的算法如下:

def get_gcd_1(num1, num2):
    while num1 != num2: 
        if num1 > num2:
            num1 = num1 - num2
        else:
            num2 = num2 - num1
    return num1print(get_gcd_1(25, 5))
print(get_gcd_1(5, 20))
print(get_gcd_1(30, 10))
print(get_gcd_1(1500, 75))
print(get_gcd_1(20, 35))

图一。第一 GCD 函数

这是一个相当简单的算法,并给出了 GCD。然而,有更好的方法和更有效的方法来构造算法。首先,让我们看看该算法找到 GCD 需要多少次迭代。

def get_gcd_1(num1, num2):
    while num1 != num2: 
        if num1 > num2:
            num1 = num1 - num2
        else:
            num2 = num2 - num1
        print(num1, num2)
    return num1print(get_gcd_1(1500, 75))

图二。迭代直到找到 GCD

我们可以看到,在找到 GCD 之前,该算法将经历 20 次迭代。如果我们要在一个大得多的数据集上运行这个算法,那么大量的迭代肯定是一个问题。

相反,让我们尝试一个简单得多的算法。

def get_gcd_2(num1, num2):
    while num2 != 0:
        num1, num2 = num2, num1 % num2
    return num1print(get_gcd_2(25, 5))
print(get_gcd_2(5, 20))
print(get_gcd_2(30, 10))
print(get_gcd_2(1500, 75))
print(get_gcd_2(20, 35))

图 3。第二 GCD 函数

第二个函数明显比第一个干净。我们还使用了 %模块化操作符。它实际上是返回两个数之间的余数。如果第一个数字能被第二个数字整除,那么运算将返回 0。

print(10 % 5)
print(20 % 7)
print(100 % 99)
print(1000 % 10)
print(25 % 6)

图 4。模运算符示例

现在让我们检查一下第二个 GCD 函数在返回 GCD 之前要经历多少次迭代。

def get_gcd_2(num1, num2):
    while num2 != 0:
        num1, num2 = num2, num1 % num2
        print(num1, num2)
    return num1print(get_gcd_2(1500, 75))

图 5。迭代直到找到 GCD

太神奇了!我们可以看到,这个算法只需要 2 次迭代就可以得到 GCD。这比我们的第一个函数效率高得多。我们实际上可以对函数再做一次改进。线 num2!= 0 其实是多余的。我们可以简化为 while (num2)。我们得到的函数如下。

def get_gcd_2(num1, num2):
    while (num2):
        num1, num2 = num2, num1 % num2
        print(num1, num2)
    return num1print(get_gcd_2(1500, 75))

图 6。缩短功能

太好了,我们找到了一个非常有效的算法来搜索 GCD。从这里我们实际上可以找到最小公倍数(LCM)。

"创建一个函数,将两个整数作为输入,并找出它们的最小公倍数."

简单来说,最小公倍数就是能被两个或两个以上的数整除的最小数。为了构造一个可以搜索它的函数,我们可以利用我们的 GCD 函数。

def get_lcm(num1, num2):
    return (num1*num2)/ get_gcd_2(num1,num2)print(get_lcm(1500, 75))
print(get_lcm(117 , 33))
print(get_lcm(56, 5))

图 7。最小公倍数函数

我们可以看到,最小公倍数函数能够检索出正确的数字。现在让我们推广我们的函数,使它们能够处理任何数量的数字。

"创建一个函数,该函数将整数列表作为输入,并找到它们的最大公分母和最小公倍数。"

为了做到这一点,我们必须从 functools 库中导入 reduce 函数。

from functools import reduce
def get_gcd_lcm(list_of_ints):
    help_func = lambda x,y : x if y == 0 else g(y, x % y)  
    gcd = reduce(lambda x,y : help_func(x,y), list_of_ints)
    lcm = reduce((lambda x, y: x * y), list_of_ints) / gcd
    return gcd, lcm

results = get_gcd_lcm([75,1500,25,50,100])
print(f'GCD : {results[0]} LCM : {results[1]}')

图 8。广义 GCD 和 LCM 函数

很好,我们能够生成一个函数,它可以接受一个列表并生成 GCD 和 LCM。

总之

在本文中,我们能够成功地创建函数来查找整数列表的 GCD 和 LCM。这两个值对于数据科学家来说都非常有用,因为它们允许我们更好地了解我们正在处理的数据。尽管这篇文章非常笼统,但在以后的文章中,我们将回顾这些统计数据的许多用途,并将它们应用到真实世界的数据中。现在,我希望您能够找到对您当前任务有用的信息。

Pythonic 提示和技巧——使用理解

原文:https://towardsdatascience.com/pythonic-tips-tricks-using-comprehension-e7e91d6769f0?source=collection_archive---------41-----------------------

毒蛇之路

使用列表理解的有趣方法

凯瑟琳·拉威利在 Unsplash 上拍摄的照片

列表理解是一个强大的工具,它允许 Python 程序员以极其简洁的方式编写代码。在这篇文章中,我们将讨论如何使用列表理解来简化复杂的函数。

我们开始吧!

首先,让我们创建一个示例字符串。

sample_string = 'sample_for_list_comprehension'

现在让我们创建一个 Python 列表,其中包含该字符串的每个字符作为其元素。

sample_list =[]
for s in sample_string:
    sample_list.append(s)
sample_list

创建列表

我们实际上可以将这段代码一般化,并将其转换成一个 Python 函数。下面的代码就是这么做的。

def list_creator(input_string):
    final_list = []
    for i in input_string:
        final_list.append(i)
    return final_listsample_list = list_creator(sample_string)
sample_list

Python 函数

虽然这个函数完成了它的任务,但是我们实际上可以通过使用列表理解以一种更简洁的方式来编写它。下面的代码就是这么做的。

def list_comp_creator(input_string):
    return [i for i in input_string]
sample_list = list_comp_creator(sample_string)
sample_list

列表理解功能

我们看到这个函数现在基本上是一行程序。当然,这个例子相当简单。现在让我们用列表理解来完成更复杂的任务。

集成 If-Else 逻辑

我们实际上可以构建我们的列表理解来包含 If-Else 逻辑。例如,让我们创建一个只返回偶数索引字符的函数(注意 Python 编程语言从 0 开始)。

def list_comp_creator_even(input_string):
    return [i for n, i in enumerate(input_string) if (n%2 == 0)]even_list = list_comp_creator_even(sample_string)

集成逻辑

注意我们是如何使用枚举函数的。对于那些不熟悉这个函数的人来说,它实际上是一个计数器。如果它所属的函数有一个 For 循环,枚举函数将返回它当前所在的迭代。这可以通过下面的代码来演示。

for n, i in enumerate(range(-10,5)):
    print(n , i)

枚举输出

左边的数字代表索引,本质上它们显示了我们的函数当前处于 For 循环的哪个阶段。注意 Python 的枚举是如何从 0 开始的。这使我们能够编写代码,如前面的列表理解函数。为了更进一步,让我们明确地编码当遇到奇数索引字符时函数应该做什么。

def list_comp_creator_even_v2(input_string):
    return [i if (n%2 == 0) else 'odd' 
            for n, i in enumerate(input_string)]even_list_v2 = list_comp_creator_even_v2(sample_string)

集成 If-Else 逻辑

我们可以看到,该函数现在将返回所有偶数索引的字符,并用字符串**‘odd’**替换所有奇数索引的字符。

创建元组列表

列表理解不仅仅是创建一个简单的列表。我们实际上也可以使用列表理解来生成更复杂的输出。以下代码将接受输入字符串并返回包含(字符索引,字符)的元组列表。

def index_creator(input_string):
    return [(f'{n}', i) for n, i in enumerate(input_string)]
indexed_list = index_creator(sample_string)
indexed_list

创建元组列表

很好,我们已经使用列表理解创建了一个元组列表。虽然这已经很有用了,但是让我们试着增加复杂性。让我们创建一个包含(字符索引,字符,大写字符,索引奇偶校验)的元组列表。

def index_creator_v2(input_string):
    return [(f'{n}', i, i.upper(), 'Even' if (n%2==0) else 'Odd') 
             for n, i in enumerate(input_string)]
indexed_list = index_creator_v2(sample_string)
indexed_list

复杂列表理解

注意,我们不局限于简单地创建一个元组列表,我们还可以创建一个列表列表。

def index_creator_v3(input_string):
    return [[f'{n}', i, i.upper(), 'Even' if (n%2==0) else 'Odd'] 
             for n, i in enumerate(input_string)]
indexed_list = index_creator_v3(sample_string)
indexed_list

列表输出列表

太好了,我们确实进入了列表理解的更复杂的一面。

将列表加载到熊猫数据帧中

当然,为了便于数据分析,让我们将输出加载到 pandas 数据框架中。

import pandas as pddef panda_dataframe_creator(input_string):
    final_list = [[f'{n}', i, i.upper(), 'Even' if (n%2==0) 
                   else 'Odd'] for n, i in enumerate(input_string)]
    return pd.DataFrame(final_list, 
                        columns =['character_pos','character',
                                  'upper_character','index_parity'])df = panda_dataframe_creator(sample_string)
df.head(5)

作为熊猫数据帧加载的列表列表

总之

对于任何使用 Python 语言的数据科学家来说,列表理解都是一个极其强大的工具。这篇文章只是触及了它的可能性的表面。在以后的文章中,我们将处理更具挑战性和更复杂的问题,这些问题需要我们利用列表理解。

Pythonic 提示和技巧—使用 Lambda

原文:https://towardsdatascience.com/pythonic-tips-tricks-working-with-lambda-987444d80517?source=collection_archive---------44-----------------------

毒蛇之路

如何在 Python 中应用 Lambda 函数

蒂姆·马歇尔在 Unsplash 上的照片

Python 语言最有趣的方面之一是 Lambda 函数。对于擅长将代码简化为简单逻辑语句的程序员来说,Lambda 函数有助于大幅减少代码行数。在这篇文章中,我们将介绍什么是 Lambda 函数,以及如何在 Python 中以实用的方式应用它们。

我们开始吧!

Lambda 的优势

假设你有一个函数,可以将你输入的任何数字乘以 10。

def mult_ten(original):
    return original * 10number = mult_ten(5)

功能输出

这个函数实际上可以写成一行文字。这通过使用λ来实现。

mult_ten = lambda i : i * 10
print(mult_ten(5))

完全相同的输出

但是等等,更复杂的函数呢,比如下面这个:

def mult_int(original, multiplier):
    return original * multipliernumber = mult_int(5, 11)

二元函数的输出

这也可以浓缩成一个 lambda 函数。

mult_int = lambda i, j : i * j
print(mult_int(5, 11))

完全相同的输出

事实上,我们可以有任意多的变量。下面是一个更复杂的函数的例子。

def complicated_function(a, b, c, d, e):
    return ((a+b) / (c+d)) * enumber = complicated_function(1, 2, 3, 4, 5)

复杂函数的输出

同样,我们可以将整个函数浓缩成一个λ函数。

complicated_function = lambda i,j,k,l,m : ((i + j) / (k + l)) * m
print(complicated_function(1, 2, 3, 4, 5))

完全相同的输出

使用 IF-ELIF-ELSE 逻辑应用 Lambda 函数

当然我们并不局限于纯数学运算。我们可以使用 If-Else 语句来创建更有趣的函数。以下面的函数为例。它将一个数字分类为奇数还是偶数偶数

def odd_even(figure):
    if figure % 2 == 0:
        return 'Number is Even'
    else:
        return 'Number is Odd'number = odd_even(2)

功能输出

这也可以折叠成一行λ函数。**

**odd_even_lambda = lambda i: 'Number is Even' if i % 2 ==0 
                             else 'Number is Odd'
new_num = odd_even_lambda(3)**

正确输出

现在让我们尝试加入一个 elif 元素。下面的函数将告诉我们这个数字是负数、正数还是零。

**def pos_neg(figure):
    if figure < 0 :
        return 'Number is Negative'
    elif figure == 0:
        return 'Number is Zero'
    else:
        return 'Number is Positive'
print(pos_neg(-1))
print(pos_neg(0))
print(pos_neg(1))**

功能输出

实际上没有直接的方法将 elif 元素合并到 lambda 函数中。但是我们可以通过使用嵌套 if 的来达到同样的效果。

**pos_neg_lambda = lambda i: 'Number is Negative' if i < 0  else ('Number is Zero' if i == 0 else 'Number is Positive')
print(pos_neg_lambda(-2))
print(pos_neg_lambda(0))
print(pos_neg_lambda(2))**

完全相同的输出

将 Lambda 应用于熊猫数据帧

当然,要真正体会 lambda 的强大,我们必须看到它的实际应用。下面是一个通过 NumPy 和 Pandas 创建的数据帧的示例。

**import pandas as pd
import numpy as np
df = pd.DataFrame(np.arange(-10,11,1))
df.head(5)**

构建的数据框架

利用 lambda 的强大功能,我们可以基于引用列创建包含元素的新列。

**df['number_type'] =  df[0].apply(odd_even_lambda)
df['number_sign'] = df[0].apply(pos_neg_lambda)**

编辑数据帧

仅用两行代码,我们就能够为数据帧生成新列。这展示了 lambda 函数能够以简洁优雅的方式将逻辑应用于大量数据的能力。

总之

Lambda 函数是 Python 编程语言的一个非常强大的特性。经常使用它们可以训练你的思维,将你的编程逻辑提炼到它的核心元素。这是一项不仅在 Python 编程语言中,而且在大多数编程语言中都有帮助的技能。尽管我们讨论的例子非常基础,但我希望它们足以激起您的兴趣,并让您习惯于使用 lambda。

使用 Xarray 跨多个变量执行统计的 Pythonic 方式

原文:https://towardsdatascience.com/pythonic-way-to-perform-statistics-across-multiple-variables-with-xarray-d0221c78e34a?source=collection_archive---------25-----------------------

气候数据科学

首先在数据集中创建一个分类维度

pawel szvmanski 在 Unsplash 上拍摄的照片

考虑到这一点,您有一个存储在数据集(ds)中的模型集合,每个集合成员存储为一个不同的变量。你觉得总体*均水*如何?如果你有三个成员(ens1ens2ens3),一种方法是这样做:

ens_mean = (ds['ens1'] + ds['ens2'] + ds['ens3']) / 3

虽然这对于小型合奏来说没什么问题,但是有一种更 Pythonic 化的方法可以做到这一点。让我们假设每个成员代表地球上每个位置的月温度,我们真正想要的是这样的代码:

ens_mean = ds['temperature'].mean('ensemble')

这是干净的,需要键入的字符更少,代码要完成的任务也很清楚。

在这篇文章中,我将演示如何操作一个Xarray数据集,这样你就可以对一个新的分类变量进行*均。我希望任何使用Xarray分析地理空间数据或集合模型的人都能在这篇文章中找到相关性。

1.创建一个示例数据集

如果你想继续,我将使用一个包含合成数据的数据集来解释这个概念。这个数据集的代码如下。让我们假设这个数据集以 1 度的空间分辨率表示来自不同模型的月温度输出。

数据集的info()如下所示。请注意,每个系综成员(ens1ens2ens3)都是不同的变量。

将集合成员显示为不同变量的数据集输出。(图片由作者提供)

2.将变量合并到一个新维度

现在我们有了一个示例数据集,让我们将所有成员合并到一个名为temperature的变量中,该变量具有维度(ensembletimelatlon),其中新的ensemble坐标将提供对每个成员的访问。下面的代码完成了这项任务。

2.1 每行的含义

这里发生了很多事情,让我们一行一行地来看一下。

  • variables = list(ds) →这将创建数据集中所有变量名的列表。如果您不需要所有的变量,那么您可以简单地硬编码一个变量名列表。

第二行做了三件事:

  1. expand_dims(‘ensemble’, axis=0) →获取每个变量,并在第一个轴位置添加一个新尺寸。
  2. assign_coords(ensemble=[var]) →创建一个与上一步中的新尺寸同名的新坐标。这也将变量名分配给坐标
  3. rename("temperature") →将变量重命名为temperature

注意,这是通过列表理解对每个变量进行的。如果这令人困惑,我鼓励你在单个变量上运行这个程序,看看对象在每一步之后是如何变化的。下面的代码可以对此有所帮助。

var = 'ens1'
expand = ds[var].expand_dims('ensemble',axis=0)
assign = expand.assign_coords(ensemble=[var])
rename = assign.rename('temperature')

然后,您可以输出expandassignrename来查看对象在每一步之后如何变化。

一旦我们有了一个数据数组列表,我们需要使用xr.merge()沿着集合维度将它们缝合在一起。

  • xr.merge(da_list) →这将所有数据阵列合并成一个数据集。新的数据集只有一个变量(temperature),所有的数据数组沿着ensemble坐标合并

最后两行可能没有必要。这些行保留了ds中的全局属性。

global_attrs = ds.attrs
ds_merged.attrs = global_attrs

最终数据集将如下所示:

包含一个变量(温度)和存储在新坐标(集合)中的每个集合成员的合并数据集输出。(图片由作者提供)

2.2 合并变量的另一种方法

有多种方法可以将变量合并到一个新的维度上。这是另一种方法。

2.3 使用我们的合并数据集

现在我们有了合并的数据集,我们可以很容易地跨集合成员进行统计。

  • ds_merged[‘temperature’].mean(‘ensemble’)
  • ds_merged[‘temperature’].var(‘ensemble’)

如果我们只想绘制ens1,我们可以像这样选择它

ds_merged.sel(ensemble='ens1')

虽然访问单个集合成员稍微麻烦一些,但我认为能够在整个集合中执行统计的好处更重要。

3.概括代码

在这一节中,我将描述两种方法来推广这种技术。我给出的代码仅仅是为了给你一些如何扩展这篇文章的想法。

3.1 选项 1-使用管道()

第一种方法是将函数[pipe()](https://xarray.pydata.org/en/stable/generated/xarray.Dataset.pipe.html)到数据集。这更适合于将多个功能链接在一起。但是,根据具体情况,您可能想要pipe()单一功能。

3.2 选项 2-注册访问者

另一种选择是通过注册一个访问器来扩展 Xarray 。实际上,这是通过简单地添加一个类装饰器来实现的。如果您正在处理一组特殊用途的函数,这种方法是合适的。例如,也许你正在研究一类处理集合模型的方法。下面的代码展示了如何装饰一个具有var_merger()函数的类。

请记住,一旦注册了访问者,就不能对其进行更改。例如,如果您在笔记本中运行这段代码,然后决定向该类添加另一个函数并重新运行它,则该新函数将不会向访问器注册。您必须首先重启内核才能使它生效。因此,注册访问器放在代码库中更合适。

您可以在数据集(ds)上使用新的访问器,如下所示:

ds_merged = ds.ensemble.var_merger()

注意,类名是不相关的,因为它是我们提供给数据集的访问器名。

4.最后的想法

希望本教程能帮助你更有效地使用合奏。在撰写本文时,我无法在文档以及它们的“How I…”部分中找到像这样合并变量的单行方法。

Xarray功能极其强大,许多“我该怎么做”的问题都可以用现有的功能来解决。话虽如此,找到正确的功能并以正确的方式将它们链接在一起以实现期望的目标可能会很棘手。

如果你对如何改进这篇文章有任何意见或建议,请告诉我。此外,请让我知道,如果你知道一个更优雅的解决方案,将变量合并到一个共同的坐标。

一如既往,我很乐意帮助解决您可能遇到的任何问题。

感谢阅读和支持媒体作者

https://lukegloege.medium.com/membership

Python 的数据类被低估了

原文:https://towardsdatascience.com/pythons-data-classes-are-underrated-cc6047671a30?source=collection_archive---------9-----------------------

看看 Python 中的数据类以及它们如何节省大量时间

(src =https://pixabay.com/images/id-1076536/

介绍

在计算机编程的世界里,有许多程序员为了节省时间而使用的技术。在软件工程的世界里,时间总是一个问题,而且期限很难达到。因此,节省时间的技术非常受欢迎,更重要的是,非常有用。虽然 Python 可能是一种编写速度很快的语言,但是有一些方法可以加快编写 Python 的过程,甚至变得更快。记住这一点,请允许我向您介绍 Pythonic 编程的概念和对象:

数据类。

Python 中的数据类是使用标准库中的工具修饰的类。dataclass 装饰器@dataclass 可用于向用户定义的类添加特殊方法。这样做的好处是,这是自动完成的,这意味着您可以更多地关注类中的函数,而不是关注类本身。

笔记本

创建数据类

虽然 dataclass decorator 包含在标准库中,但像 Python 的许多其他组件一样,它包含在自己独特的模块中。记住,创建数据类的第一步是实际导入模块。

import dataclass

现在我们可以创建一个保存任意数据的新类,并用@dataclass 装饰器来装饰这个类。我们将使用 PEP526 中描述的注释来定义这个类中的数据。

from dataclasses import dataclass[@dataclass](http://twitter.com/dataclass)
class Food:
    name: str
    unit_price: float
    stock: int = 0

    def stock_value(self) -> float:
        return(self.stock * self.unit_price)

现在我们可以通过调用这个构造函数来创建一个新类,如下所示:

carrot = Food("Carrot", .45, 25)

现在让我们看看如果我们不使用 dataclass 装饰器编写相同的代码会发生什么:

class Food:
    name: str
    unit_price: float
    stock: int = 0

    def stock_value(self) -> float:
        return(self.stock * self.unit_price)carrot = Food("Carrot", .45, 25)

我们看到,当我们调用这个构造函数时,我们得到了一个类型错误。这是因为现在我们的数据仅仅被存储为成员变量。因此,我们的 init 函数采用默认形式。默认表单不包含任何参数,这是我们试图创建数据时调用的函数。为了解决这个问题,我们需要为我们的类编写一个新的 _ _ init _ _ 函数:

class Food:
    name: str
    unit_price: float
    stock: int = 0

    def __init__(self, name: str, unit_price: float, stock: int):
        self.name, self.unit_price, self.stock = name, stock, unit_price

    def stock_value(self) -> float:
        return(self.stock * self.unit_price)

现在我们看到,我们可以成功地创建我们的食物类型:

carrot = Food("Carrot", .45, 25)

我们刚刚添加到函数中的所有代码都是由我们的 dataclass 装饰器自动添加的。不用说,这是相当可怕的!

他们为什么伟大?

现在我们对这个装饰器的作用有了基本的了解,让我们考虑一下为什么您可能想要将它应用到您的代码中。首先,您将需要编写许多不太特殊的方法。这可以节省时间,提高你的工作效率。在上面的例子中,如果我没有在一行中完成所有的自我赋值,我们可以节省四行代码。此外,每当我们调用 dataclass 装饰器时,这不是唯一生成的特殊函数。这意味着我们的类在编写更少的代码的同时,在特殊函数方面会更有说服力。

使用这个装饰器的一个非常有效的优点是它避免了类中的许多混乱。将 Python 类中的每个值赋给成员变量的例子实际上非常普遍。当这完全可以通过简单地使用装饰器来避免时,没有什么理由用大量的self.x = x来编写整个函数。这也可以使一个类更具可读性。很容易理解为什么会这样

[@dataclass](http://twitter.com/dataclass)
class Food:
    name: str
    unit_price: float
    stock: int = 0

    def stock_value(self) -> float:
        return(self.stock * self.unit_price)

可能比这更具可读性

class Food:
    name: str
    unit_price: float
    stock: int = 0

    def __init__(self, name: str, unit_price: float, stock: int):
        self.name = name self.stock = stock
        self.unit_price = unit_price

    def stock_value(self) -> float:
        return(self.stock * self.unit_price)

此外,使用 dataclass decorator 还可以将焦点放在这个函数中真正重要的地方。因为在这个类的初始化中不需要做任何事情,所以把一个大的 init 函数放在中间是没有意义的。这里的重点应该是类内部的数据和处理这些数据的函数。

最后,需要注意的是,这并不是装饰者所做的全部工作。数据类可以用来快速创建一些非常复杂的惊人结构。例如,我们也可以使用 dataclasses 作为 JSON 数据的 Pythonic 方法。在很多方面,我们可以把它们想象成 Javascript 中的数据数组。不用说,这在 Python 编程中很方便。考虑这个例子:

[@dataclass](http://twitter.com/dataclass)
class Point:
     x: int
     y: intp = Point(10, 20)
assert dataclasses.asdict(p) == {'x': 10, 'y': 20}

在这里,我们将一个类转换成一个包含这两个值的字典。这两个基本上已经成为等价的数据类型,它们的定义需要大约相同的时间。编写一个比字典还快的类似乎是不可能的,但是在上面的例子中我们做到了。不用说,这些肯定会派上用场。对于我们这些处理大量数据的人来说尤其如此。

结论

总的来说,数据类太棒了!如果你想了解更多关于数据类的内容,你可以查看 PEP557 来获得关于数据类和使用它们的全面的信息来源。有很多很好的理由将这些实现到您的代码中。这不仅可以让你的代码更容易阅读,而且也让你的代码更容易编写!感谢您的阅读,我希望这个装饰器将带您踏上 Python 之旅!

Python 的依赖性冲突剥夺了数据科学家的睡眠

原文:https://towardsdatascience.com/pythons-dependency-conflicts-are-depriving-data-scientists-of-sleep-ff52c5689336?source=collection_archive---------2-----------------------

关于掌握你的个人数据科学项目中的依赖关系并找到一种可能的方法来处理新出现的冲突的指南。

照片由alt umcodeUnsplash

介绍

分享知识是我所在的 FH Kufstein Tirol 主数据科学智能分析博客项目的核心。在https://FH-kuf stein . AC . at/Studieren/Master/Data-Science-Intelligent-Analytics-BB查看该程序。

让我们实话实说:图书馆是一切的解决方案!你在一个项目中遇到了问题,或者需要一个特殊的功能,而你不想自己编写代码?可能会有人以前遇到过同样的问题,并且可能已经为此开发了一个库!然而,正如生活中经常发生的那样,并非所有闪光的东西都是金子。

当然,库是非常有用的,但是它们会很快把你带到一个你不得不处理一些*“依赖冲突”*的境地。迟早,几乎每一个软件开发人员都会发现自己到了那一步。如果项目仍然很小,避免它会容易一点,但是一旦项目变大,你肯定需要更加小心。

用哪种编程语言编写代码并不重要;不幸的是,它可以发生在几乎每一个人身上。例如,Python 是一种众所周知的编程语言,大多数人听到“依赖冲突”时都会想到它。

什么是依赖冲突?

首先,让我们从澄清软件开发上下文中的术语“依赖性”开始。依赖是指一个软件,例如一个包或库,依赖于另一个软件,通常是由他人编写的(第三方代码)。一般来说,每当您在项目中使用新的包或库时,您也在添加它的依赖项。[1]

我会给你一个简单的例子,看起来像什么依赖关系。

*你开始着手一个新的软件项目;让我们简单地称它为当前版本 1 的 APP。在这个应用程序中,您需要某些功能,您可以通过添加新的库 LIB A(版本 1)和 LIB B(版本 1)来包含这些功能。LIB A 和 LIB B 都在代码中使用了另一个功能,LIB C(版本 1)。*

使用库的应用程序(图片由作者提供)

*有一天,LIB B 获得了版本 2 的更新,它实现了 LIB C 的最新版本 2。到目前为止,它并不影响您的应用程序,因为它仍然使用版本 1 的 LIB A 和 LIB B。*

特定库的新版本(图片由作者提供)

*当您想要将应用程序更新到包含最新版本的 LIB B 的版本 2 时,依赖性冲突就出现了。现在这将导致一个问题,因为 LIB A 仍然使用版本 1 的 LIB C,而 LIB B 已经依赖于 LIB C(版本 2)——但同时只能使用一个版本的 LIB C。*

应用程序更新一个库,这可能会由于依赖性问题而导致问题(图片由作者提供)

为了摆脱这个依赖问题,现在必须包含最新版本的 LIB A,希望它和 LIB B 依赖于相同版本的 LIB C。如果使用最新的 LIB C 版本还不支持 LIB A——欢迎来到一个充满依赖冲突的世界!

为什么会发生依赖冲突?

正如我在上一节中试图向您展示的简化版本,一个原因可能是不同的库或包之间的不兼容性。通常,如果有许多不同的库和包在使用,它们是由许多不同的人在不同的时间点开发的,并且在许多情况下,那些库不再被维护。

你几乎无法控制所有这些图书馆。如果您正在使用的某个库的开发者决定更新他们的库,添加功能或删除部分代码(不管出于什么原因),这可能是新的依赖冲突的开始。

由于第三方库可能没有使用编写良好的代码,或者可能缺少该库的良好文档,也可能会发生冲突。在这两种情况下,当一个 bug 突然出现,但你没有(或几乎没有)机会摆脱它时,它会非常烦人。

但是我能避免吗?

理论上,当然,你可以避免它。但是——特别是如果你在一些编程语言中没有使用包管理器,比如‘Anaconda’——要真正避免依赖冲突几乎是不可能的。尽管如此,还是有一些小贴士可以帮助你尽可能避免这种情况。所以,我们开始吧:

使用库之前:

  • 有没有可能使用像“Anaconda”这样的包管理器?如果答案是肯定的,强烈建议这么做。
  • 想想你要解决的问题。独自编写代码需要付出多大的努力?你有可能(现在或者在开发的后期)在某个时候去掉那个特定的库吗?
  • 如果可能,检查下载计数器和库的分级。它是一个已经被很多人使用的受欢迎的工具,还是不那么出名?
  • 这个库也包含很多依赖项吗?如果是这样的话,它可能会更快地导致冲突。
  • 检查文档:它是否写得很好,是否足够详细,足以让您使用这个库?
  • 到目前为止,这个库发布了多少版本/更新/错误修复?是维护的很好,定期更新,还是还有 0.0.1 版本,十年前就发布了,再也没碰过?

使用库时:

  • 如果您添加/删除了许多包和库,请检查清单。清单是否与您真正使用的包相匹配?
  • 还有其他提供相同功能的库吗?(也许这一步包括一些代码调整。)如果是,您可以替换这些库。
  • 如果可能的话,你可以尝试派生和修复开源库,推动这些变化,并希望维护者接受补丁。

再来两个提示:

  • 监视该库如何影响应用程序的性能。是否提高了性能?还是对它产生了不好的影响?也许有一个更轻便的。
  • 您可以对项目进行版本锁定,这无疑有助于避免依赖冲突。然而,这一步意味着您不能再实现正在使用的库的新特性或改进,因为您需要坚持使用所选择的版本。[2]

如何管理您的依赖性

现在,您已经听到了一些您可能会在下一个数据科学项目中考虑的提示。下一步,让我告诉你一些可能的方法,告诉你如何以适当的方式管理你的依赖关系。

首先,有必要在团队内部建立一些指导方针,指导你如何处理依赖关系。您是否希望在知道某个特定版本可能存在漏洞的情况下坚持使用它?或者,您是否承担了更新依赖项所带来的风险?[3]制定每个团队成员都必须遵守的规则;这将有助于你的项目,并防止挫折肯定!

区分依赖关系的优先级也非常重要。不是每一个都和另一个一样重要。如果可能,尝试先更新您的优先依赖关系。这有几个好处:您可以更好地了解仍然需要更新的依赖项和那些您已经更新的依赖项,这些更新可能会修复潜在的安全问题。

使用 Anaconda 管理依赖关系可能会容易一点,因为它会关注您正在使用的环境。然而,如果不注意你的环境,它会在短时间内变得很大。

概述

几乎在每个软件开发项目中都会发生依赖冲突。在一个项目中长时间使用大量第三方库(尤其是那些可能没有以适当方式维护的库)的组合可能会导致一些问题,这些问题与那些库的不同版本有关,这是由于可传递的依赖性。完全避免陷入这种情况确实很难,但是也许你在这篇文章中读到的一些提示会有所帮助。

  • 如果可能,使用包管理器
  • 试着将去掉一个特有的
  • 使用流行的
  • 尝试避免已经有很多依赖项的库
  • 检查文档的质量
  • 使用维护良好的
  • 留意你的清单
  • 使用具有相同功能的其他库
  • 自行修复开源
  • 如果可能,使用轻量级
  • 版本锁定你的项目

结论

库、包、依赖项——使用它们是并且可能永远是开发人员、数据科学家或软件工程师的一部分。通常,与从头开始编写重要的功能相比,它们可以节省我们大量的时间。依赖性冲突一开始可能看起来很可怕,甚至更容易发现自己在处理它们,但是如果你记住几个简单的技巧,你就能够战胜冲突并掌握依赖性!

我希望你喜欢阅读这篇文章,也许它会对你的下一个项目有一点帮助。谢谢大家!

参考

[1] Prana,G.A.A .,Sharma,a .,沙尔山,L.K. 眼不见,心不烦?易受攻击的依赖项如何影响开源项目。 Empir 软件工程 26, 59 (2021)。https://doi.org/10.1007/s10664-021-09959-3

[2] 田边 y,青谷 t,增原 H. 2018。一种面向上下文的编程方法来解决依赖地狱。《第十届面向上下文编程国际研讨会论文集:运行时组合的高级模块化》(COP '18)。计算机械协会,纽约,纽约州,美国,8-14。土井:https://doi.org/10.1145/3242921.3242923

*[3] 帕先科一世、武德、马萨奇 F. 2020。依赖管理及其安全含义的定性研究。2020 年 ACM SIGSAC 计算机和通信安全会议论文集(CCS '20)。计算机械协会,纽约,纽约州,美国。土井:https://doi.org/10.1145/3372297.3417232*T42

Pythons inspect 模块——如何在您的下一个 python 项目中节省时间和精力

原文:https://towardsdatascience.com/pythons-inspect-module-how-to-save-time-and-effort-in-your-next-python-project-8f2d269b8ed?source=collection_archive---------15-----------------------

检查所有的东西!节省配置!现在技术债务减少了 30%!

作者图片

目前,我正在用 Python 后端构建一个 web 应用程序。本质上,后端对 Pandas 数据帧执行操作,而前端是一个 react 应用程序,它允许一个拖放界面来构建操作图,以处理和分析数据!

作为该应用程序的一部分,我需要一种方法来定义该应用程序中使用的处理节点的类型。这意味着前端和后端都需要知道定义了哪些节点以及每种不同类型的节点所需的参数。最初,我通过在 Python 后端将节点维护为类,并在前端分别保存每个节点的 JSON 模板来解决这个问题。这是一个概念验证,但是当我在后端定义一个新的节点类或者更改一个现有节点所需的参数时会发生什么呢?我需要在前端添加或更新 JSON。这意味着每次我想更新后端时,我都需要部署新版本的前端。

在考虑解决这个问题时,我意识到几乎所有前端需要的东西都已经在后端类中定义了!在适当的时候,我用默认参数编写类型暗示的 python 代码。我只是需要一种方法来“检查”这些类并提取相关信息。如果我可以获得这些信息,那么我就可以将它作为 API 端点暴露给前端,这样每当我在后端添加或更改一个节点时,就可以有效地消除对前端进行新部署的需求。

Python 的 inspect 模块从 Python 2.x 时代就已经内置,直到现在,对我来说,它是一颗藏在毛料中未被充分利用的钻石。inspect 模块允许对“活动对象”进行分析,本质上使其成为一种提取当前 python 代码片段信息的编程方式。这个模块对于手头任务的相关动作归结为识别模块中定义的类,提取这些类上函数的参数、类型提示和默认值。使用下面的代码可以从 python 模块中提取类。

从 python 模块中提取类

现在我们有了一种从模块中获取类的方法;我们需要提取这些类中函数的参数、类型提示和默认值。我做了一个设计选择,在操作节点类的 init()方法中定义操作的所有必需参数。这意味着我需要为每个操作节点类只提取那个函数的参数。我们可以使用下面的代码来提取参数名、类型提示和默认值。

从函数中提取参数

我对上面的代码不太满意。那些查找字典看起来很麻烦,但是它们很有用,我不认为自己必须频繁地更新它们,除非我想开始向操作节点类传递更奇特类型的参数。在定义这个的时候,EMPTY_MAP 是我的痛处。在前端处理它是有意义的,因为它应该是解释空值意味着什么的部分。然而,我不太喜欢它给我的 API 客户机代码增加的臃肿。此后,我选择通过后端处理它,但这可能会反复几次,直到我很高兴自己做出了正确的设计选择。

将前面这两个方法结合在一起的实际代码称为 FastAPI 依赖项。这段代码导入(重新导入)相关的模块,然后使用预定义的函数提取 classes/init 参数,并构建一个操作元数据集合。

现在,您可能会注意到从这些类中提取了一些独特的属性。这些是普通类定义中缺少的信息。我首先尝试了一些不同的方法,包括使用超类名和其他类似的东西,但是它们没有用自己的类型/子类型和显示名来注释每个类那样灵活。下面定义了一个输出 CSV 类的例子,看看我是如何添加这些额外的属性的。

最后,将它暴露给前端的 FastAPI 端点会是什么样子?请参见下面的代码以获得一个快速示例:

作者图片

我希望这已经被证明是有用的,并且作为一个很好的例子来展示 Python 的 inspect 内置模块的巨大效用。

请不吝赐教,与我分享您在本模块中完成的其他一些令人兴奋和独特的事情!

Python 最令人困惑的操作符

原文:https://towardsdatascience.com/pythons-most-confusing-operator-96c67d6e661a?source=collection_archive---------14-----------------------

有趣地看看 Python 的加等于运算符的怪癖和特性

(src =https://pixabay.com/images/id-534103/

介绍

运算符是现代编程中绝对重要的组成部分。它们通常用于数学运算,但也可用于变异不同类型的泥沼并比较这些类型。Python 编程语言有一系列不同的操作符,新的操作符也在不断增加,最新的是 Python 3.8 中添加的 walrus 操作符。实际上,我写了一篇关于该操作符的文章,这篇文章还包括了一些其他有趣改进的亮点,对于感兴趣的人来说可能值得一读:

一个具有一些有趣功能和怪癖的操作符是加号-equals 操作符。乍一看,这似乎只是加法和断言操作符的基本组合。虽然可能是这种情况,但是有一些真正有趣的特性使这个操作符与众不同。今天,我将展示这个操作符的一些有趣的特性,并演示是什么让它如此有趣。如果您想看看这些古怪的行为,您也可以从这里获得我用来演示这些代码的笔记本的副本:

https://github.com/emmettgb/Emmetts-DS-NoteBooks/blob/master/Python3/interesting%20plus-equals.ipynb

加号-等号概述

在我们深入这个操作符的古怪之处之前,我们可能应该熟悉一下这个操作符在语言中通常是如何使用的。在这种意义上,加号-等号可以被认为是加法和断言的捷径。而不是做一些像

x = 5
x = x + 5
print(x)10

我们可以做些类似的事情

x = 5
x += 5
print(x)

当然,这个运算符的这种使用方式归结为个人喜好。一些用户可能更喜欢后一种添加数字的方法,但是在我的主观意见中,我总是发现加号-等号运算符可以使这样的表达式更加简洁。

加号-等于和变异元组

您可能熟悉 Python 中的元组和列表数据结构。这是 Python 中两种不同类型的可迭代一维数组,它们之间有一个关键区别。元组是不可变的结构,这意味着一旦被定义,元组就不能被改变。列表当然是可变的等价物,包含 append()之类的方法。

虽然元组不是可变的对象,但是有一个关键的区别是许多程序员忽略了的。虽然元组本身可能不是可变的,但其中的类型仍然是可变的。元组仍然是一种数据结构,并且该元组中的数据仍然属于其各自的类型。然而,Python 仍然拥有人们可能期望的来自尝试改变不可变类型的抛出。考虑到这个功能,我们可以预期下面的代码会抛出一个错误,指出元组是不可变的。

z = (5, 10, 15)
z[4] = 15

然而,为了证明我的观点,即元组中包含的数据仍然是可变的,如果我们要从元组中取出一些数据,我们可以对其进行变异:

newnum = z[3]

愚蠢的 Julia 程序员,在 Python 中索引从零开始。

newnum = z[2]
newnum += 5
print(newnum)20

考虑下面的列表元组:

letters = (["S", "T"],
                 ["A", "D"])

元组本身是不可变的类型。然而,其中的列表,一个是字母表中 L 后面的字母,另一个是前面的字母,仍然是可变的。如果我们现在尝试在列表中添加一个新字母,你认为会发生什么?

letters[0] += "Q"

不出所料,我们得到了另一个类型错误,因为元组一旦创建就不能变异。然而,只有在数据结构上调用我们的操作符之后,才会抛出这种类型错误。换句话说,我们可能遇到了一个异常,但是如果我们现在从元组中打印出我们的列表,我们将看到该元素实际上已经被追加到我们的列表中:

print(letters[0])['S', 'T', 'Q']

关于这一点需要注意的一点是,它不能直接处理数据类型。关于 Python 有趣的事情是,基本上在库内的数据类型和我们可能在库外使用的数据类型之间没有关键的区别。这意味着我们可以用纯 Python 重写这些类型。这与大多数语言不同,大多数语言通常包含基本的数据类型,而这些数据类型不是语言本身结构化的。

信不信由你,这种类型错误和元组可变性是 Python 的一个特性,而不是 bug。乍一看,这里似乎犯了某种错误,但是考虑到 Python 的方法论,这在某种程度上确实是有意义的。这都与 Python 如何处理数据有关,更重要的是,plus-equals 如何处理不同的数据类型。这也是加号-equals 如此棒的部分原因!

看看加号等于

既然我们知道了加等于运算符有多奇怪,那么让我们来看看运算符实际上是如何工作的,以便更好地理解其用法中的奇怪现象是如何产生的。当然,这将是 Python 在这个操作符上实现相同功能的一个更粗糙的版本。代码看起来就像我们期望的那样:

def plusequals(num1, num2):
    total = num1.__iadd__(num2)
    num1 = total

然而,每当我们处理更大的表达式,而不是简单的数据类型时,我们可以认为代码看起来更像这样:

def plusequals(x[0], element): total = x[0].__iadd__(element)
   x[0] = total

当然,正如我们前面提到的,对于从元组中提取的元素,加法是完全可能的。然而,每当我们将 x 的第一个(第零个)索引赋给新的总数时,就会遇到元组的类型错误。

结论

加号-等号运算符肯定比第一次看到的要多。简单的代码允许它做一些非常有趣和古怪的事情,它甚至可以用来提供对 Python 编程的更坚实的理解。之所以认为这是一个特性,而不是一个错误,是因为事实上这正是操作符的工作方式。简单数据类型(如整数)的基本操作实际上完全依赖于这一点,因此它不是必须改变的东西是有意义的。也就是说,虽然它有点古怪和令人困惑,但它确实很酷!感谢您的阅读!

Python 的 predict_proba 实际上并不预测概率(以及如何修复它)

原文:https://towardsdatascience.com/pythons-predict-proba-doesn-t-actually-predict-probabilities-and-how-to-fix-it-f582c21d63fc?source=collection_archive---------0-----------------------

实践教程

如何评估和修复校准误差概率

[图片由作者提供]

数据科学家通常根据准确度或精确度来评估他们的预测模型,但很少问自己:

"我的模型能够预测真实的概率吗?"

然而,从商业的角度来看,对概率的准确估计是非常有价值的 ( 有时甚至比精确的预测更有价值)。想要个例子吗?

假设你的公司正在出售 2 个马克杯,一个是普通的白色马克杯,另一个上面有一只小猫的图片。你必须决定向给定的顾客展示哪个杯子。为了做到这一点,你需要预测一个给定的用户购买它们的概率。所以你训练了几个不同的模型,你得到了这些结果:

ROC 相同但校准不同的模型。[图片由作者提供]

现在,你会向这位用户推荐哪个杯子?

两个模型都认为用户更有可能购买普通的杯子(因此,模型 A 和模型 B 在 ROC 下具有相同的面积,因为该度量仅评估排序)。

但是,根据模型 A,你会通过推荐普通杯子来最大化预期利润。然而,根据模型 B,小猫杯使预期利润最大化。

在像这样的应用中,找出哪个模型能够估计更好的概率是至关重要的。

在本文中,我们将看到如何测量概率校准(视觉上和数字上)以及如何“修正”现有模型以获得更好的概率。

predict_proba 怎么了

Python 中所有最流行的机器学习库都有一个方法叫做 predict_proba : Scikit-learn(例如 LogisticRegression,SVC,RandomForest,…),XGBoost,LightGBM,CatBoost,Keras…

但是,尽管名字如此,predict_proba 并不能完全预测概率。事实上,不同的研究(尤其是这一个和这一个)表明,最流行的预测模型都没有经过校准。

一个数在 0 和 1 之间的事实不足以称之为概率!

但是,我们什么时候才能说一个数字实际上代表了一种概率呢?

想象一下,你训练了一个预测模型来预测一个病人是否会患癌症。现在假设,对于一个给定的病人,模型预测有 5%的可能性。原则上,我们应该在多个*行宇宙中观察同一个病人,看看他是否有 5%的几率患癌症。

因为我们不能走那条路,最好的替代方法是选取所有大约 5%概率的患者,并计算他们中有多少人患了癌症。如果观察到的百分比实际上接* 5%,我们说模型提供的概率是“校准的”。

当预测的概率反映真实的潜在概率时,它们被称为“校准的”。

但是如何检查你的模型是否经过校准呢?

校准曲线

评估模型校准的最简单方法是通过一个名为“校准曲线”(也称为“可靠性图表”)的图表。

这个想法是将观察结果分成概率箱。因此,属于同一仓的观测值共享相似的概率。此时,对于每个箱,校准曲线将预测*均值(即预测概率的*均值)与理论*均值(即观察到的目标变量的*均值)进行比较。

Scikit-learn 通过函数“calibration_curve”为您完成所有这些工作:

from sklearn.calibration import calibration_curvey_means, proba_means = calibration_curve(y, proba, n_bins, strategy)

您只需在以下选项中选择箱数和(可选)宁滨策略:

  • “均匀”,间隔 0-1 被分成等宽的n _ bin
  • “分位数”,箱边缘被定义为使得每个箱具有相同数量的观察值。

宁滨的策略,箱数= 7。[图片由作者提供]

出于绘图目的,我个人更喜欢“分位数”方法。事实上,“统一的”宁滨可能会产生误导,因为一些箱可能包含非常少的观测值。

Numpy 函数返回两个数组,每个数组包含目标变量的*均概率和*均值。因此,我们需要做的就是绘制它们:

import matplotlib.pyplot as pltplt.plot([0, 1], [0, 1], linestyle = '--', label = 'Perfect calibration')
plt.plot(proba_means, y_means)

假设你的模型有很好的精度,校准曲线会单调递增。但这并不意味着模型校准良好。的确,只有当校准曲线非常接**分线(即灰色虚线)时,您的模型才校准良好,因为这意味着预测概率*均接*理论概率。

让我们来看一些常见类型的校准曲线示例,它们表明您的模型存在校准错误:

校准错误的常见例子。[图片由作者提供]

最常见的校准错误类型有:

如何修复校准错误(Python 中)

假设你训练了一个分类器,它能产生精确但未经校准的概率。概率校准的想法是建立第二个模型(称为校准器),能够将它们“校正”成真实概率

注意,校准应该而不是在已经用于训练第一分类器的相同数据上执行。

通过两步方法进行概率校准。[图片由作者提供]

因此,校准在于将(未校准概率的)一维向量转换成(校准概率的)另一维向量的函数

两种方法主要用作校准品:

  • 等渗回归。一种非参数算法,将非递减自由形式线拟合到数据。该行是非递减的这一事实是最基本的,因为它尊重原始排序。
  • 逻辑回归

让我们在玩具数据集的帮助下,看看如何在 Python 中实际使用校准器:

from sklearn.datasets import make_classificationX, y = make_classification(
    n_samples = 15000, 
    n_features = 50, 
    n_informative = 30, 
    n_redundant = 20,
    weights = [.9, .1],
    random_state = 0
)X_train, X_valid, X_test = X[:5000], X[5000:10000], X[10000:]
y_train, y_valid, y_test = y[:5000], y[5000:10000], y[10000:]

首先,我们需要安装一个分类器。让我们使用随机森林(但是任何具有 predict_proba 方法的模型都可以)。

from sklearn.ensemble import RandomForestClassifierforest = RandomForestClassifier().fit(X_train, y_train)proba_valid = forest.predict_proba(X_valid)[:, 1]

然后,我们将使用分类器的输出(验证数据)来拟合校准器,并最终预测测试数据的概率。

  • 等渗回归:
from sklearn.isotonic import IsotonicRegressioniso_reg = IsotonicRegression(y_min = 0, y_max = 1, out_of_bounds = 'clip').fit(proba_valid, y_valid)proba_test_forest_isoreg = iso_reg.predict(forest.predict_proba(X_test)[:, 1])
  • 逻辑回归:
from sklearn.linear_model import LogisticRegressionlog_reg = LogisticRegression().fit(proba_valid.reshape(-1, 1), y_valid)proba_test_forest_logreg = log_reg.predict_proba(forest.predict_proba(X_test)[:, 1].reshape(-1, 1))[:, 1]

此时,我们有三个预测概率的选项:

  1. *原随机森林,
  2. 随机森林+保序回归,
  3. 随机森林+逻辑回归。

但是我们如何评估哪一个是最校准的呢?

量化误差

每个人都喜欢情节。但是除了校准图之外,我们需要一种定量的方法来测量(误)校准。最常用的指标称为预期校准误差。它回答了这个问题:

*均来说,我们的预测概率离真实概率有多远?

让我们以一个分类器为例:

单仓校准。[图片由作者提供]

很容易定义单个库的校准误差:它是预测概率的*均值和同一库内阳性分数的绝对差值

如果你仔细想想,这是非常直观的。取一个 bin,假设其预测概率的*均值为 25%。因此,我们预计该仓中的阳性部分约等于 25%。该值离 25%越远,该容器的校准越差。

因此,预期校准误差(ECE)是单个箱校准误差的加权*均值,其中每个箱与其包含的观测值数量成比例加权:

预期校准误差。[图片由作者提供]

其中 b 标识一个箱,并且 B 是箱的数量。请注意,分母只是样本总数。

但是这个公式给我们留下了定义箱数的问题。为了找到尽可能中性的度量,我建议根据弗里德曼-迪康尼斯规则 (这是为找到使直方图尽可能接*理论概率分布的箱数而设计的统计规则)来设置箱数。

在 Python 中使用 Freedman-Diaconis 规则极其简单,因为它已经在 numpy 的 histogram 函数中实现了(将字符串“fd”传递给参数“bins”就足够了)。

以下是预期校准误差的 Python 实现,默认采用 Freedman-Diaconis 规则:

def **expected_calibration_error**(y, proba, bins = 'fd'): import numpy as np bin_count, bin_edges = np.histogram(proba, bins = bins)
  n_bins = len(bin_count) bin_edges[0] -= 1e-8 # because left edge is not included
  bin_id = np.digitize(proba, bin_edges, right = True) - 1 bin_ysum = np.bincount(bin_id, weights = y, minlength = n_bins)
  bin_probasum = np.bincount(bin_id, weights = proba, minlength = n_bins) bin_ymean = np.divide(bin_ysum, bin_count, out = np.zeros(n_bins), where = bin_count > 0)
  bin_probamean = np.divide(bin_probasum, bin_count, out = np.zeros(n_bins), where = bin_count > 0) ece = np.abs((bin_probamean - bin_ymean) * bin_count).sum() / len(proba) return ece

现在我们有了一个校准的度量标准,让我们比较一下上面获得的三个模型的校准(测试集上的**):**

模型的预期校准误差。[图片由作者提供]

在这种情况下,就校准而言,保序回归提供了最好的结果,*均距离真实概率只有 1.2%。如果你考虑到普通随机森林的 ECE 是 7%,这是一个巨大的进步。

参考

如果你想深化概率校准的主题,我推荐一些有趣的论文(本文基于这些论文):

  • Caruana 和 Niculescu-Mizil 于 2005 年在监督学习下预测好的概率。
  • 【关于现代神经网络的标定】郭等 (2017)。
  • nae ini 等人使用贝叶斯宁滨 (2015)获得校准良好的概率。

感谢您的阅读!我希望这篇文章对你有用。

我感谢反馈和建设性的批评。如果你想谈论这篇文章或其他相关话题,你可以发短信给我我的 Linkedin 联系人。

Pytorch Conv2d 砝码说明

原文:https://towardsdatascience.com/pytorch-conv2d-weights-explained-ff7f68f652eb?source=collection_archive---------2-----------------------

作者图片

了解重量尺寸、可视化、参数数量和臭名昭著的尺寸不匹配

在我使用 Pytorch 的过程中,我发现的最常见的问题之一是在向我的模型上传权重时出现尺寸不匹配错误。如您所知,Pytorch 在您保存模型权重时不会保存模型的计算图(与 TensorFlow 相反)。因此,当您训练具有不同配置(不同深度、宽度、分辨率……)的多个模型时,经常会拼错权重文件,并为您的目标模型上传错误的权重。

这个拼写错误转化为 Conv2d 权重的臭名昭著的 Pytorch 错误:大小不匹配。这就是你要的:

In [11]: conv_layer = nn.Conv2d(3, 15, 5,5)
In [12]: conv_layer.load_state_dict(weights)
----------------------------------------------------------
RuntimeError    Traceback (most recent call last)[... Traceback ommited for this post... @jvgd]RuntimeError: Error(s) in loading state_dict for Conv2d:
 size mismatch for weight: copying a param with shape torch.Size([10, 3, 5, 5]) from checkpoint, the shape in current model is torch.Size([15, 3, 5, 5]).
 size mismatch for bias: copying a param with shape torch.Size([10]) from checkpoint, the shape in current model is torch.Size([15]).

听起来熟悉吗?如果是这样,我可能会有一些见解与您分享,关于 Pytorch Conv2d 权重如何以及您如何理解它们。

Conv2d

Conv2d 层可能是计算机视觉中最常用的层(至少在变形金刚出现之前是这样)。如果您曾经在 Pytorch 中实例化过该层,您可能会编写类似以下的代码:

In [5]: conv_layer = nn.Conv2d(in_channels=3, out_channels=10, kernel_size=5)

你和我通常会使用该层,而不会过多地检查它,但既然我们在这里弄脏了我们的手,让我们看看引擎盖下。如果继续检查层权重,您可以检查权重尺寸:

In [6]: conv_layer.weight.shape
Out[6]: torch.Size([10, 3, 5, 5])

那么这个怎么解读呢?

为什么权重张量有 4 个维度?不应该是二维张量,因为它是二维*面上的卷积。好吧,让我们先把它形象化,然后再分析它。

作者图片

如果你给这个卷积层输入一个图像[H,W,3],你会得到一个输出特征图[H ',W ',10]。这个操作的卷积层的权重可以如上图所示。

在图中可以看到 5×5 核是如何与来自输入图像的所有 3 个通道(R、G、B)进行卷积的。从这个意义上来说,我们需要 5×5 内核来为每个输入通道设置权重。这自然转化为形状张量[3,5,5]。

在输出端,我们设置了 10 个通道的输出特性图。这意味着,对于每个卷积步骤,我们都希望得到[1,1,10]的输出(图中的紫色张量)。这种从输入到输出通道的扩展由额外的权重支持。所以我们卷积层权重的最终张量是:3,5,5,10。

那么,为什么代码片段中的权重输出形状具有[10,3,5,5]的形状呢?公*的问题。这是由于实现问题。您已经知道 Pytorch 采用渠道优先的方法。这意味着为了处理一个输入图像,你需要把它转换成一个张量[C,H,W]。这对我们来说并不自然,但有助于实现。因此,当我们读取 Pytorch 卷积层的权重形状时,我们必须将其视为:

[out_ch, in_ch, k_h, k_w]

其中 k_h 和 k_w 分别是内核高度和宽度。

好的,但是卷积层不是也有偏差参数作为权重吗?是的,你是对的,让我们检查一下:

In [7]: conv_layer.bias.shape
Out[7]: torch.Size([10])

偏置项 it 只是添加到卷积输出的输出通道的相同维度的单个向量。视觉上:

作者图片

所以最后,我们考虑了所有的事情:

  • [H,W,3]的输入张量
  • 由[3,5,5,10]的张量卷积
  • 给出[H ',W ',10]的输出特征图
  • 其中层偏置[10]被添加到每个通道分量

这就是所有的重量。那么,下一个问题是:我用这一层给我的模型增加了多少参数?这个问题的答案非常简单。首先让我们用数字计算一下:

In [8]: sum(param.numel() for param in conv_layer.parameters())
Out[8]: 760

您可能已经猜到,这只是重量尺寸加上偏差的结果:

作者图片

计算每个卷积层的参数数量的自然概括是:

作者图片

看过这一切之后,我们可以回到介绍帖子的错误:

RuntimeError: Error(s) in loading state_dict for Conv2d:
 size mismatch for weight: copying a param with shape torch.Size([10, 3, 5, 5]) from checkpoint, the shape in current model is torch.Size([15, 3, 5, 5]).
 size mismatch for bias: copying a param with shape torch.Size([10]) from checkpoint, the shape in current model is torch.Size([15]).

了解了 Pytorch Conv2d 层之后,我们已经知道错误告诉了我们什么:

  • 我们正在尝试加载卷积层的权重,该卷积层期望 3 个通道的输入图像,并且将返回 10 的具有核[5,5]的特征图
  • 然而,当前模型有一个卷积层,与前一个模型类似,它期望一个具有[5,5]内核的 3 通道输入图像,但它将返回 15 的特征图,而不是 10 的特征图

所以最后,这几乎总是由拼写错误引起的,但是如果我们要调试我们的模型,我们需要真正挖掘它们内部是如何构建的。

今天我们已经看到了 Conv2d 层,在未来,如果我能够腾出一些时间,我会写另一篇关于其他著名 Pytorch 层的帖子。

PyTorch 分发:所有你需要知道的

原文:https://towardsdatascience.com/pytorch-distributed-all-you-need-to-know-a0b6cf9301be?source=collection_archive---------25-----------------------

用 PyTorch 编写分布式应用程序:一个真实的例子

图片来自 Pixabay

深度神经网络(DNNs)一直是机器学习领域大多数最新进展背后的主要力量。dnn 改变了我们解决各种使用案例挑战的方式,从图像分类到语言翻译,从内容推荐到药物研发。

最*的进步主要是由于我们处理的数据量,这是保持深度学习引擎运行的燃料。因此,将模型训练扩展到更多计算资源的需求比以往任何时候都高。

RANZCR 剪辑挑战赛—图片由作者提供

在之前的文章中,我们关注于基础知识,并实现了一个简单的示例来巩固基础。在这个故事中,我们编写了一个 PyTorch 分布式应用程序,它解决了一个真实世界的用例:我们将通过接管 RANZCR CLiP Kaggle 挑战来扩展我们的限制。

学习率是为那些对 AI 和 MLOps 的世界感到好奇的人准备的时事通讯。你会在每周五收到我关于最新人工智能新闻和文章的更新和想法。在这里订阅!

关于比赛

作为一个图像分类问题,RANZCR CLiP 竞赛是挑战我们分布式深度学习技能的完美候选。

在这个比赛中,我们应该在胸部 x 光片上检测导管和线的存在和位置。我们有 40,000 张图像可供处理,超过 12GB 的数据可用于了解试管放置不当的情况。

如需了解更多信息,如评估指标,请访问竞赛的页面。此外,您可以在 GitHub 上找到以下示例的代码。使用feature-group-shuffle-split分支跟随。

使用 PyTorch 进行分布式培训

PyTorch 提供了几个工具来帮助我们扩展培训过程。本文将让您从分布式数据并行开始,这是分布式深度学习应用程序中最常见的方法。

这个概念很简单:

  1. 在每个 GPU 上复制模型
  2. 分割数据集并使模型适合不同的子集
  3. 在每次迭代中交流梯度,以保持模型同步

让我们一步一步地通过 PyTorch 代码来实现这个算法。

主要功能

main函数标志着我们脚本的开始。它的主要目标是解析我们传递的参数并产生不同的进程。

我们感兴趣的是第 59 行。在这一行中,multiprocessing模块产生了许多进程。多少?尽可能多的 GPU,我们已经声明我们想要使用(当然,我们可以使用)。但是每个过程中运行的是什么呢?答案就是这个train函数里面的东西。最后,它将参数(args)传递给每个进程。

训练功能

train 函数完成了大部分工作。它初始化过程组,创建模型,开始训练,在每个时期后评估模型,最后保存模型。

请记住,这个函数运行在每个 GPU 上。首先,它在第 33 行初始化进程组,因此每个进程将通过一个主进程进行协调。以下是setup功能的内容。

然后,在第 46 行,我们将模型传递给DistributedDataParallel类构造函数,它在向后传递中透明地同步渐变。这意味着我们可以继续实施我们的培训循环,而无需任何更改!

然而,有两点需要注意:(I)如何创建数据加载器,以及(ii)如何检查模型。

为了确保模型副本在它们专有的原始数据集的子集上工作,我们需要使用 PyTorch 提供的[DistributedSampler](https://pytorch.org/docs/stable/data.html#torch.utils.data.distributed.DistributedSampler)。因此,让我们看看 like 58 上的create_loaders功能是什么样子的。

最后,我们应该额外注意如何在每个时期后检查我们的模型。为了确保我们的模型保持同步,我们应该遵循以下步骤:

  1. 保存驻留在第一个 GPU 上的模型
  2. 在继续训练之前,将保存的模型加载到其他 GPU 上

然而,当我们试图保存位于第一个 GPU 上的模型时,运行在其他 GPU 上的进程继续执行。因此,假设保存模型有一些延迟,其他过程可能加载不同的或过时的模型。

为了缓解这个问题,我们添加了一个对dist.barrier()函数的调用。现在,直到每个进程都到达这个函数,执行才停止。这样,我们可以确保在第一个流程保存模型后,其他流程会加载该模型。

我们准备好了;我们需要做的就是运行main函数!不要忘记;你可以在 GitHub 上找到代码和任何其他实用函数的功能。

结论

深度神经网络(DNNs)一直是机器学习领域大多数最新进展背后的主要力量。像这样的突破主要是由于我们可以处理的数据量,这增加了将训练过程扩展到更多计算资源的需求。

在这个故事中,我们研究了如何使用 PyTorch 来解决一个真实世界的用例,以分发培训过程。

关于作者

我的名字是迪米特里斯·波罗普洛斯,我是一名为阿里克托工作的机器学习工程师。我曾为欧洲委员会、欧盟统计局、国际货币基金组织、欧洲央行、经合组织和宜家等主要客户设计和实施过人工智能和软件解决方案。

如果你有兴趣阅读更多关于机器学习、深度学习、数据科学和数据操作的帖子,请关注我的 Medium 、 LinkedIn 或 Twitter 上的 @james2pl 。此外,请访问我的网站上的资源页面,这里有很多好书和顶级课程,开始构建您自己的数据科学课程吧!

停止在一个 GPU 上训练模型

原文:https://towardsdatascience.com/pytorch-distributed-on-kubernetes-71ed8b50a7ee?source=collection_archive---------22-----------------------

今天,没有什么可以阻止你在多个 GPU 上扩展你的深度学习训练过程

科学高清照片在 Unsplash

到目前为止,深度学习需要大量数据。最*的进步主要是由于我们处理的数据量,这是保持引擎运转的燃料。因此,扩大模型培训流程的需求比以往任何时候都高。

与此同时,DevOps 领域也越来越受欢迎。Kubernetes 无处不在;单一的遗留系统正在分解成更容易维护的更小的微服务。如果出现问题,这些服务会被视为一次性代码,可以被立即删除和恢复。

我们如何将所有部分整合在一起,并将训练扩展到更多的计算资源?我们如何以自动化的方式实现这一点?可能比你想象的要简单!

那么,我们如何将所有部分整合在一起,并将训练扩展到更多的计算资源?我们如何以自动化的方式实现这一点?

这个故事比前两个故事更进了一步,在前两个故事中,我们讨论了基础,然后将我们的知识应用到现实世界的 Kaggle 比赛中。它为 Kubernetes 世界带来了分布式培训。可能比你想象的要简单。您需要做的只是添加几行代码!

学习率是为那些对 AI 和 MLOps 的世界感到好奇的人准备的时事通讯。你会在每周五收到我关于最新人工智能新闻和文章的更新和想法。在这里订阅!

PyTorch 操作员

让我们开门见山; PyTorch 操作员。PyTorch 操作符是PyTorchJob自定义资源定义的实现。使用这个定制资源,用户可以像 Kubernetes 中的其他内置资源一样创建和管理 PyTorch 作业(例如,部署和 pods)。

我们将从上次时间停止的地方继续,并将我们的解决方案转换为 RANZCR CLiP Kaggle 挑战,作为PyTorchJob操作运行。正如您将看到的,这使事情变得容易多了!你可以跟随你在这个报告中找到的代码。使用feature-pytorch-operator分支。

代码

所以,事不宜迟,让我们从主函数开始。

现在,这个函数在每个进程中都运行。PyTorch 操作员负责将代码分发给不同的pods。它还负责通过主流程进行流程协调。

事实上,您需要做的所有不同的事情就是在第 50 行初始化流程组,并在第 65 行将您的模型包装在一个 DistributedDataParallel 类中。然后,在第 70 行,你开始你熟悉的训练循环。所以,我们来看看train函数里面有什么。

如果您以前曾经编写过 PyTorch 代码,您不会发现这里有任何不同之处。还是老一套的训练程序。仅此而已!然而,你仍然应该注意如何使用DistributedSampler分割数据集,就像我们在上一篇文章中看到的那样。

我会让你参考 GitHub 来看看每一个实用函数是做什么的,但是我们在这里已经涵盖了基础知识!

配置

最后一部分是配置。为了开始我们在 Kubernetes 上的培训过程,我们需要将我们的应用程序容器化,并编写几行 YAML 代码。

要将我们的代码转换成容器映像,我们需要一个 Dockerfile。事实证明,这非常简单:复制您的代码并运行包含main函数的文件。

类似地,YAML 配置也相当简单。

我们指定想要启动一个带有一个主节点和一个工作节点的PyTorchJob定制资源。正如你所看到的,在这个例子中,我们有两个可用的 GPU。我们分配一个给主人,一个给工人。如果你有更多的 GPU,你可以提高副本的数量。最后,您需要指定保存您的代码和您想要传递的任何参数的容器映像。

恭喜你!您需要做的就是应用配置文件:

kubectl apply -f torch_operator.yaml

怎么实验?

为了开始使用 PyTorch 操作符,我们需要 Kubeflow,这是一个开源项目,致力于使 ML 项目的部署更加简单、可移植和可伸缩。来自文档:

kube flow 项目致力于使在 Kubernetes 上部署机器学习(ML)工作流变得简单、可移植和可扩展。我们的目标不是重新创建其他服务,而是提供一种简单的方法来将 ML 的最佳开源系统部署到不同的基础设施上。无论你在哪里运行 Kubernetes,你都应该能够运行 Kubeflow。

MiniKF

但是我们如何从 Kubeflow 开始呢?我们需要 Kubernetes 集群吗?我们应该自己部署整个系统吗?我的意思是,你看过库伯弗洛的清单回购吗?

不要慌;最后,我们需要用 Kubeflow 做实验的只是一个 GCP 或 AWS 账户!我们将使用 MiniKF。MiniKF 是一个单节点 Kubeflow 实例,预装了许多优秀的特性。具体来说:

  • Kale : 用于 Kubeflow 的编排和工作流工具,使您能够从笔记本电脑开始运行完整的数据科学工作流
  • Arrikto Rok**:**一个数据版本化系统,支持可再现性、缓存、模型血统等等。

因此,要在 GCP 上安装 Kubeflow,请遵循我在下面提供的指南:

或者,如果您喜欢 AWS:

结论

深度神经网络(DNNs)一直是机器学习领域大多数最新进展背后的主要力量。像这样的突破主要是由于我们可以处理的数据量,这增加了将训练过程扩展到更多计算资源的需求。

与此同时,DevOps 领域也越来越受欢迎。Kubernetes 无处不在;单一的遗留系统正在分解成更容易维护的更小的微服务。

我们如何将两个世界融合在一起?在这个故事中,我们研究了如何使用 MiniKF 上运行的 PyTorch 操作符来解决一个真实的用例。

关于作者

我叫迪米特里斯·波罗普洛斯,我是一名为阿里克托工作的机器学习工程师。我曾为欧洲委员会、欧盟统计局、国际货币基金组织、欧洲央行、经合组织和宜家等主要客户设计和实施过人工智能和软件解决方案。

如果你有兴趣阅读更多关于机器学习、深度学习、数据科学和数据运算的帖子,请关注我的 Medium 、 LinkedIn 或 Twitter 上的 @james2pl 。此外,请访问我的网站上的资源页面,这里有很多好书和顶级课程,开始构建您自己的数据科学课程吧!

PyTorch ELMo,从零开始训练

原文:https://towardsdatascience.com/pytorch-elmo-844d2391a0b2?source=collection_archive---------8-----------------------

使用 AllenNLP 在您自己的语料库上学习强大的上下文单词嵌入

照片由雷诺·莱蒂恩在 Unsplash 上拍摄

Eembeddings fromLanguageModel(ELMo)是一种功能强大的上下文嵌入方法,可在广泛的自然语言处理任务中找到应用。ELMo 和其他人一起开创了在 NLP 中预先训练上下文单词嵌入的趋势。该技术保持简单和直观,允许其自身容易地添加到现有模型中。

在本文中,我们将讨论如何使用我们自己的文本语料库从头开始训练 ELMo 嵌入,并解释如何在幕后工作。我们将使用 AllenNLP ,这是一个基于 PyTorch 的 NLP 框架,它提供了许多现成的最新模型。如果您只对使用预先训练的 ELMo 嵌入感兴趣,请随意跳到最后一节— 在下游任务 中使用 ELMo。

目录

  1. 从零开始训练埃尔莫
  2. 理解实施
  3. 在下游任务中使用 ELMo

从头开始训练 ELMo

我知道你等不及了,所以让我们开始训练吧。在下一节中,我们将逐步完成培训,了解 ELMo 是如何在 AllenNLP 中实施的。

属国

首先,我们来安装[allennlp-models](https://github.com/allenai/allennlp-models)。这个包包含了在 AllenNLP 框架中实现的所有奇特的模型。安装这个包应该也能找到 PyTorch 和 AllenNLP 所需的正确版本。

pip install allennlp-models=v2.0.1

文集

接下来,我们获取用于训练的语料库数据。出于演示目的,我们使用原始语料库—10 亿字语言模型基准。你也可以使用任何你选择的语料库。

wget [http://www.statmt.org/lm-benchmark/1-billion-word-language-modeling-benchmark-r13output.tar.gz](http://www.statmt.org/lm-benchmark/1-billion-word-language-modeling-benchmark-r13output.tar.gz)tar -xzf [1-billion-word-language-modeling-benchmark-r13output.tar.gz](http://www.statmt.org/lm-benchmark/1-billion-word-language-modeling-benchmark-r13output.tar.gz)export BIDIRECTIONAL_LM_DATA_PATH=$PWD'/1-billion-word-language-modeling-benchmark-r13output'export BIDIRECTIONAL_LM_TRAIN_PATH=$BIDIRECTIONAL_LM_DATA_PATH'/training-monolingual.tokenized.shuffled/*'

词汇

出于索引和性能的目的,AllenNLP 需要预处理的Vocabulary,它从一个包含tokens.txt的目录中读取,该目录包含每行上的所有唯一标记。(Vocabulary的内部工作实际上要复杂一点,但我们假设这是为了简单起见。)

在这里,我们下载预处理词汇。如果你用的是自己的语料库,看一下tokens.txt了解一下它的格式。请特别注意前三个标记— </S><S>@@UNKNOWN@@。它们对于 ELMo 了解词汇之外的句子和单词的界限至关重要。

pip install awscli mkdir vocabulary 
export BIDIRECTIONAL_LM_VOCAB_PATH=$PWD'/vocabulary' 
cd $BIDIRECTIONAL_LM_VOCAB_PATH aws --no-sign-request s3 cp s3://allennlp/models/elmo/vocab-2016-09-10.txt . cat vocab-2016-09-10.txt | sed 's/<UNK>/@@UNKNOWN@@/' > tokens.txt
rm vocab-2016-09-10.txt echo '*labels\n*tags' > non_padded_namespaces.txt

培养

我们已经到了从头开始训练 ELMo 的最后一步。在这一步中,我们使用预先组合的配置文件开始培训。

wget [https://raw.githubusercontent.com/allenai/allennlp-models/main/training_config/lm/bidirectional_language_model.jsonnet](https://raw.githubusercontent.com/allenai/allennlp-models/main/training_config/lm/bidirectional_language_model.jsonnet)allennlp train bidirectional_language_model.jsonnet --serialization-dir output_dir/

不满意?

以上步骤大量借鉴了官方 AllenNLP HowTo 。如果您想要调整底层架构或者想要将自己的语料库处理成想要的格式,这可能看起来很不清楚。在提供惊人的简单性的同时,AllenNLP 扫除了大量关于数据和架构的潜在假设。

在您等待培训结束时,我们将从 AllenNLP 框架的角度来看看 ELMo 是如何工作的。

了解实现

为了理解 ELMo 架构以及它是如何实现的,我们仔细看一下bidirectional_language_model.jsonnet,它本质上是一个配置文件,在一个非常高的层次上指定了一切。它允许我们轻松地定义如何处理数据(令牌化和嵌入)以及架构是什么样子。

注意:在 JSON 对象中,我们看到许多带有键"type”的对象。这是 AllenNLP 注册自定义类的语法。通过使用它的 register decorator ,我们向 AllenNLP 公开了我们的类,使我们能够在我们的配置中使用它们。

数据集阅读器

将自然语言连接到自然数

简单语言建模数据集阅读器

我们关心的第一件事是数据如何从我们磁盘上的自然语言文本流入模型。数据首先由一个Tokenizer标记,在空格处分割句子。然后,通过TokenIndexer将单个令牌转换成数字。

在许多情况下,将单词转换成词汇表中它们各自的 id 就足够了。因为我们使用字符级 CNN 来捕获子词信息,所以我们需要额外按每个字符转换单词。

模型和数据集读取器之间的界限在于内部状态是否可以学习。更具体地说,从标记到索引号的转换是一个固定的过程,通常依赖于查找表,因此它属于数据集读取器。

模型—嵌入器

将索引连接到非文本化嵌入

ELMo 架构概述

然而,将这些索引号转换成非文本化的嵌入是一个可学习的过程,与技术无关(例如,嵌入表、字符卷积等)。)

注意token_embedders如何与token_indexers共享相同的密钥。这允许模型在内部与数据集读取器连接。

在 ELMo 中,我们只使用字符级的无上下文嵌入。因此,单词的索引被empty嵌入器丢弃,而字符的索引被送入character_encoding嵌入器。

字符卷积

让我们仔细看看字符嵌入器。在内部,它用一个学习嵌入矩阵将字符索引转换成字符嵌入。然后,对于每个单词,它使用 CNN 通过卷积各个单词的字符嵌入来学习其单词嵌入。这样做给了我们一个包含子词信息的无语境的单词嵌入。

无语境的单词嵌入已经存在很长时间了。像 word2vec 、 GloVe 这样的嵌入一直是 NLP 中的标准特性。这种嵌入的一个长期存在的问题是上下文的丢失。在非语篇化嵌入中,“bank”在“river bank”和“bank account”中的嵌入是相同的。然而,我们知道这些词有完全不同的意思。接下来,我们来看看 ELMo 如何用它的上下文器处理这个问题。

模型——语境化者

通过查看句子轻推单词嵌入

biLSTM 语境化器

在最初的论文中,Peters 等人使用了一个 biLSTM 作为上下文组织器。前进的 LSTM 从前面的单词中捕捉上下文;落后的 LSTM 从后面的单词中捕捉上下文。

例如,为了将一个嵌入的 xₜ语境化,向前的 LSTM 看着 x₁,…,xₜ₋₁并创造一个隐藏的状态;落后的 LSTM 看着 xₜ₊₁,…,xₙ,创造了另一个隐藏的国家。通过连接两个隐藏状态,我们获得了考虑前向上下文和后向上下文的上下文化嵌入。

在随后的研究中,Peters 等人发现 LSTM 在下游任务中享有更高的准确性,而 transformer 在性能略有下降的情况下享有更快的推理速度。因此,实现使用了基于转换器的上下文化器。直观上,它实现了 LSTM 试图实现的相同目标——将句子上下文添加到每个单词嵌入中。

这就完成了 ELMo 架构,也就是说,我们在下游任务中使用由此产生的上下文嵌入。我们要讨论的最后一个部分是它的训练目标——双向语言模型丢失。

模型—损失

用以前的话来预测 ____

给定 n 个标记的序列,(t₁,t₂,…,tₙ),正向语言模型通过对给定历史(t₁,…,tₖ₋₁)的标记 tₖ的概率建模来计算序列的概率

向前和向后 LM 从到深层语境化的词语表征

同样,我们可以推导出反向语言模型。我们最终的双向语言模型目标只是我们语料库中所有句子的两个对数概率之和。

在 AllenNLP 中,损耗被嵌入到language_model模型中,因此我们只需在配置中将参数bidirectional设置为true。我们还可以通过设置num_samples使用采样的 softmax 损耗来加快速度。

把所有东西放在一起

咻!这就是 ELMo 架构及其在 AllenNLP 中的实现。当你在阅读的时候,你的模型可能已经完成了训练(好吧,可能没有)。在下一节中,我们将使用经过训练的 ELMo 来嵌入一些文本!

在下游任务中使用 ELMo

现在您已经完成了训练,您应该能够在output_dir/中看到每个时期的指标,在这里您还可以找到训练好的模型权重model.tar.gz。或者,您可以抓取一个预训练的权重并将其集成到您的模型中。

要将 ELMo 集成到您的 AllenNLP 模型配置中,我们只需将其用作另一个token_embedder:

使用 ELMo 从零开始训练

使用预先训练的 ELMo

不想使用配置文件?也可以用 Python 直接调用 ELMo:

你已经坚持到最后了!虽然 AllenNLP 的语法可能仍然让您感到困惑,但我们确实看到了它在从零开始用 0 行代码训练一个基于 PyTorch 的 ELMo 方面的威力!它还包括许多开箱即用的惊人的 NLP 模型。如果你有兴趣使用它们,看看它的官方指南和文档。

参考

  1. 深度语境化的文字表述 首创 ELMo 架构。它获得了当时许多 NLP 任务的最新技术,而该架构仍然可以很容易地集成到其他模型中。
  2. 剖析上下文单词嵌入:架构与表示 进一步研究了 ELMo 架构。它试图说明预训练的语境嵌入是如何以及为什么如此有效。在这项研究中,通过使用不同的上下文环境(LSTM、变形金刚、门控 CNN)进行实验,发现了速度和准确性之间的折衷。报告了网络深度和捕获的信息之间的关系,其中较低层捕获句法特征,而较高层捕获语义特征。
  3. 长短期记忆 首先被引入以捕获序列中的长期依赖性,并解决传统 RNNs 中的消失梯度问题。ELMo 使用 LSTM 生成隐藏状态的方式来将单词嵌入上下文化。关于 LSTM 更复杂的解释,请查看科拉的博客。
  4. 关注是你所需要的 将变形金刚引入世界。它使用自我关注来捕获令牌之间的依赖关系,使其大规模并行化。HavardNLP 在带注释的 transformer 中深入探讨了它的实现,而 Jay Alammar 在他的带插图的 transformer 中提供了一个很好的直觉。
  5. AllenNLP:深度语义自然语言处理*台 为我们带来了 AllenNLP 框架。它提供了许多处理文本数据的常见抽象,并智能地处理诸如批处理和填充之类的事情。我个人认为它非常有用,因为它收集了大量的 NLP 模型。欲了解更多信息,请查看其官方指南、文档,以及开源模型的 GitHub repo。

PyTorch 几何图形嵌入

原文:https://towardsdatascience.com/pytorch-geometric-graph-embedding-da71d614c3a?source=collection_archive---------9-----------------------

使用 PyTorch 几何模块中的 SAGEConv 嵌入图形

图形表示学习/嵌入通常是用于将图形数据结构转换为更结构化的向量形式的过程的术语。这通过提供更易管理的固定长度向量来实现下游分析。理想情况下,除了节点特征之外,这些向量还应包含图结构(拓扑)信息。我们使用图形神经网络(CNN)来执行这种转换。要对 GNNs 有一个基本的高级概念,您可以看一下下面的文章。

在本文中,我将讨论 GraphSAGE 架构,它是消息传递神经网络(MPNN)的一种变体。MPNN 是一个描述 gnn 如何有效实现的奇特术语。

广义 GNN 表示

任何 MPNN 都可以用两个函数集合组合来正式表示。

作者引用的等式(https://arxiv.org/pdf/1810.00826.pdf)

聚合函数控制如何为给定节点收集或聚合邻居信息。

作者引用的等式(https://arxiv.org/pdf/1810.00826.pdf)

combine 函数控制节点本身的信息如何与来自邻居的信息相结合。

图表法

GraphSAGE 代表图形样本和集合。让我们首先定义 GraphSAGE 的聚合和组合函数。

合并 —使用相邻要素的元素均值

聚合 —将聚合的特征与当前节点特征连接起来

图形解释

GraphSAGE 层可以直观地表示如下。对于给定的节点 v,我们使用*均聚合来聚合所有邻居。结果与节点 v 的特征连接在一起,并通过多层感知(MLP)以及类似 RELU 的非线性反馈。

作者图片

人们可以很容易地使用 PyTorch geometric 这样的框架来使用 GraphSAGE。在我们开始之前,让我们建立一个用例来继续。嵌入图形的一个主要重要性是可视化。因此,让我们用 GraphSAGE 构建一个 GNN 来可视化 Cora 数据集。请注意,这里我使用的是 PyTorch 几何知识库中提供的例子,没有什么技巧。

图表用法细节

GraphSAGE 的核心思想是采样策略。这使得该架构能够扩展到非常大规模的应用。采样意味着,在每一层,仅使用最多 K 个邻居。像往常一样,我们必须使用一个顺序不变的聚合器,如均值、最大值、最小值等。

损失函数

在图形嵌入中,我们以无人监督的方式操作。因此,我们使用图的拓扑结构来定义损失。

来自 GraphSAGE 论文:【https://arxiv.org/pdf/1706.02216.pdf

这里 Zu 展示了节点 u 的最终层输出。 Zvn 表示负采样节点。简单来说,等式的第二项表示负(节点 u 和任意随机节点 v )的求反点积应该最大化。换句话说,随机节点的余弦距离应该更远。对于节点 v 来说,第一项说的是另外一种情况,这是一个我们需要嵌入得更靠* u 的节点。这个 v 被称为正节点,通常使用从 u 开始的随机行走来获得。 Evn~Pn(v) 表示负节点取自负采样方法。在实际实现中,我们将直接邻居作为正样本,随机节点作为负样本。

构建图嵌入网络

我们可以从导入以下 python 模块开始。

import torch
import torch.nn as nn
import torch.nn.functional as F
from torch_cluster import random_walk
from sklearn.linear_model import LogisticRegressionimport torch_geometric.transforms as T
from torch_geometric.nn import SAGEConv
from torch_geometric.datasets import Planetoid
from torch_geometric.data import NeighborSampler as
                                    RawNeighborSamplerimport umap
import matplotlib.pyplot as plt
import seaborn as sns

初始化 Cora 数据集;

dataset = 'Cora'
path = './data'
dataset = Planetoid(path, dataset, transform=T.NormalizeFeatures())
data = dataset[0]

请注意,dataset 对象是一个子图列表。对于 Cora,我们有一个,所以我们选择索引为 0 的图。

采样器组件(这里我们扩展 NeighborSampler 类的 sample 方法,用正负样本创建批次);

# For each batch and the adjacency matrix
pos_batch = random_walk(row, col, batch, 
                          walk_length=1,
                          coalesced=False)[:, 1]
# row are source nodes, col are target nodes from Adjacency matrix
# index 1 is taken as positive nodes# Random targets from whole adjacency matrix
neg_batch = torch.randint(0, self.adj_t.size(1), (batch.numel(), ),
                                  dtype=torch.long)

GNN 可以在 PyTorch 宣布如下;

class SAGE(nn.Module):
    def __init__(self, in_channels, hidden_channels, num_layers):
        super(SAGE, self).__init__()
        self.num_layers = num_layers
        self.convs = nn.ModuleList()

        for i in range(num_layers):
            in_channels = in_channels if i == 0 else hidden_channels
            self.convs.append(**SAGEConv(in_channels,
                                   hidden_channels)**) def forward(self, x, adjs):
        for i, (edge_index, _, size) in enumerate(adjs):
            x_target = x[:size[1]]  
            x = self.convs[i]((x, x_target), edge_index)
            if i != self.num_layers - 1:
                x = x.relu()
                x = F.dropout(x, p=0.5, training=self.training)
        return x def full_forward(self, x, edge_index):
        for i, conv in enumerate(self.convs):
            x = conv(x, edge_index)
            if i != self.num_layers - 1:
                x = x.relu()
                x = F.dropout(x, p=0.5, training=self.training)
        return x

注意,我们使用 PyTorch 几何框架中的 SAGEConv 层。在前向传递中,邻居采样器为我们提供要在每一层中传递的数据作为数据索引。这是一个相当复杂的模块,所以我建议读者阅读论文(第 12 页)中的 Minibatch 算法和 PyTorch Geometric 中的 NeighborSampler 模块文档。

形象化

在没有使用图形结构的情况下,下面是 UMAP 图。

作者图片

当我们使用 GraphSAGE 嵌入时,我们可以有如下更好的嵌入:

作者图片

我们可以看到,与朴素 UMAP 嵌入相比,这些嵌入要好得多,并且分离得很好。然而,这并不完美,需要更多的工作。但我希望这是一个足够好的演示来传达这个想法。😊

希望你喜欢这篇文章!

完整的代码和 Jupyter 笔记本可从这里获得。

PyTorch Ignite 教程—使用高效网络对微型图像网络进行分类

原文:https://towardsdatascience.com/pytorch-ignite-classifying-tiny-imagenet-with-efficientnet-e5b1768e5e8f?source=collection_archive---------6-----------------------

实践教程

使用 PyTorch Ignite 简化您的 PyTorch 深度学习实施的分步指南

照片由 Olga Bast 在 Unsplash 拍摄

PyTorch 是一个强大的深度学习框架,已经被科技巨头采用,如特斯拉、 OpenAI 和微软用于关键的研究和生产工作负载。

它的开源特性意味着 PyTorch 的功能也可以被公众所利用。

深度学习实现的一个问题是,代码可能会快速增长,变得重复和过于冗长。这引发了高级库的创建,以简化这些 PyTorch 代码,其中之一就是 PyTorch Ignite 。

本文就如何使用 PyTorch Ignite 来简化 PyTorch 中深度学习模型的开发提供了一个解释清楚的演练。

内容

(1)关于 PyTorch 点燃(2)分步实施(3)把东西包起来

关于 PyTorch Ignite

在 BSD 3 条款许可下使用的图像

PyTorch Ignite 是一个高级库,可以灵活透明地帮助训练和评估 PyTorch 中的神经网络。

它减少了构建深度学习模型所需的代码量,同时保持了简单性和最大程度的控制。

PyTorch 点燃代码( ) vs 纯 PyTorch 代码( ) |图片在 BSD 三条款许可下使用

上图展示了 PyTorch Ignite 将纯 PyTorch 代码压缩到更简洁的程度。

除了消除低级代码,PyTorch Ignite 还提供了对度量评估、实验管理和模型调试的实用支持。

逐步实施

本教程的演示任务是在 Tiny ImageNet 数据集上建立一个图像分类深度学习模型。

Tiny ImageNet 是著名的 ImageNet 大规模视觉识别挑战赛 (ILSVRC)中 ImageNet 数据集的子集。

数据集包含 100,000 张缩小到 64×64 彩色图像的 200 类(每类 500 张)图像。每个类有 500 幅训练图像、50 幅验证图像和 50 幅测试图像。

来自微型 ImageNet 数据集的样本图像|作者提供的图像

让我们详细介绍使用 PyTorch 和 Ignite 尽可能准确地对这些图像进行分类的步骤。

步骤 1 —初始设置

我们将使用 Google Colab ,因为它提供对 GPU 的免费访问,我们可以随时使用。请随时跟随本 完成演示的 Colab 笔记本

确保您已经将 Colab 运行时设置为 GPU 。完成后,执行以下步骤作为初始设置的一部分:

  1. 安装导入必要的 Python 库

2.**定义** GPU 对 PyTorch 的支持(即使用 CUDA )。

步骤 2 —下载微型 ImageNet 数据集

有两种方法下载微型 ImageNet 数据集,即:

对于这个项目,我使用 wget 来检索原始数据集(在一个 zip 文件中)。下载后,我们可以解压缩 zip 文件,并为提取的图像设置各自的文件夹路径。

如果操作正确,您应该会看到文件夹出现在 Colab 侧边栏上:

作者图片

*步骤 3 —设置助手功能*

我们定义辅助函数是为了让我们以后的生活更轻松。创建了两组功能,它们是:

  • 显示单个或一批样本图像

这使我们能够可视化我们正在处理的图像的随机子集。

  • 为图像数据集创建数据加载器

数据加载器的工作是从数据集生成小批量数据,让我们可以灵活地选择不同的采样策略和批量大小。

在上面的代码中,我们使用了来自 torchvision.datasetsImageFolder函数来生成数据集。为了使ImageFolder工作,训练和验证文件夹中的图像必须按照以下结构排列:

图像数据的预期文件夹结构:根/标签/文件名 |作者图像

*步骤 4-组织验证数据文件夹*

*您会注意到培训文件夹符合**步骤 3** 中ImageLoader所需的结构,但是**验证文件夹不符合。***

验证文件夹中的图像都保存在一个文件夹中,因此我们需要根据它们的标签将它们重新组织到子文件夹中。

验证文件夹包含一个val _ annotations . txt文件,该文件由六个制表符分隔的列组成:文件名、类标签和边界框的细节(x,y 坐标、高度、宽度)。

val_annotations.txt 文件中的数据|作者图片

我们提取前两列,将成对的文件名和相应的类标签保存在字典中。

每个验证图像的相应标签|按作者分类的图像

要了解每个类标签的含义,可以阅读 words.txt 文件。例如:

相应类别标签代码的标签描述符示例|作者图片

之后,我们执行文件夹路径重组:

步骤 5 —定义图像预处理转换

所有预训练的 Torchvision 模型都希望输入图像以相同的方式归一化(作为预处理要求的一部分)。

它要求这些图像为 shape (3 x H x W)的 3 通道 RGB 格式,其中 H(高)和 W(宽)至少为 224 像素

然后需要根据 (0.485,0.456,0.406)均值值和 (0.229,0.224,0.225)标准差值对像素值进行归一化。

除此之外,我们可以引入各种变换(例如,中心裁剪、随机翻转等。)来扩充图像数据集并提高模型性能。

我们将这些转换放在一个 Torchvision Compose包装器中,将它们链接在一起。

步骤 6-创建数据加载器

*我们在**步骤 3** 中描述了数据加载器的概念,并为设置数据加载器创建了一个帮助函数。是时候通过为**训练集和**验证集创建数据加载器来很好地利用这个函数了。*

我们在步骤 5 中指定转换步骤,并定义 64 的批量。这意味着 DataLoader 每次被调用时将推出 64 个图像。

步骤 7 —定义模型架构

火炬视觉模型子包 torchvision.models包含了大量预先训练好的模型供我们使用。这包括流行的架构,如 ResNet-18、VGG16、GoogLeNet 和 ResNeXt-50。

我们将为这个项目做一些不同的事情,在 Torchvision 模型的默认列表中选择一个而不是的预训练模型。特别是,我们将使用 EfficientNet

EfficientNet 是 Google 在 2019 年开发的卷积神经网络架构和缩放方法。它超越了最先进的精度,效率提高了 10 倍(即更小、更快)。

下图说明了 EfficientNet ( 红线)在准确性(在 ImageNet 上)和计算资源方面如何优于其他架构。

在 Apache 2.0 许可下使用的图像

不同版本的 EfficientNet (b0 到 b7)基于模型参数的数量而有所不同。参数数量越多,精度越高,但代价是训练时间越长。

在本教程中,我们将使用高效网络的 PyTorch 实现来建立一个高效网络-B3 架构。我选择 B3 是因为它在准确性和训练时间之间提供了一个很好的*衡。

更新的 EfficientNet v2 的 PyTorch 实现即将推出,请继续关注GitHub repo的最新更新。

步骤 8-定义损失函数、超参数和优化器

最适合图像分类任务的损失函数是分类交叉熵损失

我们将使用一组模型参数的基线值,例如学习率、时期数、日志记录频率和优化器类型。

步骤 9-实例化教练引擎

Ignite 框架的主要本质是**Engine**类,它对输入数据执行处理功能并返回输出。

当我们创建一个训练器引擎时,我们正在初始化一个类,该类将被重复调用,以根据数据加载器生成的批量数据训练模型。

PyTorch Ignite 自带内置助手函数,只需单行代码即可创建训练器引擎。对于我们的监督图像分类用例,我们利用了create_supervised_trainer函数。

这些引擎还允许我们附加有用的事件处理程序,比如监控训练的进度条。

步骤 10 —定义评估指标

用于图像分类模型评估的度量是准确度(用于我们解释模型的性能)和交叉熵损失(用于模型迭代改进)。

在将指标附加到引擎之前,您也可以创建自己的自定义指标。例如, F1 分数可以从默认精度和召回指标中通过算术方法得出:

***from** ignite.metrics **import** **Precision,** **Recall**

**precision** **=** **Precision(average=False)**
**recall** **=** **Recall(average=False)**
**F1** **=** **(precision** ***** **recall** ***** 2 **/** **(precision** **+** **recall)).mean()****F1.attach(engine,** "F1"**)***

步骤 11-实例化评估引擎

在定义评估指标之后,我们可以初始化评估器引擎来评估模型性能。评估引擎将把模型和评估指标(来自步骤 10 )作为参数。

我们为训练集定义了一个评估器引擎,为验证集定义了一个单独的评估器引擎。这是因为他们在整个模型训练过程中的角色不同。

验证评估器将用于保存基于验证指标的最佳模型,而训练评估器将只记录训练集中的指标。

步骤 12—创建事件处理程序

为了提高[**Engine**](https://pytorch.org/ignite/generated/ignite.engine.engine.Engine.html#ignite.engine.engine.Engine)的灵活性,引入了一个事件系统,以便于在训练运行的每一步进行事件互动,例如:

  • 发动机启动/完成
  • 纪元开始/完成
  • 批量迭代开始/完成

在 decorators 的帮助下,我们可以创建被称为事件处理程序的定制代码。事件处理程序是在特定事件发生时执行的函数。例如,我们可以在每次迭代(Events.ITERATION_COMPLETED)和时期(Events.EPOCH_COMPLETED)完成时记录度量。

此外,我们想要一个检查点处理程序来保存我们最好的模型(如)。pt 文件)基于验证准确度。这可以通过公共模块中的辅助方法save_best_model_by_val_score轻松完成。

在伴随每个事件的函数中,您会注意到我们使用了在前面的步骤中已经构建好的变量和引擎。

步骤 13 —设置 Tensorboard

Tensorboard 作为机器学习实验的一部分,是一个跟踪和可视化指标(如损失和准确性)的有用工具包。

PyTorch 与 Tensorboard 集成在一起,因此我们可以从创建一个 Tensorboard logger 处理程序并指定存储日志的目录开始。

初始化 Tensorboard logger 后,我们可以附加输出处理程序来指定事件和相应的度量,以保存供以后可视化。

虽然我们在这里使用 Tensorboard,但我们可以轻松地使用其他流行的日志工具,如 Weights and Biases、ClearML 和 MLflow。查看通用模块文档了解更多信息。

步骤 14-开始模型训练

我们终于到了可以开始实际模型训练的阶段了。我们通过让训练器引擎在训练集数据加载器上运行来做到这一点。

这是 Colab 中第一个训练纪元的样子:

作者图片

仅用一个纪元,B3 效率网就已经取得了令人印象深刻的验证准确度 61.1%

训练完成后,我们可以运行以下代码来获得验证集的最终评估指标:

*print(evaluator.state.metrics)*

三个时期后得到的最终准确率得分为 66.57%

步骤 15 —在 Colab 中查看 Tensorboard

我们调用一组神奇的命令来加载 Colab 笔记本中的 Tensorboard。

执行上述命令后,Colab 笔记本中将加载以下 Tensorboard 接口。这个可视化仪表板为我们提供了从训练运行中获得的指标信息。

Tensorboard 截图|图片作者

包装东西

在本教程中,我们介绍了利用 Ignite 框架的灵活性和简单性来构建 PyTorch 深度学习模型的步骤。

PyTorch Ignite 具有许多其他功能来满足更复杂的神经网络设计的需求,因此可以随意浏览文档和示例笔记本。

例如,代替先前使用的静态学习率,我们可以结合一个学习率调度器(LRScheduler)处理程序到在训练期间调整学习率值。灵活性也意味着我们可以在设置中包含其他算法,如 FastAI 的学习率查找器。

项目链接

  • GitHub 回购
  • Colab 笔记本

照片由阿齐兹·阿查基在 Unsplash 上拍摄

在你走之前

欢迎您加入我的数据科学学习之旅。跟随这个媒体页面并查看我的 GitHub 以了解实用和教育数据科学内容。同时,祝你考试顺利!

参考

  • PyTorch 点燃 GitHub
  • PyTorch 点火文档
  • efficient net-py torch GitHub

PyTorch Lightning 和 Optuna:多 GPU 超参数优化

原文:https://towardsdatascience.com/pytorch-lightning-and-optuna-multi-gpu-hyperparameter-optimisation-7b42c3c28df0?source=collection_archive---------14-----------------------

如何使用 PyTorch Lightning 快速设置用于超参数优化的多 GPU 训练

古斯塔沃·坎波斯在 Unsplash 上的照片

可能大多数阅读这篇文章的人都至少训练过一次 ML 模型,花了相当多的时间。当我在为高精度文本分类微调 RoBERTa 时,情况确实如此。根据数据和模型,利用 GPU 进行训练可以将训练时间缩短一个数量级。对于卷积神经网络(CNN)和图像或视频流数据,如果将一批数据传递给 GPU 比传递给模型花费的时间更少,这种情况就很常见。

一个脑袋好,两个更好。当不止一个 GPU 可用时就是这种情况。对我来说, PyTorch Lightning 最吸引人的特性之一是无缝的多 GPU 训练能力,这需要最少的代码修改。PyTorch Lightning 是 PyTorch 之上的一个包装器,旨在标准化 ML 模型实现的常规部分。额外的 GPU 带来的额外速度提升对于耗时的任务来说尤其方便,例如超参数调整。在这篇文章中,我想分享我使用 PyTorch Lightining 和 Optuna 的经验,这是一个用于自动超参数调优的 python 库。

数据和预处理

在本例中,我选择了 Kaggle 提供的英特尔图像分类数据集。完整的代码可以在这个笔记本里找到。数据集被分成训练和测试子集,每个子集有 14034 幅图像。数据集中有六个类:山脉、冰川、海洋、街道、建筑物和森林。以下是一些示例图像:

大多数图像是 150×150 像素,在宽度上有少量异常值。Pytorch Lightning 提供了一个基类 LightningDataModule 来下载、准备和为模型提供数据集。它定义了在训练、验证和测试期间调用的方法。我的子类是这样的:

其中transforms是从torchvision导入的。随机水*翻转仅用于训练数据的增强;ToTensor()在预处理变换中,将所有通道缩放至[0,1]间隔(除以 255)。该类还将训练数据分为训练集和验证集,其中 20%的图像属于后者。PyTorch Lightning 的另一个优点是,初始化IntelDataModule时不需要指定设备,因为一切都是由训练员稍后自动完成的。

基准模型

首先,让我们来看一个基本的 CNN。它将作为比较优化模型的基准。为了减少代码重复,让我们为我们的模型引入一个基本的构建块

这只是一个卷积层,有一个可选的批范数和一个激活函数。基准模型应该是这样的

该模型没有任何Linear层,并且是完全卷积的,这是一个很好的技巧,从 Aurelien Geron 的书“用 scikit-learn,Keras 和 Tensorflow 实践机器学习”中增加了一点 GPU 的速度。基类LightningModule的类方法..._step..._epoch_end分别定义了在训练/验证/测试步骤和时期结束时要采取的动作。这是将输出传递到感兴趣的度量、日志数量的地方。我选择不在每一步之后记录,而是在一个时期结束时记录。这也是计算指标的地方,可以节省一点计算时间。在 PyTorch Lightning 中训练模型是通过一个训练器类来完成的,就像这样简单

大多数论证都是自明的,用deterministic参数控制再现性。出于演示的目的,我为 20 个时期选择了固定的学习率 0.0001。这个基准模型在测试数据集上达到了 0.8213 的精度,这是混淆矩阵

作者图片

这个模型似乎经常混淆冰川和山脉。因为前者常常以群山为背景,所以很难指责它。总的来说,这是一个不错的结果,但可能不是最好的结果。改善的方法之一是通过超参数调谐。

多 GPU 训练和超参数优化

使用 Pytorch 的nn.ModuleDict模块可以简化超参数(包括层类型)的自动选择:

其中Residual是来自论文的全卷积模块。要优化的可调模型如下所示

在上面的代码中,..._epoch_end方法与基准模型相同,为了简洁起见被省略了。不过,还需要定义一些额外的方法..._step_end。在每个设备上评估了批次的所有部分后,调用这些方法,并在此汇总结果。backbone是将被优化的模型组件。它是通过从预定义的集合中选择超参数并将其传递给conv_block的功能创建的

每个参数的概率分布由 Optuna 控制,Optuna 是一个瑞士军刀库,用于微调 Pytorch、Tensorflow、Scikit-learn 等模型。它设置简单,易于使用。它需要一个用户定义的目标函数,接受一个trial对象,负责超参数选择。它从选定的超参数中创建一个模型,并使用它返回要优化的分数。在上面的函数create_backbone为一个模型实例创建了一个可调组件之后,它被训练,验证的准确性被用作一个客观的分数:

上述函数中的每个模型都在两个 GPU 上训练,由gpus=2accelerator='dp'参数控制。值'dp'表示所有的 GPU 都位于一台计算机上。定义objective后,运行调谐程序很简单

上式中的采样器对象决定了采样超参数的策略。TPESampler从具有获得较高分数的超参数的区域中采样更多的点,并在每次trial后更新这些区域。与 Sklearn 的GridSearchCV类似,可以使用study.best_scorestudy.best_params提取最佳参数和分数。通过优化参数,精确度提高了 1.27%,达到 0.8340:

作者图片

摘要

我当然喜欢 PyTorch Lightning,因为它有简化和标准化 ML 模型创建的崇高目标。几乎免费获得的分布式培训的无缝可扩展性尤其有用。一个明显的应用训练速度增益的地方是超参数优化,Optuna 帮助实现。虽然 Optuna 是许多这样的库之一,但它很容易为来自几乎任何框架的模型进行设置。我希望这个小例子能帮助你攀登挡在优秀和优秀模特之间的大山!

Pytorch 模型视觉解释

原文:https://towardsdatascience.com/pytorch-model-visual-interpretation-7133383bb6ae?source=collection_archive---------23-----------------------

用 Captum 解释 Pytorch 模型

来源:作者

Pytorch 是一个开源的 python 库,用于创建深度学习/机器学习模型,是继 TensorFlow 之后使用最多的库之一。它基于 Torch 库,主要用于计算机视觉和 NLP 应用。

根据我们正在开发的应用程序,我们可以使用不同的预训练 PyTorch 模型。但问题是,我们真的了解这个黑匣子里到底有什么吗?模型解释很重要,因为我们需要了解模型是如何工作的,以及我们如何改进模型。

Captum 是一个开源 python 库,用于解释 PyTorch 模型。它在模型理解和可解释性方面创造了透明性。它有助于理解数据所具有的最重要的特征,以及它如何影响模型的输出。

在本文中,我们将为 PyTorch 模型解释创建一个 Captum insights 仪表板。

让我们开始吧…

安装所需的库

我们将从使用 pip 安装 flask_compress 和 Captum 开始。下面给出的命令可以做到这一点。

!pip install flask_compress 
!pip install captum

导入所需的库

在这一步中,我们将导入创建 PyTorch 模型所需的库和用于模型解释的 Captum 仪表板。在这篇文章中,我不会讨论如何创建一个模型。

import osimport torch
import torch.nn as nn
import torchvision
import torchvision.transforms as transformsfrom captum.insights import AttributionVisualizer, Batch
from captum.insights.attr_vis.features import ImageFeature

创建模型

对于本文,我将使用一个预先训练好的模型来创建我们的模型。

def get_classes():
    classes = [
        "Plane",
        "Car",
        "Bird",
        "Cat",
        "Deer",
        "Dog",
        "Frog",
        "Horse",
        "Ship",
        "Truck",
    ]
    return classesdef get_pretrained_model():
    class Net(nn.Module):
        def __init__(self):
            super(Net, self).__init__()
            self.conv1 = nn.Conv2d(3, 6, 5)
            self.pool1 = nn.MaxPool2d(2, 2)
            self.pool2 = nn.MaxPool2d(2, 2)
            self.conv2 = nn.Conv2d(6, 16, 5)
            self.fc1 = nn.Linear(16 * 5 * 5, 120)
            self.fc2 = nn.Linear(120, 84)
            self.fc3 = nn.Linear(84, 10)
            self.relu1 = nn.ReLU()
            self.relu2 = nn.ReLU()
            self.relu3 = nn.ReLU()
            self.relu4 = nn.ReLU()def forward(self, x):
            x = self.pool1(self.relu1(self.conv1(x)))
            x = self.pool2(self.relu2(self.conv2(x)))
            x = x.view(-1, 16 * 5 * 5)
            x = self.relu3(self.fc1(x))
            x = self.relu4(self.fc2(x))
            x = self.fc3(x)
            return xnet = Net()
    net.load_state_dict(torch.load("cifar_torchvision.pt"))
    return netdef baseline_func(input):
    return input * 0def formatted_data_iter():
    dataset = torchvision.datasets.CIFAR10(
        root="data/test", train=False, download=True, transform=transforms.ToTensor()
    )
    dataloader = iter(
        torch.utils.data.DataLoader(dataset, batch_size=4, shuffle=False, num_workers=2)
    )
    while True:
        images, labels = next(dataloader)
        yield Batch(inputs=images, labels=labels)

创建模型解释仪表板

这是最后一步,将创建用于模型理解和解释的仪表板。

normalize = transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
model = get_pretrained_model()
visualizer = AttributionVisualizer(
    models=[model],
    score_func=lambda o: torch.nn.functional.softmax(o, 1),
    classes=get_classes(),
    features=[
        ImageFeature(
            "Photo",
            baseline_transforms=[baseline_func],
            input_transforms=[normalize],
        )
    ],
    dataset=formatted_data_iter(),
)#Visualize Model
visualizer.render()

仪表板(来源:作者)

在这里,您可以清楚地看到模型是如何工作的,您可以选择您想要解释的类,类似地选择归因方法和参数。它清楚地显示了归属量级、标签和预测。

继续尝试使用不同的数据集,并创建漂亮的 Captum 仪表板来解释模型。如果您发现任何困难,请在回复部分告诉我。

本文是与 Piyush Ingale 合作完成的。

在你走之前

感谢 的阅读!如果你想与我取得联系,请随时通过 hmix13@gmail.com 联系我或我的 LinkedIn 个人资料 。可以查看我的Github简介针对不同的数据科学项目和包教程。还有,随意探索 我的简介 ,阅读我写过的与数据科学相关的不同文章。

PyTorch + SHAP =可解释的卷积神经网络

原文:https://towardsdatascience.com/pytorch-shap-explainable-convolutional-neural-networks-ece5f04c374f?source=collection_archive---------16-----------------------

学习如何用 PyTorch 和 SHAP 解释卷积神经网络的预测

由 Fernand De Canne 在 Unsplash 上拍摄的照片

黑盒模型已经成为过去——即使有了深度学习。你可以用 SHAP 来解释深度学习模型的预测,而且只需要几行代码。今天,您将在著名的 MNIST 数据集上了解如何操作。

卷积神经网络可能很难理解。网络从图像中学习最佳特征提取器(内核)。这些特征对于检测任何帮助网络正确分类图像的模式是有用的。

你的大脑没什么不同。它还使用一系列模式来识别你面前的物体。例如,是什么使一个数零成为零?它是一个圆形到椭圆形的轮廓形状,里面什么也没有。这是卷积层背后的内核试图学习的一般模式。

如果你想直观地表达你的模型的解释,只需看看 SHAP(SHapely Additive explaining)——一种解释任何机器学习模型输出的博弈论方法。你可以参考这篇文章获得完整的初学者指南。

这篇文章的结构如下:

  • 定义模型架构
  • 训练模型
  • 解释模型
  • 结论

这里可以下载相应的笔记本。

定义模型架构

您将使用 PyTorch 训练一个简单的手写数字分类器。这是深度学习的首选 Python 库,无论是在研究还是商业领域。如果你之前没有用过 PyTorch 但是有一些 Python 的经验,会觉得很自然。

在定义模型架构之前,您必须导入几个库。这些大多和 PyTorch 有关,后面会用到numpyshap:

模型架构很简单,借鉴了官方文档。请随意声明您自己的架构,但是这个架构已经足够满足我们的需求了:

下一节将向您展示如何训练模型。

训练模型

让我们从声明几个变量开始:

  • batch_size–一次向模型显示多少幅图像
  • num_epochs–通过训练数据集的完整次数
  • device–指定训练是在 CPU 还是 GPU 上完成的。如果你没有兼容 CUDA 的 GPU,用cpu代替cuda:0

接下来,您将声明几个函数— train()test()。这些将用于在单独的子集上训练和评估模型,并打印中间结果。

完整的代码片段如下所示:

接下来,您可以使用torchvision.datasets模块下载数据集。然后,数据集被加载和转换(转换为张量和归一化)并分批组织:

现在你已经为模特训练做好了一切准备。以下是如何实例化该模型并针对之前声明的历元数对其进行训练:

您将看到在培训阶段打印出来的中间结果。这是它们在我的机器上的样子:

图 1 —使用 PyTorch 进行模型训练—中间结果(图片由作者提供)

请记住,在您的机器上,实际值可能略有不同,但是您应该在测试集上达到 95%以上的准确度。

下一步——与 SHAP 一起演绎!

解释模型

预测解释现在就像写几行代码一样简单。以下代码片段从测试集中加载了一批随机图像,并解释了其中五幅图像的预测:

执行上述代码片段后,您将看到下图:

2 — SHAP 对手写数字分类器的解释(图片由作者提供)

输入图像显示在左边,每门课的解释显示在右边。任何红色的东西都会增加模型输出(模型对分类更有信心),而任何蓝色的东西都会减少输出。

这就是 SHAP 对卷积神经网络的解释。让我们在下一部分总结一下。

结论

今天,您已经学习了如何使用 PyTorch 创建一个用于分类手写数字的基本卷积神经网络模型。您还学习了如何解释模型做出的预测。

现在,将这种解释技能引入您的领域就像改变数据集和模型架构一样简单。解释代码应该是相同的,或者需要最小的改变来适应不同的子集。

感谢阅读。

喜欢这篇文章吗?成为 中等会员 继续无限制学习。如果你使用下面的链接,我会收到你的一部分会员费,不需要你额外付费。

https://medium.com/@radecicdario/membership

了解更多信息

  • SHAP:如何用 Python 解读机器学习模型
  • 莱姆:如何用 Python 解读机器学习模型
  • 莱姆与 SHAP:哪个更好地解释机器学习模型?
  • Python 中计算特性重要性的 3 种基本方法
  • 新的 M1 macbook 对数据科学有好处吗?让我们来了解一下

保持联系

  • 在媒体上关注我,了解更多类似的故事
  • 注册我的简讯
  • 在 LinkedIn 上连接
  • 查看我的网站

原载于 2021 年 2 月 1 日 https://www.betterdatascience.com**T21

PyTorch TabNet:与 MLflow 集成

原文:https://towardsdatascience.com/pytorch-tabnet-integration-with-mlflow-cb14f3920cb0?source=collection_archive---------21-----------------------

TabNet 是一个用于表格数据的现代神经网络架构。在本文中,我想让大家关注一个很好的开源实现,它基于 PyTorch,以及我如何将它与 MLflow 集成在一起。

深度学习和表格数据。

TabNet 是一种神经网络架构,专门设计用于充分管理我们以表格格式处理数据的所有情况。

想想看,对于许多公司(如银行和政府机构)来说,绝大多数数据都属于这一类。当然,所有由事务性应用程序“生产”并浓缩到“数据仓库”中的数据都是这种类型的。

在最*十年中,深度学习在非结构化数据上显示出了非凡的结果,例如在图像识别和对象检测(图像)领域,在自然语言处理(文本)领域,在“语音到文本”领域,经常被证明能够达到并克服“人类级别的性能”。

但到目前为止,普遍的想法是,对于表格数据,使用不基于神经网络的算法,如梯度推进算法,通常更容易、更快地获得结果。事实上,在 Kaggle 上分析这些类型的数据就足够了:使用实现可以获得最好的结果,这些实现也可以在 GPU 上快速运行,如 XGBoost,LightGBM,CatBoost。

TabNet 是一种专用于表格形式的结构化数据的神经网络架构。它于 2020 年底推出,其最佳描述可以在常见的ArXiv:https://arxiv.org/pdf/1908.07442.pdf上的文章中找到

TabNet 上的一些信息。

介绍用 Python 开发神经网络的书籍中充满了处理表格数据的例子。原因是样本数据集通常很小,最大为几百 MB,因此网络训练不需要很大的计算能力。当您继续处理图像时,维度会变成(甚至数百)GB 的量级,很快就解释了使用 GPU 的必要性。

在上面提到的那种例子中,网络架构几乎总是一个全连接网络。每一层由一定数量的神经元组成,与前一层的所有神经元相连。

为这样的网络编写代码很容易,但是这些网络不能很好地处理结构化(表格)数据有几个原因。

一个例子:通常这种类型的数据包含相关的特征,特别是当我们有很多的时候。网络总是可以将一些权重置零,但是这个问题会导致过度拟合的问题。

描述 TabNet 的内部结构并不容易,那些想了解其内部结构的人最好读一下我链接的文章。

最有趣的设计元素之一是 TabNet(具有编码器-解码器架构)提供了一系列模块(在编码器部分),数量与超参数 N_STEP 相等,每个模块都有一个顺序注意机制,用于选择(或至少尝试)哪些功能最重要。

与注意力机制直接相关的另一个因素是,TabNet 一旦被训练,就可以很容易地为“可解释性”提供有用的信息。特别是,获得相对的特性重要性或多或少需要一行代码。在下图中,我展示了一个结果的例子。

相对特征重要性(图片由作者提供)。

最后,TabNet 管理并使用嵌入来处理高维分类特征。并且既可以用于分类问题也可以用于回归问题。

对这种架构的关注与日俱增。一个迹象是 Kaggle 上越来越多的人开始尝试使用 TabNet。

如何使用 TabNet?

与关于 ArXiv 的文章一起提供的代码实际上并不能用于具体的应用程序。

但是一家名为 DreamQuark 的公司的一个团队已经开发出了一个开源实现,这个实现叫做 pythorch-tabnet

这个实现也可以从通常的 pypi (pip install pytorch-tabnet)上下载,从 pypi 的统计数据中可以看出,它非常有趣(每月有 9500 次下载)。

一个疑问可能会出现(它出现在我面前,因为我更喜欢使用 TensorFlow,很少使用 PyTorch):但是要使用它,我必须使用并了解 PyTorch 吗?

不。PyTorch TabNet 的开发者已经提供了一个兼容的 scikit-learn 接口。只需实例化 TabNetClassifier 类或 TabNetRegressor 类,传递正确的参数,并在无需远程了解 PyTorch 的情况下使用。

非常详细的例子包含在 GitHub 库中。最好从这些开始。

PyTorch-TabNet 在幕后悄悄地使用 GPU 。在我的 Oracle 云环境中,我所要做的就是启动笔记本电脑。从日志中可以看出他在用 CUDA,显然训练速度更快。

然而,在本文中,我想关注另一个方面:我想说明我已经实现的将 TabNet 与我最喜欢的工具 MFlow 集成的解决方案。

MLflow 集成。

MLflow 是 Databricks 开展的一个开源工具和项目。

GitHub 库位于 URL:https://github.com/mlflow/mlflow

设计师给的定义是:“一个管理机器学习模型生命周期的*台”。它可以做很多事情,甚至可以在你的 MacBook 上使用。

在我的例子中,我用它来跟踪和追踪超参数优化阶段完成的所有“实验”的进度,通常与 Optuna 结合使用。

特别是如果您有大量数据,超参数的优化阶段可能需要大量测试和时间,并且以有组织和结构化的方式跟踪所有结果是很重要的,以便能够重建哪些变化有效,哪些变化无效。

MLflow 可以很容易地安装在 Ubuntu VM 上(需要半个小时),并在 REST 端点上公开一个跟踪 API。

我所做的是:

在 Ubuntu 虚拟机上安装跟踪服务器,使用一个漂亮的 MySQL 数据库作为存储库

在我的笔记本上使用 MLflow Python API

特别是当培训持续很长时间(几个小时)时,在 MLflow 提供的指标图上实时跟踪进度会更加舒适和有趣。

但是让我们来看看与 TabNet 的集成。

TabNet 允许您在 TabNetClassifier 和 TabNetRegressor 类的参数中指定在训练周期中“插入”的回调

对于了解 TensorFlow 的人来说,这些回调遵循相同的模式:它们必须是定义方法的类:

on_train_begin

on_train_end

on_epoch_end

但是,它们扩展了py torch _ tabnet . callbacks . callback类。

总的来说,我的解决方案使用了 MLFlow 跟踪 API 和:

on_train_begin 方法开始实验和运行

在相同的方法中,它将包含我正在试验的参数的字典发送到 MLFlow,以跟踪它们并将所使用的值与获得的性能相关联

使用 on_epoch_end 中的 MLFlow API 更新指标(例如:准确度、AUC)

on_train_end 方法中标记运行的结束

下面是回调的完整代码:

使用 PyTorch TabNet 的完整代码是:

为了满足你合理的好奇心,这是我在一个信用评分模型上运行的图表:

MLflow,训练的进度(图片由作者提供)。

一些安全措施。

对于那些真正希望在自己的云环境中实施这种类型的解决方案的人来说,有一个重要的细节。

如果您在 Linux 机器上安装 MLflow,您将暴露一个端点,而没有任何形式的安全性。因此,建议使用代理,如 NGINX,在代理上实现安全性,并公开 NGINX 端点,而不是直接公开 MLflow。

但我同意,这不太像是数据科学家的工作。您可以从在 Web 应用程序和安全性方面更有经验的同事那里获得帮助。

在我的示例代码中,我没有展示如何传递凭证,但它是有文档记录的。

总之…

我真的认为,我们将看到越来越多有趣的新想法的一个领域是神经网络在表格数据中的应用。

TabNet 是一个有趣的解决方案,有一个很好的开源实现,基于 PyTorch。

(好的)开源的好处是很容易找到文档并将其集成到整个管道中。

我试图展示与 MLflow 集成是多么容易。希望你会感兴趣。

py torch Tabular——表格数据深度学习框架

原文:https://towardsdatascience.com/pytorch-tabular-a-framework-for-deep-learning-for-tabular-data-bdde615fc581?source=collection_archive---------24-----------------------

众所周知,当涉及到表格数据时,梯度推进模型通常会击败所有其他机器学习模型。我写了大量关于梯度推进、背后的理论的文章,并涵盖了不同的实现,如 XGBoost 、 LightGBM 、 CatBoost 、 NGBoost 等。详细地说。

照片由米卡·鲍梅斯特在 Unsplash 上拍摄

以许多其他形式显示的深度学习的不合理的有效性——如文本和图像——还没有在表格数据中得到证明。但是最*,深度学习革命已经将一点点焦点转移到了表格世界,因此,我们看到了专门为表格数据形态设计的新架构和模型。其中许多模型相当于甚至略好于调整良好的梯度推进模型。

PyTorch 表格是什么?

PyTorch Tabular 是一个框架/包装器库,旨在使使用表格数据的深度学习变得容易,并可用于真实世界的案例和研究。图书馆设计背后的核心原则是:

而不是从零开始,框架已经搭在了 PyTorch (显然)和py torch Lightning这样的巨人肩上。

它还配备了最先进的深度学习模型,可以使用熊猫数据框架轻松训练。

  • 高级配置驱动的 API 使得使用和迭代非常快速。您可以只使用 pandas 数据框架,所有标准化、规范化、分类特性编码和准备数据加载器的繁重工作都由库来处理。
  • BaseModel类提供了一个易于扩展的抽象类,用于实现定制模型,并且仍然利用库打包的其余机制。
  • 实现了最先进的网络,如用于表格数据深度学习的神经不经意决策集成和 TabNet:专注的可解释表格学习。有关如何使用它们,请参见文档中的示例。
  • 通过使用 PyTorch Lightning 进行培训,PyTorch 表格继承了 Pytorch Lightning 提供的灵活性和可伸缩性

为什么是 PyTorch 表格?

PyTorch Tabular 旨在降低行业应用和表格数据深度学习研究的准入门槛。就目前的情况来看,使用神经网络并不容易;至少不像传统的 ML 模型用 Sci-kit Learn 那么容易。

PyTorch 表格试图使使用神经网络的“软件工程”部分尽可能简单和容易,并让您专注于模型。我还希望将表格空间中的不同开发统一到一个具有 API 的单一框架中,该框架将与不同的最新模型一起工作。

目前,表格深度学习的大多数发展都分散在各个 Github repos 中。而且除了(我又爱又恨),没有一个框架真正关注过表格数据。这也是 PyTorch 表格产生的原因。

PyTorch 表格怎么用?

装置

首先,让我们看看如何安装这个库。

虽然安装包括 PyTorch,但是最好的和推荐的方法是首先从这里安装 PyTorch,为你的机器选择正确的 CUDA 版本。(PyTorch 版本> 1.3)

一旦安装了 Pytorch,只需使用:

pip install pytorch_tabular[all]

使用额外的依赖项安装完整的库(权重&偏差用于实验跟踪)。

并且:

pip install pytorch_tabular

最基本的必需品。

pytorch_tabular 的源代码可以从Github repo下载。

您可以克隆公共存储库:

git clone git://github.com/manujosephv/pytorch_tabular

一旦您有了源代码的副本,您就可以使用以下软件进行安装:

python setup.py install

设置配置

您需要提供四个配置(其中大多数都有智能默认值),这将驱动其余的过程。

  • DataConfig —定义目标列名、分类列名和数字列名、您需要做的任何转换等。
  • 型号配置 —每个型号都有特定的配置。这决定了我们要训练哪个模型,也让您定义模型的超参数
  • TrainerConfig —这让你可以通过设置诸如批量大小、时期、提前停止等来配置训练过程。绝大多数参数直接从 PyTorch Lightning 借用,并在训练期间传递给底层的训练器对象
  • 优化器配置 —这让您可以定义和使用不同的优化器和学习率调度器。支持标准 PyTorch 优化器和学习率调度器。对于自定义优化器,您可以使用 fit 方法中的参数来覆盖它。定制优化器应该是 PyTorch 兼容的
  • 实验配置 —这是一个可选参数。如果设置,这将定义实验跟踪。目前,只支持两个实验跟踪框架:Tensorboard 和 Weights&bias。B 实验跟踪器有更多的功能,如跟踪跨时代的梯度和逻辑。
data_config = DataConfig( target=['target'], continuous_cols=num_col_names, categorical_cols=cat_col_names, ) trainer_config = TrainerConfig( auto_lr_find=True, batch_size=1024, max_epochs=100, gpus=1) ptimizer_config = OptimizerConfig() model_config = CategoryEmbeddingModelConfig( task="classification", layers="1024-512-512", # Number of nodes in each layer activation="LeakyReLU", # Activation between each layers learning_rate = 1e-3 )

初始化模型和训练

现在我们已经定义了配置,我们需要使用这些配置初始化模型,并调用 fit 方法。

tabular_model = TabularModel( data_config=data_config, model_config=model_config, optimizer_config=optimizer_config, trainer_config=trainer_config, ) tabular_model.fit(train=train, validation=val)

就是这样。将针对指定数量的时期对模型进行训练。

型号列表

  • 具有类别嵌入的前馈网络是一个简单的 FF 网络,但是具有用于类别列的嵌入层。这与 fastai 表格模型非常相似
  • 用于表格数据深度学习的神经不经意决策集成是在 ICLR 2020 上提出的一个模型,据作者称,该模型在许多数据集上击败了经过良好调整的梯度推进模型。
  • TabNet:Attention 可解释表格学习是 Google Research 的另一个模型,它在决策的多个步骤中使用稀疏注意力来模拟输出。

要实现新模型,请参见如何实现新模型教程。它涵盖了基础架构和高级架构。

根据看不见的数据评估模型

为了在训练期间使用的相同度量/损失的新数据上评估模型,我们可以使用方法。

result = tabular_model.evaluate(test)-------------------------------------------------------------------------------- DATALOADER:0 TEST RESULTS 
{
'test_accuracy': tensor(0.6924, device='cuda:0'), 
'train_accuracy': tensor(0.6051, device='cuda:0'), 
'train_loss': tensor(0.6258, device='cuda:0'), 
'valid_accuracy': tensor(0.7440, device='cuda:0'), 
'valid_loss': tensor(0.5769, device='cuda:0')} 
--------------------------------------------------------------------------------

根据看不见的数据进行预测

为了获得数据帧形式的预测,我们可以使用predict方法。这将把预测添加到传入的同一数据帧中。对于分类问题,我们以 0.5 为阈值,得到概率和最终预测

pred_df = tabular_model.predict(test)

保存和加载模型

我们还可以保存一个模型,并在以后加载它进行推理。

tabular_model.save_model("examples/basic") loaded_model = TabularModel.load_from_checkpoint("examples/basic") result = loaded_model.evaluate(test)

代码、文档和如何贡献

该框架的代码可在 PyTorch Tabular:为表格数据建立深度学习模型的标准框架(github.com)获得。

文档和教程可以在py torch Tabular(py torch-Tabular . readthedocs . io)找到

我们非常欢迎投稿,关于如何投稿的详细信息也在这里列出。

相关著作

fastai 是最接* PyTorch 表格式的,都是建立在 PyTorch 之上。但是 PyTorch Tabular 与 fastai 的不同之处在于它的模块化和解耦性质,以及它对标准 PyTorch 和 PyTorch Lightning 组件的使用,这使得采用(包括新模型)和破解代码比使用 fastai 要容易得多。

参考

[1]塞尔戈·波波夫,斯坦尼斯拉夫·莫罗佐夫,阿尔滕·巴本科。 “用于表格数据深度学习的神经不经意决策集成” 。arXiv:1909.06312 [cs。LG] (2019)

[2]塞尔詹·奥·阿里克,托马斯·普菲斯特;。《TabNet:专注可解释性表格学习》 。arXiv:1908.07442 (2019)。

接下来呢?

我将继续撰写单独的博客文章来谈论到目前为止 PyTorch 表格中已经实现的不同模型。小心他们。

原载于 2021 年 1 月 27 日 http://deep-and-shallow.com**的

Pytorch 培训技巧和提示

原文:https://towardsdatascience.com/pytorch-training-tricks-and-tips-a8808ebf746c?source=collection_archive---------15-----------------------

在 Pytorch 中优化深度学习模型训练的技巧/提示

在 Unsplash 上 ActionVance 拍照

在这篇文章中,我将描述并展示我个人发现的 4 种不同 Pytorch 训练技巧的代码,以改善我的深度学习模型的训练。

16 位精度

在常规训练循环中,PyTorch 以 32 位精度存储所有浮点变量。对于使用严格约束来训练他们的模型的人来说,有时,这可能导致他们的模型占用太多的内存,迫使他们使用较小的模型和较小的批量进行较慢的训练过程。但是,以 16 位精度存储模型中的所有变量/数字可以改善并修复大多数这些问题,例如显著降低模型的内存消耗并加快训练循环,同时仍然保持模型的相同性能/准确性。

在 Pytorch 中将所有计算转换为 16 位精度非常简单,只需要几行代码。以下是方法:

scaler = torch.cuda.amp.GradScaler()

用和我上面一样的方法创建一个渐变缩放器。在你写你的训练循环之前这样做。

optimizer.zero_grad()
with torch.cuda.amp.autocast():
   output = model(input).to(device)
   loss = criterion(output, correct_answer).to(device)
scaler.scale(loss).backward()
scaler.step(optimizer)
scaler.update()

当您使用 loss 和优化器进行反向传播时,您需要执行 scaler.scale(loss),而不是 loss.backward()和 optimizer.step()。向后和 scaler.step(优化器)。这使得您的定标器可以转换所有梯度,并以 16 位精度进行所有计算。

当您以 16 位精度执行所有操作时,可能会出现一些数值不稳定的情况,从而导致您可能使用的一些函数无法正常工作。在 16 位精度下,只有某些操作才能正常工作。这里是这方面的更多信息。

进度条

有一个进度条来表示每个时期已经完成的训练的百分比是非常有用的。为了获得进度条,我们将使用 tqdm 库。以下是下载和导入它的方法:

pip install tqdmfrom tqdm import tqdm

在培训和验证循环中,您必须这样做:

for index, batch in tqdm(enumerate(loader), total = len(loader), position = 0, leave = True):

仅此而已。一旦您为您的训练和验证循环做了这些,您将得到一个进度条,它代表您的模型已经完成的训练的百分比。它应该是这样的:

在图中,691 表示我的模型必须完成多少批,7:28 表示我的模型在 691 批上训练/评估所用的总时间,1.54 it/s 表示我的模型一批所用的*均时间。

梯度累积

如果您遇到 CUDA 内存不足错误,这意味着您已经超出了您的计算资源。要解决这个问题,您可以做几件事情,包括将所有内容转换为 16 位精度,正如我上面提到的,减少模型的批量大小,以及在创建数据加载器时减少 num_workers 参数:

train_loader = DataLoader(dataset=train_data, batch_size=batch_size, shuffle=True, num_workers=0)

但是,有时,切换到 16 位精度并减少 num_workers 可能无法完全解决问题。解决这个问题最直接的方法是减少批量,但是假设你不想减少批量。如果你不想减少你的批量,你可以使用梯度积累来刺激你想要的批量。请注意,CUDA 内存不足问题的另一个解决方案是简单地使用多个 GPU,但这是许多人无法实现的选项。

假设您的机器/型号只能支持 16 的批处理大小,增加它会导致 CUDA 内存不足错误,而您想要 32 的批处理大小。梯度累积的工作方式是,以 16 的批量运行模型两次,累积为每批计算的梯度,最后在这两次正向传递和梯度累积后执行优化步骤。

为了理解梯度累积,重要的是理解在训练神经网络中完成了什么特定功能。假设您有以下训练循环:

model = model.train()
for index, batch in enumerate(train_loader):
    input = batch[0].to(device)
    correct_answer = batch[1].to(device)
    optimizer.zero_grad()
    output = model(input).to(device)
    loss = criterion(output, correct_answer).to(device)
    loss.backward()
    optimizer.step()

查看上面的代码,需要记住的关键是 loss.backward()创建并存储模型的渐变,但是 optimizer.step()实际上更新权重。在调用 optimizer 累积梯度之前,调用 loss.backward()两次。以下是如何在 PyTorch 中实现渐变累积:

model = model.train()
optimizer.zero_grad()
for index, batch in enumerate(train_loader):
    input = batch[0].to(device)
    correct_answer = batch[1].to(device)
    output = model(input).to(device)
    loss = criterion(output, correct_answer).to(device)
    loss.backward()
    if (index+1) % 2 == 0:
       optimizer.step()
       optimizer.zero_grad()

如您所见,以上面的例子为例,我们的机器只能支持 16 个批次,而我们想要 32 个批次,我们实际上是计算 2 个批次的梯度,然后更新实际重量。这导致有效的批量大小为 32。

做 16 位精度的梯度累加很像。

model = model.train()
optimizer.zero_grad()
for index, batch in enumerate(train_loader):
    input = batch[0].to(device)
    correct_answer = batch[1].to(device)
    with torch.cuda.amp.autocast():
         output = model(input).to(device)
         loss = criterion(output, correct_answer).to(device)
    scaler.scale(loss).backward()
    if (index+1) % 2 == 0:
       scaler.step(optimizer)
       scaler.update()
       optimizer.zero_grad()

对结果的评估

在大多数机器学习项目中,人们倾向于手动计算他们用于评估的指标,然后报告它们。尽管计算准确度、精确度、召回率和 F1 等指标并不难,但在某些情况下,您可能希望使用这些指标的某些变体,如宏观/微观精确度、召回率和 F1,或者加权精确度、召回率和 F1。计算这些可能需要更多的工作,有时,您的实现可能是不正确的。要高效、快速、无误地计算所有这些指标,可以使用 sklearns classification_report 库。这是一个专门为计算这些指标而设计的库。这是你如何使用它。

from sklearn.metrics import classification_report
y_pred = [0, 1, 0, 0, 1]
y_correct = [1, 1, 0, 1, 1]print(classification_report(y_correct, y_pred))

上面的代码是用于二进制分类的。您可以为更多目的配置/使用该功能。第一个列表代表模型的预测,第二个列表代表正确答案。上面的代码会输出:

结论

在这篇文章中,我讨论了 4 种优化深度神经网络训练的方法。16 位精度减少了内存消耗,梯度累积允许您通过模拟更大的批处理大小来解决任何内存限制,tqdm 进度条和 sklearns 分类报告库是两个方便的库,允许您轻松跟踪模型的训练并评估模型的性能。就我个人而言,我总是用上面所有的训练技巧来训练我的神经网络,并且在必要的时候使用梯度累积。

我希望您发现这些内容易于理解且信息丰富。如果你有任何问题,请在评论中告诉我。

Pytorch vs Tensorflow 2021

原文:https://towardsdatascience.com/pytorch-vs-tensorflow-2021-d403504d7bc3?source=collection_archive---------2-----------------------

PyTorch (1.8)和 Tensorflow (2.5)最新版本的比较

Vanesa Giaconi 在 Unsplash 上拍摄的照片

Tensorflow/Keras & Pytorch 是目前最受欢迎的两个主要机器学习库。Tensorflow 由 Google 维护和发布,Pytorch 由脸书维护和发布。在本文中,我想从以下几个方面对它们进行比较:

  • 最新发布版本中的新增功能
  • 使用哪一个?为什么(基于 2 年的 ML 项目)

Tensorflow 2.x:

Tensorflow 1 和 Tensorflow 2.x 之间有许多变化,我将尝试找出最重要的变化。

第一个是 Tensorflow.js 的发布随着 web 应用程序越来越占主导地位,在浏览器上部署模型的需求已经增长了很多。使用 Tensorflow.js,您可以使用 Node 在浏览器中运行现有的 python 模型,重新训练现有的模型,并完全使用 Javascript 构建&训练您自己的模型(您不需要 python)。

Tensorflow 2.x 的另一个版本是 **Tensorflow Lite,**一个“在移动和嵌入式设备上部署模型的轻量级库”。这是有意义的,因为移动网络应用是两种最主要的应用类型。使用 Tensorflow Lite,您可以简单地将现有模型转换为“压缩*面缓冲区”,并将该缓冲区加载到移动设备或任何其他嵌入式设备中。发生的主要优化过程是将 32 位浮点转换成更适合嵌入式设备的 8 位浮点(占用更少的内存)。

最终的主要版本是 Tensorflow Extended (TFX) ,这是一个用于部署生产 ML 管道的端到端*台。我不得不承认,他们在确定机器学习的 3 个最重要的领域(网络应用、移动应用&生产管理)方面做得很好。ML 生产管道仍然需要大量的研究和开发。TFX 帮助解决传统的软件生产挑战,如可伸缩性、可维护性和模块化。此外,它有助于应对机器学习的特定挑战,如持续在线学习、数据验证、数据管理等。

Pytorch 1.8:

与 Tensorflow Lite 类似,Pytorch 也改进了他们现有的 Pytorch Mobile 。一个框架可以量化、追踪、优化和保存 Android 和 iOS 的模型。他们还发布了 Pytorch Lite 解释器的原型,减少了移动设备上的二进制运行时大小。

此外,他们还通过更具体的错误处理和流水线并行性,增加了对分布式培训的更多支持。最后,他们推出了 Pytorch Profiler ,这是一款用于调试和排除大规模深度学习模型故障的工具。

虽然 Pytorch lightning 不是 Pytorch 1.8 的一部分,但我认为它值得一提。 Pytorch lightning 已经发布,使神经网络编码更加简单。你可以把它想象成 Pytorch 的 Keras。它已经获得了很大的吸引力。我认为值得一提的原因是,Keras 一直在显著改进 Tensorflow,因为它使实现模型变得更加容易和简短。Pytorch 闪电对 Pytorch 做了同样的事情。

用哪个?

迈特·沃尔什在 Unsplash 上拍照

每个人心中的下一个问题是使用哪一个。在过去的 2 年里,每当我开始一个机器学习项目时,我都在想这个问题。我会给你一个快速反映我的经验,以及如何选择它们的提示。

本质上,这两个库都非常好,它们在性能和提供的特性上非常接*。总的来说,你必须意识到这两者在编码风格上是有区别的,当你在进行机器学习项目时,这实际上是有区别的。

Pytorch 以其 OOP(面向对象编程)风格而闻名。例如,当您创建自定义模型或自定义数据集时,您很可能会创建一个继承默认 PyTorch 库的新类,然后修改您自己的方法。就我个人而言,我不是 OOP 的忠实粉丝。尽管它以某种方式为代码提供了一种结构,但就代码行数而言,它使实现变得更长。

另一方面,当您使用 Tensorflow 时,您很可能会使用 Keras。在进行 Kaggle 竞赛(监督图像分类、对象检测、图像分割、NLP 等)时,我几乎总是发现 Keras 的实现比 Pytorch 的短。这对于初学者/中级人员来说是非常好的,因为您不必花费大量时间阅读和分解代码行。

在某些情况下,你会在一个特定的机器学习子领域中寻找一个特定的模型。根据我的经验,一个非常有用的提示是,在这种情况下,您很可能只在其中一个库中找到对该模型的更多支持。原因很简单,因为它是在那里第一次实现的,教程也是从第一次实现开始堆积的。在这种情况下,只要图书馆有更多的支持,因为它会让你的生活更容易。例如,当我在做一个对象检测比赛,我想实现 DETR(脸书的数据高效转换器),我发现的大多数资源都是用 Pytorch 编写的(显然),因此在这种情况下使用 Pytorch 要容易得多。

此外,最后一个反思是,我发现 Pytorch 的实现更长,因为它们似乎覆盖了许多底层细节。这既是优点也是缺点。之所以说是 pro,是因为当你是一个初学者的时候,最好先学习那些底层的细节,然后再转到 Keras 等更高级的 API。然而,这是一个缺点,因为你会发现自己迷失在许多细节和相当长的代码中。所以本质上,如果你的工作期限很紧,最好选择 Keras 而不是 Pytorch。

最后的想法

我希望你喜欢这篇文章,并发现我的提示很有帮助。我知道可能会有一些关于它们的不同意见,但这仅仅是因为人们对这两个库有不同的体验。但是,我总是发现初学者/中间用户在使用这两个库时有非常相似的经历。由于这两个库在性能上非常相似,我认为选择哪个库可能只是一个简单的偏好问题。

如果你想了解更多关于 TensorFlow 的知识,我建议在这里购买这本书。我完整地阅读了这本书,它极大地帮助了我的 ML 知识。另外,请注意,这是一个亚马逊联盟链接,如果你买这本书,我会得到佣金。我只是推荐这本书,因为我已经买了它,我学到了很多非常有用的概念,帮助我找到了一份新工作。

如果你想定期收到关于人工智能和机器学习的最新论文的评论,请在这里添加你的电子邮件并订阅!

https://artisanal-motivator-8249.ck.page/5524b8f934

pytorch-widedeep:表格数据的深度学习

原文:https://towardsdatascience.com/pytorch-widedeep-deep-learning-for-tabular-data-9cd1c48eb40d?source=collection_archive---------16-----------------------

这是系列文章的第三篇,介绍了[pytorch-widedeep](https://github.com/jrzaurin/pytorch-widedeep)一个将表格数据与文本和图像相结合的灵活包(它也可以单独用于“标准”表格数据)。之前的两篇帖子,以及这篇帖子的原始版本都托管在我自己的博客里,以防万一。

在写这篇文章的时候,我假设读者不熟悉前两篇文章。当然,阅读它们会有所帮助,但是为了理解这篇文章的内容,然后能够在表格数据上使用pytorch-widedeep,这不是必需的。

首先,一如既往,只需安装软件包:

pip install pytorch-widedeep

这将安装v0.4.8希望是最后一个测试版本*。在代码方面,我认为这可能已经是v1,但在此之前,我想在更多的数据集中尝试一下,并选择好的默认值。此外,我还打算实现其他算法,特别是TabNet【1】,已经有一个非常好的实现。

继续,正如我前面提到的,pytorch-widedeep的主要目标是通过宽和深的模型促进图像和文本与表格数据的结合。为此,宽和深模型可以用多达四个模型组件构建:widedeeptabulardeeptextdeepimage,它们将处理不同类型的输入数据集(“标准”表格,即数字和分类特征、文本和图像)。这篇文章只关注所谓的deeptabular组件,以及该库中可用于构建该组件的 3 种不同模型。尽管如此,为了完整起见,我将首先简要描述其余的组件。

宽深模型的wide组件只是一个线性模型,在pytorch-widedeep中,这样的模型可以通过[Wide](https://pytorch-widedeep.readthedocs.io/en/latest/model_components.html#pytorch_widedeep.models.wide.Wide)类创建。对于deeptext组件,pytorch-widedeep提供了一个模型,可通过[DeepText](https://pytorch-widedeep.readthedocs.io/en/latest/model_components.html#pytorch_widedeep.models.deep_text.DeepText)类获得。DeepText构建一个简单的 LSTMs 堆栈,即标准的 DL 文本分类器或回归器,具有使用预训练单词嵌入、全连接头(FC-Head)等的灵活性。对于deepimage组件,pytorch-widedeep包括两种选择:预训练的 Resnet 模型或从头开始训练的 CNN 的“标准”堆栈。这两个都可以通过[DeepImage](https://pytorch-widedeep.readthedocs.io/en/latest/model_components.html#pytorch_widedeep.models.deep_image.DeepImage)类获得,和DeepText的情况一样,在构建架构时提供了一些灵活性。

为了澄清术语“模型”和“w ide 和深度模型组件”的用法(以防有一些混淆),让我们看一下下面的代码:

wide_model = Wide(...)
text_model = DeepText(...)
image_model = DeepImage(...)

# we use the previous models as the wide and deep model components
wdmodel = WideDeep(
    wide=wide_model, 
    deeptext=text_model, 
    deepimage=image_model
)

...

简单地说,一个宽而深的模型有模型组件,这些组件(当然)就是模型本身。请注意,四个宽和深模型组件中的任何一个都可以是用户定制的模型。事实上,虽然我推荐使用在pytorch-widedeep中可获得的模型用于widedeeptabular组件,但是用户很可能想要使用他们自己的模型用于deeptextdeepimage 组件。这是完全可能的,只要定制模型有一个名为output_dim的属性,该属性具有最后一层激活的大小,这样就可以构造WideDeep(参见回购中的这个示例笔记本)。此外,四个组件中的任何一个都可以单独使用。例如,您可能想只使用一个wide组件,它只是一个线性模型。为此,简单来说:

wide_model = Wide(...)

# this would not be a wide and deep model but just wide
wdmodel = WideDeep(wide=wide_model)

...

如果您想了解更多有关不同模型组件和pytorch-widedeep中可用模型的信息,请查看回购中的示例文件夹、文档或配套帖子。现在让我们深入了解一下deeptabular组件可用的模型

*如果您使用的是 Mac、python 3.8 或 Pytorch 1.7+,请检查repo或此post以了解安装中的注意事项。注意,这是 包没有直接关系,而是 Mac 与 OpenMP 之间的相互作用,以及 Mac 的 multiprocessing 库的新默认值)。

1.deeptabular组件

当我开发这个包的时候,我意识到也许pytorch-widedeep中最有趣的产品之一与deeptabular组件可用的模型有关(也许…)。请记住,每个组件都可以独立使用。考虑到这一点,构建一个只包含一个deeptabular组件的WideDeep模型通常被称为表格数据的 DL。当然,这样的模型不是一个又宽又深的模型,是“仅仅”深的。

目前,pytorch-widedeep提供三种型号可以用作deeptabular组件。按复杂程度排列,它们是:

已经有(并且正在有)很多关于使用 DL 处理表格数据的文章,当然每个模型本身都应该有一个帖子。在这里,我将尝试详细描述它们,并举例说明它们在pytorch-widedeep中的用法。在不久的将来,将进行一次适当的基准测试。

1.1 TabMlp

下图说明了TabMlp模型架构。

图一TabMlp. (图片作者)

TabMlp是 simples 架构,非常类似于 fantastic fastai 库中可用的表格模型。事实上,MLP 的密集层的实现与该库中的实现基本相同。图中的虚线框表示这些组件是可选的。例如,如果我们愿意的话,我们可以使用不带分类成分或者不带连续成分的TabMlp

让我们看看这个模型是如何与众所周知的成人人口普查数据集一起使用的。我假设您已经下载了数据并将其放置在data/adult/adult.csv.zip。以下片段是数据的简单准备,与pytorch-widedeep没有直接关系(记住,你可以运行的笔记本可以在这里找到):

片段 1。

现在我们只需要定义将被表示为嵌入的列和数字(也称为连续)列。pytorch-widedeep附带了一系列方便的预处理工具,为我们准备数据:

片段 2

到此为止的代码对所有型号都是通用的,对TabTransformer做了一些小的改动。在内部,TabPreprocessor label 对“嵌入列”进行编码,并对数字列进行标准化。请注意,您可以选择不标准化数字列,然后在构建模型时使用BatchNorm1D层。这也是一种有效的方法。或者,你可以两者都用,就像我一样。

在这一阶段,数据已经准备好,我们可以开始构建模型了

片段 3

在我继续之前,有一件重要的事情要提一下,对所有模型来说都是一样的,那就是pytorch-widedeep模型(在这种情况下是TabMlp)不建立最后的连接,即与输出神经元的连接,这取决于这是一个回归、二进制还是多类分类。这样的连接是由WideDeep“constructor”类构建的。这意味着即使我们想使用单组件模型,这个模型仍然需要用WideDeep类来构建。

这是因为这个库是先验的,旨在构建WideDeep模型(因此得名)。一旦模型建立,它就被传递给Trainer(正如我们现在将看到的)。Trainer类被编码为接收类WideDeep的父模型,其子模型是模型组件。这对图书馆的许多方面都非常方便。

这实际上只需要一行额外的代码(代码片段 3 中的第 10 行)

让我们看看我们刚刚构建的模型,以及它与图 1 的关系(注意,为了保持 post 的大小“易处理”,我将只打印TabMlp模型的架构。如果你想看TabRensetTabTransformer的建筑,可以看看最初的帖子。

modelWideDeep(
  (deeptabular): Sequential(
    (0): TabMlp(
      (embed_layers): ModuleDict(
        (emb_layer_education): Embedding(17, 8, padding_idx=0)
        (emb_layer_marital_status): Embedding(8, 6, padding_idx=0)
        (emb_layer_occupation): Embedding(16, 8, padding_idx=0)
        (emb_layer_race): Embedding(6, 6, padding_idx=0)
        (emb_layer_relationship): Embedding(7, 6, padding_idx=0)
        (emb_layer_workclass): Embedding(10, 6, padding_idx=0)
      )
      (embedding_dropout): Dropout(p=0.1, inplace=False)
      (norm): BatchNorm1d(4, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      (tab_mlp): MLP(
        (mlp): Sequential(
          (dense_layer_0): Sequential(
            (0): Dropout(p=0.1, inplace=False)
            (1): Linear(in_features=44, out_features=200, bias=True)
            (2): ReLU(inplace=True)
          )
          (dense_layer_1): Sequential(
            (0): Dropout(p=0.1, inplace=False)
            (1): Linear(in_features=200, out_features=100, bias=True)
            (2): ReLU(inplace=True)
          )
        )
      )
    )
    (1): Linear(in_features=100, out_features=1, bias=True)
  )
)

我们可以看到我们的model是由单个组件deeptabular构成的WideDeep类的对象,T5 本身是TabMlp类的模型。TabMlp 由一系列将被表示为嵌入(例如emb_layer_education)的列组成,这些列被连接以形成 dim (bsz, 40)的张量,其中bsz是批量大小。然后,“batch formed”连续列也与该张量连接,产生 dim (bsz, 44)张量,该张量将被传递给tab_mlp,即 2 层 MLP [200 -> 100]。最后,dim (bsz, 100)的输出张量被“插入”到最后一个神经元中。总结一下:嵌入+连续+ MLP。

我们现在准备训练它。下面的代码只是用默认值运行。人们可以使用任何torch优化器、学习率调度器等。看看回购中的文档或示例文件夹即可。

片段 4

一旦我们理解了TabMlp是做什么的,那么TabResnet应该非常简单

1.2 TabResnet

下图说明了TabResnet模型架构。

图二TabResnet. (图片作者)

TabResnet模型类似于TabMlp,但是嵌入(或者嵌入和连续特征的连接,标准化或未标准化)通过一系列由密集层构建的 Resnet 块。虚线框表示组件是可选的,虚线表示不同的路径或连接,这取决于我们决定包含哪些组件。

这可能是本文讨论的三个模型中最灵活的,因为可以通过参数定义许多变量。例如,我们可以选择将连续的特征(通过BatchNorm1d层标准化或未标准化)与嵌入连接起来,然后通过一系列 Resnet 块传递这种连接的结果。或者,我们可能更喜欢将连续的特性与通过 Resnet 块传递嵌入的结果连接起来。另一个可选组件是输出神经元之前的 MLP。如果不存在 MLP,来自 Resnet 块的输出或者将该输出与连续特征(标准化或未标准化)连接的结果将直接连接到输出神经元。

每个 Resnet 块由以下操作组成:

**图 3** 。“密集”Resnet 块。b是批量大小,d是嵌入的尺寸。(图片由作者提供)

让我们构建一个TabResnet模型,它与构建一个TabMlp模型所需的代码基本相同。

正如我们之前对TabMlp所做的那样,让我们“浏览”一下这个模型。同样,如果你想看这个架构的完整版本,请访问最初的帖子。在这种情况下,model是由单个组件形成的WideDeep对象的实例,deeptabular是一个TabResnet模型。TabResnet由一系列Embedding层(例如emb_layer_education)、一系列所谓的致密 Resnet 块(tab_resnet)和 MLP ( tab_resnet_mlp)形成。嵌入本身被连接,然后进一步与标准化的连续列连接。dim (bsz, 44)的结果张量然后通过两个密集的 Resnet 块。一个 Resnet 块的输出是下一个的输入。因此,当设置blocks_dim = [200, 100, 100]时,我们正在生成输入/输出分别为 200/100 和 100/100 的两个模块。dim (bsz, 100)的第二 Resnet 模块的输出通过 2 层 MLPtab_resnet_mlp,最终“插入”输出神经元。总结一下:嵌入+连续+稠密伦集+ MLP。

为了运行它,代码与前面显示的TabMlp代码相同。

代码片段 5

现在,最后但并非最不重要的是,库的最后一个成员TabTransformer

1.3 TabTransformer

亚马逊的聪明人在 TabTransformer:使用上下文嵌入的表格数据建模【2】中详细描述了TabTransformer。是一篇有趣的论文,当然,如果您打算在表格数据上使用这个模型(如果您对表格数据的 DL 感兴趣,我强烈推荐您使用这个模型)。

我的实现不是唯一可用的。鉴于该模型是由亚马逊的研究人员构思的,它也可以在他们神奇的[autogluon](https://github.com/awslabs/autogluon)图书馆中找到(你一定要去看看)。此外,你可以在这里找到王飞的另一个实现,他的整个 github 非常出色。我的实现部分受到了这些的启发,但是有一些特殊性和适应性,所以它可以在pytorch-widedeep包中工作。

下图说明了TabTransformer模型的架构。

**图 4** 。TabTransfomer. (图片作者)

图中的虚线框表示该组件是可选的。

与前面的案例一样,在构建模型时需要考虑许多变量和细节。我将在这里描述一些,但是对于所有可能参数的完整视图,请查看文档。

在这篇文章中,我不想详细讨论什么是变压器。如果你想了解它,有大量的文献,最受欢迎的可能是注释转换器。也可以看看这篇文章,如果你是一个数学“狂人”,你可能会喜欢这篇文章。不过,让我在这里简单描述一下,这样我就可以介绍一下这篇文章需要的数学知识。用一句话来说,Transformer 由一个多头自我关注层和随后的前馈层组成,在每一层之后进行元素式添加和层规范化。

正如你们大多数人所知,自我关注层由三个矩阵组成,键、查询和值。每个输入分类列,即嵌入,都被投影到这些矩阵上(尽管见本文后面的fixed_attention选项),以生成它们相应的键、查询和值向量。形式上,让kr{e×d}、qr{e×d}和kr 然后,每个输入分类列,即嵌入,通过一个关注头关注所有其他分类列:

等式 1

在哪里

等式 2

这就是我们需要的所有数学知识。

当我用一个图形来说明变压器模块时,我意识到读者可能已经看到了每一个可能的表示/图形。因此,我决定用一种与实现方式直接相关的方式来说明 transformer 块。

图 5 。变压器块。(图片由作者提供)

括号中的字母表示在相应框中表示的运算之后张量的维数。例如,张量attn_weights有 dim (b, h, s, s)

如图所示,输入张量 X 被投影到它的键、查询和值矩阵上。然后这些被"重新排列成"多头自我关注层,其中每个头将处理部分嵌入。然后我们计算出 A (等式 2),再乘以 V 得到我所说的attn_score(等式 1)。然后,attn_score张量被重新排列,这样我们“收集来自所有头部的注意力分数,并再次投影以获得结果attn_outattn_out将被添加到输入(X)中并被归一化,产生Y。最后Y经过前馈层和进一步的加法+范数运算。

在转到与构建模型本身相关的代码之前,实现中有几个细节值得一提。这些选项在自动引导库中也可用。

**FullEmbeddingDropout**

当构建一个TabTransformer模型时,有可能完全删除对应于分类列的嵌入。这是由指向类[FullEmbeddingDropout](https://github.com/jrzaurin/pytorch-widedeep/blob/be96b57f115e4a10fde9bb82c35380a3ac523f52/pytorch_widedeep/models/tab_transformer.py#L153)的参数full_embed_dropout: bool设置的。

**SharedEmbeddings**

当构建一个TabTransformer模型时,代表一个分类列的所有嵌入可以共享它们嵌入的一部分,或者为每个列定义一个公共的独立嵌入,该嵌入将被添加到该列的嵌入中。

这个所谓的“列嵌入”背后的想法是使模型能够区分一列中的类和其他列中的类。换句话说,我们希望模型不仅要学习列中不同分类值的表示,还要学习列本身的表示。这通过shared_embed组参数实现:share_embed : booladd_shared_embed: boolfrac_shared_embed: int。第一个简单地指示嵌入是否将被共享,第二个设置共享策略,第三个设置将被共享的嵌入的部分,取决于策略。它们都与类别[SharedEmbeddings](https://github.com/jrzaurin/pytorch-widedeep/blob/be96b57f115e4a10fde9bb82c35380a3ac523f52/pytorch_widedeep/models/tab_transformer.py#L165)相关

例如,假设我们有一个包含 5 个不同类别的分类列,它们将被编码为 dim 8 的嵌入。这将导致 dim (5, 8)列查找表。两种共享策略如图 6 所示。

图 6 。两种共享嵌入策略。上图:列嵌入替换了代表分类列不同值的总嵌入的embedding_dim / frac_shared_embed(本例中为 4)。下面板:在原来的嵌入查找表中增加了列嵌入(嗯,技术广播和增加)。注意这里的n_cat指的是这个特定列的不同类别的数量。(图片由作者提供)

**fixed_attention**

正如我在之前提到的,我实现固定注意力的灵感来自于自动增长图书馆。所以有时间就去查查这两个吧。

当使用“固定注意”时,关键矩阵和查询矩阵不是输入张量 X 的任何投影的结果,而是在实例化模型时单独定义的 dim (number of categorical columns x embeddings dim的可学习矩阵(称为fixed_keyfixed_query)。fixed_attention不影响值矩阵 V 的计算方式。

让我举一个有数字的例子来说明问题。假设我们有一个包含 5 个分类列的数据集,它们将通过 dim 4 的嵌入进行编码,我们使用的批量大小(bsz)为 6。图 7 显示了在有和没有固定注意的情况下,如何为给定的批次计算关键矩阵(同样适用于查询矩阵)。

图 7 。具有和不具有固定关注的给定批次的关键矩阵计算(同样适用于查询矩阵)。矩阵中的不同色调是我试图说明的,在没有固定注意力的情况下,关键矩阵可以在矩阵中的任何地方具有不同的值,而在固定注意力的情况下,关键矩阵是“固定键”重复bsz次的结果。当然,上方面板中的项目层是沿着bsz维度广播的。(图片由作者提供)

正如我多次提到的(公*起见),这个实现是受 Autogluon 库的启发。既然是亚马逊的人想出了TabTransformer,那么认为他们找到了注意力实现的用途也就顺理成章了。然而,在撰写本文时,我对这种用法并不是 100%清楚,而且我也没有在论文中找到任何有用的信息。尽管如此,众所周知,在像机器翻译这样的问题中,大多数注意力头学习冗余模式(参见例如 Alessandro Raganato 等人,2020 [4]以及其中的参考文献)。因此,也许这里讨论的固定注意机制有助于减少涉及表格数据的问题的冗余。

总的来说,用外行人的话来说,我解释fixed_attention的方式如下:当使用固定注意力时,键和查询矩阵是在模型被实例化时定义的,并且直到注意力权重(attn_weights)乘以值矩阵以获得我在图 5 中称为attn_score的值时才知道输入。这些注意力权重实质上是关键字和查询矩阵之间的矩阵乘法(加上 softmax 和归一化)的结果,对于给定批次中的所有样本,这些注意力权重对于所有头部都是相同的。因此,我的解释是,当使用固定注意力时,我们降低了转换器的注意力能力,它将集中于输入的较少方面,减少了潜在的冗余。

总之,足够的猜测。是时候看看代码了。注意,因为我们要堆叠嵌入(而不是连接它们),所以它们必须具有相同的维度。这种尺寸是在我们建立模型时设置的,而不是在预处理阶段。为了避免输入格式冲突,我们在预处理时使用了for_tabtransformer参数。

代码片段 6

现在,简单地说

代码片段 7

如果你转到最初的帖子,并且查看模型架构,你会发现model一如既往地是由单个组件构成的WideDeep对象的实例,deeptabular 是一个TabTransformer模型。TabTransformer由一系列嵌入层(如emb_layer_education)、一系列变换编码块** ( blks)和一个 MLP ( tab_transformer_mlp)构成。这里的嵌入是我之前描述过的SharedEmbeddings类。这些嵌入件堆叠在一起,并穿过三个变压器块。所有分类列的输出被连接,产生 dim 的张量(bsz, 192),其中 192 等于分类列的数量(6)乘以嵌入 dim (32)。然后将这个张量与“层形成的连续列连接起来,得到一个 dim (bsz, 196)的张量。像往常一样,这个张量通过tab_transformer_mlp,它遵循论文中的指导()MLP 层大小被设置为{4 × l,2 × l},其中 l 是其输入的大小。))是[784 -> 392],还有“出发吧”。总之SharedEmbeddings +连续+变压器编码器模块+ MLP。

为了运行它,代码与之前显示的TabMlpTabRenset的代码相同。

代码片段 8

** 注意 TabTransformer 主要部件的命名相对于其他两款有一点不一致。如果您通过 pypi 安装了这个包,那么转换器编码器块被命名为 blks 。一个与其他型号更一致的名称会是,例如, tab_transformer_blks 。在将 v0.4.8 发布到 pypi 之后,我就意识到了这种不一致。这样的小问题不值得另一个子版本。然而,如果你从 github 安装这个包,这个问题就解决了,并且 pypi 和 github 版本在将来的版本中是一致的。

2.结论和未来工作

在这篇文章中,我的意图是说明如何使用pytorch-widedeep作为“表格数据的标准 DL”的库,即不构建宽和深的模型,以及不涉及文本和/或图像的问题(如果你想了解关于库的更多信息,请访问 repo 、文档或以前的文章)。为此,我们需要的唯一组件是deeptabular组件,为此pytorch-widedeep提供了 3 个“开箱即用”的模型:TabMlpTabResnetTabTransformer。在这篇文章中,我详细解释了它们的架构以及如何在库中使用它们。在不久的将来,我打算实现 TabNet ,也许还有Node【6】,同时进行适当的基准测试,这样我就可以设置健壮的默认值,然后发布版本1.0。当然,您可以通过使用数据集中的包来帮助我🙂。如果你觉得这篇文章有用,并且你喜欢这个库,请给这个库打个星。除此之外,快乐编码。

3.参考

[1] TabNet:专注的可解释表格学习,塞尔詹·奥·阿里克,托马斯·普菲斯特, arXiv:1908.07442v5

[2] TabTransformer:使用上下文嵌入的表格数据建模。黄鑫,阿什什·赫坦,米兰·茨维特科维奇,佐哈尔·卡尔宁,2020。 arXiv:2012.06678v1

[3]关注是你所需要的一切,Ashish Vaswani,Noam Shazeer,Niki Parmar 等人,2017 年。 arXiv:1706.03762v5

[4]《注意力的数学理论》,詹姆斯·武科维奇,阿里斯蒂德·巴拉廷,雷米·塔切特·德斯·科姆,2020 年。arXiv:2007.02876v2

[5]基于转换器的机器翻译中的固定编码器自我注意模式。亚历山德罗·拉加纳托,伊夫·舍勒,约尔格·蒂德曼,2020。arXiv:2002.10260v3

[6]用于对表格数据进行深度学习的神经不经意决策集成。塞尔戈·波波夫,斯坦尼斯拉夫·莫罗佐夫,阿尔特姆·巴本科, arXiv:1909.06312v2

pytorch-widedeep,用于表格数据的深度学习 IV:深度学习与 LightGBM

原文:https://towardsdatascience.com/pytorch-widedeep-deep-learning-for-tabular-data-iv-deep-learning-vs-lightgbm-cadcbf571eaf?source=collection_archive---------19-----------------------

理解大数据

对于分类和回归问题的表格数据,DL 算法和 LightGBM 之间的彻底比较

在这里,我们继续这个系列的另一篇文章。这是系列的第四部。之前的三个帖子,以及这个帖子的原始版本都托管在我自己的博客里,以防万一。

几个月前,当我发布了库 pytorch-widedeep 的最后一个测试版本(0.4.8)时,我就开始计划这篇文章了。然而,从那时起,一些事情变得优先,这意味着运行我运行的数百个实验(可能超过 1500 个),花费了我比我预期多得多的时间。尽管如此,我们还是来了。

这个项目的所有深度学习模型都在一个p2.xlarge实例上运行,所有的LightGBM实验都在我的 Mac Mid 2015上运行。

首先,我要感谢 AWS 社区建设者们,尤其是卡梅隆,让我在 AWS 周围的生活变得更加轻松。

这个项目的所有深度学习模型都在一个p2.xlarge实例上运行,所有的LightGBM实验都在我的 Mac Mid 2015上运行。

一旦得到了适当的认可,让我告诉你一些关于所有这些实验和这篇文章的背景。

1.简介:为什么会这样?

嗯,在过去的几年里,特别是去年,我一直在努力改进 pytorch-widedeep 。这真的很有趣,我学到了很多。然而,当我向库中添加模型时,尤其是表格组件(见这里的),我想知道除了学习这些模型本身之外,它是否还有其他用途。你看,我是一名教育科学家,我在学术界呆了十多年。在那里,我们曾经做了很多不太有用的事情,很酷(有时),但不是很有用。几年前,驱使我进入私营部门的一个原因是寻找一种“有用”的感觉,在那里我可以建造一些既有科学意义又有用的东西。考虑到这一点,原谅我的多余,我希望这个库是有用的。形容词“有用的”在这里可以指许多事情。这可能意味着直接使用库,或者派生回购并使用代码,或者只是复制并粘贴给定项目的部分代码。然而,最终,我想回答的一个问题是:这些模型比其他更“标准”的模型(如 GBMs)的性能更好,甚至更好吗?。注意,我写的是“一个问题”,而不是“问题”。稍后会有更多关于这个的报道。

当然,我不是第一个将深度学习(以下简称 DL)方法与 GBMs 进行表格数据比较的人,也不会是最后一个。事实上,在我写这几行字的时候,一篇新论文:表格数据:深度学习不是你需要的全部 [1]发表了。这篇文章和那篇文章非常相似,结论也完全一致。但是,也有一些不同之处。将 DL 算法与XGBoost【2】和CatBoost【3】进行比较,而我使用LightGBM【4】(参见第 2.3 节对该算法使用的解释)。此外,我要说,我在这里使用的四个数据集中的三个比他们论文中的数据集更具挑战性,但这可能只是我的看法。最后,除了TabNet之外,我这里用的 DL 模型和那篇论文里的是不一样的。尽管如此,在结论部分,我将写一些关于如何处理这个基准/测试练习的想法。

除了那篇论文,在发布新模型的所有论文中,经常有 DL 架构和 GBM 之间的综合比较。我对其中一些出版物的主要警告如下:我经常无法在论文中重现结果(这当然可能是我的错),我有时发现优化 DL 模型的努力比优化 GBM 的努力要多一些。最后但并非最不重要的一点是,有些论文中的结果表格缺乏一致性,有时令人困惑。例如,论文 A 将使用 DL 模型 A 来查找性能优于所有 GBM 的,通常为XGBoostCatboostLightGBM。然后,论文 B 将推出新的 DL 模型 B,其性能也将优于所有 GBM,但在他们的论文中,结果表明模型 A 不再优于 GBM。

考虑到所有这些,我决定使用 py torch-wide deep 并运行一组相当大的实验,包括针对表格数据和[LightGBM](https://lightgbm.readthedocs.io/en/latest/#)的不同 DL 模型。

在我继续之前,让我评论一下回购中的代码“质量”。人们必须记住,这里的目标是以严格的方式测试算法,而不是编写生产代码。如果你想看更好的代码,你可以去 pytorch-widedeep 或者我的其他一些回复。只是说以防一些“纯粹主义者”试图浪费宇宙的时间。

2.数据集和模型

对于这里的实验,我使用了四个数据集和四个 DL 模型。

2.1 数据集

  1. 成人普查(二元分类)
  2. 银行营销(二元分类)
  3. 纽约市出租车乘坐时长(回归)
  4. 脸书评论卷(回归)

在 repo 中的 bash 脚本get_data.sh有你需要的所有信息来获取这些数据集,以防你想自己探索它们。当然,用于运行实验和再现结果的所有代码也可以在该报告中获得。

以下是关于数据集的一些基本信息:

**表 1** 。本帖中使用的数据集的基本信息

我选择这些数据集是有原因的。

总的来说,我寻找的是二元、多类和回归数据集,这些数据集如果不是由分类特征主导,也有很多。这是因为根据我的经验,表格数据的 DL 模型在存在分类特征的大型数据集中变得更有用和更有竞争力(尽管[5]表明编码数字特征也能获得更好的结果),而且如果这些分类特征有很多类别。这是因为嵌入获得了更重要的价值,即我们学习那些分类特征的表示,这些分类特征编码了与所有其他特征以及特定数据集的目标的关系。请注意,使用 GBMs 时不会发生这种情况。即使使用了目标编码,实际上也没有太多的学习元素(当然仍然有用)。

当然,我们可以获取以数字特征为主的数据集,并以某种方式将它们绑定起来,使其成为分类数据集。然而,这对我来说似乎有点太“强迫”了。本着让这篇文章的内容尽可能接*真实用例的想法,我很难想到许多“真实世界”的场景,在这些场景中,我们被提供以数字特征为主的数据集,然后这些数据集在被馈送给算法之前被转换/分类。换句话说,我不想考虑那些为了比较 GBM 和 DL 模型而必须将数字特征绑定到分类中的数据集。

另一方面,我还寻找我已经熟悉的数据集,或者不需要太多功能工程就可以将数据传递给模型的数据集。这样,我也许可以在这方面节省一些时间,把更多的精力放在实验上,因为我打算进行大量的实验。最后,我寻找在某种程度上尽可能与“真实世界”中的数据集相似的数据集,但是具有易于处理的大小,以便我可以在合理的时间框架内进行实验。

虽然我确实找到了适合二元分类和回归的数据集,但我没有找到我特别喜欢的多类分类数据集(如果有人有任何建议,请在下面发表评论,我很高兴尝试一下)。也许将来我会包括 CoverType 数据集,但是是在 UCI ML 存储库中的那个,而不是 Kaggle 的*衡版本。现在,我将继续上面列举的四个。让我简单评论一下每个数据集。

我会将成人人口普查数据集称为“最简单的数据集”,在这个意义上,简单的模型(即朴素贝叶斯分类器)已经可以在没有任何特征工程的情况下达到大约 84%的准确率。就个人而言,我通常在现实世界中找不到这些好的数据集。然而,它是 ML 教程、帖子等最受欢迎和最著名的数据集之一,我最终决定将它包括在内。

银行营销数据集也广为人知。这些数据与基于电话的直接营销活动相关,试图预测客户是否会订购产品。在这种情况下,提及几个相关方面是很重要的。首先我使用了原始数据集,这个数据集有点不*衡(正负类比是 0.127)。第二,你可能会四处看看,发现有些人获得了比我稍后将展示的更好的结果。我发现的所有这样的案例要么使用 Kaggle 的*衡数据集,一个叫做duration的特性,要么两者都用。duration特性指的是通话的持续时间,是在通话后才知道的,对目标影响很大。因此,我没有在实验中使用它。这个数据集比成人数据集更像一个真实的用例,因为数据是不*衡的,预测根本不是一件容易的事情。尽管如此,数据量还是很小,也没有那么不*衡。

纽约市出租车乘车时长数据集也是众所周知的,是我使用的所有数据集中最大的。我们的目标是预测纽约市出租车出行的总时长。我没有从 Kaggle 网站获取数据集,而是从这里手动下载了一个扩展版本,那里所有的功能工程都已经完成。

最后,脸书评论卷数据集是另一个理想的候选,因为它有一个很好的大小,并且所有的特征工程都是为我做的。我们的目标是预测帖子将收到的评论量。事实上,这个数据集最初是用来比较决策树和神经网络的。数据集和预处理的详细描述可以在原始出版物【6】中找到。特别是,我在本文的实验中使用了他们的训练变量— 5 数据集,它有 199029 行和 54 列。

在数据输入算法之前,数据准备步骤的所有代码可以在这里找到

2.2.DL 模型

正如我在帖子前面提到的,所有 DL 模型都是通过 pytorch-widedeep 运行的。该库提供了四个宽深模型组件 : widedeeptabulardeeptextdeepimage。让我简要地评论一下其中的每一个。更多详情,请参见伴随帖子、文档或源代码本身。

  1. wide:这只是一个通过Embedding层实现的线性模型
  2. deeptabular:该组件将处理“标准”表格数据(即分类和数字列),有 4 个选项:

2.1 TabMlp:简单的标准 MLP。例如,非常类似于 fastai 库中的表格 api 实现。

2.2 TabResnet:类似于 MLP,但是我没有使用密集层,而是使用 Resnet 块。

2.3 Tabnet [7]:这是一个非常有趣的实现。很难用几句话来解释,因此我强烈建议阅读论文。Tabnet旨在与 GBMs 竞争,并通过特性重要性提供模型可解释性。pytorch-widedeepTabnet的实现完全是基于 dreamquark-ai 的家伙们的奇妙实现,因此,所有的功劳都归于他们。简单地说,我对它进行了修改,使其能够在一个宽而深的框架内工作,并添加了几个额外的功能,比如 GLU 块中的内部丢失和不使用 ghost 批处理规范化的可能性[8]。

注意,最初的实现允许分两个阶段进行训练。首先通过标准编码器-解码器方法进行自我监督训练,然后仅使用编码器进行监督训练或微调。在pytorch-widedeep只实施监督培训(即编码器)。作者表明,无监督预训练主要在低数据量区域或当未标记数据集比标记数据集大得多时提高性能。因此,如果你处于其中一种场景中(或者简单地作为一个通用语句),你最好使用 dreamquark-ai 的实现。

2.4.TabTransformer【9】:这与TabResnet类似,但是作者使用了 Transformer [10]块,而不是 Resnet 块。与Tabnet的情况类似,TabTransformer允许两个阶段的训练过程,无监督的预训练,然后是有监督的训练或微调。pytorch-widedeepTabTransformer的实施旨在以“标准”方式使用,即监督培训。注意与塞尔詹的结果一致。ark,Tomas Pfister 针对Tabnet,作者发现,无监督预训练主要在低数据量范围内或当未标记数据集远大于标记数据集时提高性能。pytorch-widedeep中可用的TabTransformer实现部分基于自动引导库中的实现和王飞这里的实现。

3.deeptext:由一堆 rnn(lstm 或 gru)组成的标准文本分类器/回归器。此外,还可以选择在 rnn 堆栈之上添加一组密集层,以及其他一些额外功能。

4.deepimage:使用预训练网络(特别是 ResNets)或 4 个卷积层序列的标准图像分类器/回归器。此外,还可以选择在 CNN 和其他一些额外功能的基础上添加一组密集层。

2.3.为什么是LightGBM

如果你和我一起工作过,或者甚至和我聊过一些 ML 项目,你会知道我最喜欢的算法之一是[LightGBM](https://lightgbm.readthedocs.io/en/latest/)。我广泛使用的是。事实上,我最*生产的 3 毫升系统都依赖于LightGBM。即使不比它的兄弟姐妹(如[XGBoost](https://xgboost.readthedocs.io/en/latest/)[CatBoost](https://catboost.ai/))更好,它的表现也类似,速度明显更快,并支持分类特征(见此处)。尽管在支持分类特性方面CatBoost可能是更好的解决方案)。此外,还提供了 GBM 通常的灵活性和性能。

2.4.实验设置和其他注意事项

正如我在帖子前面提到的,我针对四个数据集运行了许多实验(并非所有实验都被记录和/或发布),重点关注可用于deeptabular组件的不同模型。所有的实验运行都可以在这里的 repo 中找到。

实验不仅考虑了模型的不同参数(即单元数量、层数等..)还包括不同的优化器、学习率调度器和训练过程。例如,所有实验都提前停止,大多数情况下有 30 个周期的patience。我使用了三种不同的优化器(Adam【11】、AdamW【12】和RAdam【13】)和三种不同的学习率调度器(ReduceLROnPlateauOneCycleLR【14】、CyclicLR【15】)。以下命令对应于一个实验运行:

python adult/adult_tabmlp.py --mlp_hidden_dims [100,50] --mlp_dropout 0.2 --optimizer Adam --early_stop_patience 30 --lr_scheduler CyclicLR --base_lr 5e-4 --max_lr 0.01 --n_cycles 10 --n_epochs 100 --save_results

上面的命令将为成人数据集运行一个TabMlp模型。大多数args都很容易理解。也许唯一值得一提的是,这个特定的实验是用一个CyclicLR调度程序运行的,其中学习率在 0.0005 到 0.01 之间振荡,在 100 个时期内振荡 10 次(即每 10 个时期一个周期)。

值得一提的是,在运行实验时,我假设在 DL 模型参数和训练设置中有一个固有的层次结构。因此,我没有一次优化所有的参数,而是选择了那些我认为更相关的参数,并运行了重现该层次结构的实验。例如,当运行一个简单的MLP时,我假设层中神经元的数量是一个比我是否在最后一层使用BatchNorm更重要的参数。这可能是,或者肯定是,最好的办法是一次优化所有参数,但遵循这种“分层”方法也让我感觉到改变一些单独的参数如何影响模型的性能。尽管如此,每个模型和每个数据集*均运行了大约 100 次实验,所以探索是相对详尽的(只是相对而言)。

另一方面,LightGBM通过使用[Optuna](https://optuna.org/)【16】、[Hyperopt](https://github.com/hyperopt/hyperopt)【17】或两者,并选择导致最佳度量的参数来优化。所有的代码都可以在这里找到。注意,实验和 repo 中的代码代表了一个关于如何使用pytorch-widedeep(如果你想使用库)的非常详细和全面的教程。

值得一提的是,在进行实验时,DL 模型和LightGBM的早期停止标准都是基于验证损失。或者,可以监控一个指标,例如 f1 分数的准确性。请注意,精确度(或 f1)和损失并不一定完全成反比。可能存在边缘情况,其中算法确实不确定某些预测(即,预测接*度量阈值,导致高损失值),但最终做出正确的预测(更高的准确度)。当然,在理想情况下,我们希望算法是确定的,并做出正确的预测,但你知道,现实世界是混乱和嘈杂的。尽管如此,出于好奇,我尝试在一些实验中监控指标。总的来说,我确实发现结果与那些监控损失值一致,尽管在某些情况下可以实现稍微好一点的指标。

另一条相关信息与用于表示分类特征的嵌入数量有关。可以想象,这里的可能性是无限的,我必须找到一种方法,在所有实验中始终自动化这个过程。为此,我决定使用 fastai 的经验法则。对于给定的分类特征,嵌入的数量将是:

Eq 1。Fastai 的嵌入数经验法则

例外的是TabTransformerTabTransformer将分类特征视为序列的一部分(即上下文相关),其中序列顺序无关紧要,即不需要位置编码。因此,它们不是“一个挨着一个”堆叠,而是“一个在另一个上面”堆叠。这意味着所有分类特征必须具有相同的维度。请注意,当数据集中的分类要素有大量类别时,这有点不方便。

例如,假设我们有一个只有 2 个分类特征的数据集,分别有 50 个和 3 个不同的类别。例如,虽然使用 16 维的嵌入对于前者来说似乎是合适的,但是对于后者来说,它确实看起来像是一种“过度表示”。人们仍然可以使用 fastai 的经验法则,用更低的维度填充嵌入,但这将意味着一些注意力头在整个训练过程中会关注零/无,这对我来说似乎是一种浪费。尽管有这种潜在的“浪费”,我正在考虑将它作为pytorch-widedeepTabTransformer实施的一个选项。与此同时," T8 "所有的 " TabTransformer实验都使用一个附加的设置来运行,其中带有少量类别的分类特征通过wide组件。

最后,对于所有实验,我使用 80%的数据进行训练,10%用于验证/参数调整。然后,在最后一次训练运行中将这两个数据集合并,并在剩余的 10%的数据上测试该算法。除非有时间成分,否则数据集是随机分割的。在这些情况下,我使用了按时间顺序排列的训练/测试分割(注意,在脸书评论卷数据集的情况下,我没有使用论文中使用的测试集。所有训练、验证和测试数据集都是本文中描述的 Variant-5 数据集的拆分。

就这些了,事不宜迟,我们来看结果。

3.结果

前面几节提供了这个“项目”的背景和我所做的实验的细节。在本节中,我将简单地展示所有数据和模型组合的前 5 个结果,并在我认为必要时加上一些注释。所有实验结果的完整表格可以在这里找到。

3.1 成人人口普查数据集

3.1.1 TabMlp

表二。使用TabMlp获得的成人人口普查数据集的结果。

也许要做的第一个评论与列/参数有关。很容易理解,并非所有的参数/列都适用于每个实验/行。例如,base_lrmax_lrdiv_factorfinal_div_factor等参数/栏仅在学习率调度器为CyclicLROneCycleLR时适用。

另一方面,MLP 的密集图层是使用与fastai库中非常相似的方法构建的。这种方法在 MLP 的每个密集层内的操作方面提供了灵活性(详见。在该上下文中,三列mlp_batchnorm_lastmlp_linear_first设置这些操作发生的顺序。例如,如果对于一个给定的密集层,我们设置了mlp_linear_first = True,那么实现的密集层将如下所示:[LIN -> ACT -> DP]。另一方面,如果mlp_linear_first = False,则密集层将按照以下顺序执行操作:[DP -> LIN -> ACT]

在成人普查数据集的情况下,循环学习率调度器产生非常好的结果。事实上,具有足够参数的一个周期的学习率将在仅仅一个时期内导致可接受的验证损失(假设批量足够小),这可能说明该数据集不是特别困难。尽管如此,最好的结果(可以忽略不计)是用ReduceLROnPlateau学习速率调度程序获得的。这实际上在不同数据集的所有实验中都很常见,也与我在许多不同场景中运行 DL 模型的经验相一致,无论是表格数据还是文本。使用 10 个时期的patience运行ReduceLROnPlateau学习率调度程序。这与 30 个周期的EarlyStopping耐心一起意味着,当使用ReduceLROnPlateau时,在实验被迫停止之前,学习率将降低 3 倍。

关于实验设置、模型实现和每个参数/列背后含义的完整细节,请查看两个pytorch-widedeep文档和实验报告。

3.1.2 TabResnet

**表 3** 。使用TabResnet获得的成人数据集的结果。

3 中的block_dim = same表示由密集层组成的 Resnet 块具有与传入嵌入相同的维度(参见此处的了解实施细节)。

另一方面,TabResnet模型提供了在 Resnet 块“之上”使用 MLP 的可能性。当mlp_hidden_dims = None指示没有使用 MLP,并且最后一个 Resnet 块的输出被直接“插入”到输出神经元中时。因此,如表 3 所示,使用TabResnet获得的前 5 个结果对应于没有 MLP 的架构。因此,所有与 MLP 相关的参数/列对于这些实验都是多余的。

我发现有趣的是,无论是Adam还是AdamW,最好的结果都是使用OneCycleLR获得的。当使用这个调度程序时,我通常将 epochs 的数量设置在 1 到 10 之间。通常情况下,我会在少量的时段(\(\leq 5\))和小批量的情况下获得最佳结果,这意味着与使用大批量的情况相反,学习率的增加/减少会更加缓慢(即,分布在更多的步骤上)。最后注意,参数/列n_cycles仅适用于CyclicLR调度程序。因为它没有在任何一个前 5 名的实验中使用,所以在表 3 中可以忽略。

3.1.3 Tabnet

**表 4** 。使用Tabnet获得的成人数据集的结果。

Tabnet最*受到了一些关注,因为它与 GBM 竞争,甚至超过了 GBM。此外,它是一个非常优雅的实现,通过使用注意力机制获得的特征重要性来提供模型可解释性。

事实是,对于成人人口普查数据集,我在验证集上获得了最差的损失值(但正如我们将在后面看到的,不是最差的度量)。也许我只是错过了能够带来更好结果的精确的参数集。然而,值得强调的是,我对Tabnet的研究与其他 3 种模型的研究具有相同的详细程度。

另一方面,有趣的是,在所有运行的实验中,在没有重影批次标准化的情况下始终获得最佳结果。因此,表 4 中的参数/列virtual_batch_size可以忽略。类似地,由于最佳结果都是使用ReduceLROnPlateau获得的,所以在表 4 中可以忽略与循环学习率调度器相关的所有参数。

最后,与我过去运行的一些实验一致,使用RAdam获得的最佳结果通常涉及相对较高的学习率。

3.1.4 TabTransformer

表 5 。使用TabTransformer获得的成人人口普查数据集的结果。

与所有以前的模型一样,如果您想要了解每个参数/列的详细含义,请查看[源代码]本身的[文档]。

或许值得一提的是,当前馈隐藏尺寸(ff_hidden_dim)被设置为NaN时,模型将默认为等于 4 倍输入嵌入尺寸的ff_hidden_dim值(在表中所示的所有实验/行中为 16)。这将产生一个尺寸为[ff_input_dim -> 4 * ff_input_dim -> ff_input_dim]的前馈层。类似地,当mlp_hidden_dims = None时,模型将默认为输入尺寸的 4 倍,导致尺寸[mlp_input_dim -> 4 * mlp_input_dim -> 2* mlp_input_dim -> output_dim]的 MLP。

此外,如前所述,TabTransformer也是用包括wide组件的设置运行的。这由with_wide参数指定。

值得注意的是,最佳损失值与其他 DL 模型的损失值相似,通常使用RAdam优化器获得。

3.1.5 DL 与LightGBM

看完每个 DL 模型获得的结果后,这是关键时刻,让我们看看 DL 结果与使用LightGBM获得的结果相比如何。

表六。使用四个 DL 模型和LightGBM获得的成人人口普查数据集的结果。runtime单位是秒。

让我再次强调,表 6 中显示的指标都是针对测试数据集获得的。runtime列显示了使用验证期间获得的最佳参数的最终训练数据集(即包含 90%数据的数据集)的训练时间,单位为秒。DL 模型在 AWS 上的p2.xlarge实例上运行,所有的LightGBM实验都在我的 Mac 上运行。

它们是值得评论的几个方面。首先,所有 DL 模型获得的结果与LightGBM的结果相当,但并不比后者更好。第二,表现最好的 DL 模型(很少)是最简单的模型TabMlp。最后,使用LightGBM时的训练时间比使用任何 DL 型号都要短得多。

3.2 银行营销数据集

上一节中的大多数注释适用于本节中显示的表。

请注意,正如我在帖子前面提到的,银行营销数据集有点不*衡。因此,我还使用焦点损失【18】(可通过参数或损失函数输入在pytorch_widedeep中访问)进行了一些实验。见此处。总的来说,所获得的结果与没有焦点损失的结果相似,但并不比没有焦点损失的结果好。这与我在其他数据集上的经验一致,我发现当数据集高度不*衡时(例如,大约 2%的正类与负类比率),焦点损失会导致明显更好的结果。

3.2.1 TabMlp

表 7 。使用TabMlp获得的银行营销数据集的结果。

3.2.2 TabResnet

表 8 。使用TabResnet获得的银行营销数据集的结果。

同样,非常有趣的是,RAdam optimizer 和OneCycleLR为这个 DL 模型带来了一些最好的结果。

3.2.3 Tabnet

表 9 。使用Tabnet获得的银行营销数据集的结果。

注意,在这种情况下,使用Tabnet获得的前 5 个结果都具有相对较高的学习率值(lr = 0.03)。此外,与成人人口普查数据集的情况类似,Tabnet产生最差的验证损失值。

3.2.4 TabTransformer

表 10 。使用TabTransformer获得的银行营销数据集的结果。

或许值得注意的是,与之前的一些结果一致,这里使用RAdam获得的最佳结果涉及相对较高的学习率(与使用AdamAdamW获得的结果相比是 10 倍)。)

3.2.5 DL 与LightGBM

表 11 。使用四个 DL 模型和 LightGBM 获得的银行营销数据集的结果。

我必须承认,表 11 中显示的结果起初让我感到惊讶(至少可以这么说)。我再次运行了几个 DL 模型,并多次运行LightGBM进行双重检查,最终得出结论(剧透警告)这将是我在本文中运行的所有实验中唯一一个 DL 模型表现优于LightGBM的情况。事实上,如果我们以我的工作经验加入这里的实验,这是我第二次发现 DL 模型比LightGBM表现得更好(稍后会有更多)。此外,使用TabResnetTabTransformer获得的改进非常显著,如果这是一个“真实世界”的例子,人们可能会考虑使用 DL 模型,并接受运行时间和成功指标之间的折衷。

当然,您可以更深入地研究LightGBM,设置样品重量,甚至使用自定义损耗,但 DL 型号也是如此。因此,总的来说,我认为这种比较是公*的。然而,我感到非常惊讶,我认为我可能在代码中有一个我没有发现的错误。因此,如果有人在某个时候检查代码,发现确实有 bug,请告诉我🙂。

最后,有人可能会对Tabnet的表现感到失望,就像我一样。有可能我没有正确地实现它,尽管代码完全基于 dreamquark-ai 的实现(全部归功于他们),并且当用更简单的数据集测试时,我获得了与 GBM 类似的结果。我发现Tabnet是一个非常优雅的实现,不知何故我相信它应该表现得更好。我将在结论部分回到这一点。

3.3 纽约市出租车行程持续时间

正如我前面提到的,这是最大的数据集,因此,我试验了更大的批量。虽然这可能会稍微改变一些个别的结果,但我相信这不会改变本节的整体结论。

3.3.1 TabMlp

表 12 。使用TabMlp获得的纽约市出租车行程持续时间数据集的结果。

这种情况下的验证损失是MSE。验证集中目标变量的标准偏差(std下文)约为 599。假设 std 是我们在预测期望值时获得的RMSE,我们可以看到这不是一个非常强大的模型,即预测出租车行程持续时间的任务确实相对具有挑战性。

再来看看其他 DL 车型表现如何。

3.3.2 TabResnet

表 13 。使用TabResnet获得的纽约市出租车行程持续时间数据集的结果。

3.3.3 Tabnet

表 14 。使用Tabnet获得的纽约市出租车行程持续时间数据集的结果。

3.3.4 TabTransformer

表 15 。使用TabTransformer获得的纽约市出租车行程持续时间数据集的结果。

3 . 3 . 5 D1 vsLightGBM

表 16 。使用四个 DL 模型和 LightGBM 获得的纽约市出租车出行持续时间数据集的结果。

在这种情况下,TabTransformerTabnet是性能最差的型号。正如我前面提到的,我将在结论部分思考潜在的原因。

3.4 脸书评论卷

这是我们将在本帖中讨论的四个数据集的最后一个,第二个回归问题。

3.4.1 TabMlp

表 17 。使用TabMlp获得的脸书评论量数据集的结果。

与纽约市出租车行程持续时间的情况一样,验证损失是MSE损失。在脸书评论量数据集的情况下,目标变量的std约为 13。因此,按照同样的推理,我们可以看到,使用这个特定的数据集来预测脸书评论量的任务是具有挑战性的

再来看看其他 DL 车型表现如何。

3.4.2 TabResnet

表 18 。使用TabResnet获得的脸书评论量数据集的结果。

3.4.3 Tabnet

表 19 。使用Tabnet获得的脸书评论量数据集的结果。

3.4.4 TabTransformer

表 20 。使用TabTransformer获得的脸书评论量数据集的结果。

3.4.5 DL 与LightGBM

表 21 。使用四个 DL 模型和 LightGBM 获得的脸书评论量数据集的结果。

4.摘要

我已经使用了四个数据集,运行了超过 1500 次实验(意味着在参数设置下运行),将四个 DL 模型与LightGBM进行了比较。这是一些结果的总结。

  • 赢了,而且从来没有打架

除了一个例外,LightGBM比 DL 模型表现得更好,而这个例外恰恰就是例外。除了这里运行和讨论的实验之外,我还可以添加两个在我工作的公司中使用 DL 处理表格数据的场合。具体来说,这里称为TabMlp的型号在一种情况下带有wide组件,而在另一种情况下单独存在。

在 2016 年发表广受欢迎的Wide and Deep【19】论文后不久,Wide & Deep 模型被用于推荐算法的上下文中。当时,我使用 XGBoost 来预测一个兴趣度,并根据这个兴趣度对报价进行排名。然后用[Keras](https://keras.io/)实现的宽深度模型获得了比 XGBoost 略好的 MAP 和 NDCG(几乎相同的度量,尽管略低,当仅使用深度组件时获得)。考虑到在生产过程中需要考虑的其他因素,我们最终使用了 XGBoost。

在第二种情况下,一个更*的项目,TabMlp本身获得了非常相似的,但仍然比使用LightGBM获得的 RMSE 和 R2 值低。尽管TabMLP的预测没有被直接使用,但我们发现这些嵌入对一些额外的项目很有用,我们围绕TabMlp构建了一个生产系统。

到目前为止,我关注的是通过成功指标衡量的绩效。然而,当涉及到训练(和预测)时间时,差异是如此之大,以至于在这个阶段,这些算法中的一些只是用于研究目的和/或 kaggle 比赛。不要误会我的意思,你只是通过挑战当前的解决方案和既定的概念来推动一个行业的技术发展。我只是说,在现阶段,在生产环境中,很难想象围绕这些算法构建一个健壮的系统。这就是我写“从来没有打架”的原因。当您上线时,通常不仅仅是成功指标,还有速度和弹性。总的来说,在我看来,表格数据的 DL 模型离正常插入生产系统还有点远(但请阅读下文)。

最后,你可能会在这里或那里读到,通过适当的功能工程,噪声消除,*衡和“谁知道还有什么”DL 模型优于 GBM。事实是,在我的经历中实际上是相反的。当一个人设法设计出好的、强大的特性时,GBM 甚至比 DL 模型表现得更好。这也和最*一些比赛的结果一致。例如,在 2020 年的 RecSys 挑战赛中,NVIDIA 的家伙使用聪明的工程(例如,面向目标的编码)“插入”到类固醇(或者更好,GPU)的 XGBoost 中而获胜。我不确定使用这些特性和 DL 模型是否真的会改善他们的结果。

总的来说,如果我加入这篇文章中的结果,加上我发现在行业中真实数据集的表格数据上尝试 DL 模型,我只能得出结论,就整体性能而言,表格数据的 DL 模型“还不太好”。

  • TabNetTabTransformer

一个相当令人惊讶的结果是Tabnet的糟糕表现,也许在较小的程度上,TabTransformer也是如此。

一种可能是,我还没有找到导致良好度量的正确的参数集。事实上,使用TabnetTabTransformer时的过拟合量非常显著,高于使用TabResnetTabMlp时的过拟合量。这使我相信,如果我找到一组更好的正则化参数,或者简单地对每个分类特征使用不同数量的嵌入,我可能能够改进上表中显示的结果。然而,我也应该说,考虑到这些算法的良好接收和我获得的差的结果,我在尝试一些额外的参数时更加强调了一点。不幸的是,我的尝试都没有带来显著的改善。

当然,第二种可能性是在pytorch-widedeep的实现是错误的。我想随着我不断发布版本和使用这个包,我会发现这一点的。

总的来说,我发现Tabnet是表现最差的(也是最慢的),我肯定会在接下来的几周里投入一些额外的时间来看看这是否与输入参数有关。

  • 简单胜过复杂。

有趣的是,总的来说,实现与LightGBM相似性能的 DL 算法是一个简单的 MLP。在我写这篇文章的时候,我想知道这是否与正在兴起的将 MLP 带回来的趋势有关(例如,[20]、[21]或[22]),更复杂模型的出现只是炒作的结果,而不是对当前解决方案的适当探索。

当然,对于更复杂的模型,探索和超参数优化的空间更大。虽然这是我打算继续探索的东西,但在空间和时间中有一个时刻,人们会怀疑"这真的值得吗?”。

让我们看看我能否在下一节回答这个问题

5 结论

当我开始考虑这篇文章时,我已经知道 DL 模型对LightGBM来说不是真正的挑战。如果我们只关注性能指标和运行时间,唯一可能的结论是,在现实环境中,用于表格数据的 DL 模型仍然不是 GBM 的竞争对手。然而,在行业/市场的现阶段,这真的是要回答的问题吗?我不这么认为。

这不是竞争,也不应该是,这应该是一个联盟。要回答的问题是:“表格数据的 DL 模型如何在行业中提供帮助并补充当前的系统”。让我们思考一下这个问题。

根据我的经验,表格数据上的 DL 模型在涉及许多分类特征的大型数据集上表现最佳,这些数据集本身有许多类别。在这些情况下,人们可以尝试 DL 模型,最初的目标是直接使用预测。然而,即使预测最终没有被使用,嵌入也包含了大量有用的信息。关于每个分类特征如何相互作用的信息以及关于每个分类特征如何与目标变量相关的信息。这些嵌入可用于许多附加产品。

例如,假设您有一个数据集,其中包含数千种品牌及其相应产品的元数据和价格。你的任务是预测价格如何随时间变化(即预测价格)。分类特性brand的嵌入将为您提供特定品牌如何与数据集中的其余列和目标(价格)相关的信息。换句话说,如果给定一个品牌,你找到了由嵌入邻*度定义的最接*的品牌,你将“自然地”直接找到给定空间内的竞争对手(假设数据集代表市场)。

此外,GBM 不允许迁移学习,但 DL 模型允许。此外,正如在TabNetTabTransformer论文中提到的,自我监督训练在数据量低或未标记数据集比标记数据集大得多的情况下会产生更好的性能。因此,在一些场景中,DL 模型非常有用。

例如,让我们假设你在一个国家有一个关于给定问题的大型数据集,但是在另一个国家有一个关于完全相同问题的小得多的数据集。我们还假设数据集在列方面非常相似。人们可以使用大数据集训练 DL 模型,并将学习“转移”到第二个更小的数据集,希望获得比仅使用小数据集高得多的性能。

还有一些我能想到的其他场景,但我会把它留在这里。总的来说,我只是想说明,如果你来这里是为了享受 GBM 比 DL 模型表现更好的事实,我希望你喜欢这一过程(并开始在一个好的治疗师那里思考),但在我看来,这不是重点。

就指标而言,GBM 比 DL 模型表现更好,这是正确的,但后者带来了 GBM 所没有的一些功能,因此是对它们的完美补充。

6.未来的工作

几个月前我就开始考虑这个帖子了。然后,一些其他的事情在我的生活中占据了优先地位(加上大量的工作),这变成了一个有点漫长的旅程。我现在希望能从我团队中非常聪明的人那里得到一点帮助,并改进 repo 中的表格 vs DL 代码,也许自动化一些过程,这样我就可以在未来轻松地添加更多数据集。

这也是对[pytorch-widedeep](https://github.com/jrzaurin/pytorch-widedeep)图书馆的一个很好的测试(如果你喜欢它,或者觉得它有用,请给它一颗星😊).本帖所有链接都指向回购中的tabnet分支,是最新的。在接下来的几天里,我将合并并发布 v1 版本的包,然后更新链接和帖子。从那里,有一系列的算法,我们想带来(如圣),也增加了一些不同形式的训练。

除了向库中添加更多的算法或改进基准代码之外,我想用最后一个想法来结束本文。正如我在文章开头提到的,论文之间有不一致的地方。不同的论文会发现不同的结果为所有算法考虑,GBMs 或基于 DL。当你阅读它们的时候,你会感觉到有一种急切的心情,想要发表一些获得成功的东西。对于像我这样的背景与计算机科学不同的人来说,这在某种意义上让我想起了我作为天文学家的日子。几年后,我发现我所在领域的大多数出版物都不是很好,但是因为评判你的标准只是出版物和引用,所以你可以发表任何东西,而且越快越好。

在这个阶段,抛开出版物和引用不谈,我认为我们中的一些人以及一些公司(以便我们可以使用实际的真实数据集)有机会合作,并为表格数据正确地基准测试 DL 算法。我相信这些算法在行业中的潜力是巨大的,通过适当的基准测试,我们不仅可以了解它们在哪里表现得更好,还可以了解如何更有效地使用它们。

就是这样!如果你是在这里做的,我希望你喜欢和/或觉得这有用。

参考

[1]表格数据:深度学习不是你需要的全部:拉维德·施瓦兹-齐夫,阿米泰·艾蒙,2021, arxiv:2106.03253

[2] XGBoost:一个可扩展的树提升系统。陈天琦,卡洛斯·盖斯特林 2016, arXiv:1603.02754

[3] CatBoost:具有分类特征的无偏增强。柳德米拉·普罗霍伦科娃,格莱布·古塞夫,亚历山大·沃罗贝夫,安娜·维罗妮卡·多罗古什,安德烈·谷林, arXiv:1706.09516

[4] LightGBM:一种高效的梯度推进决策树。柯,,托马斯·芬利,王泰峰,2017,第 31 届神经信息处理系统会议

[5] SAINT:通过行注意和对比预训练改进用于表格数据的神经网络。Gowthami Somepalli,Micah Goldblum,Avi Schwarzschild,C. Bayan Bruss,Tom Goldstein,2021, arXiv:2106.01342

[6]使用神经网络和决策树进行评论量预测,Kamaljot Singh,Ranjeet Kaur,2015 年第 17 届 UKSIM-AMSS 建模与仿真国际会议。

[7] TabNet:专注的可解释表格学习,塞尔詹·奥·阿里克,托马斯·普菲斯特, arXiv:1908.07442v5

[8]训练更长,推广更好:缩小神经网络大批量训练中的推广差距。Elad Hoffer,Itay Hubara 和 Daniel Soudry,2017 年, arXiv:1705.08741

[9] TabTransformer:使用上下文嵌入的表格数据建模。黄鑫,阿什什·赫坦,米兰·茨维特科维奇,佐哈尔·卡尔宁,2020。 arXiv:2012.06678v1

[10]关注是你所需要的一切,Ashish Vaswani,Noam Shazeer,Niki Parmar 等人,2017 年。 arXiv:1706.03762v5

[11] Adam:一种随机优化方法,Diederik P. Kingma,Jimmy Ba,2014, arXiv:1412.6980

[12]解耦权重衰减正则化,Ilya Loshchilov,Frank Hutter,2017 年。 arXiv:1711.05101

[13]关于自适应学习率的方差及其超越,,,蒋鹏程,,陈,,高剑锋,韩家伟,2019, arxiv.org:1908.03265

[14]训练神经网络的循环学习率,Leslie N. Smith,2017, arxiv.org:1506.01186

[15]超收敛:使用大学习率的非常快速的神经网络训练,Leslie N. Smith,Nicholay Topin,2017, arxiv.org:1708.0712

[16] Optuna:下一代超参数优化框架。秋叶拓哉,佐野正太郎,柳濑俊彦,太田赳,小山正典,2019, arXiv:1907.10902

[17]超参数优化算法,James Bergstra,Rémi Bardenet,Yoshua Bengio,Balázs Kégl,2011 年,第 25 届神经信息处理系统会议

[18]密集物体探测的焦点损失,宗-林逸,普里亚·戈亚尔,罗斯·吉斯克,明凯·何,彼得·多拉尔,2017 年, arxiv.org:1708.02002

[19]推荐系统的广度和深度学习,Heng-Tze Cheng,Levent Koc,Jeremiah Harmsen 等人,2016, arxiv.org:1606.07792

[20] FNet:用傅立叶变换混合令牌,李中清-索普,约书亚·安斯利,伊利亚·埃克斯坦,圣地亚哥·翁塔农,2021, arxiv.org:2105.03824

[21]关注 MLPs,,戴子航,David R. So,Quoc 诉乐,2021,【arxiv.org:2105.08050

[22] ResMLP:利用数据有效训练的图像分类前馈网络,Hugo Touvron,Piotr Bojanowski,Mathilde Caron 等人,2021, arxiv.org:2105.03404

pytsviz:使用 Plotly 和 Python 的交互式时间序列图表

原文:https://towardsdatascience.com/pytsviz-interactive-time-series-charts-with-plotly-and-python-68cf30d9f822?source=collection_archive---------23-----------------------

引入新的库来加速时间序列分析

艾萨克·史密斯在 Unsplash 上拍摄的照片

pytsviz 的案例

在数十亿参数模型主宰计算机视觉和 NLP 的时代,人们认为“传统”数据科学和机器学习已经死亡是情有可原的。

幸运的是,古老的没有免费的午餐定理仍然在现代研究中找到实验证实:在表格数据集中,基于树的机器学习模型,如 XGBoost,可以轻松胜过神经网络——例如,见 Ravid shw artz-齐夫,Amitai Armon, 表格数据:深度学习不是你需要的全部 ,arXiv,2021。

此外,在商业应用中,结果的准确性并不总是最重要的。可以通过简单的可视化或探索性分析来收集有价值的信息。通常,在一个合适的模型投入生产之前,这些知识会产生可操作的见解。

这为探索性分析和数据可视化的有效工具提供了论据。Python 已经有很多这样的工具:matplotlib、seaborn 和 plotly 只是一些例子。这些库很棒,但它们被设计成通用的:为了生成特定的图表,数据科学家必须建立在它们的原语之上。

例如,当处理时间序列数据时,就会发生这种情况。每次分析的开始包括创建一些有用的图表。在 xtream 中,我们多次构建了这样的图形,最终我们开发了一个小型库来简化这个过程。

如果它对我们有用,对其他人也可能有用。

那么,为什么不开源呢?

使用 pytsviz

https://github.com/xtreamsrl/pytsviz

为了展示 pytsviz 的一些特性,我们将假设在一个类似于文档中所示的的笔记本上编写代码。

首先,让我们导入库并获取一些数据。我们将使用太阳黑子的单变量序列,但 pytsviz 也可以很好地处理多变量序列。然后,我们可以立即利用 pytsviz 创建一个折线图。

太阳黑子发生的线图。作者图片。

不是很令人印象深刻,是吗?

然而,我们注意到该系列是嘈杂的。出现了明显的季节性,但是被高频噪音掩盖了。因此,移动*均线有助于*滑序列并使模式看起来更清晰。

太阳黑子每月出现次数的年移动*均值。作者图片。

红线清楚地描述了一个大约 9 到 11 年长的周期。

pytsviz 支持一系列转换,包括对数、Box-Cox 等。

如果我们想深入挖掘序列的结构,一个好的选择是使用 ACF、PACF 和周期图。它们都可以与以下内容一起可视化:

ACF,PACF 和周期图。作者图片。

周期图的峰值正好在 10 年,表明这是该系列的主要周期。

pytsviz 提供了更多的方法来研究时间序列的结构,比如周期图、分解等等。请随意查看文档以获取更多示例。

最后,让我们假设我们已经到了这个过程的末尾,并且我们已经有了一个模型来预测我们的时间序列。为了清楚起见,我们将使用该系列的 lag-1,一个公共基线。

pytsviz 提供图表来研究我们的模型的拟合优度。

拟合优度和残差分析。作者图片。

我们注意到,我们指示函数不绘制图表,而是返回 plotly 对象。这是该库中所有函数的一个共同特征,并且允许容易地个性化图表的布局。

贡献给 pytsviz

pytsviz 是一个开源项目。如果你发现它有任何价值,请随意投稿。

该项目仍处于非常早期的阶段,可以极大地利用每个领域的学者和从业人员的工作。查看 Github 资源库了解更多!

承认

pytsviz 由数据科学团队在 xtream 设计和构建。Riccardo Nakamoto Maganza、 Riccardo Zoncada 和 Gabriele Orlandi 是主要贡献者。

我的读者,谢谢你来到这里!

如果你资助了故事中的任何价值,请考虑 免费订阅我的帖子 ,成为 中等会员 无限制访问类似内容。

我们随时欢迎您的每一条评论、问题或总体反馈。如果你对 me xtream感兴趣,可以上 LinkedIn 看看我们!

如果你喜欢这篇文章,你可能会感兴趣:

Pyvis:用 Python 可视化交互式网络图

原文:https://towardsdatascience.com/pyvis-visualize-interactive-network-graphs-in-python-77e059791f01?source=collection_archive---------2-----------------------

只需要几行代码

动机

给你一张显示一组人之间友谊的表格,你被要求把这个组分成两个一组。但是,通过查看表格很难做到这一点。

如果您可以使用如下的交互式网络图来可视化他们的联系,这不是很好吗?

作者 GIF

这时候 pyvis 就派上用场了。

皮维斯是什么?

Pyvis 是一个 Python 库,允许你用几行代码创建交互式网络图。

要安装 pyvis,请键入:

pip install pyvis

添加节点

要向网络图添加节点,只需使用net.add_node(id, label)id是每个节点独有的。label用于显示图中节点的标签。

运行net.show('nodes.html')之后,一个名为 nodes.html 的文件应该会保存到您当前的目录中。你可以像下面这样和它互动。

要查看 Jupyter 笔记本中的图表,只需将参数notebook=True添加到Network

作者图片

添加节点列表

如果您有许多节点,您可能希望添加一个节点列表。这可以用add_nodes方法来完成。

不错!让我们通过给节点添加颜色来让它们看起来更有趣一点。

单独拥有节点并不好玩。让我们弄清楚如何用边将这些节点连接在一起。

添加边缘

要给图表添加边,使用add_edge方法。该方法获取边的源和边的目的地。

要一次添加多条边,使用add_edges,它的参数是元组列表。

酷!但是,如果一些边的权重比其他边大(例如,奥利弗可能更喜欢亚历克斯而不是迈克尔),该怎么办呢?

我们可以通过向add_edge添加参数value或者向add_edges的每个元组添加第三个值来指定每个边的权重。

现在,当您选择特定节点时,连接到该节点的边将高亮显示。

作者 GIF

调整节点距离和弹簧长度

节点距离是节点之间的距离。弹簧长度是边的剩余长度。

我们可以使用repulsion方法调整节点之间的距离和弹簧长度。让我们看看当我们改变参数node_distance和参数spring_length时,图是如何变化的。

默认值:

增加弹簧长度:

增加节点距离:

可视化脸书社交网络

让我们应用我们所学的知识来想象一个真实的社交网络。我们将使用脸书数据,由来自脸书的朋友列表组成。

脸书的数据是从调查参与者那里收集的,这些数据中的用户已经匿名。

你可以从这里下载数据。下载完数据后,解压保存为facebook_combined.txt

data = pd.read_csv("facebook_combined.txt", sep=" ", header=None)
data.columns = ["person1", "person2"]

由于这是一个大型数据集,我们将只可视化数据中的 1000 个样本。

sample = data.sample(1000, random_state=1)
sample.head(10)

可视化网络:

net = Network(
    notebook=True,
    cdn_resources="remote",
    bgcolor="#222222",
    font_color="white",
    height="750px",
    width="100%",
)
nodes = list(set([*sample.person1, *sample.person2]))
edges = sample.values.tolist()
net.add_nodes(nodes)
net.add_edges(edges)

过滤并突出显示节点

通过设置select_menu=Truefilter_menu=True,可以高亮显示一个节点及其相邻的边和节点:

net = Network(
    notebook=True,
    cdn_resources="remote",
    bgcolor="#222222",
    font_color="white",
    height="750px",
    width="100%",
    select_menu=True,
    filter_menu=True,
)
net.add_nodes(nodes)
net.add_edges(edges)

动态调整Network设置

我们还可以使用配置 UI 来更改网络设置。这允许我们快速试验不同的布局和图形物理。

作者 GIF

设置filter_=True会显示三个控件(nodesedgesphysics)。如果您想只显示physics小部件,使用filter_=['physics']:

作者 GIF

结论

恭喜你!您刚刚学习了如何使用 pyvis 创建网络图。我希望这篇文章能激励你创建你的网络图。既然创建一个交互式网络图只需要几行代码,为什么不试试呢?

在这个回购中,您可以随意使用这篇文章的代码:

https://github.com/khuyentran1401/Data-science/blob/master/visualization/pyvis_examples/pyvis.ipynb

我喜欢写一些基本的数据科学概念,并尝试不同的算法和数据科学工具。你可以在 LinkedIn 和 Twitter 上与我联系。

如果你想查看我写的文章的代码,请点击这里。在 Medium 上关注我,了解我的最新数据科学文章,例如:

[## 如何用 Excalidraw 勾画您的数据科学想法

towardsdatascience.com](/how-to-sketch-your-data-science-ideas-with-excalidraw-a993d049f55c)

PyWebIO 1.3.0:添加选项卡、锁定输入,并基于另一个输入更新一个输入

原文:https://towardsdatascience.com/pywebio-1-3-0-add-tabs-pin-input-and-update-an-input-based-on-another-input-e81a139fefcb?source=collection_archive---------18-----------------------

用几行代码创建有用的 Python 应用程序

动机

PyWebIO 是一个 Python 库,它使您可以用几行代码轻松创建如下所示的 web 应用程序。

作者 GIF

在上一篇文章中,我介绍了 PyWebIO 的一些基本用法,但是您可能想做的不仅仅是这些基本功能。幸运的是,PyWebIO 1.3.0 引入了更有用的工具,可以用几行代码创建复杂的接口。

在本文中,您将学习如何:

  • 添加选项卡以便更好地导航
  • 根据一个输入的值更新另一个输入
  • 持续输入

要安装 PyWebIO 1.3.0,请键入:

pip install pywebio==1.3.0

put_tabs —添加选项卡以实现更好的导航

PyWebIO 1.3.0 允许你添加标签到你的应用程序,如下所示:

作者 GIF

要添加选项卡,只需使用put_tabs小部件。put_tabs的参数是如下的字典列表:

完整代码:

源代码

输入 _ 更新—当一个输入改变时更新另一个输入

有时,您可能希望一个输入的选项根据另一个输入的值而改变。

比如下面的 GIF,当我改变话题时,文章的选项也随之改变。

作者 GIF

我们如何在两个小部件之间创建这样的依赖关系?首先,让我们创建两个互不依赖的下拉菜单:

正如我们所料,当我们改变话题时,文章的选项不会改变。

作者 GIF

现在让我们添加一个组件,它将在这两个下拉列表之间创建依赖关系。那就是input_update

input_update应该放在onchange回调的一个输入函数中。这意味着如果输入功能改变,将触发input_update:

代码onchange=lambda t: input_update('article', options=topics_to_articles[t])根据选择的主题更新输入 'article'的选项。

完整代码:

源代码

输出:

作者 GIF

固定—固定输入

PyWebIO 有很多有用的输入法比如selectinputradiocheckbox。通常,输入表单会在您提交输入后消失,如下所示:

作者 GIF

代码:

完整代码

但是如果你不想让输入表单像下面这样提交输入后消失呢?

作者 GIF

这时候pin就派上用场了。为了让您的输入持久化,首先,用pin.put_select小部件替换input.select小部件。

提交成功后pywebio.input中的小部件会消失,而pywebio.pin中的小部件不会在提交后消失。

现在我们使用pin_wait_change来监听一个 pin 部件列表。如果任何小部件的值发生变化,该函数将返回该小部件的名称和值。

在下面的 GIF 中,每当我们改变一个小部件,形状就被打印到终端上。这意味着当我们改变一个小部件时,一个新的值被赋予。

作者 GIF

要获取新形状并将其分配给当前的pin小部件,请使用:

最后但同样重要的是,将上面的代码放入with use_scope(..., clear=True)中,以便在新图像出现之前旧图像被清除。

将所有东西放在一起:

完整代码

输出:

作者 GIF

你可以在这里找到别针小工具的完整列表。

结论

恭喜你!您已经了解了 PyWebIO 的 3 个新特性。PyWebIO 还有很多很酷的特性,我在这里无法一一列举。我推荐你去看看 PyWebIO 网站了解其他功能。

您可以在这里随意使用本文的源代码:

https://github.com/khuyentran1401/Data-science/tree/master/applications/pywebio_1_3_0

我喜欢写一些基本的数据科学概念,并尝试不同的算法和数据科学工具。你可以在 LinkedIn 和 T2 Twitter 上与我联系。

星这个回购如果你想检查我写的所有文章的代码。在 Medium 上关注我,了解我的最新数据科学文章,例如:

[## 使用 SimPy 在 Python 中模拟真实事件

towardsdatascience.com](/simulate-real-life-events-in-python-using-simpy-e6d9152a102f)

PyWebIO:使用 Python 以脚本方式编写交互式 Web 应用

原文:https://towardsdatascience.com/pywebio-write-interactive-web-app-in-script-way-using-python-14f50155af4e?source=collection_archive---------6-----------------------

在没有 HTML 和 JS 知识的情况下,用几行 Python 代码构建 Web 应用程序

作者 GIF

动机

您是否曾经希望只用几行 Python 代码创建一个 web 应用程序?Streamlit 允许您这样做,但是它没有提供很多选项来定制您的输入框、输出、布局和页面。

如果你正在寻找比 Django 和 Flask 更容易学习,但比 Streamlit 更容易定制的东西,你会喜欢 PyWebIO。

PyWebIO 是什么?

PyWebIO 是一个 Python 库,允许你在没有 HTML 和 Javascript 知识的情况下构建简单的 web 应用。PyWebIO 也可以很容易地集成到 Flask 或 Django 等现有的 web 服务中。

要安装 PyWebIO,请键入

pip install -U pywebio

开始

投入

PyWebIO 提供了各种选项来获取用户的输入。下面的 GIF 展示了这些选项的样子。

作者 GIF

输出

PyWebIO 还提供了多种输出选项。点击查看输出的完整列表。让我们看看这些输出选项是什么样子的。

作者 GIF

在上面的代码中,我们使用:

  • put_markdown写减价
  • put_text创建纯文本
  • put_table创建菜单表
  • select创建一个下拉栏
  • put_image输出食物图像
  • put_file输出下载文件的链接

我们可以用几行代码创建这样一个有趣的应用程序。多酷啊!

让我们应用我们到目前为止学到的知识来创造:

  • 一个简单的应用程序,生成提供给定文本的 PDF
  • 一个为我们上传的 CSV 文件生成美丽、高密度可视化效果的应用程序

创建应用程序以生成 PDF

我们将创建一个简单的应用程序来生成一个 PDF 格式的给定文本如下:

作者 GIF

让我们从创建应用程序的输入框和输出开始。

上面代码的解释:

  • input:创建一个文本输入框。
  • textarea:创建一个多行文本输入框。
  • placeholder:向用户建议可以在输入框中输入的内容
  • required:指定是否需要输入
  • start_server:启动服务器,将 PyWebIO 应用程序作为 web 服务提供。debug=True告诉服务器在代码改变时自动重新加载。

我们应该看到类似下面的内容:

作者图片

去 http://0.0.0.0:36535/ 玩 app。现在我们有了一个简单的 web 应用程序,带有一个输入框,如下图所示!

作者 GIF

不错!现在我们知道了如何创建一个输入框和输出,让我们写下为输入文本生成 PDF 的代码。我们将使用 PyFPDF 来完成这项工作。

pip install fpdf

输入所有信息后,一个名为 output.pdf 文件的文件将被保存到您当前的目录。如果你输入的文字是“你好”,你会看到下面有些像!

作者图片

现在我们已经了解了应用程序的基础知识。让我们添加更多的功能,让用户更容易使用。

添加一组按钮

用户可能希望在 PDF 中添加多个页面。因此,我们将添加一组按钮,使用户能够决定是否添加另一个页面。我们可以用actions方法做到这一点。

  • label:按钮名称
  • value:用户点击按钮时的值

我们将使用while add_more不断要求用户添加另一个页面,直到他们点击“否”。我们还将修改我们之前的函数create_pdf,这样它将接受一个页面列表作为输入。

我们来试试吧!

作者 GIF

酷!现在我们应该有一个 2 页的 PDF 文件,如下图所示!

作者图片

分组下拉和数字输入

我们还可以使用一个下拉框和一个数字输入框,使用户能够定制他们的文本大小和字体。但是,这一次,我们将把它们组合成一个表单,这样我们就可以立刻从用户那里请求一组输入。

现在我们可以使用text_info['fonts']text_info['size']来访问text_info中字体和大小的值。使用这些值作为create_page功能的参数。

作者 GIF

而且我们在同一个表单中既有下拉框又有输入框!最终输出应该如下所示:

作者 GIF

创建一个应用程序,为上传的 CSV 文件生成美丽的可视化效果

您是否曾经想要创建一个 web 应用程序,为您上传的 CSV 文件自动生成漂亮的可视化效果?使用 PyWebIO 可以轻松做到这一点。下面是它的样子。

作者 GIF

上面生成 web 应用程序的代码。

我们只需要 3 个步骤来生成如上的 web 应用程序:

多酷啊!

结论

恭喜你!您刚刚学习了如何使用 PyWebIO 创建简单的 web 应用程序。通过使用 PyWebIO,您可以在短时间内构建一个有用的 web 应用程序!我鼓励你阅读 PyWebIO 的文档,找到你可以用 PyWebIO 做的其他很酷的事情。

随意发挥,并在这里叉这篇文章的源代码:

https://github.com/khuyentran1401/Data-science/tree/master/applications/pywebio_examples

我喜欢写一些基本的数据科学概念,并尝试不同的算法和数据科学工具。你可以通过 LinkedIn 和 Twitter 与我联系。

如果你想查看我写的所有文章的代码,请点击这里。在 Medium 上关注我,了解我的最新数据科学文章,例如:

基于深度神经网络的钢琴器乐生成

原文:https://towardsdatascience.com/pyzzicato-piano-instrumental-music-generation-using-deep-neural-networks-ed9e89320bf6?source=collection_archive---------23-----------------------

使用扩张卷积或 LSTM 网络构建一个有趣的制作钢琴器乐的网络应用程序

作者:菲利普·帕特、加廷·瓦瑟、埃姆纳·拜拉姆和弗拉基米尔·雷蒙——2021 年 4 月

图片通过 CC0 许可下的hippopx.com

1.语境

Pyzzicato 项目是我们为期三个月的数据科学家训练营的最后一部分。

该项目旨在构建一个 web 应用程序,允许用户根据他们选择的作曲家的风格生成新颖的音乐曲目(使用 streamlit)。基本上,这些模型通过使用一套深度学习技术和 midi 文件数据集,自动学习音乐风格,并基于现有的作品生成新的音乐内容。

在深度学习算法领域,生成模型有一个特殊的目标:它们旨在创建原始输出,这意味着*“不是一个精确的副本”它们已经学会了复制。从这个意义上说,输出必须是原创的,但不能过分原创*:评委(这里是:我们人类)必须至少在生成的输出中识别出模型训练输入数据的主要特征

一言以蔽之:好的生成模型是其输入数据集的“精神孩子”,但不是专业的造假者。完美再现其输入数据的生成模型未能完成其使命,因为生成的内容已经存在

著名的网站thispersondoesnotexist.com是这个定义的一个极好的例证,因为它产生了普通人类的图像——而不是现存的人类。

在音乐生成的背景下,生成模型的一个潜在目标是学习模仿他们已经得到的音乐数据的“风格”。所产生的音乐输出的听众必须感觉它可能是由人类作曲家写的,并且识别一种风格或音乐流派。

2.该项目

数据集和数据探索

即使对人类来说,创作音乐也不是一件容易的事情,音乐曲目是一个非常复杂的对象。为了简化模型化,我们选择生成单乐器音乐,因此选择了包含单乐器钢琴演奏记录的数据集。我们使用了作为 Magenta 项目的一部分而创建的 MAESTRO 数据集(为同步轨道和组织而编辑的 MIDI 和音频)。

该数据集由 1276 首曲目组成,涵盖了 17 世纪至 20 世纪初大多数古典音乐作曲家的 200 多个小时的钢琴录音。这个数据集相当大,我们必须在合理的时间内选择子数据集来执行深度学习模型的训练。

图 1 示出了(I)每个作曲家在数据集中演奏和收集的作品的总数,以及(ii)数据集的 14 个主要作曲家的每个作曲家的独特作品的数量。

*图 1:每位作曲家的独特曲目和总曲目数(图片由作者提供)*

我们已经决定选择表现出非常独特的作曲风格的作曲家,这些作曲家的独特作品的总数足够大,可以在没有太多冗余输入的情况下训练模型。我们还选择了普通观众熟知的作曲家,以方便对结果的评估。基于此,我们选择了三位作曲家——肖邦、巴赫和德彪西——来训练我们的生成模型。

数据可视化和预处理

用 python 可以很容易地操作 MIDI 文件,例如使用 Colin RAFFEL 创建的 Pretty_MIDI 包。[1]

pretty_midi 对象的类将 midi 文件的音乐数据编码为音符形式的音符事件列表(start=0.000,end=0.124,pitch=76,velocity=80)。在本例中,音高编号为 76 的音符在 t=0 和 t=0.124 秒之间弹奏,力度(表示弹奏音符的力度)等于 80。音高和力度编码在 0 到 127 之间。

这种音乐轨迹的表示不太适合深度学习建模。因此,为了对数据进行编码,我们使用了一种基于矩阵的方法,该方法使用了音乐曲目的钢琴卷帘窗表示。

钢琴卷帘窗表示将 pretty-MIDI 对象的音符事件列表转换为[Pitches vs Timestep] 2d 矩阵(参见图 2)。

*图 2:用于生成的输出的数据可视化和视觉评估的音乐轨道(巴赫 D 小调半音阶幻想曲)的钢琴卷首表示。(图片由作者提供)*

下面是我们使用 Librosa 和 Pretty-MIDI(在 streamlit web 应用程序中)绘制钢琴卷帘窗矩阵的代码:

钢琴卷帘窗矩阵可以被视为维度为 128(MIDI 标准中不同音高的最大数量)的音高向量的串联,这些音高向量在表示时间流动的每个不同时间步长(这里是矩阵的一列)被连续播放。

时间步长的持续时间由采样频率 fs 决定,时间步长= 1/fs。fs=20 时,钢琴卷帘窗矩阵的每一列代表 1/20 秒的音乐。

使用这种表示,我们可以定义要给予深度学习模型的特征和目标(参见图 3):

特征将是长度为 n_feat 的音调向量序列

-目标将是 n_feat + 1 个音调向量,模型将基于特征序列学习预测该向量

*图 3:左图:音乐输入被编码为特征(基音向量序列)和相应的目标(下一个基音向量)。Nb_note = 88(而不是标准 MIDI 编码中的 128),因为钢琴板只包含 88 个键。
右:将输入的 MIDI 文件分割成特征序列(音高向量序列)和目标序列(后续音高向量)。(图片由作者提供)*

3.深度学习模型

音乐曲目基本上是音符的时间序列。与噪音相反,音乐曲目中的音符不是随机播放的,而是通过和声、旋律等概念彼此紧密相连的。因此,我们用来生成音乐的深度学习模型必须能够学习音乐“上下文】 来跟踪被相对大量的时间分开的音符。然后,我们使用了两种已知在这项任务中有效的深度学习架构:扩张卷积网络递归神经网络。

我们建立的深度学习模型还必须能够预测包含零音符(静音)、**几个音符(一个和弦)、**的音高向量,这是音乐曲目中非常常见和重要的特征。为了在我们的建模中以简单的方式允许它,我们在所有模型的输出层中使用了 sigmoid 激活函数。

扩张卷积模型

【图 4】核大小= 3,膨胀大小= 1,2,4 的膨胀因果卷积 1D 的例子(来源:https://arxiv.org/pdf/1803.01271.pdf)

图 4 给出了扩展卷积的原理。扩张卷积模型的主要优点是,通过适当选择层和超参数,它可以在合理的时间内产生像样的结果,可以用作比较的起点。我们首先设计了一个具有几层的简单卷积模型(在 Pyzzicato 应用程序中称为 Conv1D,但不在此讨论),由于获得了相当好的结果,我们受著名的 WaveNet 架构(我们称之为“伪 wavenet”)的启发,构建了一个更复杂的卷积模型。该模型由五个卷积 1D 块(膨胀率在 2 和 16 之间)和一个分类块组成,如图 5 所示。注意最后一层的 **sigmoid 激活函数**允许在单个音高向量中生成多个一键编码音符事件。

图 5:伪波网架构(图片由作者提供)

请在此处查看代码:

RNN (LSTM)模型

LSTM 是时间序列预测的有效模型,特别适合我们的目的。这是因为它们有选择性地短时间或长时间记忆模式的特性。

普通的 LSTM 单元由一个单元、一个输入门、一个输出门和一个遗忘门组成。该单元记忆任意时间间隔的值,并且三个门按照图 6 中描述的策略来调节进出该单元的信息流:

图 6:LSTM 电池原理(datascientest.com)

我们的 LSTM 模型是一个序列模型,具有 2 个 LSTM 层和 3 个密集层,如图 7 所示。

图 Pyzzicato 中使用的(原始)LSTM 模型的架构(图片由作者提供)

请在此处查看代码:

由于 Google Colab 的免费 GPU 功能,模型得到了训练。

我们如何生成音乐曲目?

为了生成音轨,我们将输入序列(称为“种子”)提供给一个经过训练的模型,并要求它依次预测下一个音高向量,以便模型逐步生成一个新的音乐音轨。还可以调整一些参数,如生成轨道的总时间长度,或 sigmoid 输出层的阈值。

下面是我们使用的函数:

4.结果

生成模型的分析原理

评估生成模型的性能是一个未解决的问题,并且面对创造力的基本概念。总的来说,我们缺乏量化指标来评估产出的质量。的确,当我们听一首由生成模型产生的音乐曲目时,我们如何判断它的质量呢?

这个问题最常见的答案是主观评估法,基于以下标准:

  • 1/ **音乐性:**音符和和弦的连续是否类似于普遍接受的“音乐”?(*即*我们能在其中分辨出一个“旋律”吗?它“听起来像”一首人类创作的音乐吗?)

  • 2/ **保真度:**这首音乐是否包含一些特征模式?它属于预期的“音乐流派”(古典音乐、说唱、摇滚等)吗?

  • 3/ **表现力:**一个非常主观的标准,对应的问题是“这首音乐是否在听者的头脑中实例化了“情绪?”

尽管评估这个项目成果的最好方法是听音轨(使用 pyzzicato 应用程序!),则 piano_roll 表示允许对前两个定性标准 音乐性保真度 进行视觉估计。

结果概述:

伪波网和朴素 LSTM 模型都能够在音乐性和保真度方面产生良好的结果,尽管在这两种模型之间可以发现一些微妙的行为差异。以下是一些生动的例子:

**例 1:**

图 8:使用从巴赫半音阶幻想曲和 D 小调赋格中提取的种子序列的输出。(图片由作者提供)

第一个例子使用 10 秒钟的巴赫半音阶幻想曲作为种子,说明了我们的模型生成可听音乐的能力:音符间隔相当规则,音高不同,与种子中看到的音符在同一范围内。半音阶的上升和/或下降唤起了种子中所看到的,在两个音轨中都可以看到。然而,伪波网似乎表现出更好的保真度,因为音符持续时间更接*于种子轨道中看到的音符持续时间。

单击此处收听种子序列:

以及使用伪波网模型生成的轨迹:

例二:

图 9:使用从肖邦 G 小调第一叙事曲中提取的种子序列的输出,作品 23。(作者图片)

第二个例子使用肖邦的叙事曲 n 1 的 10 秒钟作为种子,说明了这样一个事实,即与伪波网相比,天真 LSTM 更容易适应较慢的节奏和更持续的音符。尽管这两种模式在旋律方面产生了非常相似的输出,但 LSTM 设法再现了种子开始时可见的长时间持续的音符,而伪波网产生了一种沉默(在音轨的 6s 和 7s 之间)。

单击此处收听种子序列:

以及使用伪波网模型生成的轨迹:

**例 3:**

再来听一个例子,用的是从肖邦的玛祖卡舞曲(作品 17)中摘录的一个种子:

最有说服力的轨迹是由天真-LSTM 模型生成的:

限制

虽然相当令人满意,但上述生成轨迹的示例也凸显了我们的模型的主要局限性:

  • 不太好的概括:输出强烈依赖于选择的种子,有时模型产生很少的音符,甚至完全无声,或有噪音的输出
  • 生成长音轨的困难:当我们要求我们的模型生成超过训练序列长度(这里是 10s)的音轨时,我们的模型有困难。随着时间的推移,它们有“干涸”的趋势,产生越来越少的音符。

5.使用案例和潜在改进

潜在的使用案例

属于领域或者创作,为这个项目提出现实的用例不是一件容易的事情。然而,我们可以想象这项工作用于:

  • 为呼叫中心或管理部门自动生成等待/等待音乐
  • 音乐制作人/作曲家的旋律建议工具

潜在的改进

  • 标准化 MIDI 数据集,以控制种子序列在速度、音高范围等方面的可变性。
  • 使用最先进的生成对抗模型(gan)
  • 通过使用生成器改进训练数据管理,以增加我们的训练数据集的大小(基于矩阵的方法在内存需求方面相当贪婪)
  • 使用另一种数据编码模式,如 NLP 启发的方法,而不是一次性编码的钢琴卷帘窗矩阵
  • 实施结果评估的量化指标。我们的方法主要基于主观评价,但文献中的一些参考文献提出了有趣的统计方法[2]。

6.概括起来

作为结论,我们首先要强调的是,尽管我们的三个模型化架构相对简单,但这里呈现的结果非常令人鼓舞。

在这个项目中,我们设法使用三种不同的生成模型制作音乐,从处理顺序数据的卷积模型到更高级的递归神经网络模型,这些模型考虑了数据结构中更长的路径依赖,如 RNN。

我们已经观察到种子和相应输出之间的真实音乐相干性,特别是对于伪波网和 LSTM 模型。

正如文章中所讨论的,更大的数据集肯定会改善我们的结果,首先是通过减少对训练数据的过度拟合。实现量化指标来评估生成轨迹的质量也是非常有趣的。由于时间不够,我们的分析仅限于主观评价方法。

使用人工智能深度学习模型的艺术创作是一个非常年轻的研究领域,未来将取得巨大成功。我们很高兴也很自豪有机会一睹这些创新算法的成果!

对题目感兴趣?以下是令人惊叹的项目的非详尽列表:

还有更多更多!

7.下一步是什么?

首先,我们希望通过向读者提供一种生成他们自己的音乐曲目并评估结果的方式,来扩展这里呈现的结果的主观评估。多亏了我们开发的 Pyzzicato 网络应用程序,这才成为可能。它可以在 datascientest studio 上获得!

其次,我们对与任何对设计新型深度学习生成模型感兴趣的人合作持开放态度。用于预处理和模型训练的代码可以在 github 上找到,请随意查看!

在这里享受 streamlit 应用带来的乐趣!

非常感谢Juliette Voyez在整个冒险过程中对她的帮助!

[1]科林·拉弗尔和丹尼尔·埃利斯。使用 pretty_midi 对 MIDI 数据进行直观的分析、创建和操作。在 2014 年第 15 届音乐信息检索国际会议上的最新突破和演示论文。

[2]参见杨等*。,对音乐中的生成模型的评估*,神经计算和应用,2020 年 5 月,以及其中的参考文献以了解更多细节。

将历史数据纳入 SageMaker 特征库的问答

原文:https://towardsdatascience.com/q-a-for-ingesting-historical-data-into-sagemaker-feature-store-239e918ec594?source=collection_archive---------35-----------------------

通过直接写信给 S3,回答如何将数据导入 SageMaker 离线功能商店的问题

艾米丽·莫特在 Unsplash 上的照片

在之前的一篇博文中,我展示了如何通过直接写入 S3 将数据导入 SageMaker 离线功能商店。我收到了关于高级场景的反馈和建议,我将在 Q & A 中讨论这些反馈和建议。

问:在要素记录具有不同时间戳的情况下,我如何获取历史数据?

我通过给每个特性记录分配相同的时间戳来简化我前面的例子。然而,在真实场景中,历史特征记录更有可能具有不同的时间戳。在这种情况下,我们可以使用相同的方法,但在设置 S3 文件夹结构时,我们必须更复杂一点,我们必须根据时间戳分割数据集。

让我们首先创建一个每个记录具有不同时间戳的要素数据集:

此代码创建将 2021 年 1 月 1 日晚上 8 点到 2021 年 12 日上午 10 点之间的随机时间戳追加到数据集。

关于离线商店的 S3 文件夹结构的文档告诉我们,我们必须为这些时间戳的年、月、日和小时的每个唯一组合创建不同的文件夹。它还告诉我们,每个特性子集的文件名需要文件中最新时间戳的时间戳:

S3 的命名惯例(图片由作者提供)

为此,我们需要为数据集中的每条记录创建一个键。该键的格式为 *YYYY-MM-DD-HH,*表示该记录时间戳的年、月、日和小时。然后,我们将所有具有相同关键字的特征记录组合在一起:

根据时间戳分割特征数据(按作者分类的图片)

对于每个子集,我们还需要识别相应的文件名。为此,我们需要识别每个子集内的最新时间戳。在上例中,关键字为2021–01–01–22的子集的文件名将以“ 20210101224453 _”开头,因为该子集中的最新条目来自 22:44:53。

以下代码为每个子集生成密钥以及 S3 路径和文件名:

为了根据时间戳键分割数据集并将它们保存到 S3 的相应 S3 路径中,我们可以简单地利用 pandas 的 groupby() 方法:

结论

在这个例子中,我们已经将带有不同时间戳的历史特征记录摄取到 SageMaker 离线特征存储中。我们根据特征记录时间戳分割数据,根据文档创建 S3 路径,并将每个子集存储在相应的 S3 位置。完整的例子和代码可以在这本笔记本中找到。

问:我对每个特性记录都有几个版本。我希望将历史功能记录导入到离线商店,但也希望将每个功能记录的最新版本与在线商店同步。我该怎么做?

在这种情况下,我们必须根据事件时间戳来识别每个特性记录的最新版本。然后,我们将通过将这些记录直接写入 S3 来回填所有比最新版本旧的版本。包含我们将使用常规摄取 API 摄取的最新记录的子集。最终,我们将在在线和离线功能商店中获得每条记录的最新版本。所有其他(历史)记录将仅在离线商店中可用。

让我们首先创建一个数据集来反映这个场景。下面的代码为每笔交易创建 3 条记录,每条记录都有不同的时间戳:

结果数据集有 6,000 条记录,每笔交易三条。现在我们想将数据分成两组:

第一个子集( df_online )包含每个事务的最新版本。这一个我们将使用 API 调用摄取。第二个子集( df_offline )包含每个特征记录的旧版本。这一个我们将直接摄入 S3 以同样的方式在上面。

将要素数据分为历史子集和当前子集(按作者分类的图片)

因为填充线下商店的代码同上,这里就不赘述了,不过你可以在这个笔记本里找到。我想明确指出的一个区别是,在创建功能组时,我们需要确保在线商店已启用:

一旦 S3 回填完成,我们就可以通过 API 获取每个要素记录的最新版本。这将把子集写入在线商店和离线商店:

写入在线商店是即时的,我们可以通过调用 GetRecord API 立即测试它:

通过 API 写入离线商店需要几分钟时间。等待大约 5-10 分钟后,我们可以测试离线商店现在是否已正确填充:

如果一切正常,您应该会看到三条记录,其中两条是我们通过直接写给 S3 获取的,另一条是通过 API 获取的:

作者图片

结论

在这一部分,我们通过直接写信给 S3,将历史特征记录回填到离线商店中。我们还通过使用摄取 API 将当前版本的功能记录与在线商店同步。最后,我们在离线商店中有所有的特性记录,在在线商店中有最新的版本。这个例子的完整代码可以在这个笔记本中找到。

感谢所有提供反馈和建议的人。如果您还有任何问题或反馈,请随时联系我们。

特定信息抽取的问答模型

原文:https://towardsdatascience.com/q-a-models-for-specific-information-extraction-2a204f7f3521?source=collection_archive---------35-----------------------

从非结构化文本中获取相关实体提取的转换器的能力

作者图片

一天 121,一个月 3751,一年 44165。据有人计算,那是一般人收到的邮件数量。我不知道你怎么想,但对我来说,有很多邮件需要我花时间去处理…

作者图片

如果你能根据邮件的内容、他们是否想卖东西、你是否必须回覆某人等等,立即将他们分类,那不是很好吗??我测试了一些传统的信息提取技术以及一种更具创新性(并且相当强大)的技术。下面是它们的工作原理和不同结果的总结。

示例电子邮件

为了保持一致,我们将使用以下示例电子邮件(主要使用 GPT-3 生成)来比较和观察当前针对此问题的不同解决方案:

嗨,杰克,

对不起,我被我新的特斯拉汽车公司分散了注意力,恐怕我要把比特币项目的领导权交给别人了。

我需要一个非常非常熟练的人。你能建议我任命谁吗?埃隆·马斯克已经忙得不可开交了!

非常感谢,

Sat。

附注:我也有一辆特斯拉跑车!

中本聪

电话号码:+1(650)566–1191

比特币:1 gttzecjym 19 Xu 3 IC 8 neum 7 MB 5 uzqbkd

如果你很好奇,给 GPT-3 的输入是:“*这是中本聪给杰克的一封电子邮件,解释比特币网络似乎已经断开。邮件:“。*结果并不真正相关,但作为一个例子,效果很好。

技术

选项 1:正则表达式

信息提取问题最古老的解决方案是正则表达式。我们可以使用这些来从文本中提取匹配特定模式的字符串。为简单起见,我们将只提取我们应该联系的人的姓名(在许多情况下,不是发送电子邮件的人的姓名)。

让我们首先假设全名可以遵循以下格式:

Satoshi NakamotoSatoshi K. NakamotoSatoshi K NakamotoSatoshi Nakamoto-KoaSatoshi Nakamoto Koa

因此,一个可能的正则表达式可能是:

(?:[A-Z][a-z]+)+(?:[\- ][A-Z](?:[a-z\.]+)?)+

使用 regex 从电子邮件中获取所有匹配项,如下所示:

它将从我们的电子邮件中选择以下字符串:

正则表达式提取:按作者排序的图像

正如你所看到的,这种方法有利于减少可供选择的选项数量,但是我们仍然会得到不是名字的字符串,并且我们没有一种有效的方法来识别重要的那个(我们不能总是选择最后一个)。

选项 2:使用实体检测

解决前一个问题(检测不是名字的字符串)的常见方法是使用 spaCy 。

spaCy 是一个用于高级自然语言处理的开源软件库,用编程语言 Python 和 Cython 编写。

spaCy 将允许我们快速建立一个实体检测器模型,这将有望帮助我们从电子邮件中提取我们想要的信息。要下载您喜欢的型号,您需要运行:

python -m spacy download <MODELNAME>

由于我们将使用 spaCy 3.1 的 transformer 模型,我们需要运行:

python -m spacy download en_core_web_trf

完整的代码如下所示:

这将从我们的电子邮件中选择以下内容:

空间提取:作者提供的图像

如您所见,这个模型比 regex 做得好得多。它忽略了类似“特斯拉 Roadster ”或者“嗨 Jack ”这样的案例,却没有得到正确的称呼:【中本聪】!这可能是因为 spaCy 在其训练数据中从未遇到过此名称。

虽然这并没有真正改变结果…即使空间探测到了*“中本聪”*,我们仍然不知道哪个人实体是重要的。

怎么才能做到呢?这可能吗?这么多问题!嗯……问题。如果我们只问计算机哪个名字是重要的呢?!让我们试试那个。

选项 3A: Q &变压器型号

使用一个已经通过像 SQuAD 这样的问答数据集进行了微调的转换器,将允许我们向它询问任何关于它接收的上下文(在这种情况下是电子邮件)的问题。

令人欣慰的是,HuggingFace 库包含了许多预先训练好的模型,可以开箱即用。为简单起见,我们将使用其中的一个,因为不是每个人都能负担得起像伯特那样大的 TPU 现成模型。你可以随意看看他们的型号目录,选择你最喜欢的型号。我将使用*"Bert-large-un cased-whole-word-masking-fine tuned-squad"*模型,根据他们的说法,该模型实现了 93.15 的 F1 分数。

像 spaCy 一样,HuggingFace 也有一个 API,允许用户快速与预先训练好的模型进行互动,以完成特定任务:管道。

在此之前,我们首先需要知道要问哪些问题。以下是我能想到的问模特的五个问题:

  • 他叫什么名字?
  • 她叫什么名字?
  • 他们叫什么名字?
  • 这是谁送的?
  • 寄这个的人叫什么名字?

现在我们有了问题,我们的代码看起来像这样:

它为每个答案输出以下概率:

问答模型提取:作者图片

【特斯拉汽车公司】【聪】【特斯拉】?这里我们有一个类似于正则表达式提取(选项 1)的问题:我们得到的格式看起来不像我们想要的格式,并且实体不是一个名称。

要解决这个问题,我们有三个选择:我们可以使用 regex、spaCy,或者两者都用。因为我们已经看到 spaCy 不能检测名字*“中本聪”*作为一个人,我们将使用正则表达式。您应该尝试使用 spaCy,因为它在其他情况下也很有用。如果你想冒险,两者同时尝试。

选项 3B: Q &变压器型号+正则表达式过滤

加上我们在选项 1 中看到的过滤步骤,我们得到:

如果我们运行这个代码,我们会得到这些新的概率:

问答模型提取+正则表达式:作者图片

结果

我们走吧!跳出思维定势,我们能够从文本中提取相关信息。想想看:如果你有关于邮件的问题,直接问电脑!;)

以下是我问这位模特的其他问题,我觉得很有趣。尝试你自己的问题,看看模型给你什么答案,但是如果不是关于名字的话,记得移除/改变正则表达式检查!

Q: What is his phone number?
A: +1 (650) 566–1191 -- 91.74% SureQ: What does he need?
A: someone really really skilled -- 42.43% SureQ: What is his bitcoin wallet address?
A: 1GttzecjYm19xu3iC8i8NEuM7mB5uZQbKD -- 1.72% SureQ: What car does he have?
A: Tesla Roadster -- 75.22% SureQ: Who doesn't have time?
A: Elon Musk -- 95.99% Sure

结论

尝试非传统的方法有时能给你更好的效果!即使有数据集可以提取相关信息,如安然人名注释数据集,但它们都没有包含足够的信息用于此目的。

我很高兴看到机器学习社区的未来,以及这些超级强大的模型如何用于日常目的。

全 Colab 笔记本:https://Colab . research . Google . com/drive/1 nctya 2 qwxov 77 gzbfua 9 nndv 3 quzinns?usp =共享

q-Fin:Python 库

原文:https://towardsdatascience.com/q-fin-a-python-library-84b611c44411?source=collection_archive---------21-----------------------

证券定价的工作库

Levi Meir Clancy 在 Unsplash 上拍摄的照片

量化金融

在过去,我写了大量关于证券定价的文章…

  • Black-Scholes 算法 Delta 对冲
  • 什么是隐含波动率?
  • 鞅和马尔可夫过程
  • 随机积分
  • 推导布莱克-斯科尔斯模型
  • 为外来物种定价的 Python
  • 波动率交易 101
  • 风险中性投资组合管理
  • 布莱克-斯科尔斯期权定价有误
  • 几何布朗运动
  • Python 中的选项希腊文
  • Python 中的量化金融
  • 什么是波动风险溢价?
  • 什么是布莱克-斯科尔斯定价法?
  • 高级期权交易
  • 算法投资组合对冲
  • Python 中的蒙特卡洛定价
  • Python 中的随机波动率定价

在我的所有文章中,我使用 gists 或 GitHub 库以编程方式展开讨论。我决定是时候开始将所有这些主题和代码聚合到一个开源的 Python 库中了: Q-Fin(定量金融)

GitHub 存储库:

问 -Fin 是一个用于量化金融的(工作中的)Python 库,由不同的模块组成,用于帮助不同证券的定价。

从版本 0.0.19 起可用的模块

随机过程

  • 几何布朗运动
  • 赫斯顿的随机波动模型

期权定价

  • 布莱克-斯科尔斯看涨/看跌期权和希腊人
  • 普通期权的模拟定价

奇异期权定价

  • 二元期权的模拟定价
  • 障碍期权的模拟定价
  • 亚式期权的模拟定价

模块即将推出

分析学

  • 投资组合优化、模拟和分析
  • 投资组合构建的随机优化

期权定价

  • 动态对冲模拟

奇异定价

  • 计算希腊人的数值方法

固定收益定价

  • 债券定价、期限、凸度等…

未来定价

  • 包含存储成本、股息等的资产的合同定价…

如何使用 Q 型鳍

Q-Fin 发布到 PyPi,可以用 pip 包管理器安装。

pip install qfin

安装后, qfin 可以与所需的定价模块一起导入。

当实例化 BlackScholesCallBlackScholesPut 的对象时,会自动生成价格和希腊值…

12.361726191532611
0.5596176923702425
0.018653923079008084
39.447933090788894
-6.35319039407325

qfin 中的模型旨在易于实现和直观使用。如果对如何实现任何模型感到困惑,请参见 GitHub 上的Q-Fin查看每个模型的示例。

如果我们想生成一个基于几何布朗运动的资产样本路径,我们可以从模拟模块导入几何布朗运动

[93.91122929151561, 91.6343053296407, 88.05914544360269, 93.59884276992683, 88.91826221393717, 96.24731044166496, 100.47778492860515, 101.67238049186874, 100.9417850393366, 96.82424825907334, 93.7151718797683, 92.49545149500011, 96.53263786045804, 89.28059081778505, 90.45872657119223, 86.93126385081307, 85.16673100265604, 79.15865514214676, 84.19274677422355, 85.24206893196425, 78.62525272868086, 83.54358623217077, 81.62201286740276, 82.42635804232602, 84.94263935198337, 83.33105946122816, 77.8595164393749, 80.27197581106154, 79.47579375600165, 82.71376020485647, 80.70778436356143, 80.05570601837579, 80.07619597627252, 79.20938156016103, 75.90700126416749, 78.48328669684636, 77.64791411275162, 81.39837629677785, 79.03009362423532, 79.7491267612585, 81.64910423114112, 83.09606367629651, 81.20209669000536, 82.12241299592519, 81.05241267445211, 78.94737177934881, 80.74689754653583, 83.62309595971432, 92.88481220356229, 95.76653063259874, 91.28249515120577, 93.13154530372866]

生成的样本路径的可视化结果

基于几何布朗运动的模拟资产路径

类似地,如果希望根据随机波动模型(赫斯顿模型)生成样本路径,也可以用类似的方式完成…

[105.42093152811593, 104.59360758315175, 95.39486402258322, 95.4249148299477, 89.20333260783619, 91.19985569745064, 91.80869313310264, 94.26238888554938, 92.48431868124044, 94.29279102140633, 90.24378843974851, 95.45949462821744, 99.65404275857998, 99.37220075852828, 95.20171170639435, 95.69256680383415, 93.77186210135861, 99.27046893569022, 103.27587414833025, 106.655336480214, 109.69803170503143, 107.77485958774615, 107.53817208839943, 106.19690441993153, 105.39424156717143, 105.50808774366415, 93.98202958489428, 95.63909466199506, 92.14292356838752, 89.41142436545123, 86.09698832674296, 84.0572388626126, 84.63149048687042, 83.39001169580843, 87.13058171725491, 88.2510341053498, 87.83915958025074, 85.12312257176154, 80.6653790514404, 77.69004067122552, 79.58811490693624, 80.22513954798012, 81.19071476078733, 84.35002848454775, 80.88207502476948, 83.83626736045116, 85.01532060624201, 86.89395166366388, 84.31302211711235, 88.64369851332083, 86.47542281928952, 90.42790068755158]

生成的样本路径的可视化结果

基于随机波动模型的模拟资产路径

这个模型通常捕捉了经验观察到的资产收益的异方差性。

还有一些模块可用于模拟定价。很可能用户想要改变哪一个潜在的随机过程负责产生该外来物的公*价格。模拟定价总是需要几何布朗运动参数。然而,如果提供附加参数,将使用随机方差过程。

12.73812121792851
23.195814963576286

该库将证券定价中的抽象概念放入直观、易于实现的类和函数中。如果有任何建议、错误或一般意见,我希望听到任何反馈。

Q-Learning 和 SARSA,使用 Python

原文:https://towardsdatascience.com/q-learning-and-sasar-with-python-3775f86bd178?source=collection_archive---------3-----------------------

解释无模型 RL 算法的基础:Q-Learning 和 SARSA(带代码!)

法比奥·巴拉西纳在 Unsplash 上拍摄的照片

强化学习(RL)是机器学习中的一种学习范式,它通过与环境交互来学习将状态映射到动作的最优策略,以实现目标。在本文中,我将介绍两种最常用的 RL 算法:Q-Learning 和 SARSA。

类似于蒙特卡罗算法(MC),Q-Learning 和 SARSA 算法也是无模型 RL 算法,即不使用与马尔可夫决策过程(MDP)相关的转移概率分布。相反,他们从经验中学习最优政策。MC 与 Q-Learning 或 SARSA 算法的主要区别在于 MC 需要采样整个轨迹来学习价值函数,寻找最优策略。然而,对于一些问题来说,获得一个完整的轨迹可能是耗时的。因此,如果算法能够在每个动作之后更新策略,而不是在获得整个轨迹之后,这可能是好的。

在 Q-Learning 和 SARSA 中,我们只需要一步轨迹 (𝑠,𝑎,𝑟,𝑠')而不是整个轨迹。此外,从这两个算法中,我们还可以突出显示出符合策略的不符合策略的学习之间的差异,我将在本文稍后讨论这一点。

q 学习

Q-Learning 中的更新规则如下:

作者图片

新样本和旧估计之间的差异用于更新旧估计。

图 1:Q-Learning——一种非策略学习算法[1]

一步一步的例子

假设一个 MDP 的 6 室环境。我们将每个房间编号为 0 到 6,房间由门/箭头连接,如下图所示。目标是让代理移动到房间 5。注意,只有 2 号房、4 号房、6 号房可以通往 5 号房(目的地)。

2: MDP 6 室环境。作者图片

目标:把一个代理放在任何一个房间里,从那个房间到 5 号房间。
奖励:直接通往目标的门有 100 的即时奖励。其他不直接与目标房间相连的门奖励为 0。

本教程将通过简单易懂的例子介绍 Q-learning 的概念性知识。它描述了智能体如何通过强化学习方法从未知环境中学习。

从上面的代码收敛后输出 Q 值表。作者图片

3:根据上述 Q 值矩阵,5 个房间环境的最佳策略。。作者图片

根据上面的 Q 表,我们可以根据最大 Q 值选择行动。例如,如果代理在房间 1,它将有两条不同的路线可以通向房间 5。代理可以从房间 1 移动到房间 4 或房间 6,这两个房间的最大 Q 值都是 80。之后,在两种情况下,代理将移动到房间 5,这给出了 100 的最大 Q 值。类似地,如果代理在房间 3,它可以移动到房间 1 或房间 4,这给出了相同的最大 Q 值。

萨尔萨

SARSA 中的更新规则如下:

作者图片

更新规则类似于 Q-Learning,但有一些不同。

3: SARSA —一种基于策略的学习算法[1]

ε-贪婪算法中的探索是指以ε概率,智能体随机采取行动。这种方法用于增加探索,因为没有它,代理可能会陷入局部最优。

SARSAon-policy ,它用当前策略生成的(S,A,R,S’)样本更新 Q 表。(S’,A’)是转换样本中的下一个状态和下一个动作。在到达 S '之后,它将采取动作 A '并使用 Q(S ',A ')来更新 Q 值。而 Q 学习非策略,其使用状态 S’中的最大 Q 可能值来更新未来的 Q 值。然而,具有最大 Q 可能值的动作可能不是代理在未来将采取的实际动作,因为以ε概率代理将采取随机动作。换句话说,用于在 Q-Learning 中更新策略的动作不同于代理将采取的真实动作。

我们将使用这个工具包来解决 FrozenLake 环境。有各种各样的游戏,如雅达利 2600 游戏,基于文本的游戏等。点击查看全部。

下面的教程将使用 SARSA 算法来解决来自健身房环境的 FrozenLake 。

参考

[1]萨顿和巴尔托(2017 年)。强化学习:简介。麻省理工学院出版社

表格中的象限分析

原文:https://towardsdatascience.com/quadrant-analysis-in-tableau-6a3ee42d26ff?source=collection_archive---------8-----------------------

了解如何在 Tableau 中以动态象限图的形式分析数据

作者图片

如果你是数据科学生态系统的一员,你一定听说过 Gartner 的魔力象限 (MQ)。这些 MQs 是一系列包含几个技术公司的市场研究和分析的报告。它们是这一领域最受期待和期待的报告之一。这是针对分析和商业智能*台的 2020 年魔力象限。您可以清楚地看到代表四个不同类别的四个不同象限。

来源:https://www . qlik . com/us/Gartner-magic-quadrant-business-intelligence

象限图在技术上是一个散点图,分为四个相等的部分或象限,因此得名。象限分析图组合了相似的数据点,从而将它们放在同一个象限中。 Tableau 是当今业界广泛使用的一款出色的数据分析和可视化工具。在本文中,我们将学习如何在 Tableau 中创建象限图。我们将更进一步,让它也能响应。

个案研究

我们将使用著名的超市数据集进行演示。该数据是美国一家超市的数据,包含产品、销售、利润等信息。您可以使用它来确定这个虚构公司中需要改进的关键领域。第一步是导入数据集并浏览它。你可以从这里下载数据。

先决条件

本教程假设您对 Tableau、它的属性以及它在分析中的用途有所了解。

Python 中的基本 EDA

让我们使用 Python 来导入数据集并探索它。

import pandas as pd  
import numpy as np
superstore_data = pd.read_excel('Sample-Superstore .xls',header = 3)  
superstore_data.head()

数据帧显示了包含不同属性的数据集的前五行。让我们检查是否有任何丢失的值:

superstore_data.info()

上面的信息显示没有空值,总共有 9994 条记录。我们现在将数据集导入 tableau 工作区。

连接到数据源

  • 将数据从计算机导入 Tableau 工作空间。
  • 在“工作表”选项卡下,三个工作表将变得可见,即。OrdersPeopleReturns。在本文中,我们将只关注Orders数据。双击Orders表单,它就会像电子表格一样打开。
  • 我们观察到前三行数据看起来不同,并且不是期望的格式。我们将使用数据解释器,也在工作表标签下,来纠正这个问题。数据解释器将现有的工作表转换成格式良好的工作表。

作者图片

创建散点图

假设我们想知道Discount与超市的Profit之间的关系,以及哪些产品比其他产品更有利可图。第一步是在折扣和利润率之间创建一个简单的散点图。Profit Ratio给人一种比利润单独领域更好的感觉。原始数据集中不存在利润率属性。因此,我们将如下创建它:

  • 打开一个新工作表,使用下面提到的公式创建一个名为Profit Ratio的新计算字段。

sum([Profit])/sum([Sales])

作者图片

  • Discount拖至列,将Profit Ratio拖至行。然后将Sub Category拖到颜色架上。将Discount的测量值转换为Average。你会得到一个散点图,每个圆圈代表一个特定的产品。

作者图片

创建参数

现在我们有了一个基本的散点图,是时候创建两个参数了,它们将作为象限计算的参考。在我们的例子中,我们将创建一个Discount Parameter和一个Profit Ratio Parameter,它们将分别来自DiscountProfit Ratio字段。

  • 折扣参数

点击折扣字段 > 创建 > 参数并填写如下图所示的数值。将当前值设置为您选择的任何值。(本例中为 0.2)。

作者图片

  • 利润率参数

使用利润率字段执行与上述相同的步骤。这将创建Profit Ratio参数。

作者图片

一个名为Parameters的新标题现在出现在包含两个新创建参数的工作表中。

作者图片

添加参考线

Tableau 的参考线只是图形上代表参考点的一条垂直线或水*线。例如,在销售图表上画一条参考线可以很容易地区分高销售点和低销售点。我们将创建两条参考线,每个轴一条。

  • 右击Profit Ratio axis (y)并选择添加参考线
  • 将参考线值设置为Profit Ratio参数。
  • 保留其他字段的默认值,并点击确定
  • 右击Avg Discount轴(x)并选择添加参考线
  • 将参考线值设置为Avg Discount参数。
  • 保留其他字段的默认值,并点击确定

作者图片

给每个象限分配一种颜色

我们现在有了四个象限,但是每个象限中的数据点都是随机着色的。我们可以编写一个简单的计算,根据象限(即右上、左上、右下或左下)为数据点分配颜色。

  • 点击分析 > 创建计算字段,并将该字段命名为象限颜色
  • 输入如下所述的公式:
IF AVG([Discount]) >= [Discount Parameter]  
AND [Profit Ratio] > [Profit Ratio Parameter] THEN 'UPPER RIGHT'  
ELSEIF AVG([Discount]) < [Discount Parameter]   
AND [Profit Ratio] > [Profit Ratio Parameter] THEN 'UPPER LEFT'  
ELSEIF AVG([Discount]) >[Discount Parameter]   
AND [Profit Ratio] < [Profit Ratio Parameter] THEN 'BOTTOM RIGHT'  
ELSE 'BOTTOM LEFT'  
END

作者图片

  • 数据点目前已经被子类别着色。然而,我们想用它们各自的象限来给它们着色。将Sub-Category字段拖到细节卡上,将Quadrant Color字段拖到颜色卡上。现在,所有数据点立即按照它们所在的象限进行着色。将Shape更改为圆形以获得填充的圆形,并相应地调整尺寸和工具提示偏好。

作者图片

所以这里我们有一个美国超市的折扣率和利润率的象限分析。从图中可以明显看出,位于Bottom Right的产品是利润最低的产品,而位于Upper Left的产品是利润最高的产品。

创建动态象限图

上图中的象限是固定的,只有当我们改变链接参考线的值时才能改变。如果我们想试验不同的阈值,看看产品如何实时改变它们的象限呢?这是可能的,被称为动态象限图,通过Actions.实现

行动

动作是 Tableau 中的一个特性,它允许我们为数据添加交互性。在这个特性的帮助下,我们可以通过选择、点击或悬停来与可视化交互。动作可以在可视化中执行以下任务:

作者图片

现在,让我们通过以下步骤设置一个工作表操作:

  • 转到工作表>动作并添加Change Parameter动作。这是因为我们想要创建一个相应地改变参数的动作。

作者图片

  • 接下来,我们将动作命名为Update Discount Parameter,并选择相应的Target ParameterValue,在本例中分别为Discount ParameterAVG(Discount)

作者图片

  • 类似地,我们将创建另一个名为Update Profit Ratio Parameter的动作。

作者图片

现在,当我们单击一个数据点时,它就成为象限的中心。因此我们现在得到的是一个动态象限图。我们可以看到,改变利润率和折扣的值会改变产品在象限中的位置。

作者图片

结论

在本文中,我们学习了如何将散点图转换为交互式象限图。象限图给出了数据的总体视图。作为一项活动,您可以从现有数据集中选择两个字段,并在这些字段中执行象限分析来巩固这一概念。

二次判别分析

原文:https://towardsdatascience.com/quadratic-discriminant-analysis-ae55d8a8148a?source=collection_archive---------5-----------------------

二次判别分析(QDA)理论和 Python 实现的深度介绍

由 QDA 生成的决策边界的图示。图片作者。

内容

这篇文章是我将要发表的一系列文章的一部分。你可以通过点击这里在我的个人博客上阅读这篇文章的更详细版本。下面你可以看到该系列的概述。

1.机器学习导论

  • (一)什么是机器学习?
  • (b)机器学习中的模型选择
  • (c)维度的诅咒
  • (d)什么是贝叶斯推理?

2.回归

  • (a)线性回归的实际工作原理
  • (b)如何使用基函数和正则化改进您的线性回归

3.分类

  • (a)分类器概述
  • (b)二次判别分析(QDA)
  • (c)线性判别分析
  • (d)(高斯)朴素贝叶斯

设置和目标

如上一篇所述,生成分类器对输入和目标变量 P ( xt )的联合概率分布进行建模。这意味着,我们将最终得到一个可以生成(因此得名)具有各自目标的新输入变量的分布。

这个模型,我们将在这篇文章中看到,属于一个叫做高斯判别分析(GDA) 模型的类别。现在是术语开始变得棘手的时候了!注意,高斯判别分析模型是生成模型!尽管它的名字是歧视性的,但它不是歧视性的。

给定具有相应目标变量 tN 输入变量 x 的训练数据集,GDA 模型假设类条件密度是正态分布的

其中 μ类特定均值向量,而σ类特定协方差矩阵。利用贝叶斯定理,我们现在可以计算后验概率

然后我们将把 x 分类到类中

衍生和培训

对于每个输入变量,我们定义了 k 个二进制指示变量。此外,让 t 表示我们所有的目标变量,并且 π 先验带有表示类的下标。假设数据点是独立绘制的,则似然函数由下式给出

为了简化符号,让 θ 表示所有的类先验、类特定的均值向量和协方差矩阵。我们知道,最大化似然相当于最大化对数似然。对数可能性是

扩展(1)将在接下来的衍生中极大地帮助我们:

我们必须找到特定类别的先验、均值和协方差矩阵的最大似然解。从先验开始,我们必须对(2)求导,将其设为 0,并求解先验。然而,我们必须保持约束

这是通过使用拉格朗日乘数 λ ,而不是最大化来实现的

使用来自(2)的结果,我们然后对(3)关于类特定的先验求导,将其设置为等于 0,并且求解

其中 Nc 是 c 类中数据点的数量。使用约束的知识,我们可以找到 λ

λ= N代入(4),我们得到

(5)告诉我们类别先验仅仅是属于类别的数据点的比例,这在直觉上也有意义。

现在我们转向最大化关于特定类别均值的对数似然。同样,使用(2)的结果使我们很容易求导,将其设为 0,并求解

为了评估这个导数,我们使用一个矩阵演算恒等式。具体身份可以在我个人博客的我更详细的帖子里找到。

然后我们得到

让我们花一点时间来理解(6)的意思。(6)左侧的总和仅包括属于类别 c 的输入变量 x 。然后,我们将向量的和除以类中数据点的数量,这与取向量的*均值相同。这意味着特定类别的*均向量是属于类别的输入变量的*均值。同样,这也有直观的意义。

最后,我们必须最大化关于特定类别协方差矩阵的对数似然。同样,我们使用(2)的结果求导,将其设为 0,然后求解

这个导数需要 3 个属性,我已经在我的个人博客上的中列出了我这篇文章的更详细版本。然后我们得到

就像类特定的均值向量只是类的向量的均值一样,类特定的协方差矩阵只是类的向量的协方差,我们最终得到我们的最大似然解(5)、(6)、(7)。因此,我们可以使用以下内容进行分类

Python 实现

让我们从一些数据开始——你可以在下面的图表中看到。你可以在这里下载数据。

要分类的数据点被绘制成散点图。图片作者。

下面的代码是我们刚刚讨论过的 QDA 的简单实现。

我们现在可以用下面的代码进行预测。

这为我们提供了高斯分布以及预测,如下所示。

使用 QDA 发现的高斯分布图,以及数据点的预测类别。图片作者。

为了更容易地说明 QDA 是如何工作的,以及它工作得有多好,我们可以绘制决策边界上数据点的原始类别。这显示在下面。

拟合 QDA 的判定边界以及数据点的原始类别。彩条显示属于类别 1 的概率。图片作者。

摘要

  • 二次判别分析(QDA)是一个生成型模型。
  • QDA 假设每个类别都遵循高斯分布。
  • 特定于类别的先验就是属于类别的数据点的比例。
  • 特定类别的*均向量是属于类别的输入变量的*均值。
  • 特定于类别的协方差矩阵就是属于类别的向量的协方差。

数据管道中的质量保证

原文:https://towardsdatascience.com/quality-assurance-in-data-pipelines-19ebe1cb1a8e?source=collection_archive---------32-----------------------

在您的数据解决方案中建立信任的技巧

Zan 在 Unsplash 上的照片

数据工程师的核心职责是构建数据管道。数据从一个位置流向另一个位置,途中会发生变化,这是任何数据工程师的主要关注点。然而,当你改变数据时,你使人们感到紧张。即使您正在积极地将数据从原始形式转换成可用的形式,这也不会改变它不再是所收集的数据这一事实。为了减轻这些担忧,数据工程师在解决方案中尽可能多地构建数据完整性措施至关重要。

质量保证技术

1.逐字节比较

执行逐字节比较以确认数据的完整性。逐字节比较包括以下内容:

  1. 接收源数据,转换它,并将其加载到目标中
  2. 读取目标中的数据,反向转换,并逐字节与原始源进行比较

虽然逐字节比较可以确认数据的完整性,但是就时间和资源而言,这是非常昂贵的操作。这也增加了开发工作的复杂性,因为您不仅需要开发转换,还需要开发逆转这些转换的逻辑。但是,它可以与常规接收过程一起执行,以确保数据完整性。

*用例:*这可以在大多数管道中使用,因为它转换数据并反转转换以确保有效性。

2.总和检查(checksum 的复数形式)

校验和允许我们检查数据完整性,而不需要像逐字节比较那样多的开销。有几种计算校验和的方法,但通常涉及对哈希值求和。换句话说,如果您计算源数据的校验和,并将其与目标数据的校验和进行比较,只要数据中没有突变,这两个值就应该匹配。

*用例:*这可以用在数据集从源到目的地不会改变的地方。

3.行数

确认数据完整性的一个代价不高的操作是简单的行计数。如果将源中的行数与目标中的行数进行比较,应该是相同的。如果没有,可能会出现意想不到的转变。但是,行数应该仅用作数据的第一次检查。依靠行数来验证整个数据存在许多问题。它不验证实际数据,也不考虑迁移过程中可能出现的空值。

*用例:*这可用于数据集从源到目的地不会发生变化的情况。

4.单元测试

单元测试是你开发的任何代码的标准;尤其是主动改变数据的数据管道。管道可以在微观和宏观层面进行测试。微单元测试着眼于单个方法。这些测试更容易创建,因为您不需要一个完整的数据集来确认它们的功能(例如,如果一个函数要过滤掉空值,一个具有各种数据类型的简单矩阵就足够了。).宏单元测试将管道作为一个整体来关注。宏单元测试稍微更难测试,因为它涉及一个完整的数据集,以确保所有的转换步骤都按预期执行。单元测试对于确保在数据管道中的任何和所有突变期间保持数据完整性是必不可少的。

*用例:*这可以在大多数管道中使用,因为它转换数据并反转转换以确保有效性。

最后的想法

作为一名数据工程师,在构建管道时,数据完整性应被视为重中之重。它不仅让您确信您的代码正在按预期运行,还让用户确信他们的数据是准确的。通过确保数据功能的任何移动或突变符合预期,将完整性融入您的解决方案。希望上述技术为构建可靠的解决方案提供了一个良好的起点。

高质量的核心数据和非参数模型使得以前不可能的数据项目成为可能

原文:https://towardsdatascience.com/quality-core-data-non-parametric-models-are-enabling-previously-impossible-data-projects-31aa3751b79f?source=collection_archive---------29-----------------------

我们建立了一个机器模型,预测超级碗将会有创纪录的低收视率。这是它必须首先学会的三件事。

对于任何雄心勃勃的数据科学家来说,“也许这是一个疯狂的想法”这句话是一个需要仔细聆听的线索。解决问题和创造力是优秀数据科学家最重要的特质之一,因此,随着数据科学每年取得的巨大进步,你总是会听到一些疯狂的想法。

去年,随着全世界努力遏制新冠肺炎,许多零售、食品和快递公司的最大需求驱动力之一变成了电视体育节目。对许多美国人来说,聚在一起看球赛成了他们一周的一大亮点。我的团队建立了聚合和预测需求驱动因素(如会议、音乐会和恶劣天气)影响的模型,因此我们决定建立一个能够预测电视体育节目收视率的模型。可用的收视率数据总是赛后收视率,并且在指定的营销区域级别,而不是按县。我们知道食品零售商、配送集团和食品杂货需要前瞻性的、更精细的数据,并决定建立一个模型来识别这些数据。

我们知道这会很困难。但我们也怀疑,我们以前建立的一系列模型可能会派上用场。我们开始建立模型,学到了很多东西,必须部署尖端技术,我们最终实现了对电视体育节目收视率的准确预测。我想与其他数据科学家分享我们是如何做到这一点的,但在我们深入研究如何构建模型之前,对于任何拥有预测模型的人来说,主要问题是:它有效吗?

超级碗 2021 成功:我们预测的 9600 万与美国消费者新闻与商业频道赛后 9690 万相符

距离 2021 年超级碗还有两周,我们的模型给出了一个令人惊讶的预测——20 年来最低的超级碗收视率。它预测观众人数为 9600 万,比 2020 年减少 600 万,是 1997 年以来的最低水*。来自美国消费者新闻与商业频道的赛后观众达到了 9690 万——所以我们的新模型做得很好!

数据科学家经常被要求解释他们的预测。深入研究这一结果,我们发现这主要是由三个因素造成的:

  • 体育迷预测:由于 NFL 的受欢迎程度下降,体育观众人数低于去年。像大多数运动一样,它在 2020 年吸引了更少的观众。我们的模型考虑了几个因素,包括历史趋势和监测联赛受欢迎程度的变化。
  • 超级碗比赛球队排名:由于两支进入决赛的球队的受欢迎程度,正如他们的常规赛排名所反映的那样,我们的模型预测收视率比去年略低。这是因为尽管堪萨斯城酋长队是今年最受欢迎的球队,但他们对阵的是排名第十的坦帕湾海盗队。去年,酋长队与旧金山 49 人队比赛,这是第三大最受欢迎的球队。受欢迎程度对收看的观众数量有很大影响。
  • 游戏不确定性:这是我们模型中最有趣的特征之一——它计算了一个团队获胜的不确定性会对收视率产生多大的影响。一般来说,一场更不均衡的比赛会有更少的观众,因为每个人都认为他们已经知道结果了。鉴于今年酋长队对海盗队的不确定性较低,我们的模型预测粉丝收看今年比赛的概率较低,因此收视率也较低。

没有一个模型在第一次是完美的,虽然这个预测有 99.6%的准确性,但有很多死胡同,深夜和无尽的时间来努力思考。每一个机器学习模型都旨在提供比我们之前更多的上下文和智能,并在更多的时间内获得更智能、更准确的结果。我想分享我们一路走来学到的三大经验,这样任何致力于极具挑战性的新模式的团队都可以更快地获得更好的结果。

图片由 PredictHQ(作者)提供

1.当涉及到你的核心数据和假设时,要注重质量

在我们最初几周的实验后,我感到很失败。我们设计了一个基于外部历史收视率数据的监督机器学习架构,以预测未来游戏的收视率。我们调查了几个外部观众数据来源,这些数据虽然对一些用例非常有用,但并不适合我们的用例(预测)。它不起作用。我和我的团队尝试了十多种不同的方法来评估历史游戏的收视率,但我们运气不佳。

我已经让我们公司管理团队的其他人知道,周五的工作不尽如人意,并一直持续到周末。在网上冲浪时,我意识到有一种方法,我们可以在不依赖外部收视率数据的情况下进行预测,以建立有监督的机器学习模型。我们将估计每个县的体育迷数量,然后基于每场比赛的数百个因素建立一个概率模型,以确定粉丝群中有多大比例会收看某场比赛。

这是可能的,因为我们已经为 predict HQ 建立了一个广泛的实体系统,它跟踪全美的每一场体育比赛。为了准确地做到这一点,我们还使用超过五年的历史数据来跟踪团队、个人表演者、场地等等。例如,我们知道每个比赛场地的确切纬度和经度,因此很容易确定比赛场地距离其所在县的距离,我们确认这对于观众人数有重大影响。我们还知道哪些球队的球票售罄,比赛时间对门票销售的影响等等。

这为我们建立模型提供了可靠的基础。它支持深度分割,这对于高度复杂模型的准确性至关重要。但是,如果不采取一个飞跃,不使用新兴的数据科学方法,我们就无法创建我们的模型。

2.不要回避创新:我们的模型需要一个非参数模型来工作

长期以来,概率模型对数据科学家来说一直很有价值。我们想使用一个,因为可解释性对于数据科学家来说非常重要——特别是当他们的工作是为需求预测决策提供信息,可以产生数百万美元的额外收入和节省时。

但是,当你在处理一个全新的问题和大量的数据时,概率模型也会变得难以处理。我们需要创建一个可以从数据本身学习的模型,而不是我们的团队设置参数、测试和检查。

参数概率模型需要强有力的数据假设才能发挥作用。但实际上,真实的数据分布远比那些数据假设复杂,因此参数模型不足以正常工作。然后,您需要让模型自由地从数据中学习——纯粹地、直接地。这些传统的参数模型需要强大的学习曲线,在我数十年的数据科学生涯中,我确信这只能来自高级的非参数元素。将新方法整合到模型中可能令人望而生畏,但数据科学作为一种职业发展如此之快。几年前甚至几个月前都不可能实现的壮举现在都有可能实现。我们的非参数模型通过不对映射函数做出假设,从我们的深度变换的原始特征中捕获和学习非线性关系。

例如,我们需要准确地模拟球迷在赛季不同阶段的运动兴趣差异(这只是数百个具体因素中的一个)。我们的体育 SME 和研究表明,一般来说,我们认为人们的兴趣会随着赛季的进行而增长,在季前赛开始时会有所下降,到季后赛时会有所增加。但我们也意识到这种趋势是非线性的,因为其中存在一些局部极大值,如常规赛的开球比赛等。我们需要一种非参数方法来确定趋势和起作用的因素,例如团队表现和不确定性。不使用非参数方法会阻碍我们、模型和我们所有的客户。对于参数方法,我们需要做出假设,进行线性回归和假设,但我们必须重复数千次(数年的工作)才能得出结论,而对于非参数方法,我们的非参数模型在最初几周内就确定了结论。

3.以客户为中心:与精心挑选的用户一起测试你的模型

一旦我们有了一个模型,我们确信它运行良好,并正在达到激光般的精度,我们就联系了一些最积极参与的食品和送货客户,与我们的团队一起测试它。

这是至关重要的。作为一名数据科学家,很容易专注于科学以及实验和发现的乐趣。但是如果你不能让你的工作为你的公司和客户带来价值,你就不会长久地创造令人兴奋的工作。

我们使用双轨基准系统来评估我们的模型:

  1. 在全国范围内,根据转播后的估计模型,我们对已经发生的比赛的预测观众人数与历史观众人数相差不大。从上面的图表中可以看出,我们的预测与外部赛后来源类似,如 NBC 的赛后收视率数据。
  2. 我们还跟踪我们的收视率数据是否与对我们关键客户需求的预期影响相关。

跟踪这两个元素对于创建预测级别的数据至关重要。与客户交易数据不相关的准确预测表明,我们没有为更智能的需求预测识别相关信息。

大胆用数据解决问题

这只是三课,我们还学到了更多。但我想尽快分享这三个,因为我知道有这么多数据科学家正在展望 2021 年及以后,并面临重大挑战。随着我们进入一个期待已久的高度分散的复苏,企业需要数据驱动。数据科学家站出来宣传数据科学对于需求规划、公司效率和创新的真正力量从未像现在这样重要。

图片由其他的高温层上不飞溅

质量多样性算法:地图极坐标

原文:https://towardsdatascience.com/quality-diversity-algorithms-a-new-approach-based-on-map-elites-applied-to-robot-navigation-f51380deec5d?source=collection_archive---------24-----------------------

思想和理论

一种基于地图精英的机器人导航新方法

进化算法在包括机器人在内的许多应用领域都占有重要地位。在本文中,我们将首先介绍机器人学中的导航问题。之后,我们将展示为什么寻找多样性比寻找质量更好,并尝试一些众所周知的方法来做这种事情。最后,我们将提出一种新的直觉,将质量与多样性联系起来,以胜过面向质量和多样性的算法。

导航任务

在机器人学中,通常将代理粗略地表示为几个收集关于环境的传感信息的传感器和几个可以在离散或连续范围内取值的参与者。

代理人与其环境之间关系的代表图(由我绘制)

在这篇博文中,我们对导航任务感兴趣。这个任务由一个带有接*传感器的智能体组成,它在环境中移动以达到目标。
我们的实验传感器是布置在智能体周围的测距仪和雷达,如取自【3】的插图所示。

演员是一个马达,它可以向前或向后产生一个脉冲,向左或向右产生一个脉冲,这两个脉冲都用速度来表示,在实数区间[-2,2]内取值。

代理传感器(测距仪和雷达)由我。

环境是一个迷宫,只有一个目标,代理必须通过最小化移动距离和碰撞来达到。

在他们的文章中,Lehman 和 Staley[3]使用了两个迷宫,一个他们称之为“中等”,但它更像是一个“标准”迷宫。他们认为第二个是“困难的”,因为跟踪目标的距离会导致欺骗行为。

我在模拟中使用的迷宫拓扑

Lehman 和 Staley[3]在他们的文章中声称,在第二种配置中,如果不仅仅依赖于每个解决方案的质量(在这种情况下是到目标的距离),而且还依赖于群体的多样性,那么探索会更有效。

因为在下文中,我们将探索不同的最先进的方法,以一种进化的方式将质量和多样性结合起来,所以在硬地图上评估它们的性能更方便。

技术要点

在下文中,我们将解释用于构建神经网络控制器的框架,之后,我们将解释进化算法的模拟过程,以评估每个神经网络的行为。

神经网络设计

根据传感器的数量和机器人演员的数量,我们可以想到的处理这种导航任务的每个神经网络都具有相同的输入数量和固定输出数量。

我将要使用的神经网络的架构(由我使用)

在进化算法的上下文中,我们必须区分两个概念:个体的基因型和表现型(在这种情况下是神经网络)

  • **基因型:**在这种情况下,基因型是权重的向量,其定义了神经网络将从其从传感器获得的输入生成输出的方式;更一般地,基因型指的是制剂的隐藏构型。
  • 表现型:这一术语指的是与编码隐藏特征的基因型相反的药剂的实际可观察行为。在我们的例子中,表型是当使用以基因型权重为参数的神经网络时,代理在迷宫中采取的轨迹。

我们的设计框架必须考虑并允许访问神经网络基因型的紧凑表示。为此,我们将使用 PyTorch 框架,并为我们所有的神经网络创建一个基类;之后我们将设计的每一个神经网络都将扩展这个类来继承基因型操作方法。

例如,我们能想到的最简单的架构是一个多层神经网络,它有一个双曲正切激活函数,该函数被缩放 2 倍以适应我们的动作域;我们扩展基类来继承所有操纵神经网络基因型的功能。

体验的模拟

既然我们有了一种从其基因型构建神经网络的方法,我们就必须模拟神经网络在迷宫中的行为,以评估这种行为(在这种情况下也称为表型)。

为了管理模拟,我们将使用 fastsim,可以在这里找到:【https://github.com/jbmouret/libfastsimT4https://github.com/alexendy/fastsim_gym一个健身房绑定。

在返回的观测值中,我们可以得到碰撞的次数和位置。两者都可以是每个个体表型的有效描述符。

最新算法综述

现在我们将回顾并试验一些最先进的算法来完成这项任务,

查新:

新奇搜索是最著名的基于多样性的算法之一。Lehman 和 Stahley 在[3]中介绍了它,它使用对每个个体的新鲜感的估计作为适应度函数。

新颖性独立于任务的上下文定义如下:

粗略地说,它是个体表现型到 k 个最*表现型的距离的*均值。它将促进导致新职位行为的基因型。

正如 Lehman 和 Stahly 所解释的,在困难的迷宫中使用这个目标函数会导致研究的分歧。

该图代表了 200 代生成的个体的最后位置。

地图-精英搜索:

在 JB Mouret 和 Jeff Clune[2]介绍的这种算法中,在我们对它的改编中,我们将迷宫分成网格单元,并将每个个体映射到与其在迷宫中的最终位置相对应的单元。

之后,我们将为每个单元保留“最佳”个体,在我们的情况下,这意味着可以以最少的冲突次数到达该单元的个体,在选择阶段,我们将从网格档案中选择个体,以确保我们选择的个体可以到达网格的不同单元。

我们现在可以像在前面的部分中一样,比较适合导向和地图精英方法中的个体。

正如我们所见,地图精英的选择在某种程度上是客观导向的。尽管如此,依赖于每个网格单元选择一个个体的选择过程,允许算法走出僵局,并增加多样性的选择压力。

**空间、层次、照明神经进化(SHINE) 😗*

这种方法是由 Davy Smith、Laurissa Tokarchuk 和 gerint Wiggins[1]提出的,

它通过绘制这些个体在迷宫中的最终位置来保存它们的档案。它不使用网格,而是使用基于树结构的分层映射;超参数“alpha”限制了树的高度。

每一片树叶都用一个基于新颖性的标准进行筛选,以保留最好的“beta”个体。

这种方法远远胜过新奇搜索和地图精英,正如我们在这个可视化中看到的。

这种可视化缺乏要点,因为该算法比其他算法在最小数量的代中到达出口,但是我们稍后将回到这一点。

超参数影响

正如你可能已经注意到的,所有的先例算法都依赖超参数来调节它们从一代到另一代保存和/或选择个体的方式。

查新

新颖性搜索是更“超参数自由”的方法;它只取决于 k,即计算新颖性分数时要考虑的邻居数量,除了极端值,这个超参数并不真正影响性能。

地图精英

MAP-Elites 算法由于使用基于适应度的方法来过滤每个网格单元中的最佳个体,所以受到单元数量或等效地受到每个单元大小的影响。

取极值时可以观察到细胞数的影响。例如,假设我们想要选择 10 个个体,如果我们有 1000 个单元,该算法将选择 10 个最好的单元,由于单元的尺寸很小,它们可以彼此非常接*,相反,如果我们在整个地图上只选择 20 个单元,选择将必须覆盖迷宫总面积的至少 50%,因此它将更加分散。

因此,当采用小网格尺寸时,算法可能无法走出僵局。

闪耀

最后,SHINE 算法依赖于两个超参数:每片叶子的最大个体数量和树的最大高度。尽管它使调整过程变得复杂,因为这会引起组合问题,但这两个参数对算法的影响更模糊,因此更难调整它们。

存档构建从 MAP-Elits 算法开始,但只有四个单元。每次产生一个新的个体,它被映射到一个单元,如果一个单元有多于一个的 alpha 元素,它被分裂成 4 个单元,并且在树中创建一个新的级别,如果算法因为树达到它的最大高度而不能创建一个新的级别,我们根据新颖性搜索标准从叶子中移除最差的个体。

我们可以看到,一片叶子的深度表明个体的网格有多小;这旨在允许非常频繁区域被更精细地切割。

质量-多样性权衡

在我们的研究中,我们将使用两个指标来跟踪进化算法的进展。

  • 我们可以测量群体的*均适应度,因为算法旨在优化适应度。这通常是一个很好的跟踪指标。
  • 由于算法旨在创建一个控制器,我们可以对迄今为止找到的最佳适应度感兴趣,以了解找到到达迷宫出口的控制器需要多长时间。

关于这两个指标,让我们分析优先算法的性能。

正如我们所看到的,SHINE 算法和新颖性搜索都优于 MAP-Elites,因为 MAP-Elites 单元中使用的适应度目标具有欺骗性。我们还注意到,SHINE 收敛得更快,因为它更精确地切割了迷宫中访问量最大的区域。

然而,作为地图精英中使用的适应度目标的副作用,我们注意到个体的*均质量不断增加。相比之下,其他算法的质量停滞不前,因为新奇的目标。

我的方法:地图-极坐标

这最后一部分将解释我在设计我的方法时所考虑的直觉和我所获得的结果。

从上面已经介绍的内容中综合出可以保留的内容

正如我之前所说的,所有先例方法的主要缺点是它们对超参数的依赖。然而,这些参数允许它们校准它们的质量-多样性权衡。

此外,我们看到 SHINE 算法优于其他算法的原因有很多;一个主要原因是它构造了定制大小的网格单元。

我的方法的一般原则

在我的方法中,我试图将我的档案从任何超参数中解放出来,同时维护定制大小的网格单元档案。

为了做到这一点,我首先注意到,当我们离目标很远的时候,求新是有用的。当我们开始靠*时,它变得越来越没用,所以与 MAP-Elites 相比,我做的第一个修改是,我使用极坐标来描述最终位置,而不是笛卡尔位置。我以目标为原点。

在那之后,为了适应 SHINE 的想法,我将网格大小缩放到指数级,当远离目标时有大的单元,当接*目标时有小的单元。

实验结果

让我们来看看这个新版的 MAP-Elits 与新奇搜索和 SHINE 相比表现如何。

如我们所见,我们以相当的速度到达目标。尽管如此,我们保留了地图精英算法的副作用,导致人口质量的整体改善。

还有一点,我们没有任何超参数调整。

参考书目

[1]d .史密斯,l .托卡尔丘克和 g .威金斯,2016 年。通过层次空间划分进行快速表型景观探索。

[2]让·巴普蒂斯特·穆雷和杰夫·克伦。(2015).映射精英照亮搜索空间。

[3]j .雷曼和 k .斯坦利,2011 年。放弃目标:仅通过寻求新奇的进化。进化计算,19 ,第 189–223 页。

特别感谢 Cassy Deplace 在这个项目中对我的帮助。

项目监督人:公关。斯特凡·东舍

质量>数量:使用训练动力学清理噪声数据集

原文:https://towardsdatascience.com/quality-quantity-cleaning-noisy-datasets-using-training-dynamics-97769b4aaa3f?source=collection_archive---------26-----------------------

看看最*的一篇论文,它利用一种新的信息源来清理分类数据集:训练动态

由纳吉布·卡利尔在 Unsplash 上拍摄的照片

有了深度学习的掌舵,我们已经看到了 NLP 领域中使用的数据量的巨大增长。然而,控制这些数据集的质量成为一项具有挑战性的任务。例如,数据集设计者蜂拥至众包*台,以获得注释任务的帮助。然而,这种不受控制的环境也会降低注释的质量。

在数据集制图论文中,作者提出了一种诊断分类数据集的方法,承诺将数据集的大小减少多达 70%,同时保持分布内(ID,在同一数据集的测试分割上)和分布外(OOD)性能!

资料图

这个想法很简单,非常直观。让我们直接进入(简单的 4 步)算法。

要求:

  1. 数据集应该已经针对二元或多类分类任务进行了标注。
  2. 在该数据集上训练一些时期的神经网络(作者使用至少 5 个时期来帮助收集所需的统计数据)

步骤:

  1. 训练:开始训练神经网络(使用 BCE 损失进行二元分类,使用 CCE 损失进行多类分类)。
  2. 统计数据收集:跨所有时期,为每个数据样本计算真实标签的概率(注意:不是预测标签)
  3. 最终统计计算:一旦训练结束,对每个数据样本计算**:**

a.置信度:所有时期真实标签的概率的*均值

b.可变性:所有时期真实标签的概率的标准偏差

c.正确性:占 ' 预测标签==真实标签的总时期的比例

4.最后,在数据图上绘制所有数据样本:一个 x-y 图,其中(x 轴为可变性)(y 轴为置信度**),也可选择(色调为正确性)。**

我以推特上的有毒言论数据集为例。在 roberta-base 模型上训练 5 个时期产生:

不同标签的不同图(0 =无毒,1 =有毒)。色调彩条:(黄色/浅色=高正确率,紫色/深色=低正确率)(注:图片由作者提供)

作为健全性检查,该图类似于作者在他们研究的数据集上看到的图。

**应用 1:发现错误标记的数据**

现在最酷的部分来了。让我们看看如何使用这些数据图在数据中找到错误标记的样本。

在上面生成的数据图中,位于“难以学习”区域的样本是被分配了属于其标签的低概率的样本(低置信度),并且模型也从未对此不确定(低可变性)!模型字面意思是告诉我们这些样品贴错标签了!

在手动检查示例数据集时,这些样本看起来确实被错误地标记了:

标签位于“难学”区域的样本。“有毒”的看似无毒,标着“无毒”的本质上明显是性别歧视(/有毒)。(注:图片由作者提供)

**应用 2:减少数据集大小**

作者已经表明,仅在来自“模糊”区域的总训练数据的 33%上的训练模型产生了相等(甚至更好)的 ID 和 OOD 总体性能。考虑到数据量的巨大减少,这一发现意义重大。

**应用 3:发现意外偏差**

对于“易于学习”区域中的样本,模型具有高置信度低可变性**。由于移除这些样本(仅保留“不明确的”样本)不会影响模型的性能,因此可以肯定的是,这些样本包含某些不需要的属性,这些属性会降低模型的可推广性,例如,非预期的偏差。**

密码

查看我的 GitHub 项目,该项目将该算法实现为 PyTorch Lightning⚡️回调。它超级容易使用,更多关于该项目的信息自述!

您也可以查看这个项目以找到 TensorFlow 回调实现。

结论

通过使用统计方法的样本过滤或注释校正,数据集去噪是一个流行的概念。然而,数据集制图论文从利用训练动态的有趣角度来处理这个问题。它有一点开销:在完整的数据集上训练一个模型一次,但我认为与它提供的简洁应用程序相比,这并不算过分。此外,引入一种新的媒介(训练动力学)为同一方向的进一步研究打开了大门。

参考

论文讨论:数据集制图:利用训练动力学绘制和诊断数据集,Swayamdipta 等人,2020 年

使用的示例数据集:可恶的符号还是可恶的人?Twitter 上仇恨言论检测的预测特征,Waseem 等人,2016 年

刑事法庭中的量化

原文:https://towardsdatascience.com/quantification-in-criminal-courts-d9162f75004b?source=collection_archive---------33-----------------------

法典正义还是算法不公*?

T 预测分析或风险评估工具侵入司法环境的例子是“COMPAS”在美国的实施(威斯康辛州,早在 2012 年,经过 20 世纪 90 年代以来的多年发展)。这种算法软件使用机器学习技术,在大量“数据”中寻找模式或相关性。法官使用它们来评估罪犯再犯的可能性,同时做出假释、缓刑、保释和量刑决定——谁有可能在未来的某个时间点再次犯罪或无法出席法庭听证会。

由于司法系统已经存在需要解决的传统问题,作为强大的高科技解决方案成果的累犯风险等级被选为“一种强大的、普遍的、不可阻挡的力量”。事实上,这种风险评估算法是重新设计的,(zavrnik,2019)[1],旨在改变传统的保释和判决系统,并最大限度地减少“导致法律适用不*等的人为偏见”(见 Samuel Greengard ,2020)。毫无疑问,这种量化的风险评分方法提高了法院决策过程的效率和效力,以[预测正义](https://www.nst.com.my/opinion/columnists/2020/02/565890/ethical-questions-risks-using-ai-predictive-justice%20(last%20visited%20Oct%202)的名义,生动地改变了刑事司法范式。

但现有的文献和研究表明,它们反而引起了强烈的担忧——用“通过排斥和歧视的历史产生的偏见数据”创建了一个编纂司法的系统2。、 ProPublica ,2016; Dressel & Farid ,2018; Deeks ,2019)。因此,司法部门的这种量化( Angèle Christin ,2015 年)同时被指明显具有影响力,并且"在宪法、技术和道德上令人不安"( Starr ,2014 年);这最终会助长决策过程中的不公*做法。现在,让我们来看看这种方法如何以及为什么会引起人们对法庭上量化和不公*做法的关注。

法典化的正义和算法的不公*:

“法典化——正义”的新趋势(参见例如。理查德 M. Re 等人。,2019)主张统一和标准化高于自由裁量权。刑事司法算法的支持者认为,它们更可取,因为它们可以通过技术手段提高法院的效率、可及性和一致性;也减少了法官的偏见、自由裁量权和任意性。因此,司法环境中的量化和标准化促使法官推动司法法典化的事业,尽管这有可能限制他们的司法自由裁量权。在描述限制自由裁量权的好处时,人们可能会认为人工智能算法是一种使司法判决更加一致和有效的简单解决方案;这将有助于确定法官和检察官对其决定的责任。

牛/Unsplash

但大量前沿研究和调查已经表明,算法透明度和可解释性的缺乏在司法背景下会产生严重影响,并在法庭实践中不利地破坏正当程序原则和公*原则。在一个特定的司法系统中,毫无疑问,公*和个性化的司法原则或自由裁量的道德判断是首要考虑因素。

因此,司法系统的基本价值将受到损害;这“倾向于以牺牲公*正义为代价来加强成文正义”(Richard m . Reet alT3)。, 2019).这就是为什么,尽管量化和智能计算的这种司法用途可以说改变了法官的态度和法院的做法,但公*原则和正义的理想教会我们将这些伤害最小化。

然而,预测分析的倡导者也认为,在处理保释、判刑和缓刑时,仍然有充分个性化刑事案件的空间。之所以如此,是因为累犯风险评分可能不是做出决定的唯一依据,法院仍然应该有酌情权和必要的信息,在适当的时候不同意评估。[3]但反对者在其他地方发现了问题,因为“这样的警告可能不会起作用,因为它倾向于向判决法院提供的信息数量而不是信息质量”,这最终可能导致“更严厉的判决”(Christin,Rosenblat&Boyd,2015),其基础是“未言明的临床预测”(Hyatt,Chanenson,T11 博格斯特伦,2011)。

再者,是这些分数依赖于群体数据的事实,所以累犯分数无法识别特定的高危个体;它更倾向于概率。此外,由于缺乏透明度和可解释性,对司法论坛中这种干预的效力知之甚少。

然而,考虑到司法系统中的公*和*等机会,也可以这样认为,只有专家才能理解的算法不能明确地为所有用户和主体提供公*的游戏;哪些司法机构,特别是法院,承诺这样做。同样,人们可能会认为,在发布这些(不公*和非法的)警告时,'卢米斯'法院清楚地表明了其愿望灌输对该工具的准确性和该工具对少数民族罪犯构成的风险的评估的普遍怀疑'。

在另一种意义上,有人恰当地指出,机器学习算法不可能符合惩罚的交流理论。[4]但这种叙述并不完全适用于审前保释或警务预测分析的情况,这发生在确定一个人的罪行之前(【焦】 ,2019 年),也有人恰当地认为,风险评估本身并不削弱惩罚的沟通潜力,或者不一定与莫里斯的“有限报应主义”理论不相容(加勒特和莫纳汉,2019 年)。

然而,同样不可否认的是,惩罚、威慑和康复的崇高考虑并未嵌入这些算法的当前版本( Angèle Christin et al,2015)。此外,风险评估没有充分纳入因果关系,相反,它“强调一个主要理由,而损害其他理由:丧失能力”。甚至“没有有说服力的证据表明累犯评估工具优于法官的‘非正式预测’(评估风险的个体-临床-判断),或者是歧视性更小的替代工具”( Starr ,2014)。因此,这当然是一个问题,这种不公*在司法环境中是永远不光彩的。

需要考虑的事项:

总之,提及电子前沿基金会( EFF )的提案是恰当的,该提案对计分系统的使用施加了限制。它生动地强调了精算工具应该是决定拘留个人的决定性因素,因为这种工具可以复制与依赖人类判断的现有系统相同的结果——甚至会犯下新的意想不到的错误(杰米·威廉姆斯,2018)。由于司法系统总是敏感的,必须是公*和可信的,任何人工智能方法都被认为不仅仅是消除偏见;他们还解释他们的结果,为用户解释它们,并提供结果如何得出的透明度(Colin Johnson,2018)。

换句话说,任何被告都应该有机会看到和质疑用于训练算法的“数据”和分配给每个输入因素的“权重”的延伸,而不仅仅是源代码(s ee Chander ,2017。总之,如果在使用的计算方法中有透明度、可解释性和可解释性,那么在法庭诉讼中也会推进和促进公*。然而,算法是否应该用于仲裁法庭判决的公*性仍然是一个复杂的问题。因此,仍然有一个更大的问题,即他们是减少了现存的不*等还是使之变得更糟。[5]

注意事项&参考文献:

[1]另见 alezavrnik,《算法司法:刑事司法环境中的算法和大数据》,欧洲犯罪学杂志 1–20(2019)。[这篇论文提到“人工智能工具将事半功倍,蒸发人类判断和推理中固有的偏见和启发”,这反过来将增加刑事司法机构的合法性,并将施加惩罚限制在“纯”科学方法和“理性”范围内]。

[2]参见 Ruha Benjamin,《技术之后的种族:新吉姆法典的废奴主义工具》(2019)[特别是针对偏见和默认歧视]。

[3]州诉卢米斯,881N . w . 2d 764–65(wis . 2016),证书申请。已提交,编号 16–6387(美国 2016 年 10 月 5 日)

[4]参见安东尼·达夫《刑法的境界》(牛津大学出版社 2018)。

【5]‌karen 郝与乔纳森流浪,你能让 AI 比法官还公正吗?玩我们的法庭算法游戏,麻省理工科技评论(2019)。[有人生动地争辩说,既然公*的概念在不同的环境中有不同的含义,它在数学领域也是如此。Karen Hao 等人恰当地举例说明了公*的两个定义:保持不同组之间的通行费错误率的可比性,以及以同样的方式对待具有相同风险评分的人。然后有人争辩说,“这两种定义都是完全站得住脚的!但是同时满足两者是不可能的。】

量化模型容量:VC 维度

原文:https://towardsdatascience.com/quantifying-model-capacity-the-vc-dimension-d4eb76dd26f7?source=collection_archive---------24-----------------------

VC 维是一种用数学方法来表示模型容量的方法。它也有实际的机器学习用途。请继续阅读,了解更多信息。

Ashkan Forouzani 在 Unsplash 上的照片

机器学习中的常识是,一些模型比其他模型具有更大的容量。例如,神经网络能够学习比线性模型多得多的各种功能。直觉上,这是有道理的。然而,拥有更高的容量在数学上意味着什么?模型容量如何影响测试集误差?幸运的是,有一个叫做“VC 维度”的重要量回答了这些问题。

我觉得理解 VC 维度最好的方法就是用例子,那就从一个开始吧。我们在 2D 的飞机上有两个点。我们有两种颜色,蓝色和红色。我们要问的问题是:我们的两个点是否有一种配置,使得无论我们赋予它们什么颜色,我们总能用一条线将蓝色和红色的点分开?让我们试一试。我们打算把这两点看成是这样:

图片作者。

现在,对于上述点的 2 = 4 种可能的颜色分配中的每一种,我们的目标是画一条线,使得所有的蓝色点在一边,所有的红色点在另一边。这很简单,我们可以展示所有 4 种颜色分配是如何完成的:

图片作者。

所以我们问题的答案是肯定的——存在两点的排列,因此有一种方法可以为所有可能的颜色分配分离颜色。在统计理论中,我们称之为粉碎。所以我们可以说两点可以用一条线击碎。

现在我们问同样的问题,只是带着三点。三分可以用一条线击碎吗?让我们用这三个点的排列来试试:

图片作者。

这种排列有 2 = 8 种可能的颜色。我们研究了所有 8 种安排,我们发现确实有三点可以打破:

图片作者。

在这一点上,我们应该澄清一些事情。看看这三个点的排列:

图片作者。

这种排列有一种颜色,因此无法用一条线将颜色分开:

图片作者。

那么为什么我们说 3 分可以粉碎呢?这不是反例吗?重点是**只要有一个**排列的 3 点可以粉碎,我们就说 3 点可以粉碎。不是所有的 3 点排列都要求是可破坏的。

最后,我们来考虑 4 点。在尝试了 4 个点的不同排列后,你会注意到,无论如何排列,这 4 个点总会有一种颜色,不能用一条线来区分。其中一种颜色是这样的:

图片作者。

换句话说,无论你怎么努力,都没有 4 点的排列是不可打破的。所以 4 分不能粉碎。

现在,我们已经介绍了粉碎的概念,我们可以谈谈风险资本的层面。简单来说,一组函数的 VC 维就是可以粉碎的最高点数。在我们上面的例子中,因为一条线可以粉碎 3 个点,但不能粉碎 4 个点或更高,所以线的 VC 维是 3。作为另一个例子,考虑所有常数分类器的集合。任何常量分类器甚至不能粉碎 1 个点,因为我们可以将该点的颜色设置为与分类器定义的常量颜色不同。因此,这个集合的 VC 维是 0。另一个例子是所有圆的集合。我们可以对这个集合进行非常类似的分析,就像我们对所有线的集合所做的那样,以显示圆的 VC 维度是 3(这是一个很好的练习,试试吧!).

对于更复杂的分类器,我们不能仅仅通过画一些图,试图分离彩色点来计算 VC 维数。然而,根据经验,VC 维度与模型的参数数量相关。例如,神经网络的 VC 维数与网络中的节点数和边数相关。更准确地说,已经表明,对于使用 sigmoid 激活函数的神经网络,VC 维数最多为 O(E * V),其中 E 和 V 是网络中边和节点的数量。

现在我们已经看到了一些 VC 维的具体例子,我们能在高层次上解释一下什么是 VC 维吗?简单来说,VC 维度衡量的是一个模型的容量。“容量”是指可以学习的不同分类的数量,它与可以分解的点的数量以及模型的有用性直接相关。想一想行量词和常量量词的区别。常数分类器甚至不能粉碎 1 个点(VCdim = 0),它们只能预测所有数据点的是/否。不是很有用,而且容量低。相比之下,线可以粉碎 3 点(VCdim = 3),使他们能够学习非*凡的模式。这更有用,容量也更高。然后我们有神经网络,其具有比线和常数分类器高得多的 VC 维数,因此具有高容量和有用性。这就是神经网络能学会下棋和开车,而线性模型和常数分类器不能的原因。

我们已经看到了如何计算 VC 维以及直观地计算它与模型容量的关系,但是它可以直接使用吗?事实证明,VC 维将训练误差与测试误差联系起来。最著名的结果是这个不等式:

我们称之为“VC 绑定”。图片作者。

n 是训练集的大小,D 是 VC 维数,1 — η是任意概率。这个不等式表明,在高概率下(将 1-η设置为接* 1),测试误差与训练误差相差一个常数,该常数取决于 VC 维数和样本大小。这是一个极其有力的声明。机器学习的一个基本问题是,你不知道你在训练集的良好表现是否会转化为测试集。这个不等式提供了解决这个问题的方法。如果你知道你的模型的 VC 维,你可以使用上面的不等式来保证你的测试误差不会超过一定的水*。

当然,一旦你代入 D、N 和η的值,你得到的界限可能太大而没有用。如果你的训练误差是 10%的错误分类,而界限说你的测试误差会比 50%的错误分类好,那并不能告诉你太多。这就是为什么我们在实践中很少听到 VC 维界限,因为通常测试误差超过 VC 界限。尽管如此,在你的脑海中保留这些类型的界限仍然是好的——因为它们仍然可能是有用的,并且因为在未来可能会找到更好的界限。

VC dimension 的另一个用途是它从数学上证明了模型容量和性能之间的权衡。我们已经讨论过低模型容量如何阻止我们学习有趣的东西。然而,高模型容量(VC 界中的高 D)也不都是好的。它导致*方根下的项增加,从而导致测试误差中更多的方差。这演示了过度拟合是如何发生的——模型的容量不必要的高。在我们还想用神经网络这样的高容量模型的情况下,该怎么做?VC 界也回答了这个问题:增加样本量 n

在本文中,我们讨论了粉碎和 VC 维的概念。我们看了几个简单的例子,通过一些分析可以找到 VC 维。然后,我们探讨了 VC 维度背后的直觉以及如何使用它,特别关注 VC 界限的后果。请留下任何问题/评论,感谢阅读!

量化 A/B 测试的价值

原文:https://towardsdatascience.com/quantifying-the-value-of-an-a-b-test-821aecfd2ef?source=collection_archive---------26-----------------------

在做决定之前,你应该付多少钱来获得更多的信息?

奈杰尔·塔迪亚恩多在 Unsplash 上拍摄的照片

介绍

数据科学的主要产品是信息。但是,这个产品是有成本的;收集、存储和分析数据需要时间和资源。那么,值得吗?

信息的价值(VOI) 的概念可以说为这个问题提供了最好的答案。根据维基百科,VOI 是决策者在做出决定之前愿意为信息支付的金额。

人类的经验是由众多的决定形成的。决策的范围从相对琐碎的——比如某一天在哪里吃午饭——到非常重要的——比如如何应对气候变化的威胁。

决策的一个主要障碍是不确定性。当我们选择一个特定的选项(一系列行动)时,我们通常不知道会发生什么。相反,我们继承了一个结果彩票*。有些结果比其他结果更好,因此有些彩票比其他彩票更有价值。当信息提供了一种可操作的手段来改善我们所面临的决策结果时,它就提供了价值。*

想了解更多关于 VOI 的信息,我强烈推荐埃里克·比克尔教授的演讲。计算 VOI 通常包括计算:

  1. 当前决定情况的值 *。*如果您不得不在没有额外信息的情况下做出决定,最佳行动方案是什么?结果抽签的价值是什么?
  2. **免费信息彩票的价值。**如果你可以免费收集这些信息,那么结果彩票的价值会是多少?

在大多数情况下,VOI 是具有自由信息的彩票的和当前决策情况的之间的差值。

A/B 测试的背景

A/B 检验本质上是双样本假设检验的别称。决策者(一家公司)必须选择推出一种产品的两种变体中的一种。这个产品可以是一个网站登录页面或广告。A 和 B 这两个变量都有一个未知参数θ,它描述了每个变量的功效。例如,θ可以是广告的转换率或点击率。为了了解哪个选项的θ更理想,决策者进行了一次控制实验。向一些用户显示变体 A,向一些用户显示变体 b。然后,实验的结果告知是否启动变体 A 或 b 的决定。

A/B 测试有频率主义和贝叶斯风格。布莱克·阿诺德的博客文章很好地概括了这些方法之间的差异。我在这篇文章中提出的方法本质上是贝叶斯 A/B 测试的扩展,它使用 VOI 而不是显式损失函数。

方法学

让我们想象一下,我们正面临一个可能从 A/B 测试中受益的决定。例如,我们有两个不同版本的网站登录页面——A 和 B,我们希望选择转化率更高的选项。

详细说明前科

首先,我们需要考虑我们之前对 A 和 b 的转化率的信念。

例如,我们知道转换率必须在 0 到 100%之间,这表明 beta 分布适合表达我们对两个登录页面转换率的先验信念。此外,我们知道转换率通常是个位数,因此我们可以将此信息纳入我们之前的分布规范中。

两个重叠的 Beta 分布,描述了我们对变量 A 和 b 的转换率的先验信念。

把它和$$$联系起来

一个必要的步骤是定义一个特定的转换率对我们的业务价值有多少。这种规格可能取决于各种因素,如产品成本和网站流量。最终,我们必须回答这个问题,“我们的企业愿意支付多少来提高 1%的转化率?”为简单起见,我们假设转换率和价值之间存在线性关系。

假设转换率每增加 1 %,对我们的业务来说就值 10,000 美元。从那里,我们可以将先前的分布表示为货币彩票,如下所示。

在指定了转换率和价值之间的关系之后,我们基于 Beta 先验获得了两个重叠的货币彩票。

货币彩票值多少钱?这个问题的答案归结为决策者的风险态度。在这篇博文中,我们假设风险中性,这意味着货币彩票的价值等于彩票的预期价值或均值。当一个给定决策的风险金额相对于决策者的总财富较小时,风险中性是一个很好的工作假设。我想在使用 A/B 测试的决策中通常是这种情况。

另一个有用的概念是当前 决策情况的值,这是在没有额外信息的情况下,如果我们必须选择 A 或 B,我们会选择的彩票值。在这种情况下,我们选择哪个选项并不重要;两种货币彩票的期望值约为 38,500 美元,这也是当前决策情况下的值。

换句话说,对我们的企业来说,部署其中一个变体而不进行任何测试的价值是 38,500 美元。

模拟 A/B 测试

既然我们已经指定了我们的先验信念,我们可以在收集任何数据之前模拟 A/B 测试的结果。为了模拟样本大小为 n 的 A/B 测试的结果(对于每个变量),我们

  1. 从先前的分布中提取转换率。
  2. 给定绘制的转换率,使用二项式分布从模拟 A/B 测试中为每个变量绘制转换率。

使用这种方法,我们获得了以下结果:

Variant A: 11 conversions out of 100
Variant B: 2 conversions out of 100

用结果更新我们的信念

基于这些测试结果,变体 A 看起来比变体 B 好得多。最初,我们不知道是 A 还是 B 更好,但这个结果无疑更新了我们的信念。

利用贝叶斯法则,我们可以推导出 A 和 B 的转换率的后验分布,即贝塔分布。关于贝叶斯更新过程如何工作的更多信息,我强烈推荐 Shaw Lu 的优秀博文。使用带有 Beta 先验的二项式似然的分析结果,我们得到如下所示的后验 Beta 分布:

如果我们获得了前面提到的测试结果,我们将更新我们对变量 A 和 b 的转换率的信念。

相应的后验货币彩票是这样的:

在观察测试结果和更新我们先前的信念后,更新的货币彩票。

在收集了这些假设的测试结果后,我们将选择选项 A,其更新的彩票的期望值约为 73,500 美元。

简而言之,这代表了 A/B 测试的可能结果之一——一个价值 73500 美元的决策情境。

模拟另一个测试

这次我们随机抽取这些结果:

Variant A: 4 conversions out of 100
Variant B: 5 conversions out of 100

然后,在更新我们的信念之后,我们将面临一个价值 44,100 美元的决策情况:

运行 A/B 测试可能会产生另一组可能的货币彩票。

请注意,这种“更新”的决策情况不如我们最初的决策情况有价值。这一发现并不令人惊讶,因为 A/B 测试可以告诉我们 A 和 B 的表现都很差。

模拟 10,000 次测试

所以我们随机抽取了两个可能的 A/B 测试结果——一个会给我们一个价值 73,500 美元的决策情境,另一个会给我们一个价值 44,100 美元的决策情境。接下来,让我们看看当我们以蒙特卡洛方式模拟 10,000 次 A/B 测试时会发生什么。下面显示了 10,000 个随机生成的 A/B 测试的决策情况值的直方图。

从 A/B 测试的 10,000 次模拟中得出的决策情况值(浅蓝色)的直方图。在每个模拟测试中,我们获得测试结果,更新我们的信念,并继承 A 和 b 之间更好的彩票。绿色虚线表示这次彩票的价值(期望值)。黑色虚线是我们当前决策情况的值。VOI 是经过测试的彩票价值和当前决策情况下的价值之间的差值。

上面的图表明 A/B 测试给了我们一个决策情况的抽签。

这张彩票的价值(*均值)大于我们当前决策情况的价值。为什么?因为我们可以收集数据并然后选择看起来更好的选项,而不是现在就选择 A 或 B(当前的决策情况)。

如果我们是风险中性的**,测试的价值就是有测试的彩票价值和没有测试的彩票价值之差。**

有可能在执行 A/B 测试后,我们将面临一个价值低于我们当前决策情况的决策情况。这并不意味着测试有负价值。事实上,VOI 不能消极。

VOI 作为样本量的函数

之前,我们计算了一个 A/B 测试的值,每个变量有 100 个参与者。但是测试的价值是如何依赖于参与人数的呢?为了回答这个问题,我用不同的样本量进行了多次蒙特卡罗模拟,每个变量的样本量从 0 人到 500 人不等。我还对蒙特卡洛结果应用了一个 LOWESS *滑器,以使趋势更加清晰。

据推测,参与者越多的测试越昂贵。因此,我假设每个参与者的成本为 25 美元,以在测试产生的价值和执行测试所需的成本之间建立权衡。测试的成本因各种因素而异,如数据收集服务的成本和推迟产品发布的成本。

结果如下所示。

使用蒙特卡罗技术计算的具有不同数量参与者的 voi 图。黑点是原始结果。绿线是拟合原始结果的*滑线。红线是假设的测试成本,随着参与者的增加而增加。当 VOI 大于测试成本时,测试是有利可图的(绿色区域)。当测试的成本超过它所提供的信息时,就会产生亏损(红色区域)。随着参与者数量的增加,随机误差趋*于零,使得 VOI 接*千里眼的数值。

正如所料,增加参与者的数量会增加测试的价值。这种趋势的存在是因为许多参与者的测试强烈地将我们的先验拉向真实的转换率值。因此,当我们对更多的参与者进行测试时,我们更有信心选择更好的变体。

请注意,测试的值似乎“变*”并收敛到一个特定的值。这个值被称为千里眼的*值,*本质上是完美测试的值。想象一下,去拜访一个透视者,他会看着水晶球,非常自信地告诉你变体 A 和 B 的转化率。在你拜访之前,你不知道透视者会告诉你什么,但是你知道在你拜访之后你一定会选择更好的变体。虽然计算千里眼的值超出了这篇博客的范围,但是我在我的存储库中的一个测试案例中使用了它。

透视的价值是数据科学中的一个重要概念,因为它建立了信息价值的上限。任何花费超过千里眼价值的分析或测试都不值得做。

最后,该图显示了使测试净值最大化的参与者数量,即 VOI 减去成本。

我们应该早点停止测试吗?

假设我们看了前面的图表,决定 100 个参与者的 A/B 测试使测试的价值最大化。在仅向 30 名参与者展示每个变体后,我们看到了以下结果:

Variant A: 2 conversions out of 30 
Variant B: 4 conversions out of 30

变体 B 看起来很有希望。因此,我们想知道我们是应该继续测试还是尽早停止测试并部署 B。在频繁的 A/B 测试中,尽早停止测试被认为是一种不好的做法。这是因为 frequentist 测试是以实现统计显著性为导向的,早于计划停止测试会增加假阳性率。

这里我们不关心假阳性;我们关心价值。因此,我们没有理由不能用目前收集的结果来更新我们的信念,给出如下所示的分布。

货币彩票在更新我们的信念后,每个变体有 30 个参与者。我们想知道继续测试是否有价值,或者我们是否应该只部署变体 b。

更新我们的信念后,我们可以像以前一样绘制 VOI 与参与者数量的关系图:

使用蒙特卡罗技术计算的 voi 图,从更知情的先验开始改变参与者的数量。在这种情况下,无论我们获得多少参与者,测试都是无利可图的。

在这里,我们可以看到,对于任何数量的参与者来说,成本都大于测试的价值。**因此,继续测试没有净值。**此时,最佳行动方案是部署 B,即使 A 和 B 之间的差异可能没有统计学意义。注意,这个结论取决于测试的成本。如果测试更便宜,继续测试以获取信息价值是值得的。

结论

这篇博文概述了量化 A/B 测试的经济价值的蒙特卡罗方法。一般方法如下:

  1. 使用统计分布指定关于变量品质因数的先验信念。
  2. 量化当前决策情况的值,这是我们在没有获得额外信息的情况下选择一个变体时所继承的彩票的期望值。
  3. 使用先验分布,随机生成潜在 A/B 测试的结果。计算每个模拟测试得出的决策情况的值。
  4. 取蒙特卡洛抽奖的决策情况值的*均值。将该*均值与当前决策情况的值进行比较(步骤 2 ),以获得测试值。

我希望这篇文章能提供信息,并展示如何通过价值创造的视角来看待统计测试。与其他只关注统计能力或显著性的方法相比,这种方法更符合 A/B 测试的实际目标。我认为 A/B 测试的目标不是确定更好的选择,而是产生价值。虽然这些目标通常是同义的,但在有些情况下,人们很容易投入过多的资源来检测两个变量之间的微小差异。质疑我们的假设并优化最重要的东西总是好的。

分位数编码器

原文:https://towardsdatascience.com/quantile-encoder-eb33c272411d?source=collection_archive---------19-----------------------

在回归任务中处理高基数分类特征

帕特里克·拉兹洛在 Unsplash 上的照片

在这篇博客中,我们介绍了分位数编码器和汇总编码器。这是与大卫·马斯普、约尔迪·宁、奥里奥尔·普约尔 & 卡洛斯·穆根合作完成的一篇已发表论文的简短综合。该项目包括:

  • 一篇会议论文
  • 在 category_encoders 库中的 python 实现
  • 一个开放数据实验库

TL;速度三角形定位法(dead reckoning)

我们修改了传统的均值目标编码(使用 M 估计正则化)以包含分位数而不是均值。这有助于推动机器学习模型。尤其是在使用广义线性模型和测量*均绝对误差(MAE)时。此外,这允许我们创建一组包含每个类别不同分位数的特征,从而进一步改进模型预测。关于会议论文集和类别编码器库中的实现的更多信息。

引用

要引用这篇论文,请使用:

[@InProceedings](http://twitter.com/InProceedings){quantile2021,
author="Mougan, Carlos and Masip, David and Nin, Jordi and Pujol, Oriol",
editor="Torra, Vicen{\c{c}}
and Narukawa, Yasuo",
title="Quantile Encoder: Tackling High Cardinality Categorical Features in Regression Problems",
booktitle="Modeling Decisions for Artificial Intelligence",
year="2021",
publisher="Springer International Publishing",
address="Cham",
pages="168--180",
isbn="978-3-030-85529-1"
}

介绍

回归问题已经在机器学习文献中被广泛研究,导致了大量的回归模型和性能测量。然而,很少有技术专门致力于解决如何将分类特征结合到回归问题中的问题。通常,分类特征编码器足够通用,可以涵盖分类和回归问题。这种特异性的缺乏导致表现不佳的回归模型。

我们深入分析了如何用分位数处理高基数分类特征。当考虑*均绝对误差时,特别是在长尾或偏斜分布的情况下,我们的建议优于最先进的编码器,包括传统的统计*均目标编码器。

最后,我们描述如何通过创建一组具有不同分位数的特征来扩展编码值(汇总编码器)。这个扩展的编码器提供了关于所讨论的分类特征的更多信息输出,进一步提高了回归模型的性能

分位数编码器

我们提出的编码包括使用不同于*均值的编码函数。*均值只是一个特定的统计量,我们可以通过使用其他聚合函数给出更丰富、更有意义的编码。对于这项工作,我们使用分位数作为不同类别中聚合目标的替代方法。

**正则化:**使用目标编码对分类特征进行编码时的一个常见问题是对于某些类别没有足够的统计量。

分位数编码器 M 估计正则化

x_i 是应用于类别 i 的正则化分位数编码器。
q(x_i) 是应用于类别 i 的非正则化分位数编码器,其是第 I 个类别中目标的普通分位数。
n_i 是类别 i 中的样本数。
q(x) 是目标的全局分位数。
m 是一个正则化参数, m 越高分位数编码特征越趋向于全局分位数。它可以解释为使局部贡献(类别的分位数)等于全局贡献(全局分位数)所需的样本数。

结果

让我们来看看它应该如何工作的代码片段:

为了对这种方法进行基准测试,我们使用 StackOverflow 数据集对这三种方法进行了基准测试。

Stackoverflow 数据集不同编码器的 MAE 比较

汇总编码器

分位数编码器的概括是计算对应于每个分类特征的不同分位数的几个特征,而不是单个特征。这允许模型具有关于该特性的每个值的目标分布的更广泛的信息,而不仅仅是单个值。这种更丰富的表示将被称为 Summary Encoder(也可以在 python 库中找到)。

汇总编码器方法提供了比分位数编码器更广泛的分类变量描述。在本实验中,我们根据经验验证了两者在应用于不同数据集时的 MAE 性能。在这个实验中,我们选择了 3 个分位数,将汇总编码器的数据分成相等的比例,即 p = 0.25、p = 0.5 和 p = 0.75。

使用交叉验证的 MAE 误差比较汇总、分位数和目标编码器。

上图描绘了实验的结果。请注意,与目标编码器相比,概要编码器的*均性能表现出更好的性能。在某些情况下,与分位数编码器相比,可以观察到相同的行为。必须注意的是,使用摘要编码器时需要格外小心,因为使用的分位数越多,过度拟合的风险就越大。分位数编码的这种使用需要更多的超参数,因为每个新编码需要两个新的超参数 m 和 p,这使得超参数搜索在计算上更加昂贵

结论

我们的第一个贡献是分位数编码器的定义,它是一种以比均值目标编码更鲁棒的方式对噪声数据集中的分类特征进行编码的方法。当类别以长尾或偏斜分布显示时,分位数编码使用比其他比较编码更合适的统计聚合来映射类别。

不同分位数的连接允许更广泛和更丰富的目标类别表示,这导致回归模型的性能提升,这种编码技术我们称之为汇总编码器

引用

要引用这篇论文,请使用:

[@InProceedings](http://twitter.com/InProceedings){quantile2021,
author="Mougan, Carlos and Masip, David and Nin, Jordi and Pujol, Oriol",
editor="Torra, Vicen{\c{c}}
and Narukawa, Yasuo",
title="Quantile Encoder: Tackling High Cardinality Categorical Features in Regression Problems",
booktitle="Modeling Decisions for Artificial Intelligence",
year="2021",
publisher="Springer International Publishing",
address="Cham",
pages="168--180",
isbn="978-3-030-85529-1"
}

参考

..[1]分位数编码器:处理回归问题中的高基数分类特征,https://link . springer . com/chapter/10.1007% 2f 978-3-030-85529-1 _ 14

..[2]分类和预测问题中高基数分类属性的预处理方案,等式 7,来自https://dl.acm.org/citation.cfm?id=507538

..[3]关于在树修剪中估计概率,等式 1,来自https://link.springer.com/chapter/10.1007/BFb0017010

..[4]加法*滑,摘自https://en . Wikipedia . org/wiki/Additive _ smoothing # Generalized _ to _ the _ case _ of _ known _ incidence _ rates

..[5]目标编码做对了https://maxhalford.github.io/blog/target-encoding/

..[6] Mougan,c .,Alvarez,J. M .,Patro,G. K .,Ruggieri,s .,和 Staab,S. (2022 年)。编码受保护分类属性的公*含义https://arxiv.org/abs/2201.11358

分位数是理解概率分布的关键

原文:https://towardsdatascience.com/quantiles-key-to-probability-distributions-ce1786d479a9?source=collection_archive---------2-----------------------

实践教程,数据科学家的数学复习

如果你在使用概率分布时感到困惑,这篇文章就是为你准备的。

由约书亚·厄尔在 Unsplash 上拍摄的照片

你多次遇到概率分布。你知道有几种不同的类型。但是在你内心深处,当你需要在实践中使用它的时候,你会感到困惑。概率分布和累积概率分布到底有什么区别?我应该检查 X 轴或 Y 轴上的置信水*或 alpha?如果是这样,这篇文章是给你的。最后,你会对使用离散或连续随机变量的概率分布感到舒适。让我们开始吧!

我们将在本文中讨论以下主题:

  1. 概率密度函数
  2. 概率质量函数(PMF)
  3. 累积概率分布(CDF)
    3.1 离散随机变量的累积概率分布(CMF)
    3.2 连续随机变量的累积概率分布(CDF)
  4. 概率分布汇总
  5. 分位数函数
  6. 感谢阅读和参考

1。概率密度分布(PDF)

正态分布的概率密度分布是人们听到“分布”时大多想到的。它有一个特殊的钟形:

标准正态分布的 PDF(零均值和标准差为 1)。来源:图片由作者提供

概率密度函数(PDF)将一个值映射到其概率密度[1]。这是一个类似于物理学的概念,物质的密度是其每单位体积的质量。例如,1 升水重约 1 kg,因此水的密度约为 1 kg/L 或 1000 kg/m。类似地,概率密度测量单位 x 的概率。

PDF 指的是一个连续随机变量,这意味着该变量可以取实数定义范围内的任意值。随机显示变量取值的不确定性。它给出了无限多种可能性,例如 0.1,但也有 0.101、0.1001 等。所以,连续随机变量等于给定值的概率为零。

PDF 图上的概率由密度曲线下的面积表示。一个点下的面积等于零。这就是为什么 PDF 用于检查随机变量落在给定值范围内的概率,而不是取任何特定值。例如,我们投资于该基金而亏损,从而导致回报为负的可能性有多大?这里我们考虑所有小于零的收益。

直观上,PDF *似于一条描述直方图的直线。例如,我们希望将实验中的 992 名参与者分成不同的年龄组(0-10 岁、11-20 岁等)。我们计算每个组中有多少成员,并在直方图上显示为条形:

992 名参与者分成不同年龄组的直方图。从正态分布生成的数据。图片作者。

我们随机选择的人成为特定年龄组成员的几率有多高?首先,我们必须将频率分布转换成概率分布。意思是根据每组参与人数计算概率密度。因为条形是矩形的,概率密度函数下的面积总是等于 1,所以我们可以使用一个简化的公式:

对于上图所示的频率,我们有:

现在,我们可以用密度代替 y 轴上的计数来绘制数据。红色曲线连接计算点,表示概率密度函数:

作者绘制的 PDF 图。红线是概率密度函数。

但是请注意,我从正态分布中为这个图生成了数据。这就是 PDF 和直方图如此吻合的原因。PDF 具有“封闭”形式,要求预先定义分布和参数(正态分布情况下的*均值和标准偏差)。直方图使用原始数据,所以它显示真实的分布。它允许检测异常,尤其是在有大量条形的情况下。

对用于描述分布的其他参数感兴趣(期望值、方差、偏度和峰度)?跳到这里:

根据上述分析,需要记住以下要点:

  • 概率是概率密度曲线下的面积(PDF)。
  • 连续随机变量取给定值的概率为零。所以,对于 x 的一个指定值,我们只能查概率密度,用处不大。
  • 这就是为什么我们关注值的区间。它允许我们对一系列值进行概率陈述。例如,有 50%的可能性参与者将至少 40 岁。

2。概率质量函数(PMF)

概率质量函数(PMF)指的是离散随机变量。与连续随机变量相比,离散随机变量只能取可数个离散值,如 0,1,2,…简单的例子是掷骰子、掷硬币或检测欺诈交易(要么有欺诈,要么没有欺诈)。

与连续随机变量类似,我们可以创建离散数据的直方图。但是不需要将值聚合到区间中。让我们考虑一对骰子的掷数总和。结果的数量是有限的,因为两个骰子上的值都是从 1 到 6。下图显示了 1000 卷公*骰子的直方图示例:

掷出 1000 次公*的一对骰子时的总和直方图。图片作者。

两个骰子都是公*的,这意味着从 1 到 6 滚动每个数字的概率是相同的,等于 1/6。所以最受欢迎的和是 7。类似于连续随机变量,我们可以将每个结果表示为一个概率。

如果我们掷一对骰子,有 36 种可能的结果(每个骰子有 6 个选项)。如果和等于 2,只有一种可能的组合:(1,1)。所以得到和等于 2 的概率是 1/36 = 0.0278。类似于 12 的和,只可能用于(6,6)。我们可以用同样的方法计算其他可能结果的概率。图中显示的结果创建了概率质量函数(PMF):

公*双骰子和的 PMF..图片作者。

总而言之,到目前为止,我们考虑了以下类型的情节:

  • 直方图是描述每个数值范围在数据集中出现次数的图形。它不需要任何关于分布的假设,但是我们必须预先指定条形的数量。直方图是根据有限数量的样本绘制的。直方图中所有条形的值之和等于样本总数。
  • 概率密度函数(PDF) 描述了连续随机变量的概率密度。PDF 上的概率是密度曲线下的面积。由于对于连续随机变量,给定值的概率为零,因此 PDF 用于检查变量落在给定区间内的概率。PDF 下的整个面积等于 1。
  • 概率质量函数(PMF) 描述了离散随机变量的概率。这意味着变量只能取可计数的离散值,如 0、1、2 等等。PMF 中所有离散值的概率之和等于 1。

虽然都是非常有用的,在业界也是常用的,但是还有一个重要的概率分布——累积分布函数(CDF)。

3。累积分布函数

随机变量 X 的累积分布函数(CDF)描述了 X 取值等于或小于 X 的概率(机会)。在数学上,我们可以将其表示为:

3.1。离散概率分布(CDF 或 CMF)的累积分布函数

以前面掷骰子的公*对为例,我们可以问:两个骰子之和小于或等于 3 的概率是多少?我们需要将和等于 2 的概率(0.0278)和和等于 3 的概率(0.0556)相加,所以 x=3 的累积概率是 0.0278+0.0556=0.0834。然后,我们对每个离散值重复加法过程,以获得离散概率分布的累积分布函数:

离散概率分布的累积概率函数。图片由作者提供。

从图中可以看出,最高可能结果的累积概率函数等于 1。因为两个骰子的和只能取整数值,所以一个图可以用条形表示:

离散概率分布的累积概率函数。图片由作者提供。

**3.2。**连续概率分布(CDF)的累积分布函数

连续变量的 CDF 的思想和离散变量是一样的。y 轴显示 X 取值等于或小于 X 的概率。不同之处在于,即使 X 轴上有微小的移动,概率也会发生变化。
考虑参与者年龄分组的例子,累积分布函数如下:

连续概率分布(CDF)的累积分布函数。图片由作者提供。

下图比较了均值为零且标准差为 1 的正态分布的 PDF 和 CDF:

正态分布 N(0,1)的 PDF 和 CDF。图片由作者提供。

我们可以得出结论:

  • CDF 是一个非减函数。它表示变量等于或小于 x 的概率,所以它只能随着 x 值的增加而上升。
  • 我们可以从两个图中检查概率,但是使用 CDF 更简单。CDF 在 y 轴上显示概率,而 PDF 在 y 轴上显示概率密度。在 PDF 的情况下,概率是 PDF 曲线下的面积。
  • 因为正态分布是对称的,所以 x=0(*均值)上的 CDF 是 0.5。
  • 左侧的 CDF 渐*于图右侧的 0 和 1。x 的精确值取决于分布类型和参数(正态分布的*均值和标准偏差)。

4.概率分布汇总

到目前为止,我们回顾了三种描述概率分布的方法:概率密度函数(PDF)、概率质量函数(PMF)和累积分布函数(CDF)。下表总结了 PDF 和 PMF 之间的主要区别:

PDF 和 PMF 的主要区别。图片由作者提供。

累积分布函数显示 X 取最大值 X 的概率。它将所有较低值和等于 X 的概率相加。因为 y 轴是概率,所以 CDF 的使用通常比 pdf 更直接。

下面的图表显示了每个分布的典型图形,从左上方开始顺时针方向:PDF,PMF,CMF,CDF。它概括了高级特征,并描述了给定类型的分布函数之间的关系。

不同类型分布的比较。作者的图片受[1,2]启发。

从上面可以看出,显示概率分布的不同方法之间存在某种联系。

  • 对于连续的随机变量,我们可以很容易地画出 PDF 和 CDF。PDF 下面的面积是一个概率,所以我们要积分才能把 PDF 变成 CDF 或者微分才能从 CDF 变成 PDF。
  • 对于离散随机变量,PMF 表示概率,CDF (CMF)表示累积概率。为了从 PMF 得到 CMF,我们必须把概率加到给定的 x 上。反过来(从 CMF 到 PMF),我们必须计算两个步骤之间的差。
  • 如果我们把所有的值分成一组面元(见上面直方图的例子),我们可以把 PDF 变成一种 PMF。它使用值/区间的范围,可以被认为是 PDF 的*似值。从离散累积分布到连续函数,需要某种形式的*滑。这可以通过假设数据来自特定的连续分布,如正态或指数分布,并估计该分布的参数来实现。以两种方式改变离散和连续的随机变量应该被认为是*似的。

5.分位数函数

让我介绍一下分布中的超级明星——分位数函数。它允许将分布用于许多实际目的,例如寻找置信区间和假设检验。

数学定义是分位数函数是分布函数在α的倒数。它指定随机变量的值,使得变量小于或等于该值的概率等于给定的概率:

其中 F⁻ (α) 表示 x 的α分位数

现在听起来可能有点玄乎,但细究一下就会释疑。假设我们要检查分布较低尾部总面积的 5%。我们称之为 x 的下 5%分位数,记为 F⁻ (0.05)。分位数是概率分布被分成等概率区域的地方。如果我们考虑百分比,我们首先将分布分成 100 块。当我们研究 PDF 时,第 5 个分位数是在分布的较低尾部切掉 5%区域的点:

正态分布 N(0,1)的下 5%分位数。图片由作者提供。

红线左侧 PDF 下的面积正好是曲线下总面积的 5%。这意味着有 5%的可能性。画红线的第一步是计算总面积的 0.05(这里 x=-1.645)。可以通过软件(例如 R 中的 qnorm() 函数或者 Python 中的 scipy.stats.norm.ppf() )或者手动使用 z-tables(这里的一个例子)来完成。

由于 CDF 在 y 轴上有概率(α),因此更容易在此找到该值:

正态分布 N(0,1)的下 5%分位数。图片由作者提供。

这表明 CDF 图是多么有用。我们可以通过两种方式使用 CDF:

  • 如果我们有一个 z 值(或 X 值,X 轴上的值),我们可以检查 X 取值等于或小于 X 的概率。例如,客户在网上商店花费的*均时间长度为半小时或更短的概率是多少?
  • 如果我们有概率,我们就可以检查切割给定α的面积的值。例如,在 90%的置信度下,我们可以说客户在网上购物至少花费了 X 个小时。

在上面的例子中,我们只考虑了单侧的 5%分位数(较低的尾部)。我们可以对两边 5%的概率做同样的事情。这意味着我们在 PDF 下寻找总面积的 5%,但分成 2.5%的下分位数(在图的左侧)和 2.5%的上分位数(在图的右侧)。

双边 5%概率。图片作者。

因此,分位数是这些图之间的直接联系。

基于这些图,我们可以说,我们有 95%的信心,真实参数(*均值)位于-1.96 和 1.96 之间。或者有 5%的可能性它位于从-1.96 到 1.96 的范围之外。

上述解释突出表明:

  • 置信水*告诉我们所考虑的事件发生的可能性有多大,或者给定参数在给定值范围内的可能性有多大。
  • α或显著性水*是一种概率。我们可以在 CDF 图上的 y 轴上检查它。阿尔法是一个负置信水*。

需要注意的几件事:

  • 反函数φ⁻(α)是 α分位数
  • 当α较小时,分位数也称为临界值
  • 一些分位数有特殊的名字。如果我们把概率除以 100,我们就有了百分位数。我们可以说第 5 百分位,而不是 5%分位数。4 分位数被称为四分位数,它们被分成 4 个部分,分别为 25%、50%(中间值)和 75%。
  • 对于关于零对称的标准正态分布(具有零均值和一个 N(0,1)的标准差的正态分布),我们有:

这在上面的图中得到了证明,因为我们在下尾部得到了-1.96,在上尾部得到了 1.96。

使用分位数、pdf、CDF,我们可以根据我们拥有的信息回答不同的问题,例如:

  • 考虑样本均值,包含总体均值的值的范围是多少,我们有理由相信?“合理地”可能采用不同的百分比值,取决于我们研究的目标。
  • 我们有多大的信心可以说回报不会是负的?

感谢阅读!

我很高兴你看到了这篇文章的结尾。我们经历了不同类型的概率分布:概率密度函数(PDF),概率质量函数(PMF),和累积密度函数(CDF)。然后,我们讨论了数量函数。它链接了描述发行版的不同方式(PDF 和 CDF ),并允许我们以一种非常实用的方式使用这些发行版。我希望这对你来说是一次激动人心的旅行。

记住,学习(数学)技能最有效的方法是实践。所以不要等到你觉得“准备好了”,就拿起笔和纸(或者你最喜欢的软件),自己尝试几个例子。我为你祈祷。

我很乐意在下面的评论区听到你的想法和问题,可以直接通过我在 LinkedIn 的个人资料或者在akujawska@yahoo.com 联系我。后会有期!

您可能还喜欢:

*

参考

[1] A.B .唐尼:“想统计。Python 中的探索性数据分析"

[2] C .亚历山大(2008):《市场风险分析》。第一卷:金融中的定量方法”。约翰·威利父子有限公司,国际标准书号 978-0-470-99800-7。*

预训练 BERT 模型的定量评估

原文:https://towardsdatascience.com/quantitative-evaluation-of-a-pre-trained-bert-model-73d56719539e?source=collection_archive---------15-----------------------

思想和理论

一个先决条件使用一个预训练模型作为**,没有微调**

**图一。**对预训练的 BERT 模型进行定量评估。该测试定量地评估预训练模型的(a)通过模型预测屏蔽位置的能力的上下文敏感向量,以及(b)通过检查屏蔽短语的向量质量的[CLS]向量质量。底层词汇向量的聚类质量,尤其是实体类型到聚类的分离在其中起着隐含的作用。该测试通过使用三元组的测试数据集来完成(具有屏蔽短语的句子、句子中的屏蔽短语、句子上下文中的屏蔽短语的实体类型)。模型在句子上的性能由屏蔽位置的预测的实体类型和屏蔽短语的[CLS]向量来确定。屏蔽位置或[CLS]向量的预测的实体类型由上下文无关向量的聚类确定-其质量由聚类的性质定性地确定(实体类型的分离程度)。定量测试产生混淆矩阵和每种实体类型的 F1 分数。作者创作的图像。

TL;速度三角形定位法(dead reckoning)

自我监督的学习正在使用变形金刚大规模使用,不仅用于文本,最*还用于图像 ( 剪辑 对齐 ) ,以解决传统的监督任务*(例如分类),要么原样,要么进行后续微调。虽然大多数(如果不是全部的话)下游 NLP 任务被执行,但是迄今为止,随后对预训练的变压器模型进行微调,有可能照原样使用预训练的模型,而没有随后的微调*。

例如,一个预先训练好的 BERT 模型对于多种 NLP 任务的效用**,没有任何微调就被大大忽略了。**直接使用预训练的 BERT 模型而无需微调的例子有

  • 无人监管的 NER 。 NER,传统的监督任务可以完成,而不必标记句子中的单个术语。相反,对于感兴趣的实体类型,一次性标记 BERT 词汇向量集群就足够了,其中词汇向量直接从预先训练的模型中获得。
  • **无监督的句子表述。**训练有素的关于下一句预测的 BERT 模型可用于创建短语、句子片段或完整句子表示,其表现与在监督下创建的句子表示一样好,甚至在某些任务中取代它们。
  • Un 监督同义词采集 *(或一般关系抽取)。*预训练的 BERT 模型与依存解析器/位置标记器组合可用于关系提取基线,并为同一任务的下游监督模型创建弱监督标记数据。
  • **条件句分类。**在给定输入句子中指定的术语的情况下,与依存解析器/词性标注器组合协同工作的预训练 BERT 模型可用作选择句子分类任务的基线模型,并为同一任务的下游监督模型创建弱监督标记数据。
  • **弱监督带置信度评分。**为任何下游微调任务创建带有关联置信度得分的标记数据。下游微调模型可以学习使用置信度得分作为附加的输入特征。这对于选择性地手动处理那些低置信度的输入来提高模型性能也很有用。
  • **下游微调任务的训练集覆盖量化。**在训练鉴别器模型 p(y/x)时,量化训练集对底层分布 p(x)的覆盖。在为监督任务创建标记数据集时,这在实践中非常有用,在监督任务中,预先训练的模型被微调。
  • **相对于预训练模型量化微调模型的学习。不仅根据测试集分数,而且根据底层输入分布样本(由训练集捕获)如何映射的相对方式,对微调模型的学习进行量化(与预训练模型创建的聚类相比,微调可以改变从训练集创建的聚类的性质)。
  • **在部署期间检测 OOD 或罕见输入。*为部署模型的输出(可以是预训练模型或微调模型)*赋予置信度得分,特别是对于它很少或从未见过的输入。这在模型的生产部署中变得至关重要——以识别 OOD *(不在分布范围内)*或在培训期间很少出现的情况并检测错误分类/误报。

然而,预训练模型的这种直接用例突出了对定量 ( )的需要,这与任务上的预训练模型的定性测量 ) 评估方法相反,而不仅仅是模型训练损失*(或固定步数的训练)*。

本文的前半部分描述了这种定量测试的实现。文章的第二部分描述了预训练步骤——自定义词汇生成、数据准备、多 GPU 预训练、预训练的超参数选择等。下面描述的所有模型的预训练都是在托管英伟达 DGX A100 机器的核心科学云上进行的。

需要评估的 BERT 组件

BERT 模型的自我监督训练产生

  • 词汇向量。这些是上下文不敏感的向量,其中一个单词的所有含义都被压缩到一个向量中(例如,细胞——其所有含义、手机、监狱细胞、生物细胞都被压缩到一个向量中)
  • 将由所学习的词汇向量表示的输入句子转换成每个符号化输入单词的上下文敏感向量的模型权重。模型权重也有助于为输入句子创建丰富的句子表示*(假设模型在下一个句子预测任务中被训练)*。

虽然在之前已经对预训练模型进行了定性检查,但本文描述了一种同时测试所有三个输出的评估方法

  • 句子中屏蔽短语的模型上下文敏感输出向量的定量度量
  • 句子中屏蔽短语的[CLS]向量的定量度量
  • 上下文无关词汇向量聚类的定性和定量测量,其计数是定量测量*(大量的单个聚类意味着不适当的/较差的预训练模型),并且聚类的实体分离水*是定性测量(考虑到词汇向量的上下文无关性质,必然会有混合实体聚类)*。这些群集的质量对上面提到的两个定量测试有直接影响。

定量测试*(针对上述测试一和测试二)*产生一个混淆矩阵,其中包含每个实体类型的 F1 分数。

模型评估方法

从事实正确性的角度来看,BERT 对句子*(即使被删除的术语存在于底层词汇表中)中被删除的确切术语的预测是不可靠的,就像对任何语言模型一样,不管它是自回归模型还是自动编码器模型。然而,如果模型是在语料库上用真正代表输入句子的自定义词汇预先训练的,则 BERT 对屏蔽位置的实体类型的预测始终是准确的。也就是说,如果我们输入句子“_ _ _ 用于治疗癌症*”,一个训练有素的模型的最高预测将主要是药物和治疗。下面利用 BERT 的这种健壮的实体类型预测能力来评估模型*(这基本上是用于执行无监督的*NER的相同方法,在 以前的文章 中有描述)。

试验结果

三种实体类型*(药物、疾病和细胞类型)*的屏蔽位置预测的累积 F1 分数如下所示。对三种模型进行了预训练和评估——大套管、大无套管和基本无套管。

**图二。**通过下述方法预训练的模型的评估结果,以及可公开获得的预训练模型。作者创造的形象。

这篇文章的其余部分描述了完成上面列出的分数所遵循的训练前步骤。评估步骤也很详细。

培训前的步骤

从语料库预处理开始的预训练过程在实现良好的模型性能方面起着关键作用。

  • **语料预处理。**句子边界检测,在句子边界上分割句子,起到取得良好性能的作用。不在句子边界上折叠句子的缺点是在训练的最大 510 (考虑到 CLS 和 SEP) 边界限制时剪裁长序列的风险。对于超过句子长度限制的病态长句,一种方法是将它们以一定长度折叠成一个新行,其中换行的限制因素是句子的*均标记化长度。句子的标记化长度通常高于原始句子长度。理想情况下,可以使用用于训练的相同标记器对句子进行标记,然后基于它进行折叠,但对于大规模语料库来说,这可能是一个过于昂贵的操作——句子长度增加的*均度量(*~ 1.5–2x)可能是一个实用的选择。使用额外的换行符分隔段落和文档(即,两个换行符分隔文档,一个换行符分隔文档内的句子)*也是确保下一句预测的高准确度的关键,这反过来会影响[CLS]向量的质量
  • *词汇生成。在感兴趣的语料库上创建自定义词汇也在模型性能中发挥作用。如果预训练的目标不是为了学习数字术语(例如,年)和外语句子的良好表示,则确保所学习的词汇真正代表基础语料库的一种方法是消除纯粹是数字术语(不是任何其中含有数字的术语,如 CD20-a 基因)和外语字符(相对于感兴趣的语言)的单词(仅用于词汇生成)。一种简单的方法是删除这些术语,然后只添加数字 0-9(确保它们被重复足够的次数,以满足最小阈值,从而有资格被词汇生成挑选出来)*以确保它们出现在最终的词汇中。然而,请注意,生成训练记录的输入没有删除任何术语,它是应用了句子边界的原始原始语料库。这个原始语料库中的数字项将被转换成由数字项组成的子词。外语字符将被转换成[UNK]标记,保留它们穿插其中的句子结构。
  • 培训记录生成。 Google 的 Github 存储库有训练记录生成功能,支持全词屏蔽。PyTorch HuggingFace 也支持它——但是,它仍然不适合训练大型语料库,因为它将整个语料库读入内存。使用 Google code 生成训练记录可能需要很长时间,因为它是按顺序执行的*(甚至比实际训练时间还要长)* —所以最好通过拆分语料库*(使用 parallel 等)来并行生成训练记录。)*
  • 实际训练流程。谷歌用于培训的 TensorFlow 版本不支持多 GPU*(NVIDIA-SMI 输出可能会产生误导,除非使用 dmon 选项来查看实际的多 GPU 使用情况)。然而,Nvidia 有一个多 GPU 容器版本,它确实使用所有 GPU 进行训练 ( 在内部使用 Horovod)*——所以这可能是一个理想的选择。然而,Nvidia release 迄今为止还不支持整个单词屏蔽来生成训练记录——所以我们可能必须使用谷歌的记录生成过程,然后用 Nvidia 的代码进行训练。此外,如果选择了正确的学习率,Nvidia 的优化器变化可能有助于更快的收敛。在 A100 上使用 4 个 GPU 在两天多一点的时间内完成了针对 BERT-Large 的 24 GB 大小的语料库的大约 500k 步的训练过程。批量是影响模型性能的一个关键超参数。A100 机器大 GPU 内存 40GB 有助于训练大批量 (128 序列长度—批量 64,然后是 512 序列长度,批量 16) 。使用 4 个 GPU 的 A100 上的吞吐量约为 1200 个序列/秒。学习率为 7.5 e-4 时,训练损失约为 1.5,NSP 损失约为 0.005。

**图三。**预训练的带全词屏蔽的 BERT base 未区分大小写词汇表中约 1000 个词的随机样本的余弦分布直方图。如图 2 所示,模型 F1 值在 90%左右。用于训练的较大批量对直方图分布图中间接捕获的模型性能有影响(较小批量的预训练产生较低的模型性能以及均值向右移动的直方图分布图-较小的尾部)。以上图片作者

**图 4。**预训练的带全词屏蔽的 BERT 大型未区分大小写词汇表中约 1000 个词的随机样本的余弦分布直方图。如图 2 所示,模型 F1 得分在 90 分左右。用于训练的较大批量对直方图中间接捕捉的模型性能有影响(较小批量的预训练产生较低的模型性能,以及均值向右移动的直方图——较小的尾部)。以上图片作者

**图五。**预训练 BERT 大容量词汇中约 1000 个词的随机样本的余弦分布直方图,具有全词掩蔽。如图 2 所示,模型 F1 值在 90%左右。用于训练的较大批量对直方图分布图中间接捕获的模型性能有影响(较小批量的预训练产生较低的模型性能以及均值向右移动的直方图分布图-较小的尾部)。以上图片作者

**图六。**本次评测使用的其他公共模型(MSPubmed,SciBERT)的词汇表中约 1000 个词的随机样本的余弦分布直方图。MSPubmed 的直方图类似于高性能的预训练模型,但在掩蔽测试中表现不佳,这表明底层词汇向量训练得足够好,但模型层没有将它们转换为上下文敏感向量。以上图片由作者摘自文章 最大化 BERT 模型性能。

评估步骤

通过将 Tensorflow 检查点转换为 PyTorch 模型进行评估。预训练模型的评估如下进行。

  • BERT 学习的词汇向量提取和聚类——这可以使用下面列出的库中的实用程序来完成。一旦聚类完成,需要为感兴趣的特定实体手动标记这些聚类。如果聚类通常产生大约 3000 个聚类,那么对于大约 30000 个词汇,这最多需要一个小时的人工标记。
  • 运行评估脚本。这需要一个三元组(1)包含一个屏蔽短语的句子(2)屏蔽短语的句子(3)屏蔽短语的实体类型。测试数据的格式如下所示。评估产生带有感兴趣实体的混淆矩阵的输出。F1 得分报告的默认行为是所有被评估实体的最大 F1 得分值。

**图 3。**评估测试样本。作者创建的图像

最后的想法

当前标准方法的一个最新替代方案是用特定任务的头部对预训练模型进行微调——提示预训练模型进行少量镜头学习,这显示了前景。这种方法的灵感来自于由 GPT-3 推广的提示,其中模型执行新任务而无需微调*(无梯度更新),只需与预训练的自回归模型进行文本交互。虽然基于提示的监督不同于传统的监督,但它仍然需要人工来为模型准备(或者微调)*特定于任务的提示。本文中描述的 BERT 模型的使用为同一问题提供了一种不同的方法,其中人类的努力花费在标记几个聚类上,其大小受聚类过程的限制,与标记句子的传统监督或更*的基于句子提示的方法形成对比。

承认

用于评估的 Pubmed 和 SEC 测试数据集是由我的同事Harish通过从已知实体类型的选择术语中收集句子并在句子中屏蔽它们而用算法创建的。

代码用于 进行评估,此处可用 。准备用于评估 (集群创建)的模型的代码可在此处 获得。使用[CLS]向量创建句子表示的代码可在此处获得

时间数据序列中的量化器

原文:https://towardsdatascience.com/quantizers-in-time-data-series-8dc0149c4872?source=collection_archive---------22-----------------------

一个 C99 源代码的基本实现

图片来源: NPTEL 。许可知识共享 C-BY-NC-SA

在人工智能和人工智能项目中,数据准备预计占 80%的工作量。这一价值主要来自数据清理的需要,但也有与数据标准化相关的活动,这些活动需要相当长的时间,尤其是在找到正确的方法之前。

正如数据科学的许多其他方面一样,代码量方面的结果并不大,因为我们正在处理更精确、编写得更好的紧凑算法,而不是大型编程任务。

金融数据处理中经常发现的活动之一是对所涉及的不同时间序列进行标准化。例如,需要对不同资产的价格进行标准化,在某些情况下,还需要根据允许的步骤数进行标准化。

最后一个过程称为量化、离散化或宁滨,它在电气工程和数据信号处理等领域广为人知,允许处理实数以外的离散值。就数据科学而言,它允许我们减少数据集中离散值的数量。这并不完全是一种量化,因为输入序列本质上是不连续的,例如在电域中,但实际上可以这样认为。

连续信号的量化值。来源:维基百科

均匀量化器

虽然有几种离散化或量化信号的方法,但经典的方法是均匀量化器。公式如下:

Given:x :
a real number to be quantizeddelta : 
the quantization step, i.e. the bin size in which we want to divide the continuous value spaceQ(x) :
the quantized value for xQ(x) = delta * floor((x+delta/2)/delta)

C99 量化功能定义

注意: *inline* 属性建议编译器用内嵌编码代替函数,节省函数调用的开销。该请求可以被接受,也可以不被接受, *gcc* *clang* 都允许使用 *always_inline* 属性来强制内联用法。这种技术可以用来在需要速度的地方更好地构建代码,例如数据科学例程中经常发生的情况。

数据系列的完整示例

将输入值离散化为0.05值的步骤。第一列显示来自命令行的输入值。第二列显示使用quantize功能的输出,第三列显示使用quantize_mid功能的输出。

结论

将量化器应用于数据序列是一个简单的操作。如果数据序列的每个值都将作为累积输入(例如应用聚合值)应用于神经网络,则需要该参数。通过这一过程,我们有效地减少了神经网络的输入数量。这种操作在图像处理中很容易理解,在图像处理中,可以降低分辨率以减少所需的计算工作量,但其他数据科学数据集也有同样的需求,这些数据集的值本身被用作后续处理的输入。

quantra——学习量化金融的 Python 编码*台

原文:https://towardsdatascience.com/quantra-a-python-coding-platform-to-learn-quantitative-finance-8e5e88c89120?source=collection_archive---------8-----------------------

alevision.co在 Unsplash 上拍照

这是一个开始您的量化金融和 Python 冒险的好地方

老实说,这篇文章的标题很好地描述了 Quantra 到底是什么。这是一个帮助潜在学生学习 Python 量化金融的*台。该*台提供各种难度的课程,所以我非常肯定,每个对该领域感兴趣的人都会找到适合自己的东西。

截至目前,我已经完成了两门课程——动量交易策略和新手日内交易策略。在本文中,我将尝试展示该*台的一些特性,并分享我个人的观点和经验。

免责声明:本文包含附属链接

*台概述

课程和学习路线

来源

Quantra 提供独立课程(目前有 36 门课程)和专门的学习课程,后者是一系列课程的集合,旨在介绍从初级到高级的特定主题。我发现这个功能很有帮助,因为它可以为你的学习提供一种课程。

照片由海拉戈斯蒂奇在 Unsplash 上拍摄

多样化的学习方式

关于 Quantra 的课程包括以下类型的活动:

  • 录像
  • 专用环境中的编码练习—类似于 DataCamp 和 DataQuest 等新时代数据科学学习门户,您需要通过编写几行代码来完成一些简短的练习。万一卡住了,可以查看提示。
  • Jupyter 笔记本——课程的大多数部分都包含一些笔记本,允许更深入地研究实际编程。所以你不仅可以看到发生了什么,还可以进一步实验和尝试其他事情。
  • "琐碎问题
  • 阅读材料——相关论文、博客文章等的链接。

我真正喜欢讲座内容的是它的*期性和相关性。例如,COVID 疫情经常被认为是一个重大事件,作者分析了它对金融市场的影响。

技术堆栈

Quantra 上的所有课程都无一例外地使用 Python。相当多的内容是基于流行的库,如pandasnumpymatplotlib,但他们也引入了更多的金融专用库,如yfinance用于下载股票价格、ta-lib用于计算技术指标,以及zipline用于创建交易策略并回溯测试它们。

不需要安装

该*台的另一个好处是,所有环境(纯 Python 和 Jupyter 笔记本)都集成在 Quantra 中,因此对于学生来说,一个浏览器就足以完成所有练习。这绝对是一个优势,因为在本地机器上安装 Python 并确保依赖项与所需的相一致没有任何麻烦。

安妮·斯普拉特在 Unsplash 上拍摄的照片

社区

万一你卡住了,想不出什么办法(虽然这不太可能,因为笔记本上写满了描述+评论和编码练习的专用提示),或者只是想讨论一下你的交易策略,你可以通过论坛联系 Quantra 社区(其他学生+员工)。

现场交易

大多数学生都对构建自己的算法感兴趣,并尽快开始交易(鉴于潜在的财务损失,自然不总是建议这样做……),Quantra 提供了一个很好的功能,即 blue shift——他们基于zipline的专用实时交易*台。在这些课程中,你将学习如何创建交易策略,使用 Blueshift 上的历史数据进行回溯测试,并通过连接到你选择的经纪人开始实时交易。Blueshift 目前支持的一些券商包括羊驼、FXCM 和 MasterTrust。

这些课程还包括通过 IBridgePy API 在交互式经纪人、Robinhood 和 TDAmeritrade 上进行实时交易。

就我个人而言,我没有使用过这个功能,但我确信它对那些想玩算法交易的人会很有用。这肯定比从头开始构建自己的管道并直接连接到经纪人的 API 更容易。

对反馈的开放性

我真正喜欢 Quantra 的是他们对反馈的开放程度。我报告了一些在课程中可以改进的建议,团队实际上实施了这些建议。

个人观点

到目前为止,我已经在 Quantra *台上完成了两门课程,我必须说,我很享受这种体验,我学到了一些有趣的东西。

至于目标受众,我想说该*台最适合初学者,他们希望有一种结构化的方式来研究 Python 在量化金融方面的应用。至少可以说,即使从经验上讲,从零开始,然后试图找出最好的课程或在哪里找到一些信息也是令人生畏的。初级到中级水*的课程包含专门的介绍 Python 的部分,因此,即使以前没有 Python 或库(如pandas)经验的人也可以快速上手并能够跟进。

对于更有经验的观众,我建议浏览一下课程大纲,看看你实际上能学到多少新东西。因为你可能会跳过相当多的章节,因为你已经知道了这些内容。在这种情况下,我建议尝试不同的课程,可能是针对更高级的用户。

此外,Quantra 的商业模式与 Udacity 和 Udemy 等其他 MOOC *台类似,这意味着课程经常打折。至于 Udemy,可能不是 90%,但相当可观:)此外,如果你想参加 Quantra 的课程,你可以在 Twitter 上联系我,获取折扣代码。

我不认为这些课程是完美的,仍然有一些地方可以改进,但我相信随着时间的推移,他们会变得更好,因为团队在实施建议的变化方面工作效率很高。

如果你对我可能回答的课程有任何问题,请在评论中自由提问。另外,我很想听听你对 Quantra 的体验。

喜欢这篇文章吗?成为一个媒介成员,通过无限制的阅读继续学习。如果你使用这个链接成为会员,你将支持我,不需要额外的费用。提前感谢,再见!

量子振幅和概率

原文:https://towardsdatascience.com/quantum-amplitudes-and-probabilities-b49a6969b0b9?source=collection_archive---------29-----------------------

你看到的并不总是你得到的

本帖是本书的一部分: 用 Python 动手做量子机器学习

量子位状态向量包含振幅而不是测量概率。

振幅属于波。因为在量子力学中,量子粒子的行为是用波函数来描述的。

波浪有三个特征。

  • 波长是波的形状重复的距离。
  • 波的相位是波形周期上某一点的位置。
  • 波的振幅是波的中心和波峰之间的距离。

下图描述了这三个特征。

作者弗兰克·齐克特的图片

正如我们在图中看到的,振幅可以是正的,也可以是负的。振幅是正还是负取决于虚点 x,如果你选择不同的点 x *,同样的波会有负振幅。

如果取两个相同但有位移的波,在 x 点,一个可能有正振幅,而另一个有负振幅,这两个波的相位不同。但是当你测量这两个波中的任何一个时,它们是一样的。你看不出有什么不同。它们对测量概率的影响是相同的。

数学上,量子位被测量为 0 或 1 的概率是相应振幅的*方。振幅是正还是负并不重要。

随着

In fact, the measurement probabilities are the squares of the amplitude absolutes ( |alpha|^2 + |beta|^2 = 1 ).

不过,幅度和概率是两回事。

到目前为止,我们只关心测量概率。我们只关心数学中的振幅。我们还没有研究量子位的振幅和相位。

但是我们也可以研究振幅。例如,Z 门切换幅度的符号。

当我们查看量子位的最终振幅时,我们看到量子位在状态 1 中具有负振幅。但是测量概率都是正的。

作者弗兰克·齐克特的图片

虽然振幅可以是负的,但概率不能。这是量子比特状态标准化α2+β2=1.的一个结果

归一化公式限制了测量概率可以获得的值。它不限制振幅。

但是,如果量子计算中一个态的振幅符号对结果概率无关紧要,我们为什么还要关心它呢?

答案是波干涉。波有一个明显的特点,就是它们会互相干扰。简单来说,它们加起来。如果有两个波在同一介质中传播,它们会形成第三个波,代表每个点的振幅之和。

作者弗兰克·齐克特的图片

作者弗兰克·齐克特的图片

量子位相应地工作。虽然量子位的振幅的符号和产生的相位不影响测量概率,但是一旦量子位相互干扰,它们产生的振幅可能不同,因此影响测量概率。

这就是所谓的振幅放大。它是我们量子计算工具箱中的一个重要工具。

我们来看看下图描绘的量子电路。

作者弗兰克·齐克特的图片

我们有两个量子位。在我们对它们中的每一个应用了第一个阿达玛门之后,它们都处于*衡的叠加态。

此时,我们有四种可能的状态,它们具有相等的振幅和相等的测量概率。

下面的受控 z 门仅切换状态|11⟩.的幅度符号除了应用门之外,它的工作原理和我们所知道的其他受控门一样。在这里,它是 Z 门。

以下 Z 门序列和封装在 H 门(HZH)中的另一个受控 Z 门称为 Grover 迭代。它是一个反射电路,将所有关于*均振幅(而不是*均概率)的状态反转。

对于四个状态中的三个具有正幅度(0.5),但只有一个具有负幅度(0.5),*均幅度为 0.25。

由于 0.5(2÷0.25)= 0,因此具有正幅度的这些状态的幅度为 0。

负幅度的一个状态导致幅度为 1,因为 0.5(2 ÷( 0.75))= 1。

即使量子位元状态向量的相位(振幅的符号)对结果的测量机率并不重要,但在量子电路内部却很重要。因为我们可以研究这些阶段。

以下代码显示了该电路的实现和测量概率。

作者弗兰克·齐克特的图片

结论

结果显示有 1.01.0 的几率测量系统为11。与以前不同,当我们直接处理测量概率时,我们只改变量子位的相位。

量子位类似于波的行为。因此,当它们相互作用时,它们的振幅很重要。简而言之,我们实现的电路首先改变了状态|11⟩的相位(通过对其幅度求反)。然后,它添加了另一个与|11⟩.州具有相同相位的波它使该状态的幅度加倍,从()0.5 到 1。因为这个相位与其他三个状态的相位正好相反,它们相互抵消。

本帖是本书的一部分: 用 Python 动手做量子机器学习

免费获取前三章点击这里。

量子计算:如何使用它

原文:https://towardsdatascience.com/quantum-computing-how-it-could-be-used-6f873dfb7cea?source=collection_archive---------34-----------------------

量子计算:为什么你应该关心(不到 30 分钟)

看看量子计算可以解决的问题的本质,以及来自多个行业的例子

在 Unsplash 上由 Ibrahim Boran 拍摄的照片

这是我最*关于量子计算商业相关性的电子书的第 2 部分,共 3 部分。

前一章(见此)简要概述了什么是量子计算。这一节讲的是现实中的意思。最后一节讨论了量子就绪性,并阐明了为什么经理和高管需要从今天开始理解这一点,即使他们的第一步很小。

为什么这实际上是有用的?

你和你的同事的相关问题

从外行人的角度来看,有几类问题量子计算机已经被认为非常适合解决。随着时间的推移,我们会发现更多。

组合问题。这是我们一次询问大量交织在一起的数字的地方。初始数据的实际量通常并不大(不是在“大数据”分析方面,大数据分析广泛地不适合量子计算机),但大的是组合的数量。看两个例子,你可以从你所在的行业和公司中推断出来。

物理过程的模拟。鉴于量子计算机反映了我们宇宙的量子本质,它们适合于模拟宇宙中发生的事情就不足为奇了;包括模拟分子间的相互作用。目前,流行的模拟主题包括新药设计中的蛋白质折叠、新电池设计最佳材料的确定(在电动汽车的背景下)以及催化过程的建模;例如,肥料的制造。下面将从行业角度对其中一些进行详细介绍。

量子机器学习。“QML”越来越引起人们的兴趣,但对于一个非技术高管来说,相对更难理解。机器学习当然是一项技术,当在会议上提到它时,我们都会热情地点点头,但实际上却很难向我们的姑姥姥杰拉尔丁解释清楚!很明显,这是一项长期存在的技术,我们将利用它做的事情正变得越来越复杂和有价值。机器学习的一个关键方面是使用适当的数据集训练工具。量子计算可以帮助创建这些数据集,从而训练机器,特别是日益重要的“无监督学习”领域。

组合优化问题的例子

当你继续你的旅程时,你会遇到几个问题。这些问题包括旅行推销员问题和背包问题。这些很容易理解(即使你确实需要一台量子计算机来解决它们!)

旅行推销员问题

一名销售人员需要访问 N 个城市,每个城市之间的旅程都有一个或多个不同的成本(时间、距离、费用)。最佳路线是什么?

除了销售人员、旅游和物流公司等面临的明显挑战之外,它对于优化仓库布局、印刷电路板制造以及其他许多涉及移动的活动也同样重要。

在技术语言中,这是以“节点之间的最短距离是多少?”但是,如果你考虑的不仅仅是距离——例如,时间和燃油效率——这很快就变成了一个“困难”的优化问题。每增加一个节点,计算量就会增加一个数量级,以至于传统计算机无法优化大型网络。然而,由于它们不同的工作方式——有效地一次性考虑所有选项——量子计算机将这种问题作为早餐。

背包问题

这个问题简单地陈述了给定一组物品,你如何在给定的限制内优化——例如,最大化你在背包中可以得到的东西,同时满足诸如最大重量、一件物品不能超过 x 等约束条件。如果您考虑添加物品的顺序,例如,优化多站包裹递送的卸载,那么这一系列问题就变得更有意义、更复杂。

变量的数量迅速增加,这使得用传统的计算机进行优化变得不可能,尤其是当你需要每天处理数百辆卡车时。然而,它非常适合量子计算机计算问题的方式

这与每个行业都相关。是的,甚至是你的!

量子计算将影响一切这听起来可能很老套。然而,即使是对用例最简短的考虑也确实表明,展望 10-30 年,我们生活的每个方面都将以某种形式被 QC 优化。现在,正如引言中所提到的,这些变化并不都是显而易见的,许多变化只会在以后的创新浪潮中显现出来。20 年前有多少人会预测到我们会利用人们手机的信号来识别道路上哪里拥堵?虽然现在这可能是一个很好理解的例子,但我们中有多少人知道利用雨水对这些手机信号的干扰来提供超本地天气信息?

所以,是的,很明显一切都将被触及,但不是以同样的速度,最初可能只是以小的方式。我们仍然处在这样一个点上,围绕 QC 应该集中在哪里的讨论比真实的、有影响力的工作的例子更多。

不难认识到,那些复杂的组合问题很常见的行业将在某个时候受到影响,但技术的时间表到价值也将改变努力和投资的可见性。你还应该预料到,不同公司、不同行业(以及资助这项工作的机构)的公开程度会有所不同,因此仅仅关注头条新闻并不是跟踪进展的有效方式。以下是*期和中期的一些预期活动。

制药、生命科学、医药和医疗保健

药物开发的核心是测试蛋白质分子如何相互作用。足够强大的量子计算机将以一种前所未有的方式对此建模。这不仅会加快传统方法的速度,还会给科学家和研究人员解决问题的方式带来范式转变。这将导致更多的新药和更短的开发周期。

同样可以预见的是,可以创建更详细的病毒与人体相互作用的模型。这是当前一个非常热门的挑战,应该允许对未来的流行病采取更快、更复杂的方法。

理查德·费曼

令人难忘的是,理查德·费曼在他的开创性演讲“用计算机模拟物理”中首次提出了量子机器在模拟自然系统中有用的可能性。费曼是 20 世纪美国的一位著名理论物理学家,他说:“自然不是经典的,该死的,如果你想模拟自然,你最好用量子力学来模拟,天哪,这是一个非常好的问题,因为看起来并不容易。

化学和材料科学

就像生命科学一样,无机化学过程的模拟和纳米技术材料的 T2 开发将受益于使用量子计算驱动模型的能力。能够采用一种完全不同的方法进行模拟将会加速当前的技术,并允许新的方法,从而产生未来的新材料浪潮。

汽车、物流和旅游

汽车行业最初看起来可能不是量子计算的显而易见的候选人,但是通过大众汽车与 D-Wave 和谷歌之间的高调合作,人们已经关注到两个关键话题。一个是电池技术,另一个是路线规划。 Quantum London 有幸听取了大众汽车前首席技术官的发言,这位首席技术官是他们早期量子工作的先锋,很明显,人们真诚地认为这是公司技术创新战略的重要组成部分。

电池技术点链接到上面提到的材料科学。随着大众致力于电动汽车的未来,它正在寻找增加电池容量、降低制造成本和减少使用不常见或有环境损害风险的组件的方法。所有其他车辆制造商都在寻找相同的逻辑。电池的价格高达汽车价格的三分之一,而且它们目前提供的短程是一个主要的阻碍因素。因此,这可能是量子计算支持的材料科学研究最明显的商业驱动力。

同样重要的是量子路由优化。在过去的二十年里,我们都看到了“卫星导航”路由的巨大改进。当每辆车都以同样的方式绕过一次堵车时,我们都经历过这种挫败感,只是造成了第二个夹点。虽然我们距离每次旅行都有实时量子计算驱动的更新还有一段距离,但利用量子计算开发一系列不同的路线,然后由经典计算机实时选择,已经有所不同。这个话题自然也适用于物流行业的日常工作。

对于航空公司来说,航线和设备的优化至关重要且复杂。考虑到计算工作量,这通常不经常进行,并且很少会真正得到优化。量子计算将使这一过程更加频繁,优化程度更高。在某种程度上,这将是隐藏的个人传单,虽然会有成本节约可能会被传递。更明显的是,在重大事故迫使飞机转向或停飞后,混乱会减少。目前,对于数百架飞机、数千名乘客以及缺乏明确约束的情况,没有一种简单的方法来逐小时优化计划。未来,量子计算机将允许航空公司最小化这类问题的影响。

信息管理。

数据分析预计不会成为量子计算的优先事项,尽管一些评论者将量子计算的计算能力与完成许多分析任务的适用性混为一谈。然而,当涉及到通过“数据库”搜索时,QC 将是相关的。在大多数情况下,传统计算机花费的时间足够短,大多数公司不会立即看到用例,但对于那些对大规模数据集进行结构化搜索的人,或者那些希望进行比目前可行的更复杂的搜索的人,将会有重大的机会。

金融服务

鉴于银行的规模、可观的 IT 支出、熟悉技术的团队,以及在许多领域边际改进的明显价值,银行通常处于任何新技术的前沿。

虽然量子计算的银行试验通常是离散和保密的,但一些相关主题是什么是足够清楚的。很多努力的最前沿是投资组合优化的主题。不仅 100 亿美元中的 0.05%很容易支付你的初始 QC 支出,而且正如我们都经历过的那样,获得大项目预算的最佳方式是展示早期试点的真正财务效益。

这种“很大数量中的很小一部分”的价值点也意味着,与许多其他技术不同,新银行不太可能处于量子应用的前沿。他们只是还没有积累足够的资产来实现微%的收益,从而有所作为。

迄今为止,保险业的参与有限。这是可以理解的,因为定价风险和管理索赔的主要领域并没有明显受益于量子计算的早期机遇。一个可能变得相关的领域是围绕飓风、野火和洪水进行的建模,这与天气预报模型一样,将受益于 QC。

应对气候变化

从行业角度来看,主题也很有趣。在争取政府、非政府组织和其他机构的支持和资助时,这一点尤其重要。

由于可持续发展是 2021 年“重建更好”议程的一部分,因此毫不奇怪,人们正在做大量工作将质量控制与降低能耗的努力联系起来。实际机会属于我们上面定义的各种类别,但综合起来描绘了一幅令人信服的画面,说明为什么技术主导的可持续发展之路必须包括量子计算。

然而,令人担忧的事实是,足够强大的量子计算机产生真正影响的时间范围比我们管理气候危机的时间范围更长。因此,虽然量子计算将在中期发挥重要作用,但任何人将它吹捧为我们当前日益升温的世界的快速救星都是不诚实的。

广泛讨论的主题包括:

  • 新型催化剂用于降低化肥生产的能源成本(例如,生产氨肥的哈伯-博世工艺消耗了全球 3-5%的天然气),更好地储存能源,以及寻找碳捕获方法(例如,在混凝土中)
  • 生产和使用更加环保的新材料(例如,因为它们重量更轻)
  • 改进设计——例如,利用计算流体动力学(CFD)来制造需要更少燃料的船只、飞机、火车等
  • 优化的物流和行程 —减少物体需要移动的距离

随着 2021 年的进展,注意力从 Covid 转移到气候变化,看看量子计算如何融入对话将是一件有趣的事情。

城市规划

智能设备现在为城市规划者提供了关于人类及其车辆如何使用城市的*乎无限的数据。对于传统的优化模拟来说,这种数据太多了,因此 quantum 可以发挥作用。无论是交通信号排序建模、道路工程管理,还是确定如何让公共汽车、电车和火车时刻表同步,都有大量的机会,随着城市变得越来越拥挤,我们希望减少每次旅行的足迹,这些机会将变得越来越重要。

其他一切

以上只是简单的例子。考虑你所在行业的类似活动,以及优化可能带来的商业优势。

感谢您阅读本部分。通过下面的链接或通过此链接继续。或者阅读电子书(美国链接,英国链接)

  • 简介(链接此处)
  • 为什么质量控制是有用的和潜在的商业应用(这篇文章)
  • 为什么忙碌的高管应该参与到这个话题中来(链接将在发布后添加)

量子计算没有这么宽容

原文:https://towardsdatascience.com/quantum-computing-is-not-as-forgiving-4b6b2e0c5074?source=collection_archive---------25-----------------------

您的经典编程方法将不起作用

量子机器学习要不要入门?看看 动手量子机器学习用 Python

量子计算是一块难啃的骨头。尤其是如果你习惯于编写经典计算机的程序。但是量子计算不像经典编程那样宽容。

作者图片

让我简单描述一下我是如何经典地开发软件的。我从一个简单的案例开始。一旦成功了,我就能应付边缘情况。另外,我从一个具体的案例开始。一旦成功了,我就把具体的东西抽象出来,找到一个通用的解决方案。

这在量子计算中不起作用。量子计算是个 b…这么说吧,有点反直觉。每当你认为你理解了它是如何工作的,并且你继续下一步,它就给你一条腿。在那里,你会发现自己在哭泣。

作者图片

先说量子纠缠。量子纠缠是量子力学的惊人特征之一。两个纠缠的粒子共享一个叠加态——不管它们相距多远。实际上,我们纠缠量子位来构建超越单个量子位的有意义的量子系统。你需要理解的只是 CNOT 门,即“受控而非转化”

假设你有两个量子位,P 和 Q,你对这两个量子位应用 CNOT 门,P 是控制量子位,Q 是目标量子位。然后,P 保持不变,Q 仅在 P 为 1 时翻转其状态,但在 P 为 0 时保持不变。换句话说,只有当控制量子位为 1 时,CNOT 门才像一个简单的 X(或非)门一样作用于目标量子位。

下面的真值表描述了这种影响。

作者图片

就这样? “你以为。从物理角度来看,纠缠听起来很神秘,但从编程角度来看,它看起来简单而直观。很快,你就用它来给其他量子比特变换添加控制。更进一步,你给控件添加控件。CNOT 成为你最喜欢的创建概率量子系统的工具,比如量子贝叶斯网络。 命好 ,“你想想。

然后,突然,它踢回来了!字面意思!

你偶然发现量子相位反冲。它否定了 CNOT 门明显的片面性。控制量子位不会保持不变。对于物理学中的每一个作用,都有一个相反的反应。所以,对控制量子位有影响。控制呈现目标量子位的相位。

虽然量子位相位对于 0 的量子位无关紧要,但是对于叠加态的量子位却很重要。例如,相位显示了|+⟩态和|−⟩态量子位之间的差异。

当你从震惊中恢复过来后,你会爱上量子位相位和相位反冲。原来不仅仅是著名量子电路的一部分,比如多伊奇的算法。但事实上,它是量子优势的基石。

人生好,再来, 你想想。

你是做什么的?你开始尝试。到目前为止,你已经学会了如何给你的量子转换门添加多重控制。而且,你已经学会了如何利用相位反冲效应。

如果把这两样东西结合起来岂不是很棒?如果我们有一个多控制相位反冲不是很好吗?

首先,让我们看看 CNOT 门的相位反冲。我们的量子电路有两个量子位。我们把第一个放在|+⟩,第二个放在|−⟩.

布洛赫球表明量子位处于不同的相位。

作者图片

接下来,我们对这个状态应用 CNOT 门,位置 1 的量子位是控制量子位,位置 1 的量子位是目标。

作者图片,

我们看到控制量子位改变了它的相位。当我们在量子位元上应用更多的哈达玛门时,我们测量两者都是 1。

作者图片

下图描绘了这种反冲情况下的量子电路。

作者图片

所以,CNOT 门的控制量子位接管了目标量子位的相位。如果我们有两个控制量子位,你认为会发生什么?让我们以下面的电路为例。我们也知道 CCNOT 门是 Toffoli 门。它有两个控制量子位,并且只有当两个控制量子位都处于|1⟩.状态时,才对目标量子位应用“非”门

作者图片

作为一个经典的程序员,我希望我们将目标量子位的相位应用于两个控制量子位。因此,电路应该产生测量结果 111——所有三个量子位都测量为 1。让我们看一看。

作者图片

哦,又来了!一旦你认为你理解了量子门,一些完全意想不到的事情发生了。我们看到目标量子位总是 1(左下方的数字)。但是控制量子位可以是 0 和 1 的任意组合。

让我们看看布洛赫球!

作者图片

哦,它们好像坏了。我们在|1⟩.态看到目标量子位但是我们没有看到控制量子位的任何东西。

问题是,托夫里门导致了一个纠缠的量子系统。虽然布洛赫球体关注的是单个的量子位,我们不能再单独代表一个纠缠的量子系统了!

结论

显然,在量子计算中,我们不能像在经典编程中那样,从一个简单具体的例子开始,然后增加更多的复杂性和抽象性。在这篇文章中的每个案例中,纠缠的行为都有很大的不同。

  • 当我们将 CNOT 闸应用在|0⟩和|1⟩两个基本态的量子位元上时,我们可以在真值表中推论出它们的结果。
  • 当我们在其他国家(如|+⟩和|−⟩)对量子位做同样的事情时,量子位的纠缠被解决了,我们看到了相位反冲。
  • 最后,当我们使用多个控件时,就像我们在 Toffoli 门中所做的那样,我们最终会得到完全不同的结果。系统仍然纠缠不清!

量子机器学习要不要入门?看看 动手量子机器学习用 Python

免费获取前三章这里。

量子计算没什么不同,对吧?

原文:https://towardsdatascience.com/quantum-computing-isnt-so-different-is-it-9db28842a4ea?source=collection_archive---------24-----------------------

Qiskit 中如何实现“and”和“or”

量子机器学习要不要入门?看看 动手量子机器学习用 Python

量子计算从根本上不同于经典计算。在经典计算中,我们使用布尔逻辑。我们评估变量的真值来决定是否执行一个动作或另一个动作。

标准的 if then else 结构最好地说明了这一点。

这种结构的 if 子句通常包含纯布尔逻辑。这里,我们大量使用布尔运算符,如而不是

但是布尔逻辑在量子计算中不起作用。在量子计算中,一个量子位的所有转换必须是可逆的。这意味着你不仅需要在给定输入的情况下计算输出,还需要能够在给定输出的情况下计算输入。而这对于一些布尔运算符来说是不可能的,例如,

让我们看看代表这些运算符的真值表。

作者图片

真值表表明,我们不能总是告诉什么是输入,什么是输出。例如,如果我们应用P or Q,结果得到,我们不知道是PQ为真,还是PQ都为。而如果我们应用P and Q,如果我们得到作为输出,我们就无法分辨出输入。

这些算符是不可逆的,因此,它们不适合量子比特变换。

幸运的是,我们可以把这些算符转换成可逆的。诀窍是保留原始输入,并将输出写入辅助量子位。让我们考虑一下操作符。

我们用三个而不是两个量子位。我们评估前两个量子位是否*为真,*如果是,我们翻转第三个量子位的值。下图描绘了该运算符的真值表。

作者图片

技术上,我们计算𝑃∧𝑄⊕𝐴.这意味着“P 和 Q 异或 a”。异或运算符是异或运算符。只有当 P 或 Q 为真,而另一个为假时,它才为真。如果两者都为真或都为假,则为假。

因为我们有三个变量,每个变量有两个可能的值,所以有 2 =8 种不同的组合。如你所见,辅助变量 A 的输入也很重要。P 和 Q 的列保持不变,但是如果 P 和 Q 为真,辅助值的值会改变。最重要的是,给定任何输出,我们可以识别输入是什么。这种转变是可逆的。

在量子计算中,我们不处理真值,而是处理量子位状态。一个量子位的基本状态是|0⟩,我们测量为 0,|1⟩,我们测量为 1。

当我们在|0⟩态的量子位上应用哈达玛门时,会产生|+⟩.态在这种状态下,我们测量量子位为 0 或 1,各有 50%的概率。让我们看一看。

作者图片

所以,我们准备一个三个量子比特的量子电路。此外,我们将它们的引用存储在 Python 变量PQA中。

当我们对 P 和 Q 应用 Hadamard 门时,我们看到四种不同的状态。在图中,量子位从右(P)到左(A)读取。我们测量量子位 A 总是为 0,因为我们没有接触它。

作者图片

应用𝑃∧𝑄⊕𝐴的量子变换门是托夫里门。它也被称为 CCNOT 门,其中 CC 代表“受控-受控”如果你再看看真值表,你可以看到,如果另外两个量子位(它们是控制量子位)处于|1⟩.状态,这个算符就改变了辅助量子位(也就是目标量子位)的值

作者图片

结果表明,只有当量子位 p 和 q 处于|1⟩态并且被测量为 1 时,我们才测量目标量子位 a(左手量子位)为 1。

接下来,我们来看看运营商𝑃∨𝑄⊕𝐴.如果量子位 p 或量子位 q 处于|1⟩.状态,这个操作者应该在目标量子位上应用非门

作者图片

正如我们在结果中看到的,如果一个或两个控制量子位处于|1⟩.状态,我们测量目标量子位 a 为 1 这里的技巧是应用两个额外的受控非门。单个受控非门将量子位𝑃⊗𝐴转换成𝑃⊗𝑃⊕𝐴.

如果𝑃在|1⟩.,第一个受控非门将𝐴从|0⟩切换到|1⟩如果𝑄不在|1⟩状态,其他两个门什么也不做,因此,留下量子位𝐴在|1⟩.状态

因此,如果只有量子位𝑄处于|1⟩状态,那么门qc.cx(Q, A)将𝐴从|0⟩切换到|1⟩,而其他两个门什么也不做。

最后,如果两个量子位都在|1⟩态,那么所有三个门都适用。第一扇大门把𝐴从|0⟩变成了|1⟩.第二扇门把它转回|0⟩,第三扇门又把它转回|1⟩。

结论

量子计算在许多方面不同于经典计算。我们需要处理的一个特性是转换门所需的可逆性。经典算符没有这个要求。而且不是所有的都是可逆的。

在这篇文章中,我们学习了一些技巧,让我们把一个不可逆的算符变成一个可逆的量子算符。但是我们必须谨慎对待这些知识。我们不应该试图在量子电路中复制经典逻辑。当我们想要挖掘量子优势时,我们需要换一种方式思考。

然而,即使在先进的量子算法中,我们也使用这样的基本结构来准备量子态,以代表我们要解决的问题。

量子机器学习要不要入门?看看 动手量子机器学习用 Python

在这里免费获得前三章。

量子计算——它是关于什么的

原文:https://towardsdatascience.com/quantum-computing-whats-it-all-about-cf2482fd7583?source=collection_archive---------3-----------------------

量子计算:为什么你应该关心(不到 30 分钟)

我们理解为什么企业高管需要了解量子计算的介绍

托马斯·弗兰科斯基在 Unsplash 上的照片

欢迎

你对量子好奇吗?你是否一直觉得量子计算很重要,但不知道为什么?您是否总是在思考您的公司如何更好地利用数据?你想为下一次技术革命做好准备吗?那么这就是适合你的地方。

量子计算作为一个严肃的商业话题是新的。是的,它很复杂,有很多未知因素,但是你花一些时间去更好地理解它是完全正确的。如果这是一篇 LinkedIn 文章,它会说“阅读时间:30 分钟”。如果你在 1995 年有机会花 30 分钟阅读有关互联网的内容,或者在 2000 年花 30 分钟阅读社交媒体,或者在 2005 年阅读智能手机,这些时间都是值得的。接下来的 30 分钟在 2021 年对你的公司未来 5 年或 10 年的运营同样重要。

这本入门书是针对那些很少或没有技术知识的人的。这将帮助你充分了解量子计算,从而判断你应该如何以及何时确保你的组织正在讨论这个话题。它将回答一些基本问题,并帮助你开始规划通往量子准备状态的道路。

24 个月前,我刚接触这个话题,并成立了量子伦敦公司(Quantum London)来调查这项迷人的新(ish)技术的商业影响。我不得不问许多愚蠢的问题,并努力解决太多深奥的技术解释。我现在已经到了我个人量子旅程的这一步,我觉得我可以向你解释足够多的基础知识,对你有所帮助。它故意简短:没有借口不完成它,也没有理由你不能理解每一个单词和想法。

30 分钟后(或者一个小时后,如果你决定需要重新阅读,你会有足够的信息来决定是否要进一步研究这个话题。然后,你将准备好接触不断增长的书籍、网站、协会和其他方式,发现更多更多的东西。

在引言之后,小册子分为三个主要部分:

1.这是怎么回事

2.它是如何产生影响的

3.为什么通常的推脱借口在这里行不通

我考虑过是否给那些想要更多信息的人附上一份详细的附录。我已经决定不这么做了。我希望你——读者——感觉你已经自信地完成了整篇文档,并且已经能够开始做决定了。你应该在完成时获得一种真正的成就感,而不是当你惊恐地盯着一个详细的附录时叹息,尽管它写得很好,但可能并不完全有意义。

2021 年 7 月,英国塞文奥克斯,保罗·科莫

本文分为三部分

  • 介绍(这篇文章)
  • 为什么质量控制是有用的和潜在的商业应用(发布后将添加链接)
  • 为什么忙碌的高管应该参与到这个话题中来(链接将在发布后添加)

我们为什么在这里?

从思想实验到下一个改变范式的技术

量子计算的概念已经存在了几十年。过去几年发生的变化是,技术突破意味着我们可以自信地认为“它是可行的”。因此,问题不是“如果”,而是“何时”量子计算将成为我们如何操纵数据和解决问题的一部分。

几十年后,我们不会对量子计算感到兴奋。同样,在我们第一次听到 3G 和宽带这两个术语不到 20 年后的今天,我们显然不再认为我们可以将大量数据传输到移动设备或电视屏幕上。量子计算的使用将像今天移动数据和宽带互联网的使用一样具有变革性和无处不在。同样,随着时间的推移,它将成为大多数用户的“幕后”。

然而,与任何新技术一样,过渡点是那些提前计划并适当定位其公司的人有机会比竞争对手更快地从小变化中受益。量子计算影响的广度和深度意味着我们将在几十年内处于一个全面的过渡阶段。然而,对于每个特定的行业和用例,过渡将在更短的时间内进行,放大一些人的潜在收益,但缩短他们保持优势的持续时间。

因此,这本简短的入门书的主要目的必须是让你了解量子就绪的概念,并分享一些问题,供你在定义量子就绪状态时开始考虑。

量子计算和互联网黑客攻击

在我们开始之前,这里有一个重要的补充点,因为它可能会把你吸引到这个话题上来。这就是量子计算在破解加密和解密机密数据中的作用。对于每个使用数据的人来说,这是一个明显而现实的危险。 明确 因为目前硬件发展的进步表明,许多最常见的加密技术在十年内都承受不了量子计算机。因为对信息技术和通信系统的任何重大升级都需要数年时间,所以工作必须在威胁显现之前开始,而不是简单地在第一次攻击成功时就开始。

这种风险真实而重要,需要首席风险官、首席信息官和首席信息安全官制定计划并实施。然而,我不希望这种零和的防御活动分散本书的主要意图,即量子计算带来的机遇。

因此,除了结尾的一小段,我将避免任何提及,而是将您引向我们在 Quantum London 建立的讨论 Quantum as-a-risk 的各种社区,这些社区可以分享详细的、以行动为导向的计划。

关注积极的机会,而不是狭隘的消极因素

质量控制提供了一个更加高效和有效的未来的承诺:供应链真正优化——节省时间、金钱和燃料;重新设计的能源网和交通信号灯网络——增加弹性、节省时间、减少污染;创造新的电池材料,重新设计制造工艺,发现新的延长寿命的药物。

当然,量子计算本身不会实现这一点。它是聪明、有动力的个人和公司的工具,通过他们可以实现这些变化。我希望专业人士和高管关注这些可能性,而不是被解密风险过度分散注意力。

量子计算:那么,它到底是什么?

技术视角

量子计算机是一种新型计算机,它以一种完全不同的方式进行计算。它们进行某些计算的速度将比目前的计算机快得多。这将使我们目前不常回答的一些业务问题得到更快、更频繁的回答。它也将允许我们问一些我们以前认为不可能回答的问题。而且,正如任何新技术一样,随着我们对能力的了解,它将让我们提出(并回答)我们以前从未考虑过的问题;解决未知的未知数,如果你愿意的话。

我所看到的最清晰的概念是在一个简单的图表中,如下图所示。我第一次看到这个是 IBM 的 Andy Stanford-Clark 给量子伦敦做的一个精彩的演讲,名为“T2 量子计算:困惑者指南”。恰当的标题和精彩的演讲。

量子问题的本质—作者创作的图像

目前这个领域最显著的科学和工程技术是量子计算硬件的设计和建造。然而,对你来说最重要的不是物理计算机本身,而是其他同样令人印象深刻的技术专家正在开发的软件和算法。

也就是说,我们确实需要简短地转移到底层技术上来

这都是关于量子位的

目前所有的计算机(除了已经建成的大约 100 台量子计算机)都使用比特。位是 1 或 0。你在电脑、手机上做的任何事情,或者通过汽车或微波炉中的系统做的任何事情,都会把指令变成一系列的 1 和 0。这些被处理以给出一个输出,然后触发一个动作或在屏幕上显示一些东西。自从半个世纪前晶体管和微芯片发明以来,计算机就一直是这样工作的。

1 或 0 特性意味着位可以代表两种状态中的一种。要得到四个状态,你需要两位,而三位给你八个状态。(如果库存中有八种不同的产品,你可以给它们一个唯一的代码:000,001,010,011,100,101,110,111)。这种二进制工作方式非常适合我们目前的“经典”计算机的工作方式。每一位都是有限的,所以为了处理大量的数据,计算机要并行使用大量的位和大量的微芯片。

量子位(Quantum bits)从根本上说是不同的,因为一个量子位的工作方式远远不止两种状态。有很多方法可以用非数学的方式来解释这一点,聪明的外行人可能会理解。我不会这样做来强调我的观点,你不需要理解技术。就像你不需要我来解释当你在高速公路上行驶时,每次你在不同的无线电发射塔之间移动时,手机是如何发送控制信号的,也不需要我来解释见鬼的工程师是如何在微芯片上获得 10 亿个晶体管的,事实上也不需要我来解释 100 年前的汽车内燃机设计。

关于量子位,你需要知道的两件事是:( a)更多的量子位给你更多的计算能力,以及(b)并非所有的量子位都是*等的,因此某人关于“250 量子位”机器的头条新闻可能不如另一家公司的“150 量子位”设备强大。

超越 HPC

超级计算机,通常被称为高性能计算(HPC ),允许我们操纵难以理解的大量信息来预测天气、模拟核爆炸、勘探石油等等。正如“谁拥有最高的摩天大楼”这一地缘政治游戏一样,谁拥有最强大的超级计算机也是一个不断来回的问题。目前,它是日本神户的超级计算机,拥有超过 700 万个内核,运行速度超过 400 petaflops。这些数字对你我来说没有任何意义,但关键是这个世界已经有了一些非常非常大的计算机。

在 2019 年我主持的第一届量子伦敦活动中,最常见的问题是“量子计算机就像拥有一台超级计算机或访问一台真正大型的云计算机一样吗?

从本质上来说,答案是否定的。对于 QCs 适合的那些类型的计算,它们是如此好得多,以至于无法想象一台“经典”超级计算机的规模可以与之相比。因此,将量子计算机视为一种完全不同的操作方式是非常有用的。

这个比喻怎么样?想象一个芝加哥的销售人员,他唯一的设备是一辆汽车。如果你给她一架飞机,她就能飞起来。这打开了一个全新的潜在客户群,但最重要的是,她以同样的面对面的方式经营。如果你发明了电话会怎么样?这给她的*工作方式带来了非常不同的变化,*也给她的经营方式和竞争优势带来了更根本的变化。她可能仍然希望与新客户面对面,但为了与老客户保持联系,她突然完全不需要出差了(开车或坐飞机)。

同样,不要认为量子计算机“通常比经典计算机更好”,而是把它们视为“做某些任务比经典计算机好得多”的工具。

因此,经理和高管的问题是:“这些任务是什么?”。

是时候忘记了

重复一遍,我们不需要知道这里发生了什么。因此,如果有人使用叠加纠缠这个词,礼貌但坚定地告诉他们移到下一张幻灯片,或者给能够专注于业务问题的人腾出空间。同样,忽略所有关于退火量子机器、绝对零度、相干时间、掺杂钻石、传送协议、量子逻辑门或拓扑量子位的参考。是的,如果你和你的公司开始你的量子之旅,你的员工可能会使用这些短语,你会慢慢学会,但未来几个月你接触的销售人员、顾问或量子大师都不应该浪费一秒钟的时间来提及这些。

最简短的历史

虽然完全没有必要了解量子计算机的历史,但如果你感兴趣的话,这里有一个 60 秒的总结。

量子物理学在 20 世纪初开始被讨论,事实上,2021 年是阿尔伯特·爱因斯坦获得诺贝尔奖 100 周年,因为他提出光子是一种行为类似于粒子的光量子。在计算机中使用这些量子力学概念是由英国物理学家大卫·多伊奇在 20 世纪 80 年代首次提出的,算法的概念工作进展远远快于任何可以运行算法的硬件。在 20 世纪 90 年代和 21 世纪初,从量子位的角度来看,建造硬件的第一次努力开始并进展缓慢,大量新知识正在开发。

最初的纯学术研究演变成了高科技初创企业和大型科技公司专注于商业的实验室。这加速了事情的发展,在 2010 年,越来越多的公司开始制造可用于研究的机器。随着转向商业相关机器的时间表越来越清晰,识别该技术实际应用的公司迅速增加,与硬件制造商建立了共生关系。在过去五年中,知名终端客户和越来越多的风险投资公司的参与继续加速。

NISQ

在下一节中,我们将详细讨论 QC 最擅长解决的特定类型的问题。在此之前,虽然有一个流行语我们需要谈谈。

至少在接下来的几年里,你应该注意的一个术语是 NISQ。这代表嘈杂的中间尺度量子。它是由领先的量子思想家约翰·普雷斯基尔在 2017 年创造的,用来描述这一技术发展的早期阶段。在这一点上,工程师可以进行基本的质量控制工作,但在控制这些工作时,他们仍然是如此“业余”,以至于“噪音”的数量意味着我们无法完成全部工作。在这种情况下,“噪声”不是指听得见的声音,而是指导致错误的各种类型的干扰。世界正处于 NISQ 时代,至少在未来几年内将保持这种状态。这是必要的一步。

一个类似的例子可能是飞行的早期。像莱特兄弟这样的先驱已经明白,如果你制造出某种形状的机翼,并使飞机加速到足够大,你就可以实现比空气重的飞行。最初几年是没有“实际用途”的实验。尽管飞机升得更高,飞得更远,但这些仍然只是实验,没有创造商业价值。然而,这是完全必要的一步。人们可以认为当前的 NISQ 时代对于量子计算机也是如此。正如飞行的先驱们相信,随着时间的推移,他们将开发更多的控制,质量控制的先驱们确信,在短短几年内,更复杂的硬件和改进的误差校正将意味着他们可以提供真正有价值的解决方案。

量子计算机长什么样?

到现在为止,你可能已经看到了大多数讨论量子计算的文章所附带的看起来怪异的金蜘蛛网。这种复杂性是机器内部的,就像如果你打开你的笔记本电脑或 iPhone,你会看到复杂性。出于时代的原因,人们认为值得让这些设备看起来像它们的潜力一样令人兴奋,这就是为什么它们看起来如此时髦。

事实上,IBM 在 2019 年更进一步,他们要求为英国皇冠珠宝设计展示柜的同一家公司为 IBM System Q One 开发一个外壳。其结果是一个非常有影响力和美丽的设计主导的技术未来的代表。

应该注意的是,你经常看到的金蜘蛛网的图像是典型的超导量子计算机,其中大部分可见的是冷却和控制单元,以保持设备在 15 毫开尔文左右,不高于(流行语警告!)绝对零度,比太空冷 100 倍。这些通常和人类的身高差不多,重 100 公斤

带有“量子位”的实际量子芯片位于该装置的底部,是一个直径几厘米的镀金铜盘,上面装有一片硅片。

怎么给自己弄个 QC?

玩家们

许多公司都在开发量子计算机——这些公司包括传统的大型科技公司,自 21 世纪初以来一直在研究这一主题的大型企业,以及成立仅几年的新兴企业。

大多数情况下,没有人会买一整台量子计算机。相反,他们将通过云连接到一个服务器,并在需要时使用其容量。与购买一台机器需要十亿美元的赌注相比,这种“QCaaS”的概念——量子计算即服务——将有助于更快地吸收该技术。

出于成本和安全原因,一些组织(如政府、大学和重度企业用户)将购买自己的设备,但截至 2021 年初,只有少数机器被购买。IBM 已经向德国政府出售了一台,向一所日本大学/研究机构出售了另一台。2021 年 3 月,克利夫兰诊所成为美国第一个私营部门现场安装 IBM Quantum System One 的头条新闻。在英国,没有商用量子计算机,尽管这将很快改变,因为 Rigetti 目前正在牛津郊外为英国政府建造一台量子计算机,最初的机器将于 2021 年底准备就绪。

硬件开发领域正在快速发展,因此一个全面的列表很快就会过时。当您开始量子就绪之旅时,您的专家将考虑与哪种类型的硬件和哪家提供商合作最有意义。硬件领域备受瞩目的公司包括 IBM、Google、Honeywell、Rigetti、D-Wave、IonQ、Oxford Quantum Circuits 和 PsiQuantum。许多初创/扩大规模的企业已经获得了包括高盛、安德森·霍洛维茨和 Y-Combinator 在内的主要投资者的数亿美元投资。

我选哪一个?

同样值得一提的是,有不同类型的底层技术正在被使用。名字和物理差异并不重要。唯一的“游戏规则改变者”将是制造商能够找到比竞争对手多几个数量级的量子比特。如果你看看任何经典电脑或智能手机的内部,底层的微芯片技术是非常标准化的。量子计算离标准化的水*还很远。

如果你关注过你的数字中断历史,你当然会问是否存在“VHS vs Betamax”的困境。对于绝大多数潜在用户来说,答案是否定的。这是因为他们可以忽略底层硬件,因为开发算法和软件的公司正在确保他们可以在多家制造商的硬件上工作,在许多情况下,可以在根本不同的量子计算技术上工作。

很有可能,随着未来十年复杂性的发展,不同类型的硬件将被证明更适合解决特定类型的问题。我们目前离这还有一段距离,当你考虑如何做好量子准备时,这种不确定性不应该成为公司的障碍。

访问 QC

如上所述,大多数公司将通过远程连接制造商的机器(“通过云”)来获得量子计算能力。随着你的公司在量子计算领域向前发展,你的团队将迅速了解最佳选择,在需要外部帮助的地方,有越来越多的专业咨询公司提供一系列服务,旨在尽可能容易地获得新生量子机器的能力。

量子优势、量子优势和混合量子

QC 团体的一个引人注目的*期目标是首先证明量子优势,然后证明量子优势。

量子优势是一个模糊的术语,用来指量子计算机执行任务的速度比经典计算机快得多。

量子优势的定义更加明确,量子计算机可以在任何合理的时间内完成经典计算机无法完成的任务。最*,谷歌和中国合肥的一组大学科学家宣称量子至上。这两种说法都围绕着解决问题的速度比超级计算机快几十亿或几万亿倍。尽管从最纯粹的意义上来说,它们可能是真实的,但它们是在解决没有商业价值的深奥问题,本质上是一种公关活动。(事实上,一些评论者认为这是对整个行业的负面公关,因为它吸引了非技术业务人员的注意力,但又如此不相关,以至于这些观众很快失去了兴趣,并怀疑整个话题是否无关紧要。)

在 NISQ 期间,可用量子位的数量将被测量到 100 个数量级,它们的大多数应用既没有给我们带来量子优势,也没有给我们带来量子优势。与商业人士更直接相关的是不断增长的混合量子领域。

混合量子是量子旅程中的天然垫脚石,其中那些适合量子计算机的计算部分使用超级计算机(HPC)上的量子模拟器。这迫使研究这个问题的团队去理解哪些计算是 QC 相关的,并给他们以适合量子计算的方式打包它们的练习。当合适的量子计算机可用时,将这些计算从量子模拟器转移到实际的量子计算机是一件简单的事情。

缩放:如前所述,并不是所有的量子位都是同等创造的。重要的不是数量,而是“可用的”,有时被称为无错量子位。目标是将这些规模扩大到数百个,然后数千个,最终数百万个。这种缩放是科学家和工程师面临的技术挑战,但因为每个额外的无错误量子位会使设备的计算能力加倍,所以这是一种边际收益非常值得追求的情况。这种“量子位增长”的速度对你和你的量子准备时间表很重要。工程师们实际上是如何做的,你不需要了解。

成本和能源使用

在量子伦敦网络研讨会的专家 Q & A 中,经常出现的两个问题与成本和能源使用有关。

成本。 虽然没有人清楚 QCaaS(量子计算即服务)的成本,但共识似乎是,一开始会很昂贵,但成本会迅速降低,不再是决定进行量子计算的决定性因素。虽然花费在研发上的数十亿美元将需要收回,但预计硬件领域将有足够的竞争来保持价格可控。鉴于该产品基于服务的性质,在过去五年中,AWS、微软 Azure 和谷歌云之战如何展开,可能有很多东西可以学习。

能量使用 。目前,信息和通信技术设备的电力使用受到了极大的关注,考虑到它使用了世界能源的 5-10 %,这是恰当的。然而,尽管大多数量子处理单元需要冷却,但整体能源需求很小,当然,通过量子计算机解决问题所需的能源将少于在大型超级计算机上解决相同问题所需的能源。

你如何给量子计算机编程?

尽管建造量子计算机的技术非常复杂,但商业用户的编程方法并不复杂。由于硬件制造商和第三方开发的接口和“库”,企业中的最终用户拥有了一个与他们用于任何其他计算系统的开发环境没有太大不同的开发环境。

因此,业务用户可以专注于将他们的业务问题分解为那些将受益于或不会受益于 quantum 的部分,并专注于开发组合算法。2020 年末,Quantum London 为那些想体验直接编码量子计算机的人推出了一个编码社区。用户发现这个过程与他们在非量子机器上完成的编码没有明显的不同。

混合技术堆栈—图片由作者创建

即使在适合 QC 的问题世界中,也有更容易和更困难的问题,因为问题的表述和开发算法的复杂性各不相同。这与经典计算世界中的情况相同,在经典计算世界中,数据分析师的一些工作比其他部分更复杂。

开发正确算法的“困难”完全与在量子机器上实际运行它的时间无关。例如,将一个较大的数分解成它的组成素数是一个简单的算法,它在几十年前就已经在理论上被确定了。优化旅行推销员问题的变化相对更难。

感谢您阅读本部分。继续通过下面的链接或阅读电子书(美国链接,英国链接)

  • 介绍(这篇文章)
  • 为什么 QC 是有用的和潜在的商业应用(链接此处)
  • 为什么忙碌的高管应该参与到这个话题中来(链接将在发布后添加)