TowardsDataScience-博客中文翻译-2021-七十二-
TowardsDataScience 博客中文翻译 2021(七十二)
图片中的概率公理
通过图片呈现的概率三大公理。
三个 Kolmogorov 公理支撑着概率论。在探讨公理之前,先介绍一些常见的概率语言。
一个实验是一个观察过程,由于随机效应,其输出不能被确定地预测。举例:掷骰子。
试验是一项实验的单次发生。一个实验的多次尝试可以形成一个新的实验。例:一个实验由两次掷骰子组成,试验是两次掷骰子实验的一个实例。
结果是试验的观察输出。例:两次掷骰子实验中掷出 3 或掷出 1 和 5。
样本空间ω是一个实验所有可能结果的集合。例子:骰子的每一面或有序双掷骰子的实验。
试验产生结果。图片作者。
一个事件T22 一个是样本空间ω中结果的子集。例子:滚小于 4 的边,滚偶数或滚 2 再滚 3。
一个事件是一组结果。图片作者。
集合论的运算适用于事件。事件的联合是 A 、 B 或两者中的一组结果。事件的交集是 A 和 B 的结果集合。
两个事件的并集和交集。图片作者。
不包含任何结果的事件称为空事件。如果事件 A 和 B 没有共同的结果,那么 A 和 B 就是不相交事件(互斥)。
概率测度 P 是为每个可测量事件分配一个实数的函数。概率度量必须遵循概率公理。
概率度量将事件映射到必须遵循概率公理的实数。图片作者。
现在我们将探讨概率的三个公理。
第一公理:非负,实数
事件的概率是一个非负的实数。
概率必须是非负实数。图片作者。
这个公理意味着一个事件的最小概率为零。它没有规定上界,但是概率定理规定了上界。
第二个公理:单一性
样本空间中至少有一个结果出现的概率是 1。
结果发生的概率是 1。图片作者。
这个公理意味着,观察一个实验必然会产生一个结果。
第三公理:可数可加性
如果在样本空间ω中有一个无限的不相交事件集,那么事件联合的概率等于所有事件的概率之和。
不相交事件的并集的概率等于所有事件的概率之和。图片作者。
该公理在样本空间中的一组不相交事件和每个事件的个体概率之间形成了一种关系。一个概率定理展示了不相交事件的有限集也可以表示为一个无限集。
概率公理随后可以用来推导概率定理。
概率分布:数据科学家的直觉
艾米丽·莫特在 Unsplash 上的照片
高斯、二项式和泊松分布的直觉和使用案例
作为一名数据科学家,如果你被要求找出客户的平均收入,你会怎么做?拥有所有的客户数据当然是“最好的”,但在现实中,它从来不存在,也不可行收集。
取而代之的是,你得到一个小样本,对它进行测量,然后对整个人口进行预测。但是你对你的样本统计数据代表总体有多大把握呢?
统计分布在测量这种不确定性和给你信心方面起着重要的作用。简单来说,概率分布是描述一个变量的特定结果(值)的可能性的函数。平均收入是一个连续变量,这意味着它可以取任何值——2 万美元/年或 8.09 万美元/年或介于两者之间的任何值。这种连续变量可以用特定的概率函数来描述。
然而,并不是所有的变量都是连续型的。一些变量具有二元结果(例如,下雨/不下雨),而其他变量只是离散的数字(例如,游客数量/小时)。因此,根据变量的类型,概率分布对结果的描述是不同的。
有多少种概率分布?可能是无穷无尽的——看看这个维基百科页面就知道了,但是人们很少知道它们全部!在本文中,我将讲述代表三种不同数据类型的三种常见统计分布:
- 连续变量的正态分布
- 二元结果变量的二项式分布
- 泊松分布为计数数据类型
如果您对这三个发行版有了很好的直觉,那么其他发行版应该很容易理解。
那里的大多数资料本质上都是学术性的,充满了数学和方程式,但是我尽量避免它们。相反,我的兴趣是给出这些分布及其关键特征的直觉。额外收获——我还会添加一些用例。
正态(高斯)分布
在理解什么是正态分布之前,让我们看看什么不是正态分布。
根据 2000 年美国人口普查,1 . 24 亿人花费大量时间上下班。如果你做一张人们平均旅行时间的直方图,你会得到下面的分布。
通勤者上班旅行时间直方图。来源:维基百科授权: CC BY-SA 3.0
正如你所看到的,很多人的通勤时间在 10-30 分钟之间,很少有人花不到 10 分钟或超过 50 分钟。
现在将该直方图与以下正态分布的假想变量的理论分布进行比较。如果行驶时间呈正态分布,分布的形状应该是这样的。
数据的理论正态分布。来源:
如果你喜欢玩数字,有一个奇妙的 Python 模块叫做scipy.stats
,它使用norm.rvs()
方法生成不同种类分布的随机数据。你也可以用你最喜欢的可视化库(又名seaborn
)来可视化这个分布。
# import libraries
from scipy.stats import norm
import seaborn as sns
import matplotlib.pyplot as plt# generate poisson distributed variable
# here loc = mean, scale = SD
data_norm = norm.rvs(size=1000, loc=0, scale=1) # visualize
sns.distplot(data_norm, bins = 100, kde=True)
均值= 0 的正态分布
特性
正态分布是描述随机变量对称分布的概率函数。也就是说,在正态分布的变量中,数据在均值的两侧是对称的。如果我们从正态分布中随机选取一个观察值,它很可能位于平均值附近。
一些关键特征是:
- 它是连续变量的分布
- 正态分布的曲线是钟形的
- 集中趋势的三个关键指标——均值、中值和众数——都是相等的
- 大约 68%的数据落在平均值的 1 个标准偏差内,95%落在 2 个标准偏差内
- 两种统计方法——偏斜度和峰度——告诉我们一个分布与正态分布有多远。
用例
日常生活中有很多正态分布的例子。人的身高、GRE/SAT 和其他测试分数、测量误差等。通常呈正态分布。以下是一些实际使用案例:
- 正态分布表明某些测量值是错误的还是在预期测量值的范围内
- 它可用于检测异常值和极值,这些异常值和极值位于分布的任意一个尾部
- z 分数或标准分数是在假设数据呈正态分布的情况下计算的
- 许多假设的统计检验要求检验统计量或误差是正态分布的
- 正态分布是许多机器学习算法中的隐含假设
二项式(伯努利)分布
顾名思义,二项分布是二元结果的概率分布。二元结果的一个例子是询问顾客是否喜欢某个特定的产品。他们的答案——是/否——将形成二项分布。在统计学中,二项分布的一种特殊情况称为伯努利分布。
二项式分布由两个参数描述:
- n ,试验次数
- p ,每次试验成功的概率
我们可以使用scipy.stats
模块生成生态分布,并使用seaborn
库将其可视化,如下所示:
# import libraries
from scipy.stats import binom
import seaborn as sns
import matplotlib.pyplot as plt# generate poisson distributed variable
data_bin = binom.rvs(n=10, p=0.7,size=1000)# visualize
sns.distplot(data_bin, bins = 50, kde=False)
n=10 次试验的二项分布
特征
对于描述结果概率分布的二项式分布,必须满足以下条件:
- 存在固定/已知数量的观察值,并且这些观察值彼此独立
- 只有两种可能的结果:例如对/错
- 在所有的试验中,获得任何结果的可能性都是一样的
用例示例
- 调查 300 人,询问他们是否对产品满意(是/否)
- 订阅时事通讯的人中有 65%是女性;如果我们想选出 30 名女性,你需要联系多少订阅者?
泊松分布
泊松分布模拟/预测/预测某一事件在某一区间内可能发生的次数。每天看医生的病人数量可以被认为是一个泊松过程。
泊松分布仅由一个参数描述,即μ,也称为速率参数。μ代表事件发生的平均速率。
我们可以使用scipy.stats
模块再次生成泊松分布。poisson.rvs()
方法只接受速率参数——μ。
# import libraries
from scipy.stats import poisson
import seaborn as sns
import matplotlib.pyplot as plt# generate poisson distributed variable
data_pois = poisson.rvs(mu=2, size=1000)# visualize
sns.distplot(data_pois, bins = 50, kde=False)
μ = 2 的泊松分布
用例
- 在医疗保健中,估计下午 1 点至 2 点间去诊室的病人数量
- 在金融中,预测下个月的保险索赔数量
- 在交通运输中,估计公共汽车在接下来的 10 分钟内到达车站的概率
摘要
综上所述,概率分布是描述一个变量发生特定结果(值)的可能性的函数。有不同类型的概率分布,但在本文中,我们描述了三种代表连续、计数和二元结果数据的概率分布:
- 正态分布适用于连续变量,在平均值的两侧对称
- 二项式分布适用于二元结果,可用于调查二元反应,如真/假、是/否等。
- 泊松分布适用于计数数据类型,可以预测某一事件在某一区间内可能发生的次数
我希望这篇文章是有用的。如果你有意见,请随意写在下面。你也可以通过媒体、推特或 LinkedIn 与我联系。
去神秘化的概率分布函数
试图将概率密度函数分解成最基本的原理
(图片由作者提供)
我最近决定尝试使用 Twitter 来谈论数据科学话题。我的目标是从统计学开始,随着我的学习,转移到数据科学中更复杂的主题,并简洁地解释这些主题。以下是我在这里整理的一些关于概率分布函数的帖子。
概率质量函数或 PMF:
- 概率是以样本量“n”的分数表示的频率。为了从频率中推导出概率,你用“n”除频率。这个过程称为规范化。
- 概率质量函数(PMF)将每个值映射到其对应的概率。PMF 是针对离散分布绘制的。对于连续分布,我们有 PDF 或概率密度函数,我们将在后面看到。
- 离散数据采用有限数据集的形式,而连续数据采用无限数据集的形式。
- 让我们举一个离散分布的数据的例子,就像一个无偏差骰子的滚动。样本集看起来会像这样:[1,2,3,4,5,6]。每个数字出现的概率会是 1/6 或者 0.167。这是一个均匀分布,因为样本中所有值的概率都是相同的。所以 PMF 看起来会像这样:
import seaborn as sns
from pandas import DataFrame
sample1 = [1,2,3,4,5,6]
df2 = DataFrame (sample1,columns=['DiceRolls'])probabilities = df2['DiceRolls'].value_counts(normalize=True)
sns.barplot(probabilities.index, probabilities.values, color='blue')
掷骰子的 PMF
- 再举一个下面这个例子:【10,15,20,20,25,33,40,51,51,60,64,64,64】。PMF 将如下所示:
sample2 = [10,15,20,20,25,33,40,51,51,60,64,64,64]
df3 = DataFrame(sample2,columns=['Sample'])probabilities = df3['Sample'].value_counts(normalize=True)
sns.barplot(probabilities.index, probabilities.values, color='grey')
样本数据的 PMF
- 要从 PMF 中得出平均值,需要计算概率和相应值的乘积之和
- PMF 有它的局限性。当样本中有太多的值时,阅读和解释 PMF 图就变得很困难。随着每个值的概率降低和随机噪声的影响增加。解决这个问题的一个方法是对数据进行分类。宁滨将数据划分为非重叠值范围。但是很难确定箱子的大小。
- 因此,为了解决这两个问题,我们使用 CDF 或累积分布函数。
累积分布函数或 CDF:
- CDF(x)是随机值“X”小于或等于 X 的概率,或者 CDF(x)表示小于或等于 X 的值的分数
- 让我们举一个标准化测试的例子。如果班上有 A、B、C、D、E 五个人,他们对应的分数是 10、20、30、40、50。CDF(30)表示得分低于或等于 30 的学生百分比,等于(3/5)*100,即 60%。
- 假设你需要找出 x1 和 x2 之间的值的分数,其中 x1>x2,那么你应该找到 CDF(x2)-CDF(x1)。
- 类似地,为了找到特定值大于 x1 的概率,我们做 1-CDF(x1)。
- 让我们看看 PMF 部分中以前的样本的 CDF 是什么样的:
a .掷骰子
fig, ax = plt.subplots(figsize=(8, 4))
ax.hist(df2.DiceRolls, density = 1, histtype='barstacked',cumulative=True, label='CDF')
plt.show()
骰子滚动的 CDF
b .样本:[10,15,20,20,25,33,40,51,51,60,64,64,64]
fig, ax = plt.subplots(figsize=(8, 4))
ax.hist(df3.Sample, density = 1, histtype='barstacked',cumulative=True, label='CDF')
plt.show()
样本数据的 PMF
PDF 的概率密度函数:
- 我们前面看到,PMF 是为离散分布定义的。对于连续分布,我们画出一种叫做 PDF 或概率密度函数的东西。
- 根据定义,x 的概率密度是 x 的每单位概率的度量。
- 在 PMF 中,如果选择一个值,比如说 1(在掷骰子的例子中),并试图找到它出现的相应概率,那么我们可以很容易地找到它的概率是 0.167。然而,在 PDF 中,这并不那么简单。
- 在 PDF 的情况下,我们发现 x 的概率位于某个值的范围内,任何单点的概率密度为零,原因是:
- PDF 计算为图表下的面积,一般来说,面积通过乘以长度和高度来计算。任何一个点的长度都为零。然而,如果我们考虑许多单点的范围或集合,我们可以发现其相应的概率密度是非零的。
便携文档格式
- 让我们考虑另一个连续分布的例子,比如年降雨量
- 假设我们需要找到 3 厘米降雨量的可能性,实际上我们无法精确确定 3 厘米降雨量的概率,因为在任何特定的一天,它可能不精确为 3,它可能是 3.3,3.5,3.001,2.9999——所以我们想找到的是 x 在某个范围内的概率或(3-0.1)的概率
- Think of it another way, random variable x in case of a continuous variable can take infinite possible values, and in that case probability of x being any single value is very very small or zero
- Conversely we can say any distribution that can be defined by a PDF is a continuous random variable
- Just as the sum of all probabilities in a PMF equal 1, the total area under a PDF equals 1
PDF formula
如果你喜欢这个内容并寻找更多,那么请继续关注,不要忘记关注我的 Twitter 帐户以获得类似的内容【T5
参考
如果没有艾伦·b·唐尼的《Thinkstats》这本书和像汗学院https://www.youtube.com/user/khanacademy和奇妙的 zed statisticshttps://www.youtube.com/channel/UC6AVa0vSrCpuskzGDDKz_EQ这样的 youtube 频道,这种概率分布函数的汇编是不可能的
使用 Python 的 SciPy 介绍概率分布和分布拟合
如何用分布来模拟随机过程,并使它们适合观察数据
针线缝纫线眼——pix abay上的免费照片,由 Myriams-Fotos 拍摄
以下示例教程:
- SciPy 的概率分布及其性质和方法
- 通过拟合威布尔极值分布来模拟组件寿命的示例
- 一个自动化的拟合程序,从大约 60 个候选分布中选择最佳分布
概率分布描述了受随机过程影响的现象:
- 自然发生的随机过程;
- 或者知识不全导致的不确定性。
随机过程的结果称为随机变量 x。分布函数将概率映射到 x 的出现次数。
SciPy 统计了 104 个连续分布和 19 个离散分布,这些分布可以在它的 stats.rv_continuous 和 stats.rv_discrete 类中实例化。离散分布处理可计数的结果,例如顾客到达柜台。连续分布计算 x 轴上两个结果或点之间发生的概率,如高度、温度或时间的变化。
近年来,在传感器数据和其他大量可量化信息来源的快速增长的推动下,与工程和技术相关的分布已经蓬勃发展,这些分布试图对设备的寿命或故障时间以及生物学和药剂学进行建模。SciPy 包括威布尔和极值分布的几种变体,以及对数正态和疲劳寿命或伯恩鲍姆-桑德斯分布,它们适用于可靠性工程中的任务。在文献中,人们可以找到更多最近的合并,如 Weibull-Birnbaum-Saunders 或 Kummer-beta-Birnbaum-Saunders 分布等,用于生产、供应链、建筑、生物、水文和工程等领域。因此,概率分布不仅是股票和期权交易者的主要工具。除了我们经常遇到的均匀分布和正态分布之外,我们还应该看一看。我们的许多数据科学方法依赖于正态分布的数据或残差。然而,为了模拟真实世界的随机过程,我们需要准备识别和评估可选的随机模型。
SciPy 中提供了 123 个发行版:
我已经将 60+的 Scipy 连续分布导入到下面将要描述的 fitter 脚本中。不可否认的是,当我看到 fitter 可以相对较快地处理我导入的最初 10 个发行版时,我感到非常高兴。我的结论是,我可以在几分钟内追加 50 个,看看拟合过程是否会在一个或另一个更具外来参数化的分布上出错。到目前为止,它工作得很顺利。
0.属国
我们首先引入——除了我们的全天候核心库如 pandas 和 numpy——SciPy 的 stats 类。
应该添加两个常数:Kolmogorov-Smirnov 拟合优度检验将从选定的分布中抽取的样本数;显著性水平为 0.05。
接下来,我们编写了一个大约 60 个 SciPy 发行版的列表,我们希望为 fitter 实例化并导入它们。
Python 在其导入语句中不接受像发行版这样的列表对象,因此相同的发行版名称必须列出两次。像 AST 这样的库理论上可以帮助自动读取和重新插入导入的内容,但是这些循环比将导入的发行版复制到列表发行版需要更多的代码行。
列表分布包含我们想要作为我们选择的候选分布传递给 fitter 程序的选择。当然,您可以将列表缩减到更少的候选人,以便从中搜索最合适的候选人。
1.SciPy 分布及其性质
在我们深入研究钳工的构造之前,让我们围绕 SciPy 的分发对象进行一次快速的观光旅行,以了解它们是如何工作和交互的。
1.1 选择并实例化一个发行版
我们选择β分布作为第一个例子,并通过将其两个形状参数 a(α)和 b(η)设置为 2 和 6 来对其进行参数化。
贝塔分布有非常灵活的形状,比正态分布更灵活。它的默认支持或域是区间[0;下面,我们将看到如何通过向两个共享参数添加位置和比例参数来将支持扩展到更宽的区间。我们对形状参数的选择将使它看起来类似于正态分布的钟形曲线,但并不完全与之一致——它将有点正偏,右边有一条拉长的尾巴。
我们通过应用 rvs() 函数生成 1000 个遵循 Beta(2,6)分布的随机变量 x,然后将它们绘制成直方图。
1,000 个随机变量的 Beta(2,6)
SciPy 的 beta 是一个类型为分布生成器 beta_gen 的对象,继承自它的连续分布类。
1.2 分布的属性
让我们检索 Beta(2,6)的更多属性:矩和形状参数。我们分别计算它们,然后将它们收集在字典 stats 中,在那里我们将它们与描述性名称配对,最后我们使用列表理解来逐行美化字典。
为了一次获得四个核心统计量,即阶为 4 的矩,我们使用了带有关键字矩=‘mvsk’:均值、方差、偏斜度和峰度的统计量方法。
我们观察到一个偏斜度不同于我们从标准正态分布中得到的尺度 0。偏斜度衡量分布关于其平均值的不对称性。Beta(2,6)是中度正偏、右尾或右偏的。它的曲线看起来是左倾的,但偏度指的是尾巴的不对称。尾部不会相互平衡,相反,分布的主体或质量集中在平均值的左边,而右边的尾部被拉长。
1,000 个随机变量的 Beta(2,6)
峰度是对分布“尾部”的度量(不是它的“峰值”,与各种来源提供的解释相反)。偏斜度测量尾部之间的不平衡。相反,峰度高的分布在或尾部有产生更多异常值的倾向;它相对于中心是尾部加权的,因此它的中心看起来更薄。尾部比正态分布的平均值的三个标准差延伸得更远,这构成了其质量的 99.7%。在金融或工程中,高峰值表示经历非常小或非常大的观察值的风险较高。
作为衡量标准,正态分布的峰度为 3.0。但是 SciPy 使用了过度峰度并将正态分布的度量校准为 0。过度峰度衡量尾部与正态分布的差异程度。
Beta(2,6)有一个 0.11 的小正超额峰度。接近于零的值表示它是中间型-它的尾部仅比正态分布略重,因此其极值模式将是相似的。一个更有特色的正峰度使分布变细,意为“瘦”;它在 x 轴上的异常值离中心更远,因此看起来没那么大。相比之下,具有负峰度的宽峰分布有短尾巴;与正态分布相比,它表现出降低的极值倾向。它的行为更容易预测。我们的β(2,6)变量具有足够接近于零的过度峰度,我们可以认为它是中等峰度,仍然非常类似于正态分布。
1.3 冻结参数化
在之前的所有代码行中,我们一次又一次地引用 beta(a,b ),将 Beta(2,6)参数化。如果我们不需要在未来的代码行中修改参数,我们可以将分布对象及其当前参数赋给变量 rv。SciPy 称之为冻结随机变量对象。
1.4 获取概率和分位数
我们引入另外两个函数:概率密度函数 pdf 和百分点函数 ppf。
可移植文档格式文件的扩展名(portable document format 的缩写)
为了绘制概率密度函数 pdf,我们通过让 linspace() 绘制一条坐标点线来校准 x 轴。作为端点,我们选择 1%和 99%的分位数:这是β(2,6)分布分别以 1%和 99%的概率不会超过的 x 值。百分点函数 ppf()提供了这些概率下的 x 值。最小值和最大值不适用于具有无界区域的许多分布:理论上,它们的 x 轴可以延伸到正和/或负无穷大,就像正态分布的情况一样;实际上,脚本会报告一个设置为无穷大的轴端点的错误。
1.5 发行版的领域或支持
贝塔分布的范围是什么?让我们通过调用它的 support() 方法来询问 SciPy。
标准的贝塔分布沿着 x 轴从 0 延伸到 1。在这一点上,它与以平均值 0 和标准偏差 1 为中心的标准正态分布没有什么不同,这意味着除了零左右 3 个偏差之外,标准正态分布只携带其质量的极小部分——尽管其尾部理论上确实延伸到无穷大,这些值被观察到的概率越来越小。
伽马(1,1)分布沿着 x 轴一直延伸到正无穷大。
标准的贝塔分布对于许多实际应用来说太有限了。所以,除了它的两个形状参数,我们再给它配备位置和比例参数。“位置”和“比例”是参数化的,也适用于大多数其他 SciPy 发行版。
1.6 重新调整和改变分布
我们通过在行 5 中输入 beta(shape_a,shape_b,loc,scale)来定义移位和重新缩放的 beta 分布,并将结果赋给变量 rv。
可移植文档格式文件的扩展名(portable document format 的缩写)
位置参数 loc 沿 x 轴将其最小支持移动到值 100;位置和比例之和将其最大值移动到 100+220 = 320。这种分布可以很好地反映一种现实世界的现象,这种现象服从于 100 和 320 边界之间的随机过程或不确定性。
接下来,我们通过应用 stats()计算前四个矩,并将结果元组转换为第 3 行中的一个列表。因为方差很难解释,我们也计算标准偏差,我们把它附加到第 4 行的矩列表中。我们写下五个指标的名称,然后将值列表与字典中的名称列表进行压缩组合 moments ,最后将字典交给 list comprehension,它将在第 8 行中一行一行地打印其内容。
随机过程以平均值 155 为中心,标准偏差为 31.8。因为我们只移动和缩放了曲线,所以它的形状保持不变——它保留了它的偏斜度和过度峰度。
让我们画出累积分布函数 cdf 和它的逆函数,百分点或分位数函数 ppf 。
累积分布函数(Cumulative Distribution Function 的缩写)
反向 cdf 或 ppf
我们将 x 轴上选定的点(第 2 行中的均值、中值、1%和 99%分位数)提供给 cdf 和 pdf 函数,以获得比浏览图表更精确的结果。第 5 行中的字典理解将 x 值(用作键)与 cdf 和 pdf 值组合,然后打印它们,cdf 在左边,pdf 在右边。
- 在 x = 150.3 时,我们观察到中位数:50%的观察值 x 低于 150.3。
- 在 x = 241.5 时,我们观察到 99%的分位数-99%的 x 值不超过 241.5。
最后,我们将 cdf 的累积概率传递给百分点函数 ppf。第 5 行中的字典理解用 ppf 函数返回的分位数列表压缩了我们上面评估的 x 值。ppf 计算的 x 值应该与我们传递给 cdf 的原始 x 值相匹配。在下表中,我们看到左侧列中的 x 值和右侧的匹配分位数。因此,我们证实了它们的一致性。
2.分布拟合
2.1 原则
描述随机过程时,首先想到的分布是正态分布。尽管它在教科书中占主导地位,但它不符合大量随机过程的条件:
- 正态分布关于它的平均值和中间值是对称的。但通常,观察数据表明,产生它们的随机过程是左偏或右偏的,尤其是如果定义域是有界的,例如,如果它在零处有一个硬最小值。
- 正态分布是无界的:它的定义域延伸到正负无穷大。大多数真实的现象都无法用无限的增长或数量来描述;或者它们不能假设负值,这使得正态分布的概率质量的一半无效。
- 正态分布不是重尾的。许多随机过程,例如金融市场中的随机过程,表现出所谓的长尾效应:极端结果发生的概率——对工程或金融决策至关重要——高于正态分布本身的预测。
我们希望符合观测日期的候选分布应根据以下标准进行选择:
- 随机过程的性质如果我们能辨别它的话。在现有的 200 多种分布模型中(是的,真的——甚至 SciPy 也没有列举出所有这些模型),许多模型都是为描述特定类型的随机过程而定制的。示例:为了模拟设备故障前的时间——如果故障取决于设备运行的时间——威布尔分布应该是拟合过程中选择的候选分布之一;疲劳寿命或伯恩鲍姆-桑德斯分布是模拟设备寿命的另一种方法。然而,排队——一种不同的随机过程——需要泊松变量来描述。
- 分布的域或支持代表定义它的 x 值的区间。例如,大多数自然现象和大多数技术过程不能假定为负值。技术故障间隔时间不能为负数。这些过程的候选分布需要有一个非负域。对他们中的一些人来说,这个领域是他们数学定义中固有的。其他的可以进行变换:它们可以被截断或移位,并重新缩放以将其重新校准到非负域,例如半正态分布。SciPy 提供了位置和比例参数,用于将分布的域移动到适当的区间。大多数正态变量代表标准正态分布的移位和重标度变量:平均值从 0 移位到观察值或估计值;标准偏差已从 1 重新校准为围绕移动平均值的重新调整区间,因此,3 个偏差范围内包含约 99.7%的所有事件。
- 观测数据的形状。如果随机过程的性质不能被先验地描述,那么直方图的形状将提供关于例如对称、左偏或右偏分布是否将是更好的拟合的线索。
- 多模型验证:当我们运行数据科学模型时,我们不会将调查局限于单个模型。相反,我们将几个备选模型或至少备选超参数元组应用于数据。类似地,我们需要在拟合过程中尝试几种可选的分布。我们将传递给 fitter 的大量 60+候选人将有助于确定我们不会因为缩短搜索而最终得到一个次优的模型。
- 拟合优度测试,如 Kolmogorov-Smirnov 测试,为我们提供了比图表更精确的测试结果,这些图表将观察值的直方图与拟合的分布曲线进行比较。
2.2 设备故障时间:拟合威布尔分布
在我们构建自动拟合器之前,我们希望了解如何将选定的个体分布拟合到观测数据中。让我们举一个例子,用威布尔分布来拟合设备寿命问题。
我们将使用一个虚构的数据集:电子电路板的故障时间,该数据集将用于第一艘将宇航员送往火星的宇宙飞船。因此,在地球轨道之外的一些卡车停靠站将没有容易的方法来获得替换零件。
威布尔模型可以从数学上推导为平行失效过程中最薄弱环节出现的时间。它被广泛应用于可靠性工程、水文学、生物学或风能分析等领域。
SciPy 的“威布尔最小极值”分布(威布尔最小极值分布—SciPy 1 . 7 . 1 手册,与 SciPy 的“威布尔最大极值”变量相对)相当于 NIST 解释的威布尔分布(美国国家标准与技术研究所, 1.3.6.6.8)。威布尔分布(nist.gov))和维基百科(威布尔分布—维基百科)。您会发现,用于参数的符号在不同的源之间有很大的不同,将它们标识为形状、比例和位置更安全。
SciPy 以标准化的形式表达它的分布对象。weibull_min 分布只有一个形状参数 c(在维基百科中称为 k,在 NIST 称为 gamma,在其他来源中称为 beta)。比例和位置参数是可选的。
- 如果形状< 1: the failure rate decreases with time, after a kind of burn-in phase
- if shape =1: constant failure rate =>威布尔分布符合指数分布
- 如果形状> 1:故障率随时间增加
比例参数——在各种来源中表示为λ、α或β——也称为特征寿命。随着比例的增加,威布尔分布变得更加对称,类似于正态分布。
一个位置参数,如果包括在内形成一个三参数威布尔变量,也被称为等待时间:在 L 小时之前不会发生故障,L ≥ 0。
首先,在等待电路板制造商提供真实数据的同时,我们创建了一组合成观察数据来准备我们的模型。我们希望模拟 1000 个组件的故障时间,以小时为单位,这些组件的测试数据很快就会到达。
我们选择 1.5 的形状——因此,故障率随时间增加——和 50,000 小时的特征寿命或标度。
右长尾表明实际数据相当右偏,高于 1.0,而正态分布为 0。故障从时间零点开始,因此不涉及等待时间或位置参数——相反,质量问题会在第一批电路通过工厂大门并在航天中心制成原型后立即出现并开始淘汰。
接下来,我们将实际数据输入到第 6 行中 weibull_min 分布对象的 fit() 函数中。
注意参数 floc = 0 。 floc 表示我已经通过输入带有前缀‘f’的参数名称将位置参数的值固定为 0。fit()函数将搜索形状和比例值,以便威布尔分布反映实际数据;但是它会将位置保持在 0 不变。原因是灵活的位置参数可以极大地影响形状和比例的值。如果我们不阻止 fitter 访问 location 参数,它会在所有三个参数之间寻找最佳折衷。如果我们没有关于随机过程的先验知识,我们可能会对位置犹豫不决。但是,如果我们知道故障可能从时间零点开始发生,即使实际记录开始得稍晚一些,我们也应该将位置设置为零,并让验配员专注于形状和比例,而不受不准确的位置值的不当干扰。验配员不应在较高的位置值与较高或较低的比例或形状值之间进行权衡。
pdf 曲线紧密地跟踪直方图的轮廓。让我们用一个拟合优度的假设检验来确认视觉印象:Kolmogorov-Smirnov KS 检验。
KS 检验非常高的 p 值几乎肯定地证实了三参数威布尔最小值(1.54,0,49201)变量反映了实际数据的结构。
平均无故障时间为 45,137 小时。
让我们假设一个阈值时间点位于 T = 20,000 小时。在时间 T 之后,电路不再被认为是任务关键的,它们将完成它们被分配的任务。
- 下面,我们计算累积分布函数 cdf,它告诉我们在 20,000 小时后,所有电路的 22.4%将已经烧坏。
- 生存函数 sf(T)也可以计算为 1 — cdf(T)。它报告说,在 20,000 小时时,77.6%的电路仍将起作用。
- 在 10,000 小时时,91.4%的电路将会存活。
- 我们还观察到,在最初的 500 小时测试中,只有前 0.1%的电路会出现故障。
- 百分点函数 ppf 告诉我们,直到 1.0%的组件出现故障,需要 2329 个小时。因此,生产后立即进行 500 小时甚至 2300 小时的延长测试不会显著减少故障数量。大多数电路稍后会断开,即不会在地面条件下断开。
- 我们的结论是,我们需要大量的备件库存来替换在最初的 20,000 小时内会烧毁的 22.4%的电路;并且需要训练宇航员安装它们。或者,我们可以尝试提高电路设计的鲁棒性或减少使用时间。
3.自动装配工
SciPy 提供了一种方法。分别为每个分布对象安装()。为了建立多模型评估流程,我们将为自动验配程序编写一个脚本。我们将把 60 个候选人的列表输入到装配员的嘴里,让它找到一个与实际观察最接近的随机变量。
我们从 KS 拟合优度测试的函数开始,该函数将被调用 60 次,以比较给定候选分布与实际数据的距离。
接下来,我们编写一个短函数 fitdist(),,它适合所选的候选项,然后调用 KS 测试。和以前一样,我们将所有候选对象的位置参数冻结为 0:floc = 0。这是一个默认设置,如果您想扩大解决方案的范围,可以选择修改它。
第二行让列表理解发挥作用。理解从脚本顶部的列表分布、中提取每个分布对象,并将其传递给 fitter 函数。
comprehension res 返回所有拟合分布的参数,以及它们各自 KS 测试的 p 值,并将它们列表在数据框中。
数据框包含 61 个拟合分布及其参数。
我们准备了一个绘图函数,可以绘制出函数调用方传递给它的所有分布。
绘图功能
- 确定绘制数据帧中的所有分布需要多少行,每行三个子图,
- 从数据帧中提取分布的行及其参数,
- 忽略具有 NaN 值的参数,
- 将 x 轴的端点校准到 1%和 99%分位数,
- 绘制实际观察值的直方图,
- 并绘制所选分布的 pdf。
在调用绘图函数之前,我们在第 2 行中进行选择:我们只需要那些显示 KS 测试结果的候选分布的图表,其 p 值高于显著性水平 ALPHA = 0.05。我们忽略提供更差匹配的候选人。在这个例子中,61 个候选人中只有 6 个成功通过了 KS 测试。在代码调用绘图仪函数 plot_fitted_pdf() 之前,脚本创建一个数据帧 df_ks,该数据帧仅限于这六个 ks 批准的发行版。
- 不出所料,双参数威布尔最小分布以 42.5%的 p 值领先该领域。
- 具有 2 个形状值和 1 个比例值的 3 参数 Beta 分布达到 35.2%。
- 双参数 Nakagami 分布是 Gamma 家族的亲戚,达到 26.9%的固体 p 值。
- 非中心 f 分布、Mielke 分布和 Burr 分布是更奇特的候选分布,其 p 值超过了显著性阈值,但与威布尔分布和贝塔分布相比,拟合质量有所下降。
脚本选择具有最高 KS p 值的候选,weibull_min,并将其赋给变量 D_opt 。
我们可以开始检查和处理这个最佳拟合,就像我们在示例中处理任何其他分布实例一样。
- stats()函数报告拟合分布的四个主要矩。
- 我们可以获得平均故障时间,并询问在该时间点有百分之多少的组件会发生故障:57.8%。统计和百分点函数告诉我们平均无故障时间将是 42,879 小时。当所有电路的 50%将变暗时,平均故障时间将为 36,949 小时。
- 威布尔最小分布的支持当然是在从时间零到正无穷大的区间内。
今天的教程到此结束。
- 我们已经回顾了 SciPy 的分布类以及与它们相关的方法和属性。
- 我们将选定的分布(威布尔最小值)拟合到一组代表故障时间观察值的合成数据中,从而模拟了可靠性工程中的一个问题。
- 最后,我们编写了一个更合适的过程,根据 61 个候选分布的列表测试实际分布,其中三个在我们的示例中提供了非常好的拟合,当我们对它们运行 Kolmogorov-Smirnov 测试函数时,p 值超过 20%。
Jupyter 笔记本可以在 GitHub 上下载:h3ik0th/dist fitter:SciPy distribution fitting(github.com)
- 标题图片:皮查拜的 Myriams-Fotos
- 所有其他图片:作者
数据科学家的概率论
概率、条件概率和联合概率
莫里茨·金德勒在 Unsplash 上拍摄的照片
生活充满了惊喜,所以我们永远不知道事情会如何发展。但是,我们可以根据以前的经验或逻辑进行猜测。根据一个事件的特征,我们的猜测可能会非常成功。
在数学中,我们不做猜测。相反,我们计算概率。生活也充满了数学的惊喜,所以有一个计算的概率值并不能保证正确的答案。但是,我们会知道,我们的猜测是基于数学的,客观的,没有偏见的。
概率论对于数据科学非常重要。要成为一名出色的数据科学家,需要对概率论有全面的理解。例如,概率分布在预测分析中起着关键作用。
概率只是指事件发生的可能性,通常取 0 到 1 之间的值(0 和 1 包括在内)。事件 A 的概率被表示为 p(A) ,并且被计算为期望结果的数量除以所有结果的数量。例如,当你掷骰子时,得到小于 3 的数字的概率是 2 / 6。期望结果的数量是 2 (1 和 2);总结果数为 6。
(图片由作者提供)
我有一个盒子,里面有 1 个蓝色的球和 4 个黄色的球。如果我从这个盒子里随机选择一个球,它很可能是黄色的。
选到黄球的概率 p(黄色)是 4 / 5,等于 0.8(或 80%)。期望结果的数量是 4,总结果的数量是 5。
照片由Dawid za wia在 Unsplash 上拍摄
条件概率和联合概率
当有人问你是否认为会下雨时,你的第一反应通常是看天空。如果有乌云,你更有可能回答“是”。你先查条件再给答案。
我们在条件概率的概念后面观察到一个相似的逻辑。假设事件 B 已经发生,事件 A 的概率表示为 p(A|B) 。
条件概率是在与事件 A 相关的另一事件已经发生的情况下,事件 A 发生的可能性。
条件概率的公式如下所示:
(图片由作者提供)
P(A ∩ B)是事件 A 和 B 都发生的概率。P(B)是事件 B 发生的概率。
让我们通过一个例子来理解条件概率的概念。在下图中,我们看到一个概率空间(ω),表示所有概率的总和为 1。
(图片来源:维基百科)
事件 A 的无条件概率,
P(A) = 0.1 + 0.3 + 0.12 = 0.52
给定 B2 事件发生的条件概率 P(A | B2),
P(A | B2)= P(A∩B2)/P(B2)= 0.12/0.16 = 0.75
假设 B2 发生,事件 A 的概率增加。
条件概率是概率论和统计学中的一个基本概念。例如,贝叶斯统计源于对条件概率的解释。在机器学习中,朴素贝叶斯算法基于贝叶斯定理和条件概率。
关于条件概率,有几个要点需要强调。
- 给定的条件可能对事件没有任何影响,因此条件概率和无条件概率相等(即 P( A | B) = P(A))。在这种情况下,事件 A 和 B 被认为是独立的。
换句话说,如果两个事件相互独立,给定 B 的条件概率等于 A 的概率。它来自于联合概率的一个性质。
联合概率是两个事件同时发生的概率。如果两个事件是独立的,则联合概率是通过将每个事件的概率相乘来计算的。
P(A ∩ B) = P(A) * P(B)
如果我们把它放到条件概率的等式中:
(图片由作者提供)
- 条件概率通常不可交换,这意味着 P(A | B)不等于 P(B | A)。
例如,我们之前已经计算出 P(A | B2)为 0.75。让我们也计算 P(B2 | A)并比较结果。
P(A | B2)= P(A∩B2)/P(B2)= 0.12/0.16 = 0.75
P(B2 | A)= P(B2∩A)/P(A)= 0.12/0.52 = 0.23
最后,让我们再做一个例子。下表显示了选修社会学和音乐课的男女学生人数。
(图片由作者提供)
有 103 名学生。我们将首先计算无条件概率。
p(女性)= 52 / 103 = 0.505
p(男性)= 51 / 103 = 0.495
p(音乐)= 47 / 103 = 0.456
p(社会学)= 56 / 103 = 0.544
p(女)表示一个学生是女性的概率。
联合概率可以通过将单元中的数字除以学生总数来计算。例如,一个学生是女性并且注册了社会学课程的概率:
p(女性∩社会学)= 24 / 103 = 0.233
我们可以类似地计算其他联合概率。下表包含了这些事件的所有概率。
(图片表格)
我们现在将计算条件概率。
- 假设一个学生注册了音乐班,那么这个学生是女性的概率是多少?
P(女|乐)= P(女∩乐)/ P(乐)
= 0.272 / 0.456 = 0.596
- 假设一个学生注册了社会学课程,那么这个学生是男性的概率是多少?
P(男性|社会学)= P(男性∩社会学)/ P(社会学)
= 0.311 / 0.544 = 0.572
结论
不确定性在我们的生活中无处不在。因此,任何科学领域都需要在实践和理论上处理不确定性。
概率论在数据科学领域也是至关重要的。由于我们无法消除不确定性,我们需要积极主动的方法来有效地处理它。例如,变量的概率分布在预测分析中起着关键作用。要成为一名优秀的数据科学家,需要对概率论有全面的理解。
感谢您的阅读。如果您有任何反馈,请告诉我。
改进商业决策的概率论
安迪·汉德森在 Unsplash 上的照片
商业中概率的概念和应用
让我们从简单的开始。你已经调查了 50 位顾客,了解他们对你的产品/服务是否满意。其中,35%的人说他们很快乐。
仅仅基于这些信息,你能预测一个随机的顾客的态度吗?你可以用概率做出这样的预测:
35/50 = 0.7
这意味着,有 70%的机会,一个样本外的客户会有一个积极的看法的产品。用最简单的术语来说,那就是概率——某事发生的可能性。
上面的例子只有一个结果——客户的积极态度。然而,一些客户可能会表现出其他偏好,如消极或中立态度。对于诸如此类的多重结果,计算概率略有不同。下面结合实例探讨两种方法。
加法规则
在 50 名客户中,35 名持正面观点,10 名持负面观点,5 名持中立观点。现在,您需要预测客户对产品持正面或中立观点的概率。
解决方案听起来很简单——增加两个概率。
P(正)= 35/50
P(中性)= 5/50
因此,客户持正面或中性观点的概率为 35/50 + 5/50 = 80%。
加法规则的一般公式如下:
P(A 或 B) = P(A) + P(B) — P(A 和 B)
通过使用集合论中的形式符号:
p(a⋃b)= p(a)+p(b)—p(a⋂b)
乘法法则
加法规则适用于两个互斥的事件。在上面的例子中,顾客不能同时持有正面的和负面的观点,因为它们是互斥的。
然而,存在两个事件不互斥的情况,因此,两种结果都有可能同时发生。比方说,一个学生要参加两个科目的考试——物理和文学。学生通过每次考试的概率是:
P(物理)= 0.4
P(文学)= 0.3
基于这些信息,你能预测学生通过物理和文学考试的概率吗?
从数据来看,这个学生似乎很难通过任何一门考试。所以通过两个考试的概率肯定极低。这就是乘法规则在计算联合概率时派上用场的地方:
P(物理与文学)= P(物理) P(文学)*
答案当然是 0.4*0.3 = 12%。
将此类问题推广到两个不互斥的事件 A 和 B :
P(A⋂B) = P(A)。P(B)
条件概率
条件概率是在满足另一个条件的情况下,某事发生的可能性/机会。
作为例子,让我们考虑两个相关的事件。
如果一家新的创业公司熬过了第五年,它最终成为十亿美元公司的概率有多大?
因此,对于一家有朝一日成为十亿美元公司的初创公司来说,它必须熬过 5 年。在所有创建的创业公司中,如果 20%存活下来,存活下来的公司中有 5%成为十亿美元公司,那么新公司最终成为十亿美元公司的概率是:0.2* 0.05 = 1%。
条件概率的一种形式表示是: P(B|A) = P(A 和 B)/P(A)
概率的商业应用
概率的关键作用是在面对不确定性时改善决策。它有助于决策的客观和数据驱动,而不是基于本能。
比方说,海滨商店的冰淇淋销售取决于两个因素——好天气和大量的游客。
因此,一周后,如果好天气的概率为 90%,预期游客率为 30%,那么预期销售额的概率为:
P(天气) P(预期访客)= 0.90 * 0.30 = 27%。*
这个数字本身就可以帮助老板决定当天安排多少员工。
商业应用的几个其他例子:
- 根据风险调整后的投资回报(ROI)选择投资选项,尤其是在投资选项为:a)高风险高回报,b)高风险低回报,或 c)低风险低回报的情况下。
- 财产保险公司使用概率来计算一部分客户的保险索赔几率,并决定收取的保费。同样,人寿保险公司使用概率来估计客户的预期寿命,以设定保费。
一锤定音
企业——无论大小——经常根据直觉、本能和经验主观地做出重要决定。但是,随着数据量、工具和技术的增加,许多组织在面临不确定性的情况下正在转向数据驱动的决策。这篇短文介绍了概率的一些基本概念及其应用。关于概率的相关文章,请查看文章:概率分布:数据科学家的直觉。
如果你有评论和想法,请随意写下来,或者通过媒体、推特或 LinkedIn 与我联系。
数据科学和机器学习的概率与统计
有什么区别,为什么数据科学家应该知道它们
Wolfgang Hasselmann 在 Unsplash 上拍摄的照片
概率和统计——哪个更重要?
我们几乎总是一起讨论概率和统计。
事实上,学生不知道统计与概率的区别并不少见。
但是哪个先来呢?甚至有什么区别?
这是个先有鸡还是先有蛋的问题。没有统计就不可能有概率,反之亦然。我这么说是什么意思?我会在这篇文章中解释更多。
在这篇短文中,我将强调概率和统计之间的一些差异,以及概率和统计在数据科学中的一些应用。
由此,我希望你能获得或有一个新的视角,为什么数据科学家需要知道概率和统计。
可能性
爱德华多·苏亚雷斯在 Unsplash 上拍摄的照片
概率是数学的一个分支,根据维基百科,它用数字描述一个事件(如抛硬币)发生的可能性。
让我们继续公平抛硬币的例子。
我们没有实际将要发生什么的数据,但是我们仍然可以用概率来回答下面的问题——
- 下一次抛硬币是正面的概率是多少?
- 5 次翻转中有 3 次是正面的概率是多少?
- 在我们看到尾巴之前,我们将硬币翻转 9 次的概率是多少?
这些概率问题向我们展示了以下内容—
在概率方面,我们从描述随机事件发生可能性的模型开始。然后我们预测事件发生的可能性。
在这里,抛硬币的'模型'是硬币是公平的,即硬币正面或反面落地的概率是 50%。我们假设这个模型是正确的,反映了事实。
当我们使用概率模型时,我们没有关于实际上 会发生什么的数据,但是我们可以量化我们预期 会发生什么。
综上所述,在概率上,我们基于一个没有实际数据的模型来预测未来事件发生的可能性有多大。
统计数字
要做统计,必须先有数据。艾萨克·史密斯在 Unsplash 上拍摄的照片
这种概率总结与统计学形成对比。在统计学中,我们根据观察到的实际数据推断出真相或模型。
事实上,统计学是一种数学分析的形式,根据维基百科的说法,它对一组给定的实验数据使用量化的模型、表示和概要。
注意对的强调,“一组给定的实验数据”。做任何统计工作都需要我们从一组数据开始。
现在,我们得到了一枚我们一无所知的硬币。假设在 100 次抛硬币中,我们观察到 90 次正面和 10 次正面,我们可以使用统计学中的概念回答以下问题:
- 硬币公平吗?(大概不会)
- 我们有多大把握认为这枚硬币是不公平的?(相当)
- 从硬币中得到一条尾巴的实际概率是多少?(最有可能 10%)
从这里,我们可以看到
在统计学中,我们查看给我们的一组数据,然后我们对用于生成这组数据的模型做出推断。
在这个例子中,我们不知道硬币的真相,就像概率一样。相反,我们知道一些关于硬币的数据,我们回顾过去对硬币做出推断。
概率与统计
概率和统计是硬币的两面。西蒙在 Unsplash 上的照片
我喜欢把统计和概率看作是两个硬币的不同端——它们有着内在的联系,但彼此相反。
- 概率是从模型到数据的,而统计是从数据到模型的。
- 概率讲的是向前看(基于事实做出预测),统计讲的是向后看(基于事件理解事实)。
事实上,斯坦福大学统计学教授佩尔西·戴康尼斯很好地总结了这些差异
概率和统计考虑的问题是互逆的。在概率论中,我们考虑一些由随机变量模拟的具有随机性或不确定性的潜在过程,并找出发生了什么。在统计学中,我们观察已经发生的事情,并试图找出解释这些观察结果的潜在过程。
概率与统计
概率是统计学和机器学习的阶梯。照片由约瑟夫·阿克布鲁德在 Unsplash 上拍摄
现在我们已经比较了概率和统计,我们是否也可以看到概率和统计是如何相关的?
概率和统计是同床异梦。人们通常先学习概率,然后在此基础上学习统计学,而概率是通向统计学的阶梯。对统计学的深刻理解也会增强一个人对概率的鉴赏能力。
在我最喜欢的一个讲座中,Tsitsiklis 教授在 6.431x 概率系统分析和应用概率中描述了概率和统计之间的关系
概率至少给了我们一些系统思考不确定情况的规则。如果我们的概率模型与现实世界有某种联系,那么概率论可以成为一种非常有用的工具,用于做出适用于现实世界的预测和决策。
你的预测和决策是否有效取决于你是否选择了一个好的模型。有一个完整的领域,统计学领域,其目的是通过使用数据提出好的模型来补充概率论。【1】
下图总结了这种关系。
概率与统计的关系。图片作者。
概率、统计、数据科学和机器学习
在这里,我将概述一些概率和统计对数据科学家至关重要的情况。
- 计划 A/B 测试或实验
为了理解 AB 检验,我们需要理解 p 值的概念,这是一个概率概念,它告诉我们当零假设实际上是正确的时候,它是错误的概率。为了获得对 p 值的直观理解,需要很强的概率基础。
AB 测试的另一个步骤是获得实验的样本量。为了将实验结果推广到一般人群,需要一个适当的抽样方案。为了计算所需的样本量,需要理解实验中的功效和误差类型的概念,这些概念包含在统计学中。
2.机器学习建模
此外,许多有抱负的数据科学家不知道,机器学习的理论基础本质上是先进的统计概念和数学。许多机器学习模型建立在数据遵循特定类型分布的假设上。
如果不了解概率,数据科学家将无法决定数据是否满足机器学习模型的假设,因此可能无法在机器学习模型中做出最佳选择。
例如,线性回归模型假设每个数据点的噪声遵循正态分布。为了理解这意味着什么,数据科学家应该知道什么是正态分布,也就是你在概率中所学到的。
因此,无论你是使用普通的机器学习方法还是深度学习方法来运行回归、分类或聚类模型,你都不能逃避统计。
在哪里学习数据科学中的概率
对于也想学习编程和编码的新学员,Datacamp *上的 HarvardX 的数据科学-概率(PH125.3x)将提供一个关于概率的温和介绍,同时允许您在 r 中实现概率。此外,这还附带了一个关于 2007-08 年金融危机的激励性案例研究,这可能是一个您可以进一步研究和展示您的投资组合的项目。
对于进阶学习者,我建议参加 HarvardX Stat 110:概率入门 *。这个班是迄今为止我参加过的最有收获的一个班。布利茨坦教授是我最喜欢的概率教授之一,他严谨而直观地讲述了这些话题。
这里还有一个完整的指南—
在哪里学习数据科学中的统计学
对于新的学习者,我推荐学习 EdX 基础的统计推断和使用 R* 建模。这门课是为统计背景有限且实践经验很少的新学员开设的。它的目的是让学生理解为什么这种方法有效(理论),如何实现它(使用 R 编程)以及何时应用它(如果特定的方法在特定的情况下不适用,在哪里寻找)。如果你喜欢的话
对于高级学习者,我建议参加 MITx:统计学基础 *。这门课是严格的,是为雄心勃勃的学习者设置的研究生水平,这些学习者希望理解高级概念,包括拟合优度测试、一般线性模型和主成分分析,以及上述典型的统计学主题。
这里说的课都是可以免费旁听的。如果你喜欢这门课,你可以追求一个经过验证的证书来突出你在概率方面的知识。
结论
概率和统计是数据科学的重要组成部分。事实上,根据 IBM 数据科学技能能力模型,以下是数据科学家 28 项主要能力中的 2 项。
理解概率论和概率分布
展示推理统计学的知识
它们对数据科学家来说都很重要。所以,同时学习这两种语言总是一个好主意。
如果您有任何问题,或者只是想一起学习数据科学,请随时与我联系。
http://www.linkedin.com/in/travistang [## Travis Tang —数据分析师
www.linkedin.com](http://www.linkedin.com/in/travistang)
这是概率系列的一部分。这是一个很小的数据科学,你现在可以在 5 分钟内学会。
- 概率模型和公理
- 概率 vs 统计(你来了!)
- 条件概率(即将推出)
- 贝叶斯统计(即将推出)
- 离散概率分布(即将推出)
- 连续概率分布(即将推出)
- 平均值和大数定律(即将推出)
- 中心极限定理(即将推出)
- 联合发行(即将推出)
- 马尔可夫链(即将推出)
参考文献和脚注
[1]约翰·齐茨克里斯。6.041 概率系统分析和应用概率。【2010 年秋天。麻省理工学院:麻省理工学院开放课件,【https://ocw.mit.edu】T2。许可:知识共享协议 BY-NC-SA 。
*这些是我推荐的课程的附属链接。这意味着,如果你点击链接后购买,我会收到一定比例的费用,没有额外的费用给你。
图形百分比的问题
有时候你需要知道什么时候打破规则
作者图片
当创建带有数字 y 轴的图形时,我们经常被告知“从零开始”的规则。但是和所有规则一样,我们需要知道什么时候遵守规则,什么时候打破规则。鉴于此,我想谈谈绘制百分比。
我最近参加了一个关于数据可视化的课程,经常听到一些同学提到这个规则(可能不是用那些确切的词),就像它是福音一样。当我回答时,我通常会说这样的话,“但是百分比很奇怪。”
我的背景是化学,所以当讨论百分比时,我首先想到的是纯度。当谈到化学纯度时,我们通常会报告我们最感兴趣的东西的百分比。因此,如果我们幸运的话,化学合成可能有 90%的纯度,如果我们运气不好,可能有 30%的纯度。这两个数字之间的差异是显而易见的。不那么明显的是 99.0%和 99.9%的纯度有多大区别。这些数字看起来非常接近,差别可以忽略不计。但真的是这样吗?
假设我们用稍微不同的反应条件进行了六次反应,得到的纯度范围为 99.0–99.9%。如果我们要绘制“从零开始”的图,我们会得到这样的图:
作者图片
从这个图表中,看起来不同的反应之间几乎没有区别。事实是,如果我们看事物的另一面(污染物),从 99.0%纯度到 99.9%纯度意味着污染物从 1.0%下降到 0.1%,这意味着我们的污染物下降了一个数量级。如果污染物是水,这可能没什么大不了,但如果污染物是砷,那就麻烦了。现在,如果我们知道只有一种污染物,我们可以创建一个污染物浓度图,如下所示:
作者图片
但是如果污染是由多种物质混合造成的呢?好吧,你可以像上面的图一样画出污染物总量,但是如果你想关注纯度,那么你可能想画第一张图。但是,如果我们想显示反应之间的差异,那么我们需要以某种方式调整尺度。在这种情况下,我们抛出“从零开始”的规则,从更合理的东西开始。在这一点上,你的问题是决定最好的规模。这是最难的部分。你想展示不同之处,但又不想表现得太极端。这里有三个选择。
作者提供的图片
你会选择哪个?你想讲什么故事?小心不要过度推销你的想法。
正如我在开头所说的,百分比很奇怪,也很棘手。
看看我以前的文章。
使用 Vaex 处理具有 2 亿行的数据集
使用 vaex 数据框对大型数据集执行操作
图片由皮克斯拜的 Gerd Altmann 提供
Pandas 是最受欢迎的用于数据科学案例研究的图书馆之一。是 探索性数据分析 和 数据角力 的最佳工具之一。Pandas 可以高效地处理最适合内存的小型或中型数据集。对于非核心数据集或大型数据集,pandas 执行操作的效率很低。人们需要花费大量时间使用 pandas 数据框对大型数据集进行探索性数据分析。
在这里,Vaex 进入了救援阶段,它有一个类似于熊猫的 API,在执行内存不足的数据集时非常有效。
Vaex 是什么?
Vaex 是一个替代 pandas 的高性能 Python 库,它使用表达式系统和内存映射,允许开发人员在标准机器上对内存不足或大型数据集执行操作。它涵盖了一些熊猫的 API,更侧重于数据探索和可视化。
Vaex 并不完全兼容 Pandas API ,但是大部分数据争论和探索功能 Vaex 也是可用的。数据集特征的可视化是使用直方图、密度图和 3d 体绘制完成的。
Vaex 的效率如何?
与 Pandas 相比,Vaex 在大型数据集上执行操作时效率非常高。在下一篇文章中,我们将看到 Vaex 大规模数据争论实验的基准数据。Vaex 的一些关键效率包括:
- 能够在您的机器上立即读取大约 1.2TB 的数据。
- 可以在您的笔记本电脑上轻松处理和执行超过 10 亿行的操作
- 与 pandas 相比,能够加快字符串处理速度10–1000 倍。
Vaex 怎么这么高效?
Vaex 可以加载非常大的数据集(几乎 1.2TB),并且能够在您的机器上执行探索和可视化。Vaex 不是将整个数据加载到内存中,而是内存映射数据并创建一个表达式系统。
Vaex 使用内存映射、零内存复制策略和惰性计算来获得最佳性能,因此不会浪费内存。每次您对数据框进行任何更改时,它实际上都引用相同的数据,只是向表达式添加了一个新的状态。
实验:
我生成了一个人工数据集,有 2 亿行和 4 列(' id ',' name ',' x ',' y') ,以'时间戳'作为索引。数据集的总大小为 12GB 。
**timestamp**: index, 1s time frequency from [2014-09-01, 2020-12-31]
**id**: int
**name**: string
**x**: float
**y**: float
(图片由作者提供),左:数据集结构,右:数据集的前 5 行
一些流行的数据探索实验是在 windows 操作系统和 8GB 内存上对 2 亿行数据集进行的:
- 读取数据
- 数据形状
- 数据描述
- 数值计数
- 【按列分组】和
- 第 10 百分位计算
- 可视化一列
- 应用功能
- 添加新列
- 过滤数据帧
读取数据:
该实验的设计遵循了每种工具的最佳实践,即对 Vaex 使用二进制格式 HDF5。需要将 CSV 文件转换为 HDF5 格式,以便 Vaex 能够发挥最佳性能。Vaex 需要 33 分钟将 CSV 文件的 2313 个分区转换为 HDF5 格式。
现在从磁盘读取 HDF5 数据:
**df = vaex.open("200M_data_hdf5/analysis_*.hdf5")**
Vaex 需要 6 分钟来读取整个数据集。
数据形状:
使用 Vaex 计算数据集中的行数需要 no time (0 ns) 。整个数据大约有 2 亿行。
(图片由作者提供)
数据描述:
使用**.describe()**
函数生成描述性统计数据,包括数据集分布的集中趋势、离差和形状,不包括NaN
值。
(图片由作者提供),描述函数的结果
Vaex 花费了大约 15 分钟来计算每一列的描述性统计数据。
数值计数:
使用 Vaex 数据框中的函数**.value_counts()**
计算分类列“名称”的频率分布。
(按作者分类的图片),名称列的前 6 个频率分布
Vaex 用了大约 3.5 分钟返回“名称”列的频率分布。
分组依据:
与 pandas API 类似,Vaex 也提供了一个计算分组和聚合的函数。以下命令对“name”列进行分组,并聚合“x”列的平均值。
(按作者排序的图像),按名称排序的结果列
Vaex 花费了大约 2.5 分钟来计算上面的分组和聚合命令。
计算“id”列的分组以及“x”和“y”两列的平均聚合。
(按作者排序的图片),按姓名排序的结果列
Vaex 花费了大约 11.5 分钟秒来计算上述分组和聚合命令。
第 10 百分位计算:
Vaex 有 percentile_approx 函数来计算给定百分位数的近似值。
(图片由作者提供)
Vaex 用了 46.8 秒来计算“id”列的第 10 个百分位。
可视化列:
绘制大规模数据的直方图是有问题的,因为传统的数据分析工具没有优化来处理它们。
(图片由作者提供),列“x”的直方图
使用 Vaex 中的 plot1d 函数绘制数值向量的直方图,用了3.5 分钟返回图。
应用功能:
类似于 Pandas API,Vaex 有apply
函数来沿着数据帧的轴应用一个函数。返回名称列中元音列表的函数:
(图片由作者提供)
Vaex 几乎没花多少时间( 132 ms )就处理了姓名列的 2 亿条记录。
添加新列:
Vaex 实际上不需要花时间向数据集添加列,因为它不会立即添加新列,而是使用表达式系统来生成新列的表达式。
添加新列 Vaex 花费了将近251 毫秒。
(图片由作者提供),添加新列后的结果
过滤数据帧:
与 pandas API 类似,Vaex 也有类似的选择概念,根据任何给定的条件过滤数据。Vaex 不会立即过滤数据帧,而是生成一个表达式。
(图片由作者提供),过滤后的结果
Vaex 几乎不需要任何时间( 273 ms )来应用过滤器,从上图可以看出,数据帧的形状从 2 亿减少到了 9800 万。
(图片由作者提供),2 亿数据集上 Vaex 操作的时间约束
结论:
在本文中,我们已经生成了 2 亿条时间序列模拟数据记录,这些数据有 4 列,大小接近 12GB。使用熊猫图书馆不可能读取数据集并对其进行探索和可视化。
Vaex 数据框架可以轻松读取数据,并执行所需的探索和可视化。这是对 Vaex 数据帧的唯一要求,适用于 HDF5 数据。因此,CSV 文件需要转换为所需的 HDF5 格式。
此外,Vaex 库中提供了大多数流行的 Pandas API,因此它是处理大型数据集最有用的库。
参考资料:
[1] Vaex 文件:https://vaex.readthedocs.io/en/latest/
感谢您的阅读
从点击流数据中评估应用用户行为的流程挖掘
对 pm4py python 库的深入介绍
图片由 Gerd Altmann 从 Pixabay 拍摄
不久前,我写了一篇文章,描述了一些有用的查询,人们可以对点击流事件的数据进行查询,以收集一些可操作的见解。这篇文章可以被认为是它的自然继任者,因为(a)我们将在类似的数据集(点击流)上工作,以及(b)具有相同的潜在动机(收集一些见解)。然而,我们将从应用程序上的maximum_time_spent
和most_played_podcasts
等原始分析转向更全面的相关流程视图——从用户打开应用程序开始,到收听、评级、分享,最后关闭/卸载应用程序。
流程挖掘
流程挖掘可以被认为是用户在你的应用上做什么的可视化描述。
流程挖掘可以用来描绘用户如何在应用程序中导航的画面。它可用于指向:
- 主要的棘手问题包括应用程序安装和用户参与播客应用程序之间的长时间延迟,
- 可以提高效率的流程,例如优化购物应用程序中的结账体验,
- 经常被触发的事件,例如紧随播客下载事件之后的应用卸载,
诸如此类…
虽然有许多流程挖掘算法可供使用,其中一些在本文中做了很好的总结,但是我们在 Podurama 的团队发现直接遵循 Graph (DFG) &启发式挖掘器(HM)最适合上面定义的用例。随后,这些就是我们将在本教程中重点关注的内容!
直接跟随图表
dfg 是探索事件数据的很好的第一手段。它们背后的基本思想非常简单——它是一个图,节点对应于事件/活动,有向边对应于直接跟随关系。例如,连接事件e1
和e2
的边对应于观察到事件e1
紧接着事件e2
的。连接两个事件的边可以用额外的信息来修饰,例如有多少实例沿着这条边被发现,或者每个实例从e1
到e2
花费的平均时间是多少。
启发式挖掘器
HM 是一种流行的流程挖掘算法,用于对事件流进行建模。与 DFG 的替代方案相比,HM 试图找到事件遵循的规则。更具体地说,该算法能够滤除异常边缘,甚至检测两个事件(如sleeping
和snoring
)何时同时发生。因此,这些在图中没有联系,即使在事件日志中一个可能直接跟随另一个。在某种程度上,HM 更适合处理日志中的噪音,并找到公共的结构(两个活动之间的依赖)。
让我们看看他们的行动吧!
注意:在我们开始编码之前,请确保已经使用 *pip install pm4py*
安装了 pm4py 库。
让我们直接进入编码..
装置
import pm4py
import pandas as pd
import numpy as np
from pm4py.objects.conversion.log import converter as log_converter
from pm4py.objects.log.util import dataframe_utils
from pm4py.algo.discovery.heuristics import algorithm as heuristics_miner
from pm4py.algo.discovery.dfg import algorithm as dfg_discovery
from pm4py.visualization.heuristics_net import visualizer as hn_visualizer
from pm4py.visualization.dfg import visualizer as dfg_visualization
日常数据集
在我们看到实际数据集上的结果之前,我将使用一个(非常)小的虚拟事件数据集,因为我希望能够显示每个阶段的中间输出。如果你想跟着去,这里有一本 Jupyter 笔记本。
注意:请随意用您选择的数据集替换虚拟数据集,只要它遵循我们稍后将讨论的命名约定。
dummy = pd.DataFrame(np.array([['A', '06/10/2021' , 'waking' ],
['A', '06/11/2021' , 'eating'],
['A', '06/12/2021' , 'sleeping'],
['B', '06/12/2021' , 'waking'],
['B', '06/13/2021' , 'eating'],
['B', '06/14/2021' , 'sleeping'],
['C', '06/06/2021' , 'waking'],
['C', '06/11/2021' , 'sleeping'],
['C', '06/12/2021' , 'eating'],
['D', '06/15/2021' , 'eating'],
['D', '06/16/2021' , 'eating'],
['E', '06/15/2021' , 'eating'],
['E', '06/26/2021' , 'eating'],
['F', '06/11/2021' , 'waking'],
['F', '06/15/2021' , 'sleeping']]),columns=['case:concept:name', 'time:timestamp', 'concept:name'])
它包含三个主栏— case:concept:name
、time:timestamp
和concept:name
。这些分别对应于用户名、时间戳和事件类型。为简单起见,我们将处理六个用户— A
: F
和三种事件类型,即waking
、eating
和sleeping
。
现在您可能想知道— 为什么要使用神秘的列名!为什么不简单地分别命名为“用户标识”、“时间戳”和“事件类型”?简短的回答是——因为 pm4py 喜欢那样。如果您的列有不同的名称,我强烈建议在进行进一步分析之前重命名它们。
需要记住的另一件重要事情是时间戳列应该遵循MM/DD/YYYY
格式。也就是说,用户A
决定在 6 月 10 日wake
起床,然后在 6 月 11 日eat
起床,接着在 6 月 12 日sleep
起床。原因是——pm4py 对数据集中的时间戳列进行了一些预处理,从而自动转换为YYYY-MM-DD HH:MM:SS+MS:MS
格式。为了让这个预处理正确工作,它期望日志中使用MM/DD/YYYY
格式。
dummy = dataframe_utils.convert_timestamp_columns_in_df(dummy)
dummy.head()
注意:不要认为 pm4py 会根据列中的值自动确定时间戳格式。它盲目地将所有内容转换为 *MM-DD-YYYY*
格式,这可能会导致一些时间戳被错误地转换!
直接跟随图表
log = log_converter.apply(dummy)
variant=dfg_visualization.Variants.FREQUENCY
dfg = dfg_discovery.apply(log)gviz = dfg_visualization.apply(dfg, log=log, variant=variant)# display viz
dfg_visualization.view(gviz)
作者图片
左边的 DFG 有几件事需要注意:
- 这些框表示事件类型,并遵循基于框内值的热图颜色。
- 方框内的值指示数据集中有多少行包含该特定事件。例如,event_type =
waking
在dummy
数据集中出现了四次(在第 0、3、6 和 13 行)。 - 箭头/边上的数字表示频率,即该特定有向边观察到多少个实例。例如,围绕
eating
的边缘刻有2
的循环指的是来自用户 D 和 e 的两个实例。类似地,在日志中只有一个实例,我们观察到一个sleeping
事件后跟一个eating
事件,即来自用户C
。最后,箭头越亮,意味着沿着这条路径找到的实例越多。
注意:请记住而非的频率并不对应于遵循该路径的唯一用户数量。换句话说,如果我们在日志中为用户D
增加了两行数据,那么:
dummy = pd.DataFrame(np.array([
.
.
.
['D', '06/16/2021' , 'eating'],
['D', '06/16/2021' , 'eating'],
.
.
.]),columns=['case:concept:name', 'time:timestamp', 'concept:name'])
如果我们创造了新的 DFG,它会是这样的:
作者图片
注意围绕eating
的循环现在如何将频率值显示为 4(而不是 2 ),因为现在有 4 个eating
事件的实例,后面跟着另一个eating
事件,一个来自用户E
,三个来自用户D
。
如果您想在 edges 的顶部打印平均时间(而不是频率),那么需要对代码进行一点修改,我们在apply
方法中引入一个名为variant
的参数,并将其值设置为PERFORMANCE
而不是FREQUENCY
:
log = log_converter.apply(dummy)
**variant=dfg_visualization.Variants.PERFORMANCE** dfg = dfg_discovery.apply(log, **variant=dfg_discovery.Variants.PERFORMANCE**)gviz = dfg_visualization.apply(dfg, log=log, variant=variant)# display viz
dfg_visualization.view(gviz)
作者图片
produce 树看起来与前面的树非常相似,但是,现在边对应于时间(以天D
和秒s
表示)。例如,根据 DFG,刚吃完饭平均需要 6 天才能回到正餐,即eating->eating
(围绕event = eating
的循环)。让我们从dummy
数据集中验证这一点。
在那里,只找到了这个特定跟踪的两个实例(用户D
和E
各一个),分别花费了前一个用户一天的时间(6 月 15 日-6 月 16 日)和后十一天的时间(6 月 15 日-6 月 26 日)。平均花费的时间是(1+11 / 2) = 6 天。
dfg 中的过滤边
有时候,你的 DFG 看起来会像一个意大利面条模型,尤其是当你要处理很多事情的时候。在这种情况下,我喜欢通过设置MAX_NO_EDGES_IN_DIAGRAM
参数来限制输出树中的边数。此外,通过分别设置START_ACTIVITIES
和END_ACTIVITIES
参数,根据事件日志标记开始和结束活动也是有用的。
log = log_converter.apply(dummy)
**_, start_activities, end_activities = pm4py.discover_dfg(log)**
variant=dfg_visualization.Variants.FREQUENCYdfg = dfg_discovery.apply(log)**parameters = {
dfg_visualization.Variants.FREQUENCY.value.Parameters.MAX_NO_EDGES_IN_DIAGRAM: 3,** **dfg_visualization.Variants.FREQUENCY.value.Parameters.START_ACTIVITIES: start_activities,****dfg_visualization.Variants.FREQUENCY.value.Parameters.END_ACTIVITIES: end_activities
}**gviz = dfg_visualization.apply(dfg, log=log, variant=variant, **parameters= parameters**)dfg_visualization.view(gviz)
作者图片
正如你所看到的,我们已经成功地去除了一些混乱,只保留了那些权重最高的边。还有两个额外的节点—绿色和橙色。这些圆圈分别对应于流程流的开始和结束状态。这些可以解释为—观察到 4 个实例,其中waking
是第一个调用的事件。查看dummy
数据集,我们可以验证这对于用户A
、B
、C
和F
都成立。
记住:尽管上图中看起来可能有 3 条以上的边,但请注意来自 *start_activity*
的所有边 和进入 *end_activity*
的所有边都被排除在过滤过程之外。排除它们,我们可以确认只存在 3 条边: *waking->eating*
、 *waking->sleeping*
和 *eating->sleeping*
。
启发式挖掘器
为了确定是否有可以为日常日志建模的事件流,让我们生成一个启发式网络。
log = log_converter.apply(dummy)
heu_net = heuristics_miner.apply_heu(log)gviz = hn_visualizer.apply(heu_net)hn_visualizer.view(gviz)
作者图片
启发式网络的输出可以用和 DFG 一样的方式来解释。正如前面介绍中所述,主要区别在于算法试图确定事件遵循的一些规则。它不仅考虑了边沿的频率,还考虑了边沿的重要性。如果你想知道显著性是如何计算的,公式和算法可以在这里找到。
根据上述启发式网络,在典型的日常工作流程中,用户通过waking
或eating
开始他们的一天。在后一种情况下,这是他们在一天结束前唯一做的事情(橙色圆圈),而在前一种情况下,他们决定在完成日常工作前做sleep
或eat
。
如果你仔细观察,该算法已经发现了这样一个事实,即sleeping
和eating
是并发活动,因此它们之间没有边。
在 HM 中过滤边缘
HN 经常变得太乱,无法提供任何有用的见解。在这种情况下,我们可以基于最小频率值,仅过滤并保留一些边,同时保持所有节点(正方形块)完好无损。例如,在上面的启发式网络中,为了只保留 DFG 出现次数至少为 4 的边,我们将引入MIN_DFG_OCCURRENCES
参数。由于只有一条边START->waking
带有权重为 4 或更大的箭头,这应该是输出 HN 图中出现的唯一的边:
log = log_converter.apply(dummy)
heu_net = heuristics_miner.apply_heu(log, **parameters={ heuristics_miner.Variants.CLASSIC.value.Parameters.MIN_DFG_OCCURRENCES: 4}**)gviz = hn_visualizer.apply(heu_net)hn_visualizer.view(gviz)
作者图片
虽然修剪边缘的好处在这个虚拟示例中无法直接观察到,但我发现它在现实世界的数据集中非常有用。仅当边缘超过特定阈值时才过滤边缘,这有助于理解意大利面条模型。让我们来看看实际播客事件数据集上的流程挖掘结果。
播客数据集
在 Podurama,我们有兴趣使用流程挖掘来解决两个特别有趣的问题— (a)用户流失,以及(b)冷启动推荐。对于前者,我们决定对安装后 15 天内离开应用程序的(大约 4k)用户进行进程发现,看看我们是否可以得出一些见解。对于后者,我们使用活跃用户的日志生成了一个 DFG,以直观地检查他们在特定流派中收听的播客流。
用户流失
我们没有调查所有记录的事件,而是决定把重点放在主要的事件上,比如app_open
、search
、podcastseries_click
和app_remove
。
事件数据的前几行如下所示:
在不修剪边的情况下生成 DFG 会产生意大利面模型:
作者图片
将最大边数设置为 10 后,我们观察到:
作者图片
快速浏览一下,很明显存在大量重复使用搜索功能的情况。然而,即使许多用户都在搜索播客,即搜索事件(~ 31k),但只有少数情况(~ 8k)是通过点击任何搜索结果直接跟随的,即播客系列点击事件。同样,我们观察到在应用程序已经打开( app_open )之后,播放播客( playAudio) 的情况非常少(~ 6k)。
另一个有趣的观察是(在最大边上过滤后),我们在图中没有发现app_remove
事件,这意味着它不经常被触发。换句话说,没有多少用户明确卸载应用程序,而是在一段时间后停止登录应用程序。这可能表明需要一个及时的(非侵入性的)轻推来提醒人们使用我们的应用。
另一个值得注意的观察是关于到达流程流中的最终状态(橙色圆圈)。我们观察到大多数实例从 playAudio 事件到达结束状态,很少从 subscribePodcast 事件到达结束状态。这不太理想,因为这意味着用户在决定退出之前不会通过我们的应用程序订阅播客(这可能表明对我们的应用程序的长期承诺),而是简单地浏览一下,看看它是否坚持下来!总的来说,我们的目标是让新用户在加入应用程序的第一周订阅至少 10 个播客,因为他们订阅的播客越多,他们继续使用应用程序收听他们订阅的播客的新发布剧集的机会就越高!
冷启动建议
虽然使用流行的 python 库肯定有更好的方法来解决推荐系统的冷启动问题,但我们决定使用 dfg 来可视化用户收听的播客。这背后的主要思想很简单——一旦我们知道一个用户收听了播客 A,尝试并推荐来自相同流派的其他播客,基于在 DFG 具有最高频率的输出优势。
为了简洁起见,我们过滤了日志,删除了用户暂停并在下一次继续播放相同播客的事件。这允许我们摆脱事件周围的循环(类似于我们看到的eating
事件)
这是真实犯罪类型的 DFG 奖:
mask = (~usr_podcast['concept:name'].isna()) & (usr_podcast.genre == '**True Crime**')
log = log_converter.apply(usr_podcast.loc[mask])parameters = {
dfg_visualization.Variants.FREQUENCY.value.Parameters.MAX_NO_EDGES_IN_DIAGRAM: 10
}variant=dfg_visualization.Variants.FREQUENCY
dfg = dfg_discovery.apply(log)
gviz = dfg_visualization.apply(dfg, parameters=parameters, variant=variant)# display viz
dfg_visualization.view(gviz)
作者图片
从上面的图表中,很明显有更多的例子表明犯罪爱好者是用户收听的第一个真实犯罪播客。这是可以理解的,因为我们在主页上为新加入的用户提供了建议。基于边缘权重(越黑越好),似乎有相当多的情况下,用户在犯罪爱好者之后收听案件档案真实犯罪和未破谋杀案。这是一条有用的信息,因为现在,在我们数据库中的所有真实犯罪播客中,我们可以开始向观看历史有限的用户推荐这两个播客(以及其他类似于我最喜欢的谋杀和**一代为什么)推荐系统可能不会为他们提供有用的推荐。
作为另一个不同类型的例子,这是 DFG 科技播客的样子:
作者图片
结论
让用户参与到你的应用中是相当棘手的,尤其是当 80-90%的应用在试用一周内被删除。从用户那里评估事件的顺序会给你一个新的视角,让你明白为什么这个应用对他们不起作用。调查也有帮助,但是对于那些刚刚使用你的应用一周的用户来说,可能会显得很烦人。
流程建模的内容比本文所涵盖的要多得多,但这应该给你一个良好的开端,让你开始分析你的事件日志,并收集有用的见解,以提高保留率。
直到下次:)
用 ELK 离线处理 SCADA 报警数据
工业自动化的开源工具
本文是一个系列的开始,在这个系列中,我们将使用开源数据科学工具来分析 SCADA 系统产生的警报和事件日志。但是首先,什么是 SCADA 系统,为什么需要分析它们的警报?
工业控制系统(ICS,又名 SCADA 系统)用于控制发电站、水处理厂、面包店、啤酒厂等生产设施。工业自动化系统将由传感器、控制器和 HMI(人机界面)组成。
SCADA 系统生成大量的警报和事件数据(在准备本文时分析的系统在 12 个月内生成了超过 5700 万个警报和事件)。数据科学工具非常适合处理这些大型数据集,本文探讨了使用 ELK 堆栈进行 SCADA 警报数据分析。
什么是 SCADA?
SCADA 是基于计算机的工业控制系统的通称。SCADA(监控和数据采集)系统可用于监测和控制工业过程,包括水处理、制造、发电站、食品和饮料等。事实上,任何使用自动化制造设备的工业过程。
典型系统架构
工业控制或 SCADA 系统的目的是监测或控制物理过程。我们系统的最低层,I/O(输入输出)层包括传感器和执行器。
传感器测量温度、压力、液位等物理参数。而致动器是像马达、阀门或螺线管等装置。影响身体运动。
传感器和执行器与过程控制器或 PLC(可编程逻辑控制器)通信。过程控制器通常是用于运行软件来控制过程参数的专有计算机硬件。
SCADA 服务器使用工业协议与过程控制器通信,并执行许多功能,
- 维护传感器和致动器参数值(通常称为点)的数据库。
- 存储过程参数的历史值(趋势)
- 基于与阈值相比的参数值创建警报和事件。
- 提供 HMI(人机界面),其通常是包括动态传感器和致动器数据的图形过程表示。HMI 允许过程操作员与控制系统交互(启动和停止设备和过程,确认警报等)。).
图片来源于 Flaticon.com
人机界面
SCADA 系统的 HMI 允许操作员监控过程和远程操作设备。HMI 的一个重要部分是报警系统,该系统提醒过程操作员需要他们干预的异常过程条件。
什么是警报和事件?
警告
警报的目的是提醒操作员需要干预的异常或危险过程条件。
ICS 中的每个测量参数通常都配置了阈值,当参数超过阈值时,就会产生一个事件,称为报警。
实现过程警报的最常见方式是将过程变量 x(t) 与恒定的高和/或低阈值xₜₚ【1】进行比较,
作者图片
报警系统是所有现代 SCADA 系统的一部分。这些系统提供报警事件通知、确认、显示和过滤功能。
事件
事件类似于警报,主要区别在于警报是意外的,可能需要采取纠正措施,而事件通常不需要任何操作员措施,并且被捕获以支持审计、维护和事件调查。
一些 SCADA 系统具有独立的报警和事件系统,而其他系统将事件存储在低优先级的报警处理系统中。
为什么报警管理很重要?
特别是警报管理和警报洪水[2],几年来一直是个问题[3]。对警报洪水问题的反应导致了几个国家和国际标准的发展[4-6]。
性能不佳的报警系统与许多重大的加工工业灾难有关;埃索朗福德、三里岛、德士古米尔福德港湾和深水地平线,仅举几例。除了重大事故之外,有证据表明,报警系统性能不佳会给设施所有者带来持续的重大损失。霍尼韦尔过程解决方案公司估计,在一个典型的 100,000 BPD 炼油厂,报警系统性能的改进将每年节省 589 万美元[8]。
有人认为[9]报警泛滥的主要原因是过程状态变化,因为报警系统通常是为稳态条件设计的。在过程状态改变期间,前一个或两个警报是关键的,并指示根本原因;随后的警报是必然的,仅仅是噪音。这种噪音会极大地分散过程操作员的注意力,以至于无法确定事件的根本原因。
图片由作者使用以下图片;(左)美国能源部,公共领域,经由维基共享(中)科林·贝尔,从彭布罗克码头看到的米尔福德港炼油厂,经由维基共享(右)美国海岸警卫队,公共领域,经由维基共享
在多个标准中定义为触发 10 个以上新警报的 10 分钟周期(每个控制台操作员)。
麋鹿是什么?
ELK (Elasticsearch、Logstash 和 Kibana)堆栈是一个开源的数据收集和可视化堆栈,主要用于收集和监控服务器和应用程序日志,尽管也可以容纳许多其他用例,如商业智能和 web 分析。堆栈的主要组件是,
弹性搜索
基于 Apache Lucene 搜索引擎的开源全文搜索和分析引擎。
Elasticsearch 通过分片和聚类支持非常大的数据库。
日志存储
一个日志收集、转换和聚合工具。
基巴纳
基于网络的弹性研究分析和可视化工具。
跳动
轻量级数据转发代理或日志运送者。
安装 ELK
Windows 操作系统
- 从下载页面下载 Elasticsearch 和 Kibana(我们在这里执行的分析不需要 Logstash)
- 将文件解压缩到您选择的文件夹中(文件非常大,尤其是 Kibana,所以这可能需要一些时间)
- 确保您已经安装了 Java 并且设置了 ES_JAVA_HOME 环境变量(注意:如果在您的系统上设置了 JAVA_HOME 变量,Elasticsearch 将使用该变量,但它已被弃用,因此将来某个时候该选项将不起作用)
- 通过打开命令提示符启动 Elasticsearch,导航到解压安装文件的文件夹,将目录改为 bin 并运行 elasticsearch.bat
- 类似地,通过打开命令提示符启动 Kibana,导航到解压安装文件的文件夹,将目录改为 bin 并运行 kibana.bat
- 在以下地址打开网页浏览器, http://localhost:5601/ 您应该会看到基巴纳主页,
作者图片
其他平台
对于在其他平台上的安装,参见。
准备和加载报警数据
数据格式
我手头的报警日志文件是由施耐德电气 CitectSCADA 产品生成的。
这些文件被命名为AlarmLog.NNN
,其中NNN
是一个从 001 到 365 的零填充整数,在我们的例子中每天一个文件。
这些文件是以空格分隔的文本文件,下面是一个典型文件的前五条记录的示例,
作者图片
数据准备
数据准备非常简单,包括将空格分隔的文件转换为 CSV 文件,并清除一些无效记录。
Python 脚本用于初始转换和清理。
第一步是导入所需的库,
- pandas——一个数据分析库,我们将使用它来读写数据文件,并使用数据帧来操作数据
- glob 检索输入数据文件的文件名。
- 日期时间—用于日期和时间值操作
- tqdm —一个进度条实用程序,用于显示长时间运行的作业的进度(如处理多个 GB 的数据文件)
import pandas as pd
import glob
from datetime import datetime
from tqdm import tqdm
宽度数组是定义的,包含输入文件中每个固定宽度字段的字符宽度。names 数组包含将包含在 CSV 文件中的字段名称。
widths = [
12, #date
12, #time
4, #pirority
20, #status
12, #state
32, #tag
64, #description
]names = [
'date',
'time',
'priority',
'status',
'state',
'tag',
'description'
]
在下面的代码中,我们处理文件夹./1-raw/
中的所有原始日志文件,创建 CSV 文件并将其写入./2-csv/
。
使用 glob 函数可以获得原始输入文件路径的集合。
我们使用 for 循环来遍历每个文件。因为这是一个长时间运行的过程,所以在 for 循环中使用 tqdm 函数来显示进度条。
作者图片
对于每个文件,
- 使用 pandas read_fwf 函数读取文本文件。这个函数读入一个空格分隔(固定宽度)的文件,并返回一个数据帧
- 日志文件包含一些日期和/或时间字段值为“0”的无效记录。日期或时间值为零的记录将被删除。
- 从单独的数据和时间字段创建 ISO 格式时间戳字段,并将其添加到数据帧中
- CSV 文件被写入磁盘
files = glob.glob("./1-raw/AlarmLog.*")for f in tqdm(files):
d = "./2-csv/" + f.split("\\")[1] + ".csv"
log = pd.read_fwf(f, widths=widths, names=names)# Some errors present in the log files where 0 values present for date
# and time columns
log = log.drop(log[log.date=="0"].index)
log = log.drop(log[log.time=="0"].index)log['[@timestamp](http://twitter.com/timestamp)'] = log.apply(
lambda row: datetime.strptime(
row.date + " " + row.time, '%d/%m/%Y %I:%M:%S %p').isoformat(), axis=1)
log.drop(['date','time'], axis=1).to_csv(d,index=False)
装载到基巴纳
数据可以从用户界面直接加载到 Kibana 中( http://localhost:5601 )。这用于在处理整整 12 个月的数据之前测试预处理脚本(超过 8GB,需要另一种方法来上传数据,这将在后面描述)。
要通过 Kibana 手动上传数据,首先通过点击煎饼图标打开左侧菜单。从侧面菜单中选择Analytics > Machine Learning
。
作者图片
点击Select file
按钮,
作者图片
选择或拖动一个生成的 CSV 文件到下面屏幕上的目标,
作者图片
按Import
选择默认选项,
作者图片
下一个屏幕要求我们输入一个索引名,数据将被加载到这个索引名中。输入一个索引名称并按下Import
作者图片
几秒钟后数据将被加载,按下View index in Discover
按钮,
作者图片
我们刚刚上传了一天的警报事件。在 discover 应用程序中,我们可以看到我们上传的数据摘要,涵盖时间范围从 6 月 12 日 00:00:01 到 6 月 13 日 00:00:02。摘要图表将数据分成 30 分钟的间隔。
作者图片
如果我们将鼠标悬停在任何条形上,我们可以看到该桶中日志条目的计数。下面我们看到从 11:30 到 12:00 有 2765 个警报事件。
作者图片
Kibana 有一种查询语言(KQL,Kibana Query Language),允许我们对 ElasticSearch 数据库中的数据应用复杂的过滤器。下面,我们使用 KQL 应用了一个过滤器,将我们的记录限制为“介质过滤器 5”的优先级 1 警报。
作者图片
删除数据
我们已经证明了我们的 CSV 数据上传正确。由于我们有 365 个文件,我们不希望手动上传到数据库中。我们现在将删除手动添加的数据,并创建一个 Python 脚本来以编程方式加载整个数据目录。
在搜索栏中输入“索引”,然后从下拉列表中单击“数据/索引管理”。
作者图片
选中我们需要删除的索引旁边的复选框。
作者图片
从Manage index
按钮打开下拉列表,选择Delete index
。
作者图片
使用 Python 将数据加载到弹性搜索
之前,我们将 365 Citect 警报日志文件处理为 CSV 格式。这些文件非常大(365 个文件,总共 6.15 GB)。在上一节中,我们将一天的数据加载到 ElasticSearch 中,以验证文件格式。我们现在将创建一个 Python 脚本来加载整个数据集。
使用 Python 将数据直接加载到 ElasticSearch 相当简单,因为 ElasticSearch 已经提供了一个官方的客户端,可以使用 pip 安装,
python -m pip install elasticsearch
代码重复如下,由以下内容组成:
- 在 ElasticSearch 中创建“警报日志”索引
- 打开 data 文件夹中的每个 CSV 文件,并使用批量上传功能将其上传到 ElasticSearch。body 参数定义 CVS 列名到 ElasticSearch 数据类型的映射。
import pandas as pd
import globfrom tqdm import tqdm
from elasticsearch import Elasticsearch
from elasticsearch.helpers import bulkindexName = "alarm-log"body = {
"settings": {
"number_of_shards": 1
},
"mappings": {
"properties": {
'[@timestamp](http://twitter.com/timestamp)': {'type': 'date'},
'description': {'type': 'text'},
'priority': {'type': 'long'},
'state': {'type': 'keyword'},
'status': {'type': 'keyword'},
'tag': {'type': 'keyword'}
}
}
}client = Elasticsearch()
client.indices.delete(index=indexName)
client.indices.create(index=indexName,body=body)files = glob.glob("./2-csv/AlarmLog.*")
for f in tqdm(files):
df = pd.read_csv(f)
documents = df.to_dict(orient='records')
bulk(client, documents, index=indexName, raise_on_error=True)
首次分析
日志条目的结构
我们将使用 Kibana,Discover 工具来检查我们使用之前的 Python 脚本创建的索引的结构,以便批量加载我们的警报条目。
打开 Kibana(http://localhost:5601,打开左侧菜单,从分析组中选择发现。
作者图片
选择alarm-log
作为索引模式。完成此操作后,警报日志索引的所有属性都会显示在索引选择器下方的面板中。
作者图片
即席分析
我们从 SCADA 系统收集的数据包含报警和事件。警报的优先级为 1-3,而事件的优先级为 4 和 5。过程报警有一个生命周期,由于状态变化和操作员确认,会导致生成和记录多个事件(这将在以后的帖子中详细解释,我们将把多个报警生命周期事件合并到一个报警记录中,该记录将包括整个报警生命周期的信息,并允许进行更详细的分析。
下面的 KQL 查询过滤警报事件,只显示警报启动,
作者图片
作者图片
仪表盘
除了允许您以交互方式查询和分析数据,Discover 还允许使用各种可视化方式将查询保存并显示在仪表板中。在基巴纳术语中,保存的观想是已知的,是一个透镜。
下面的仪表板包含四个镜头组件(从左上角开始按顺时针顺序描述),
- 度量—显示一个数字,在这种情况下是所选时间段内的警报计数
- 垂直条形图—在这种情况下显示每日报警计数
- 树形图—在这种情况下,按标签和报警优先级(1–3)显示前十个报警计数
- 水平条形图—在这种情况下,显示每个标签的前十个报警计数(报警标签是唯一的 id,用于识别产生报警的设备)
作者图片
洞察力
现在我们已经创建了我们的仪表板,它告诉我们什么?
每日警报计数
从每日警报计数图表中,我们可以看到每天有 500 到 3000 个警报。由于生成这些警报日志的 SCADA 系统由 1 到 2 名操作员监控,这对于操作员来说是不切实际的管理工作量。
作者图片
前十名
前 10 个图表用于识别频繁触发的警报。我们可以看到 AIT0411203_aH 警报在 90 天内被激活了大约 18,000 次(大约。每天 200 次)。该警报明显配置错误,需要调查和补救。
作者图片
优先级分布
从树形图中我们可以看到,“其他”警报分布在 22%优先级 1、61%优先级 2 和 16%优先级 3。这表明系统很可能配置了太多优先级为 1 和 2 的警报(这将在以后关于警报系统管理和性能指标的帖子中详细解释)。
作者图片
结论
ELK 已被证明是分析 SCADA 系统中可用的报警事件日志数据的有用工具。虽然在这一初步分析中获得的见解是有用的,但它受到我们使用原始警报记录这一事实的限制。
当原始报警事件在加载到 ELK 之前被转换成覆盖报警生命周期的报警记录时,更高级的分析将是可能的。
在以后的文章中,我们将探讨,
- ICS 警报管理标准和性能指标
- 处理 SCADA 报警记录
- 使用 ELK 在线处理 SCADA 报警记录
- 先进的 SCADA 警报分析技术(序列挖掘和时间序列分析)
参考
- 工业报警系统概述:报警过载的主要原因、研究现状和存在的问题。2016 年 IEEE 自动化科学与工程汇刊。 13 (2):第 1045–1061 页。
- Vancamp,k .,通过数字进行报警管理。化学工程,2016。第 50–55 页。
- ASM,ASM 财团。
- ISA, ANSI/ISA‐18.2‐2009:流程工业报警系统的管理。2009 年:国际自动化学会。
- EEMUA, EEMUA-191:报警系统——设计、管理和采购指南。2013 年:英国伦敦工程设备和材料用户协会。
- IEC, IEC 62682 流程工业报警系统管理。瑞士日内瓦国际电工委员会。2014 年:国际电工委员会。24–26.
- 戈埃尔、p .达塔和 M.S .曼南,工业报警系统:挑战和机遇。流程工业损失预防杂志,2017 年。T21:第 23–36 页。
- Ayral,J. Bilyk 和 K. Brown,案例:量化警报管理的好处。碳氢化合物加工,2013 年。 92 (2013)。
- Beebe,d .、S. Ferrer 和 D. Logerot,峰值报警率与工厂事故的关系以及如何尽量减少事故。工艺安全进展,2013 年。 32 (1):第 72–77 页。
- Lucke,m .等人,警报数据分析的进展以及在线警报洪水分类的实际应用。过程控制杂志,2019。 79 :第 56–71 页。
进一步阅读
感谢阅读,希望你喜欢这篇文章。
为了进一步探索,
- 订阅电子邮件通知
- 点击文章顶部的“关注”按钮
- 对于工业 4.0 的所有内容,请查看我的工业数字化转型&工业 4.0 出版物
- 欢迎在 LinkedIn 上加入我的社交网络(记得提到你已经读过这篇文章)
为了支持中型作者,可以考虑订阅。
记得点击订阅和关注按钮,
作者图片
使用 Python 优化采购流程
使用非线性规划来寻找使资本、运输和存储成本最小化的最优订购策略
优化商场采购策略—(图片由作者提供)
采购管理是在您确定的预算范围内,在特定的截止日期或之前,从首选供应商处获取商品或服务的战略方法。
你的目标是平衡供应和需求,确保最低库存水平满足你的商店需求。
在本文中,我们将介绍一种使用非线性规划为中型零售店设计最优库存补充策略的简单方法,考虑:
- 从供应商仓库到商店储备****($/箱)的运输成本
- 库存融资成本(占库存价值的百分比,单位为美元)
- 储备(商场的仓库)存储租赁费用($/箱)
💌新文章直接免费放入你的收件箱:时事通讯
**SUMMARY**
**I. Scenario** As a Supply Planning manager you need to optimize inventory allocation to reduce transportation costs.
**II. Build your Model
1\. Declare your decision variables**
*What are you trying to decide?* **2\. Declare your objective function** *What do you want to minimize?*
**3\. Define the constraints**
*What are the limits in resources?* **4\. Solve the model and prepare the results** What is the suggestion of the model?
**III. Conclusion & Next Steps**
一、如何用 Python 实现采购自动化?
问题陈述
作为一家中型零售点的商店经理,您负责在 ERP 中设置补货数量。
对于每个 SKU,当库存水平低于某个阈值时,您的 ERP 会自动向您的供应商发送采购订单(PO) 。
您需要平衡库存容量、运输成本和库存成本的限制,以便为您的 PO 确定正确的数量。
- 1 供应商通过 EDI 连接 (与您的ERP)接收您的订单,并使用第三方运输公司运送,费用由您承担
注:本文中我们不考虑任何交付时间 - 60 个有效库存单位(SKU) 采购价格(美元/箱)和年销售数量(箱/年)
- 运输使用第三方公司进行包裹递送,每箱发票($/箱)
- 储存位置(商场储备),容量 480 箱上架储存
容量为 16 盒的细胞—(图片由作者提供)
为了简化理解,让我们介绍一些符号
符号—(作者提供的图片)
每个 SKU 的年度需求
运输
方程式—(图片由作者提供)
b = 42.250 美元
A = -0.3975 美元/箱
资本成本
方程式—(图片由作者提供)
作为一家中型企业,我们认为你的资本成本相当高:12.5%。
仓储费用
方程式—(图片由作者提供)
在这个模型中,我们假设我们有世界上最好的房东。她按纸箱数量给我们开发票,取每年的平均值。我们不会为空的位置付钱。
IMAX = 480
Rmonth = 2000 美元/月
问题
您应该在 ERP 中设置每个补货 Qi 的哪个数量以最小化总成本?
二。构建您的模型
与本系列的前一篇文章不同,我们不会使用 PuLP,因为我们没有遇到线性规划问题。我们将使用 SciPy 优化函数来解决这个非线性最小化问题。
1.声明您的决策变量
你想决定什么?
我们想设置由我们的 ERP 发送给供应商的每个补货订单的数量。
方程式—(图片由作者提供)
然而,为了简化我们的计算,我们将使用每年的补货数量 Ri 作为决策变量。
方程式—(图片由作者提供)
补货数量将使用以下公式计算。
注意:我们接受非整数的补货箱数量。
2.宣布你的目标函数
你想最小化什么?
方程式—(图片由作者提供)
采购成本本身不包括在目标函数中,因为它超出了我们的优化目标范围。
代码
3.定义约束
决定你可行区域的资源限制是什么?
方程式—(图片由作者提供)
这是问题开始的地方,因为我们有一个非线性约束(1/Ri)。
方程式—(图片由作者提供)
4.求解模型并准备结果
你模拟的结果是什么?
初步猜测
与线性规划不同,我们需要为算法的第一次迭代提供一个潜在解的初始向量来启动它。
这里,我们假设所有 SKU 每年 2 次补充是一个很好的选择。
**$63,206.7** total cost for initial guessing
Hopefully the optimal solution will be lower
求解
评论
我找不到任何使用 Scipy 解算器实现整数非线性编程的方法。如果你有一个解决方案,比这种快速和肮脏的舍入更好,使用 python 的另一个库,可以请你在评论部分分享吗?
For 100 Iterations
-> Initial Solution: **$28,991.9**
-> Integer Solution: **$29,221.3** with a maximum inventory of 356 cartons
三。结论和后续步骤
关注我的 medium,了解更多与供应链数据科学相关的见解。
结论
这一优化解决方案比最初猜测的所有参考每年 2 次补货的高出 56%。
需求分布
如果我们有你的需求的随机分布,我们想避免缺货怎么办?在下面的文章中,您可以找到一种简单的方法来建立补货规则,假设您的需求是随机分布的。
https://www.samirsaci.com/inventory-management-for-retail-stochastic-demand-2/
后续步骤
我们可以看到,我们的解决方案主要是由运输成本决定的,因为我们的最大库存为 356 箱。
在下一篇文章中,我们将执行探索性数据分析,以了解我们的决策变量的分布,并了解是什么驱动了每个参考的结果。
我们还将尝试理解从连续决策变量到整数决策变量的转换会产生什么影响。
最后,我们将尝试几个场景,看看模型如何反应:
- 高租赁成本和低运输成本
- 非线性采购成本
- 更高的最小订购量
欢迎随时联系我,我愿意就数据科学和供应链相关话题进行分享和交流。
关于我
让我们连接上 Linkedin 和 Twitter ,我是一名供应链工程师,正在使用数据分析来改善物流运作和降低成本。
如果你对数据分析和供应链感兴趣,可以看看我的网站
参考
[1] SciPy 优化库,官方文档,链接
[2] Samir Saci ,使用 Python 进行线性规划的供应计划,链接
生产用于实验的优质废咖啡渣
咖啡数据科学
主流之外的探索
之前,我已经用用过的咖啡渣做了实验,给出了一个控制变量,以测试咖啡也能过滤的理论。然而,麻烦的是,用过的地面不像新鲜的地面,我不完全明白如何到达真正好的用过的地面。
我将在这里探索我是如何得到一套相当不错的咖啡渣的,这些磨粒将成为一些实验的基础,以更好地了解咖啡。
期望的属性
好的用过的咖啡渣应该能够用在浓缩咖啡篮中,并且与好的咖啡渣有相似的镜头:
- 没有或几乎没有咖啡萃取物了
- 水流均匀
- 与常规注射相比具有相似的流速
用过的咖啡渣有两个挑战需要克服:
- 二氧化碳消失了。
- 咖啡被提取出来了。
初始配方
我最初的食谱是从一个用过的咖啡球开始的:
- 混淆
- 加入篮子
- 运行 3 或 4 个镜头,直到颜色变得清晰
- 拉出冰球
- 放在托盘上
- 将托盘在 200 华氏度的烤箱中干燥 20 分钟
- 随时可以使用
干燥后,你需要两个用过的咖啡壶中的咖啡渣才能得到一杯咖啡。所以 20 克的投入将产生大约 10 克的干废咖啡。
所有图片由作者提供
我使用了 Flair 的 Bplus 金属过滤器来帮助处理淋浴屏幕。我不知道,我做得还不够。
麻烦
问题是,现在我的镜头跑得超级快。在预灌注时,我的注射花了 5 秒钟装满杯子。
让我们再试一次。把咖啡弄干,然后挤一挤!
我想可能是咖啡结块了,但这不是问题所在。
咖啡分销
所以我做了一些测量。我们可以很容易地重新研磨,但如果不知道研磨后的样子,那就有点盲目了。我测量了分布,并将其与利基市场上的典型 S13 进行了比较。
控制转向更精细,但我想可能是咖啡块以奇怪的方式结块。所以我用一个杯子打碎了较大的团块,然后我又拍了一张照片。
我比较了三者的颗粒分布,当粉碎的地面稍微变细时,有一些结块。
这仍然没有达到目的,所以我把它磨得更细。首先是 Lume,但是太慢了。于是我转行做了小众(父亲,请原谅)。事实上,我花了一分钟试图想出一个不涉及利基的解决方案,但唉,我知道它会做得最好。
这有助于稍微改变粒子分布,但没有我希望的那么多。
在拍摄过程中,它跑得更长了一点,这是一个好迹象。
之后,用过的冰球看起来与正常冰球不同。
数据分析
从这些照片中,我看了一些数字,看看这些照片拍摄了多长时间,这些场地里还剩下多少咖啡,尽管它们被认为已经被用完了。我把照片分成两半来帮助理解,它们大概是 1:1 和 2:1 的咖啡投入产出比。所以这些是 20 克进的,前半部分是 20 克出的。这些镜头的问题是,因为它们跑得太快,很难切换杯子,即使下面有刻度。
对于拍摄顺序,这是在拍摄之间使用相同的地面和干燥的顺序:
- 控制镜头
- 输出物干燥并捣碎
- 在壁龛上使用设置 0 (S0)进行更精细的研磨
- 再送他们一次
对于拍摄时间来说,更好地利用利基市场有所帮助。覆盖过滤器(TCF)的时间增加了一点,击中前半部分和后半部分的时间也增加了。
就温度输出而言,它似乎相当稳定。对于后半部分的“控制干燥和捣碎”来说,输出重量是关闭的,但是除此之外,我能够获得相对的一致性。
还剩下一点摘录。这背后的整个想法是了解什么提取物被留下,当使用这些理由进行实验时,我如何估计误差。咖啡萃取物的总量很少,但在重新研磨后迅速增加。这就是再多拉一杆的动机。
这些结果表明,即使地面已经彻底运行,重新研磨和推动水通过他们几次是可取的。我期望从咖啡中完全提取出所有的东西,这需要做更多的工作。
最令人惊讶的结果是,即使地面很好,水还是流得很快。这似乎表明,沟道效应是由圆盘的某些区域被更快地抽出而引起的,直到水以同样快的速度流过它们。这也表明,即使微粒迁移到过滤器的底部,它们也不会阻碍咖啡的流动。我将在另一篇文章中更好地探讨这个问题。
如果你愿意,可以在 Twitter 和 YouTube 上关注我,我会在那里发布不同机器上的浓缩咖啡视频和浓缩咖啡相关的东西。你也可以在 LinkedIn 上找到我。也可以在中和 Patreon 上关注我。
我的进一步阅读:
产品分析师,那是什么?
总结产品分析师的工作,以及这项工作所必需的技能,并提供一些学习资源。
斯蒂芬·道森在 Unsplash 上拍摄的照片
在过去的三年里,我一直是一名产品分析师,最近,很多人问我在日常工作中做些什么。由于在技术领域,他们经常要求记录一切,我想在这里总结一下我的答案,希望能帮助其他人了解产品分析是否可能是他们职业生涯的下一步。
快速免责声明:这篇文章将涵盖我对这个角色的体验,不一定代表我现在或以前的雇主的想法。我的经历可能和其他人不同,尤其是产品分析师这个头衔往往很模糊:在一些公司,同样的职位被称为数据科学家或业务分析师。在一个公司内,可能会发生这样的情况,一些产品分析师的角色更接近于数据工程师,而另一些则更接近于产品经理。此外,我只在科技行业工作过,在其他行业,这个角色可能会有很大不同。
我将从我对产品分析师的定义开始,然后我将谈论一些产品分析师的职责(度量、细分、实验和探索性数据分析),以及这份工作的一些基本技能(沟通、商业知识和技术技能)。我将以为什么我喜欢做产品分析师来结束我的演讲。
这篇文章的目的是给你一个角色的概述,所以我不会给出不同职责和技能的细节,但我为不同的主题添加了一些资源,欢迎在评论中提问。
什么是产品分析师
我最喜欢的定义是,产品分析师是用户的拥护者。他们确保用户享受并从产品中获得价值。他们用数据去理解如何让产品惊艳,如何取悦用户,如何让产品有用,对用户有帮助。数据有助于了解所有用户的需求,不仅仅是那些给你发长篇大论的电子邮件或者更有可能回答 CSAT 调查的用户。
产品分析师通常分析大量的汇总数据,了解用户的痛点,他们的啊哈时刻,他们喜欢什么,产品的哪些部分没有意义,什么让用户留下来,什么让用户离开,以及这些东西如何因国家、类型、年龄等而变化。
产品分析师的职责
产品分析师使用定量数据为产品团队的产品决策提供支持,为后续步骤提供信息。例如,如果需要营销活动,产品分析师可能会建议构建哪些新功能,或者建议保留策略。经常听到“基于数据的决策”或“数据驱动的公司”。我更喜欢“见多识广”这个词,因为我认为知道数据不能提供全貌是很重要的,因此需要某种程度的产品直觉,这就是为什么产品分析师通常需要有很强的商业敏锐性(我将在后面谈到)。
在这一部分,我想解释一下产品分析师的一些职责。我将该部分分为:度量、细分、实验和探索性数据分析。
指标创建和监控
衡量的内容会得到改进。度量有助于理解产品的变化是否达到了预期的结果。这就像当你试图减肥时:如果你从不检查体重,很难决定少吃美味的冰淇淋是否是一个有效的策略,或者你在拒绝自己应得的零食。
类似地,产品分析师经常创建度量标准,或者使用现有的度量标准,并监控它们以评估产品方向是否让用户满意。分析师必须深刻理解指标,因为为什么指标以某种方式变化并不总是显而易见的。例如,你可能不是在减肥,而只是因为你在练肌肉。这就是为什么经常有大量的指标需要查看。
幸运的是,您总是可以从一些度量标准开始。一个很好的框架是谷歌心脏框架。或者,如果你喜欢不那么浪漫、冒险的海盗, AARRR 框架是另一个不错的选择。这些框架有助于衡量用户旅程的重要阶段,不同阶段的不良表现需要不同的行动(例如,低采用率可能需要营销活动,而不良的任务成功指标可能表明该功能难以使用)。
这些都是框架,因此你需要让它们适应你的产品。不同的产品、行业和业务类型需要不同的指标。一个有用的练习是选择一个你真正喜欢的产品(它可以是一个应用程序,也可以是一个邮局服务或一个玩具),并根据两个框架中的一个,考虑你将为该产品创建哪些指标。尝试创建特定于该产品的指标,捕捉用户旅程,并考虑如何解释指标变化。
这些框架使用多种度量标准,但是您可能听说过一个经常被单独提及的度量标准:北极星度量标准。每个公司、每个产品、甚至每个特性都有不同的定义。北极星指标是产品和/或功能优化的主要指标,它必须是可操作的、可理解的、可衡量的、敏感的和不可游戏的。我不会说得太详细,但有一点我认为需要记住的是,这个指标必须足够简单,每个人都能理解,这样更容易传播。如果它太难解释,团队就不会使用它,他们不会谈论它,甚至更糟,他们会曲解它。此外,北极星度量通常需要护栏度量,以确保我们没有遗漏其他重要信息。
通常,所有这些指标都是通过仪表板来监控的。不要相信耸人听闻的文章说仪表板已经死了。他们没有,他们还活着。的确,他们通常不会给出足够的细节(例如,为什么指标在第 X 天急剧增加),但这是分析之旅和产品问题的起点。我不认为你需要任何关于构建仪表板的特定课程,但是知道不同的图表如何改变人们对正在发生的事情的看法是很好的,所以一些数据可视化的基础知识会有所帮助(参见沟通段落)。
片段
产品分析师需要理解他们所有的用户,有时这意味着理解为什么一个产品可能对一些人有用而对另一些人没用。因此,分析师通常在他们的分析中包括不同的切片。一些最常见的是人口统计维度,如性别、年龄、国家,还有非人口统计维度,如参与度、客户年龄以及新用户与现有用户的对比。一般来说,它们用于理解为什么产品对某些人有效,而对其他人无效。
有时,由于算法的不公平性,一群用户比其他人更关注产品,在这种情况下,了解对不同群体的影响甚至更重要。几乎总是有简单的解释为什么某些产品更受特定人群的喜爱:例如,一个儿童产品对儿童比对成人更有效。但重要的是,由于算法中的一些偏差,该产品并没有对某些群体产生更好的效果。
细分是理解用户的一个非常强大的工具。不过,考虑用户的隐私:使用包含足够多用户的群组是很重要的,因为不可能对这些用户的数据进行分解,这样可以保护用户的隐私。
实验
根据你公司的成熟度和数据成熟度,以及你正在开发的产品类型,你可能会进行大量的实验。在技术方面,我们通常使用 AB 测试,但是不同的技术也可以用于其他产品(例如,自然实验、因果推断等)。).在线 AB 测试的一个非常好的课程是免费课程。它真的做得很好,涵盖了大量的主题,并解释了企业如何使用 AB 测试来做决策(这在理论课程上经常被跳过)。如果任何人对自然实验和因果推断文章有建议,请随时在评论中提出!
探索性数据分析(特别请求)
产品分析师经常会收到利益相关者的问题,询问为什么 dashboard X 中的指标 Y 变化如此突然,或者他们的假设是否有数据支持,或者某个项目是否值得投资。这通常会生成需要几个小时(特别请求)到几周才能回答的项目。你通常希望在短期需求和长期项目之间取得平衡,并无情地划分优先级。
这些问题中的大多数需要一些数据探索来回答,通常称为 EDA(探索性数据分析),这是产品分析师工作的一个重要部分。它包括了解存在哪些数据、导入数据、清理数据以及提出大量问题。我认为强烈的好奇心是使 EDA 阶段有效的关键。
有不同的工具可以用于 EDA(后面会有更多的细节),但是从我的经验来看,你无法避免 SQL。如果你需要学习或者需要复习,我推荐 Udacity 免费课程: SQL by Mode 。
数据洞察和建议
从根本上说,度量、分割、实验和 EDA 用于获得数据洞察。我在第一份工作中犯的一个错误是展示我从分析中看到的东西,而没有“那又怎样”的跟进。洞察力可以是“度量 X 改进的”,但是大多数时候你想要解释为什么会发生这种情况,以及可以用这些信息做什么。例如,指标的提高可能是由于您可能考虑继续进行的营销活动,或者是您应该考虑进一步发展的一些外部因素。大多数时候,产品分析师处于独特的地位,可以根据数据给出建议。这并不意味着他们将做出最终决定,但通常鼓励他们就下一步措施提出建议。
然而,并非所有的产品分析工作都是如此。我面试了一些职位,公司只要求运行 SQL 查询和显示图表,所有的解释和建议都由利益相关者完成。
这也是我认为分析师和数据科学家之间存在更多差异的领域之一。产品分析师通常希望提供及时的数据见解,而数据科学家可能会从事更长期、更深入的项目。数据科学家可能会处理更复杂的模型,而产品分析师则更接近业务。因此,如果你真的对机器学习感兴趣,产品分析可能不是正确的选择。虽然说到底,这总是取决于你所在的公司和团队。
米利安·耶西耶在 Unsplash 上拍摄的照片
产品分析师技能
在前一部分,我试图列出产品分析的职责。在第二部分,我将重点放在工作所需的技能上。
沟通
产品分析师(以及分析师和数据科学家)的一项基本技能也是经常被忽视的技能是沟通。也是我最纠结的一个。产品分析师的价值是通过他们提供的影响来衡量的,如果数据结果沟通不畅,很难推动行动和影响产品。产品分析师非常了解数据,并且他们处于独特的位置,通过阐明用户的观点(或者至少是数据所显示的)来影响产品团队。
关于这个话题,我最喜欢的一本书是《T4 用数据讲故事》。它展示了如何创建强大的可视化和演示,如何使用数据来讲述故事和推动行动。数据可视化经常被忽视,但是一个简单易懂的图表通常比一个复杂的显示每一个可能的数据点的图表更有效。选择要展示的数据,并推荐团队应该采取的最有效的行动,这对于成为一名优秀的分析师至关重要。除了视觉化之外,讲述一个引人注目的故事对于获得人们的认同并提出有影响力的建议也很重要。
商业头脑
对业务的良好理解对于产品分析非常重要。首先,并不是所有的事情都可以通过数据推断出来,理解如何解释现有数据和缺失数据(例如,因为它们没有被跟踪,因为产品的用户仍然很少……)是非常重要的。了解业务方向、目标用户和竞争通常会有所帮助。
其次,产品分析师还必须注意产品指标的不良后果。例如,你不想让人们陷入不健康的行为,让他们在你的产品上花费更多不必要的时间。解释数据有时会很困难,人们必须考虑导致分析中显示的数字的所有可能的行为。
第三,企业也需要关注收入,因此有时要求产品分析师也关注收入。收入对产品决策的影响程度取决于公司的成熟度和业务类型。例如,B2B 企业或小公司可能需要关注高付费客户。
对于科技商业新闻,我会遵循 Stratechery ,但你可能想寻找任何与你的行业相关的博客/时事通讯。在我看来,你不需要深入了解所有细节,这在你职业生涯的后期会变得更加相关,但对业务的整体理解会有所帮助。
问正确的问题
对于任何分析师来说,提出正确的问题无疑是最重要的技能。良好的业务和产品知识、对产品的热情以及对用户的好奇心是提出正确问题的重要因素。
激情不是你能学会的,但绝对能提高。找到任何产品中你最喜欢的东西,尽可能多地使用它,思考你将如何改进它(如果你不是 100%喜欢这个产品,那就更好了)。进行伟大分析所需的好奇心会自然而然地产生。关于用户的问题会很容易出现。问题应该是是什么推动了你的分析,而不是可用的数据。
技术
即使拥有最棒的提问技巧,你也需要能够与数据对话:)。一般来说,工具可以根据公司的不同而改变,但是对统计学的良好理解是必须的。为此,我推荐斯普林格的书《统计学习及其在 R 中的应用导论》。它用 R 中的例子总结了很多可以用来建模数据的技术(即使我更喜欢 Python,我也认为这本书真的很有帮助)。
我还发现在 Kaggle 上回顾探索性数据分析(EDA)的例子很有帮助。它们通常以 Python 笔记本的形式出现(R 也有笔记本),分析师经常使用它们。特别是在 Kaggle 中,有很多 EDA 笔记本使用 pandas 和 matplotlib/seaborn 进行可视化的例子。请注意,Python 中的可视化并不是最好的,有时很难配置(有些人复制电子表格上的最终分析,并从那里创建可视化),但知道自己能做什么仍然很有帮助。
不同公司使用的大多数工具都会发生变化,但有一些工具是非常通用的:SQL、Python 和/或 R 和电子表格。我仍然不太了解电子表格,但是 SQL 方面的知识对我有帮助。我说过,学习 SQL 我建议 Udacity 免费课程: SQL by Mode (顺便说一下 Udacity 免费课程还挺多的)。我用那门 SQL 课程为我的第一次产品分析师职位面试做准备(它对许多其他职位也有帮助)。
你也可以加入当地谈论这些语言的聚会或团体(例如 R-Ladies )。请注意,与 Python 相比,R 通常更侧重于数据分析,因此,如果您加入了 Python 小组,请确保您加入的小组侧重于数据分析/数据科学。
通常也有工具来构建仪表板,例如 Tableau 或 Looker。Tableau 中有一个关于数据可视化的 Udacity 免费课程,我在第一份产品分析工作中学习了这个工具。你可能应该参加一个关于你公司使用的工具的课程,但是如果你只是想学习一种工具,Tableau 可以用学生许可证来使用,它对于 EDA 来说非常容易使用,所以你也可以快速测试你的“问正确的问题”技能。
其他资源
我想与你分享的一些其他资源是(I)有关于产品分析的伟大文章的振幅博客(但当然他们试图出售产品,所以有时它感觉像一个广告);(二)重整关于成长的博客真的很有趣,但有点难以导航;(iii)最近推出的由 Eric Weber 撰写的时事通讯(并在 Linkedin 上关注他),它专注于分析和数据产品,我发现它非常有趣和有用。
最后的想法
我喜欢这份工作,因为它让我在工作技能、提高沟通能力以及学习商业知识之间找到了平衡点。看到一个产品如何发展也是非常有趣的,帮助产品变得更好并使它们更好地帮助人们也是非常令人兴奋的。
感谢您花时间阅读这篇文章,希望对您有所帮助。欢迎提问或在评论中给我反馈!
产品分析推动移动游戏及其他领域的免费增值转化
照片由 Unsplash 上的 Onur Binay 拍摄
细分和查询客户数据将照亮增加货币化的道路
一个全球性的疫情颠覆了娱乐圈,削弱了好莱坞,而像网飞这样的平台却吸引了数百万新订户。然而,社交距离遥远的娱乐世界的最大受益者可能是视频游戏行业。
根据【2021 年 1 月的市场观察报告,整个游戏世界的收入预计将在去年激增 20%以上,这将使近 1800 亿美元的产业成为比北美体育和全球电影业务总和更大的赚钱机器。值得注意的是,移动游戏在 2020 年的增长速度超过了 PC 和主机市场,即使是在微软和索尼平台发布以及对任天堂 Switch 需求惊人的一年。
移动游戏随处可见。它在你的苹果和安卓手机上,在你的平板电脑和手表上,移动应用市场的成熟为许多不同的货币化途径铺平了道路。
虽然许多开发者寻求多样化的收入来源,但免费增值模式——免费使用应用内购买——已经获得了动力。然而问题依然存在:为什么游戏玩家要为他们可以免费玩的游戏元素付费?
答案在于用户数据。开发人员有数百万美元的风险,他们收集的数据必须准确,以更好地了解用户将为哪些高级功能付费。产品团队必须对用户行为进行迭代和有价值的分析,因为用户数据是一个更成功的应用的地图。
几乎没有不利用你的机会的余地。在游戏之外,运营免费增值模式的 B2B 和 B2C 软件公司可以看到不到 5% 的用户从免费层转移到高级全价产品。一份最近发布的报告显示,在不断增长的手机游戏市场,这个数字甚至更低。只有大约 2%的手机游戏用户会花钱购买游戏内商品。
这在整个用户群中只占很小的比例,但这些玩家对游戏本身的财务成功至关重要。开发者有数百万美元的资金,他们收集的数据用来回答诸如“我们的免费增值游戏中的功能是否鼓励用户参与?”必须精确。产品团队必须对用户行为进行迭代和有价值的分析,因为用户数据是一个更成功的应用的地图。
产品分析:数据中有
用户行为数据有助于开发人员、产品经理和营销人员收集有价值的见解。一个典型的用户获取漏斗类似于一个倒置的三角形,但在免费增值模式中,获得一个新玩家远不如该玩家在应用中的行为重要。
想象两个漏斗连接在一个点上,像沙漏一样向相反的方向张开。顶部的漏斗跟踪所有最终促使用户下载应用程序并第一次启动它的行为。这包括游戏的营销和广告。
在数十亿潜在玩家中,只有一小部分会被广告激起兴趣,或者跟随朋友发送的链接,引导他们开始在你的游戏中的旅程。然而,当新玩家加入时,这种转变并没有停止。一旦他们打开应用程序,他们就进入了第二个漏斗——跟踪参与度、订阅和微交易的漏斗。
从第一次下载到多次购买,所有用户参与的记录都应该收集在云数据仓库或数据湖中。访问这些信息对于每个部门的决策过程至关重要,查询数据是获得有关用户体验质量(UX)或带来最佳客户的营销渠道问题的可靠答案的最佳方式。将产品分析平台连接到您的数据仓库可确保用户数据的真正民主化,甚至允许非技术员工跟踪新功能或促销的成功。手机游戏的生命周期永远不会结束,每次更新用户的反应都会通过数据反映出来。
把免费用户变成付费用户
一旦公司收集了数据并将其提供给组织,就可以开始进行严格的分析。虽然只有一小部分免费增值用户会付费玩这个游戏,但还是有一些重要的细微差别需要考虑。在转换漏斗中,产品负责人和开发人员有责任评估哪些可行,哪些不可行。
如果一款游戏平均每月有 20 万用户,其中只有 2%的用户会完成微交易或订阅无广告版本的游戏,那么付费观众只有 4000 人。但是那 4000 个玩家是谁呢?是什么渠道带他们下载游戏的?除了喜欢打开钱包之外,他们还有什么共同之处?
任何扩大免费增值受众付费基础的举措都应该从发现成功之处开始,并努力复制它们。从免费用户到付费用户的整体转化率可能在 2%到 3%之间徘徊,但在更广泛的用户群中,有一些细分市场的转化率要高得多。那些购买了人物角色的人可能会转化 5%或 10%,使他们成为顾客的价值增加两到五倍。
了解如何分割用户数据至关重要,因为每个用户都包含许多数据身份。给定用户通过点击不同移动游戏上的广告来下载游戏。他们也有一个 iOS 设备,更喜欢以 10 分钟为增量玩游戏,主要在晚上玩。产品负责人和开发人员需要理解用户数据中的哪些元素是相关的,以及这些共性如何影响未来的营销和开发策略。
更新和迭代
专注于付费玩家自然会导致产品更新,定制游戏以鼓励最关键玩家的参与。当更新游戏的新功能或简单地修复损害整体 UX 的错误时,请再次检查数据。产品和客户旅程的变化会影响转化率和保留率。人们是如何使用新功能的——他们到底使用了哪些功能?你可能会发现一个没有被使用的游戏模式或装饰性附件。
免费增值模式中的一个主要考虑因素是免费层与完整产品相比的质量和功能。与 Dropbox 或谷歌云(Google Cloud)等 B2B 软件相比,在微交易驱动大部分优质层的手机游戏中,这两种体验之间的差异可能较小,但无论是哪种情况,开发人员都必须有意构建产品。游戏必须有趣——否则,我们不会玩它们——但要监控高利用率的免费功能,以确保它们不会阻碍玩家转向付费产品。
然而,如果你不能吸引大量玩家下载你的游戏,那么转化现有用户群的努力将会受到限制。游戏开发商受分析启发的增长战略是双管齐下的:建立一个庞大的玩家群体,这样即使是很小比例的付费客户也是一个重要的人群,并使用数据来确定如何最好地为像你的最佳客户这样的社区定制产品和营销工作。
典型的基于产品的分析周期是基于形成假设和实施改变。在这个旅程中,你可以看到无限多的方面,但是你的资源是有限的。你的假设越合理,你的测试就越有用。
产品分析,作为一个过程,从来都不是完整的。测试、测量、迭代和重复。为了成为一个更好的产品和开发团队,你需要能够问上百个关于顾客旅途中发生了什么的问题,并快速得到答案。你的下一个大发现可能来自于只发生在一小部分用户身上的事情。
免费增值模式在游戏中行得通,因为那部分玩家,真正的粉丝,深深地支持他们所关心的产品。该行业的增长表明了这一点。
产品分析:参与模式
创建一个全面的工具来解释产品中的用户行为,并考虑该工具如何帮助发现洞察力
先决条件
随着产品的发展,用户的某种行为是否与他们退货或购买任何东西的机会有关,这是一个额外的问题。来自不同层次的众多利益相关者的问题包括:如果我们将访问过一个产品十个以上页面的用户与访问过不到十个页面的用户进行比较,ARPU 会更高吗?用户在第一周下了两个以上的订单后,返回某个产品的可能性更大吗?使用特定功能超过五次的消费者寿命更长吗?或者,更好的是,让我们把重点放在影响主要指标的功能上…完成了这一级别的任务和分析的产品分析师将能够立即预测这类问题的答案…
所有上述问题,以及其他类似类型的问题,都可以有把握地回答。用户采取的每一个动作都增加了前进到下一个步骤(转换到目标动作、返回到服务等)的可能性。).这正是参与度的含义:用户在其当前发展水平下对产品采取行动的意愿,如选择 UI 中提供的商品/服务/某些内容,通过主要产品漏斗,并在完成目标行动后继续参与产品。因此,可以通过开发参与度模型来实现用于与其他用户相比评估用户的统一方法。
型号
参与模型分析每个用户关于产品的活动,表明用户参与当前产品的愿望。
表 1。此类分析如何产生的示例
这里 id 为 13 的用户参与度最高,id 为 10 的用户参与度最低。
作为这种分析的结果,我们可以将用户分类为那些知道并理解如何使用产品的人,那些有兴趣了解产品当前状态的人,以及那些访问了产品并在执行简单操作后没有发现任何实质性内容就很快离开的人。
最后,正如本文第二部分所展示的,通过参与度的细分,可以将 CR、CTR、留存率、粘性等产品指标和 ARPU、ARPPU、LTV 等业务指标联系起来。
总体而言,敬业度模型在以下方面有所帮助:
1.设计产品开发战略;
2.分析度量在产品和业务度量中的适应性。例如,如果跳出率降低 5 %,产品中的 LTV 会改变多少;
3.深入了解是什么让一个产品成为习惯,或者什么行为表明用户对该产品的参与度正在下降。让你找到顿悟的时刻;
4.跟踪用户对产品兴趣的起伏,以及随之而来的行动。
在接下来的部分中,我们将讨论如何使用约定模型来处理这些任务,在这一部分中,我们将讨论可以根据您的产品的特性、数据的存在或缺乏、实现资源来开发什么样的约定模型,并考虑如何创建它。
参数
该模型的逻辑是基于用户在产品漏斗深处采取行动的可能性(从一个产品点转换到另一个产品点):当打开页面时,用户必须在特定设备上启动应用程序,某些块将被显示。当块被显示时,用户可以看到各种各样的报价(在线商店中的产品,在服务的情况下是专门的内容),并且很可能用户将打开特定产品的页面,并且最终可能购买某些东西。
可以使用参数来指示与产品的动作、发现和页面上的参与之间的联系。该模型的参数对于一个产品来说是相当通用的,并不严重依赖于它的特性。构建模型时需要考虑的基本参数示例:
设备/平台 —访问两个或更多平台(通常通过两个设备完成)肯定意味着用户愿意使用产品,例如,使用不同的界面。
会话 —一段时间内(一天或一周)的访问。例如,在早上和晚上输入产品反映了用户将尝试经历选择产品/内容、支付漏斗和其他具有不同动机的相关动作的阶段。
App 启动 —打开/关闭应用,让用户再次看到相同的页面,相同的区块但反应可能不同。
页面打开 —以上所有操作都会以某种方式影响打开页面的数量。
区块视图 —与网站互动(垂直滚动,或水平滚动单个区块)。
产品预览计数 —用户已经看到的关于产品/内容的元素(预览、海报)的数量。用户在屏幕上看到的商品越多,他就越有可能被激起兴趣,这将影响转化率和漏斗流量。
点击 —点击块和元素中的不同组件,既可以转移到另一个页面,也可以滚动浏览元素或打开弹出窗口或打开任何工具提示(或其他信息元素)
弹出窗口打开 —期间打开的弹出窗口的计数
等。指示与产品或界面的接触的其他用户动作可能在特定产品中被突出显示。
一个需要数据的例子,你很可能已经有了(类似的东西):
表二。面向个人用户的事件管道/事件流
表 3。事件管道的聚合数据
所有基本功能的聚合将充分反映用户与产品的交互。可能需要这种类型的数据结构来使用下述方法之一创建模型。
各个特征之间的高度相关性支持了模型中和本节开始时提出的基本原理。考虑描述几个基本行为之间的概率联系的相关性。
表 4。产品中基本行为的成对比较在相关矩阵中进行对比。
也就是说,相关性的分布表明所有的用户行为都是紧密相关的,反映了通过产品中漏斗步骤的概率。
车型变化
根据用于构建模型的选项,可以采用各种方法,这些方法在分析的准确性方面有所不同。在最基本的版本中,在产品中使用一种类型的动作:例如,打开页面。所有的基本动作可以应用在更复杂的模型中,关于该模型的信息存储了所获得的数据。
让我们考虑建立模型的可能方法:
一个精确动作的计数器——单个特征的选择,比如打开一个页面(page open)为主的动作。
适用于数据多样性有限的小型项目。这是一种从许多来源获得最多关注的技术,但它的问题是它没有考虑到每个单独页面上的交互,以及用户对屏幕上显示的附带项目的反应。
所有事件计数器 —对于每个用户,事件的周期计数器,结果数字作为模型。如果您不想进行任何复杂的计算,例如快速的初步评估,或者如果对报告/仪表板的数据输出有各种限制(主要是计算性的),可以使用这种方法。
缺点包括所有产品的动作具有相同的权重(点击等于打开一个页面),以及基本活动之间缺乏任何联系。
特征中值表示。在这种创建参与模型的技术中,使用了与服务上的所有动作相关的个体用户活动的标准化。用户参与度将反映在最终价值中。以下是粗略的计算公式:
也就是说,我们通过计算用户执行的动作的数量来计算每个模型特征的算术平均值。此外,还可以使用特征的其他归一化缩放方法(如最小最大归一化)。
该方法的优点是相对容易使用分析仪器(通常是对数据库的查询)进行计算,并获得每个用户活动的整体分析。
缺点是仍然忽略了模型中包含的参数之间的概率联系。
模型的输入数据是通过类比表 3 创建的。
降维方法。当您希望强调模型中包含的所有指标(特征)的最一般比率时,您可以使用更复杂的计算作为模型。在这种情况下,可以应用一种降维方法来优化计算。
例如,主成分分析(PCA)可用于创建用户执行的最具可比性的活动的数学模型。
PCA 的主要目标是通过将相关性(或者更确切地说,先前归一化值的协方差)与参数的特征向量相交,从 k 个参数中获得 n 个分量。
由于所有的基本参数之前都被认为是概率相关的(从一个点到另一个点的转换变化不大——今天 100 人进入,30 人点击,明天 110 人进入,32 人点击,等等),所以所有的参数都将具有高水平的相关性,允许我们通过一个分量来计算 PCA。
因此,模型的计算如下:
PCA 有许多计算限制,其中之一是所有参数都必须缩放,这意味着在计算 PCA 之前必须对数据进行标准化。
这种方法创建的模型的主要优点是,它包括了服务上的主要活动之间的交互,这使它有别于以前的方法。
这可能是创建敬业度模型的最佳技术,因为它允许您根据模型更准确地比较具有相似敬业度值的用户。类似的方法,如独立成分分析或矩阵分解,也可以用于计算模型,但最终结果将非常相似。
模型的输入数据也类似于表 3 中的数据结构。
基于事件频率的建模。用户对特定事件在流中出现频率的技术与上一点相反。使用这种方法不需要通过基本参数进行聚合;取而代之的是,获取事件流并对其应用众所周知的 Tf-Idf 文本处理。作为利用这种技术的结果,最不常见的用户活动将被给予更大的权重;例如,5 个已浏览页面的权重将低于 1 个添加意愿列表的权重。
这种技术的好处是,它允许我们从最重要的方面来衡量参与度,无论是特定的搜索功能,特定产品的评级,还是对特定部分的访问。
这种技术需要使用来自事件管道的数据来构建参与模型。(见表 2)
使用上面的任何一种方法,计算的可视化结果应该是用户的分布(如图 1 所示),根据方法的不同,从执行较少活动的反弹用户到熟练使用产品并具有独特功能的超级参与用户都有不同程度的准确性。
极高(> 0.99)和极低(< 0.01) quantiles can also be excluded, depending on the quality of the input data.
Chart 1: Value distribution according to the engagement model
The preliminary phase can be finished here. At the moment, we have considered the data used for modeling, ways of building the model. The most interesting and important information about using the model will be discussed further in the second part of the article.
https://medium . com/@ sqweptic/product-analytics-engagement-model-part-2-1733 b460 e68
产品分析:参与模型(第 2 部分)
使用参与模型了解产品开发的当前状态并获得产品洞察力。
这是关于参与模型的文章的后续,其中我们将讨论构建的模型如何有助于产品知识、产品开发目标、洞察力的搜索和啊哈时刻的选择。为了便于理解,所有的挑战都将使用抽象市场的例子来完成;绝大多数读者都会熟悉这种服务,所以它会简单明了。
第一部分:https://medium . com/@ sqweptic/product-analytics-engagement-model-22d 53 c 96d 169
这个模式为什么有效?
首先,确保创建的模型能够正常运行。以前假设基本参数有密切的联系(除其他外,表现为高度相关),表明所有参数都有可比较的模式。因此,通过根据用户模型强调细分(例如,通过将所有用户分成十分之一),可以追踪每个细分中所有参数的动态,从而允许测试和验证模型。
图表 1。用于建立模型的基本参数的分布
因为基本参数是模型的一个直接元素,所以预计所有参数都会同步上升,但所有指标也会从十分位数上升到十分位数。
图表 2。按模型的十分位数划分的主要产品指标分布
所有其他指标,如 ARPU、保留率和生存期(LT)也在增加。因此,该模型准确地反映了用户在项目中的参与度,并且因为这直接反映在群组中。
产品的目标应该是什么?
在将参与模型付诸实践之前,考虑用户在模型中的分布如何揭示产品的当前状态是很重要的。
首先考虑以下分布变化:
图表 3。展示了不同的敬业度模型分布
a) 理想的产品假设大多数用户愿意学习和使用产品的所有功能;他们成功地通过了漏斗,当问题出现时没有负面影响;并且用户在完成预期活动后感到满意。所有的指标都非常高:跳出率(对于付费客户)接近 0%,保留率接近 80%-100%,粘性系数接近 1。折扣的影响微乎其微,而新开发的功能对所有细分市场的影响都是一样的,因为用户已经形成了使用该产品的习惯,这使得它几乎成为一个必不可少的产品。
由于产品范围或内容的有限和持续波动、定价和营销策略的定期修订、外部条件的变化、风险等等,这种程度的参与几乎是不可行的。
或许这种参与度的一个例子是谷歌搜索,它是大多数互联网用户的必备产品,用户与产品的互动在一段时间内都是如此。
b) a 饱和产品 —大多数用户认为产品不错,有相当数量的用户是忠诚的,产品的结构和可用性对大多数用户来说是显而易见的。
指标:目标行动的转化率非常高,跳出率低,用户群表现良好,尽管在最初阶段略有下降,但稳定期正在形成。
产品分类和内容之间的联系以及提供的便利性是维持和吸引用户的基础(用户理解并使用推荐,搜索总是导致各种选择,即使没有可用的产品,目录也不会限制用户,并且总是为当前请求和未来提供产品替代)。营销运营有效地吸引了新用户(回头客不会回应,因为他们熟悉产品),其中大部分人参与了产品。大幅价格优惠对新用户来说更成功,对老用户来说就没那么有效了。
c) 一个 优化的产品——产品拥有稳定且显著的用户群,这种用户群表现在特定的有限细分市场(用户被某些功能或优惠所留住),消费者愿意与其他页面进行互动。
度量标准包括期望行为的平均转换率、平均跳出率和愿意返回产品的人数。
在这一点上,相当比例的消费者已经准备好与产品进行互动,这种效果开始在 a/b 实验中更加明显地显现出来。
d)a****conversation product—只有极小比例的用户养成了使用产品的习惯,大多数用户对产品的功能不感兴趣(或者不知道如何利用这一功能),忠实受众不稳定,易受外部影响(竞争对手的活动、负面评价)。
衡量标准:低保留率、期望行动的平均转化率和高跳出率。产品不能有效留住消费者;营销努力和价格优惠是获得受众的最重要因素。
因为只有极小比例的访问者访问产品的子页面,在基本流程之外使用 a / b 测试的假设测试的影响是最小的,a / b 测试很少使用。例如,对于 100,000 个用户的 DAU,从 1000 个人访问的子部分获得 2%的转化率,在管理和开发支出方面很难补偿。因此,通过提高产品参与度,增加章节/页面之间的切换,以及产品参与度,您可以通过实验(a/b 测试)扩展评估产品假设的选项。
在产品开发过程中确定优先级
让我们更深入地了解一下互动模式如何使您能够确定产品中应该改变的内容。为此,让我们回到与敬业度模型十分位数相关的目标指标分布。例如,这是 LTV。
图表 4。通过模型的分布选择估计的 LTV。
假设我们需要提高产品 10%的生命周期价值;根据敬业度模型的分布,我们寻找价值相似的分位数;这样的 LTV 水平有 67-70 个分位数。
通过将这些分位数划分为一个精确的细分市场,您可以表征该细分市场的指标,也就是说,分析它们如何参与产品,并将其与现有平均值进行比较。考虑下面这个抽象市场的度量表。
表 1。产品中用户行为模式的差异
比平均水平提升 10%的用户更有可能与搜索和推荐互动。例如,将重点放在与用户向搜索的过渡相关的功能/页面以及作为产品假设的推荐解释上是有意义的。在在线商店的情况下,这可以是“针对特定产品的相似搜索查询”块的功能,其将是推荐,并且也将转移到搜索或其他推荐算法中,例如在为产品/用户构建推荐向量时获取关于用户搜索查询的数据。
因此,采用参与度模型使得确定应该优先考虑哪条产品开发路径以实现指标的预期值变得简单。
在这种情况下,产品负责人、经理和分析师建立指标变化因果关系的能力至关重要(如上表所示)。
通过这样的分析,应该记住,如果我们比较非常不同的细分市场,例如,搅动和返回的细分市场,我们将比较完全不同的细分市场,这将不会给出产品中值得改进的确切点。
指标的弹性。财务和产品指标及其关系
你知道如果跳出率降低 5%,LTV 会发生多大的变化吗?
如果用户不在跳出率范围内,他们很可能会通过支付漏斗(CTR),如果他们通过支付漏斗,则很可能会变成目标行动(CR)。因此,用户将拥有以下二项式参数:bounce — 0,ctr — 1,cr — 1。用户之所以有这些标准,是因为他们对产品感到满意,所以被驱使并渴望使用该产品。使用参与度模型,基于用户参与产品的倾向来识别可比用户是可行的;这种相似用户的细分将在每个动作中具有特定的概率,并且在度量之间具有一定的联系。
通过选择大量这样的细分市场,您可以获得整个产品的指标关系,这非常容易——每个 n 分位数中的客户已经被归类为参与度方面的可比用户。
让我们以两个指标为例:ARPU 和购买转化率(CR)。使用这些指标,我们将通过以下结构收集敬业度模型的日期和分位数的统计数据:
表二。模型分位数度量关系的数据
每天/每周改变度量是有意义的,因为度量会随着时间而波动(季节性—冬季/夏季、营销活动和其他原因)。同样重要的是,要仔细检查分位数部分是否具有适当的代表性,并且该部分中的指标变化相对较小。
收到这些信息后,您可以创建一个图表来描述指标之间的联系。
图表 5。度量关系的可视化。雇佣的分位数
该图表描述了一段时间内(几个月来收集的)指标之间的关系;因此,您可以使用线性回归来近似这种关系,并获得指标每次变化的估计值:
收到最终 ARPU 后,您可以计算指标的弹性:
因此,可以评估产品中所有指标的弹性以及它们与财务和业务指标的关系,从而允许将这种工具用于产品假设的财务评估,并突出产品假设执行中的优先级。线性回归不仅可以基于两个度量的关系来创建,还可以基于许多度量的关系来创建,例如 CR/CTR 和 ARPU。产品和商业指标之间相关性的利用也可以包括在单位经济计算中。
当然,因为它仅仅基于系统内部过程的相互连接,所以这种工具与模拟建模更密切相关,容易受到偏差的影响,并且不能完全反映因果关系。
跟踪产品的吸引力并寻求洞察力
使用参与模型,除了能够识别与产品交互的特定用户之外,还可以跟踪用户与产品的交互如何随时间演变。与任何群体一样,随着时间的推移,一些消费者开始不太积极地使用该产品,而另一些消费者开始更积极地使用它。
通过将用户划分为三个参与度段,相对模型的分布为:高(参与度最高的用户)、中和低(参与度最低的用户)。
图表 6。领料基础接合段
随着时间的推移,参与度最高的用户更有可能留在产品中。那些参与度较低的人更有可能陷入混乱。
图表 7。一段时间内用户参与度的群组。
结果,存在用户状态的两种动态:一种是用户与以前的访问相比开始采取较少的活动,与其他用户相比降低了他的参与度(红色箭头),另一种是用户增加了他的活动(绿色箭头)。
与昨天相比,对今天增加了产品参与度的用户的分析,使我们能够确定哪些因素,例如与产品的哪个功能或部分的参与度,影响度量的增长,从而导致使用产品的习惯的形成。
因为在这种分析中,有可能形成开始更多地与产品互动的用户的准确细分,以研究该细分在产品中影响产品回报和参与度增加的活动和行为。
另一方面,继续与产品交互的用户,尽管不太活跃(红色箭头),也容易受到通过 ui 的产品内保留方法的影响,而不是主要集中在搅动用户的邮件/推送通知/警报。
其他用途
敬业度模型是一种可在多种情况下使用的工具。
该模型可以展示产品假设在不同参与细分市场中对产品的影响。例如,影响值可以在高度参与的用户的子集上进行评估,这些用户更专注于界面并且更好地准备参与界面。或者,评估对产品不感兴趣的消费者对新功能的反应。
该模型还允许您比较执行和未执行相同操作的相似用户。例如,如果您通过与产品的接触来获取相同的细分市场,并将那些加入意愿列表的人与那些没有加入的人进行比较,您可以更精确地说出该功能对一个或多个指标的影响。这种机会的获得是因为与产品的交互是与特征相似的用户进行比较的(比较的是更具代表性的群体),因此降低了偏差。
例如,高参与度用户的动态指标可以成为 NPS 分析的定量对应物,允许更快速地跟踪和检查最投入的用户的行为。
为了理解用户在产品中的行为、使用模式、特征和交互方式,有必要进行各种各样的研究,然而,有一种研究可以使用本文描述的工具来完成。
当然,可以提供更多关于参与模式的信息。在文章中,我想简化许多要点,并讲述更多关于通过构建模型可以获得的结果,因此,也许文章在某些部分变得令人困惑。
产品案例访谈数据科学家的注意事项
办公时间
如何在产品案例面试中给人留下深刻印象
照片由 LinkedIn 销售解决方案在 Unsplash 上拍摄
目录
- 你不知道
- 你的想法太多了
- 你盲目地遵循一个框架
- 你无法为自己辩护
在这篇博客中,我们将会看到科技公司的数据科学家,尤其是产品数据科学家的一次采访:产品案例采访。如果你喜欢看而不是读,你也可以在我的频道查看我的视频。
产品案例面试是技术和非技术技能的混合,所以要成功通过这些面试,你需要在这两个方面都达到最佳状态。在这篇博客中,我们将探索一些提示和危险信号来帮助你做到这一点。这些建议来自我自己作为面试候选人的时候,以及我面试候选人和做模拟面试的经历。
在这里,我们将重点关注产品案例访谈中应该做和不应该做的事情。如果你想更详细地了解商业案例面试,可以看看我和 Rob Wang 写的关于这个话题的两部分博客在这里和在这里。
当你读完的时候,你就会知道下次你在产品案例面试中应该做什么和应该避免什么。
技巧
先说产品案例面试的小技巧!这五个建议会帮助你在面试中给人留下最好的印象。
犯错是正常的
我们从一个有点不寻常的提示开始。虽然感觉像是世界末日,但是犯错误也没什么。如果你提出一个你认为有意义的概念,然后发现它不是,最好的方法是承认它。有时你的第一个解决方案会被证明是有缺陷的,承认这一点比继续追求一个糟糕的答案要好得多。
例如,您收到一个关于使用哪个指标来衡量一个新特性的成功的问题,经过再三考虑后,您意识到您刚才提到的指标没有太大意义。你可以这样说“我现在意识到这有点武断。它没有反映我们真正想要衡量的东西,我认为更好的衡量标准是其他东西”。在面试中承认错误可能会感觉尴尬,但你不想浪费时间谈论你不完全相信的事情。
现在,如果你不断地犯错误,并且经常不得不纠正自己,这会给人留下你无法捍卫自己观点的印象。然而,在面试中犯一两个错误并不是世界末日,也不是你作为候选人的机会。
质量比数量更重要
这里的要点是,没有必要提供太多的想法。质量比数量更有价值。例如,当您被要求为 a/b 测试提供指标时,提供 5 个指标不如提供 3 个可靠的指标。当我说“可靠”时,我的意思是您对指标有一个清晰的定义。
例如,当你说客户保持力时,你需要定义什么是活跃用户,你在什么时间段测量保持力,以及为什么,而不是简单地说我将使用客户保持力来测量成功。它表现出缺乏理解和严谨。通常,为一个问题提供 3 个想法就足够了。与其试图添加更多的想法,不如集中精力对每一个想法进行清晰的描述。
与面试官互动
你永远都不想失去你的观众。记住面试官是你在有限的时间内唯一想说服的人,所以要确保面试官完全理解你。采访不是报告。你想吸引面试官。他们可能不同意你的每一个想法,但是你想展示你解决问题的能力和强大的沟通能力。
但是,如何实现这一点呢?你也可以先简单分享一些想法,然后问你的面试官在哪里分享更多细节。
例如,如果一个问题是一个宽泛的问题,比如你如何设计一个实验,你可以说:“我能想到选择正确的度量,获得最小的可检测效应,选择随机化单元,计算样本量等,你有什么想让我具体谈谈的吗?”,然后让面试官选择潜入哪个部分。这是一种有效的方法,因为面试时时间有限。
此外,在你花一两分钟解释你的想法后,你可以问类似“这对你来说有意义吗?”这样的问题来和面试官确认一下。你想让我解释更多吗?”。与面试官互动并问这类问题有助于提供中肯的答案。
总是澄清问题
照片由克里斯蒂娜@ wocintechchat.com在 Unsplash 上拍摄
这条建议非常重要,我要再次强调:永远澄清问题。在回答问题之前,你需要确保自己完全理解了这个问题,这样你和面试官才能达成共识。
事实上,这不仅在面试中很重要,在现实中也很重要。当你从其他人或其他团队那里得到一个问题时,你不只是对它做出反应,你想在提供建议之前了解背景和问题的来源。然而,这是转向一个不同的话题。让我们用一个例子来关注一下技巧。
点击率是一种常用的衡量标准,用于衡量广告的表现。通常情况下,它是通过总点击次数除以总展示次数来计算的。然而,在现实中,有时它可能是指点击用户在所有浏览页面的用户中所占的百分比。有必要弄清楚面试官想用哪个定义。
你可以简单地说,“只是为了确认,当我们说点击率时,我们将其定义为点击次数与印象次数之比,而不是点击的用户占查看页面的所有用户的百分比”。
澄清不仅仅是度量的定义。你可以问的其他几个澄清性问题是:某个功能或产品的目标是什么,某个特定功能是如何工作的,我们有哪些数据来支持某项发现,等等。你从面试官那里得到的信息很重要。
有时候,面试官可能不会直接回答你的问题。相反,他们可能会问“你怎么看?”。那样的话,你可以说说你的理解和假设,问问有没有道理。例如,你可以说,“我对这个新功能的目标的理解是双重的,一个是增加平台上的参与度,另一个是推动长期收入。我的理解对吗?”。
请注意,这并不意味着你想问显而易见的问题,你可以很容易地在网上找到答案的问题。例如,如果你采访脸书,不要问什么是脸书新闻或什么是脸书集团。如果你在 Quora 上面试,不要问你是否能投票赞成或反对一个答案。这表明你还没有对公司或其产品做足够的研究。但是确认你对特定功能的理解是完全没问题的,例如,一些脸书群组允许任何用户加入,但是一些群组只接受邀请。
花点时间组织你的答案
最后,在你开始说话之前花些时间计划你的答案是有帮助的。在回答之前,让面试官花几分钟时间写下一些东西,组织好你的交流。通常情况下,少于 3 分钟就可以了。一旦你对将要谈论的内容有了大致的想法,你可以在深入细节之前提供一个结构和概要。这非常有助于让面试官保持在正确的轨道上。
如果三分钟后你想不出任何话题,你可以花更多的时间去思考。与其说是随便说说的想法或者自己都不相信的想法,不如说是沉默。
你可能会想,如果我花了 5 分钟仍然没有任何想法怎么办?如果发生这种情况,这是一个很好的迹象,表明你需要在面试前进行更多的培训和练习。如果你花时间学习和准备,你会在面试中有一些想法。提前做好准备,这样你就可以避免这种情况。
红旗
现在我们已经介绍了一些技巧,我想和你分享一些产品案例面试中的危险信号,这样你就知道在这种面试中应该避免什么了。
你不知道
最大的危险信号是你根本不知道如何解决问题。甚至在你花了几分钟思考之后,你仍然说“我不知道”,或者你一直说“我能得到提示吗?“针对多个问题。
这几乎可以保证你会得到面试官的拒绝。澄清一下,这和你得到问题的那一刻没有任何想法的情况是不一样的。这很正常,你可以从问一些澄清性的问题开始,利用面试官给你的信息提出一些想法。
我知道有些人在面试时变得非常紧张,以至于无法进行逻辑思考。如果你是这种人,我有两个建议给你。一是记住面试是一种对话。试着忘记面试官决定你是否得到一份工作的事实。你可以告诉自己,这是和同事或者朋友的对话,和朋友说话没什么好怕的。
另一个提示很简单,你需要更多的练习。在正式面试之前,试着和不同的人进行多次模拟面试。我知道有些非常擅长产品案例面试官的人,在实际面试之前已经做过几十次模拟面试了。一旦你可以克服恐惧和紧张,你就可以轻松地谈论你的想法,甚至享受解释你的想法。
你的想法太多了
另一个危险信号实际上与第一个相反:想法太多。这也是一面巨大的红旗,但为什么呢?一个原因是,当你分享许多想法时,你的想法可能听起来很随意,面试官可能会感到不知所措。
另一个原因是,如果你分享了太多的想法,会导致每个想法都缺乏深度。在产品案例面试中,目标是展示你对一家公司产品的深刻理解和你解决问题的技巧,所以深度比广度更重要。如果你有很多想法,你可以这样说:“这是一个非常好的问题,我可以想到 4 个想法:a、b、c 和 d。让我知道你希望我深入到哪一个”。
你盲目地遵循一个框架
你永远不要让人觉得你在使用一个框架,并且盲目地遵循你的框架。你甚至可能从我这里学到了一些很有帮助的框架,但是你永远不要让人觉得你在使用一个框架而没有考虑问题的上下文。如果你这样做,许多面试官会试图破坏你,看看你能表现如何。
遵循框架也是一个问题,因为框架是通用的,而问题是特定的。思考解决特定问题的方案比遵循一个框架更重要。这又回到了我们已经讨论过的技巧:问一些澄清性的问题,并与面试官互动。你从面试官那里获得的背景和信息比遵循框架重要得多。
你不能为自己辩护
最后,不能为自己辩护也是一个危险信号。这是指无法回答后续问题,对自己的想法感到不确定,或者频繁改变论点。
例如,当面试官问你为什么选择米制 A 而不是米制 B 时,你可以说这样的话,“哦,我认为米制 B 更好”,然而当面试官问你为什么时,你又变回米制 A,或者没有一个好的理由。
再比如面试官问你为什么认为网络效应使得治疗效果被低估。你不知道怎么分析,或者你变得紧张,说“也许是高估了”。这给人的印象是你没有仔细考虑你的答案。如果你不能为自己辩护,你就无法让面试官相信你有很强的解决问题的能力。
现在,您已经了解了产品案例访谈中的四大危险信号。如果你在模拟面试中发现自己犯了这些错误,不要紧张。毕竟那是修行的目标。练习帮助你发现自己哪里做得好,哪里可以改进。好的一面是,你在真正的面试之前发现了危险信号。你做的练习越多,你就越能意识到自己的错误,你就有更多的机会去改正它们。
摘要
我们在这个博客中已经讨论了很多,所以让我们快速回顾一下产品案例访谈的提示和危险信号。我对产品案例面试的五大建议是:
- 犯错没关系。
- 质量胜于数量。
- 与面试官互动。
- 总是问清楚问题。
- 花点时间组织你的答案。
有了这些建议,你可以确保给出高质量的答案,让你和面试官保持一致。
现在我们来看看需要避免的事情,这四个危险信号是:
- 你不知道。
- 你的想法太多了。
- 你盲目地遵循一个框架。
- 你无法为自己辩护。
学习使用这些技巧和避免这些危险信号的最好方法是练习。模拟面试是一种很好的方式来了解你的优势和劣势,这样你就可以在真正有价值之前提高自己。
感谢阅读!
如果你喜欢这个帖子,想支持我…
- 订阅我的 YouTube 频道 !
- 跟我上 中 !
- 连接上Linkedin!
- 前往emmading.com/resources获取更多关于数据科学面试技巧和策略的免费资源!
使用 Python 进行零售产品细分
根据营业额和需求变化对产品进行细分的统计方法
使用统计数据进行产品细分—(图片由作者提供)
产品细分是指将具有相似特性和服务于相似市场的产品分组的活动。通常与营销(销售类别)或制造(生产流程)有关。
但是,作为物流经理,你在管理货流的时候很少关心产品本身;危险和超大产品除外。
你的注意力主要集中在销量分布(快速/慢速移动)需求可变性和交货提前期 。
您希望努力管理具有以下特征的产品:
- 对你总营业额的最高贡献: ABC 分析
- 最不稳定的需求:需求可变性
在本文中,我们将介绍简单的统计工具来结合 ABC 分析和需求可变性来执行产品细分。
💌新文章直接免费放入你的收件箱:时事通讯
**SUMMARY**
**I. Scenario** 1\. Problem Statement
2\. Scope Analysis
3\. Objective
**II. Segmentation** ABC Analysis
Demand Stability: Coefficient of Variation
Normality Test **III. Conclusion**
一、如何用 python 做产品细分?
1.问题陈述
你是当地 配送中心(DC) 的运营总监,为 10 家大卖场发货。
在您的职责范围内,您有责任
2.范围分析
该分析将基于沃尔玛商店销售记录的 M5 预测数据集(链接)。
我们假设我们只有第一年的数据(D1 到 365):
- 10 家店在 3 个州(美国)
- 一千八百七十八独一无二的 SKU
- 3 个类别和 7 个部门(子类别)
除了仓库布局之外,类别和部门对您的订购、提货或运输流程没有任何影响。
代码—数据处理
3.目标
什么会影响您的物流绩效?
产品轮换 推动您大部分销售的参考资料是什么?
- 快速移动者:前 5%(A 级)
- 以下 15%的快速行动者(B 级)
- 其余 80%的非常缓慢的移动者(C 类)
这种分类将影响,
-
仓库布局:使用 Python 根据帕累托原则减少仓库空间
-
提货流程:使用 Python 的订单批处理提高仓库生产率
需求可变性 你客户的需求有多稳定?
- 平均销售额:
- 标准偏差:
- 变异系数:CV = σ/
对于 CV 值高的 SKU,您可能会面临不稳定的客户需求,这将导致工作量高峰、预测复杂性和缺货。
密码
- 筛选第一年销售的爱好 SKU
- 计算销售额的平均值、标准差和 CV
- ABC 分析的排序(降序)和累计销售额计算
二。产品细分
这项分析将在 SKU 的爱好类别中进行。
1.ABC 分析
推动您大部分销售的参考资料是什么?
爱好 ABC 分析 SKU —(图片由作者提供)
**Class A: the top 5%** - Number of SKU: 16
- Turnover (%): 25%**Class B: the following 15%** - Number of SKU: 48
- Turnover (%): 31%**Class C: the 80% slow movers** - Number of SKU: 253
- Turnover (%): 43%
在这个例子中,我们无法清楚地观察到帕累托定律 (20%的 SKU 制造 80%的营业额)。
然而,我们仍然有 80%的投资组合实现了不到 50%的销售额。
代码
2.需求稳定性:变异系数
您客户的需求有多稳定?
从物流经理的角度来看,处理销售高峰比全年统一配送更具挑战性。
为了了解哪些产品会带来规划和分销方面的挑战,我们将计算每个参考的年度销售分布的变异系数。
CV = f(%TO)对于爱好 SKU —(图片由作者提供)
**Class A** Fortunately, most of the A SKU have a quite stable demand; we won't be challenged by the most important SKUs.
低 CV 的 A 类参考—(图片由作者提供)
**Class B** The majority of SKUs are in the stable area; however we still spend effort on ensuring optimal planning for the few references that have a high CV.
高 CV 的 B 类推荐人—(图片由作者提供)
**Class C** Most of the SKUs have a high value of CV;
For this kind of reference a cause analysis would provide better results than a statistical approach for forecasting.
CV 非常高的 C 类参考—(图片由作者提供)
代码
3.正规性检验
我们可以假设销售额服从正态分布吗?
大多数简单的库存管理方法都是基于需求服从正态分布的假设。
为什么? 因为容易。
健全性检查 在开始实施规则和执行预测之前,最好验证这个假设是不可反驳的。
我们将使用夏皮罗-维尔克检验来检验正态性;它可以使用 Scipy 库来实现。零假设将是 (H0:需求销售服从正态分布)。
红色(p 值< alpha) — (Image by Author)
**Bad News** For an alpha = 0.05, we can reject the null hypothesis for most of the SKUs. This will impact the complexity of inventory management assumptions.
代码
三。结论
关注我的 medium,了解更多与供应链数据科学相关的见解。
这种运营驱动的细分让我们深入了解了您的运营在规划和管理物流以满足商场需求时将面临的挑战。
关于我
让我们在 Linkedin 和 Twitter 上连线,我是一名供应链工程师,正在使用数据分析来改善物流运作和降低成本。
如果你对数据分析和供应链感兴趣,可以看看我的网站
参考
[1] Scipy stats Shapiro 测试文档,链接
Python 中制造系统的生产计划和资源管理
高效的供应链、生产计划和资源分配管理比以往任何时候都更加重要。Python 是一个极好的平台,可以模拟我们的需求和约束,甚至可以可视化我们的级联通信时间表。
作者在谷歌地球上拍摄的制造工厂照片
众所周知,供应链管理是当今全球经济中最需要改进的领域之一。将货物从目的地 A 运送到目的地 B 已经够有挑战性了,但是制造足够的材料却是相当困难的,硅芯片和制药组件的短缺也会成为头条新闻。
对这些制造过程建模需要对生产线固有的约束和依赖性有充分的了解。一些混合整数规划模型,如 CPLEX 或 Google 的 OR framework 导出优化目标函数的解决方案,如最小化成本或将资源分配给具有明确约束和优先级的固定班次。然而,这些模型难以模拟连续系统,并且需要缩放以确保输入在数学上甚至可能实现。
我们可以使用众所周知的包(如 Pandas)在 Python 中相对容易地开发一个带有资源平衡的基本生产计划,并且可以使用 Plotly 以图形方式展示一个交互式甘特图,以显示我们垂直级联到管理层和车间的计划。
首先,让我们列出我们将在本练习中使用的案例研究。在这个应用中,我们将讨论制药行业的生物制品生产。许多疫苗,如今天生产的 COVID 疫苗,都是从微生物中获得的,这些微生物被修饰以产生引发预防疾病传播和疾病所必需的免疫反应的大量物质。这些过程的细节通常是专有的并且非常复杂,但是对于我们今天的练习,让我们假设我们正在制造一种从发酵过程中的微生物生长的产品,使用过滤器和离心机彻底净化,然后在添加辅料和灌装到小瓶中之前灭菌。下图所示的过程是通用的,可以在实验室规模和工业规模的许多不同组织中找到。
按作者划分的模拟案例研究流程图
真正的制造过程会有数百个相互关联的约束和依赖的任务。对于我们的模型,我们将有一个或两个前面的任务的渐进依赖性,以及技术操作人员劳动力和可用设备的限制。我们将在 JSON 文件中以下列格式对这些任务和约束进行建模。
任务:
"Items": [
{
"ID":1,
"Name":"Seed",
"Description":"Start fermentation from vial of seed material.",
"Process":"Fermentation",
"ResourcesRequired":
{
"Operator":2,
"SafetyCabinet":1
},
"Dependencies":[0],
"Duration":2
},
{
"ID":2,
"Name":"Flasks",
"Description":"Ferment seed material in flasks.",
"Process":"Fermentation",
"ResourcesRequired":
{
"Operator":2,
"SafetyCabinet":1
},
"Dependencies":[1],
"Duration":3
},
{
"ID":3,
"Name":"Small Fermentor",
"Description":"Ferment in small fermentation vessel.",
"Process":"Fermentation",
"ResourcesRequired":
{
"Operator":2,
"SmallFermentor":1
},
"Dependencies":[2],
"Duration":3
},
{
"ID":4,
"Name":"Large Fermentor",
"Description":"Ferment and inactivare in large fermentation vessel.",
"Process":"Fermentation",
"ResourcesRequired":
{
"Operator":2,
"LargeFermentor":1
},
"Dependencies":[3],
"Duration":4
},
{
"ID":5,
"Name":"Prepare Filters",
"Description":"Prep purification filters for next step.",
"Process":"Purification",
"ResourcesRequired":
{
"Operator":1,
"PurificationTank":1
},
"Dependencies":[3],
"Duration":1
},
{
"ID":6,
"Name":"Purification Filters",
"Description":"Start purification in first purification assembly.",
"Process":"Purification",
"ResourcesRequired":
{
"Operator":3,
"PurificationTank":1
},
"Dependencies":[4,5],
"Duration":4
},
{
"ID":7,
"Name":"Centrifuge",
"Description":"Separate material in centrifuges.",
"Process":"Purification",
"ResourcesRequired":
{
"Operator":2,
"Centrifuge":2
},
"Dependencies":[6],
"Duration":4
},
{
"ID":8,
"Name":"Sterile Filter",
"Description":"Start sterilization of material.",
"Process":"Sterile Boundary",
"ResourcesRequired":
{
"Operator":3,
"SterileAssembly":1
},
"Dependencies":[7],
"Duration":2
},
{
"ID":9,
"Name":"Adjuvants",
"Description":"Add adjuvants to bulk material.",
"Process":"Sterile Boundary",
"ResourcesRequired":
{
"Operator":2,
"SterileAssembly":1
},
"Dependencies":[8],
"Duration":2
},
{
"ID":10,
"Name":"Prepare Vials",
"Description":"Sterilize bulk vials.",
"Process":"Sterile Boundary",
"ResourcesRequired":
{
"Operator":2,
"VialFiller":1
},
"Dependencies":[8],
"Duration":1
},
{
"ID":11,
"Name":"Fill",
"Description":"Fill vials with bulk material.",
"Process":"Sterile Boundary",
"ResourcesRequired":
{
"Operator":2,
"VialFiller":1
},
"Dependencies":[9,10],
"Duration":3
}
],
请注意,我们批次的每一步都包括:
- ID —整数值
- 名称-字符串简短描述
- 描述—字符串详细描述
- 流程-字符串(步骤的三个类别之一)
- 所需资源—所需资源和整数计数的字典
- 依赖项-依赖的 ID 整数列表
- 持续时间—所需小时数的整数(不必是整数)
约束:
"ResourceCapacities":{
"Operator":3,
"SafetyCabinet":1,
"SmallFermentor":1,
"LargeFermentor":1,
"PurificationTank":1,
"Centrifuge":3,
"SterileAssembly":1,
"VialFiller":1
}
请注意,我们的约束是来自任务的资源和我们的案例研究模型团队可用资源的字典。我们的生产套件有 3 名操作员、1 个安全柜、1 个小型发酵罐等,随时可供使用。
如果我们把所有任务的持续时间加起来,我们认为我们的每一批任务大约需要两天,但是,我们注意到一些任务依赖于两个前置任务。如果我们有足够的可用资源,这些可以同时进行!我们程序的目标将是调度运行时间最短的批处理。理论上,该生产计划可用于安排一系列生产,并让管理层更清楚地了解在一段时间内有限的资源下可实现的目标。
要开始在 Python 中开发我们的模型,我们首先必须导入我们将使用的库。
import pandas as pd
import datetime
import numpy as np
import plotly.figure_factory as ff
import json
import random
import plotly.express as px
Pandas 将是用于多种数据框架和操作的主要库。Plotly 将是我们以后可视化的主要库。导入我们的数据 JSON 文件很简单。
with open('tasks.json') as json_file:
data = json.load(json_file)
然后,我们可以用这些加载的数据初始化我们的任务数据框架。
TasksDF = pd.DataFrame.from_dict(data['Items'])
CapacitiesDict = data['ResourceCapacities']
TasksDF 加载的数据帧
我们的数据框看起来非常类似于您在 excel 或其他基于表的工具中看到的数据框,但是,我们的 ResourcesRequired 列包含字典,我们的 Dependencies 列包含一个或两个元素的列表。我们希望将容量字典直接初始化为该对象。
接下来,我们希望初始化一个时间间隔列表,该列表涵盖了我们期望流程达到的整个范围。我们的持续时间列的总和是 29 小时,所以让我们取整为两天。
start_date = datetime.datetime(2021, 1, 1,0,0,0)
number_of_days = 2intervals = []
for minute in range(number_of_days*96):
interval = (start_date + datetime.timedelta(minutes = 15*minute)).isoformat()
intervals.append(interval)
我们区间的粒度是十五分钟组块,一天有 96 个十五分钟组块。因此,我们的整个列表将是这些 15 分钟间隔中的 192 个。我们还可以将时间间隔扩大到 30 分钟或 1 小时,以节省处理时间,或者以 5 分钟或 10 分钟的较低时间间隔获得更精确的时间表;确定粒度的一个决定性因素是 duration 列的特殊性。
接下来,我们需要制作一个矩阵,列出我们需要加载的所有时间和资源。请注意,我们的列是所有资源名称和 capacities 字典中的键。
headings = list(CapacitiesDict.keys())
loading = pd.DataFrame(columns = headings)
loading['index']=intervals
loading = loading.fillna(0)
加载没有加载资源的数据帧
然后,我们将对最大负载矩阵进行同样的操作,但这一次,我们的内容将是在该时间间隔内我们可以加载的最大资源数。如果试图调度任何任务,但在该时间间隔内资源不可用,则应将其推迟到下一个有可用资源的时间间隔。为简单起见,在本练习中,我们将保持我们的值不变(三名操作员全天候可用),但实际上,这些值预计会随着班次和设备可用性而变化。不管在什么情况下,我们后面的算法都将起同样的作用。
loadingMax = pd.DataFrame(columns = headings)
loadingMax['index']=intervals
for item in headings:
loadingMax[item] = CapacitiesDict[item]
加载最大数据帧
我们的任务现在需要追加一些额外的数据。我们需要知道还需要调度多少分钟,并考虑它们的资源,然后在我们的间隔索引内初始化一些开始和结束时间。
jobsToAdd = TasksDF.copy()
jobsToAdd['TimeAddedToSchedule']=jobsToAdd['Duration']*60
jobsToAdd['Start'] = start_date
jobsToAdd['End'] = start_date
添加数据框的作业
我们现在已经准备好了所有要处理的数据。现在到了具有挑战性的部分:处理我们在资源和依赖性方面的各种约束,并安排我们的任务。
我们的算法将按以下顺序运行:
- 逐行加载加载矩阵
- 按作业的任务加载任务以添加任务
- 检查当前作业是否还需要添加;如果是,请继续
- 检查资源在加载矩阵的当前时间间隔内是否可用;如果是,请继续
- 检查依赖关系是否已经计划,并且不会在下一个间隔结束;如果它们是完整的,请继续
- 如果这是计划的第一个时间间隔,则取开始时间的时间戳
- 扣除剩余的计划时间并分配资源
- 如果没有更多的剩余时间要被安排,则取结束时间的时间戳
本质上就是这样。要深入研究代码,请复制代码块并将其粘贴到 Atom 或 Visual Studio 等开发环境中,以查看突出显示的所有语义。
for i in range(len(loading.index)): # Go through loading schedule time by time
print(str(round(i/len(loading.index)*100,2))+'%')
for j in range(len(jobsToAdd.index)): # Go through list of jobs, job by job
if jobsToAdd['TimeAddedToSchedule'][j]>0: # Continue if job needs to be scheduled still
available = True
for resource in list(jobsToAdd['ResourcesRequired'][j].keys()): # Continue if all required resources are available
if loading[resource][i] + jobsToAdd['ResourcesRequired'][j][resource] > loadingMax[resource][i]:
available=False
if available:
dependenciesSatisfied = True
if jobsToAdd['Dependencies'][j][0] == 0: #Skip checking dependencies if there are none
pass
else:
for dependency in jobsToAdd['Dependencies'][j]: # Check if a task's dependencies have been fully scheduled
if jobsToAdd.loc[jobsToAdd['ID'] == dependency]['TimeAddedToSchedule'].item() > 0:
dependenciesSatisfied = False # Check if fully scheduled
if jobsToAdd.loc[jobsToAdd['ID'] == dependency]['End'].item() == datetime.datetime.strptime(loading['index'][i],'%Y-%m-%dT%H:%M:%S')+ datetime.timedelta(minutes = 15):
dependenciesSatisfied = False # Check that dependent end time isnt after the start of this time
if dependenciesSatisfied:
if jobsToAdd['TimeAddedToSchedule'][j]==jobsToAdd['Duration'][j]*60: # Set the start time
jobsToAdd['Start'][j]=datetime.datetime.strptime(loading['index'][i],'%Y-%m-%dT%H:%M:%S')
for resource in list(jobsToAdd['ResourcesRequired'][j].keys()): # Allocate Resources
loading[resource][i] = loading[resource][i] + jobsToAdd['ResourcesRequired'][j][resource]
jobsToAdd['TimeAddedToSchedule'][j] = jobsToAdd['TimeAddedToSchedule'][j]-15 # Reduce needed time
if jobsToAdd['TimeAddedToSchedule'][j] == 0: # Set the end time
jobsToAdd['End'][j]=datetime.datetime.strptime(loading['index'][i],'%Y-%m-%dT%H:%M:%S')+ datetime.timedelta(minutes = 15)
运行我们的算法应该只需要一两秒钟,因为我们的任务数量有限。正如我们可以想象的那样,如果要调度更多的任务,或者间隔更长的时间,时间会明显变长。
算法运行完成后,我们可以看到添加数据帧的工作已经完成,占位符时间戳被替换。没有剩余的时间需要安排。
添加数据框的已完成作业
在检查我们的加载数据帧时,我们还可以看到我们的资源已经被分配到或低于最大加载数据帧的容量。
加载数据框已完成,任务已加载资源
虽然我们的时间表现在已经完成,但数据很难以一种容易向观众呈现的形式呈现,无论是在车间还是在上层管理。为了更好地说明我们的时间表,让我们用 Plotly 建立一个甘特图。
首先,我们需要将我们的数据配置成一种可由 plotly figure factory 读取的形式。
x = jobsToAdd[['Name','Start','End','Process']].copy()
x = x.rename(columns={'Name':'Task','Process':'Resource','End':'Finish'})
在 Plotly 图形工厂中实现的 x 数据框架
# Configure data for data frame formatting
df = []
for r in range(len(x.index)):
df.append(dict(Task=x['Task'][r],Start=x['Start'][r],Finish=x['Finish'][r],Resource=x['Resource'][r]))# Assign color pallete randomly for dynamic number of resource categories
r = lambda: random.randint(0,255)
colors = ['#%02X%02X%02X' % (r(),r(),r())]for i in range(len(x.Resource.unique().tolist())):
colors.append('#%02X%02X%02X' % (r(),r(),r()))fig = ff.create_gantt(df, colors=colors, index_col='Resource', show_colorbar=True, group_tasks=True)
fig.show()
我们可以在下面看到交互式甘特图。
生产计划甘特图
这个生产计划可以导出到我们需要的任何地方,我们可以在屏幕上选择我们想要深入研究的区域。我们现在还可以直观地看到,我们可以在大型发酵罐中分批发酵,同时准备用于纯化的过滤器,因为我们有足够的操作人员,纯化需要完成这两项任务才能进行,但是由于缺乏可用的操作人员资源,准备小瓶和添加佐剂无法同时进行。
如果我们想为数据收集一些统计数据,比如资源利用率,该怎么办?所有这些数据都在我们的加载数据框中捕获,并可用于报告。
loading.describe()
加载数据框统计数据
使用 Pandas describe()函数,我们可以看到可用的最大资源利用率。这对于确定完成工作所需的正确资源量非常有用。我们看到,至少在短时间内,我们的可用操作员数量已经达到极限。如果我们有四个可用的,我们将能够完成我们的添加佐剂和准备小瓶在同一时间,并完成我们的过程更早。
fig = px.line(loading, x="index", y="Operator")
fig.show()
间隔时间内的运算符使用
与包含数百个任务和数十个要加载的资源的实际制造车间批处理相比,我们在这里一起工作的案例研究范围相对较小。这些数据会很快累积起来,并且会因行业而异。然而,我们的代码是可伸缩的,可以处理这些依赖性,尽管代价是更长的处理时间。我们的算法需要改进的地方还包括其他项目管理套件中的功能,如延迟开始、库存跟踪和批处理,以及添加/跟踪延迟。我们还可以在以后补充我们的数据,并在我们的工作中添加进一步的要求,甚至是材料清单或成本,以开发次级目标函数和进一步可视化。因为我们在 Plotly 中开发了我们的视觉效果,我们也可以使用企业数据托管或制作我们自己的 Dash 仪表板。
请让我知道你的想法,并欢迎通过 LinkedIn 联系我,提出反馈、问题或看看我们如何将这些工具和思维方式引入你的组织!点击这里查看我关于数据分析和可视化的其他文章!
用 BentoML 制作 Fastai 模型
Fastai+BentoML =🔥
我欠 FastAI 社区很多,我很确定我认识的很多人也会这么说。整个 ML 工程师军团都是在杰里米的指导和他自上而下的方法下成长起来的。
FastAI 库是社区的另一个贡献,它封装了他们采用的自顶向下的方法。它足够简单,您可以从几行代码开始,但仍然足够强大,可以处理您在 ML 之旅中可能遇到的大多数用例。它设计得非常好,以至于有人说
我认为杰瑞米·霍华德是机器学习领域的唐·诺曼。— 马克·萨鲁菲姆
但是有一件事我觉得需要多做一些工作,那就是部署。有了 FastAI,你将得到一个训练有素的模型,但你必须编写自己的后端来为模型服务,或者使用其他服务来为你做这件事。在这篇博客中,我将介绍一个我发现可以很好地与 FastAI 配合使用的堆栈
Intro BentoML
在之前我已经讲过 BentoML 。我认为它是你训练好的模型的包装器,这使得部署和管理 API 变得非常容易。它很容易上手,并且内置了许多最佳实践,所以从一开始你就有了一个相当高性能的 API。BentoML 支持广泛的 ML 框架(完整列表在此)并支持大量部署选项(列表在此)。这使得它非常容易与您现有的工作流程一起使用,并且仍然可以获得所有的好处。
在这篇博客中,我将向您展示如何在 BentoML 的帮助下,将 FastAI 模型作为一个完善的 API 轻松地投入生产。然后我们可以使用 BentoML 支持的各种部署选项。
现在,让我们构建一个有用的模型来开始训练
法斯泰狗 v/s 猫
所有参加过本课程的人都已经建立了这个模型,并看到了它的实际应用。这是一个简单的模型,识别给定的图片是狗还是猫。这是官方 FastAI 教程的一部分,你可以查看一下,深入了解一下。(那些没有浏览过教程或不熟悉 Fastai 库的人,我建议先浏览一下 FastAI 教程,以便更好地理解)
首先,让我们来设置数据加载器
dls.show_batch()的输出。(来源——牛津-IIIT 宠物数据集)
现在我们已经准备好了数据,让我们对学习者进行培训。
(上图)学习器的输出显示了两个时期的训练和验证损失。(下图)模型的预测,正如你所见,我们有一个训练有素的好模型。(来源:牛津-IIIT 宠物数据集)
通过几行代码,我们有了一个非常强大的狗 v/s 猫分类器。它的速度很快。
准备发货
现在你已经有了一个训练有素的好模特,让我们开始派对吧!我们将首先尝试用 FastAI export
函数保存模型,并对其进行推理。默认情况下,learner.export(fname)
函数将保存learner
的内容,不包含数据集项和优化器状态,以便推断到learner.path
目录中。要加载模型,可以调用load_learner(learner.path/fname)
。现在这里要知道的一件事是export()
使用 python 的 pickles 实用程序来序列化learner
,因此任何用于模型、数据转换、损失函数的自定义代码都不会被保存,并且必须在调用load_learner().
之前加载。在我们的适度示例中,我们使用了一个label_func()
来将图像标记为狗或猫。这是一个与学习者相关的自定义功能,因此当我们加载模型时,我们必须将它添加到同一个模块中。为了使这更容易,我们将所有这些自定义代码移动到另一个文件中,我们称之为dogvscat_utils.py.
Bento,它将在导出时打包这个实用函数。
Saved to: /home/jithin/.fastai/data/oxford-iiit-pet
现在您已经加载了模型,您必须创建一个test dataloader
来获取预测,并使用学习器get_preds()
来获取数据加载器中所有项目的预测。下载一堆猫和狗的图片并保存在文件夹中,我们将对它们进行预测。
(#6) [Path('images/c1.jpg'),Path('images/d2.jpg'),Path('images/c2.jpg'),Path('images/d3.jpg'),Path('images/c3.jpg'),Path('images/d1.jpg')]<fastai.data.core.TfmdDL at 0x7fea7d4d6710>(tensor([[4.7564e-06, 1.0000e+00],
[9.9999e-01, 1.1712e-05],
[5.6602e-12, 1.0000e+00],
[9.9947e-01, 5.2984e-04],
[2.6386e-18, 1.0000e+00],
[9.9998e-01, 1.9747e-05]]),
None)
现在您已经了解了如何使用 FastAI 进行推理,下一个目标是在 API 背后实现这一点。你可以使用类似 FastAPI 的东西,它很容易设置,但我们将使用便当,因为它包括电池。要在模型中使用 BentoML,首先必须将模型的预测函数包装在 BentoService 中。便当服务用于指定一些事情
- 使用哪个框架来训练模型。
bentoml.frameworks
- API 期望的输入类型以及如何处理它。
bentoml.adapters
- API 应该如何接受输入,进行推理和处理输出。可能有这样的情况,一个模型的输出可能是另一个模型的输入,所以所有的逻辑都在那里。
predict()
功能内功。
根据以上信息,Bentoml 将决定包装和服务您的模型的最佳方式。我已经为我们的 FastAI 模型构建了一个样本 BentoService,让我们来看看,我将对它进行分解和解释。
如果你想要更深入的介绍,查看他们的核心概念页面。现在您已经保存并准备好了一个 bentoservice 文件。使用它,我们可以打包并保存 FastAI 模型。
包装的理念就是便当。它接受您的模型及其所有依赖项,并将其打包成标准格式,这样就可以轻松地部署到您想要的任何服务。为了打包我们的DogVCatService,
,我们必须初始化服务并调用它的pack()
函数。这将检查所有的依赖项,构建 docker 文件并将其打包成标准格式。现在可以调用save()
函数将打包的服务保存到自己的版本中。
[2021-01-19 23:20:42,967] WARNING - BentoML by default does not include spacy and torchvision package when using FastaiModelArtifact. To make sure BentoML bundle those packages if they are required for your model, either import those packages in BentoService definition file or manually add them via `@env(pip_packages=['torchvision'])` when defining a BentoService
[2021-01-19 23:20:42,970] WARNING - pip package requirement torch already exist
[2021-01-19 23:20:42,971] WARNING - pip package requirement fastcore already exist
[2021-01-19 23:20:42,974] WARNING - pip package requirement fastai>=2.0.0 already exist
[2021-01-19 23:20:44,734] INFO - BentoService bundle 'DogVCatService:20210119232042_EF1BDC' created at: /home/jithin/bentoml/repository/DogVCatService/20210130121631_7B4EDB
搞定了。现在,您已经创建了第一个 BentoService。要查看它的运行情况,请进入命令行并运行bentoml serve DogVCatService:latest.
,这将启动开发服务器,如果您前往localhost:5000
,您可以看到模型的 API 在运行。
部署您的打包模型
正如我前面提到的,BentoML 支持各种各样的部署选项(您可以在这里查看整个列表)。您可以从中选择适合您的部署。在这一节中,我将向您展示如何将 Bentoml 模型容器化,因为这是一种流行的分发模型 API 的方式。
如果您有 docker 安装程序,只需运行
bentoml containerize DogVCatService:latest
来生成准备好为我们的 DogVCatService 服务服务的 docker 容器。
[2021-01-31 12:40:24,084] INFO - Getting latest version DogVCatService:20210130144339_7E6221
Found Bento: /home/jjmachan/bentoml/repository/DogVCatService/20210130144339_7E6221
Containerizing DogVCatService:20210130144339_7E6221 with local YataiService and docker daemon from local environment\WARNING: No swap limit support
|Build container image: dogvcatservice:20210130144339_7E6221
现在,您可以通过运行以下命令来运行 docker 容器
docker run --rm -p 5000:5000 dogvcatservice:{container version}
转到 localhost:5000 来查看 swagger UI,或者运行 curl 来获得响应
curl -X POST \
--data-binary [@test_images/d1](http://twitter.com/test_images/d1).jpg \
localhost:5000/predict
它应该为猫返回 true,为狗返回 false。
结论
现在,您已经成功地用几行代码为 FastAI 模型创建了一个生产级 API(真正的 FastAI 风格😉).伴随这篇博客的还有一篇 GitHub repo 。如果你想尝试完成的版本,请查看它。
生产率提示:在 Windows 的右键上下文菜单中添加 Jupyter 和 Anaconda 快捷方式
创建有用的快捷方式来加速您的环境设置
马修·布罗德在 Unsplash 上拍摄的照片
这将是一个快速的过程,如果你和我一样,习惯于使用 Linux 计算机进行开发,你会发现这很有用。正如我刚才所说,在我生命的大部分时间里,我主要使用 Ubuntu 系统来完成几乎所有的编程工作。但是最近我有点厌倦了一些兼容性问题,决定尝试一下老式的 Windows。
因此,我开始安装全新的 Windows 系统,配置必要的编程环境,下载并安装 Anaconda 包等等。然后我注意到缺少一个我在 Ubuntu 上经常使用的简单但非常方便的工具:右击上下文菜单上的“在终端中打开”选项。
这可能看起来没什么大不了的(老实说,这甚至可能不是哈哈),但当我在不同的文件夹中处理多个 Jupyter 笔记本时,它节省了我很多时间。有了它,我可以直接导航到想要的文件夹,直接从那里打开一个新的终端窗口,运行 Jupyter,然后开始工作,而不是必须从终端本身导航到每个文件夹。
所以,我不知道这对大多数人来说是多大的“缺失功能”,但对我来说肯定是这样的!如果你已经读到这里,很有可能它也适合你,所以让我们开门见山,好吗?
将自定义命令添加到上下文菜单中
在网上搜索可能性后,我发现这并不是那么简单,但也没有那么复杂,所以请耐心等待!
要做的第一件事是找到您的 Anaconda 安装路径。如果您在安装过程中使用了默认位置,它应该位于如下位置:
C:\Users\**<your_user>**\anaconda3
我们需要知道这个位置,因为我们将使用脚本文件夹中的 activate.bat 批处理脚本来打开 Anaconda 提示符。
接下来你需要做的是运行注册表编辑器。要做到这一点,只需在 Windows 搜索中输入regedit.exe,它应该会在你完成输入之前弹出。
Windows 的注册表编辑器
你应该密切关注接下来的步骤,因为你不想在这里惹上麻烦。所以要注意!
说完这些,让我们回到正题。如您所见,左侧有一堆文件夹,您必须导航至以下文件夹:
HKEY_CLASSES_ROOT **>** Directory **>** Background **>** shell
您可以通过向左导航文件夹树或在顶部的地址栏中键入相应的路径来找到它。打开后,您应该会在左侧面板上看到类似这样的内容:
目录
您可以在这里向上下文菜单添加自定义命令。如您所见,由于我安装了 VSCode 和 PyCham,它们都有一个命令列在这里,因为它们都有一个列在右键上下文菜单中的“打开方式…”选项。
因此,在 shell 文件夹中,您应该创建 3 个新的键,每个键对应一个我们想要添加的新命令:anaconda promp、 JupyterLab 和 JupyterNotebook 。右击外壳文件夹,选择新建>键。这些键应该与系统中出现的 cdm、PowerShell 以及任何其他键在同一级别,否则它将无法工作。
当您选择其中一个新密钥时,您应该会在右侧面板上看到类似这样的内容:
新的关键属性
双击默认属性,并在值数据字段中填入您希望出现在该键的上下文菜单中的文本:“在 AnacondaPrompt 中打开文件夹”、“用 JupyterLab 打开”和“用 Jupyter Notebook 打开”。
之后,你应该在我们创建的每个新键下添加一个名为 command 的新键(我知道这很奇怪)。那么您现在应该有这样的东西:
添加新密钥后的 regedit
最后,显然,要使它工作,您必须做的最后一件事是添加实际的命令本身。为此,您将重复向其他键添加文本的过程:编辑命令键上的默认属性,并填充值数据字段,但是您将编写实际的命令,而不是纯文本。对于每个相应的键,它们是:
- anacondapromp>命令:
cmd.exe /K C:\Users\**<your_user>**\anaconda3\Scripts\activate.bat
- JupyterLab >命令:
cmd.exe /K C:\Users\**<your_user>**\anaconda3\Scripts\activate.bat && jupyter-lab
- JupyterNotebook >命令:
cmd.exe /K C:\Users\**<your_user>**\anaconda3\Scripts\activate.bat && jupyter-notebook
不要忘记添加您的实际用户名,或者甚至更改整个 anaconda 路径,以防您的用户名安装在系统的其他地方。重要的是为 activavate.bat 脚本提供正确的地址,因为它是用于实际打开 anaconda 提示符的默认脚本。
如果您想了解这些命令是如何工作的,下面是它们每个部分的作用:
- cmd.exe—打开各自文件夹下的 CMD(你右击的那个);
- /K —告诉 CMD 发出随后的命令,然后保持打开状态,以便您可以查看结果或键入更多命令;
- C:\…\activate.bat —运行 activate.bat 脚本,打开 AnacondaPrompt 环境;
- & & —一个“与”逻辑运算符。在第一个命令成功返回后,它运行该行上的下一个命令;
- jupyter-lab 或 jupyter-notebook —用于打开 jupyter lab 或 jupyter notebook 的命令,根据您的喜好而定;
所以,在所有这些之后,你应该在你的右键快捷菜单上有 3 个全新的工作选项!去某个文件夹中查看一下,它看起来会像这样:
3 在上下文菜单中添加了选项
嗯,太好了,它像一个魔咒!但是菜单看起来确实有点空,不是吗?有些人甚至会说不完整…
添加图标额外的酷!
是啊!这样做其实很简单。回到您添加的新键(顶层键,不是命令键),右键单击并选择新>字符串。这将在右侧面板上创建一个新的属性行。将该属性命名为 Icon ,并提供一个图标文件的路径(。ico 扩展)作为值数据。
我在我的 keys 中使用了 Anaconda 包附带的一些默认图标(多么方便),下面是它们的位置:
- anacondapromp>图标:
C:\Users\**<your_user>**\anaconda3\Menu\anaconda-navigator.ico
- JupyterLab 和 JupyterNotebook >图标:
C:\Users\**<your_user>**\anaconda3\Menu\jupyter.ico
然后,你最终的(更酷的)上下文菜单将看起来如下所示,但是如果你喜欢的话,可以随意使用其他图标。
最终上下文菜单
我们结束了这个简短的教程,我希望你喜欢它,甚至可能学到一些新东西!感谢你的阅读!
额外的小提示
- 您可以导出这些新创建的密钥进行备份,或者将它们添加到 Windows 的其他安装中。您所要做的就是右键单击所需的键并选择导出;
- 您也可以使用相同的过程添加其他命令来满足您的特定需求,但是注意不要弄乱您不熟悉的其他选项。毕竟,权力越大,责任越大;
使用概念 API 和 Python 进行生产力跟踪
使用新发布的概念 API 和 Streamlit 构建一个简单的项目跟踪仪表板
随着最近备受期待的测试版概念 API 的发布,许多应用程序之间的集成现在已经成为可能,整个宇宙的可能性现在为数据科学项目敞开了大门。
在这篇文章中,我将向你展示如何用 Streamlit 和概念 API 构建一个简单的项目跟踪仪表板。
如果你愿意,你可以看看我在 youtube 上关于这个话题的视频:
设置概念 API
让我们从创建关于概念的页面开始。现在,您需要在您的概念页面上创建一个集成令牌,然后与该集成共享您的数据库。关于如何做到这一点的详细教程可以在这里找到。
设置一个简单的 python API 从数据库中获取数据
现在,您可以设置一个简单的 python API 来与您的概念应用程序中的数据库进行通信,以便您可以获取数据。
在我的例子中,我创建了一个简单的数据库,每个属性(表中的列)引用一个项目,每一行与一个月中的某一天相关,每个条目包含一个复选框,指示该项目是否在给定的那一天工作。在我的概念页面上,看起来是这样的:
作者图片
为了从概念数据库中获取数据,我们可以用三种方法编写一个类:
query_databases()
从概念应用程序中获取数据;get_projects_titles
获取您正在跟踪的项目的名称(它们将是您的概念数据库中的列的标题);get_projects_data
从概念 API 获得的结果字典对象中获取项目跟踪数据。
python API 的完整代码:
让我们快速分析一下每个组件:
- 查询数据库
这里,我们使用来自概念 API 的集成令牌向我们的数据库发送一个 post 请求,并以 json 对象的形式返回响应,以便将数据格式化为我们稍后需要可视化的形状。
- 获取项目名称
这里,我们只是检索数据库中的所有属性。关于如何使用概念 API 处理数据库的更多细节可以在这里找到。
- 获取项目数据
最后,我们从每个项目以及日期列中获取数据。
使用 Streamlit 和 plotly 设置可视化
现在我们已经加载了数据,让我们使用plotly
包来绘制项目的信息。
让我们做一些简单的事情,绘制一个条形图,显示在跟踪期间(在本例中是过去 45 天)每个项目上花费的天数,以及一个项目事件散点图,显示我的日常活动(我是否参与了每个项目)。
让我们从导入依赖项开始。
import plotly.graph_objs as go
import pandas as pd
import plotly.express as px
from notion_api import *
现在,我们使用 python API 从概念数据库中获取数据。
nsync = NotionSync()
data = nsync.query_databases()
projects = nsync.get_projects_titles(data)
projects_data,dates = nsync.get_projects_data(data,projects)
我们用我们想要研究的数据建立了一个数据框架,并绘制了事件跟踪器和条形图。
效果非常好!这个工具真正酷的方面是, 当我们更新我们的概念页面时,这个图会自动更新 而不需要任何额外的努力。
现在, 让我们绘制一个事件图 ,告诉我们在给定的时间段内每天都在做什么。
我们需要将数据库中 checkbox 属性的布尔值转换为连续值,我们可以用这种方式绘制这些值,y 轴上的每条线代表一个不同的项目,该线区域上的每个点代表在给定的一天参与了该项目。
作者图片
我们找到了。最后,让我们将这段代码封装在一个 Streamlit 应用程序中,这样我们就可以在任何时候通过在终端上运行一个命令来查看我们的数据。
为此,我们只需要几行代码:
作者图片
load_data
函数负责直接从我们的概念数据库中获取数据,下面的代码只是使用plotly
包为要在 Streamlit 中显示的图设置标题和副标题。Streamlit 的极简风格让这变得非常简单。
在终端上,使用streamlit run app.py
运行 Streamlit 应用程序
作者图片
这个项目的完整源代码可以在这里找到。
关于在数据科学项目中使用概念 API 的最终想法
这个新的测试版的概念 API 非常强大。意识到这个应用程序只提供了一个 javascript 版本的 SDK,我写了这段代码来使它很好地与 python 集成。
concept 现在提供的与这个官方 API 的集成真的可以将像这样的生产力项目带到一个全新的水平,我觉得我们会在即将到来的未来看到越来越多真正有趣的功能出现。
如果你喜欢这篇文章,请在 Twitter 、 LinkedIn 上联系我,并在 Medium 上关注我。谢谢,下次再见!😃
参考
使用 FastText 进行亵渎检测
通过 Python 在文本分类模式下使用 fastText
艾莉娜·格鲁布尼亚克在 Unsplash 上的照片
如今,无数网站以文本的形式接受来自用户的内容。社交媒体平台上的帖子评论、电子商务网站上的产品评论以及 Medium 等平台上的文章等内容每天都在以比以前更快的速度增长。随着如此大量的内容产生,有越来越多的负担,主持人来调节这些内容。自动化这个审核过程的几个方面不仅有助于审核过程中的仲裁者,还会使整个过程更加有效。检测内容中的亵渎就是这样一个用例。
FastText 是脸书开发的一个单词嵌入和文本分类模型。它建立在 Word2vec 之上,依靠浅层神经网络来训练一个单词嵌入模型。fastText 从 Word2vec 继承了一些要点,我们将在继续我们的用例之前考虑这些要点,
- FastText 根据单词使用的上下文创建单词嵌入。这是在训练期间通过滑动单词窗口来完成的。它采用两种方法— 连续包字和跳过克数。
- Word2vec 模型依赖于训练数据集的词汇。具有异构单词的较大数据集导致更好的嵌入模型。为单词生成嵌入时,如果该单词不是在训练模型期间构建的词汇表的一部分,则该单词将被忽略。因此,当训练样本量很小时,Word2vec 变得不太有效。 FastText 通过将每个单词分解成 n 个字母的记号,并用这些 n 个字母的记号创建一个词汇表来代替原来的单词,从而解决了这个问题。这增加了词汇表的大小,并适应了存在单词拼写错误或训练数据集很小的情况。
- 像 Word2vec 这样的 FastText 已经被证明比更确定的特征提取/矢量化技术(如单词包)更好。用 fastText 或 Word2vec 创建的向量分布更加均匀,每个向量维度都包含一些关于单词的信息。
FastText 库被开发用于两种模式——单词嵌入 和文本分类 。在嵌入模式中,我们训练一个具有一个隐层的浅层神经网络来构建一个词向量矩阵。该矩阵可用于识别具有相似含义的单词。由于单词向量是在考虑单词上下文的情况下构建的,因此有了足够的数据,可以实现诸如 V(国王)-V(男人)+V(女人)=V(王后)的计算。在文本分类模式中,fastText 训练单词嵌入模型,建立单词向量矩阵,并使用这些单词向量训练多项式逻辑回归模型。
在这篇博客中,我将只关注文本分类模式下的 fastText。以下是我们在训练分类模型时将遵循的几个重要步骤:
- 训练和测试数据集取自 Kaggle ( 下载)。数据集可以免费分发,并归入 CC0 之下,底层评论文本由维基百科的 CC-SA-3.0 管理。该数据集包含带有 6 个标签的注释。
- 根据注释是否亵渎,预处理数据集,使其只有一种标签类型。
- 清理注释——删除停用词,删除标点符号和词条。
- 在训练集上训练 FastText 单词嵌入模型。
- 在训练的单词向量上训练逻辑回归模型。
- 使用 10 重交叉验证进行训练和测试,以更好地量化模型的准确性。
作者图片
我们开始吧。
默认情况下,数据集为 CSV 格式,结构如下:
作者图片
让我们首先重新构建这个数据集,使其只包含一种标签类型,这种标签类型将表明这个句子是否是亵渎的。如果任何有毒,严重有毒,淫秽,威胁,侮辱,身份仇恨被标记为 1 ,我们将认为该评论是亵渎。最终重构的数据集应该如下所示。请注意,数据集也应该转换为 FastText 兼容格式,标签设置为 label 前缀。在我们的例子中, __label__1 和 __label__2 分别对应被亵渎和不被亵渎的评论。
作者图片
正如你已经注意到的,数据集中仍然存在许多不必要的数据,比如停用词和标点符号。这些并不真正包含任何有用的数据,只是在训练过程中造成了噪声。因此,下一步,我们必须通过执行操作来清除数据集的这些不必要的数据,例如:删除标点符号,删除停用词和 词条匹配 词。我最终使用了来自 Sklearn 的停用词,因为它们为这个用例提供了更好的结果。为了进行词汇匹配,我使用了NLTKwordnet lemmatizer。这里值得一提的重要一点是,在去除单词方面,lemmatising 比词干做得更好。总之,在这些操作之后,数据集看起来应该是这样的,
作者图片
这个清理过的数据集现在准备好用于训练 FastText 单词嵌入模型和分类器。为此,我使用了 Python 的 fasttext 库。在文本分类模式下,可以使用fast text . train _ supervised】定义。该定义在内部负责为 多类分类 训练词向量模型和多项式逻辑回归分类器。对于我们的用例,我们只有 2 个类—世俗的(__label__1)和非世俗的(__label__2)。为了更好地量化测试准确性,使用 K 倍交叉验证是一个很好的实践。在这种情况下,我使用 sk learnStratifiedKFold进行了 10 重交叉验证。下面的代码片段显示了如何使用 FastText 库来训练模型和测试数据。
让我们来谈谈我配置的超参数。ws是窗口大小。大小为 1 意味着模型将考虑上下文单词两边的一个单词。"wordNgrams"定义了我们一次要为上下文考虑多少个单词。在这里将其设置为 2 意味着两个相邻的单词将被同时采用。Minn和maxn定义单词进一步分解成的最小和最大字符大小。请注意,正如本文开头所讨论的,这是 FastText 相对于 Word2vec 的改进之处。lr是学习率,而epoch**定义了模型应该迭代每个数据样本的次数。这里使用的损失函数是分级 Softmax 。通过反复试验,我最终在我的模型中收敛了这些值。" model.test_label" 定义返回每个标签的准确度分数字典— precision , recall 和 f1score 。它应该看起来像,********
作者图片
我不会深入研究如何对训练数据进行 K-fold 交叉验证来计算准确性分数。将来我可能会就此单独写一篇文章。
10 倍的每次迭代花费 3 分钟来训练模型,对于大约 170k 的数据集,总训练时间达到大约 30 分钟。培训是在我的 Macbook Pro Intel i7、16GB 内存、4GB GPU 内存上完成的。用这些超参数在 10 倍上平均得到的模型的最终精度是,
作者图片
如果你不知道 精度和 召回,这些本质上是精度测量,以确定我们的模型预测的假阳性和假阴性的数量。虽然这些分数的一个好值取决于用例,但是一般来说,越接近 100%,越好。
现在,确定模型的精度和召回率是非常好的,因为它有助于量化我们模型的准确性。但是,如果我们自己测试几个语句,看看模型预测的是什么,那会更令人满意。下面是我测试模型的几个样本, __label__1 为亵渎, __label__2 为非亵渎,
作者图片
作者图片
正如你所看到的,模型能够预测句子中的脏话,可信度得分非常高。不仅是简单的情况,而且该模型还能够根据使用单词的上下文来预测脏话,
作者图片
作者图片
正如你所观察到的,模型预测 pig 在一个基于上下文的评论中会被亵渎。FastText 证明有用的另一种情况是对于打字错误,
作者图片
作者图片
因为 FastText 将一个单词进一步分解成 n-gram 标记来构建其单词向量矩阵,所以用户可能输入拼写错误的单词的用例也被考虑在内。
利用我们上面训练和测试的模型,可以得出结论,文本分类模式中的 fastText 对于检测句子中的亵渎性产生了非常好的结果。凭借超过 95%的精确度和召回分数,FastText 是其他已建立的检测亵渎的方法中的一个极好的选择。FastText 的单词嵌入功能非常强大,像逻辑回归这样的相对基本的分类器能够以如此高的精度在类之间进行分类。此外,我们在上面看到,我们训练过的 fastText 模型会处理拼写错误,甚至会考虑单词使用的上下文来判断它是亵渎还是非亵渎。FastText 的这些预测能力使其成为识别句子中脏话的强大工具,它在几乎每个接受和调节用户内容的领域都有自动调节的应用。
Python 中的专业维恩图
数据可视化
从熊猫数据帧生成带有定制标签和图例的 3 向 Venns
作为一个狂热的 python 爱好者,我最近遇到了生成三向维恩图的任务。我在这个看似简单的任务上花费的时间揭示了我在 matplotlib 工作知识方面的不足。希望本教程可以帮助其他人有效地定制 matplotlib 嵌入式文本,以生成可用于发布、仪表板或报告的图形。完整的 Jupyter 笔记本托管在 GitHub 上。
设置
让我们从存储在熊猫数据帧中的数据开始。作为一个标准的数据结构,我们希望快速可视化来自三个数据帧或系列的分类数据。在本例中,我们将考虑分布在 A、B 和 c 三个组中的总共 10 个人。目标是使用维恩图将这些分组可视化。
import pandas as pd
groupA =['person1','person2','person3','person4','person5',
'person6','person7']
groupB = ['person1','person2','person3', 'person8','person9']
groupC = ['person1','person2', 'person4', 'person9','person10']dfA = pd.DataFrame(groupA,columns=[‘A’])
dfB = pd.DataFrame(groupB,columns=[‘B’])
dfC = pd.DataFrame(groupC,columns=[‘C’])
dfA、dfB 和 dfC 的内容
三向维恩图
用于分析分组的理想 python 数据结构是“集合”集合是唯一物品的无序组合。在第一行中,我们把熊猫数据帧 dfA 的 A 列称为一个系列。然后,我们将系列转换为集合。
如果您的数据列有重复项,您需要在使用此方法之前删除它们或转换它们的名称。如果您不确定,请致电 sum(dfA。A.duplicated())。如果该数字大于零,请在继续操作之前处理重复的地址。
A = set(dfA.A)
B = set(dfB.B)
C = set(dfC.C)
使用集合运算符&
和-
计算交集(共享组件)和差,可以找到 A、B 和 C 的子集。A_rest
包含仅在 A 中发现的个体。AB_only
包含在 c 中未发现的 A 和 B 中的个体。ABC_overlap
包含在所有三个组中都有成员的个体。
AB_overlap = A & B #compute intersection of set A & set B
AC_overlap = A & C
BC_overlap = B & C
ABC_overlap = A & B & CA_rest = A - AB_overlap - AC_overlap #see left graphic
B_rest = B - AB_overlap - BC_overlap
C_rest = C - AC_overlap - BC_overlapAB_only = AB_overlap - ABC_overlap #see right graphic
AC_only = AC_overlap - ABC_overlap
BC_only = BC_overlap - ABC_overlap
子集之间的交集
现在可以使用 matplotlib_venn 包使用这些变量来生成 Venn。当定制您的数据时,请跟踪分组顺序!
from collections import Counter
from matplotlib_venn import venn2, venn3
import matplotlib.pyplot as pltsets = Counter() *#set order A, B, C *
sets['100'] = len(A_rest) *#100 denotes A on, B off, C off* sets['010'] = len(B_rest) *#010 denotes A off, B on, C off* sets['001'] = len(C_rest) *#001 denotes A off, B off, C on* sets['110'] = len(AB_only) *#110 denotes A on, B on, C off* sets['101'] = len(AC_only) *#101 denotes A on, B off, C on* sets['011'] = len(BC_only) *#011 denotes A off, B on, C on* sets['111'] = len(ABC_overlap) *#011 denotes A on, B on, C on*labels = ('Group A', 'Group B', 'Group C')
plt.figure(figsize=(7,7))
ax = plt.gca()
venn3(subsets=sets, set_labels=labels, ax=ax,set_colors=
('darkviolet','deepskyblue','blue'),alpha=0.7)
plt.show()
默认三路维恩
更改“集合标签”的颜色(例如,组 A)。
这个图不错,但是看起来业余多过专业。为了让它更有趣,我们可以定制视觉效果的几个方面。首先,我们可以修改设置的标签颜色。请参见添加代码的粗体部分。
plt.figure(figsize=(7,7))
ax = plt.gca()
colors = ['darkviolet','deepskyblue','blue']
v = venn3(subsets=sets, set_labels=labels, ax=ax,set_colors=
colors,alpha=0.7) **i = 0for text in v.set_labels:
text.set_color(colors[i])
i+=1**plt.show()
将标签颜色与子集颜色匹配
更改“子集标签”的颜色(例如 1,2,3)。
既然设置的标签颜色与圆形部分相对应,我们还可以修改数字字体。为了更加清晰,我们可以改变维恩图上子集标签的颜色、字体大小和字体粗细。
plt.figure(figsize=(7,7))
ax = plt.gca()
colors = ['darkviolet','deepskyblue','blue']
v = venn3(subsets=sets, set_labels=labels, ax=ax,set_colors=
colors,alpha=0.7)
i = 0
for text in v.set_labels:
text.set_color(colors[i])
i+=1
**for text in v.subset_labels:
text.set_color('white')
text.set_fontsize(16)
text.set_fontweight('bold')**plt.show()
使子集标签在背景色上可读
用图例替换标签,并添加标题。
一旦对这些字体感到满意,我们可能希望完全消除彩色标签,以获得更正式的效果。通过生成两个列表、句柄和标签来创建图例。
Handles 是通过使用 Venn3 类中的get_patch_id()
方法遍历 Venn 的补丁并将组件添加到空列表中来生成的。根据我的经验,最好在图形上打印句柄列表,手动编写标签列表进行匹配。
plt.figure(figsize=(7,7))
ax = plt.gca()
v = venn3(subsets=sets, set_labels=('','',''), ax=ax,set_colors=
colors,alpha=0.7)
for text in v.subset_labels:
text.set_color('white')
text.set_fontsize(16)
text.set_fontweight('bold')
**h = []
for i in sets:
h.append(v.get_patch_by_id(i))
l = ['A only','C only','B only','A & C shared','A & B shared',
'B & C shared','A & B & C shared']
ax.legend(handles=h, labels=l, title="Legend",loc='lower left')** **plt.title('Membership across Three Groups')**plt.show()
取消标签,选择图例
自定义图例内容。
删除不言自明、多余或不重要的图例元素。这将把你的听众引导到你想要强调的其余部分。为此,我们手动修改上面生成的句柄和标签列表。
请注意,当计数改变时,句柄列表h
有办法移动。对于这个例子,我可以方便地使用h[0:3]
和l[0:3]
来调用h
和l
的片段。然而,如果您需要从这两个列表的中心消除色标,您可以在将它们作为参数在ax.lengend()
中调用之前,应用del h[2]
操作符从句柄和标签中移除特定索引处的元素。例如,这将从列表h
中移除索引 2 处的元素。只要记住保持这些列表的长度相等。
plt.figure(figsize=(7,7))
ax = plt.gca()
v = venn3(subsets=sets, set_labels=('','',''), ax=ax,
set_colors=colors, alpha=0.7)
**for** text **in** v.subset_labels:
text.set_color('white')
text.set_fontsize(16)
text.set_fontweight('bold') **ax.legend(handles=h[0:3], labels=l[0:3], title="Legend",
loc='lower left')**plt.title('Membership across Three Groups')
plt.show()
自定义图例的内容
双向文氏图
这些方法也可以应用于简单得多的双向图。venn2 类的符号遵循 venn3 的符号。使用相同的格式,上述所有方法都可以应用于这个基类。
sets = Counter() *#set order A, B *
sets['10'] = len(A-AB_overlap) *#10 denotes A on, B off*
sets['01'] = len(B-AB_overlap) *#01 denotes A off, B on*
sets['11'] = len(AB_overlap) *#11 denotes A on, B on*
labels = ('Group A', 'Group B') plt.figure(figsize=(7,7))
ax = plt.gca()
**venn2(subsets=sets, set_labels=labels, ax=ax,set_colors=
('darkviolet','deepskyblue'),alpha=0.7)** plt.title('Membership across Two Groups')
plt.show()
默认双向文氏
本教程到此结束。
对某些人来说,定制文本内容、位置、颜色、大小或图形的重量似乎是不必要的。对于实际应用来说,确实如此。然而,在正式的演示场合,干净、清晰的图形是至关重要的。如果一张图片能表达 1000 个单词,使用这些技巧让它们有价值。
更多信息,请查看 GitHub 上的完整笔记本。对相关文章感兴趣?多读读作者。
https://medium.com/swlh/series-on-theories-high-dimensional-data-101-81cab8e0bea6 https://www.linkedin.com/in/kate-wall/
在一行代码中分析 Python 函数的内存消耗
使用内存分析器模块逐行监控函数的内存使用情况
Python 是数据科学社区中的一种流行语言,以其健壮性和大量框架而闻名。Python 更喜欢简单而不是复杂,这使得它更受欢迎,但却牺牲了性能。Python 程序经常容易出现内存管理问题。
数据科学家使用 Python 语言在固定的内存限制下处理大量数据。如果代码执行超过 RAM 限制,可能会出现内存错误,程序执行会终止。一个快速的解决方案是增加内存分配以避免内存管理问题,但这并不总是可行的。
为什么内存分析很重要?
通常新手不会释放未使用的内存,并不断分配新的变量,这可能会增加内存消耗。当代码被执行时,越来越多的内存被分配。Python 自动管理内存,但在执行长 Python 代码时,它可能无法将内存消耗返回给操作系统。
因此需要外部工具来监控内存消耗,这将进一步帮助优化内存。
本文将讨论监控 Python 函数内存消耗的内存分析器模块。
内存分析器:
Memory Profiler 是一个开源的 Python 模块,它在内部使用**psutil**
模块,来监控 Python 函数的内存消耗。它对函数执行逐行的内存消耗分析。
安装:
可以使用以下命令从 PyPl 安装内存分析器:
**pip install -U memory_profiler**
并且可以使用导入
**from memory_profiler import profile**
用法:
设置好一切之后,使用这个模块来跟踪函数的内存消耗就相当容易了。@profile
decorator 可以用在每个需要跟踪的函数之前。这将按照与行分析器相同的方式逐行跟踪内存消耗。
用@profile
修饰所有函数后,用一组特定的参数执行 python 脚本。
绕过-m memory profiler
到 Python 解释器,执行上面的 Python 脚本。这将加载 memory_profiler 模块并逐行打印内存消耗。
使用下面的来执行 Python 脚本和内存分析器。
**python -m memory_profiler <filename>.py**
(图片由作者提供)
成功执行后,您将得到一个逐行的内存消耗报告,类似于上图。该报告有 5 列:
- 行号:行号
- 行内容:各行号处的 Python 代码
- Mem usage:Python 解释器在每次执行该行后的内存使用情况。
- 增量:当前行到最后一行的内存消耗差异。它基本上表示特定 Python 代码行消耗的内存。
- 出现次数:某一行代码被执行的次数。
可以跟踪 Mem 使用情况,以观察 Python 解释器的总内存占用情况,而可以观察 Increment 列,以查看特定代码行的内存消耗。通过观察内存使用情况,可以优化内存消耗,从而开发出生产就绪的代码。
结论:
优化内存消耗与优化 Python 代码的时间复杂度一样重要。通过优化内存消耗,可以在一定程度上加快执行速度,并避免内存崩溃。
还可以尝试自定义的@profile
装饰器来指定参数的精度。为了更好地理解,请阅读内存分析器模块的文档。
参考资料:
[1]内存分析器文档:https://pypi.org/project/memory-profiler/
喜欢这篇文章吗?成为 中等会员 继续无限制的学习。如果你使用下面的链接,我会收到你的一小部分会员费,不需要你额外付费。
https://satyam-kumar.medium.com/membership
感谢您的阅读
Python 中的剖析
帮助发现应用程序瓶颈的基本工具
当试验一个项目时,它可能是有帮助的(或者仅仅是有趣的!)来尝试查看代码的哪些部分是最消耗内存的。如果代码中的这些地方造成了问题,那么找出如何改进它们是值得的。有时候,你需要做的只是做一些小小的调整来节省一些时间。其他时候,考虑一种完全不同的方法是个好主意。
首先,值得问问自己,剖析和优化代码是否值得,因为它可能会带来过度的复杂性。在开始编辑和优化之前,仔细考虑所有的权衡是非常重要的,比如可读性和易维护性。或者,用唐纳德·克努特 (1974)的话说:
我们应该忘记小的效率,比如说 97%的时候:过早的优化是万恶之源。然而,我们不应该错过这关键的 3%的机会。
然而,如果需要的话,有几个推荐的库用于检查代码执行,可以在这里找到。我将回顾一下那里提到的timeit
和cProfile
,以及memory_profiler
。这 3 个模块是我的首选工具。
我的示例. py 文件
出于演示目的,我将使用一段相当简单的代码:
def square_ints_for_loop(limit):
"""create list of squared ints using for-loop"""
squared_list = []
for i in range(limit):
squared_list.append(i * i)
return squared_list
它所做的只是一些虚拟计算,在一个整数范围内迭代,直到一个给定的阈值,以计算出一个平方数的列表。
时机
如果您对一段代码的总执行时间感兴趣,您可以测量一下。实际上,您可以使用两个标准模块— [time](https://docs.python.org/3/library/time.html)
和[timeit](https://docs.python.org/3/library/timeit.html)
。然而,timeit
被认为是更准确的(更多细节见文档)。它多次执行语句来测量,以避免被随机因素扭曲。除此之外,timeit
暂时关闭垃圾收集器,这样也不会影响计时。
例如,如果我计算循环遍历一个 100 以内的数字列表需要多长时间,我可以看到下面的结果:
if __name__ == '__main__':
print(timeit('square_ints_for_loop(100)', 'from __main__ import square_ints_for_loop'))
输出:
9.349990251997951
如果我将其与使用列表理解进行比较,我可以看到一些改进:
def square_ints_list_comprehension(limit):
"""create list of squared ints using list comprehension"""
return [i * i for i in range(limit)]if __name__ == '__main__':
print(timeit('square_ints_list_comprehension(100)', 'from __main__ import square_ints_list_comprehension'))
输出:
6.0150625230162404
执行分析器
有几个收集执行统计数据的标准模块— [profile](https://docs.python.org/3/library/profile.html)
和 [cProfile](https://docs.python.org/3/library/profile.html)
。两者都提供相似的功能。它们详细说明了调用次数、在给定函数中花费的总时间、 percall 作为总时间除以调用次数的商、在该函数和所有子函数中花费的累计时间,另一个 percall 列作为累计时间除以原始调用的商(如下图所示)。
cProfile
应该更准确(更多细节见文档)。
它可以这样运行:
if __name__ == '__main__':
limit = 10000
cProfile.run('square_ints_for_loop(limit)')
输出:
cProfile 输出
内存使用统计
另一个流行的模块是[memory_profiler](https://pypi.org/project/memory-profiler/)
,尽管它不是标准库的一部分。该模块监控内存消耗。
它提供的几个不错的功能是逐行分析(类似于[line_profiler](https://pypi.org/project/line-profiler/)
,另一个很多人喜欢的分析工具)和使用mprof
可执行文件报告一段时间内的内存使用情况。
通过应用装饰器,它可以用来分析模块中的特定函数:
[@profile](http://twitter.com/profile)
def square_ints_for_loop(limit):
"""create list of squared ints using for-loop"""
squared_list = []
for i in range(limit):
squared_list.append(i * i)
return squared_list
要运行分析:
python -m memory_profiler example.py
输出:
memory_profiler 输出
当然,还有许多 Python 库对剖析有用。有些相当复杂,有助于钻取更多的细节。有些在报告上提供了更多的可视化。很好的例子是孔雀鱼 3 号和 T2 号。然而,我不能说我不得不经常使用它们。
有没有你觉得特别有用的工具?或者任何特别有用的情况?
利润还是信息?
机器学习实验权衡:一个框架
如果你是一家公司,你会不断寻求如何获得更多利润。如果一家公司正在寻求扩大或改变其目前的业务(无论是大还是小),一个常见的解决方案是实验。
公司可以尝试改变是否可行;如果变化看起来有希望,他们可以将这种变化融入到他们更广泛的业务中。尤其是对于基于数字的公司,实验是创新和增长的驱动力。
一个常见且相对简单的测试是 A/B 测试。一半用户随机指向布局 A,另一半用户随机指向布局 B,然后在实验结束后,可以对比布局 A 和布局 B 中用户的结果,看哪个布局的表现更好。
通过选择 A/B 测试,你在信息和利润的实验权衡中选择了信息。也就是说,A/B 测试是现存的最根本的科学测试方法;测试对照组和治疗组的想法和测试一样是科学的基础。用 A/B 检验,统计显著性是最值得信赖和严谨的。
(技术上的信息就是利润,只是延迟了,不那么具体,从某种意义上来说,但不一定不那么有价值。不过,目前保持信息或利润的平衡是值得的。)
然而,实验并没有发生在遥远的不相干的星系中——实验影响的是真实的顾客。如果你是一家公司,尤其是一家没有太多资源可花的公司,你会希望将损失最小化——一直都是如此,包括在实验期间。
因此,举例来说,如果一个新的网站布局只有 25%的成功率,而原来的网站布局有 75%的成功率,那么在 A/B 测试中,在测试期间,只有 50%的网站访问是成功的,而原来是 75%。做测试会伤害公司。
(0.5 × 0.25) + (0.5 × 0.75) = 0.5 #success rate in A/B test
事实上,《哈佛商业评论》发现,在谷歌和必应,只有大约 10%到 20%的实验产生积极的结果。鉴于这种相对较低的成功率,公司应该进行这么多这种高信息但也高风险的 A/B 测试吗?
多臂土匪(MAB)测试把旋转放在 A/B 测试上来解决这个问题——A/B 测试可能不是很有利可图。这些 MAB 测试并不是一成不变的,每一组的人口比例都是如此。
也就是说,
- A/B 测试是静态的;在对照组和治疗组中,它们将总是 50% — 50%。
- MAB 测试变化;例如,如果第二组表现得更好,他们可能从 50% — 50%开始,但在测试结束时,比例变成 10% — 90%。
A/B 测试对ε贪婪 MAB(一种 MAB)。x 和 y 代表用户被随机分成的组。该 MAB 发现 y 执行得更好并利用它,但是达到模型中编码的利用限制(即,它不能利用超过某个阈值以确保探索仍然发生)。来源:调整
在这种情况下,如果我们选择 MAB 而不是 A/B 考试,我们既有优势也有劣势。
- 优势:利润。在 MAB 测试中,在上述示例测试的末尾,我们将 40%以上的人口归入表现更好的群体。这表明,在整个测试过程中,MAB“挽救”了大量用户,否则这些用户会流向表现较差的群体。
- 劣势:信息。在 MAB 检验中,最终结果并不是按相等的比例分开的,所以用统计上严格的方法得出结论并不那么简单。事实上,如果实验者想要特别严格,根据情况人们可能甚至不能从实验中得出结论。
例如,假设在整个 MAB 实验中,30%的参与者属于 A 组,成功率为 40%,另外 70%属于 B 组,成功率为 33%。你能在 a)样本大小之间 40%的差距造成的噪音和 b)只有 7%的成功率差异之间做出自信的决定吗?
在这个利润信息框架中比较 MAB 和 A/B 测试是有用的。使用这个框架,人们可以以一种更加结构化的方式来解决使用哪种测试类型的难题。
首先,我们必须认识到 A/B 测试是 MAB 测试的一个子集。让我们从探索/利用的角度,重新定义一个更严格(但仍然相对简单)的 MAB 测试视图。考虑某个数字δ,其中
- MAB 测试是探索性的。也就是说,他们将用户随机发送到对照组和治疗组,以收集更多数据,从而使其在统计上更有意义。
- MAB 测试是 100%δ百分比剥削。也就是说,他们将用户发送到他们知道会表现更好的组,以实现利润最大化。
因此,A/B 测试是 MAB 测试,其中δ= 100 %,它一直在探索,根本没有利用。在这个框架中,我们将 MAB 模式视为探索和开发的混合体。
值得注意的是,这可能会使人误解。MAB 模型所做的探索和开发的数量是动态的,它会改变,但是把这个框架想成“一个纯探索性测试和一个纯开发性测试之间的混合程度有多大?”实际上,δ表示类似于“模型需要有多大的信心来利用?”
在这种情况下,δ越高,就需要越多的统计置信度来做出利用的决定,这意味着它将更加犹豫利用和花费时间探索。另一方面,如果δ较低,模型愿意毫无把握地利用。
回到手头的框架:我们可以开始调整δ来决定勘探(统计意义)和开采(利润)之间的权衡。
让我们举一个例子,其中人口是相同的,但是建模使用不同的δ值。
- 在δ = 100 时,如上所述,这是一个 A/B 测试。虽然利润甚至不是一个考虑因素,但收集的数据在统计上非常重要。
- 在δ = 75 的时候,模型大多在做探索,但也有一点剥削。然而,在文本开始时,这些组可能是 50% — 50%,通过这种小的利用,模型慢慢地向 40% — 60%学习(第二组更成功)。
- δ= 50时,模型在做平等的探索和开采。然而,在文本开始时,组可能是 50% — 50%,而模型相对快速地达到 30% — 70%。
- 在δ = 25 时,模型大多在做探索,也有一点剥削。然而,在本文开始时,这些组可能是 50% — 50%,随着这种大规模开发,该模型迅速向 25% — 75%的方向发展。
- 在δ = 0 时,模型在做纯剥削。然而,在文本开始时,这些组可能是 50% — 50%,通过这种小的利用,模型立即变成 100% — 0%。(无论第一个数据点是什么,纯粹的剥削模型都会遵循,所以在这种情况下,收集的第一个数据点碰巧表明第一组做得更好。)
当然,这些数字是虚构的,但它们是准现实的,我们可以对我们从这个框架中得到的虚构结果进行分析。
作为一名实验者,根据δ值分析结果会使权衡变得更加清晰。你可能会注意到两件事:
- 统计显著性。出于实用目的,当δ = 75 时,40% — 60%组与当δ = 100 时,50% — 50%组在得出具有统计意义的结论方面真的有很大差别吗?然而,当δ = 50 时,问题似乎就出现了,因为组间差异要大得多,并且会引起统计有效性和显著性的问题。
- 利润。具有讽刺意味的是,在某一点上,开发给我们带来了损失,因为它没有做足够的探索,并且正在利用错误的想法。例如,当δ = 0 时,我们的 MAB 测试实际上将 100%的用户发送到错误的组。一般来说,任何低于δ = 50 的值都显得太草率了,因为我们直觉地希望模型花尽可能多的时间去探索,就像它利用从探索中学到的知识一样。
仅基于这两个观察,我们的实验者会得出这样的结论:δ = 75 左右是最好的测试模型。
当然,这些数字并不适用于所有场景,有充分的理由选择δ的高值和低值。
例如,一家新闻机构可能会因为其业务的性质而将眼前的利润置于统计意义之上。A/B 测试的统计意义来源于它所做的探索量,而探索需要时间。考虑到新闻变化很快,当一个测试被充分利用时,新闻已经发生了变化,从探索中学到的知识不再适用。
另一方面,如果一个大公司的实验团队有几个月的时间来开发和运行一个新特性的测试,A/B 测试可能更合适。利润不是一个重要的问题,鉴于大公司通常拥有大平台,某一假设的统计意义非常重要——鉴于测试的结果将决定公司整个受众的界面。该团队可以对结果进行广泛的后处理分析,并在利用之前确定决策。
当然,在为您的实验模型决定δ值时,除了利润与信息范式之外,还有许多其他因素需要考虑。然而,通过利润与信息的镜头思考可以使δ的发现更清晰一些。
感谢阅读!
我最近推出了一个新的播客,ML & DS Papers On The Go。播客的目标是让您在旅途中也能听到最迷人和最新的进展——这是一个赶上最新研究趋势和发展的好时机!
点击 Spotify 查看。请随时联系 LinkedIn,我仍在探索如何浏览播客空间,所以如果你有任何反馈,我很乐意与你联系并听取你的反馈。
用 Python 进行利润率分析
盈利率是一种财务指标,可以洞察一家公司产生收益的效率。
在本帖中,我们将使用 Python 进行利润率分析,比较同行公司的利润率。下面是我们将计算和分析的 5 个不同的相关盈利率:
- 纯利润率
- 毛利
- 营业利润
- 资产回报率
- 股本回报率
如何计算盈利率
在开始用 Python 代码识别同行公司和分析利润率之前,我们将在本节中介绍每个比率。
利润率和利润率
利润率对于了解一家公司将收入转化为营业收入和净收入的效率非常有用。在这个故事中,我们将涵盖三个主要指标:毛利率、营业利润率和净利润率:
毛利率=毛利/收入
营业利润率=营业利润/收入
净利润率=净收入/营业收入
在进行财务分析时,我们需要意识到,在比较同行时,我们可以得出非常有见地的结论。例如,通过比较各公司的净利润率,我们可以确定哪些公司在将销售额转化为利润方面的运营效率更高。
一旦我们在一群同行中找到净利润率表现最好的公司,我们就可以进一步分析毛利率 和营业利润率,以更好地理解为什么表现最好的公司有优势。较高的营业利润率表明一家公司要么拥有极具竞争力的毛利率,要么拥有极具吸引力的营业利润率。也就是说,能够产生一个单位的销售额,减少产生一美元销售额所需的销售成本(更高的毛利率)或更低的营销&销售、研究&开发和 G & A(一般和管理)费用。
或者,为了比较同类公司,我们可以进行时间序列分析,以分析单个公司在不同时期的表现。这种比率分析对于了解公司趋势特别有用。在我上一篇关于用 Python 分析资产负债表财务比率的文章中,我谈到了财务比率的时间序列分析。
股本回报率和资产回报率
ROE 或 ROA 是盈利能力比率,衡量一家公司基于所用股本(ROE)或所用资产量(ROA)创造利润的效率。
我们已经在我的前一篇文章中看到了如何用 Python 分析公司的股本回报率。因此,关于如何解释净资产收益率(ROE) 比率,我们无需赘述。这个财务比率只是让我们知道一家公司基于股东权益能创造多少净收入。
同样,资产回报率(ROA) 表示一家公司使用多少资产来创造净收入。
净资产收益率=净收入/平均总股本
ROA =净收入/平均总资产
太好了,我们已经讨论了足够多的理论。让我们在下一节中看看我们如何首先找到对等公司,然后使用 Python 检索利润率。
用 Python 检索利润率
我们将使用financial modeling rep来检索和处理所需的财务数据。这篇文章中的代码非常简单。以下是脚本执行的高级步骤:
- 查找所有公司同辈。为此,我们将依靠 Financialmodelingprep。该应用编程接口提供了检索所选公司的同行的入口和终点。因此,我们只需要在 url 中传递公司的股票代码。然后,API 将返回一个 Python 列表,其中包含基于行业和市值的所有对等项。
- 循环遍历每只股票,从财务比率 API 端点检索盈利比率。
- 将每个财务比率提取并添加到 Python 字典中。这将有助于能够按公司列出财务比率。
- 将 Python 字典转换成熊猫数据框架
请注意,我们需要 financialmodelingprep 中的一个 API 键,以便代码工作。您可以通过每天 250 个请求获得一个免费的 API 密钥。
财务盈利能力分析
运行代码后,下面的 Pandas DataFrame 将显示使用 Python 进行的盈利能力分析的结果。我们得到一个包含苹果公司同行的财务表,因为这是我们在代码中传递的公司。请随意更改股票代码,以便为其他公司执行类似的盈利能力分析。
同行公司盈利能力分析
包扎
是不是很棒?使用不到 20 行 Python 代码,我们能够比较不同同行公司的盈利能力指标。
我们看到与其他公司相比, Adobe (ADBE)的净利润率非常高。我们可以观察到,Adobe 较高的净利润率主要是由较高的毛利率推动的,相比之下,同行的毛利率约为 87%。这可能是因为 Adobe 的数字产品缺乏实体,而且生产成本相对较低。但营业利润率与其余同行持平。可能是因为 Adobe 在新产品研发上的投入。
下一步,我们可以改进 Python 脚本来添加新的函数。例如,我们可以计算每个指标的平均值,以便将每个公司与该组的平均值进行比较。
请注意,本帖包含的任何信息都将用作财务建议。此处提供的信息可能不准确,因此不应用于投资决策。
原载于 2021 年 5 月 22 日 https://codingandfun.com**的 。
用 Julia 在 5 分钟内编写一个 Lisp REPL
使用 Julia 生态系统解释 Lisp 代码的快速而独特的方法
(图片由作者提供)
介绍
在上一篇文章中,我详细介绍了一个非常棒的 Julia 包,它完全有可能使用 REPL 的解析器创建交互式 REPLs,而无需 Julia 代码进行交互。这个包叫做 REPLMaker.jl,是一个非常酷的工具,它在很多方面改变了 Julian 的面貌。这个包产生影响的一个显著例子是在 Genie.jl 中。Genie.jl 具有交互式复制器,使得处理依赖关系和部署 web 服务器变得非常容易。
虽然这可能不是最好的例子,但在使用 Julia 中的 REPLMaker 创建 REPLs 方面,几乎有无限的潜力。在我介绍该软件的上一篇文章中,我最终创建了一个评估 Julia 表达式的 REPL。如果你想阅读我以前关于 REPLMaker.jl 的文章,你可以在这里查看:
入门指南
因为我们将使用 REPLs,所以我们将对这个 Lisp 示例中的所有代码使用朱莉娅 REPLs。包 lispSyntax.jl 提供了一个字符串宏,用于将 Lisp 风格的代码解析为 Julia 代码,然后对其进行评估,本质上创建了一种新的 Lisp 编程语言,由 Julia 代码进行解释。为了开始,我们需要添加这个包以及 REPLMaker.jl。为了做到这一点,我喜欢用]输入 Pkg REPL,而不是导入 Pkg 并使用 Pkg.add()方法。
julia> ]
pkg> add LispSyntax
pkg> add REPLMaker
下一步是将这些模块及其各自的导出加载到我们的 Julia 环境中。
using LispSyntax, ReplMaker
之后,我们将从 REPL.jl 加载一些东西。不要担心,你不需要添加这个包,因为它包含在 Julia 库中。
using REPL: REPL, LineEdit
最后,我们还需要一个不是从 LispSyntax 导出的模块,名为 ParserCombinator。我们可以称之为 LispSyntax。ParserCombinator,但是直接导入将使它更容易调用。
using LispSyntax: ParserCombinator
现在我们要获取我们的解析函数。这存储在别名为“lisp_eval_helper”的 LispSyntax 模块中。
lisp_parser = LispSyntax.lisp_eval_helper
这个函数将成为解析器,接受我们传递的 REPL 参数并生成返回。
REPL
现在我们有了一个 Lisp 解析器,我们需要一种方法来评估语法,以避免得到令人困惑的返回。如果我们不完成这一步,那么语法不好的代码很可能会抛出一个非常详细的错误,这个错误在解释函数的 Julia 代码中有所涉及。这使得在 Lisp REPL 中工作时很难追踪问题和错误。为了做到这一点,我们将使用 LispSyntax。ParserCombinator . parser exception,以便从解析器获得一个返回,LispSyntax.read()评估解析器是否能够读取传递的代码。
function valid_sexpr(s)
try
LispSyntax.read(String(take!(copy(LineEdit.buffer(s)))))
true
catch err
isa(err, ParserCombinator.ParserException) || rethrow(err)
false
end
end
我们当然可以使用 try/catch 块来实现这一点。如果解析器不能读取传递的缓冲区,那么传递的内容很可能有错误。这个过程的最后一步是用我们的解析器和错误捕捉器函数作为参数初始化一个新的 Lisp REPL:
initrepl(lisp_parser,
valid_input_checker=valid_sexpr,
prompt_text="LISP > ",
prompt_color=:blue,
start_key=")",
mode_name="Lisp Mode")
现在,我们可以使用这个新的朱利安 Lisp REPL 按下)键!现在你可以在 Julia 语言中尽情地列举了!
结论
Julian 包生态系统有许多非常酷的宝石,使包探索成为一个不断的旅程,展示编程是多么令人兴奋。这里我们看到,只需几个步骤,利用 Julia 的生态系统,使用 Julia 的垃圾收集和开源 Lisp 解析器,在几秒钟内编写一个令人惊叹的程序是完全可能的。更令人兴奋的是它的增长速度!有这么多有趣的项目,人们可以用现有的工具来做,比如这个,只需要几分钟就可以完成!感谢您的阅读!
工程直观计算机视觉
动手教程,比人工更智能
我们如何才能创建一个像人类一样做出决策和预测的图像分类系统?
图像分类是机器学习、数据科学和人工智能的最热门领域之一,通常用于测试某些类型的人工智能算法——从逻辑回归到深度神经网络。
但是现在,我想把你的注意力从这些热门技术上移开,问我们自己一个问题:如果我们人类看到一个手写字符的图像,或者一只狗或猫,我们的大脑如何直观地分类不同类型的图像?以下是图像中数字的示例;“2”、“0”、“1”和“9”。
在上面的数字例子中,我们的大脑如何区分底部的 1 和 9 呢?嗯,直觉上,我们的大脑对 1 的样子有一种“心智模型”,对 9 的样子也有一种心智模型。对于一个数字的新图像,我们的大脑可以根据每个心理模型来比较这个数字,并决定它是 1、9 还是完全不同的手写字符。事实上,我们的心智模型不仅仅可以分类:它可以生成相似数据的新实例。第一次给小孩子看数字,这样他们的大脑可以形成一个心理模型,我很肯定他们可以学会画出新的数字,比如 1 或 9,或者上面照片中的其他数字。
现在这种类型的决策、分类,与你在摄像机、医院等世界各地部署的流行人工智能视觉系统中看到的截然不同。——那些依赖于卷积深度学习的东西——看看这个视觉演示。虽然卷积神经网络(CNN)的卷积层可能类似于我们眼中视觉系统的特定部分根据作者的说法,深度 CNN 网络的实际决策主要来自网络前馈“最终完全连接层”中成千上万的调谐权重。
顺便说一句,如果你没有听说过神经网络或者不熟悉神经网络是如何用于图像分类的,我绝对推荐 3Blue1Brown (3b1b) Youtube 频道上格兰特·桑德森的这个视频;他的阐述和解释非常精彩。
但是我们将要设计的决定和分类数字的 ML 系统将不会像人工神经网络那样包含成千上万的权重和参数。
在我们继续之前,我们需要弄清楚数字在计算机中的“心智模型”是什么样的。
什么是大脑相对于计算机的“心智模型”?
简而言之,大脑中的实际心理模型——嗯,我们仍在研究中。关于大脑,我们还有很多不了解的地方,神经科学家、认知科学家和其他人有很多共同之处。
然而,越来越多的理论认为大脑中的心智模型在计算上与 T10 相似。计算神经科学家 Karl Friston 的这篇文章更多地讨论了大脑如何“调用一个内部的、精神的世界生成模型”来推断和预测从身体其他部分(包括视觉系统)接收到的感觉信号背后的原因的计算理论。在这个理论中,大脑基本上必须根据它拥有的心理模型来推断感觉的原因,关于哪些可能的原因导致可能的身体感知。弗里斯顿还提到,像这样的计算模型已经预测了广泛的真实生物生理和行为现象。我省略了一堆细节,如果你感兴趣,可以看看这篇文章:)
在我们的机器学习案例中,谢天谢地,我们不必处理所有的身体感觉——只是屏幕上的 28x28 黑白图像。但这仍然是一个具有挑战性的问题!——否则计算机视觉早就“解决”了。所以现在的问题仍然是:在某种意义上,我们如何创建一个“心理生成模型”,让计算机能够将其作为一个对象保存在内存中?
作为“分布”的生成模型
之前我说过一个“心智模型”,或者说一个生成模型,如何让你基本上创建类似数据的新实例。如果我在比如数字 2 上训练一个生成模型,那么计算机将能够画出 2 的新实例。拥有一个创建这些实例的模型就像一个粗略的蓝图:它允许你填充细节并创建房屋、建筑等的不同实例。这个“粗略的蓝图”被称为分布,因为,直觉上,你基本上是在生成新数据的集合——而分布只是集合的另一个词。专业术语称之为“概率分布”(别担心,我不会重温统计职业的恐怖!)因为在您生成的集合中,有些样本比其他样本更有可能。例如,如果计算机正在生成“2”的新实例,那么顶部缺少“弯钩”的 2 的样本将被计算为“不太可能像”2,而不是如果它有 2。不太可能只是意味着模型产生的 2 比通常的 a 2 看起来要少。
用机器学习的语言来看待生成模型的另一种方式是,它们创建“聚类”的模型——它们帮助将数据聚类到特定的区域。这将被称为“无监督学习”——在没有任何训练标签来教导机器的情况下推断数据中的结构。**
美国宇航局在 Unsplash 拍摄的照片。看到上图中城市灯光的某些“簇”是如何聚集在一起的了吗?生成模型将能够为用户提供一个“蓝图模型”,显示城市和农村地区的光照分布情况。
既然我们知道生成模型是分布,我们首先需要做的是看看如何在数字类型(0,1,2,..9).在 Python 的 sci-kit learn 库中,有一个名为密度估计模型的函数可以帮助我们。
分类的最后一步:推理
现在,我们的计算机为每种类型的数字调用它自己的内部生成“心智模型”,并生成新类型的图像——这肯定很酷!—但我们仍然需要完成我们的第一个目标:分类和预测它以前没有见过的数字的新图像。
在我们的直觉分类器中进行分类和预测的关键叫做推理。实际上,我们的大脑一直在这么做!想象一下下面这个问题:
"你今天晚餐只能吃绿色蔬菜了!"绿色指的是什么?
嗯,绿色是一种颜色,而且绿色以复数形式使用,可以推断它指的是绿色物体——但是什么物体?我们的大脑推断这些“物体”是指绿色蔬菜,如花椰菜、菠菜、甘蓝等。因为还有什么绿色的东西可以煮着吃?我们的大脑很容易一直进行这样的推理,但如果没有数十亿个数据点、参数和高能量使用,让自然语言处理(NLP)计算机系统进行这样的推理是非常困难的——这是人类大脑并不真正需要的。幸运的是,我们现在没有处理 NLP 推理,但不要误解我——视觉类人推理也是人工智能中需要克服的一个长期挑战。
总之,回到视觉。我们希望我们的计算机根据它对数字外观的“心智模型”(分布)来推断新的数字将会是什么。这样的推论会是怎样的呢?假设我们只处理两种类型的数字。一个 2 和一个 7。假设计算机已经创建了它认为 2 和 7 的 28x28 图像的分布。当一个新的未见过的图像出现时,它是如何推断的?计算机问自己:在哪个心智模型下,图像更有可能:我的 a 2 模型,还是 a 7 模型?如果它更有可能在 a 2 下,那么我将决定该图像是 a2;否则我决定 7。这样,实际的分类就变成了 if-else 条件语句,这种语句在从 Python 到 Java 再到 c 的计算机科学语言中非常普遍。有趣的是,这个决定是非常程序化的!
如果你想了解更多关于概率分布的知识或者熟悉它,我推荐来自 Youtube 频道“3Blue1Brown”的 Grant Sanderson 的这个视频,以及关于概率和统计的 KhanAcademy 视频!
所以…让我们开始实施吧!在我们开始之前,请稍作休息!
数据导入
首要任务是从某处导入手写数字数据。为此,TensorFlow 和 Keras(运行在 Tensorflow 之上的高级语言)有一个组织良好的数据集集合,而 Keras 恰好有手写数字。MNIST 数据集可以在这里找到(它的许可证是:CC BY-SA 3.0)。要导入数字数据集,我们只需使用代码:
*from keras.datasets import mnist
(x_train, y_train), (x_test, y_test) = mnist.load_data()*
这里有一个 Keras MNIST 训练图像数据的样本
来自 Keras 的 MNIST 数据集每个数字类有 6,000 个训练样本,我们有 10 个类(因此总共有 60,000 个训练数字),但我们不会使用那么多数据。为什么?首先,为了表明原则上我们可以不用那么多数据来创建这些生成模型。但是,在现实世界中,60,000 个训练数据点可能并不存在,每个类可能只有几十或几百个训练数据。所以我们要做的只是从训练组中随机选择一组,比如说 6000 个数字。这给了我们每个数字类大约 600 个训练数字样本。那已经是减少 90%* 的原始数据了!因此,如果我们可以基于这个更小的训练集来训练每个数字类的生成性“心智”模型,我们将更有数据效率。*
我们可以随机抽样,用下面的代码创建一个新的更小的训练集:
*np.random.seed(10)
random_indices = np.random.choice(60000, size=6000, replace=False)
random_x_train = x_train[random_indices, :]
random_y_train = y_train[random_indices]*
特征提取
为了创建模型,我们首先必须做所谓的“特征工程”(或者“特征选择”,如果您处理的数据中的特征是表中的列)。我们为什么要这么做?嗯,我们必须意识到一些事情:这些图像存在于一个非常非常高维度的空间中。每幅图像有 784 个像素,所以作为一个向量,每幅图像都存在于同一个 784 维空间中。就此而言,我们甚至不能在 4D 空间中绘制图形,所以放弃在 784D 空间中绘图吧!
处理高维数据可能非常困难,高维空间会发生一些奇怪的事情——例如,在 4D 和更高的地方,球体开始变得“尖锐”:如果你想体验一下,请查看 Youtube 频道 Numberphile 的视频。你可能也听说过这种现象,称之为机器学习中的“维数灾难”。
如果您有以要素为列的表格数据,有一些统计方法可以根据统计资料(如相关性和分布)预先选择一组较小的要素。如果你没有有表格数据,一般来说有几种方法可以做特征提取。对我们来说幸运的是,有一种提取特征的神奇方法——在 ML 行话中也称为“降维”——我们可以保存几乎所有关于数据的信息,然后一路走到 2D!它被称为 UMAP-y你可以在这里阅读 Python 用户手册-它是由数学家利兰·麦金尼斯在 2018 年创建的。UMAP 代表“一致流形逼近和投影”——如果这是一个口,不要担心。(如果你想要一个解释,看看 Letitia 博士的这个视频)我可以用最简单的方式解释:UMAP 算法试图根据数据的聚集程度用点和线连接 784D 图像数据(有点像社交网络!);然后使用拓扑将整个图投影,到一个低维空间。如果你想将数据点可视化,你必须一直向下投射到 2D 或 3D 空间,就像我下面所做的:
图片来源:Raveena Jayadev。“组件 1”和 2 代表 UMAP 组件。如果你听说过的话,这基本上与“PCA”中用于降维的“主成分”是一回事。
*mapper = umap.UMAP(n_components=2, random_state=40).fit(random_x_train)
umap_components = mapper.transform(random_x_train)*
你在这里看到的星团之间的距离反映了这样一个事实,以某种具体的方式,像 0 和 7 这样的数字与 7 和 9“很远”或“相当分开”。当然你可能会说“Raveena!每个数字都是分开的”这是事实,但是当手写数字时,有时笔画会令人困惑。这就是 UMAP 试图压缩到 2D 的基本结构,这样我们就可以亲眼看到了!
**(免责声明:虽然 UMAP 分量在散点图中显示出清晰的聚类,但是分量轴本身并不是人类能够真正解释的。但是,决策预测过程仍然是。)
学习“心理”生成模型
现在是时候学习“心智模型”了——计算机将从数据中学习的每一类手写数字的生成概率模型,以便在分类时做出有根据的推断。为了创建实际的模型,我们将在 sci-kit learn 模块中使用一种称为内核密度估计(KDE)* 的技术。想象一组数据点,其中一些点彼此靠近,一些点更加孤独和孤立。估算过程如下图所示:*
图片来源:维基百科。底部的黑色刻度线是集合中的数据点,红色虚线是放置在每个点上的小钟形曲线。KDE 将钟形曲线平均成蓝色实线,从而得到大致的形状。(链接:https://en.wikipedia.org/wiki/Kernel_density_estimation)
一旦我们了解了每个数字的生成性“心理”模型,我们就可以将这些模型绘制在一起,并使用 10 种不同的颜色来表示数字 0-9 及其模型形状。下面的每个“形状优美的斑点”代表计算机从每个数字的数据中学习的内部模型,类似于人类大脑对数字应该看起来像什么的内部模型。
图片来源:Raveena Jayadev
现在我们的计算机已经有了每个数字的生成模型,是时候做…推理了!在这个项目中,我想强调两件事:1)我们可以编写图像分类器,像人脑一样用内部“心理”模型进行推理和决策,2)这使得人工智能系统易于排除故障。在一个黑盒深度人工智能系统正在世界各地部署并经常歧视边缘化人群(Zachary Hay 的文章走向数据科学)的时代,很难排除这些系统可能做出的歧视性预测——如果你不能排除故障,这个系统在我看来还不如危险。
不过,让我向您展示我们如何检查我们在本文中创建的系统。下面是我们的系统将如何推断和预测一个看不见的测试图像的内容的总结代码:
*def decision(sample, mental_models):
... (summary)
**if likelihood_ratio <= 3.0:**
print("There's very little evidence that my first choice, is better than my second choice."
**return "I refuse to decide!"**
**elif (likelihood_ratio > 4.0) and (likelihood_ratio <= 10.0):**
print("There is a bit of evidence that my first choice is better than my second choice")
**return "maybe digit {} over {}"
else:** print('There is strong evidence that my first choice, is better than my second choice.'
**return [confident machine choice]***
(机器预测代码看起来就像一个 if-else 语句(如上所述),机器可以用不同的置信度进行预测,这非常直观!)
现在假设我们给这个人工智能系统一个 3 的图像,相反它预测的是 5。我们如何对模型进行故障诊断,以了解它是如何出错的?好吧,跟我来:
- 机器从 if-else 语句中决定,在其生成性“心理”模型下,5 比 3 更有可能来自图像数据。
- If-else 决策是纯逻辑,因此这意味着问题不在于决策,而在于统计心智模型或特征提取。我们可以要求机器显示心智模型和特征提取,就像我上面做的那样。从那里我们可以分析模型的缺点,并看看如何调整它们。
在这两个步骤中,我们已经确定了如果这个人工智能系统出错,它可能会在哪里出现问题,相比之下,世界上许多连接到监控技术的深度学习系统甚至不能以这种方式进行故障排除,这导致了潜在的危险人工智能。
我希望你,读者,从这篇文章中带着这样的希望离开:1)我们可以创建人工智能系统,它可能像人类一样直观地做出决策,2)如果人工智能模型出错,我们可能可以对其进行故障排除,并回溯错误直到系统的内部部分,并了解应该修复什么,应该废弃什么。
不可否认,你可能会告诉我:“Raveena,UMAP”特征提取看起来不太直观!”是的,我知道。不幸的是,这些特征不是人类可以解释的。然而,有一种方法可以修改这个系统,使它可以被解释,尽管是以不同的方式。如果你有兴趣知道,麻省理工学院教授 Joshua Tenenbaum 在 2015 年发表的这篇文章谈到了使用与我们的概率生成模型非常相似的内部“心智模型”来制作人工智能手写字符分类器系统。事实上,它是如此直观和数据高效,它只从 30 个数据点中学习,并模仿人类手写符号出奇地好。在我看来,这是人工智能未来最具革命性的创造之一。
顺便说一下,如果你想看完整的实现细节,这里有我的代码的链接:我的 Github 是公开的,可以免费访问:)
参考文献
- 托马斯·帕尔(Thomas Parr),卡尔·弗里斯顿(Karl Friston),《推理的解剖:生成模型和大脑结构》(2018),《计算神经科学前沿,2018。**
- S.R. Kheradpisheh 等人,深度网络可以在不变物体识别中类似人类的前馈视觉(2016), Nature 。
- 约书亚·特南鲍姆(Joshua Tenenbaum),布伦丹·莱克(Brendan Lake),通过概率程序归纳进行人类水平的概念学习(2015),科学。
数据科学编程:如何在几周内学会足够的知识
我希望得到更多,但我只得到几个星期。我是这样做的。
图片来自 Pixabay
这是我一直在等的邮件。这是我收件箱里一个熟悉的名字,我欣喜若狂。这是我一直努力工作的目标。我打开了邮件...我瞪着眼。
那天早些时候,我交了第一份数据科学实习项目。
“Arunn,这是你要做的关于酒店房间定价的项目。请看看问题陈述文档,我已经邮寄了一些启动代码。请一天假,如果有任何问题,请告诉我。”
“当然,我马上就开始。”
我打开了邮件。起始代码的扩展名以一个不熟悉的字母结尾。什么?
我打开代码脚本,以确保无论如何。
它慢慢地被理解了。但是所有这些 YouTubers 告诉我 Python 是最流行的编程语言。为什么现在休息?
我希望我能撕开那些 YouTubers。我已经花了一年时间学习 Python,而我现在只有几周时间?
在本文中,我将为您提供足够的资源,回答您迫切的问题,并展示如何在几周内学习足够的数据科学编程。
数据科学的最佳语言?
也许如果我像其他初学者一样有选择的话,我也会争论 Python 和 R 的问题。我没有那种奢侈。反正你一定会认识到这个道理的,我给你省点时间。
"不管你选择哪种语言,你最终都会两种都学会。"
好吧,我明白了。你还是想知道先学哪一个。
如果你想了解数据科学的第一语言,我想先了解你。
你是来自学术/统计/数学/经济学背景吗?
如果你的回答是肯定的,那么 R 编程语言最适合你。如果没有,你选 Python。这是我的经验法则。语言之间没什么好比较的了。
我理解你的担心:你想提前做出正确的决定,而不是在旅程的后期后悔。我也像你一样,直到我别无选择。准备好接受所有的语言——数据科学不仅仅是工具。
选择一种语言很容易。在学习编码方面,你需要更多的帮助。我们得到了什么?
python——你的日常语言
Python 是一种通用编程语言,这意味着它可以用于数据科学之外的多个领域。
大多数程序员从 Python 开始,因为它相对更容易选择。我建议从学习变量、循环、函数、逻辑运算符和基本数据结构等基础知识的课程开始。
我们来看看一些有用的资源?
- 密歇根大学Python for Everybody Specialization:我从这个开始,这是我见过的关于 Python 的最全面的课程。你看看评论就知道我在说什么了。从这个开始。
- Python for Data Scienceby freeCodeCamp:这个免费课程概述了你需要了解的关于数据科学的一切,包括 NumPy、Pandas 等等。
- Python for Data Scientistby Data camp:我用这个通过动手练习来固化自己的 Python 技能。这可能是你最后的触摸。
不要陷入从越来越多的课程中不断学习的陷阱;你不能那样做。记住你只有几个星期的时间了!
这比你想象的要容易
r 的学习曲线很陡,但是一旦你掌握了窍门,它就很简单了。开发人员在构建这种语言时已经考虑到了非编程受众。
让我们记住,我们只有几周的时间,并快速查看一些资源来开始?
- 约翰·霍普金斯大学数据科学基础 :毫无疑问,这是你能找到的最好的课程。这是 Coursera 上发布的第一批课程之一,现在仍然很好。我开始学习这门课程,并在一周左右的时间内完成了它。我建议你去看看他们提供的练习。
- DataCamp 的 与 R 的数据科学家:他们有条理地列出了课程,你可以用它在 R 中慢慢提升技能,我在上一个课程之后用过这个。尽管如此,这些课程最棒的地方还是实践练习——这是最好的学习方式(稍后会详细介绍)。
仅此而已;这对于掌握这门语言来说已经足够了。让我们继续讨论“宝石”吧?
SQL——被遗忘的瑰宝
人们在 Python 和 R 之间争斗,却忘记了对数据最重要的语言——SQL。
我在计算机科学学位期间学习了 SQL,并不需要在这方面花费太多时间。但是如果你来自不同的背景,理解数据的语言是至关重要的。
一些数据科学职位只测试你的 SQL 知识。如果我必须从头开始学习,我会使用 Udacity 的 SQL 教程,它是免费的,全面的数据分析,涵盖了您需要的一切。
有一件事比从课程中学习更重要。那是什么?
除非你这样做,否则你的编程将会失败
如果你想通过观看以上推荐的课程,你会成为一名程序员;你在等待一些失望。
你需要练习编码。
我怎么强调都不为过。这是大多数初学者在学习编程时犯的头号错误。用世界第一位 4x Kaggle 特级大师 Abhishek Thakur 的话说:
“不编码,就学不会。”
这就是你如何在几周内学会编程
你选择一种语言。你通过在线课程学习。你在网站上编码并试图解决问题。你失败了。你感觉被打败了。
反正你也很想学。
你看解决方案。你尝试下一个。你接受 30 天的编码挑战。你变得始终如一。你对这种语言感到舒服。
你得到了实习机会。你很兴奋,有胜利的感觉。你收到一封邮件。你得到一个不同编程语言的项目。你盯着看。你系好安全带,重新做一遍。
你以前做过。你现在要一遍又一遍地重复。
你现在是冠军了。
作为披露,本文中使用了一些附属链接来分享我使用过的最好的资源,并且不收取额外费用。
要获得更多关于进入数据科学、有趣的合作和指导的有用见解,请考虑 加入我的电子邮件好友私人列表 。
使用 dplyr 编程
在 1.0 版本中,你可以用新的方式编写函数
我们的超能力!图片作者。
如果你是 R 的新手,那么使用 dplyr 编程的文档是最好的参考……但是如果你不是,让我们看看如何从旧的方式升级你的编程。
如果您熟悉使用sym
并从标准形式转换为非标准形式,下面的进度将向您展示如何替换(和扩展)您的代码。应该大多是查找-替换!如果你有一个函数,它接受一个字符向量并使用一个_at
动词,看看相应的旧选项(这里是带有_at
的选项 1)之间的区别,并看看底部的“超级版本”是如何改变的。
老办法 1:使用*_at
动词
这是编写 dplyr 函数的老方法:
max_by_at <- **function**(data, var, by="") {
data %>%
group_by_at(by) %>%
summarise_at(var, max, na.rm = TRUE)
}
让我们试一试:
starwars %>% max_by_at("height", by="gender")
starwars %>% max_by_at(c("height", "mass"), by="gender")
starwars %>% max_by_at(c("height", "mass"), by=c("sex", "gender"))
这很有效,但是对 env 变量无效:
testthat::expect_error(starwars %>% max_by_at(height, by=gender))
testthat::expect_error(starwars %>% max_by_at("height", by=gender))
testthat::expect_error(starwars %>% max_by_at(height, by="gender"))
旧选项 2:使用*across*
这适用于字符和字符向量,但不适用于 env 变量。使用across
是使用*_at
的替代,它具有相同的功能:
max_by_across <- **function**(data, var, by="") {
data %>%
group_by(across(by)) %>%
summarise(across(var, max, na.rm = TRUE), .groups='keep')
}
这是如何工作的:
starwars %>% max_by_across("height", by="gender")
starwars %>% max_by_across(c("height", "mass"), by="gender")
starwars %>% max_by_across(c("height", "mass"), by=c("sex", "gender"))
这对向量和字符串都有效(记住,R 中的所有东西实际上都是向量)。但是我们不能使用环境变量:
testthat::expect_error(starwars %>% max_by_across(height, by=gender))
testthat::expect_error(starwars %>% max_by_across("height", by=gender))
testthat::expect_error(starwars %>% max_by_across(height, by="gender"))
旧选项 3:通过sym
从字符转换为环境变量
max_by_1 <- **function**(data, var, by="") {
data %>%
group_by(!!sym(by)) %>%
summarise(maximum = max(!!sym(var), na.rm = TRUE))
}
它不适用于传入 env 变量:
testthat::expect_error(starwars %>% max_by_1(height))
testthat::expect_error(starwars %>% max_by_1(height, by=gender))
它对字符串有效:
starwars %>% max_by_1("height")
starwars %>% max_by_1("height", by="gender")
但是,它对列表不起作用(所以,它没有across
那么通用):
testthat::expect_error(starwars %>% max_by_1(c("height", "weight")))
testthat::expect_error(starwars %>% max_by_1("height", by=c("gender", "sex")))
戴牙套更好
看看这个改进的版本!
它适用于 env 变量,所以我们可以像使用非标准 eval 的 dplyr 函数一样使用它,也可以传入sym
变量。
max_by_2 <- **function**(data, var, by) {
data %>%
group_by({{ by }}) %>%
summarise(maximum = max({{ var }}, na.rm = TRUE))
}
它对 env 变量有效!
这很酷:
starwars %>% max_by_2(height)
starwars %>% max_by_2(height, by=gender)
它不适用于现成的字符串:
starwars %>% max_by_2("height")
starwars %>% max_by_2("height", by="gender")
我们可以用sym
来解决这个问题:
starwars %>% max_by_2(!!sym("height"))
它不适用于环境变量列表:
starwars %>% max_by_2(c(height, mass))testthat::expect_error(starwars %>% max_by_2(height, by=c(gender, sex)))
超级版本
我们将使用across()
来允许字符串、环境变量列表,甚至字符串列表。默认的by=()
变成一个空列表,我们简单地用across()
包装{{}}
:
max_by_3 <- **function**(data, var, by=c()) {
data %>%
group_by(across({{ by }})) %>%
summarise(across({{ var }}, max, .names = "max_{.col}", na.rm = TRUE), .groups='keep')
}
它适用于环境变量:
starwars %>% max_by_3(height)
starwars %>% max_by_3(height, by=gender)
它适用于字符串:
starwars %>% max_by_3("height")
starwars %>% max_by_3("height", by="gender")
它适用于 env 变量列表:
starwars %>% max_by_3(c(height, mass))
starwars %>% max_by_3(height, by=c(gender, sex))
starwars %>% max_by_3(c(height, mass), by=c(gender, sex))
它适用于字符列表:
starwars %>% max_by_3(c("height", "mass"))
starwars %>% max_by_3("height", by=c(gender, sex))
starwars %>% max_by_3(c("height", "mass"), by=c("gender", "sex"))
现在您已经看到了如何使用 dplyr 编程的新功能编写一些非常灵活的函数。尽情享受吧!
逐渐接近卡格尔
使用表格游乐场系列开始 Kaggle 竞赛
布雷特·乔丹在 Unsplash 上的照片
泰坦尼克号比赛是大多数人对 Kaggle 的第一次尝试。它有一个奇妙的资源档案,但如果你正在寻找更新、更快和更先进的东西,让你熟悉 Kaggle 比赛,那么表格游乐场系列是一个很好的开始。
表格游乐场系列(TPS)
TPS 是一系列带有简单表格数据集的月度竞赛。它有一个初学者友好的设置,以帮助 Kagglers 适应 Kaggle 比赛。
它提供了一个端到端的体验,让您快速建立探索主流比赛的信心。
新的!大部分的讨论、代码和模型都与今天的机器学习非常相关。老比赛中经常发现的过时的、不重要的资源被自动过滤掉。
快了! 30 天。这是一个将它作为一个有截止日期和可交付成果的项目进行工作的好方法。在某种程度上提供了真实比赛甚至行业工作的体验。
数据科学项目就应该这样。
如果你曾经推迟、拖延或害怕尝试 Kaggle 比赛,现在是一个把它抛在脑后并开始的好时机。我的字面意思是现在是。
渐进工作
学习。去吧。迭代。(图片由作者提供)
继续练习你所知道的。不断尝试新的东西来学习。
以下是逐步完成 TPS 竞赛的框架示例。随意选择你自己的项目或者根据你自己的兴趣和技能调整方法,因为它最适合你。为了方便起见,比赛的顺序是按时间顺序选择的,但是你可以使用你自己的顺序,因为它们都是独立的。
项目 1:TPS-2021 年 1 月
https://www.kaggle.com/c/tabular-playground-series-jan-2021
一些比赛可能有更多的细节,所以养成阅读所有信息和标签的习惯。
概述页面底部提到了积分/等级标准。像 TPS 这样的初学者比赛一般不给分,因为它们主要是为了学习。
2:验证数据文件 阅读数据描述快速查看实际数据:训练,测试,样本 _ 提交。检查所有字段和描述是否匹配。
了解提交文件的格式。重新考虑如何使用提交文件来计算评估指标也是一个好主意。
3:设置您的环境 将数据集下载到您的本地机器或使用免费代码资源,如 Kaggle Notebooks 和 Google Colab 。
大部分比赛会直接让你从比赛的代码标签启动笔记本。
4:探索数据
探索数据。了解数据。
花时间在这上面。
这将是一个持续的过程,贯穿于任何项目的整个生命周期,所以要做好准备,不断地分析数据,每次都能学到更多。
一般来说,在进入公开共享的笔记本和讨论之前,先自己探索数据是个好主意。
5:阅读论坛
通读竞赛论坛。
那里有大量有用的信息和有趣的讨论。确保您了解最新情况并知晓它们。
如果你不介意通过电子邮件获得更新,你应该关注论坛。或者不时查看新的帖子和评论。
6:阅读笔记本
通读并理解公共笔记本。
这是你能得到的最好的资源之一。将会有一个初学者笔记本,这是一个开始你自己的代码的好地方,在那里你可以随着你的进步而改进和更新。
两种最受欢迎的笔记本是 EDA /分析/信息笔记本和建模/基准测试/提交笔记本。不要犹豫叉抄他们。给任何你喜欢或觉得有用的投稿投赞成票是一个很好的做法。
7:构建基线模型
构建一个简单的基础模型。
拥有一个非常基本的端到端模型,无论是来自您自己的代码管道,还是来自初学者笔记本或公共笔记本,都是很好的选择,这样它就成为了您可以进行量化改进的起点或分数。
通常有一个基线模型,它是简单的试探法或目标变量平均值的集合,不一定需要是机器学习模型。
8:提交
布丁的证明是当你最终使用一个模型对测试数据进行预测并提交给 Kaggle 排行榜时。
投降吧!成为一名卡格勒。
任何你不确定或不明白的事情,你需要做的就是问。Kaggle 社区 24 小时都很活跃,总会有人帮你解决问题。
项目 1 计划(图片由作者提供)
项目 2:TPS-2021 年 2 月
1:迭代项目 1
做你在前一个项目中做的所有事情。跳过与你无关或你不感兴趣的东西。
2:验证框架
验证你尝试的一切。
建立一个强大的验证框架是多年来 Kaggle 上许多成功解决方案中最常见的主题。
花时间建立验证管道,并在本地和公共排行榜上进行测试,以了解其可靠性。有时你甚至可能需要多种验证策略,但每个比赛都不一样。
3:数据清理
清理数据。记住 GIGO:垃圾进,垃圾出。
回到原始数据集,将它们准备成最干净的形式。不同的数据集,有时是不同的模型,需要不同种类的预处理和转换。
使用验证分数测试和验证清理转换。
4:特征工程
创建特征。这是数据科学有趣的部分之一。
新功能可以极大地帮助提高模型性能。不同类型的功能可能适用于不同的模型。
狂野一点。努力实验。尝试尽可能多的功能和想法,并使用验证分数不断测试它们。通常,给你带来最佳性能的特征空间是一些原始特征和一些工程特征的混合。
推动自己从单一模型中获得最佳性能。
5:误差分析
确定模型能够很好预测的验证观察值和模型失败的验证观察值。想想为什么,你能做些什么。调查一下。
分析模型错误是机器学习工作流程中非常容易被忽视的一部分,但获得添加新功能的想法可能至关重要。
项目 2 计划(图片由作者提供)
项目 3:TPS-2021 年 3 月
https://www.kaggle.com/c/tabular-playground-series-mar-2021
1:迭代项目 2
跳过与你无关或你不感兴趣的东西。
2:提高可视化效果
提高你在 Kaggle 上的可视化效果和演示效果。
虽然在 Kaggle 上你不会被直接评估,但在处理行业问题时,你可能需要向商业利益相关者展示你的工作,这是很有用的。
当然,这将有助于展示您的贡献,并从社区获得反馈。所以不要回避。
提升技能的最好方法是学习获得高度评价的 EDA 笔记本,并学习如何构建出色的可视化。
3:发布你的 EDA 笔记本
让 Kaggle 成为如此精彩平台的是 ka ggle 人和社区。成为它的一部分。投稿。
尝试体验一下写一个 EDA 笔记本需要什么,并让其他人提供反馈。你得到多少票并不重要。每个人都要从某个地方开始。而每个人都是从出版自己的第一本笔记本开始的,不是吗?
分享见解
如果你在数据中发现一些有趣的东西,或者如果你想分享一个有趣的见解,或者报道一个问题,或者写些什么,你将永远拥有一批读者。投稿。
论坛上的帖子。或者写点评论。开始与社区互动。你分享你的工作越多,你学到的就越多,越多的人会帮助你。
项目 3 计划(图片由作者提供)
项目 4:TPS-2021 年 4 月
https://www.kaggle.com/c/tabular-playground-series-apr-2021
1:迭代项目 3
做你在之前项目中做过的所有事情。跳过与你无关或你不感兴趣的东西。
2:探索模型
这是你试验和构建大量模型的机会,以找出最有效的模型。这是该领域的奇特之处,但如果你认为这就是机器学习的全部,那就再想想吧。在完成三个完整的项目后,您正在处理它。
阅读和理解不同模型的内部工作原理非常重要,这样您就可以在数据集上优化它们并充分利用它们。这是获得在真实数据集上实现这些的实践经验的最佳方式,大多数最新的模型将在 Kaggle 上讨论。
3:组合
组合模型。没有一种模式是完美的。
有许多方法可以将多种多样的模型结合起来,几乎总能带来更稳定的预测和更好的性能。
学习和建立许多不同的模型,并在开始集成之前,首先将它们优化到最佳的功能和超参数集。
从混合模型开始,然后学习堆叠模型。
项目 4 计划(图片由作者提供)
学习。去吧。迭代。
虽然其中一些可能看起来很简单,但需要时间来熟练掌握。所以要多多练习。许多术语和任务需要您阅读和研究,这应该作为它们的实现和实验的一部分来完成。
没有编码的阅读是不好的。
编码不理解是不好的。
它们都需要齐头并进。
没有什么是一成不变的。每个数据科学家都会经历自己独特的旅程。让它变得愉快。
项目:客户流失的建模和预测
你刚刚被聘为数据科学家
来源:https://unsplash.com/photos/lvWw_G8tKsk
目录
1。简介
2。数据角力
3。探索性数据分析
4。建模
5。参考文献
1。简介
场景:
你刚刚被聘为数据科学家。银行的一名经理对数量惊人的客户退出信用卡服务感到不安。你被聘为数据科学家,预测谁将离开他们的公司,这样他们就可以主动去找客户,为他们提供更好的服务,并将客户的决策转向相反的方向。一名数据收集员将这些数据交给你进行数据分析,并希望你检查我们数据中的趋势** & 相关性。我们想做模型&预测谁将离开我们的公司
注:退出标志是我们必须为其创建预测模型的离散目标变量,但我们也将使用信用限额进行另一个连续预测器的建模示例演练。**
目标:
- 通过及早发现这些特征,防止潜在客户流失/周转/流失的损失;我们可以防止进一步的破坏
- 预测客户是否会流失公司。这是一个二元结局。
正 (+) = 1,客户留在公司
负 (-) = 0,客户离开公司 - 用分类模型 &做实验,看看哪一个产生最大的精确度。
- 检查我们数据中的趋势 & 相关性
- 确定哪些功能对客户来说最重要
- 想出一个最符合我们数据的模型
特征和预测:
- CLIENTNUM — (Feature,dbl,continuous) 客户号
- 客户年龄—(特征,整数,连续)客户年龄
- 性别—(特征、chr、离散)客户的性别
- Dependent_count — (Feature,int,continuous) 用户拥有的受抚养人数量。也就是说,有多少人依靠信用卡用户获得经济支持。较高的计数告诉我们,支出可能会很高。
- 教育程度—(特征、字符、离散)客户的教育程度
- 婚姻状况—(特征、性格、离散)客户的婚姻状况
- 收入 _ 类别—(特征,字符,离散)客户的收入类别
- 卡 _ 类别—(特征、字符、离散)客户的卡类别
- 信用额度—(特征,dbl,连续)客户号
- attraction _ Flag—(预测值,离散,二进制):已离职(客户离开公司)或当前(客户留在公司)
2.数据争论
在数据争论的第 1 部分(共 3 部分),我们在数据文件& 中读取并安装我们项目所需的所有库/包。我们还检查我们的数据集&是否有任何问题,因此看到没有问题。
```{r DataWrangling1}**library(tidyverse)
library(plyr)
library(readr)
library(dplyr)**
**cc1 <- read_csv("BankChurners.csv")** # original dataset#----------------------------
#----------------------------**problems(cc1)** # no problems with cc1 **head(cc1)
dim(cc1)** # # returns dimensions;10127 rows 23 col **cc1 %>% filter(!is.na(Income_Category))
(is.na(cc1))
glimpse(cc1)```
头的输出(cc1)
一瞥 (cc1)的输出
在数据争论的第 2 部分(共 3 部分),我们操纵数据,只得到我们想要的列&删除数据中的未知值。我们还检查离散变量的维度&唯一值。
6 不同的离散类型为收入 _ 类别:6 万美元—8 万美元,不到 4 万美元,8 万美元—12 万美元,4 万美元—6 万美元,+12 万美元,未知
4 不同的离散类型为婚姻 _ 状态 : 已婚,单身,离婚,未知
4
注意:我们还将删除任何具有“未知”/NA 值的行/条目。
我们在这里看到,我们最初有 10,127 行和 23 列,但我们也截断了 8348 行和 9 列。
```{r DataWrangling2}# selected the columns we care about
**cc2 <- cc1 %>% select(Customer_Age,Gender,Dependent_count,Education_Level,Marital_Status,Income_Category,Card_Category,Credit_Limit, Attrition_Flag) %>% filter( !is.na(.))**
# see the head of it
**head(cc2)
dim(cc2) #dimensions 10127 rows 9 columns**
#**(cc2 <- na.omit(cc2) )** # EXACt SAME as : %>% filter( !is.na(.))#----------------------------**cc2 %>% group_by(Income_Category,Marital_Status)**#----------------------------# Lets see which distinct types there are
**(distinct(cc2, Income_Category)) ** # 6 types:$60K - $80K, Less than $40K ,$80K - $120K ,$40K - $60K ,$120K + ,Unknown
**(distinct(cc2, Marital_Status))** # 4 types: Married, Single, Divorced, Unknown
**(distinct(cc2, Card_Category))** # 4 types: Blue, Gold, Siler, Platinum#----------------------------# Drop all the "unknown" rows from Marital_Status & Income_Category
# 82x9, 82 rows must remove these rows
**cc3 <- cc2 %>% select(Customer_Age,Gender,Dependent_count,Education_Level,Marital_Status,Income_Category,Card_Category,Credit_Limit, Attrition_Flag) %>% filter(Marital_Status != "Unknown" , Income_Category != "Unknown",Education_Level !="Unknown")**#----------------------------
**head(cc3)
dim(cc3)** #8348 rows by 9 cols
#----------------------------```
****
原始和新数据框的尺寸
( distinct (cc2,Income_Category)) # 6 类型的输出:6 万美元到 8 万美元,不到 4 万美元,8 万美元到 12 万美元,4 万美元到 6 万美元,12 万美元以上,未知
( distinct (cc2,marriage _ Status))# 4 类型的输出:已婚、单身、离婚、未知
(不同** (cc2,Card_Category)) # 4 类型输出:蓝色、金色、银色、铂金**
在 3 个数据争论的第 3 部分中,我们将我们的预测器列 Attrition _ Flag 重命名为 Exited _ Flag。我们还将这个预测器的二进制输出值分别从现有客户/流失客户重命名为当前/退出客户。最后,我们还可以通过离散预测器看到每个离散特征的 cout 。****
```{r DataWrangling3}#----------------------------
#----------------------------
#install.packages("dplyr")
**library(dplyr)**# Rename Label Colum to Exited_Flag
**dataCC4 <- cc3 %>% rename(Exited_Flag = Attrition_Flag)**
#----------------------------
#----------------------------
**dataCC4 <- cc3**
#Rename values
**dataCC4 $Attrition_Flag[dataCC4 $Attrition_Flag == "Existing Customer"] <- "Current"
dataCC4 $Attrition_Flag[dataCC4 $Attrition_Flag == "Attrited Customer"] <- "Exited"**#----------------------------
#----------------------------
**(dataCC4 %>% group_by(Attrition_Flag) %>% summarize(meanAge= mean(Customer_Age), meanDepdent= mean(Dependent_count), meanCreditLim= mean(Credit_Limit)))**#AKA:
** summarise_mean <- function(data, vars) {
data %>% summarise(n = n(), across({{ vars }}, mean))
}**#**dataCC4 %>%
#group_by(Attrition_Flag) %>%
# summarise_mean(where(is.numeric))**#----------------------------
#----------------------------
#see the count of each**(dataCC4 %>% select(Gender,Attrition_Flag) %>% group_by(Gender) %>% count(Attrition_Flag) )
(dataCC4 %>% group_by(Education_Level) %>% count(Attrition_Flag) )
(dataCC4 %>% group_by(Marital_Status) %>% count(Attrition_Flag) )
(dataCC4 %>% group_by(Income_Category) %>% count(Attrition_Flag) )
(dataCC4 %>% group_by(Card_Category) %>% count(Attrition_Flag) )
summary(dataCC4)**
****
**(data cc4 % > %**group _ by**(attention _ Flag)%>%**summary**(mean Age =**mean**(Customer _ Age),mean depend =**mean**(Dependent _ count),mean Credit lim =**mean**(Credit _ Limit))**
**从上图中,我们可以清楚地看到,当前客户的平均信用额度高于频繁交易的客户。**
****
**(data cc 4% > %**group _ by**(Education _ Level)%>%**count**(attinction _ Flag))**
****
**(dataCC4 %>% **group_by** (婚姻状况)% > % **count** (减员 _ 标志) )**
****
**(data cc 4% > %**group _ by**(Income _ Category)%>%**count**(attinction _ Flag))**
****
**(dataCC4 %>% **group_by** (卡片 _ 类别)% > % **count** (损耗 _ 标志) )**
****
****概要**(数据 CC4)**
# ****3。探索性数据分析****
**在 EDA 的第 1 部分(共 2 部分)中,我们**对某些离散变量的**因子**级别进行了重新排序**,因为我们希望它们在我们的图表中按顺序显示。我们绘制了两个彼此相对的离散变量,其中一个变量是填充。我们目视检查每个的**计数**。**
# Count, with Y being Income Category, Fill is our
**ggplot(dataCC4 , aes(y = Income_Category)) +
geom_bar(aes(fill = Attrition_Flag), position = position_stack(reverse = FALSE)) +theme(legend.position = "top") + theme_classic() + xlab("Count") + ylab("Income Category") + ggtitle(" Customer Status by Income" )+ labs(fill = "Customer Status")**#----------------------------# RE-ODER factor levels
**dataCC4$Education_Level <- factor(dataaa$Education_Level, levels = c("Uneducated","High School",
"College",
"Graduate",
"Post-Graduate","Doctorate"))
ggplot(dataCC4 , aes(y = Education_Level)) +
geom_bar(aes(fill = Attrition_Flag), position = position_stack(reverse = FALSE)) +
theme(legend.position = "top") + theme_classic() + xlab("Count") + ylab("Education Level") + ggtitle("Customer Status by Education Level" ) + labs(fill = "Customer Status")**#----------------------------**ggplot(dataCC4 , aes(y = Marital_Status)) +
geom_bar(aes(fill = Attrition_Flag), position = position_stack(reverse = FALSE)) +
theme(legend.position = "top") + theme_classic() + xlab("Count") + ylab("Martial Status") + ggtitle("Customer Status by Martial Status" )+ labs(fill = "Customer Status")**#----------------------------**ggplot(dataCC4 , aes(y = Card_Category)) +
geom_bar(aes(fill = Attrition_Flag), position = position_stack(reverse = FALSE)) +
theme(legend.position = "top") + theme_classic() + xlab("Count") + ylab("Card Category") + ggtitle("Customer Status by Card Category" )+ labs(fill = "Customer Status")**#----------------------------**ggplot(dataCC4 , aes(y = Gender)) +
geom_bar(aes(fill = Attrition_Flag), position = position_stack(reverse = FALSE)) +
theme(legend.position = "top") + theme_classic() + xlab("Count") + ylab("Gender") + ggtitle("Customer Status by Gender" )+ labs(fill = "Customer Status")**
#There are more samples of females in our dataset compared to males but the percentage of difference is not that significant so we can say that genders are uniformly distributed.#----------------------------
****
**很明显,大多数退出的客户属于收入低于 4 万美元的类别,但我们的大多数现有/退出客户都属于这一类别。**
****
**我们目前的大部分客户都是大学毕业生&拥有高中学历。**
****
**客户的婚姻状况,很少比例是离婚,大多数是已婚和单身。**
****
**蓝卡是我们现有和现有客户最重要的卡类。**
****
****性别&年龄**在确定客户状态时并不重要,因为性别计数的两个平均值是相同的 46.31736(当前)& 46.51033(已退出)**
**在 EDA 的第 2 部分中,我们探索了两个带有分位数的 violin 图,两个离散变量的可视化&面积分布图。**
**dataCC4 $Income_Category <- factor(dataCC4 $Income_Category, levels = c("Less than $40K","$40K - $60K","$60K - $80K","$80K - $120K","$120K +"))****ggplot(dataCC4 , aes(Income_Category,Credit_Limit,color= Credit_Limit)) + geom_violin(draw_quantiles = c(0.25,0.5,0.75),colour="blue",size=1) + theme_classic() +xlab("Income Category") + ylab("Credit Limit") + ggtitle("Income Category by Credit Limit" )**#----------------------------# 2 discrete variables x,y
#need to specify which group the proportion is to be calculated over.
**ggplot(dataCC4 , aes(Income_Category,Attrition_Flag, colour= after_stat(prop), size = after_stat(prop), group = Income_Category)) + geom_count() + scale_size_area() + theme_classic() +xlab("Income Category") + ylab("Status of Customer") + ggtitle("Customer Status by Income Proportion" )**#----------------------------
#
#library(plyr)
#**mu <- ddply(dataaa, "Exited_Flag", summarise, grp.mean=mean(Credit_Limit))
#head(mu)**
#
**ggplot(dataCC4, aes(x=Credit_Limit, fill=Attrition_Flag)) +
geom_area(stat ="bin") + xlab("Credit Limit")+ylab("Count") +ggtitle("Customer Status by Credit Limit " ) + labs(fill = "Customer Status")**
****
**1 个离散变量和 1 个连续变量,这个小提琴图告诉我们,他们是当前客户的更大传播。红色水平线是分位数。**
**Q 可以告诉我们大量的信息。**
**第一条水平线告诉我们第一个分位数,或第 25 个百分位数——这个数字将最低的 25%的群体与最高的 75%的信贷限额区分开来。**
**接下来,第二行是中间值,或第 50 个百分位数——信贷限额中间的数字。**
**最后,第三行是第三个四分位数,即 Q3 或第 75 个百分位数,这是将该组中最低的 75%与最高的 25%分开的数字。**
**分位数显示在上面和下面的小提琴图中。**
****
**当然,较高的收入类别与较高的信贷限额相关。**
****
**2 个检验客户身份和收入类别比例的离散变量。目前,收入低于 4 万美元的客户比例更高。**
****
****地区** **信用额度的分布**与客户状态。我们在这里看到,我们的数据主要是当前客户,而不是现有客户。**
**我们只有 **16.07%** 的客户有**的搅动**。因此,训练我们的模型来预测不断变化的客户有点困难。我们将尽最大努力处理给定的数据,就像处理任何模型一样。**
# ****4。建模+置信区间****
**O 我们建模的目标是提供数据集的简单低维摘要。我们使用模型将数据分成模式和残差。拟合模型是模型家族中最接近的模型。**
****分类模型**是预测分类标签的模型。研究哪个(些)特征区分每个类别以及区分到什么程度将是有趣的。预测客户会离开还是留在公司。下面我们将使用逻辑回归算法。**
**首先,我们必须将非数字变量(离散变量)转换成因子。**因素**是数据对象,用于对数据进行分类,并将其存储为级别。**
## **4 个步骤:数据划分、构建、预测和评估**
**library(tidyverse)
library(modelr)
library(plyr)
library(readr)
library(dplyr)
library(caret)**# glimpse is from dplyr
# output shows that the dataset has
**glimpse(dataCC4)**# convert the non numeric into factors.
**names <- c(2,5,7,9) # col numbers that are chars
dataCC4[,names] <- lapply(dataCC4[,names] , factor)** ```
数据分区
```{r Modeling2}
#----------------------------
# DATA PARTITIONING
#----------------------------
#build our model on the Train dataset & evaluate its performance on #the Test dataset
# aka. holdout-validation approach to evaluating model performance.**set.seed(100)** # sets random seed for reproducibility of results
**library(caTools)** # for data partioning# create the training and test datasets. The train dataset contains # 70 percent of the data (420 observations of 10 variables) while #the #test data contains the remaining 30 percent (180 observations #of 10 variables).
**spl = sample.split(dataCC4$Attrition_Flag, SplitRatio = 0.7)****train = subset(dataCC4, spl==TRUE)
test = subset(dataCC4, spl==FALSE)****print(dim(train))** # 4957 rows/obs 9 cols/variables
**print(dim(test))** # 2124 rows/obs 9 cols/variables```
建立、预测&评估模型
我们符合逻辑回归模型。第一步是实例化算法。我们声明二项式是因为有两种可能的结果:退出/当前。
```{r Modeling3}
#----------------------------
#BUILD, PREDICT & EVALUATE the Model#----------------------------
# BUILD
#----------------------------**model_glm = glm(Attrition_Flag ~ . , family="binomial",data = train)
summary(model_glm)**# Baseline Accuracy
#Let's evaluate the model further,
# Since the majority class of the target (Y) variable has a #proportion of 0.84, the baseline accuracy is 84 percent.
**prop.table(table(train$Attrition_Flag))**#Let's now evaluate the model performance on the training and test data, which should ideally be better than the baseline accuracy.# PREDICTIONS on the TRAIN set
**predictTrain = predict(model_glm, data = train, type = "response")**# confusion matrix w/ threshold of 0.5,
#which means that for probability predictions >= 0.5, the #algorithm will predict the Current response for the Y #variable.
**table(train$Attrition_Flag, predictTrain >= 0.1)** Evaluate:#prints the accuracy of the model on the training data, using the #confusion matrix, and the accuracy comes out to be 0 percent.
**(50+774)/nrow(test)** #Accuracy - 84% (50+774)/2124-------------------------------------------------------------
#We then repeat this process on the TEST data, and the accuracy #comes out to be 84 percent.# PREDICTIONS on the TEST set
**predictTest = predict(model_glm, newdata = test, type = "response")**Evaluate:# Confusion matrix on test set
**table(test$Attrition_Flag, predictTest >= 0.5)****1790/nrow(test)** #Accuracy - 84%```
以上输出中的显著性代码' *** '显示了特征变量的相对重要性
AIC 估计给定模型丢失的相对信息量:
模型丢失的信息越少,模型的质量越高。较低的 AIC 值指示更好拟合的模型,
model _ glm = glm(attachment _ Flag ~)的输出。,family="binomial ",data = train)
汇总(model_glm)
84%的准确率
您已经学习了使用强大的逻辑回归算法在 R 中构建分类模型的技术。训练&测试数据的基线准确度为84%。总的来说,逻辑回归模型在训练和测试数据集上都大大超过了基线精度,结果非常好。
下面我将向你展示另一种建模方法,使用 SVM(支持向量机)。
```{r ModelAgain}**unique(dataCC5$Attrition_Flag)** #Levels: Current Exited# Encoding the target feature as factor
**dataCC5$Attrition_Flag = factor(dataCC5$Attrition_Flag, levels = c(0, 1))**# spliting datast into Train & Test Set**install.packages('caTools')**
**library(caTools) # for data partioning****set.seed(123) #SEED**#SPLIT
**split = sample.split(dataCC5$Attrition_Flag, SplitRatio = 0.75)****train_set = subset(dataCC5,split==TRUE) #TRAIN
test_set = subset(dataCC5,split==FALSE) #TEST**#FEATURE SCALING: age
**library(caret)
train_set[-9] = scale(train_set[-9])
test_set[-9] = scale(test_set[-9])****install.packages('e1071')
library(e1071)****model_glm = glm(Attrition_Flag ~ . , family="binomial",data = train)****classifier = svm(formula= Attrition_Flag ~ .,
data= train_set,
type= 'C-classification',
kernel = 'radial')**# predicting Test Set Results
**y_pred = predict(classifier, newdata = train_set[-9])**# making confusion matrix
**cm = table(test_set[, 3],y_pred)```
****置信区间与建模相关,因为它们经常用于模型验证。
接下来,我们考虑信用额度的 95%置信区间。
由于信用额度大于 0,我们缩小了置信区间。
有 91.75% 的数据位于置信区间内。我们将保留相应的记录,并将其余的存储在另一个变量 rest.data 中,以供以后分析。
```{r CI}**mean.Credit_Limit <- mean(dataCC5$Credit_Limit)
std.Credit_Limit <- sqrt(var(dataCC5$Credit_Limit))
df = dim(dataCC5)[1] - 9
conf.Credit_Limit <- mean.Credit_Limit + c(-1, 1) * qt(0.975, df) * std.Credit_Limit**# As the CreditLimit is greater than 0, we narrow the confidence interval**conf.Credit_Limit[1] <- 0
conf.Credit_Limit**#There are 91.75% data locates within the confidence interval. We will keep the corresponding records and store the rest in another variable rest.data for later analysis.**sum(dataCC5$Credit_Limit <= conf.Credit_Limit[2]) / dim(dataCC5)[1]**
#----------------------------
#
#----------------------------
**rest.data <- dataCC5 %>%
filter(Credit_Limit > conf.Credit_Limit[2])****dataCC5 <- dataCC5 %>%
filter(Credit_Limit <= conf.Credit_Limit[2]) %>%
filter(Credit_Limit != 0)**#We recall the historgrams of Credit Limit
**boxplot_credLim <- dataCC5 %>%
ggplot() +
geom_boxplot(aes(Credit_Limit))
(boxplot_credLim)
histplot <- dataCC5 %>%
ggplot() +
geom_histogram(aes(Credit_Limit))
(histplot)**
#----------------------------
#
#----------------------------
#We consider a log-transofrmation to convert the distribution of the histogram to normal distribution. Right-skew.
**histplot <- dataCC5 %>%
ggplot() +
geom_histogram(aes(log(Credit_Limit)))
(histplot)****qqplot <- dataCC5 %>%
ggplot() +
geom_qq(aes(sample = log(Credit_Limit)))
(qqplot)**
#It seems that normality exists. Great! There are 6 types of categorical variables.# Distrubution of Income Category
**p1 <- dataCC5 %>%
ggplot() +
geom_histogram(aes(Income_Category, fill = Income_Category), stat = "count")
(p1)**
# Box Plots of Depdent count & Income Category
**p2 <- dataCC5 %>%
ggplot() +
geom_boxplot(aes(x = Dependent_count, y = Income_Category, color = Income_Category))
(p2)```
有 91.75% 的数据位于置信区间内。sum(data cc 5 $ Credit _ Limit<= conf。credit _ Limit[2])/dim(data cc 5)[1]
我们回忆一下信用额度
box plot _ cred lim<-data cc 5%>%
gg plot()+
geom _ box plot(AES(Credit _ Limit))
(box plot _ cred lim)的历史记录
hist plot<-data cc 5%>%
gg plot()+
geom _ histogram(AES(Credit _ Limit))
(hist plot)
我们考虑使用对数变换将直方图的分布转换为正态分布**。向右倾斜。
hist plot<-data cc 5%>%
gg plot()+
geom _ histogram(AES(log(Credit _ Limit)))
(hist plot)**
QQ plot<-data cc 5%>%
gg plot()+
geom _ QQ(AES(sample = log(Credit _ Limit)))
(QQ plot)
看来常态是存在的。太好了!有 6 种类型的分类变量。
#收入类别分布
P1<-data cc 5%>%
gg plot()+
geom _ histogram(AES(Income _ Category,fill = Income_Category),stat = "count")
(p1)
#受抚养人人数和收入类别的箱线图:data cc 5%>%
gg plot()+
geom _ Box plot(AES(x = Dependent _ count,y = Income_Category,color = Income _ Category))
(p2)****
评价:RMSE
#以下旨在建立一个模型来预测价格。##评估我们将使用 MSE 作为衡量模型性能的标准。
我们将 20%作为测试数据集,80%作为训练数据集。
```{r RMSE}#Train and test split
#Split 20% as test dataset and 80% as training dataset.
### convert character to factor
**dataCC5$Gender <-as.factor(dataCC5$Gender)**# Split
**set.seed(100)
train.index <- sample(nrow(dataCC5), 0.7*nrow(dataCC5), replace = FALSE)
train.set <- dataCC5[train.index, ]
test.set <- dataCC5[-train.index, ]**
# build a model to predict the price.
## Evaluation: We will use MSE as the criteria to measure the model performance.
**RMSE <- function(true, pred){
residuals <- true - pred
res <- sqrt(mean(residuals^2))
return(res)
}
----------** #----------------------------
#Linear regression
#Linear regressoin is a kind of simple regression methods. It is easy to be conducted while it has some strict assumptions. The following code will perform the modeling process with check some assumptions. ### Multicolinearity
#**library(corrplot)
#cor <- cor(dataCC5)
#corrplot::corrplot(cor, method = 'ellipse', type = 'lower')
#cor(dataCC5$Credit_Limit, dataCC5$Dependent_count)**# IF data had more numeric values use this for
# correlation plot:
**#M <- cor(dataCC5)
#corrplot(cor(dataCC5), method = "circle")```
模特& AIC
回想一下: AIC 估计给定模型丢失的相对信息量:
模型丢失的信息越少,模型的质量越高。
因此,较低的 AIC 值指示更好拟合的模型,
```{r AIC}**AICs <- c()
models <- c()**
**start.model <- lm(Credit_Limit ~ Customer_Age, data = train.set)**# summary(start.model)
**models <- append(models, as.character(start.model$call)[2])
AICs <- append(AICs, AIC(start.model))**# Add next varaible
**update.model <- update(start.model, . ~ . + Gender)**# summary(update.model)
**models <- append(models, as.character(update.model$call)[2])
AICs <- append(AICs, AIC(update.model))**# Add next var
**update.model <- update(update.model, . ~ . + Dependent_count)**# summary(update.model)
**models <- append(models, as.character(update.model$call)[2])
AICs <- append(AICs, AIC(update.model))**# Add next var
**update.model <- update(update.model, . ~ . + Education_Level)**# summary(update.model)
**models <- append(models, as.character(update.model$call)[2])
AICs <- append(AICs, AIC(update.model))**# Add next variable
**update.model <- update(update.model, . ~ . + Marital_Status)**# summary(update.model)
**models <- append(models, as.character(update.model$call)[2])
AICs <- append(AICs, AIC(update.model))**# Add calculated_host_listings_count
**update.model <- update(update.model, . ~ . + Income_Category)**# summary(update.model)
**models <- append(models, as.character(update.model$call)[2])
AICs <- append(AICs, AIC(update.model))**# Add last var
**update.model <- update(update.model, . ~ . + Card_Category)**# summary(update.model)
**models <- append(models, as.character(update.model$call)[2])
AICs <- append(AICs, AIC(update.model))****res <- data.frame(
Model = models,
AIC = AICs
)
knitr::kable(res)**#----------------------------
#----------------------------
**par(mfrow = c(3,1))
plot(update.model, 1)
plot(update.model, 2)
plot(update.model, 3)```
表显示最佳模式是
信用 _ 限额~客户 _ 年龄+性别+赡养 _ 人数+教育 _ 水平+婚姻 _ 状况+收入 _ 类别+卡 _ 类别
带的 72870.02 的AIC,我们要最低的 AIC。
par(m flow = c(3,1))
plot(update.model,1)
plot(update.model,2)
plot(update.model,3)
```{r LogLinearRegression}**log.lm <- lm(log(Credit_Limit) ~ Customer_Age + Gender + Dependent_count + Education_Level + Marital_Status + Income_Category + Card_Category, data = dataCC5)****summary(log.lm)```
性别男性、收入类别和银卡类别对我们的信用额度预测非常重要。
以下是从我的 GitHub 页面获得的数据集和代码:
https://github.com/jararzaidi/ModelingChurningCustomersR
【https://www.kaggle.com/sakshigoyal7/credit-card-customers】
欢迎推荐和评论!
倾向得分估计和可视化
使用模拟数据集
由 Filiberto Santillán 在 Unsplash 上拍摄的照片
在我的领域——流行病学——以及其他各种科学领域,基于倾向评分(PS)的方法经常被用于混杂调整。在这篇文章中,我用两个不同的治疗变量(x1
和x2
)模拟了两个小数据集(N=20,000),患病率分别为 7%和 20%。然后,我继续模拟两种不同的结果(y1
和y2
),在模拟人群中的患病率分别为 6%和 16%。风险暴露对结果的真实影响为零。这篇文章很大程度上是受 Desai 等人(2017)的论文的启发。
这篇文章将涵盖哪些内容?
- 用两个不同暴露(
x1
和x2
)和每个暴露的结果y1
和y2
)之间的零关联来模拟我自己的数据集(4 个暴露-结果对) - 计算每次暴露的倾向分数(PS ),修整暴露和未暴露之间 PS 分布的非重叠区域
- 运行几个逻辑回归模型
在这篇文章中,我只分享一些代码片段。这篇文章的所有代码版本可以在这里找到。
数据模拟
我模拟了 10 个混杂因素,2 个治疗预测因素,2 个结果预测因素。
分析
对暴露和结果之间关联的粗略分析表明,在估计值周围有一个较宽的 95%置信区间,呈弱正相关。最大偏倚约为 10%(对于两者中不太常见的结果,y1
),在模拟人群中的患病率约为 6%。)
倾向得分
我使用混杂因素和结果预测因素分别计算了每次暴露的倾向得分(PS)。
倾向评分(PS)分布在治疗组和未治疗组之间大部分重叠,因此非重叠区域的修整不会导致许多数据点的丢失。
使用倾向评分,我计算了标准化死亡率(SMR)权重,并将其用于未暴露人群的重新加权,就好像他们暴露了一样;然后,我比较了暴露和结果之间的粗略关联,传统调整的逻辑回归模型,以及通过 SMR 加权调整的模型。
三种不同逻辑回归模型的比较揭示了以下情况:
- 粗略的分析是不精确的,表明暴露(
x1 ',
x2 ')和结果(y1 ',
y2 ')之间存在关联,而没有真正的关联(粗略分析的最大偏差约为 14.5%)。 - 逻辑回归模型中的常规调整产生了不精确的估计值,并提示弱相关性,包括一些暴露-结果对的反向相关性(最大偏差约为 11%)。
- SMR 加权产生了不精确的结果,类似于传统的调整,表明暴露和结果之间存在一些微弱的负相关(对于“x2”-“y1”暴露-结果对,观察到的最大偏差约为 7.5%)。
- 虽然不确定性在所有三个分析中都是一个问题,但 SMR 加权法可以说产生了最接近真实(模拟)无效效应的点估计。
参考
- 德赛 RJ,罗斯曼 KJ,贝特曼 BT,埃尔南德斯-迪亚兹 S,Huybrechts KF。当暴露不频繁时,基于倾向评分的混杂调整精细分层方法。流行病学。2017;28(2):249–257.doi:10.1097/ede . 50000000000001
倾向得分匹配
阿斯泰米尔·阿尔莫夫在 Unsplash 上拍摄的照片
数据科学基础
根据观察数据进行因果推断的初学者指南
因果推理是数据科学中一个被低估但强大的领域。得出因果结论的一个流行方法是进行 AB 测试。但是如果 AB 测试不可行或者根本不是一个选项呢?非实验因果推理技术使我们能够从观察数据中得出因果关系的结论。在这篇文章中,我们将学习一种叫做倾向分数匹配的技术背后的基本直觉。
📍 0.实验数据与观察数据
在本节中,我们将简要介绍与分析因果关系相关的两类数据。在我们开始之前,让我们在三个关键术语上达成一致:
◼ 结果变量:一个我们想要了解治疗效果的感兴趣的变量。这就是效果。
◼ 治疗变量:我们想要了解其对结果变量的影响的感兴趣变量。这就是原因。
◼ 混杂变量:一个影响治疗选择和结果变量的变量。在某些情况下,这也被称为协变量。
作者图片|混杂因素、治疗和结果之间的关系
实验数据📦
作为 AB 测试(又名随机实验、对照研究、随机对照试验和许多其他变体)的一部分收集的数据是实验数据。在 AB 测试中,治疗被随机分配给个体,而不是由他们选择。这意味着没有选择偏差,治疗分配不受任何混杂因素的影响。
作者图片|实验数据概述
在 AB 检验中,从总体中随机抽取足够大的代表性样本。然后,AB 测试的参与者被随机分配到不同的治疗组:A 组和 b 组。这种随机分配有助于将参与者分成相似的组,其中混杂因素是平衡的。当我们对一组人进行治疗而不对另一组人进行治疗时,两组人之间的主要区别在于治疗方法。由于两组在没有治疗的情况下是可比较的,我们可以合理地说两组之间结果的统计学显著差异是由治疗引起的。
0.B 观察数据📦
任何不是作为 AB 测试的一部分收集的数据都是观察数据。在观察数据中,治疗是由个体选择的,而不是随机分配给他们的。这意味着存在选择偏差,治疗不再独立于混杂因素。
作者图片|观察数据概述
在观察数据中,我们从总体中随机收集有代表性的样本。由于治疗是由个人选择的,因此各组之间的混杂因素可能存在差异。因此,即使在没有治疗的情况下,两组也没有可比性。当有其他移动的部分在起作用时,很难说两组之间结果的统计学显著差异是由治疗引起的。
如果你想测试你对这两种类型数据的理解,看看汗学院 的这个小测验。
更新了数据类型后,是时候学习使用倾向得分匹配从观察数据中进行因果推断的基础知识了。
📍 1.倾向得分匹配
倾向得分匹配是一种非实验性的因果推理技术。它试图在混杂因素上平衡治疗组,使它们具有可比性,这样我们可以使用观察数据得出关于治疗对结果的因果影响的结论。使用倾向评分匹配进行因果分析时,有 5 个关键步骤:
1️⃣收集数据
2️⃣估计倾向得分
3️⃣匹配记录
4️⃣评估匹配
5️⃣评估治疗对结果的影响
让我们熟悉这些步骤,并将它们应用到一个玩具示例中,以便更好地理解。
1.1.收集数据📂
这是因果分析最重要的一步。这一步的目的是根据领域专长收集所有可能的混杂因素的数据。如果重要的混杂因素被排除在数据之外,我们将有错误地推断治疗对结果的因果影响的风险。因此,数据收集步骤在因果推断的可靠性和有效性方面起着关键作用。
💡提示: 当处理时序数据(即随时间变化的数据)时,时序混杂因素应显示其在治疗前的状态,时序结果应在治疗后获取。例如,网上购物的销售收入是一个时间变量。如果我们决定使用它作为混杂因素,它应该捕捉治疗发生前的销售收入金额。
作为我们的玩具示例,我们将使用可广泛获取的泰坦尼克号数据。我们将尽量减少混淆因素,使事情易于管理和理解。这是我们假设的关系结构:
我们将试图了解获得三等舱对存活率的影响。让我们加载必要的库和数据:
import numpy as np
import pandas as pd
pd.options.display.float_format = "{:.2f}".formatimport matplotlib.pyplot as plt
import seaborn as sns
sns.set(style='darkgrid', context='talk')from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import Pipeline
from sklearn.metrics import roc_auc_score, f1_scorefrom causalinference import CausalModeldf = sns.load_dataset('titanic')
df['is_pclass3'] = df['pclass']==3
df['is_female'] = df['sex']=='female'
df = df.filter(['survived', 'is_pclass3', 'is_female', 'age'])\
.dropna().reset_index(drop=True)
df
这是治疗组的存活率:
TREATMENT = 'is_pclass3'
OUTCOME = 'survived'df.groupby(TREATMENT)[OUTCOME].describe()
三等舱乘客(治疗组)存活率为 24%,而其他舱乘客(对照组)存活率为 57%。现在让我们按治疗组检查混杂因素的分布:
C_COLOUR = 'grey'
T_COLOUR = 'green'C_LABEL = 'Control'
T_LABEL = 'Treatment'sns.kdeplot(data=df[~df[TREATMENT]], x='age', shade=True,
color=C_COLOUR, label=C_LABEL)
sns.kdeplot(data=df[df[TREATMENT]], x='age', shade=True,
color=T_COLOUR, label=T_LABEL)
plt.legend();
与对照组相比,治疗组有更多的年轻乘客。让我们来看看性别的分布:
F_COLOUR = 'magenta'
M_COLOUR = 'blue'F_LABEL = 'Female'
M_LABEL = 'Male'
gender = 100 * pd.crosstab(df[TREATMENT].replace({True: T_LABEL,
False: C_LABEL}),
df['is_female'].replace({True: 'Female',
False: 'Male'}),
normalize='index')
gender['All'] = 100plt.figure(figsize=(5, 4))
sns.barplot(data=gender, x=gender.index.astype(str), y="All",
color=M_COLOUR, label=M_LABEL)
sns.barplot(data=gender, x=gender.index.astype(str), y='Female',
color=F_COLOUR, label=F_LABEL)
plt.legend(loc='center', bbox_to_anchor=(1.3, 0.8))
plt.xlabel('')
plt.ylabel('Percentage');
治疗组男性较多。因为混杂因素在两组之间不平衡,所以说存活率的差异是由于这个阶段的治疗是幼稚的。现在,让我们学习如何使各组更具可比性。
1.2.估计倾向得分🔨
一旦我们收集了数据,我们就可以建立倾向模型,在给定混杂因素的情况下预测接受治疗的概率。通常,逻辑回归用于这种分类模型。让我们建立一个倾向模型:
# Build a descriptive model
t = df[TREATMENT]
X = pd.get_dummies(df.drop(columns=[OUTCOME, TREATMENT]))pipe = Pipeline([
('scaler', StandardScaler()),
('logistic_classifier', LogisticRegression())
])
pipe.fit(X, t)# Predict
threshold = 0.5
df['proba'] = pipe.predict_proba(X)[:,1]
df['logit'] = df['proba'].apply(lambda p: np.log(p/(1-p)))
df['pred'] = np.where(df['proba']>=threshold, 1, 0)
df.head()
我们没有将数据分为训练和测试部分,因为我们没有建立预测模型。倾向评分告诉我们在给定混杂因素的情况下,个体接受治疗的概率。我们还准备了倾向得分的 logit 变换。让我们来评估这个模型:
print(f"Accuracy: {np.mean(df[TREATMENT]==df['pred']):.4f},\
ROC AUC: {roc_auc_score(df[TREATMENT], df['proba']):.4f},\
F1-score: {f1_score(df[TREATMENT], df['pred']):.4f}")# Visualise confusion matrix
pd.crosstab(df[TREATMENT], df['pred']).rename(columns={0: False,
1:True})
我们将通过治疗组检查倾向评分的分布及其 logit 转换:
fig, ax = plt.subplots(1,2, figsize=(10,4))
# Visualise propensity
sns.kdeplot(data=df[~df[TREATMENT]], x='proba', shade=True,
color=C_COLOUR, label=C_LABEL, ax=ax[0])
sns.kdeplot(data=df[df[TREATMENT]], x='proba', shade=True,
color=T_COLOUR, label=T_LABEL, ax=ax[0])
ax[0].set_title('Propensity')
ax[0].legend(loc='center', bbox_to_anchor=(1.1, -0.3))# Visualise logit propensity
sns.kdeplot(data=df[~df[TREATMENT]], x='logit', shade=True,
color=C_COLOUR, label=C_LABEL, ax=ax[1])
sns.kdeplot(data=df[df[TREATMENT]], x='logit', shade=True,
color=T_COLOUR, label=T_LABEL, ax=ax[1])
ax[1].set_title('Logit Propensity')
ax[1].set_ylabel("");
我们可以看到,两组在整个范围内的倾向得分有一些重叠。这是很好的搭配。
1.3.匹配记录👀
在本节中,我们将把最相似的对照记录与治疗组中的乘客进行匹配。在一个维度(倾向得分)找到相似的记录比在多个维度(混杂因素)找到要容易得多。这可能会让你想起降维方法。倾向得分是一个平衡得分。这意味着,如果我们根据倾向评分来匹配记录,匹配记录之间的混杂分布可能是相似的。
在下面的脚本中,我们为治疗组中的每个例子找到一个具有最相似倾向得分的对照记录(一对一匹配)。这样做时,我们将对控制记录进行抽样替换。这意味着一些质控记录可以多次与不同的治疗记录匹配,而一些质控记录根本不匹配:
# Sort by 'logit' so it's quicker to find match
df.sort_values('logit', inplace=True)
n = len(df)-1for i, (ind, row) in enumerate(df.iterrows()):
# Match the most similar untreated record to each treated record
if row[TREATMENT]:
# Find the closest untreated match among records sorted
# higher. 'equal_or_above would' be more accurate but
# used 'above' for brevity
if i<n:
above = df.iloc[i:]
control_above = above[~above[TREATMENT]]
match_above = control_above.iloc[0]
distance_above = match_above['logit'] - row['logit']
df.loc[ind, 'match'] = match_above.name
df.loc[ind, 'distance'] = distance_above
# Find the closest untreated match among records sorted
# lower. 'equal_or_below' would be more accurate but
# used 'below' for brevity
if i>0:
below = df.iloc[:i-1]
control_below = below[~below[TREATMENT]]
match_below = control_below.iloc[-1]
distance_below = match_below['logit'] - row['logit']
if i==n:
df.loc[ind, 'match'] = match_below.name
df.loc[ind, 'distance'] = distance_below
# Only overwrite if match_below is closer than match_above
elif distance_below<distance_above:
df.loc[ind, 'match'] = match_below.name
df.loc[ind, 'distance'] = distance_belowdf[df[TREATMENT]]
匹配完成!虽然我们在这个例子中做了一个最简单的匹配版本,但值得注意的是,我们可以利用更复杂的匹配技术。我们现在将创建一个名为matched_df
的新数据集,它包含原始治疗组和一个新的对照组:
indices = df[df['match'].notna()].index.\
append(pd.Index(df.loc[df['match'].notna(), 'match']))
matched_df = df.loc[indices].reset_index(drop=True)
matched_df
根据混杂因素,新的对照组应该更类似于治疗组。让我们在下一节看看是否是这样。
1.4.评估匹配🔍
是时候评价一下搭配有多好了。让我们看看这些组在混杂因素方面是否更具可比性:
COLUMNS = ['age', 'is_female', OUTCOME]
matches = pd.merge(df.loc[df[TREATMENT], COLUMNS+['match']],
df[COLUMNS], left_on='match',
right_index=True,
how='left', suffixes=('_t', '_c'))
matches
在这 10 个例子中,治疗记录679
、586
、225
似乎与它们的对照匹配不太相似。然而,其余 7 个示例的匹配看起来非常相似。现在让我们检查一下分布情况:
for var in ['logit', 'age']:
print(f"{var} | Before matching")
display(df.groupby(TREATMENT)[var].describe())
print(f"{var} | After matching")
display(matched_df.groupby(TREATMENT)[var].describe())
匹配后,各组之间的分布看起来更相似。让我们想象一下分布情况:
for var in ['logit', 'age']:
fig, ax = plt.subplots(1,2,figsize=(10,4))
# Visualise original distribution
sns.kdeplot(data=df[~df[TREATMENT]], x=var, shade=True,
color=C_COLOUR, label=C_LABEL, ax=ax[0])
sns.kdeplot(data=df[df[TREATMENT]], x=var, shade=True,
color=T_COLOUR, label=T_LABEL, ax=ax[0])
ax[0].set_title('Before matching')
# Visualise new distribution
sns.kdeplot(data=matched_df[~matched_df[TREATMENT]], x=var,
shade=True, color=C_COLOUR, label=C_LABEL, ax=ax[1])
sns.kdeplot(data=matched_df[matched_df[TREATMENT]], x=var,
shade=True, color=T_COLOUR, label=T_LABEL, ax=ax[1])
ax[1].set_title('After matching')
ax[1].set_ylabel("")
plt.tight_layout()
ax[0].legend(loc='center', bbox_to_anchor=(1.1, -0.3));
年龄看起来比以前更平衡了。让我们来考察一下性别的分布:
print(f"{'is_female'} | Before matching")
display(pd.crosstab(df[TREATMENT], df['is_female'],
normalize='index'))
print(f"{'is_female'} | After matching")
display(pd.crosstab(matched_df[TREATMENT], matched_df['is_female'],
normalize='index'))
这两个群体之间的性别比例更加相似。
fig, ax = plt.subplots(1, 2, figsize=(10, 4))
# Visualise original distribution
sns.barplot(data=gender, x=gender.index.astype(str), y="All",
color=M_COLOUR, label=M_LABEL, ax=ax[0])
sns.barplot(data=gender, x=gender.index.astype(str), y='Female',
color=F_COLOUR, label=F_LABEL, ax=ax[0])
ax[0].legend(loc='center', bbox_to_anchor=(1.1, -0.3))
ax[0].set_xlabel('')
ax[0].set_ylabel('Percentage')
ax[0].set_title('Before matching')# Visualise new distribution
gender_after = 100 * pd.crosstab(
matched_df[TREATMENT].replace({True: T_LABEL, False: C_LABEL}),
matched_df['is_female'].replace({True: 'Female', False: 'Male'}),
normalize='index'
)
gender_after['All'] = 100sns.barplot(data=gender_after, x=gender_after.index.astype(str),
y="All", color=M_COLOUR, label=M_LABEL, ax=ax[1])
sns.barplot(data=gender_after, x=gender_after.index.astype(str),
y='Female', color=F_COLOUR, label=F_LABEL, ax=ax[1])
ax[1].set_xlabel('')
ax[1].set_title('After matching')
ax[1].set_ylabel('');
看起来匹配后两组的性别比例更有可比性。
如果我们对匹配不满意,我们可以返回重复前面的步骤并调整它们,直到我们对匹配满意为止。一旦我们对匹配感到满意,并且相信混杂因素在各组之间是平衡的,我们就可以进入下一步来理解治疗的因果影响。
1.5.评估治疗对结果的影响💭
现在,是时候熟悉一些与治疗效果相关的术语了,也称为因果效应。看一个有连续结果的小例子可能是最容易熟悉的方法:
作者图片|治疗效果概述
如果你想了解更多关于治疗效果的信息,请查看麻省理工学院开放课程 的 和普林斯顿大学 的 的本课程材料。
因果推理的根本问题是我们没有观察到两种可能的结果。我们只知道对照组的 Y0 和治疗组的 Y1。当我们匹配记录时,我们试图用 Y0 代表治疗记录,以便我们可以测量治疗效果。
现在已经理解了这些关键术语,你可能已经猜到我们要估计 ATT。这是因为我们只对处理过的记录进行了匹配。如果我们做了完全匹配,我们可以估计 ate。
以下是治疗组和新对照组之间结果的描述性统计数据:
summary = matched_df.groupby(TREATMENT)[OUTCOME]\
.aggregate(['mean', 'std', 'count'])
summary
让我们估计一下平均治疗效果:
c_outcome = summary.loc[False, 'mean']
t_outcome = summary.loc[True, 'mean']att = t_outcome - c_outcome
print('The Average Treatment Effect on Treated (ATT): {:.4f}'\
.format(att))
我们也可以用下面的方法来得到 ATT:
att = np.mean(matches['survived_t']-matches['survived_c'])
print('The Average Treatment Effect on Treated (ATT): {:.4f}'\
.format(att))
假设我们在分析中考虑了所有的混杂因素,我们可以推断,在获得三等客舱的乘客中,获得三等客舱的乘客的生存机会下降了大约 22%。虽然我们在这个简化的例子中看到了点估计,但更好的方法是看区间估计。
我们已经看了一个简单的手动例子来理解倾向分数匹配背后的直觉。在实践中,使用专用的库,比如 R 中的causan inference、Dow why或 MatchIt,往往更明智、更实用。例如,使用causalinference
库,我们可以这样估计治疗效果:
y = df[OUTCOME].values
t = df[TREATMENT].values
X = df[['is_female', 'age']]
X = pd.DataFrame(StandardScaler().fit_transform(X),
columns=X.columns).valuesmodel = CausalModel(y, t, X)
model.est_via_matching()
print(model.estimates)
我们可以看到,估计的 ATT 与我们的结果相似,但现在我们有了这个输出的置信区间。此外,我们还有 ATE 和 ATC。
由 Andrea Cau 在 Unsplash 上拍摄的照片
因果推理技术可以让我们回答关于因果关系的困难而重要的问题。如果你想了解更多关于倾向分数匹配的知识,这和这是很好的谈话。
您想访问更多这样的内容吗?媒体会员可以无限制地访问媒体上的任何文章。如果你使用 我的推荐链接成为会员,你的一部分会费会直接去支持我。
感谢您阅读这篇文章。如果你感兴趣,这里有我的一些其他帖子的链接:
◼️ 解释 scikit-learn models with shap
◼️️k 近邻解释
◼️️ 逻辑回归解释
◼️️ 比较随机森林和梯度推进
◼️️ 决策树是如何构建的?
◼️️ 管道、柱变压器及特性 Union 说明
再见🏃 💨
观察数据中的倾向评分分层:指南
实验和因果推理
倾向评分匹配的更好替代方案
Christophe Hautier 在 Unsplash 上拍摄的照片
介绍
你知道我们每天产生多少新数据吗?在 2021 年,这个数字大约是 2.5 万亿字节的数据。因此,我们并不缺乏数据,而是缺乏使其有用的正确工具。
很容易显示多种因素如何随着对大数据的访问而一起移动。然而,确定这种关系是因果关系还是仅仅是相关关系变得更具挑战性。总的来说,有三种类型的设计可以帮助我们:
- 真正的实验设计,又名 A/B 测试,或随机控制试验
- 准实验设计
- 观察设计
在一系列的博客文章中,我详细阐述了每个类别中的相关主题。例如,我们必须理解A/A 测试的价值,避免常见的陷阱,遵循最佳实践,并在运行 A/B 测试时小心用户干扰。
准实验类有各种可用的设计,包括差异中的差异、回归不连续设计、间断时间序列、综合控制和工具变量。一个常见的误导性论点是 X/Y/Z 方法是最佳设计。相反,数据科学家必须评估可用的信息,并选择最适合环境的信息。
在最后一类中,倾向得分在使用观察设计获得因果推断中起着核心作用(Rosenbaum,1983)。倾向分分析主要有四种方法: PS 匹配,PS 分层,PS 加权,协变量调整。
在之前的帖子中,我介绍了我们如何使用 PS 匹配来减少治疗组和对照组之间观察到的基线协变量失衡。在今天的帖子中,我们详细阐述了如何在 PS 的基础上对观察结果进行分层,并解释了为什么它是比 PS 匹配更好的选择。在实证部分,我们用 R 编码,并用一个真实的例子计算治疗效果。
什么是倾向得分
PS 是在 观察到 协变量的情况下接受治疗的条件概率的比例分数。因此,P.S .是有效检查治疗组和对照组之间协变量平衡的平衡指标。
任何 PS 分析的潜在风险是影响结果变量的未观察到的协变量的存在。如果未观察到的协变量存在,有三种检验方法(Rosenbaum,2005):
- 构建两个已知在未观察到的协变量上不同的对照组。
- 检查关联的一致性和剂量反应关系。
- 检查不应受治疗影响的变量。
请查看 Rosenbaum (2005)以了解更多信息。
肖恩·斯特拉顿在 Unsplash 上拍摄的照片
为什么倾向评分
让我们解决房间里的大象:
倾向评分有什么好大惊小怪的?
为什么每一种观察方法都要玩弄它?
首先,我们必须了解观察数据的本质,以及为什么很难使用非实验(观察)数据进行因果推断。我们之所以称之为观察数据,是因为数据科学家无法控制或了解治疗分配过程。我们仅仅观察到治疗组中的一些单位和对照组中的其他单位。
由于缺乏有效的干预,很难建立可信的因果关系。在 RCTs 中,我们采用两均数差估计量来量化干预效果,即比较治疗组和对照组之间的平均差异。但是,我们不能在观测数据中使用同一个估计量,观测数据中可能包含选择偏差。即人们自选进入治疗组,其他人进入对照组。
这里有一个例子。
经济学家想了解职业培训项目在影响参与者家庭收入方面的有效性。然而,如果我们简单地比较项目参与者和非参与者之间的项目后工资,我们最终会得到一个有偏见的估计,因为那些比平均水平更有积极性的人更有可能参加培训项目。
如果我们只是比较差异而不进行调整,我们可能会将共同基础变量(在这种情况下是更好的动机)引起的影响归因于干预效应:
我自己的截图
为了获得一个无偏的估计量,我们必须调整关键协变量的不平衡,减少(甚至消除)选择偏差。
我自己的截图
下一节我们来深挖 PS 分层的原因。
为什么倾向评分分层
PS 是一个平衡分数:以 PS 为条件,观察到的协变量在治疗组和对照组之间的分布看起来相似(Austin,2011)。因此,它允许你通过调整分数来调整协变量的不平衡。
一些研究人员认为,我们可以根据参与者的 PS 匹配他们,并找到可比较的案例,即 PS 匹配。然而,精确匹配过程增加了不平衡、低效、模型依赖、偏差,并且未能减少不平衡(King 和 Nielsen,2019)。相比之下,PS 分层为 PS 匹配提供了更好的替代方案。
具体步骤如下:
*1\. Estimate the PS using a logistic regression
2\. Create mutually exclusive strata based on the estimated PS
3\. Group treated and control units into each stratum
4\. Within each stratum, we calculate the difference in means between the treated and control
5\. Weight the means in each stratum and obtain the target estimate*
第 2 步和第 5 步有两个额外的注意事项。在第二步,研究表明 5 层可以消除未经调整的估计中大约 90%的偏差(罗森鲍姆和鲁宾,1984)。一个合理的假设是:如果我们尽可能多的增加地层呢?它能更好地降低偏置吗?
号码
模拟工作表明,5-10 层导致最好的结果,10+层带来的边际收益很小(Neuhä user et al .,2018) 。此外,还有一个实际的原因:我们拥有的层越多,每个层中的数据点就越少。
在第 5 步,PS 分层允许我们估计平均治疗效果( ATE )和平均治疗效果( ATT )取决于我们如何权衡平均值。
为了估计 ate,我们按每个地层中的单元数进行加权;
为了估计 ATT,我们根据每个地层中处理过的单位数量进行加权(Imbens,2004) 。
你可能会对潜在结果框架(POF)符号感到困惑:ATE 和 ATT 实际上是什么意思?
ATE:整个人群的平均治疗效果。
ATT:将接受治疗的受试者与未接受治疗的类似受试者进行比较。
从概念上讲,PS 分层被认为是对各层内一组准随机对照试验的荟萃分析(Austin,2011)。我们可以将治疗分配过程视为以观察到的协变量为条件的随机分配,也称为可忽略假设,这意味着潜在的结果与治疗状态无关(Austin,2011)。
在 POF 中,可忽略的假设允许我们获得无偏估计。
我自己的截图
哪里
- Y0i:如果 I 单元没有接受治疗
- Y1i:如果 I 单元接受治疗
- 子:治疗条件
通常,可忽略的假设在观察研究中不成立。换句话说,治疗组的单位比对照组的单位更有可能接受治疗。然而,PS 分层提供了一个解决方案:以 PS 表示的观测协变量为条件,可忽略假设成立,即条件可忽略假设(CIA)。
两组均数差估计量是 ate 的无偏阶层特异性估计量(D'Agostino,1998)。
我自己的截图
其中
- Y0i:如果 I 单元没有接受治疗
- Y1i:如果 I 单元接受治疗
- 子:治疗条件
- Xi :被观测协变量的向量,表示为 PS
这就是 PS 分层背后的所有理论。
让我们用 r 编码。
r 实施
在本节中,我们使用了 LaLonde (1986)和 Dehejia 和 Wahba (1997)使用的复合数据集。它由两部分组成:国家支持的工作示范(新南威尔士州)中经过处理的数据的子样本和来自收入动态人口调查(PSID)的比较样本。
出于教学原因,我简化了分析过程,并主要关注如何在 r 中进行倾向评分分层的工作流程。这就是为什么获得的 ATT 与原始论文略有不同。
PS 分层的快速回顾:
*1\. Estimate the PS using a logistic regression
2\. Create mutually exclusive strata based on the estimated PS
3\. Group treated and control units into each stratum
4\. Within each stratum, we calculate the difference in means between the treated and control
5\. Weight the means in each stratum and obtain the target estimate*
第 0 步:数据准备
*#install.packages(“MatchIt”)
#install.packages(“optmatch”)library(“MatchIt”)
library(“optmatch”)data(“lalonde”)
head(lalonde)
#?lalonde*
我自己的截图
```{r}
sum(lalonde$treat)
185*
*总治疗人数为 185 人。*
mean(lalonde[which(lalonde$treat==0),’re78'])
6984.17*
*对照组的结果变量是 6984.17 美元。*
mean(lalonde[which(lalonde$treat==1),’re78'])
6349.144*
*治疗组的结果变量为 6349.144 美元。*
***步骤 1:使用逻辑回归估计 PS***
PS_logit <- glm(treat ~ age + educ+race+married+nodegree+re74+re75, data = lalonde, family=binomial)
PS_Scores = fitted(PS_logit)
head(PS_Scores)
**
***第二步:根据估算的 PS 建立互斥地层***
lalonde$PS_Scores <- PS_Scores
lalonde
**
# create strata
Quintiles <- quantile(lalonde$PS_Scores, prob=seq(from=0,to=1,by=0.2),na.rm=TRUE)
lalonde$PS_Stratum <- cut(lalonde$PS_Scores, breaks = Quintiles, labels = 1:5, include.lowest = TRUE)
lalonde
**
***第三步&第四步:将处理单元和对照单元分组到各个地层中;在每一层中,我们计算治疗组和对照组之间的平均值差异***
*在 R 中,我们可以通过运行以下代码将步骤 3 和 4 结合在一起:*
# Fifth Stratum: PS_Stratum = 5diff_5 = mean(lalonde[which(lalonde$treat==1&lalonde$PS_Stratum==5), ’re78'])- mean(lalonde[which(lalonde$treat==0&lalonde$PS_Stratum==5),’re78'])diff_5
1364.036*
*在第 5 层中,治疗组和对照组之间的结果变量(“re78”)的差异为 1364.036 美元。*
*我们在下面的代码中对剩余的层重复同样的过程:*
**# Forth Stratum: PS_Stratum = 5** diff_4 = mean(lalonde[which(lalonde$treat==1 & lalonde$PS_Stratum==4),’re78']) — mean(lalonde[which(lalonde$treat==0 & lalonde$PS_Stratum==4),’re78'])
diff_4
841.7422**# Third Stratum: PS_Stratum = 3** diff_3 = mean(lalonde[which(lalonde$treat==1 & lalonde$PS_Stratum==3),’re78']) — mean(lalonde[which(lalonde$treat==0 & lalonde$PS_Stratum==3),’re78'])
diff_3
3167.41**# Second Stratum: PS_Stratum = 2** diff_2 = mean(lalonde[which(lalonde$treat==1 & lalonde$PS_Stratum==2),’re78']) — mean(lalonde[which(lalonde$treat==0 & lalonde$PS_Stratum==2),’re78'])
diff_2
2122.768# First Stratum: PS_Stratum = 1
diff_1 = mean(lalonde[which(lalonde$treat==1 & lalonde$PS_Stratum==1),’re78']) — mean(lalonde[which(lalonde$treat==0 & lalonde$PS_Stratum==1),’re78'])
diff_1
-10467.06```
需要指出的是,第一层的差异似乎是:-10467.06。原因是地层中只有一个经过处理的观测值,从技术上讲,我们应该忽略它。然而,出于教学的原因,我们在分析中保留了它。
第五步。对每一层的平均值进行加权并获得目标估计值,ATT
```{r}
# the total nunmber of treated cases
total_treated_n <- nrow(lalonde[which(lalonde$treat==1),])
total_treated_n
185
*首先,我们计算了接受治疗的病例总数,共有 185 例。*
#the number of treated cases in each stratum
stratum_5_treated_n <- nrow(lalonde[which(lalonde$treat==1 & lalonde$PS_Stratum==5),])
stratum_5_treated_n
85
*在地层中,治疗了 85 例。*
ATT_5 = diff_5*stratum_5_treated_n/total_treated_n
ATT_5
626.7194
*然后,我们计算地层 5 中的加权处理效果。*
*对状态 1–4 重复相同的程序。*
# stratum 4stratum_4_treated_n <- nrow(lalonde[which(lalonde$treat==1 & lalonde$PS_Stratum==4),])
stratum_4_treated_nATT_4 = diff_4*stratum_4_treated_n/total_treated_n
ATT_4
323.047# stratum 3
stratum_3_treated_n <- nrow(lalonde[which(lalonde$treat==1 & lalonde$PS_Stratum==3),])
stratum_3_treated_nATT_3=diff_3*stratum_3_treated_n/total_treated_n
ATT_3
359.5438# stratum 2
stratum_2_treated_n <- nrow(lalonde[which(lalonde$treat==1 & lalonde$PS_Stratum==2),])stratum_2_treated_n
7# stratum 1
stratum_1_treated_n <- nrow(lalonde[which(lalonde$treat==1 & lalonde$PS_Stratum==1),])
stratum_1_treated_nATT_1= diff_1*stratum_1_treated_n/total_treated_n
ATT_1
- 56.57873```
最后一步,我们把它加起来,得到 ATT:
```{r}
ATT = ATT_1+ATT_2+ATT_3+ATT_4+ATT_5
ATT1333.052
*使用 PS 分层法,我们发现参加职业培训项目会导致项目后家庭收入增加 1333.052 美元。实验基准是 1794 美元,原论文使用 PS 分层估算**1608**。差距是由一些技术细节造成的。*
**Medium 最近进化出了它的* [*作家伙伴计划*](https://blog.medium.com/evolving-the-partner-program-2613708f9f3c) *,支持像我这样的普通作家。如果你还不是订户,通过下面的链接注册,我会收到一部分会员费。**
*<https://leihua-ye.medium.com/membership> *
# *参考*
*奥斯汀,哥伦比亚特区,2011 年。减少观察性研究中混杂效应的倾向评分方法介绍。*多元行为研究*, *46* (3),第 399–424 页。*
*小达戈斯蒂诺,R.B .,1998 年。在治疗与非随机对照组的比较中减少偏倚的倾向评分方法。*医学统计学*, *17* (19),第 2265–2281 页。*
*Dehejia,R.H .和 Wahba,s .,1999 年。非实验研究中的因果效应:重新评估训练计划的评估。*美国统计协会杂志*, *94* (448),第 1053–1062 页。*
*Imbens,G.W .,2004 年。外源性下平均治疗效果的非参数估计:综述。*经济学与统计学回顾*, *86* (1),第 4–29 页。*
*King,g .和 Nielsen,r .,2019。为什么倾向评分不应用于匹配。*政治分析*, *27* (4),第 435–454 页。*
*拉隆德,R.J .,1986 年。用实验数据评估训练计划的经济计量评估。《美国经济评论》第 604-620 页。*
*neuhuser,m .,Thielmann,m .和 Ruxton,G.D .,2018。二元结果的倾向分数分层中的层数。医学科学档案:AMS , *14* (3),第 695 页*
*罗森鲍姆和鲁宾,1984 年。使用倾向评分子类化减少观察性研究中的偏倚。*美国统计协会杂志*, *79* (387),第 516–524 页。*
*罗森鲍姆公关公司,2005 年。观察研究中的敏感性分析。*行为科学统计百科*。*
# *喜欢读这本书吗?*
> *请在 [LinkedIn](https://www.linkedin.com/in/leihuaye/) 和 [Youtube](https://www.youtube.com/channel/UCBBu2nqs6iZPyNSgMjXUGPg) 上找到我。*
>
> *还有,看看我其他关于人工智能和机器学习的帖子。*
# 因果推理中的倾向分数和逆概率加权
> 原文:<https://towardsdatascience.com/propensity-scores-and-inverse-probability-weighting-in-causal-inference-97aa53f3b6ce?source=collection_archive---------3----------------------->
## [Bcn 因果 ALGO](https://towardsdatascience.com/tagged/bcn-causal-algo) ,[动手教程](https://towardsdatascience.com/tagged/hands-on-tutorials)
## 全球概况

来源:[https://pix abay . com/es/photos/horizon tales-edad-pesos-930716/](https://pixabay.com/es/photos/horizontales-edad-pesos-930716/)
这是在[因果 ALGO Bcn](https://medium.com/u/5637a0e9712?source=post_page-----97aa53f3b6ce--------------------------------) 内与[努里亚·科雷亚·马尼亚斯](https://medium.com/u/c05c0c26d946?source=post_page-----97aa53f3b6ce--------------------------------)、[耶稣·塞奎德斯](https://medium.com/u/b3fed4bbda55?source=post_page-----97aa53f3b6ce--------------------------------)、[霍安·卡普德维拉·普约尔](https://medium.com/u/57efda3708cd?source=post_page-----97aa53f3b6ce--------------------------------)和[博尔哈·维拉斯科](https://medium.com/u/cd162631459a?source=post_page-----97aa53f3b6ce--------------------------------)的联合作品。在这里可以找到[núRIA Correa Maas](https://medium.com/u/c05c0c26d946?source=post_page-----97aa53f3b6ce--------------------------------)的动手贴!
在本帖中,我们将讨论两种众所周知的用于计算平均治疗效果(ATEs)的技术:倾向得分分析和逆概率加权。这篇文章假设你已经有了因果推理的基本概念,也就是说,你理解在混杂情况下估计影响的问题。

混杂变量是那些影响治疗分布和结果的变量。作者图片
倾向得分分析的主要优势在于它能够将多维问题简化为一维问题。一旦为每个观察结果计算出倾向得分,我们就可以确保我们正确地比较两个可能不同的人群,即治疗组和对照组。此外,治疗的效果只能基于这样的分数进行事后计算。逆概率加权使用不同的数值公式来实现相同的目标,即计算 ate。
# ATEs 提醒
ATEs 代表平均治疗效果。也就是说,当你有两组病人,治疗组和未治疗组时,你想知道哪一组是治疗的结果(例如,康复的概率)。除非你进行随机对照试验(RCT),否则你不能只看两者之间恢复的差异,因为治疗组和非治疗组可能有非常不同的属性。

治疗组可能具有不同的属性分布。作者图片
让我们首先通过众所周知的肾结石问题来提醒混淆调整的基本原理。在这个问题中,医生想知道在 A 和 B 两种治疗方法中,哪一种有更好的恢复率。当他们查看数据时,他们发现了这个奇怪的情况。

辛普森悖论——肾结石。作者图片
当数据按结石大小分层时,治疗 A 在两种情况下都更好。然而,当综合分析时,治疗 B 更好。原来,医生在分配治疗方案时,之前一直在猜测结石的大小。由于治疗 A 是手术,而治疗 B 是某种药丸,较大的结石(更难治愈)往往主要由治疗 A 来治疗,从而在治疗之间产生分布不平衡。这种动态可以通过有向无环图(DAG)来表示

作者图片
在这种情况下,X 是一个变量,但当它是一个向量时,同样的公式也适用,这将是实际情况。为了消除混杂因素(影响治疗和结果的变量)的影响,假设没有混杂因素缺失,我们可以使用以下**调整公式计算 ate。**

这个公式基本上告诉每个人,而不仅仅是过去发生的特定选择的患者,如果你接受治疗 *t* 会发生什么。正确理解这个公式需要一些时间,我们在这里不做解释。关于这个话题的参考资料可以在[这里](http://bayes.cs.ucla.edu/PRIMER/)找到。
我们的主要目标是知道如果我们给每个人治疗 A 或 B 会发生什么。
# 逆概率加权
正如我们所见,分析数据的主要问题是群体不平衡。这可能会在群体之间产生有偏见的比较。

编造数据的动机示例。作者图片
我们能想到的一个想法是,如果我们以某种方式重新加权每一组,使它们**反映大小的全球分布**而不是每个治疗组的大小,会怎么样。例如,对于大石块分组

作者图片
我们可以使用图中所示的重量。这些权重恰恰是倾向得分的倒数,倾向得分是在给定患者属性的情况下,被分配到特定治疗组的概率(我们将在下一节更详细地讨论这一点)。
这种直觉可以正式反映在下面的公式中,其中,乘以倾向得分,我们得到逆概率加权公式

逆概率加权公式
这个公式有一个数字问题。我们必须除以倾向分数。在某些情况下,对于某些 *x* ,可能会发生处理的概率可能很低,很容易增加其方差。所以它并不总是被建议。
# 倾向得分
让我们从另一个角度来看调整公式。回忆

其中 X 是具有所有潜在混杂因素属性向量。假设您想要比较接受治疗和未接受治疗的患者的效果与您想要比较的结果 *y.*

所以前面的公式可以读作取一个具有属性的病人 *x* ,并计算治疗和非治疗的影响

并将这些量与频率加权相加你会看到这些属性 *x* ,即 *P(x)* 。
然而,这种方法有一个问题:每个患者只有一个治疗版本(治疗或未治疗),因此两个术语中的一个将不可用(当然,除非您有另一个具有完全相同属性的患者,这并不经常发生)。这就是为什么在某些情况下,因果推断问题可以被看作是一个数据缺失问题。
一个近似的解决方案是为每个具有属性 *x* 的患者找到另一个属性 *x'* 尽可能接近的患者。这就是所谓的**匹配**。这个直观的想法到了一个实际问题,主要是定义“尽可能接近”是什么意思。这是因为不同的属性可能会对我们的问题产生不同的影响,并且不清楚如何相应地对它们进行优先级排序。
Rubins 和 Rosenbaum 在“因果效应观察研究中倾向分数的中心作用”(1983)中找到了一个解决方案。要找到一个有可比性的患者,不需要再找一个有相同属性的。找到一个被选中概率相同的就够了!这个量被称为**倾向得分**。
也就是说,对于每组属性 *x* ,您需要计算

这可以通过逻辑回归(或者,事实上,任何适合你的机器学习模型)来实现。
计算整个人群的倾向得分有助于我们管理**共同支持假设。**同样,对于每个病人我们需要在其他治疗组内找到一个相似的。如果某一组中的某些类型的患者与另一组中的其他患者差异太大,该怎么办?这意味着我们找不到合适的配型,所以我们不能说如果我们做了替代治疗,这个病人会怎么样。
可视化两组的分布,我们可以识别没有适当匹配的区域。

来源:[https://www . slide share . net/ERF _ latest/prevention-score-matching-methods](https://www.slideshare.net/erf_latest/propensity-score-matching-methods)
那些在共同支持之外的患者应该从比较中去除。Rubin 和 Rosenbaum 的论文确保了在这个选择之后,比较是适当的,意思是无偏的,所以可以正式地完成。
倾向得分也可以用来计算 ate。通过一些计算(为简单起见省略),我们可以看到,治疗和未治疗的差异可以用与以前类似的方式计算,但只使用倾向得分

这个公式导致计算 ate 的不同算法,一些算法在 Rubin 和 Rosenbaum 的论文中提出。
* 版本 1:对于每个接受治疗的患者,找到另一个具有相同倾向评分的未治疗患者,并计算结果的差异。平均所有结果。
* 版本 2:对于倾向 p 的每个水平,对具有该水平的治疗和未治疗患者进行采样,并计算差异。平均所有结果。
* 亚分类:Bin 倾向评分,对于每个水平 p,计算平均治疗差异。平均所有结果。
# 结论
* 平均治疗效果可以使用等效公式计算:逆概率加权和倾向得分
* 逆概率加权,因为潜在地除以小概率会遭受大的变化
* 倾向得分可用于找到共同支持的区域
* 倾向得分可以以多种方式用于计算 ate
# 循环中的先知
> 原文:<https://towardsdatascience.com/prophet-in-a-loop-a875516ef2f9?source=collection_archive---------22----------------------->
## 一次性预测所有销售项目

[fbhler&or endt,Die verhltnisse,2011,墙板上的木制物品,210 × 440 × 25 c](http://www.boehler-orendt.com/pdfs/Boehler_&_Orendt_Die_Verhaeltnisse.pdf) m(经我的朋友马蒂亚斯·布勒友好许可)
**动机:**
我们将使用脸书的先知来预测每件商品的销售额。已经有很多很棒的教程教你如何使用 Prophet。这篇文章关注的是如何一步到位(循环)地单独预测所有销售项目。
**解决方案:**
首先,我们将导入必要的库:
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from fbprophet import Prophet
%matplotlib inline
如果你想使用谷歌的 Colab,请确保首先安装你的谷歌硬盘:
To be able to access the data stored in Google Drive, we first need to mount our Google Drive so we can load and save files to it.from google.colab import drive
drive.mount(‘/content/gdrive’)#We’ll need to put in a token which Google will generate for us as soon as we click on the link.

您将在安装驱动器后收到此消息。
然后我们将看看我们的数据框架(数据可以在我的 [github 库](https://github.com/DAR-DatenanalyseRehberg/DDDD_Data-Driven-Dealings-Development)中找到):
data = pd.read_excel('/content/gdrive/MyDrive/DDDDFolder/DDDD.xlsx')
data.head()

因此,我们得到的是每个销售日、客户(例如,客户代码 0 是一个特定的客户)和销售项目(类似于客户,销售项目 0 是一个特定的销售项目)的销售额(按金额和价值)。我们将通过查看过去的销售日期来预测未来的销售额。因此,我们将删除其他列:
dfProphet=data.drop(columns=[ 'Customer','SalesItem','SalesTransactionID','SalesValue'])

现在我们想对月销售额进行分组。我们首先需要索引销售日期:
dfProphet['SalesDate'] = pd.to_datetime(dfProphet['SalesDate'])dfProphet = dfProphet.set_index('SalesDate')dfProphet.head()

之后,我们可以使用每月的第一天(月初,毫秒)对每月销售额进行分组:
dfMonthly = dfProphet.resample('MS').sum()
M stands for end of month, MS start of month, D daily and w weekly
dfMonthly.head()

请注意,FB Prophet 据说对每日时间序列效果最好。但是在我们的例子中,我们继续按月销售。
我们必须将 SalesDate 重命名为 ds,将 SalesAmount 重命名为 y(这就是 Prophet 的工作方式)。
dfMonthlyProph = dfMonthly.reset_index().dropna()
dfMonthlyProph.columns = ['ds', 'y']
dfMonthlyProph.head()

让我们看一下时间线图:
fig = plt.figure(facecolor='w', figsize=(20, 6))
plt.plot(dfMonthlyProph.ds, dfMonthlyProph.y)

为了进行预测,我们需要通过选择季节性模式和间隔宽度来实例化模型,并通过设置周期和频率 MS(用于月开始)的变量来设置我们想要预测的月数。
之后,我们可以通过实例化模型来绘制实际值和预测值,并绘制预测值:
m = Prophet(seasonality_mode='additive',interval_width=0.95).fit(dfMonthlyProph)
additive vs multiplicative seasonality, 95% confidence intervalfuture = m.make_future_dataframe(periods=12, freq='MS')
vs just periods=365 fcst = m.predict(future)
fig = m.plot(fcst)

做同样的事情,但是这次是动态的:
from fbprophet.plot import plot_plotly
import plotly.offline as py
py.init_notebook_mode()
fig = plot_plotly(m,fcst)#py.iplot(fig)
fig.show(renderer="colab")

这张图表想告诉我们什么?蓝线是我们的预测销量(Prophet 在尝试拟合一个模型时预测的,该模型在我们的实际数据点上具有最低的总体误差)。黑点是我们过去的实际销售量。蓝色阴影是我们的置信区间,这意味着模型将估计销售量必须在此范围内。我们可以看到,在 2017 年 5 月,我们面临的第一个实际销售数量不符合模型的置信区间。
# 做循环
到目前为止,我们已经对所有客户的所有销售项目进行了预测。如果我们想要预测特定的客户和/或销售项目组合,我们必须事先进行筛选。
作为一种更方便的方法,现在让我们看看如何在一个循环中实现这一点。我们将对每个销售项目进行预测,并最终将所有销售项目的实际值和预测值导出到一个 Excel 文件中:
import pandas as pd
from fbprophet import Prophetdata = pd.read_excel('/content/gdrive/MyDrive/DDDDFolder/DDDD.xlsx')#If you should receive an error "future is not defined" when running #with your data, your SalesDate column in Excel might not be #formatted as date.gData = data.groupby(['SalesItem', 'SalesDate'])['SalesAmount'].sum() #.agg(F.collect_list("SalesAmount")).sort('SalesItem')gData = gData.to_frame().reset_index()
itemlist = gData.SalesItem.unique()
m = Prophet()
fcst_all = pd.DataFrame() # store all forecasts herefor x in itemlist:
temp = gData[gData.SalesItem == x]
temp = temp.drop(columns=[ 'SalesItem'])
temp['SalesDate'] = pd.to_datetime(temp['SalesDate'])
temp = temp.set_index('SalesDate')
d_df = temp.resample('MS').sum()
d_df = d_df.reset_index().dropna()
d_df.columns = ['ds', 'y']try:m = Prophet(seasonality_mode='additive',interval_width=0.95).fit(d_df)
future = m.make_future_dataframe(periods=12, freq='MS')
except ValueError:
pass
fcst = m.predict(future)
fcst['SalesItem'] = x
fcst['Fact'] = d_df['y'].reset_index(drop = True)
fcst_all = pd.concat((fcst_all, fcst))
print(x)
fcst_all.to_excel('ProphetFc.xlsx', index=False)

根据您的数据量,这可能需要一些时间。
恭喜,现在我们在一个数据框架(df)中拥有了每个销售项目的所有实际过去值,包括预测值。如果列 fact 用 y 填充,这意味着该记录是真实的(否则是预测的)。
非常感谢您的阅读!希望这篇文章对你有帮助。请随时在 [LinkedIn](https://de.linkedin.com/in/jesko-rehberg-40653883) 、 [Twitter](https://twitter.com/DAR_Analytics) 或[工作室](https://jesko-rehberg.medium.com/virtual-reality-vr-for-education-a532aa5b6272)与我联系。
<https://jesko-rehberg.medium.com/membership>
Jupyter 笔记本和数据文件也可以在我的 Github 存储库中找到:[https://github.com/DAR-DatenanalyseRehberg/ProphetInALoop](https://github.com/DAR-DatenanalyseRehberg/ProphetInALoop)
# 先知对神经原植物
> 原文:<https://towardsdatascience.com/prophet-vs-neuralprophet-fc717ab7a9d8?source=collection_archive---------2----------------------->
## 著名先知模型的并排比较
先知模型是有效的,可解释的,并且易于使用。但是哪个更好呢?
在这篇文章中,我们将探索 Prophet 和 Neural Prophet 的实现差异,并进行一个快速的案例研究。这里是代码。

图 Prophet 和 NeuralProphet 算法之间的训练精度示例— [src](https://neuralprophet.com/html/energy_hospital_load.html) 。图片作者。
但在我们开始编码之前,让我们快速覆盖一些背景信息,更多信息可以在[这里](/how-to-develop-interpretable-time-series-forecasts-with-deep-learning-50b27bfcb8a1)找到。
Prophet (2017)是 NeuralProphet (2020)的前身——后者融入了一些自回归深度学习。从理论上讲,NeuralProphet 应该总是拥有与 Prophet 相同或更好的性能,所以今天我们将对这一说法进行测试。
让我们开始吧。
# 基本规则
## 数据
我们将使用加州能源需求的每日时间序列(图 2)。

图 2:加州观察到的电力需求的每日时间序列— [src](https://www.eia.gov/electricity/gridmonitor/dashboard/electric_overview/US48/US48) 。图片作者。
正如你在上面看到的,有很强的年度季节性,在夏季达到高峰,可能是由于空调使用的增加。虽然从这张图表中不太明显,但我们也希望看到每周的季节性-我们的假设是,周末和工作日的用电量会有所不同。
对于像 ARIMA 这样的传统时间序列模型,所有这些季节性要求我们指定*订单*(回顾指数),对应于我们观察到的任何季节性水平。另一方面,prophet 模型自动将这种正弦运动封装在傅立叶级数中,因此这两种 Prophet 模型都应该有效地利用上述季节性。
最后,再观察一下数据,您会注意到轴名是 *y* 和 *ds* 。我们已经将我们的时间序列存储在一个熊猫数据帧中,该数据帧已经过重组,以适应 Prophet 的规格。使用的确切代码如下…
df = read_data() # helper func
df = df[['Local date','D']]
df.columns = ['ds','y']
如果你想继续下去,数据是从来源:美国能源信息署(2021 年 12 月)下载的[,也可以在这里](https://www.eia.gov/electricity/gridmonitor/dashboard/electric_overview/US48/US48)[获得](https://github.com/mberk06/DS_academic_papers/blob/master/EnergyTSData/Region_CAL.xlsx)。这里是[版权信息](https://www.eia.gov/about/copyrights_reuse.php)。
## 估价
我们感兴趣的度量将是 RMSE(图 3)。

图 3:均方根误差,其中 y_hat 是我们的预测值,y 是我们的观测值。图片作者。
为了公平地比较我们的两个模型,我们将利用交叉验证(CV)。请注意,传统的 CV 假设我们的数据是可交换的,即我们样本的任何排序都同样可能发生。对于时间序列数据,有一个时间成分,所以不幸的是,完全随机化不能应用。
相反,我们尝试许多不同的截止日期,对所有先前的数据进行训练,并用接下来的 *n* 周期进行测试。这被称为**滚动原点回溯测试**。通过迭代训练和确定样本外精度,我们可以得到模型精度的更稳健的估计。
在我们的案例中,我们从 2017 年 12 月 22 日开始迭代,以 180 天为增量,在 2021 年 12 月 6 日左右结束。
# 竞争者 1:先知
我们的第一个竞争对手是[脸书先知](https://facebook.github.io/prophet/)。这是一种开源算法,自 2017 年问世以来,已经受到了极大的欢迎。它的主要卖点是易于使用,可解释,并且易于与主题专家互动。
介绍完毕,让我们开始[编码](https://github.com/mberk06/DS_academic_papers/blob/master/28_prophet_vs_neural_prophet.py)。
首先,我们将创建我们的模型,并拟合我们的重构数据。
m = Prophet()
m.fit(df)
很复杂,对吧?接下来,我们将进行预测,并得出一些关键图。
create forecast
future = m.make_future_dataframe(periods=365)
forecast = m.predict(future)# create plots
pred_plot = plot_plotly(m, forecast)
comp_plot = plot_components_plotly(m, forecast)
首先,我们创建一个未来 365 个周期的数据框架,在我们的例子中是 365 天。然后,我们相应地进行预测,并将预测值和预测区间一起存储在变量 *forecast* 中。最后,我们创建一个预测图和组件图。让我们依次看一看每一个。
首先,我们有图 4 中的**预测图**。

图 4:365 天的预言者预测图。图片作者。
正如你所看到的,在 2022 年左右,我们的数据停止了,我们的预测开始了,这是由缺乏观测数据(黑点)所表明的。
好吧,这是一张漂亮的照片,但是它并没有告诉我们很多幕后发生的事情。为此,我们将转向组件图(图 5)。

图 5:先知组件图。图片作者。
在上面的图中,我们有三个图表,它们都提供了关于我们的数据的有用结论。首先,我们可以看到,在我们的上图中,趋势在我们数据的持续时间内相当不稳定。从 2017 年年中开始,我们看到电力需求有所下降,但总体降幅并不巨大。第二,根据第二张图表,电力需求在夏季最高,在春季最低。这些观察与我们的直觉一致。第三,周末的消费似乎明显低于工作日。同样,这也符合我们的期望。
谈一个可解释的模型!
Prophet 的组件图是了解幕后发生的事情的一个非常有用的窗口——不再有黑盒。注意 NeuralProphet 也有同样的功能。
但是,一个模型的好坏取决于它的准确性,所以让我们来看看准确性。
使用内置的交叉验证方法,365 预测观察到的 **RMSE 为 48810.12** 。我们的 y 值是几十万,在 60 万到 1.2 万之间,所以 48k 的 RMSE 似乎很低。
让我们看看我们是否可以通过一些深度学习来战胜它。
# 竞争者 2: NeuralProphet
我们的下一个模型是 Prophet 的第二次迭代。它将适合自回归(滞后)数据的深度学习项纳入我们的方程。理论上和[经验上](/how-to-develop-interpretable-time-series-forecasts-with-deep-learning-50b27bfcb8a1?source=user_profile---------0-------------------------------),NeuralProphet 是一个优越的模型。但是让我们看看这种优势是否适用于我们的能源需求数据集。
让我们得到[编码](https://github.com/mberk06/DS_academic_papers/blob/master/28_prophet_vs_neural_prophet.py)。
首先,与 Prophet 一样,我们需要创建并拟合一个模型。请注意,Prophet 的两次迭代之间的语法非常相似。
m = NeuralProphet()
metrics = m.fit(df, freq="D")
现在我们已经创建了我们的模型,让我们创建一个预测,并得到一些关键的情节。我们将使用 NeuralProphet,**中的默认参数,这些参数不包含深度学习。**
create forecast
df_future = m.make_future_dataframe(df, periods=365)
forecast = m.predict(df_future)# create plots
fig_forecast = m.plot(forecast)
fig_components = m.plot_components(forecast)
fig_model = m.plot_parameters()
和上面的值一样,让我们看看我们的预测图(图 6)。

图 6:神经营养预测图。图片作者。
上面显示的值比先前的预测放大了很多,但两者具有相同的结构。总的来说,他们似乎也有几乎相同的价值观,这是意料之中的,因为他们都在使用没有深度学习的 Prophet。
最后,使用 NeuralProphet 默认参数的内置精度度量是 62162.133594 的 **RMSE。经过一番挖掘,发现这两个库使用了不同的回溯测试函数,因此我们将创建一个自定义函数来进行公平的比较。而且,我们会爆发深度学习。**
# 判决
好了,现在我们对两个库都有了感觉,让我们开始比赛吧。
首先,我们定义自定义回滚测试函数。关键的概念是我们创建许多训练/测试分割,如下所示。
train_test_split_indices = list(range(365*2, len(df.index) - 365, 180))train_test_splits = [(df.iloc[:i, :], df.iloc[i:(i+365), :])
for i in train_test_split_indices]
从这里,我们可以迭代`train_test_splits`,训练两个模型,并比较结果。
随着我们的数据建立起来,我们已经准备好在神经营养中进行深度学习了…
neural_params = dict(
n_forecasts=365,
n_lags=30,
yearly_seasonality=True,
weekly_seasonality=True,
daily_seasonality=True,
batch_size=64,
epochs=200,
learning_rate=0.03
)
如上所示,我们已经通过 *n_lags* 参数激活了自回归功能。我们还添加了一些其他潜在有用的参数,比如设置时期数、季节性类型和学习率。最后,我们将预测范围设定为 365 天。
在通过我们的滚动原点回溯测试运行这两个模型后,我们得到以下 RMSE。注意,出于解释目的,我们还包括了平均绝对百分误差(MAPE)。

图 7: RMSE 和 MAPE 对 Prophet 和 NeuralProphet 进行滚动原点回溯测试。图片作者。
结果相当令人惊讶。
在 730 天的训练中,NeuralProphet 的表现远远超过 Prophet。然而,凭借 910 天和 1090 天的训练数据,NeuralProphet 以微弱优势击败了 Prophet。最后,凭借 1270 天或更长时间的训练数据,Prophet 在准确性上超过了 NeuralProphet。
**在这里,NeuralProphet 在较小的数据集上更好,但 Prophet 在大量训练数据上更好。**
现在,在运行模型之前,我们不会期望这个结果,但是回顾一下,这是有意义的。深度学习方法如此有效的一个原因是它们可以拟合极其复杂的数据。然而,如果提供了太多的噪声数据,它们可能会过度拟合,这使得更简单和“更平滑”的模型表现得更好。
一种可能的解释是,有了足够的训练数据,Prophet 对非常周期性的数据更有效。如果由于季节性的原因,大部分的运动是正弦的(看起来是这样),那么就很难改进基于傅立叶级数的模型。
如果你对重现结果感到好奇,请查看这里的[代码](https://github.com/mberk06/DS_academic_papers/blob/master/28_prophet_vs_neural_prophet.py)。此外,如果有不正确的执行,请在这里或回购上留下评论。
## 先知与政府
最后,我们将以一个有趣的音符结束。
EIA 每天都会发布一天的预报。出于对我们的年度模型进行对比的好奇,我很快计算了政府预测的 RMSE 和 MAPE。数值分别为 28432.85 和 0.0242。
将这些数字与上表中的数字进行比较,我们的 Prophet 模型的误差大约是两倍,**但是对于 365 天的预测范围。政府正着眼于**未来的一天。****
一个冷静而简单的后续行动将是试图用任何一个先知模型来击败政府。应该不会太难吧?
*感谢阅读!我会再写 24 篇文章,把学术研究带到 DS 行业。查看我的评论,链接到这篇文章的主要来源和一些有用的资源。*
# 2021 年数据科学的利与弊
> 原文:<https://towardsdatascience.com/pros-and-cons-of-data-science-in-2021-b3c333dfa79d?source=collection_archive---------32----------------------->
## 意见
## 5 个优点和 5 个缺点,以确定数据科学是否是你在 2021 年的最佳职业选择,或者你是否有更好的选择

本杰明·戴维斯在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍摄的照片
> **自然界的一切事物都有其优点和缺点。数据科学也不例外。**
对于现代数据科学的未来,有很多令人兴奋的炒作。关于数据科学将如何改变世界的未来以及数据如何成为当今时代的最大话题,有大量的猜测。
大多数科技巨头,无论是大公司还是小公司,都高度重视数据的重要性,将其视为彻底改变当今世界事态的主要方面。脸书、谷歌、亚马逊、微软、IBM 等。和许多其他公司一样,也在加大数据宣传的力度。
虽然数据现在比以往任何时候都更受欢迎,获得数据科学家的地位被认为是荣耀的顶峰,但获得这一职位有什么好处和困难呢?
在本文中,我们旨在回答这个存在于大多数数据科学爱好者和渴望者中的问题。我们将分析一个简明的列表,列出与数据科学相关的优点和缺点。
首先,我们将列出五个优点,以清楚地了解数据科学的一些最佳优势和优点。我们的重点将是为什么它们在现代如此有益和受欢迎。
然后,我们将讨论数据科学的缺点或不足,这些缺点有时可能会成为阻碍,导致有志之士和爱好者在选择该主题之前对他们的选择进行事后批评。
我们将尽可能中立地分析这两方面的优点和缺点。文章中提到的观点是固执己见的,它应该给观众一个坚实的立场。
最终,权衡所有可能的选择,选择最适合你的决定来实现你的成功之路,这是你个人的选择。所以,事不宜迟,让我们开始吧!

# 1.无数的职业机会

由 [Austin Distel](https://unsplash.com/@austindistel?utm_source=medium&utm_medium=referral) 在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍摄的照片
这一优点不应该让任何人感到惊讶,因为数据科学被认为是 21 世纪最性感的工作。从学生和专业的角度来看,这个领域提供了许多选择。
虽然学生可以从广泛的选项中进行选择,如数据科学、人工智能、机器人、计算机视觉、自然语言处理等等,但职业机会也很广泛。
数据科学领域的这些职业机会包括数据科学家、机器学习工程师或机器学习科学家、应用程序或企业架构师、数据或基础架构架构师、数据工程师、统计学家、商业智能开发人员和数据分析师等。
作为一名成功的数据科学家,数据科学领域的众多职业机会和最终的成功是无限的。这也是这门学科吸引人的优势之一。
我强烈推荐通过下面提供的链接查看我以前的一篇关于从数据科学中赚取收入的五种最佳方式的文章。这应该是一个很好的起点来分析你的职业选择。
</5-best-ways-to-earn-income-from-data-science-a9c8fed1eee6>
# 2.为革命的未来培养基本素质
数据科学是一门新学科,在这个领域有大量的新发现有待实现。
学习数据科学的最大好处是,它还能自动提高你在现实未来的大部分重要课题上的技能水平。
数据科学的大部分组成部分,如数据、对象存储、编程、AI、计算机视觉、自然语言处理等。,是数据科学中在未来几十年仍将保持高需求的一些方面。
你为数据科学培养的基本素质将是有益的和永恒的。他们会帮助你完成你决定说服的任何未来任务或任务。
革命性技术的步伐不会很快放缓。数据科学提供了最好的工具来掌握辉煌的未来岁月,以在未来几年中给人类带来恩惠。
# 3.技术和实践能力的权重

米歇尔·卡塔利萨诺在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上的照片
数据科学是一门可以让你充分利用大脑能力的学科。
这不是一份案头工作或复制粘贴工作,而是一个你必须不断思考以产生最佳和最有效结果的职位。
数据科学最棒的地方在于,随着你不断学习,你会不断开发新的能力。
如果您正在从事包括部署在内的端到端数据科学项目,您将会学到一些最基本的技能。
这些技能包括技术方面,如数据清理、数据分析、构建机器学习和深度学习模型,使用云技术。
所涉及的实践方面是沟通技巧、研究、有效的互动、自信和其他软技能,这些都是你在数据科学领域取得成功所必须发展的。
# 4.获得有趣和迷人的知识
数据科学不是干田!
这是一个持续不断发展的课题。每天都在开发各种新的性能指标和功能。
这些进步可以从全年连续发表的研究论文中看出。
从数据科学这门学科中可以学到很多东西。机器学习、深度学习、神经网络的一些概念极其耐人寻味,引人入胜。
由于数据科学领域在不断发展,每天都有一些东西可供您学习。这个主题非常广泛,为爱好者提供了很多可以探索和思考的东西。
提几个有帮助的资源,我主要推荐的是 Stack Overflow、discord channels、YouTube 视频、免费在线代码营、GitHub、走向数据科学等。都是有用的资源,可供我们所有人利用和提高我们的技能。
因此,请确保您继续学习数据科学并从中获得乐趣。这一点是选择一个不断发展的主题的最佳优点之一。你可以考虑很多资源,学到很多东西。
# 5.创建和开发独特的创新项目

照片由 [Aaron Burden](https://unsplash.com/@aaronburden?utm_source=medium&utm_medium=referral) 在 [Unsplash](https://unsplash.com/?utm_source=medium&utm_medium=referral)
凭借从数据科学中获得的无限和巨大的知识,您可以扩展这些技术,用于开发独特和创新的项目,以改变未来的格局。
数据科学最好的部分是你可以在数据、可视化和分析技术的帮助下创建杰出的项目,构建模型,训练这些模型并运行它们,最后部署它们以达到广泛的受众。
从使用机器学习或深度学习模型开发单个端到端数据科学项目,您可以获得大量知识。它让你对这个领域周围的美景有了深刻的见解和全面的了解。
您创建的大多数项目都可以对数据科学和世界的未来产生积极的影响。开发项目并为之努力无疑是数据科学最好的部分。

> ***免责声明:*** 根据每个人对数据科学主题的看法,这些缺点中的一些实际上可以被视为优点。所述术语倾向于更一般的受众及其方法和观点。
在继续研究本文中提到的五个缺点之前,我强烈建议你们看看我以前的一篇文章,关于追求数据科学的十个错误理由。这些缺点适用于对该领域感兴趣,但不同意以下文章中提到的 10 点中任何一点的爱好者。
</10-wrong-reasons-to-become-a-data-scientist-319531bb874e>
# 1.广袤的田野

照片由[玛利亚·什克里耶娃](https://unsplash.com/@mariashkliaeva?utm_source=medium&utm_medium=referral)在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 拍摄
数据科学是一个巨大的领域。
它包括大量子领域,包括大数据、数据挖掘、机器学习等主题。它使用科学的方法、流程、算法和系统从许多结构化和非结构化数据中提取知识和见解。
有时,这些庞大的主题和领域是极其费力的。如果你有一个时间限制来涵盖这些概念在一个特定的时期内很短的时间,它可以感觉到负担,由于大量的技术和理论参与的要求。
如果你被卡住了,很困惑,而且你没有开始准备的计划,我会推荐你看看下面这篇文章。这是在 12 个月内掌握数据科学的 12 步指南。
</12-steps-for-beginner-to-pro-in-data-science-in-12-months-c6f6ba01f96e>
# 2.陡峭的学习曲线
我们已经了解到,数据科学是一个特别庞大的领域,其广泛的信息跨越了数据科学的许多方面和子领域。
数据科学的一些概念比其他概念需要更多的时间和精力。在某些情况下,理解每个单独主题背后的复杂细节、数学和编程是耗时且费力的,尤其是在您日程繁忙的情况下。
数据科学的特定方面有一个陡峭的学习曲线。它们需要长期的奉献和练习才能掌握。和编程类似,数据科学是你需要随着时间的推移不断接触,不断刷知识的东西。
数据科学有如此多的内容和学习材料,初学者可能很难理解这些无尽的可能性,这总是会让人怀疑开始是否太晚了。
好消息是,开始永远不会太晚,你总是可以掌握这些概念并学习更多。你完全可以通过一个完美的计划来学习和研究数据科学,以达到预期的效果。
# 3.它并不总是有趣和游戏

[JESHOOTS.COM](https://unsplash.com/@jeshoots?utm_source=medium&utm_medium=referral)在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍照
数据科学并不总是有趣和游戏。
说学习过程有趣、有娱乐性之类的话很酷。但是,需要注意的是,数据科学有时会有潜在的压力和困难。
当你在业余时间做一个特定的项目时,你可能会意识到为了完全掌握这个主题你必须深入思考的基本概念的本质。
即使在工业背景下,你也被期望在特定的时间内完成分配给你的工作。整个过程中的每一步都不可掉以轻心。
所涉及的步骤包括数据收集、数据可视化、数据分析、数据清理,这些有时真的很烦人,并且很难宣布改善模型性能的最佳步骤。
完成这些步骤后,您可以立即处理数据并开始构建模型。你需要保证你的模型是完美的,你需要尝试无数的模型和准备过程。
一旦您构建了模型,它将经过大量的测试和验证过程,以确保模型适合部署。最后,在获得期望和结果之后,您的模型就被部署了。
这个周期中涉及的工作程序和压力可能不适合每个计划在未来从事数据科学工作的人。然而,一些爱好者会喜欢这整个工作流程。
所以,归根结底,这是一个选择的问题,也是你喜欢做什么的问题!
# 4.无尽的学习循环
在花了几天时间理解一个特定的概念来解决一个复杂的任务后,你能够成功地完成它。
然后,你开始用你新学到的知识做这个项目,在项目进行到一半的时候,你休息一下,读一篇新的研究论文。
在阅读了这篇新的研究论文后,你最终发现你花了几个小时研究的概念不再足够相关,并且有了一个更重要的新主题。
虽然虚拟故事可能有点夸张,但当大多数爱好者不断跟上数据科学领域发生的各种变化时,他们会有类似的感觉。
数据科学的学习过程永远不会停滞不前。你需要不断更新自己,跟上最新的和正在出现的趋势。然而,这个过程可能不是每个人都喜欢的。
# 5.数据科学有时可能会让人失去动力

照片由[埃里克·沃德](https://unsplash.com/@ericjamesward?utm_source=medium&utm_medium=referral)在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 拍摄
假设您正在从事一个复杂的数据科学项目。最初,你对你能学到的和客观创造的各种东西感到非常兴奋。
当你开始着手这个项目时,你意识到它并不像你想象的那么简单。你最终陷入了一个错误,你完全迷失了。你谷歌了一下,你做了你能做的一切,但是解决方案并没有出现。
首先,这会导致挫败感,然后产生一种消极情绪。你开始怀疑自己,得出数据科学不适合你的结论。
不过,别担心!你并不孤单,许多数据科学爱好者、渴望者和现在的专家都遇到过这些问题。最好的解决办法是克服它们,尽你所能。
数据科学有一个巨大而精彩的社区,有大量清晰的描述性资源。确保你充分利用了以下资源,并从中受益。
# 结论:

在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上 [Pablo Heimplatz](https://unsplash.com/@pabloheimplatz?utm_source=medium&utm_medium=referral) 拍摄的照片
> 当面对两种行动方案时,我在一张纸上记下支持每一种方案的所有论点,然后在另一面写下反对每一种方案的论点。然后,通过权衡赞成和反对的论点,并把它们相互抵消,我采取剩下的方法。
>
> **-** [**本杰明·富兰克林**](https://www.azquotes.com/author/5123-Benjamin_Franklin)
在这篇文章中,我们讨论了五个优点和五个缺点,以确定数据科学是否是你在 2021 年选择的完美职业选择。根据文章的老化程度,我很确定这些观点在未来几年仍然有效。
以更积极的方式结束这篇文章,数据科学有一个光明的未来。如果你是一个初学数据科学的爱好者,我强烈建议你研究一下这个壮观的研究领域的众多利弊。
在分析利弊之后,最终由您来决定数据科学是否是您的最佳选择。您想继续 kickstart 并开始您非凡的数据科学之旅吗?
无论哪种方式,生活都是一个充满变数和冒险的漫长旅程。确保你的决定符合你的个人兴趣。说到底,对自己和自己所做的事情感到开心才是人生最重要的事情!
数据科学拥有宝贵的元素和大量资源,可以发展成为一名成功的数据科学家。这个主题有时可能很复杂,但不可否认的是,你可以完成这么多漂亮的任务,并在这个领域创造出一系列令人兴奋的项目。
如果你对这篇文章中提到的各点有任何疑问,请在下面的评论中告诉我。我会尽快给你回复。
看看我的其他一些文章,你可能会喜欢读!
</11-crucial-mistakes-to-avoid-as-a-data-scientist-71c29aef028> </8-revolutionary-artificial-intelligence-technologies-of-the-modern-era-f8f22a4127d0> </mastering-dictionaries-and-sets-in-python-6e30b0e2011f> </10-best-tools-and-technologies-for-data-science-e335fb99c2f2> </15-awesome-python-and-data-science-projects-for-2021-and-beyond-64acf7930c20> </do-you-need-to-pay-to-learn-data-science-e71f75493e5a>
谢谢你们坚持到最后。我希望你们喜欢阅读这篇文章。我希望你们都有美好的一天!
# 保护您的资金:使用 ML/DL 检测信用卡欺诈
> 原文:<https://towardsdatascience.com/protecting-your-money-detecting-credit-card-fraud-with-ml-dl-2c4a9b9a0779?source=collection_archive---------9----------------------->
## 利用机器学习和深度学习检测信用卡欺诈,同时处理不平衡数据

瑞安出生于[Unplash](https://unsplash.com/photos/x8i6FfaZAbs)的照片
2019 年,全球信用卡欺诈损失达**286.5 亿美元***。正如商户风险委员会(Merchant Risk Council)首席执行官朱莉·弗格森(Julie Ferguson)所说,“如果一家小单店企业或一家餐馆突然亏损 1 万美元,这可能就是为该公司发工资和不发工资的区别。”许多公司,如 VISA,正在寻找基于人工智能的解决方案来解决这个问题。使用 ML 检测信用卡欺诈有许多好处,例如更多:*
* ***高效**数据处理*
* ***模式识别***
* ***准确的**预测*
*本文将演示不同的机器学习方法来检测信用卡欺诈。它们包括:*
* *随机森林分类器*
* *逻辑回归*
* *神经网络*
*您可以在此[链接](https://github.com/shayaf84/Credit_Card_Fraud_Detection)处找到该项目的 GitHub 存储库。*
# *检索数据*
*该项目使用来自 kaggle.com“信用卡欺诈检测”数据集的数据。您可以通过链接访问。出于这个项目的目的,我下载了这个文件,并把它放在我的谷歌驱动器中,因为这个文件很大。如果驱动器安装了谷歌合作实验室,就可以很容易地访问它。由于信用卡欺诈依赖于对敏感数据的处理,原始数据无法显示。因此,使用主成分分析(PCA)掩蔽了 28 个特征(V1 — V28)。只有两个变量没有被转换:*
* ***时间**“表示当前事务和数据集中第一个事务之间经过的秒数*
* ***金额**,表示交易总额。*
*响应变量 **Class** 有两个值:*
* *1 .办理诈骗案件*
* *非欺诈案例为 0*
# *设置环境和上传数据*
*我们将从导入我们将使用的关键包开始,并安装我们的谷歌驱动器,这样我们就可以上传我们的数据集!文件路径将根据存储数据集的文件夹而有所不同。*
*我们将使用的库有:*
* *Numpy*
* *熊猫*
* *Seaborn 和 Matplotlib*
* *不平衡-学习,Scikit-学习,Tensorflow*
* *谷歌。Colab(上传文件)*
# *探索性数据分析*
*让我们分析我们的数据!我们可以从查看数据集的前五行开始,并检索其形状。*
**
*作者形象*
*数据集中有 **31 列**,其中 28 列是 PCA (V1 — V28)的输出,以及数量和时间的值。我们的响应变量是 class,0 表示没有欺诈,1 表示有欺诈。数据的形状告诉我们有 284807 个条目和 31 列。*
*接下来,我们可以创建一个 Seaborn countplot 来可视化这两个类的分布。*
**
*作者图片*
*呃……这确实是一个两难的选择。如你所见,我们的数据集是*高度*不平衡的,因为我们绝大多数的数据都在 0 类中。这是个问题,因为**我们必须给两个班的模特同等的训练机会**。*
*接下来,我们可以绘制两个类的数量和时间分布图。我们可以通过索引数据帧来提取我们想要绘制的列。此外,我们可以使用 matplotlib 中的不同函数来调整字体大小、图形大小和轴标题,如下所示。*
****
*作者图片*
*从左边的图中,我们可以看到欺诈性分录往往具有较低的金额值,而非欺诈性分录。同样值得注意的是,非欺诈金额的值分布不均匀,因为该变量的较高值之间存在较大的间隔。*
*就时间而言,这两个类别之间似乎没有区别,因为这些值在欺诈性和非欺诈性条目中是一致的。这是有意义的,因为如前所述,时间特性表示从第一个事务开始经过的秒数。因此,时间变量将继续增加。假设欺诈性交易在时间上并非都与非欺诈性交易相隔离,它们都将具有连续的时间值。*
*另一个我们可以用来分析所有变量之间关系的技术,包括 PCA 转换的变量,是建立一个相关矩阵!*
**
*作者图片*
*从这个相关性矩阵中,我们可以看到数据集中的特征之间没有**主要*相关性*和**。大多数关系都在-0.2 到 0.2 之间。尽管如此,还是有一些例外,特别是在分析数量特性、V7 和 V20 之间的相关性时。*
*另一个观察是特性和类之间的最小关系。这可以归因于数据的不平衡性质,这可以淡化某些特征的重要性。*
# *分割和标准化我们的数据集*
*现在让我们放弃时间特性。这与分类无关,只是记录时间…然后,我们可以将数据分为训练和测试值,测试规模为 30%**。***
******
***作者图片***
***如果查看数据,您会发现金额值明显高于其他功能的条目。例如,在 data.head()的第一个条目中,amount 值是 149.62,这与 V1 的值(大约为-1.36)有很大的不同。为了解决这个问题,我们将使用**标准定标器。**通过缩放变量,模型的效率会提高。***
# ***用边界 SMOTE 平衡数据***
***现在,我们将需要平衡我们的数据,以确保模型对两个类都有足够的暴露度!这确保了模型不会偏向非欺诈性值,因为目前有超过 280,000 个条目的类别为 0,而只有 492 个欺诈性值。***
***要改变类 1 中值的数量,有两种可能的方法:随机欠采样(减少多数类的条目数量)和合成过采样(为少数类合成生成新条目)。因为我们希望为我们的模型提供尽可能多的数据,所以我们将使用一种称为 [Borderline-SMOTE](https://sci2s.ugr.es/keel/pdf/specific/congreso/han_borderline_smote.pdf) 的技术(Borderline Synthetic Minority over sampling Technique)。使用 Borderline-SMOTE 而不是 SMOTE 的原因是,它综合生成少数类**中不同元素之间的数据,同时忽略异常值**。离群点可以定义为比少数点邻近更多多数点的特征。***
***下面是应用边界平滑的代码。仅平衡训练数据很重要,因为测试系列必须反映真实世界的现场数据。***
************
***(左)过采样前的训练数据分布;(右)过采样后的数据分布***
# ***机器学习模型***
***我们的数据集现在平衡了!这确保了我们的模型可以从每个类中看到相同数量的元素。我们可以建立机器学习模型进行分类。将使用两种模型:***
* *****随机森林*****
* *****逻辑回归*****
***在我们开始构建模型之前,定义我们将使用的性能指标是至关重要的。由于我们的验证数据是不平衡的,如果模型每次都预测多数类,那么我们的模型将获得**显著偏斜的准确度,而不考虑少数类。**因此,我们将使用精确度、召回率和 F1 分数。***
***首先,我们将建立我们的随机森林模型,有 15 个估计量。请记住,由于数据集的大小,它将需要几分钟来运行。***
************
***四舍五入到两位小数***
***太好了!我们的模型具有很高的精确度和召回率,这意味着它选择的假阳性(精确度)或假阴性(召回率)没有真阳性多!正因为如此,F1 的分数也很高。***
***其次,我们还将建立一个逻辑回归模型来解决这个分类问题。***
************
***四舍五入到两位小数***
***嗯……在这种情况下,虽然召回率很高(模型选择的真阳性比假阴性多),但精确度却很低,这意味着模型选择的假阳性比真阳性多。因此,F1 分数下降。***
***在比较这两个模型时,评估我们用来比较它们的指标是很重要的。我们的**逻辑回归**模型有一个**更高的召回**(大约 5%),而**随机森林**分类器有一个**更高的精确度**(大约 78%)。因此,就 F1 分数而言,它是精确度和召回率的调和平均值,**随机森林模型**表现更好。***
# ***神经网络***
***最后,我们还将制作一个具有两个隐层的神经网络来解决这个分类问题。***
***在创建我们的模型之前,定义我们将使用的超参数是很重要的。以下是我使用过的方法,但是你可以随意调整它们,以达到更好的效果。***
******
***我们的神经网络——作者图片***
***如图所示,我们有:***
* ***具有 **ReLU** 激活的 **64 个神经元**的输入层***
* ***分别为 **32** 和 **16 神经元**的两个密集隐藏层,每个都有一个 **ReLU** 激活***
* *****1 神经元**的输出层,具有**s 形**激活***
***此外,我们还有:***
* ***一个 **Adam** 优化器,其学习率为 **1e-4** ,衰减率为 **1e-6*****
* *****二元交叉熵**和**精度和召回指标**丢失***
***模型将在 **5 个时期**进行训练***
***下面是我用来创建模型的代码,用的是 Tensorflow 的 Keras 模块。***
***现在我们已经建立了模型,让我们运行它来看看我们的结果!***
******
***模型的输出***
***我们可以看到,我们的验证精度从大约 **19.52** 到 **61.26%** ,而验证召回率从 **86.03** 到 **90.44%。**纪元 4 的验证召回率最高,纪元 1 的验证精度最高。该模型没有过度拟合,因为精度和召回率的值仍在增加。您可以随意更改历元数,以查看模型在更长时间内的表现!但是请注意,这将需要更长的时间来训练…***
***ML/DL 带来的 ***速度*** 和 ***准确性*** 在打击信用卡诈骗中如这个简单的例子**所示。**2019 年,Visa 通过其人工智能系统防止了约 250 亿美元的信用卡欺诈。Kount 的平台 AI 能够以快至 250 毫秒的速度返回风险率。通过利用机器学习和深度学习的力量,我们可以努力打击这种盗窃行为。***
***希望你喜欢阅读这篇文章!欢迎在 Linkedin 上加我,关注更多内容!***
# ***文献学***
***[1] Anon, [Visa 利用人工智能防止了约 250 亿美元的欺诈](https://www.businesswire.com/news/home/20190617005366/en/Visa-Prevents-Approximately-25-Billion-in-Fraud-Using-Artificial-Intelligence) (2019),商业资讯***
***[2] L .哥伦布,[人工智能如何防范支付欺诈](https://www.forbes.com/sites/louiscolumbus/2019/09/05/how-ai-is-protecting-against-payments-fraud/?sh=518f57044d29) (2019),福布斯***
***[3] N. Lee,[信用卡诈骗将因 Covid 疫情而增加,专家警告](https://www.cnbc.com/2021/01/27/credit-card-fraud-is-on-the-rise-due-to-covid-pandemic.html) (2021),美国消费者新闻与商业频道***
***[4]机器学习小组— ULB,[信用卡欺诈检测](https://www.kaggle.com/mlg-ulb/creditcardfraud)(2021 年更新),Kaggle***
***[5] S. Goswami,[类不平衡,SMOTE,临界 SMOTE,ADASYN](/class-imbalance-smote-borderline-smote-adasyn-6e36c78d804) (2020),走向数据科学***
# 全球规模行业中的原型机器学习解决方案
> 原文:<https://towardsdatascience.com/prototyping-machine-learning-solutions-in-globe-scale-industries-fff46ad8ade0?source=collection_archive---------22----------------------->
## [行业笔记](https://towardsdatascience.com/tagged/notes-from-industry)
## 如何使用一些可重复的技术在全球范围的行业中有效地构建机器学习解决方案的原型
现代全球产业中的很多问题,比如 H&M、宜家、GAP、麦当劳等等。,更好地通过机器学习(ML)解决方案来解决。即使是在供应链、营销、销售等方面的个位数百分比改善。,是成本节约或收入增加的主要原因。在一家全球性公司中,基于 ML 的产品开发非常复杂,通常需要重复许多次,以优化分布在许多部门的数十到数百个业务流程,从而影响全球数百万客户。虽然使用随机流程推出五个案例是可能的,但如果没有适当的流程,几乎不可能比这多几十到几百倍。

图 1:理解产品开发阶段的一个很好的心智模型。图片由作者提供。
在高层次上,基于 ML 的产品开发有四个不同的阶段(见图 1)。首先,当一个团队探索问题和解决方案时,它就是在自举。第二,当团队实现一个解决方案来测试该方法的可行性时,团队正在构建原型。第三,当团队在一个简单的环境中优化系统交付的解决方案时,它就是工业化的。最后,当团队在一个更复杂、更大规模的环境中改进交付的解决方案时,它是可伸缩的。虽然一个阶段中解决方案的核心设计可以很容易地转移到下一个阶段,但是实现的细节可能会发生巨大的变化。因此,执行策略和实现策略的团队应该改变,除非领域的知识应该在团队中根深蒂固,这应该是涉众的情况,而不是技术团队的情况。
虽然从自举走向工业化可能很诱人,但跳过原型制作并不是一个好主意。原型应该表明,解决一个想法是不可行的,因为不同的原因,如缺乏足够的数据,缺乏良好的验证机制等。,这有助于组织确定工业化的优先顺序。此外,它还找出了核心解决方案的构建模块,这有助于在工业化过程中优先考虑优化工作。因此,组织关注如何系统地进行原型开发是很重要的,这依赖于遵循一些易于遵循的方法,这些方法由可以日复一日重复的技术解决方案支持。在这篇文章中,我们提出了一个原型机器学习解决方案的系统方法。
# 一种机器学习原型的方法
## 设计文件
设计文档旨在帮助团队有效地计划、执行和与涉众沟通。一个团队应该从两个简单的设计文档开始:
* 一页纸
* 系统图
一页纸包括关于业务问题的形成和解决方案的评估标准的信息。一页纸包括以下内容:
* 商业案例定义
* 利益相关者和资源
* 性能指标和验证标准
* 解决方案优化指标和验证标准
* 原型截止日期和里程碑
系统图包含技术开发的关键元素,帮助团队有效地计划开发工作。系统图包括以下内容:
* 关键数据表
* 显示关键算法步骤和输入输出工件的管道
* 关键基础设施组件
在开始时,留下未知的组件是好的,这些组件应该随着团队的进展而被填充。我们建议在计划讨论和演示时使用这些文档。
## 敏捷的工作方式
就采用敏捷原则而言,敏捷的工作方式是必须的。敏捷例程如何执行并不重要,但是原则贯穿于某种正式/非正式的例程。
这基本上意味着利益相关者必须从一开始就参与进来,并且应该经常协商以引导开发朝着正确的方向发展。因为工作的本质是探索性的,所以保持需求的灵活性并采用看板风格的工作流比 scrum 风格的工作流更好。
## 管道开发

图 2:机器学习原型中的关键管道。图片由作者提供。
如图 2 所示,重要的是以三个独立的依赖管道的形式考虑原型解决方案:特征生成、模型训练和模型推理。特征生成流水线获取输入数据表,并使用相关数据边界(例如,日期、地理位置等)生成特定大小的特征表。管道不仅应该用于为模型训练生成特征,还应该用于推理。模型训练管道对训练特征执行步骤,以生成模型及其性能仪表板。模型推理管道对推理特征执行步骤,以生成预测目标及其性能仪表板。
虽然实现一个笔记本来管理它们很诱人,但这不是一个好主意。通常在解决方案探索过程中,某些步骤比其他步骤需要迭代更多次。例如,找到一个好的训练算法可能需要比特征生成更多的迭代。为不同类型的活动保持独立的管道使得迭代变得容易和有效。
如果执行管道中的步骤需要很长时间,并且从某个任务重新启动 dag 是有益的,则每个管道应该被设计为任务的有向非循环图。
## 流程改进
选择度量标准来改进原型制作过程。我们建议采用的两个指标是利益相关方接受的*延迟*和*移交持续时间*。对前者的关注应该支持可重用组件的开发,例如性能度量的标准化、管道库、假设检验机制等。对后者的关注应该能够采用最佳实践,例如代码/系统测试和验证、文档、归档等。
## 将旅程存档
将代码和输入/输出数据存档,用于每项工作的测试和验证。此外,对于每一项工作,保持一个主要学习的日志,即成就,陷阱和错误。代码(和数据)将有助于重用一些解决方案,只需很小的修改或不需要修改。该杂志将有助于未来的工作规划和优先次序。
# 注意
我有几句告诫的话。
首先,做一些好事可能很诱人,例如,维护高质量的代码。然而,我们建议把注意力放在球上,专注于团队的力量,也就是说,测试一个想法。早期优化是一个错误,不是团队的责任。
第二,一个人需要在跑步之前学会如何走路。我建议任何团队在前三四次做他们认为合适的事情。模式应该从这些经历中浮现出来,这应该激发可重用的设计。
最后,预计会有偏差,因为每个团队的旅程都是不同的。本文或其他地方分享的方法旨在启发一种特定的思维方式。然而,每个团队的实现现实都是独一无二的。
最重要的是,保持原型。
# 公共部门谨慎采购人工智能产品
> 原文:<https://towardsdatascience.com/prudent-public-sector-procurement-of-ai-products-779316d513a8?source=collection_archive---------38----------------------->
*本文与蒙特利尔人工智能伦理研究所人工智能伦理研究员* [*穆里亚姆·法西*](https://www.linkedin.com/in/muriamfancy/) *合著*
使用人工智能系统来简化通常是劳动密集型和时间密集型的服务正在被政府认可在多个部门实施。然而,采购这些系统和部署它们的方式有重大的影响。由于对这些系统的影响以及如何正确衡量其风险的理解存在差距,政府往往会采购和部署有偏见、高风险的解决方案,有可能对公众造成重大伤害。
# 当事情变得糟糕透顶时…

*照片由* [*杰斯温·托马斯*](https://unsplash.com/@jeswinthomas?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) *上* [*下*](https://unsplash.com/s/photos/students?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)
最近的一个案例证明了这一点,即英国联邦政府部署了一个有缺陷的人工智能系统,该系统不成比例地影响了参加 A-level 课程的学生;根据历史数据给学生打分,教师的评估和系统提供的评估之间的差异导致许多学生的大学录取通知书被取消。
在这种情况下,一个潜在的担忧是,人工智能系统在其训练阶段没有代表性的数据,并且该系统的操作对公众的透明度很低,这进一步降低了信任。尽管存在所有这些缺陷和公众的愤怒,但对于受到这一体系影响的学生,却没有明确的问责框架。
# 在为公众部署这种系统时,我们如何避免造成这种负面后果?

*照片由* [*乔恩·泰森*](https://unsplash.com/@jontyson?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)*[*拍下*](https://unsplash.com/s/photos/asking-questions?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)*
*一种方法是授权监管者在采购阶段提出正确的问题。像这样的案例表明,在人工智能系统的风险、透明度和复杂性方面存在着巨大的知识缺口。这种理解的缺乏表现在不明智的政策上,这些政策要么过度监管,要么监管不足。*
> *具体来说,在这种情况下,缺乏对系统透明度的要求以及忽视问责制和追索机制的建立加剧了英国的危机。*
# *我们如何问正确的问题?*
**
*提出正确的问题需要对人工智能系统的能力有一个基础的理解,这种理解超越了对系统可能实现的目标的低估和高估的二分法。*
*让来自研究和行业的技术专家参与进来,对于创建一个有足够能力带来公共福利的监管生态系统至关重要。目前,采购干事没有能力评价利用人工智能的系统的影响。NHSX 最近的指导方针为采购医疗解决方案的官员提供了一些指导。虽然这些在引用医疗保健领域的现有法规方面很好,但对于明确引用特定领域法规的其他领域,也需要类似的指导原则(如果存在)。*
*对人工智能的具体使用及其在该领域中的局限性的全面提升,甚至比只是对人工智能系统如何运行的总体认识更重要。我们之所以做出这种区分,是因为相同的技术可能用于不同的领域,取得不同程度的成功,因此了解特定领域的应用对于采购人员做出明智的决策至关重要。*
# *以上我都做到了,我准备好了吗?*
*这需要持续不断的努力,因为该领域的变化速度非常快,而且能力状况也在不断变化。*
*用与特定领域的需求一致的责任和义务的需求来补充这一点也是必不可少的。最后,推动这些系统的开发者远离知识产权保护作为一种不披露的方式也很重要。如果没有能力审计系统的内部运作,我们就有可能让监管者成为制造商心血来潮的傀儡,而在很大程度上,制造商为了保持竞争优势和放弃对用户的责任,会倾向于最大限度地减少信息披露。*
**
**照片由* [*杰森·哈夫索*](https://unsplash.com/@jasonhafso?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) *上* [*下*](https://unsplash.com/s/photos/canada?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)*
*一种方法是让联邦政府制定一个风险评估框架,类似于加拿大政府在衡量将部署在公共部门的人工智能系统的风险时所创建的框架。特别是如果国家专注于维护民主、人权和透明制度,建立风险框架将有助于监管者理解公众的潜在担忧,以及是否有政策或法律保护公民免受部署此类制度的潜在后果。*
*未能围绕解决方案的风险提出正确的技术问题是由政府而不是系统制造商承担的负担,正如美国好事达公司部署保险费定价模型的案例所示。令人遗憾的是,监管者不仅装备不足,而且在实施处罚和做出决定方面也效率低下,这使得好事达可以在其他州重试他们的混淆技术,他们可以在这些州躲过监管审查。*
# *与公众合作会有所帮助!*
**
**照片由* [*埃德温·安德拉德*](https://unsplash.com/@theunsteady5?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)*[*拍下*](https://unsplash.com/s/photos/questions?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)**
**第二步是直接与公众合作,这是增强监管者权力的补充方案。这可以包括公众咨询、公众成员确定应用领域,以及让公众参与围绕系统滥用及其负面后果的数据收集。一项类似于道德漏洞奖金的提案可以帮助监管机构更好地了解系统可能出现问题的地方,并创建一个证据库,推动监管机构提出艰难的问题,并限制这些公司的市场力量。**
**监管者通常不是直接受到这些系统影响的利益相关者,这使得他们很难预测潜在的风险。公众咨询可以以研讨会、会议或由有实际经验的人组成的工作组等形式存在,以便更好地确定这些影响。**
# **让我们避免参与戏剧和符号化**
**但是,公开协商的象征性是需要避免的。被称为“参与剧场”的我们需要确保来自这些咨询的反馈被有意义地合并,并在这种系统的设计、开发和部署过程中被跟踪。公众咨询的重要性在于建立信任和展示透明度。通过公众参与获得信任表明,公民将更多地参与利用这些系统,并显示出政府的能力水平,如果监管机构继续采购人工智能系统供公众使用,这将是长期的重要因素。**
> ****你可以在这里了解更多我的作品:**[**https://ATG-abhishek . github . io**](https://atg-abhishek.github.io/)**
# Ptpython:更好的 Python REPL
> 原文:<https://towardsdatascience.com/ptpython-a-better-python-repl-6e21df1eb648?source=collection_archive---------9----------------------->
## 一个 Python 交互式 Shell,具有自动完成、自动建议、Docstring、历史插入等功能!
# 动机
你有没有想过用 Python Shell (REPL)快速尝试一下脑子里冒出来的一些想法?您可能不想打开一个新的 Jupyter 笔记本,只使用几行代码进行实验。
但是您也可能不愿意使用经典的 Python shell,因为它不像 Jupyter Notebook 那样支持自动完成或 docstring。您也无法在按下 Enter 键后修复代码中的错误。
如果能把自己枯燥的 Python 外壳变成如下图的多功能外壳呢?

作者 GIF
这就是 ptpython 派上用场的时候。
# Ptpython 是什么?
[Ptpython](https://github.com/prompt-toolkit/ptpython) 可以认为是更好的 REPL。要安装 ptpython,请键入:
pip install ptpython
然后键入:
ptpython
…开始使用 ptpython。
# 输入特征
## 输入验证
如果你在一个经典的 Python shell 上犯了一个错误并按下了 Enter 键,你将无法返回去修复这个错误。

作者图片
幸运的是,ptpython 允许您在点击 Enter 按钮时验证输入。在下面的 GIF 中,我错过了一个右括号并收到一个错误,但我可以返回并修复我的代码,直到不再有错误!

作者 GIF
## 基于历史的自我暗示
你曾经希望得到基于历史的建议吗?这可以用 ptpython 来完成。

作者 GIF
默认情况下,此功能不启用。但是您可以通过按 F2 键访问菜单来启用它。然后使用向右或向左箭头打开“自动建议”功能。最后,按回车键隐藏菜单。

作者图片
现在,您应该可以在键入时看到基于历史的建议。要接受建议,只需按右箭头。
## 打字时完成
您还可以在键入点(`.`)后查看属于特定类的所有方法,如下所示:

作者 GIF
使用向下箭头找到某个建议,然后继续键入代码的其余部分。
## 从历史记录中插入
您也可以通过按 F3 来访问历史记录。使用向上或向下箭头找到要复制的代码行,然后键入空格来选择要插入的代码。
每当你选择完代码,按回车键,这些代码行将被粘贴到你当前的 shell 中!

作者 GIF
请注意,代码将按照执行顺序插入。
## 粘贴模式
您是否曾经希望编辑粘贴在 Python shell 上的代码?经典的 Python shell 不允许您这样做。

作者图片
Ptpython 允许您编辑粘贴的代码,直到您满意为止:

作者 GIF
若要打开粘贴模式,请按下 F6。当粘贴模式开启时,当您按下 Enter 键时,代码**将不会被执行**。当您准备好执行代码时,再次按 F6 关闭粘贴模式,然后按两次 Enter。
# 显示特征
## 查看签名和文档字符串
Ptpython 允许您查看 python 函数或类的参数,如下所示:

作者图片
您还可以查看类或函数的文档字符串。要启用此功能,请按 F2,然后打开“显示文档字符串”功能。

作者图片
现在你可以看到你正在使用的函数或类的 docstring!

作者图片
## 突出显示匹配的括号
Ptpython 还通过突出显示匹配的附加项,使区分括号变得更加容易。

作者图片
## 在输入或输出后添加一个空行
如果想增加可读性,还可以在输出后增加空行。

作者图片
要启用此功能,请按 F2,然后打开“输入后空行”和“输出后空行”。

作者图片
# 颜色特征
## 语法突出显示
Ptpython 还支持如下漂亮的语法高亮!

作者图片
您可以通过按右箭头来更改主题,直到找到您喜欢的主题。
总共有 39 个主题可用。如果你想和 Sublime Text 有相同的主题,选择 Monokai 主题。

作者图片
# IPython 的神奇命令
Ptpython 也支持 IPython。运行`ptipython`,获得一个漂亮的交互式 shell,拥有 IPython 提供的所有功能,包括神奇的命令!

作者图片

作者图片
# 永久更改配置
当您退出当前 ptpython shell 时,您对该 shell 设置所做的更改将被撤消。
要永久更改配置,将[该文件](https://gist.github.com/khuyentran1401/b5325ff1f3bfe1e36bf9131a0b8cd388)复制到`$XDG_CONFIG_HOME/ptpython/config.py`。在 Linux 上,这是:`~/.config/ptpython/config.py`。
将配置文件复制到您的本地目录后,您应该已经启用了我上面展示的所有特性。您可以根据自己的喜好随意更改配置文件。
# 结论
恭喜你!您刚刚学习了什么是 ptpython 以及它如何有所帮助。在本文中,我只介绍了一些我最喜欢的特性,所以我鼓励您安装 ptpython 并亲自尝试一下。
我喜欢写一些基本的数据科学概念,并尝试不同的算法和数据科学工具。你可以在 [LinkedIn](https://www.linkedin.com/in/khuyen-tran-1401/) 和 [Twitter](https://twitter.com/KhuyenTran16) 上联系我。
如果你想查看我写的所有文章的代码,请点击这里。在 Medium 上关注我,了解我的最新数据科学文章,例如:
</2-tools-to-automatically-reload-when-python-files-change-90bb28139087> </pywebio-write-interactive-web-app-in-script-way-using-python-14f50155af4e> </how-to-get-a-notification-when-your-training-is-complete-with-python-2d39679d5f0f>
# 使用 R 接口“h2o”可扩展机器学习平台的 PUBG 获胜者排名预测
> 原文:<https://towardsdatascience.com/pubg-winner-ranking-prediction-using-r-interface-h2o-scalable-machine-learning-platform-f7054c9cb435?source=collection_archive---------30----------------------->
## 基于 PUBG 的机器学习和深度学习算法预测排名

照片由 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上的 [Onur Binay](https://unsplash.com/@onurbinay?utm_source=medium&utm_medium=referral) 拍摄
## **要点**
1. PUBG 代表 PlayerUnknown 的 Battlegrounds,是一款在线多人游戏。
2. 排名对于了解不同玩家在游戏中的地位非常重要。
3. PUBG 是一款多人游戏,支持多种平台,每天都有大量玩家在线。
4. 本研究使用的算法是机器学习范畴的线性回归、随机森林和深度学习范畴的深度神经网络。
## **简介**
PUBG 代表 PlayerUnkown 的 Battlegrounds,这是一款多人游戏,可在 Windows、Android、IOS 等各种平台上使用。游戏有不同的模式,三种不同的模式是经典,街机和 EvoGround。在经典游戏中,玩家将会得到不同的地图,从 Erangel,Miramar,Sanhok 和 Vikendi。在街机模式中,有战争、迷你区、快速比赛和狙击手训练。全球有 5.55 亿玩家在各种不同的平台上玩 PUBG,这个庞大的数字带来了排名的难度。Battle Royal 的基本比赛是 100 个人打一场比赛,只有 1 个赢家(可以吃鸡饭)。根据不同的属性对这些玩家进行排名变得很困难,因为一些玩家可能有不止一个相似的排名。这就是机器学习&深度学习派上用场的地方,它通过分析各种属性,了解每种属性之间的相似性,根据训练好的模型预测球员的排名。用于训练和测试目的的数据集来自 Kaggle,ka ggle 是一个开源平台,用于收集与不同用例相关的数据,其中他们将各种匹配(SOLO、SQUAD、DUO)与所有不同的属性集相结合,以理解用例。
[https://www . ka ggle . com/c/pubg-finish-placement-prediction/data](https://www.kaggle.com/c/pubg-finish-placement-prediction/data)

包含 29 种不同属性的训练数据集-按作者分类的图片
## **方法论**
论文的这一部分讨论了用于获得期望输出的不同方法,这些方法在实验结果中进行了解释。该方法分为 3 个小节,分别是:
## A.线性回归
线性回归是一种使用最佳拟合线进行预测的机器学习算法,该算法中使用的目标属性始终是数字。为这种类型的回归绘制的数据基于 Y 轴上的因变量和 X 轴上的自变量,如下所示,关于这两个轴,将创建满足这些值的最佳拟合线或平面或斜率。斜率周围的点是实际值,斜率是预测值。

因变量和自变量的线性回归图-图片由作者提供
**B .随机森林算法**
随机森林或随机决策森林使用打包和提升方法,这是集成学习的一部分,可用于基于用例的回归和分类目的。决策树是来自多个树的单个树,这些树一起构成了多个决策的森林,与基于所有可用树的投票的单个决策树相比,它给出了更好的结果。随机森林层次结构包括各种级别,如根节点、叶节点、子节点、父节点等。随机森林或随机决策森林的工作基于关于输出的多数表决,每棵树都有一些输出,并且最终输出基于输出的平均值,这给出了准确的结果。这些形成了基于训练数据的判断的更好的扩展,与其他模型相比,其给出了更好的准确性,视觉表示可以在下面观察到。

随机森林算法与最终类-图像作者
**C .深度神经网络**
1. *非深度前馈神经网络*
神经网络是深度学习算法的一部分,它由 3 层组成。第一层是输入层,这一层由输入节点组成,通过分配一些权重和偏置来传递值。第二层是隐藏层,这一层可以取决于神经网络是深度的还是非深度的,对于非深度神经网络,隐藏层的数量通常是 1 或 2。最后一层是输出层,这一层给出输入和隐藏层的输出值,这取决于输出节点的数量。

单层前馈神经网络-作者图片
*2。深度前馈神经网络*
深度神经网络与非深度神经网络相同,不同之处在于隐含层数。深层神经网络的隐含层在 2-8 层之间,隐含层越大,神经网络越复杂。这种类型的网络通常用于解决复杂的问题。深度神经网络的结构与非深度相同,具有输入层、隐藏层和输出层。

多层前馈神经网络-作者图片
## 文献评论
这篇研究论文的实验部分由各种测试和通过执行迭代获得的结果组成。本文的实验结果包括在 29 个属性的 Kaggle 数据集上进行的各种算法迭代和不同的探索性数据分析,其中训练数据集的大小为 446966,测试数据集的大小为 1934174。实验的系统架构如下所示。

公共建筑-作者图片
探索性数据分析用于理解与目标变量相关的属性的可视化表示,这将有助于根据特征的重要性做出更好的判断。第一个 EDA 是为了了解在哪个视角下玩游戏的玩家数量,PUBG 中的视角是 FPP 或 TPP,可以从下图中观察到,FPP 代表第一人称视角,TPP 代表第三人称视角,这将用户的视角从广角变为窄视角——基于从不同视角直接聚焦武器。

TPP VS FPP 用户视角-作者图片
从图中可以看出,大多数玩家玩 FPP 视角,很少玩家玩 TPP 视角,因为 TPP 提供了一个非常窄的屏幕,很难瞄准。第二次 EDA 是为了了解玩家最常玩的游戏类别,PUBG 提供了三个类别,分别是单人、双人和小队,如下图所示。

单人对小队对双人用户-作者图片
从 Fig 可以观察到,大部分玩家都是在小队的类别下玩,然后是双人组,最少的人玩单人。

杀死类别 vs 获胜位置预测-作者图片
上图中的方框图显示了与获胜预测相关的杀戮分布,可以看出,获胜预测随着杀戮的增加而增加,接近 3-5 次杀戮有利于 0.8%的获胜预测,而在一场比赛中超过 10 次杀戮可以获得 100 %的获胜率。另一个 EDA 是绘制玩家在游戏中获得的武器的直方图(单人、双人和小队的总数)。该图显示了玩家获得的武器数量及其分布情况。从下图可以看出,武器数量在 0 到 15 之间,平均每个用户有 8 件武器。

获得的武器与作者预测的地点-图像
完成最终的 EDA 是为了理解与每个属性相关的属性的相关性,并借助于相关性矩阵对其进行可视化。数据集的相关矩阵可以在下图中看到。

作者发布的相关矩阵-图像
相关矩阵显示了数据集中的属性是如何相互关联的,要么是高度相关,要么是最不相关。在上面的图 10 中,正 1 (+1)表示高相关性,负 1 (-1)表示低相关性。有了这个 EDA,通过可视化从数据中提取重要信息。下一步是提取模型将要训练的重要特征,如果根据重要性选择这些特征,与随机传递给模型进行训练的特征相比,这些特征可以给出更好的结果。

线性回归算法的重要特性-作者图片
上图显示了在线性回归算法上创建模型时需要考虑的重要特性,可以观察到,boost & heal 等特性在总和方面最有价值,而 heal & boost 等特性在总和方面价值较低。这种观察显示了选择哪个特征来为算法建模以获得更好的结果。类似地,对神经网络进行第二特征选择,用于选择与随机选择相比可以增加模型准确性的更好的特征。这种特征选择可以从下图中观察到,该图显示了基于训练数据集的特征。总和作为另一个参数。

深度神经网络重要特征-作者图片
> 下一步是应用机器学习和深度学习算法,并检查它如何帮助预测用户的胜率。本研究中使用的线性回归是多变量线性回归,这种多变量的使用是因为从特征提取中提取的特征不同。多变量线性回归的等式可以从下面观察到,它显示了 x、y 和 z 方面的特征,w1、w2 和 w3 作为模型将尝试学习的权重。用于优化权重的多元线性回归的损失函数是均方误差(MSE ),后面是下面所示的公式,该公式显示了 f (x,y,z)的获得值与实际值的损失函数。
>
> 本文采用了两种神经网络,一种是基于非深度神经网络,另一种是基于深度神经网络,通过超参数优化来提取最佳节点数。为超参数优化传递的参数是。
## 纪元[10,20,50,60,100]
## 批量大小[10,20,30,40]
## 交叉验证=5
> 用于优化结果、也用于解决消失梯度问题的优化器是整流器激活函数,该特定激活函数用于根据线性数据给出结果,以便从数据集中预测获胜者。在反向传播的帮助下,使用随机梯度下降(单个训练数据)进行学习,可以从(3)中观察到整流器的公式。
## **f(x) = max (0,x) (3)**
> 为了理解训练和验证期间的数据损失,使用 MAE,MAE 代表平均绝对误差,它计算在适当数据的预测期间发生的绝对总误差,可以观察到 MAE 的公式,它显示了相对于实际值和所得值除以数字的绝对误差。

MAE 公式-作者图片
在许多游戏场景中,一些玩家可能是骗子,他们可能使用无效的游戏方法来获得排名和增加经验值,也有一些玩家可能是 AFK(远离键盘)。数据集中的玩家总数如下所示。

AFK vs 骗子
表 II 显示了使用所有算法对训练数据集的 PUBG 数据集进行排名预测时所使用的算法的 MAE。

训练数据集的 MAE 按作者分类的图像
表 III 显示了使用所有算法对验证(测试)数据集的 PUBG 数据集进行排名预测时所使用的算法的 MAE。

用于测试数据集的 MAE 作者提供的图像
从上表 II 和 II 可以看出,深度神经网络算法获得了最低的 MAE,对于训练数据集是 0.02012,对于测试数据集是 0.03121。MAE 的基本工作是出现的误差越少,对数据越好,根据这个概念,可以观察到,对于线性回归算法获得了最高的 MAE,对于训练数据是 0.08521,对于测试数据是 0.06295,这是对于基本随机森林算法获得的。在 R 编程的 H2O 软件包的帮助下,排序预测中使用的所有算法的 MAE 的可视化表示可以在下面的图中看到。

MAE 与使用的算法-图片由作者提供
## 结论
该研究旨在基于机器学习和深度学习算法评估 PUBG 玩家的排名,并以更好的方式进行 EDA 来分析数据集。本研究中使用的算法是线性回归、随机森林和深度神经网络,计算 MAE(平均绝对误差)以检查哪种特定算法最适合庞大的数据集。对训练和测试数据集都进行了该评估,由此观察到深度神经网络表现良好,训练数据集的 MAE 值分别为 0.02012,测试数据集的 MAE 值为 0.03121。对于训练数据集和测试数据,线性回归获得了最高的 MAE 值,其为 0.08521;对于 n_estimators=40,max_features=Sqrt 的基本随机森林,获得了最高的值。平均每人杀死 0.9248 个玩家,99%的玩家杀死 7.0 个或更少,而有记录以来最多的是单场比赛杀死 72 个玩家。最佳算法是深度神经网络,测试数据误差值最低。
## 参考
[1]丁,雍。"公共广播电视运营模式研究."MATEC 会议网。第 173 卷。EDP Sciences,2018。
[2] Rokad,Brij 等,“在玩家未知的战场上适者生存。”arXiv 预印本 arXiv:1905.06052 (2019)。
[3]德索萨、朗姿、马尼什和迪克夏。" PUBG 成瘾测验(PAT)的编制和验证."(2019).
[4]马穆尔佩特,马杜里亚·曼朱纳斯。"使用人工神经网络的 PUBG 赢家位置预测."
[5]梅尔哈特、戴维、丹尼尔·格拉维纳和圣乔治·扬纳卡基斯。"通过观察者的眼睛预测每时每刻的接触:PUBG 在 Twitch 上流动."数字游戏基础国际会议。2020.
## 在你走之前
研究论文:[https://ieeexplore.ieee.org/document/9396823](https://ieeexplore.ieee.org/document/9396823)
# 用 Python 发布 I3S 场景图层服务
> 原文:<https://towardsdatascience.com/publish-i3s-scene-layers-service-with-python-fecc50b18548?source=collection_archive---------29----------------------->
## 本地 3D 模型流服务-不使用 ArcGIS Enterprise

**斯图加特的 3D 建筑模型—在 Unity3D 中加载 I3S 场景图层服务**(作者)
在最近的地理数据科学项目中,3D 地理可视化工具在多个应用领域发挥了重要作用,如城市规划、室内/室外行人导航、环境模拟、文化遗产或设施管理[1]。近年来,ESRI 开发了 I3S 或索引 3D 场景层,这是场景层的交付格式规范。它旨在用于表示不同类型的 3D 数据,如 3D 建筑模型、集成网格、点云等等。I3S 的优势在于它能够通过 web 以高性能和可扩展性传输大型 3D 数据集。[2]此外,开放地理空间联盟(OGC)已批准 I3S 作为社区标准,并于 2017 年 9 月公开发布[3]。就个人而言,我喜欢使用 I3S,因为它易于使用,并且该服务可由 ArcGIS for JavaScript 或其他 web 可视化库(如 Deck.gl)或游戏引擎(如 Unreal Engine 或 Unity3D)使用。

**作者在 Deck.gl Web 客户端加载 I3S 中的纽约市 3D 建筑模型(左)和 3D 瓷砖(右)**
## 但是,没有 ArcGIS 它就无法发布…
根据 ESRI [4]的说法,您可以通过使用 ArcGIS 工具(ArcGIS Pro 或 ArcGIS Online 订阅)发布托管场景图层来创建流式 3D 模型服务。但是,没有 ArcGIS Enterprise 就无法创建服务。这给许多没有 ArcGIS 套件并依赖其他产品系列或开源解决方案的项目/组织带来了问题。
## 可选择的解决方案
在本帖中,我想分享一个不使用 ArcGIS Enterprise 工具发布 SLPK 或 I3S 服务的解决方案。还好有个工具叫“Quick _ SLPK _ Server”;这里是作为回购[托管的](https://github.com/CrashedBboy/Quick_SLPK_Server)。这不是 ESRI 的官方工具,但在我的数据集上测试后,我喜欢它。它非常有效。所以,让我们一起尝试使用这个工具来托管 SLPK (i3s)服务。
**克隆快速 SLPK 服务器项目**
$ git clone https://github.com/CrashedBboy/Quick_SLPK_Server
**下载开源 SLPK 服务**
在本文中,我将使用纽约市 LoD2 从[这里](http://www.arcgis.com/home/item.html?id=44039155906640438c906d47fac50301)。在我的上一篇文章中,有大量的开源 3D 数据:
</open-source-3d-semantical-building-models-in-2020-f47c91f6cd97>
准备好 SLPK 层后,只需将它放在文件夹`**Quick_SLPK_Server\slpk**`中,在那里您可以克隆 repo。
## 运行 SLPK 服务器
您可以简单地用下面的命令运行服务器
$ pip install bottlepy # install dependency
$ python quick_slpk_server.py # run server
该实现包括用 ArcGIS4JS 开源工具编写的可视化客户端,您可以通过下面的示例 URL 访问其内容:
URL Pattern > http://
For Example > http://localhost:8080/carte/Buildings_NewYork.slpk

**作者从快速 SLPK 服务器**加载纽约市的 3D 建筑模型
您还可以通过下面的示例 URL 访问此 SLPK 服务器的索引 3D 场景图层。这使您可以灵活地将此数据图层集成到您需要的任何可视化客户端,例如 Deck.gl,而无需依赖 ArcGIS 套件。
URL Pattern http://
For Example http://localhost:8080/Buildings_NewYork.slpk/SceneServer

**作者从快速 SLPK 服务器**加载 I3S 服务—纽约市
## 结论
本文简要介绍了如何在不依赖 ArcGIS 套件的情况下使用 Python 托管和发布 I3S 场景图层服务,ArcGIS 套件为您提供了将此数据图层集成到任何可视化客户端或项目的灵活性。我希望这篇文章对您有所帮助,并且能够将这种技术应用到您的项目中。
**平安健康!**
## 参考
[1] Esri (2019): **场景图层服务和打包标准**([https://github.com/Esri/i3s-spec](https://github.com/Esri/i3s-spec))
[2]https://www.ogc.org/standards/i3s(2017):**索引 3D 场景图层(I3S)标准**()
[3] Esri (2021): **发布托管场景图层**[https://enterprise . ArcGIS . com/en/portal/latest/use/Publish-scenes . htm](https://enterprise.arcgis.com/en/portal/latest/use/publish-scenes.htm)
# 在你的下一篇论文中使用 LaTeX 简化你的学术生活
> 原文:<https://towardsdatascience.com/publishing-an-apa-paper-with-latex-836a70b2f48f?source=collection_archive---------13----------------------->
## 用 LaTeX 出版 APA 论文

克里斯·亚瑟·科林斯在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上的照片
# 概观
当你刚刚开始的时候,使用 LaTeX 写一篇论文可能是一个乏味的过程;然而,我发现它是控制发布过程运行的强大工具。我发现的一些优势:
1. 文档-代码以及版本控制。
2. 能够与您的代码库集成,无缝更新。
3. 预建的模板,完美地格式化基于一个标准,如美国心理学协会或 [APA 格式](https://apastyle.apa.org/style-grammar-guidelines/paper-format)。
4. 能够插入先进的排版,如数学方程式。
我想记录我最终使用的过程。希望对你有帮助!
# 乳胶是什么?
LaTeX 是一个用于文件制作的软件系统。LaTeX 没有使用图形编辑器,而是使用纯文本和一种*标记*语言编写。与 [TeX](https://en.wikipedia.org/wiki/TeX) 排版系统结合后,文档被编译成输出格式。
# 安装 LaTeX
首先,您需要安装 LaTeX。我运行的是 Mac,因此我使用的是 MacTeX 发行版。你可以在这里下载:[https://www.tug.org/mactex/](https://www.tug.org/mactex/)。
我发现这个发行版很完整,也很容易管理。提供了一个安装程序,其中包含创作和发布 TeX 文档所需的一切。它还兼容英特尔或 Arm(苹果硅)处理器。
# 更新 TeX 分布
安装软件后,您需要确保所有适当的软件包都是最新的。您可以通过启动 **TeX Live 实用程序**并运行 *Update All Packages* 命令来实现这一点。

作者图片
# VS 代码和 LaTeX
接下来是您的创作环境。MacTeX 发行版确实附带了一个编辑器,但是它非常简单。幸运的是,VS Code 有一个很棒的扩展,可以让你直接在你喜欢的编辑器中编写文档,并在编辑时自动发布文档。
从 [VSCode 市场](https://marketplace.visualstudio.com/items?itemName=James-Yu.latex-workshop)安装**乳胶车间**扩展。
# 基于云的替代方案
还有一个很好的基于云的解决方案叫做**。APA 模板甚至可以让你更快地开始。查看背页的[了解更多信息。缺点是你没有与你的编码环境集成。](https://www.overleaf.com/)**
# APA 模板
我承认,用 LaTeX 从头开始构建 APA 格式的文档将是一种与接下来的体验非常不同的体验。幸运的是, *Daniel A. Weiss* 创建了一个很棒的 APA 模板,支持第 7 版,包含在 TeX Live 发行版中。只要你已经如上所述更新了你的包,它应该是无缝的。
请务必查看文档(PDF)。它清楚地显示了如何使用模板,并将帮助您进一步定制。
# APA 文件的构建模块
TEX 文档的几个部分创建了一篇论文所需的所有组件。我将用代码示例逐一介绍它们。
# 文档标题
首先用任意名称创建一个新文档,并添加扩展名`.tex`。在最上面插入这几行。注意`\begin{document}`和`\end{document}`标签。论文的其余部分将落在这两者之间。
\documentclass[stu]{apa7}
\usepackage[american]{babel}
\usepackage{csquotes}
\usepackage{caption}
\usepackage[style=apa,sortcites=true,sorting=nyt,backend=biber]{biblatex}
\DeclareLanguageMapping{american}{american-apa}
\addbibresource{bibliography.bib}
\begin{document}
\end{document}
我们可以看到`documentclass`被设置为使用`{apa7}`模板和选项`stu`,选项`stu`是一个关于格式化为**学生论文**和期刊文章的定义。此外,还有`\addbibresource{}`定义,我将在后面介绍,它将帮助我们在文档中添加适当格式的引用。
# 标题页和摘要
添加一个**标题页面**和**摘要**页面简直易如反掌。只需将这个插入到`begin`和`end`标签之间。
\title{Capstone Project}
\authorsnames{Brian Roepke}
\authorsaffiliations{Data Analytics, Golden Gate University}
\course{DATA190}
\professor{Dr. Professor Data}
\duedate{August 21, 2021}
\abstract{
Bacon ipsum dolor amet jerky burgdoggen turkey...
}
\maketitle
您还会注意到,编译器将开始创建您的第一个 PDF!每次更改后,您都会看到 PDF 更新。关于 VS 代码集成的一个伟大的事情是你可以并排预览 PDF,看着你的修改出现。只需打开侧面的*预览分割器窗口*,您应该会看到一个带有封面、摘要和论文首页的 PDF。**魔法**!
# 正文和章节
接下来,我们可以撰写论文。总体结构是在`\maketitle`标签下添加你的第一个介绍页面,然后是几个小节和子小节。
\maketitle
Tongue sausage ground round capicola pork pancetta brisket, strip steak...
\newpage
\section{Sentiment Analysis of Dog's Feelings Towards Bacon}
Drumstick short loin cow prosciutto flank leberkas pastrami pancetta...
\subsection{Other Sources of Bacon Goodness}
Consectetur biltong pork chop duis lorem consequat...
这里发生了什么事?
* `\newpage`确实如此——在文档中放置一个分页符,并允许您在新的一页上开始一个部分。
* `\section`是根据 APA 的二级标题。它是**左对齐,粗体,标题案例标题**
* `\subsection`是第三级标题。它的 ***左对齐,粗体斜体,标题案例标题***
* `\subsubsection`是第四级标题,以此类推。支持五个级别。查看文档以获取信息。
# 数字
接下来是如何插入插图的示例,通过使用以下语法的文本引用来完成:
(见图`~\ref{fig:my_fig}`)
然后在文档中的任何地方,用相应的匹配`label`创建图形。随着文稿的增长,您会发现对图像位置的附加控制很重要。在描述选项的 [StackExchange](https://tex.stackexchange.com/questions/8652/what-does-t-and-ht-mean) 上有一个很棒的答案。
\begin{figure}[!ht]
\centering
\caption{A puppy confused by bacon-ipsum, but really wants bacon.}
\includegraphics[width=\textwidth]{dog.jpg}
\label{fig:my_fig}
\end{figure}
# 引文
添加引用也非常容易。在文档的头中,我们已经定义了`\addbibresource{bibliography.bib}`。以此名称创建一个文件,然后可以使用 BibTeX 格式向其中添加引用,如下所示:
@book{huff2010lie,
title={How to Lie with Statistics},
author={Huff, D. and Geis, I.},
isbn={9780393070873},
url={https://books.google.com/books?id=5oSU5PepogEC},
year={2010},
publisher={W. W. Norton}
}
然后,当在你论文的正文中引用时,你像这样添加引用:
Andouille alcatra burgdoggen, tri-tip boudin shank jerky buffalo beef short loin \textcite{huff2010lie} filet mignon leberkas \parencite{huff2010lie}.
输出如下所示,显示文本和经典引文。
*and ouille alcatra burgdogen,tri-tip boudin shank 牛肉干水牛牛腰肉哈夫和盖修(2010)菲力牛排 leberkas(哈夫&盖修,2010)。*
## 使用参考经理
如果你使用像 [EndNote](https://endnote.com/) 或 [Zotero](https://www.zotero.org/) 这样的引用管理器,你可以将你的整个引用列表导出为 BibTex 并粘贴到`.bib`文件中。我在学术研究的大部分时间里都在使用 EndNote,但最近我换成了 Zotero,因为它非常棒,而且可以免费使用。这里有一个如何导出引用的快速流程。

作者图片
从 Zotero 界面,突出显示您希望导出的项目,并选择`Export items...`菜单选项。

作者图片
接下来,选择`BibTex`选项并保存文件。从这里,您可以将它们移动到`bibliography.bib`文件中。确保移除`file:`键值对。

作者图片
# 与您的项目代码集成
我上面提到过,一起使用 **LaTeX** 和 **VS Code** 的一个好处就是自动与你的代码集成。您可以在 LaTeX 文档中的图像引用中直接引用从 Python 保存的图像。
如果你正在使用`matplotlib`,在你的绘图后添加这两行:
plt.tight_layout()
plt.savefig('images_path/img_name.png', dpi=300);
然后在 LaTeX 文档中引用该图像。每次你的剧情更新,你都不用担心更新的问题。
# 结论

作者图片
当你开始的时候,这似乎是一个令人生畏的任务。通过学习一些基础知识并利用这里使用的 APA 模板,您可以轻松地发布专业的论文和文章。在你的下一个任务或项目中尝试一下吧!为了方便起见,如果你想看整个项目,我已经把源代码贴在了 GitHub 上。
尽情享受吧!
如果你喜欢阅读这样的故事,并想支持我成为一名作家,可以考虑报名成为一名媒体成员。一个月 5 美元,让你可以无限制地访问成千上万篇文章。如果你使用[我的链接](https://medium.com/@broepke/membership)注册,我会赚一小笔佣金,不需要你额外付费。
# 将开源 Python 包发布到 PyPI
> 原文:<https://towardsdatascience.com/publishing-open-source-python-packages-to-pypi-4a1458416572?source=collection_archive---------33----------------------->
## 造了很酷的东西?通过将它发布到 PyPI,使它易于访问!

由[在](https://unsplash.com/@hiteshchoudhary?utm_source=medium&utm_medium=referral) [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍摄的照片
# Python 包索引(PyPI)
Python 包索引是可用于 Python 编程的软件库的存储库。PyPI 使得分发和访问不属于标准 Python 库的有用项目变得容易。
将自己的开源项目发布到 PyPI 非常简单。在本文中,我们将介绍如何:
* 为 PyPI 准备好您的包
* 手动构建您的包并上传到 PyPI
* 在发布之前用 [TestPyPI](https://test.pypi.org/) 测试你的包
* 版本控制
* 使用 CircleCI 将存储库的主分支与 PyPI 同步
我将使用我的一个存储库作为例子: [pdf-wrangler](https://github.com/happilyeverafter95/pdf-wrangler) ,一个用于更简单的 pdf 提取的 PDFMiner 包装器。这个项目在 PyPI 上的[这里](https://pypi.org/project/pdf-wrangler/)托管。

截图来自[https://pypi.org/](https://pypi.org/)
# 为 PyPI 准备您的包
首先,您需要在根目录下创建一个`setup.py`文件来提供关于您的包的一些信息。这是`pdf-wrangler.`的`setup.py`文件的第一次迭代
最关键的字段是:
* **name:** 出现在 PyPI 上的包的名称(这个名称必须是惟一的,所以要确保没有同名的包存在)
* **版本:**这是您的软件包的唯一版本
* 安装要求:这指定了你的包的依赖关系
* **python _ requires:**python 版本需求
* **url:** 链接到项目代码库所在的位置
在这里,我也导入自述文件作为详细描述。简短和详细的描述都将显示在 PyPI 页面上。
# 构建并上传您的包
一旦我们创建了`setup.py`文件,我们就可以开始构建和上传了。
## 建设
“构建”过程为您的包创建一个源归档文件和 wheel,由以下命令启动:
python setup.py sdist bdist_wheel
这将创建一个`dist`目录,其中包含上传所需的所有文件。您应该使用`.gitignore`将这个目录从您的存储库中排除,这样它就不会使您的代码混乱。
## 上传
首先,我们需要[在 PyPI 上创建一个账户](https://pypi.org/account/register/)。我建议在 TestPyPI 上创建一个帐户,这样你可以在上传之前在一个单独的索引上测试你的包。
**注意:**新版本一旦发布,就没有办法覆盖,所以要确保你发布的所有东西都经过了适当的测试!
根据它的文档, [twine](https://pypi.org/project/twine/) 是一个“在 PyPI 上发布 Python 包的工具”。使用`twine`,我们可以在安装后从命令行发布包。
pip install twine
要上传到**测试**索引,在根目录下运行以下命令:
twine upload --repository-url https://test.pypi.org/legacy/ dist/*
准备好上传到实际的 PyPI 索引后,在根目录下运行以下命令:
twine upload dist/*
`twine upload`命令将提示您使用您的 PyPI 用户名和密码进行身份验证。一旦通过认证,`twine`将上传并分享您可以找到您的包裹的网址(您也应该能够通过在 PyPI 上的谷歌搜索轻松找到您的包裹)。

截图来自[https://pypi.org/project/pdf-wrangler/](https://pypi.org/project/pdf-wrangler/)
# 版本控制
如果您尝试上传同一个版本两次,您将会遇到此错误,告诉您每个版本必须是唯一的:
HTTPError: 400 Client Error: File already exists. See https://pypi.org/help/#file-name-reuse for url: https://upload.pypi.org/legacy
如前所述,版本在`setup.py`文件中指定。每次我们想上传一个新版本的包,我们必须改变版本。
有两种常用的版本化方案:
## **日期方案**
您可以使用发布日期来版本化您的软件包。例如,从今天开始的新版本将是`pdf_wrangler==20210704`
使用日期进行版本控制的缺点是,它不会考虑同一日期单位的多个版本。
## 基于序列的方案
对于基于序列的方案,每个版本都是由点分隔的数值序列。最常用的格式是:
MAJOR.MINOR.MICRO
* 重大变更是软件的重大修订,通常会以一种不总是向后兼容的方式彻底改变 API /现有特性
* 次要是指较小(但相当大)的更改,如功能改进,当最终用户升级时,这些更改几乎没有风险
* 微小的改变通常是为修复错误而保留的
# 用 PyPI 同步主/主分支
如果您想要将您的主分支与 PyPI 同步,以便您不需要手动构建和上传您的发行版,您将需要一个 CI/CD 工具来执行我们前面运行的命令。[这里的](https://github.com/happilyeverafter95/pdf-wrangler/blob/main/.circleci/config.yml)是`pdf-wrangler`使用 CircleCI 的一个例子。
之前,我们通过以下方式手动上传了包:
* 构建新的发行版(记住版本也需要更新)
* 使用`twine`上传到 PyPI
我们可以通过 CircleCI 构建和部署步骤自动运行这些命令。我们可以利用有用的 CircleCI 环境变量,比如`CIRCLE_BRANCH`,这样我们就可以只同步主分支的变更。
让我们看一下`build-and-test`工作流程中的每一步:
* `Run tests`使用`pytest`执行我们的测试,这样我们就可以放心地发布变更
* `init .pypirc`跟踪使用我们的 PyPI 帐户进行身份验证所需的必要环境变量;密码(这是一个秘密)作为一个秘密环境变量保存在 CircleCI 项目设置中
* `create packages`为主分支上的变更构建分发包
* `upload to pypi`执行`twine`命令对主分支进行修改(确保`twine`作为项目依赖项的一部分安装)
如您所见,发布您自己的开源 Python 包并不困难。希望这对你有帮助!
# 感谢您的阅读!
[通过 Medium](https://medium.com/@mandygu) 关注我的最新动态。😃
作为一个业余爱好项目,我还在[www.dscrashcourse.com](http://www.dscrashcourse.com/)建立了一套全面的**免费**数据科学课程和练习题。
再次感谢您的阅读!📕
# 从 PubSub 到 BigQuery:如何使用数据流、Apache Beam 和 Java 构建数据管道
> 原文:<https://towardsdatascience.com/pubsub-to-bigquery-how-to-build-a-data-pipeline-using-dataflow-apache-beam-and-java-afdeb8e711c2?source=collection_archive---------31----------------------->
## 了解如何在 GCP 创建数据管道
我最近参与了一个项目,该项目要求我从 Google PubSub 收集数据,并将其加载到不同的 BigQuery 表中。在这个过程中,我面临了许多挑战,所以我想分享一下我在谷歌云平台中构建完整数据管道的经验。
# **问题陈述**
假设我们在 GCP 的项目中有一个从后端向 PubSub 发送各种事件的库应用程序。事件是应用程序的任何用户迭代。仅为了演示,我们希望收集以下事件:
* **detailed_view** ,当用户打开一个有图书详情的页面时
* **搜索**,当用户搜索一本书时
* 当用户将某本书添加为收藏时
每种事件类型都有不同的结构,应该保存到 BigQuery 中单独的表中,以便进一步分析。我们得到 JSON 格式的事件,JSON 中的 *event_type* 参数定义了事件需要写入的表。
**详细 _ 查看**事件示例:
{“event_type”: “detailed_view”,“time”: “2021–08–01”,“visitor_id”: “bdd6a03a-390a-4e76-b098-b74ea385228e”,“book”: {“id”: “1231234”}}
**搜索**事件示例:
{“event_type”: “search”,“time”: “2021–08–01”,“visitor_id”: “bdd6a03a-390a-4e76-b098-b74ea385228e”,“search_query”: “War and Peace”}
**添加到收藏夹**事件示例:
{“event_type”: “add_to_favorite”,“time”: “2021–08–01”,“visitor_id”: “bdd6a03a-390a-4e76-b098-b74ea385228e”,“book”: {“id”: “1231234”},“rating”: 5}
我总结了下图的问题。

管道架构(图片由作者提供)
因此,你可以看到黑盒将为我们做所有的魔术,我们不需要做任何事情。
开个玩笑,真希望能这么简单。
您可能会说,好吧,太好了,但是如果数据突然以不同的结构出现或者出现在一个损坏的 JSON 中,该怎么办呢?我们都知道数据工程师的生活充满了痛苦和不断变化的数据结构。如果有人在没有任何通知的情况下从后端更改了模式,该怎么办?我们如何识别它是否发生过?我们如何在不停止管道的情况下保持和保存这样的数据,直到应用了某种修复?
因此,我们需要一个解决方案,它可以根据特定条件将输出数据流并行存储到不同的数据存储中,并且能够收集由于某种原因无法进入 BigQuery 的行。所以解决方案是 [Dataflow](https://cloud.google.com/dataflow) ,这是谷歌基于 Apache Beam 的完全托管的数据处理服务。它提供了所有需要的功能,以及无服务器方法和水平可扩展性。你可以阅读 Apache Beam [文档](https://beam.apache.org/documentation/)了解更多细节。
我想提及三个基本概念:
1. 这是一个开源模型,用于创建批处理和流数据并行处理管道,可以在不同的运行程序上执行,如 Dataflow 或 Apache Spark。
2. Apache Beam 主要由 p 集合和 p 变换组成。PCollection 是一个无序的、分布式的、不可变的数据集。PTransforms 是对作为数据管道阶段的 PCollections 的操作。Apache Beam 允许对 PTransforms 的输入和输出进行分支数据处理。
3. p 集合可以是有界的(固定大小的数据集)或无界的(无限大小的数据集)。如果从 PubSub 读取数据,那么您的 PCollection 是无限的。
在下图中,你可以看到最终的管道架构。

管道架构(图片由作者提供)
既然我们已经对数据管道有了一个概念,现在是时候让它发挥作用了。如上图所示,我们在中间有一个数据流作业,包括以下步骤:
1. 不断地从应用程序后端发布的 PubSub 中读取 JSON 事件。
2. 在 PTransform 中处理 JSON 事件。
3. 将它们加载到 BigQuery。目的地根据 JSON 事件中的 event_type 字段而不同。
4. 收集所有插入失败的行,并将它们上传到云存储桶。
5. 统计所有“好”数据和“坏”数据,并以指标形式呈现出来
# 流模式还是批处理模式?
在将数据导入 BigQuery 之前,我们需要选择适合自己的模式。数据可以在*批处理*模式下作为大块数据加载,也可以在*流*模式下加载,这意味着实时批处理更小,但价格更高。
如何做出选择?
BigQuery 使用[加载作业](https://cloud.google.com/bigquery/docs/loading-data)来接收数据。对于这个过程,谷歌有[配额](https://cloud.google.com/bigquery/quotas#load_jobs),这是选择摄取方法时必须考虑的。
1. 您的项目每天可以对每个表进行多达 1,500 次操作(如加载、复制、查询),包括目标表每天失败的作业。
2. 您的项目每天可以运行多达 100,000 个装载作业。失败的加载作业计入此限制。
3. 总请求负载必须小于 10MB。
另一个需要考虑的重要事情是 Apache Beam 的行为,即将**批处理模式**中的**无界数据**加载到 BigQuery 中。如果这样的加载失败,那么 Apache Beam 将重试 1000 次。你可以从它们的源代码[代码](https://github.com/apache/beam/blob/e76b4db30a90d8f351e807cb247a707e7a3c566c/sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/bigquery/BigQueryIO.java)本身看到证明:
没有参数可以更改这个默认值,因此这意味着如果您每天至少两次对同一个表进行两次错误加载,您将会超出加载作业配额:
1 * 1000 次重试+ 1 * 1000 次重试= 2000 次重试> 1500 次配额
对于项目级别 100,不正确的负载总数将超过定额。
在我看来,如果你使用具有无界数据的数据流(像 PubSub ),并且你的管道在生产中是高度加载的,并且使用许多表,那么使用流模式是更安全的,尽管更昂贵。否则,如果达到了该表的作业配额,那么该表的所有其他作业(如查询或复制)将在一天内无法进行。如果达到项目的作业配额,所有表的所有作业都将被阻塞。
当然,您可以在接收数据之前检查所有结构和类型,但这也不能保证管道的安全。
因此,在本教程中,我将向您展示如何在流模式下加载数据。
# 设置您的本地机器
1. 阿帕奇光束需要 [JDK](https://www.oracle.com/java/technologies/javase/javase8-archive-downloads.html) (Java SE 8 (8u202 及更早)。不要忘记设置 [JAVA_HOME](https://docs.oracle.com/cd/E19182-01/821-0917/inst_jdk_javahome_t/index.html) 环境变量。
2. 本教程的所有代码甚至更多都可以在我的 [GitHub](https://github.com/olgazju/blog_pubsub_to_bigquery_dataflow_pipeline) 上找到。
3. 你可以安装 IntelliJ,但是为了简单起见,我将使用命令行。
# 设置 Google 云
## 1.项目
如果没有,在谷歌云平台中创建一个[项目](https://cloud.google.com/resource-manager/docs/creating-managing-projects#console)。将[计费账户](https://cloud.google.com/billing/docs/how-to/modify-project)与您的项目链接,如果它没有链接的话。
## 2.服务帐户
在云控制台中,转到[服务帐户页面](https://console.cloud.google.com/iam-admin/serviceaccounts)。选择您的项目并点击*创建服务帐户*。然后你会被要求提供细节。

服务帐户详细信息
授予此服务帐户我们数据管道所需的以下权限:
* 云数据流服务代理
* 数据流管理(用于创建作业)
* 数据流工作者
* BigQuery 管理员
* 发布/订阅用户
* 存储对象管理
为了简单起见,我采用了管理员角色,但是您可以使用更精确的角色,例如 BigQuery 数据集级访问和特定于存储的存储桶访问。

服务帐户角色
然后,您应该创建并下载 SA 的 JSON 密钥。在“服务帐户”页面上:
* 点击*按键选项卡*
* 点击*添加键*下拉菜单
* 选择*创建新键*
* 选择 JSON 作为键类型
* 点击*创建*
点击*创建*在您的计算机上下载 JSON 格式的服务帐户密钥文件。
您可以将 SA 密钥文件的路径添加到 GOOGLE_APPLICATION_CREDENTIALS 环境变量中。
export GOOGLE_APPLICATION_CREDENTIALS=
## 3.数据流 API
在云控制台启用[数据流 API](https://console.cloud.google.com/marketplace/product/google/dataflow.googleapis.com)
## 4.私人谷歌访问
数据流工人要求[私人谷歌访问](https://cloud.google.com/vpc/docs/configure-private-google-access#configuring_access_to_google_services_from_internal_ips)你所在地区的网络。进入 [VPC 网络](https://console.cloud.google.com/networking/networks/list)页面,选择您的网络和地区,点击【编辑】选择上的 ***进行私人谷歌访问,然后*保存*。***

## 5.公共订阅
创建一个 [PubSub](https://cloud.google.com/pubsub/docs/admin) 主题和一个“拉”订阅:在我们的例子中是 *library_app_topic* 和*library _ app _ subscription*。

创建订阅
## 6.云存储
在云控制台[中创建](https://console.cloud.google.com/storage/browser)*library _ app _ bucket*云存储 bucket,里面还有另外三个: *tmp* 、 *staging* 和 *errors* 。Dataflow 将使用此存储桶进行部署,并保存未在 BQ 中接收的数据。

创建存储桶
## 7.BigQuery
我将展示如何创建 *detailed_view* 表,以便您可以轻松地对其他表重复相同的过程。
在 [BigQuery](https://console.cloud.google.com/bigquery) 中,在美国位置创建 *library_app_dataset* ,因为我们将在这个位置运行我们的数据流作业。然后从数据集中点击*添加表格*。

在 BigQuery 中创建一个表
选择源作为*空表*。在表名字段中写下 *detailed_view* ,然后点击*编辑为模式部分下的文本*。
在下面插入这个 JSON,点击*创建表格*按钮。
[
{
"mode": "NULLABLE",
"name": "event_type",
"type": "STRING"
},
{
"mode": "NULLABLE",
"name": "time",
"type": "STRING"
},
{
"mode": "NULLABLE",
"name": "visitor_id",
"type": "STRING"
},
{
"fields": [
{
"mode": "NULLABLE",
"name": "id",
"type": "STRING"
}
],
"mode": "NULLABLE",
"name": "book",
"type": "RECORD"
}
]
您可以在我的 GitHub [存储库](https://github.com/olgazju/blog_pubsub_to_bigquery_dataflow_pipeline/tree/main/bq_schemas)中找到所有表的所有模式。
最后,在对 *search* 和 *add_to_favorite* 表重复所有步骤后,您应该会在 BigQuery 中的 *library_app_dataset* 数据集下看到它们。
# 让我们编码
你可以在我的 [GitHub](https://github.com/olgazju/blog_pubsub_to_bigquery_dataflow_pipeline) 上找到所有代码。克隆它然后运行。/gradlew build。不要忘记为您的 SA 设置带有 JSON 键的 GOOGLE_APPLICATION_CREDENTIALS 环境变量。
这里我将只解释管道代码。
首先,我们需要从 Apache Beam 创建一个管道对象,该对象将包含所有数据和数据处理步骤。要配置数据管道选项,您可以创建自己的类(在我们的示例中为 MyOptions ),该类扩展了 DataflowPipelineOptions 和 DirectOptions 类。您可以从命令行使用这些选项来设置资源,例如,发布订阅名称和其他参数。
然后我们从 PubSub 订阅中读取无界数据。Apache Beam 中有一个 PubsubIO 类,为云发布/订阅流提供预先编写的 PTransforms。
因此,我们有了数据流管道中的第一步—**readpublibsubscription**。
然后我们需要检查 JSON 是否正确:让我们编写自定义的 PTransform,然后在管道代码中使用它作为 **TransformToBQ** 步骤。
在 Apache Beam 的 ParDo 和 DoFn PTransforms 的帮助下,我们用 processElement 函数扩展并覆盖了 PTransform 类。这个函数用 JSON 获取一个字符串,然后在 SUCCESS_TAG 和 [side output](https://beam.apache.org/documentation/programming-guide/#additional-outputs) FAILURE_TAG 下产生主输出。
SUCCESS_TAG 包括通过所有检查并成功转换为 [TableRow](https://developers.google.com/resources/api-libraries/documentation/bigquery/v2/java/latest/com/google/api/services/bigquery/model/TableRow.html?is-external=true) 对象的消息。
FAILURE_TAG 表示一个键值变量,其中键是故障类型:
* “TooBigRow”,大于 10Mb 的 JSON 消息(这是有效负载大小的一个限额。我们不想被这些信息所困)
* “JsonParseError”,在 JSON 解析期间导致各种错误的 JSON 消息
可以添加更多的副输出,并将 IOException 拆分为更详细的异常。
然后我们收集成功处理的数据,并在 Apache Beam 的 BigQueryIO 类的帮助下将其流式传输到 BigQuery 中,**WriteSuccessfulRecordsToBQ**步骤。
*用方法(BigQueryIO。*定义流模式。
*skipInvalidRows* 表示如果一组数据中存在无效行,则跳过它们,不要使所有数据组失败。这仅在流模式下有效。
在摄取过程中,我们从 *event_type* 字段的行中获取目标表的动态定义。
*withExtendedErrorInfo* 允许我们将所有失败的插入保存在一个单独的 PCollection 中,并在 **MapFailedInserts** 步骤中定义错误。
在下面的代码中,我们从 FAILURE_TAG PCollection 和 failedInserts 中获取所有数据,然后[将其合并到一个 PCollection 中。
展平后,我们需要应用](https://beam.apache.org/documentation/transforms/python/other/flatten/)[窗口](https://beam.apache.org/documentation/programming-guide/#windowing)和[触发器](https://beam.apache.org/documentation/programming-guide/#triggers)在**窗口错误**步骤中创建某个时间间隔的数据块,并在 **WriteErrorsToGCS** 步骤中将其作为一个文件写入云存储桶。因为在这种情况下我们不关心数据的顺序,所以我们可以使用 GlobalWindows。
然后运行管道。
# 添加指标
作为管道的结果,我们在 BigQuery 中拥有所有“好的”数据,在 bucket 中拥有所有“坏的”数据。让我们创建一些[指标](https://beam.apache.org/documentation/programming-guide/#metrics)。我们将使用 Apache Beam 中的计数器度量类。
因此,您可以简单地在特定位置对每个指标使用 inc()操作,然后在数据流作业中查看这些指标。
例如,让我们计算一下管道中有多少失败的插入。取*failed inserts*p collection 并应用 metric . failed insertmessages . Inc();对于 processElement 中的每个元素。因此,failedInsertMessages 包含当前失败的插入次数。
# 在本地计算机上运行管道
一旦你设置了环境和 GOOGLE_APPLICATION_CREDENTIALS 变量,并且有了你的代码,你就可以在本地机器上用 [DirectRunner](https://beam.apache.org/documentation/runners/direct/) 首先运行管道。
转到 gradlew 文件所在的文件夹,首先构建代码
./gradlew build
然后用这个命令运行它(不要忘记写下你的项目名而不是<project_name>):</project_name>
如果一切正常,您将在命令行中看到以下几行:
<==========---> 80% EXECUTING [3m 15s]
:app:run
如你所见,代码的编写提供了在不同的 GCP 项目中使用 BigQuery、PubSub 和 Dataflow 的可能性。
# 在数据流上运行管道
用 DirectRunner 检查我们的管道后,我们可以用 [DataflowRunner](https://beam.apache.org/documentation/runners/dataflow) 在数据流中运行它。这与上一段中的命令几乎相同,但是 runner 参数是 DataflowRunner(不要忘记写下您的项目名称,而不是< project_name >)。
参数*enablestreamingine*允许使用[流媒体引擎](https://cloud.google.com/dataflow/docs/guides/deploying-a-pipeline#benefits-of-streaming-engine)。数据流的流媒体引擎将管道执行从工作虚拟机中移出,并移入数据流服务后端,这意味着消耗的 CPU 和其他资源更少。
在自动缩放期间,数据流会自动选择运行您的作业所需的工作实例的适当数量,参数 *maxNumWorkers* 会限制这个数量。
参数 *usePublicIps=false* 意味着出于安全原因,您的数据流实例将没有公共 IP。
数据流的部署需要时间。最后你会看到如何取消工作的信息。
17663 [main] INFO org.apache.beam.runners.dataflow.DataflowRunner - To cancel the job using the 'gcloud' tool, run:> gcloud dataflow jobs --project=<your_project> cancel --region=us-central1 2021-09-23_04_34_38-9136796642874199393
在云控制台中,转到[数据流作业页面](https://console.cloud.google.com/dataflow/jobs)并查看您的数据流作业。您应该会在名称附近看到一个绿色圆圈,表示作业正在成功运行。
如果您不打算使用该作业,请不要忘记将其取消。
# 测试管道并检查指标
让我们向 PubSub 主题发布一些“好”数据。在云控制台中打开 PubSub [页面](https://console.cloud.google.com/cloudpubsub/topic/list),选择我们的主题,然后点击发布消息。

将下面这个 JSON 插入到*消息体*部分,点击*发布*按钮
{"event_type": "search",
"time": "2021-08-01",
"visitor_id": "bdd6a03a-390a-4e76-b098-b74ea385228e",
"search_query": "War and Peace"
}
然后转到[大查询页面](https://console.cloud.google.com/bigquery)并检查*搜索*表中的数据。
SELECT * FROM library_app_dataset.search
查询结果中有我们的“战争与和平”事件。

你可以从我的 [GitHub](https://github.com/olgazju/blog_pubsub_to_bigquery_dataflow_pipeline/tree/main/data) 中尝试更多的测试数据。
让我们检查一下我们的管道如何处理“坏”数据。
## JSON 中断
{"event_type":
"add_to_favorite","time":
}
所以这个 JSON 坏了,它应该在一分钟内出现在*错误*桶中,其名称采用下一种格式:
library _ app _ bucket/errors/JsonParseError/2021–09–23/error-13:10:09–0/1
如果您下载这个文件,您将会看到它包含了我们损坏的 JSON
## 错误的模式
让我们以 add_to_favorite 事件为例,将评级从数字更改为某个字符串,并将其推送到主题。
{"event_type": "add_to_favorite",
"time": "2021-08-01",
"visitor_id": "bdd6a03a-390a-4e76-b098-b74ea385228e",
"book": {"id": "1231234"},
"rating": "this is a string"
}
因此,由于类型不兼容,这个 JSON 不能被 BQ 接收,我们很快就会在错误桶中看到它,格式如下:
library _ app _ bucket/errors/failed inserts/2021–09–23/error-13:19:47–0/1
如果您打开该文件,您可以检查是否存在与我们推送至 PubSub 主题相同的 JSON。
# 是时候检查我们的指标了
在数据流中运行管道并在 PubSub 中获得第一条消息后,您可以在谷歌云控制台中打开数据流作业页面,并在作业信息下看到一个新的“自定义计数器”部分。在下图中,您可以看到 PubSub 中有三条消息,其中两条已成功处理,但其中一条在接收过程中失败。我们看到一条无法解析的消息。

韵律学
# 结论
总之,现在您已经知道如何将数据流用于典型的 ETL 数据管道,您还可以更深入地研究 Apache Beam 世界。在本教程中,除了保存“坏”数据之外,我们没有处理太多的“坏”数据,但是添加一些机制用于进一步处理是可能的。
就这些,感谢阅读。
你可以在 [Twitter](https://twitter.com/olgazju_dev) 和 [LinkedIn](https://www.linkedin.com/in/olgabraginskaya/) 上关注我。
# 用 CatBoost 给它打气
> 原文:<https://towardsdatascience.com/pump-it-up-with-catboost-828bf9eaac68?source=collection_archive---------29----------------------->
## 数据挖掘和简单的入门模型

sofiya kirik 在 [Unsplash](https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 上拍摄的照片
# **简介**
这篇文章是基于竞争[驱动的数据](https://www.drivendata.org/competitions/7/pump-it-up-data-mining-the-water-table/)发表的关于坦桑尼亚水泵的文章。比赛信息由坦桑尼亚水利部通过一个名为 Taarifa 的开源平台获得。坦桑尼亚是东非最大的国家,人口约 6000 万。一半的人口无法获得干净的水,2/3 的人口卫生条件差。在贫困家庭中,家庭往往不得不花几个小时步行去水泵取水。数十亿美元的外国援助正被提供给坦桑尼亚以解决淡水问题。然而,坦桑尼亚政府无法解决这个问题。相当一部分水泵完全失灵或实际上不起作用;其他的需要修理。坦桑尼亚的水资源部同意 Taarifa 的观点,他们发起了 DrivenData 竞赛。
# **数据**
该数据具有许多与水泵相关的特征。与地理位置、创建和管理地理位置的组织相关的数据,以及关于地区、地方政府区域的一些数据。此外,还有关于结帐类型、付款类型和数量的信息。供水点分为功能性、非功能性和功能性但需要维修。竞赛的目标是建立一个预测供水点功能的模型。
建模数据有 59400 行和 40 列,没有单独文件中的标签。
这个竞争使用的度量是*分类率*,它计算提交中的预测类与测试集中的实际类相匹配的行的百分比。最大值为 1,最小值为 0。目标是最大化*分类率*。
# **探索性数据分析**
以下一组关于水点的信息用于分析:
* amount_tsh —总静压头(水点可用水量)
* date_recorded —输入行的日期
* 出资人——谁为油井出资
* gps_height —井的高度
* 安装者——安装油井的组织
* 经度 GPS 坐标
* 纬度 GPS 坐标
* wpt_name —水点的名称(如果有)
* num _ private 无信息
* 盆地—地理水域盆地
* 子村—地理位置
* 区域—地理位置
* 区域代码-地理位置(编码)
* 地区代码—地理位置(编码)
* lga —地理位置
* 病房—地理位置
* 人口—油井周围的人口
* 公开会议—对/错
* recorded_by —输入该行数据的组
* scheme _ management——谁在运营供水点
* scheme _ name——谁运营供水点
* 允许—如果水点是允许的
* 建造年份——供水点建造的年份
* 提取类型—水点使用的提取类型
* 提取类型组—水点使用的提取类型
* extraction _ type _ class-water point 使用的提取类型
* 管理——如何管理水点
* 管理组—如何管理水点
* 付款——水的价格
* 付款类型—水的价格
* 水质——水的质量
* 质量组—水的质量
* 数量——水的数量
* quantity_group —水的数量(复制质量)
* 来源——水的来源
* source _ type 水的来源
* source _ class 水的来源
* 水点类型—水点的种类
* 水点类型组—水点的类型
首先,让我们看看目标——这些类的分布并不均匀。

值得注意的是,需要修理的水泵标签数量很少。有几种方法可以缓解阶级不平衡的问题:
* 欠采样
* 过采样
* 什么都不做,使用库的功能来构建模型
让我们看看水泵是如何分布在全国各地的。

某些要素包含空值。

我们可以看到很少几行有缺失值,其中 *scheme_name* 的数量最多。
下面的热图表示变量之间的存在/不存在关系。值得注意的是*许可证、安装方*和*出资方*之间的相关性。

让我们在树状图上看到这些关系的概貌。

在水泵的特性中,有一项是显示水量的。我们可以检查水量与泵的状况( *quantity_group)* 之间的关系。

可以看出,有许多水量充足的井没有发挥作用。从投资效率的角度来看,把重点放在修复这个特殊群体放在第一位是合乎逻辑的。此外,观察到大多数干式泵不工作。通过找到一种解决方案,让这些井再次充满水,它们可能会发挥作用。
水质会影响水泵的状况吗?我们可以看到按 *quality_group* 分组的数据。

不幸的是,这个图表提供的信息不多,因为优质水源的数量占优势。让我们试着只对水质较差的水源进行分组。

大多数质量组未知的泵无法运行。
水点还有另一个吸引人的特征——它们的类型( *waterpoint_type_group* )。

通过 waterpoints 对数据的分析表明,带有*其他*类型的组包含许多不工作的泵。它们过时了吗?我们可以检查泵的制造年份。

一个合理的预期结果——供水点越老,它不起作用的可能性就越大,大多数是在 80 年代以前。
现在,我们将尝试从有关资助组织的信息中获得见解。井的状况应与资金相关。只考虑那些资助超过 500 个水点的组织。

丹麦国际开发署——坦桑尼亚和丹麦在油井资金方面的合作,尽管他们有许多工作用水点,但破损的百分比非常高。RWSSP(农村供水和卫生方案)、Dhv 和其他一些项目的情况类似。应该指出的是,由德意志共和国和私人出资的大部分水井大多处于工作状态。相比之下,大量由国家出资的水井并没有发挥作用。中央政府和区议会设立的大多数供水点也不工作。
让我们考虑这样一个假设:水的纯度和井所属的水池会影响其功能。首先,我们来看看水盆。

两个盆地非常突出——鲁本和鲁夸湖。那里断水点的数量占大多数。
众所周知,有些井不是免费的。我们可以假设,付款可以积极地影响保持泵正常工作。

这个假设被完全证实了——支付水费有助于保持水源处于工作状态。
除了分类参数之外,数据还包含数字信息,我们可以查看这些信息,也许会发现一些有趣的东西。

部分数据填充了 0 值,而不是真实数据。我们还可以看到 *amount_tsh* 在可工作的水点(label = 0)更高。此外,您应该注意 *amount_tsh* 特性中的异常值。作为一个特征,人们可以注意到海拔的差异,以及相当一部分人口生活在平均海平面以上 500 米的事实。
# 数据清理
在开始创建模型之前,我们需要清理和准备数据。
* *安装程序*功能包含许多不同大小写、拼写错误和缩写的重复内容。让我们先把所有的东西都用小写字母。然后,使用简单的规则,我们减少错误的数量,并进行分组。
* 清理后,我们用“其他”项目替换出现次数少于 71 次(0.95 分位数)的任何项目。
* 我们通过类比*出资方*特征来重复。截止阈值是 98。
* 数据包含类别非常相似的要素。让我们只选择其中之一。由于数据集中没有太多的数据,我们将该特征保留为最小的类别集。删除*方案 _ 管理、数量 _ 组、水质、支付 _ 类型、提取 _ 类型、用水点 _ 类型 _ 组、区域 _ 代码。*
* 将异常值的*纬度*和*经度*值替换为相应*区域代码的中值。*
* 替换缺失值的类似技术适用于*子列*和*方案名称*。
* *public_meeting* 和 *permit* 中的缺失值被替换为中间值。
* 对于*子监督*、*公开 _ 会议*、*方案 _ 名称*、*允许、*我们可以创建不同的二进制特征来显示缺失值。
* *方案 _ 管理*、*数量 _ 组*、*水质 _ 质量*、*地区 _ 编码*、*支付 _ 类型*、*提取 _ 类型*、*用水点 _ 类型 _ 组*、*日期 _ 记录*、*_ 记录者*可以删除,不是重复信息就是无用。
# **造型**
该数据包含大量的分类特征。最适合获得基线模型的,在我看来是 [CatBoost](https://catboost.ai/) 。这是一个高性能的开源库,用于决策树的梯度提升。
我们不会选择最优参数,让它成为家庭作业。让我们写一个函数来初始化和训练模型。
def fit_model(train_pool, test_pool, **kwargs):
model = CatBoostClassifier(
max_ctr_complexity=5,
task_type='CPU',
iterations=10000,
eval_metric='AUC',
od_type='Iter',
od_wait=500,
**kwargs
)return model.fit(
train_pool,
eval_set=test_pool,
verbose=1000,
plot=False,
use_best_model=True)
对于评估,选择 AUC 是因为数据是高度不平衡的,这种指标是这种情况下最好的。
对于目标度量,我们可以写我们的函数。
def classification_rate(y, y_pred):
return np.sum(y==y_pred)/len(y)
由于数据很少,因此将数据集分成*训练*和*验证*部分并不重要。在这种情况下,最好使用 OOF (Out-of-Fold)预测。我们不会使用第三方库;让我们试着写一个简单的函数。请注意,将数据集分割成折叠必须分层。
def get_oof(n_folds, x_train, y, x_test, cat_features, seeds): ntrain = x_train.shape[0]
ntest = x_test.shape[0]
oof_train = np.zeros((len(seeds), ntrain, 3))
oof_test = np.zeros((ntest, 3))
oof_test_skf = np.empty((len(seeds), n_folds, ntest, 3)) test_pool = Pool(data=x_test, cat_features=cat_features)
models = {} for iseed, seed in enumerate(seeds):
kf = StratifiedKFold(
n_splits=n_folds,
shuffle=True,
random_state=seed)
for i, (train_index, test_index) in enumerate(kf.split(x_train, y)):
print(f'\nSeed {seed}, Fold {i}')
x_tr = x_train.iloc[train_index, :]
y_tr = y[train_index]
x_te = x_train.iloc[test_index, :]
y_te = y[test_index]
train_pool = Pool(data=x_tr, label=y_tr, cat_features=cat_features)
valid_pool = Pool(data=x_te, label=y_te, cat_features=cat_features)model = fit_model(
train_pool, valid_pool,
loss_function='MultiClass',
random_seed=seed
)
oof_train[iseed, test_index, :] = model.predict_proba(x_te)
oof_test_skf[iseed, i, :, :] = model.predict_proba(x_test)
models[(seed, i)] = modeloof_test[:, :] = oof_test_skf.mean(axis=1).mean(axis=0)
oof_train = oof_train.mean(axis=0)
return oof_train, oof_test, models
为了减少对分裂随机性的依赖,我们将设置几个不同的种子来计算预测。

其中一个折叠的学习曲线
学习曲线看起来令人难以置信的乐观,模型应该看起来不错。
考虑到模型特性的重要性,我们可以确保没有明显的漏洞。

其中一个模型中的特征重要性
平均预测后:
balanced accuracy: 0.6703822994494413
classification rate: 0.8198316498316498
这个结果是在比赛网站上传预测时得到的。

考虑到在撰写本文时,top5 的结果仅提高了大约 0.005,我们可以说基线模型是好的。

# **总结**
在这个故事中,我们:
* 熟悉数据,寻找能够引发特征生成想法的见解;
* 清理和准备提供的数据以创建模型;
* 决定使用 CatBoost,因为大部分特征是分类的;
* 为 OOF 预测编写了一个函数;
* 获得了基线模型的优秀结果。
正确的数据准备方法和选择正确的工具来创建模型,即使不增加额外的功能,也能产生很好的结果。
作为一项家庭作业,我建议添加新的功能,选择模型的最佳参数,使用其他库进行梯度增强,并从结果模型中构建集成。
文章中的代码可以在这里查看[。](https://github.com/sagol/pumpitup/blob/main/oof_model.ipynb)
# 纯粹主义者与实用主义者——为什么我们都同意自动化
> 原文:<https://towardsdatascience.com/purist-v-pragmatist-why-we-can-both-agree-on-automation-cc2f011fd34a?source=collection_archive---------31----------------------->
## 我们都在努力寻找可持续的解决方案,然而我们却坐在意大利面条式的代码上,我们的未来不可避免地…意大利面条式的代码在云中。

拉夫(拉维)凯登在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上的照片
我们见过这两个人物。喜欢对“未来数据管道”、“现代数据架构”、“最佳实践”进行理论化的纯粹主义者,都被 PowerPoint 巧妙地置于死地。他是一个真正坚持规则的人。让我们称他为固执的史蒂夫。您满怀期待地等待着下一次架构会议,届时您将会对我们接下来要实现的内容感到激动和惊讶。另一方面,英雄、实干家、在凌晨 2 点工作以将数据投入生产的人(或女孩)。姑且称她为务实审慎。她能编辑的是一个完全不同(也更可怕)的幻灯片。把她所知道的放进 PowerPoint,快速浏览一下,你可能会得到类似韦斯·克雷文电影的东西。
这两个人不会深情地谈论对方,至少在私下里不会。
普鲁登斯指责史蒂夫不能实施务实的解决方案,史蒂夫指责普鲁登斯造成了可怕的不可持续的混乱局面。
我经常发现自己是这两种角色之间的沟通者。作为一名顾问,我曾与许多史蒂夫谈论可持续发展和架构良好的解决方案,但我也曾谨慎地坐在战壕中,负责交付,将解决方案投入生产。在开发和生产的中途,我们似乎有一个模糊地带,在那里我们拿出了胶带。“最佳实践”并不能完成工作。对话慢慢地从“我们需要坚持好的概念设计”转变为“我们需要在下周之前将它投入生产,否则业务将会终止”。在此之前,我们使用锤子和钉子来精心构建史蒂夫提出的解决方案。在这个转折点之后,普律当丝带来了她信任的伙伴,胶带先生。
那么,我们如何朝着同一个方向努力呢?我非常欣赏前同事的一篇文章,关于数据架构师(纯粹主义者)如何会犯错误,以及对交付负责如何是关键的先决条件。这就是让两个角色坐在同一张桌子旁的原因。确保纯粹主义者负责将架构良好的解决方案投入生产,从而为史蒂夫的词汇增加一点实用主义。
既然我们坐在同一张桌子旁,让我们开始说同一种语言。我将把重点放在我相信双方都会同意并欣赏的一个方面……**自动化。**
Steve 会同意,构建模式、寻找可重复的组件和自动化可重复的组件绝对符合纯粹主义者的心态。凭经验,普鲁登斯知道她经常不断地重复某些测试、某些过程,以便提炼、修改并准备好最终的解决方案。
首先,我们需要回答核心问题——什么可以自动化?如果答案是“没什么,因为一切都是定制的”,我真的希望你和我一样颤抖。只要再快速浏览一遍韦斯·克雷文的幻灯片,就能确定你完全被吓到了。
只有可重复的任务可以自动化。我提倡一种不断发现、模板化、模块化和改进模式的环境。持续改进。推动这种文化的是纯粹主义者的角色,而测试任何新模式以确保其可行性的是实用主义者的角色。正如对交付负责会让史蒂夫更多地参与,一点点总体责任会迫使谨慎变得谨慎,并成为自动化的倡导者。
在这一点上,史蒂夫确信无疑。我们唯一需要说服普鲁登斯的是,尽管目前一切都是定制的,但其中隐藏着可重复的模式。谨慎需要确凿的事实和例子,我现在不会详细说明,但可能是一个很好的后续主题。
自动化的反对者通常会分享这样的画面:

图片来源:[xkcd.com](https://xkcd.com/1319/)
他们并没有完全错。这就是迷因的现实。它们之所以有趣,只是因为其中有一定的真实性。
自动化需要大量的训练。如果你没有纪律性,一旦时间压力出现,你总是会退回到以前的方式。比起不做英雄,你肯定更讨厌低效和重复的工作。
停下来反省一下——问问自己 ***我讨厌低效率吗?*对我自己来说,我会这样重新表述这个问题:我是否不断地发现自己处于这样一种情况下:“我真的认为我们几个月前就解决了这个问题,为什么我们又在想这个问题?”这种感觉让我心烦意乱,它让我烦躁不安,让我恼火,让我讨厌。那些曾与我共事的人知道,在这个阶段,我变得极其迂腐,几乎像机器人一样。必须的。求解。效率低下。现在。**
建立基础的时间可能比预期的要长,可重复模式的识别和模板化将比第一次手工做的时间要长。也许是第二次,也许是第三次,也许…好吧,好吧,你明白我的意思了。沿着这条线的某个地方,自动化将会超过手工,但是只有当你有纪律坚持这个过程。
史蒂夫绝对讨厌低效率,我相信普鲁登斯也是,她可能太喜欢当英雄了。(另一天的主题)。一旦我们在对低效率的憎恨上达成一致,我们需要相信有足够多的低效率是我们可以解决的,隐藏的模式(或明显的模式)是我们可以提取、重复并最终自动化的。不要像下图中的家伙一样,现在腾出时间是为了以后腾出时间。

由 [imgflip](https://imgflip.com/memegenerator/122160104/No-thanks-we-are-too-busy) 生成的图像
另一方面,自动化是不可避免的。我相信来自业务和 IT 的需求创造了足够的动力。数据工程师已经筋疲力尽,呼吁采用更好的方法。我们应该朝着它努力,而不是试图与之斗争,以及如何使它变得更好。
在我与客户的交谈中,数据运营、开发运营、CI/CD 这些术语每次都会出现。自动化是数据运营和开发运营的重要支柱。我不能总是问我的客户这是他们真正需要的还是他们的史蒂夫建议的,但需求是明确的。工具需要能够与詹金斯,GitHub 等集成。一切都应该是可脚本化的。我们希望设计漂亮的前端,但我们希望命令行界面操作和部署。
我们的方法和工具包需要补充我们的文化,而不是相反。我们需要一种训练有素而又务实的文化,并辅以我们的方法和软件。让人们做我们擅长的事情——为有趣的问题提出创造性的解决方案。让计算机做它们擅长的事情——重复和并行处理。
意大利面条代码不需要成为我们的命运。为了找到隐藏的模式,让我们有纪律和纯粹的天真开始解开它。然后务实的去迭代,持续改进知道 100%不是目标。
一天结束时,史蒂夫和普鲁登斯都有他们想花更多时间陪伴的家庭。自动化将有助于实现这一目标。这是他们能达成一致的第二件事。
1. Thomas, J., 2021. The rise and fall of data architecture. [online] ITWeb. Available at: https://www.itweb.co.za/content/o1Jr5Mx9AxPqKdWL [Accessed 24 November 2021].
2.WIRE, B., 2021. Data Engineers Are Burned Out and Calling for DataOps. [online] Businesswire.com. Available at: https://www.businesswire.com/news/home/20211019005858/en/Data-Engineers-Are-Burned-Out-and-Calling-for-DataOps [Accessed 24 November 2021].
# 把你的钱投在你的 ML 上:建立对商业关键人工智能的信任
> 原文:<https://towardsdatascience.com/put-your-money-where-your-ml-is-building-trust-in-business-critical-ai-f57963dc5109?source=collection_archive---------33----------------------->
## [行业笔记](https://towardsdatascience.com/tagged/notes-from-industry)
## **本文探讨了在交易处理中信任人工智能(AI)的问题。在过去的三十年中,模型治理框架已经发展,并由于模型复杂性的增加而变得紧张,使其核心方法变得站不住脚。在评估信任一个人工智能模型意味着什么时,我们将检查这个框架的现代化,其目的是使用连续统计测试的哲学来建立和维持信任。**
消费者希望银行能同时保护他们的钱,并提供不受限制的资金存取,尤其是在紧急情况下。反过来,银行别无选择,只能相信他们的自动处理系统在批准交易时做出了正确的决策。这些决策必须自动化——数千个决策必须在几毫秒内完成——因此由合格人员进行人工审查是不可能的。
从银行的角度来看,这些决定具有深刻的财务后果。在真正购买时,一个错误的决定就可能导致一个好客户的永久流失。另一方面,在欺诈性购买上的一个错误决定可能导致数百万美元的即时损失。金融机构别无选择,只能把一个勇敢的小 AI 放在城门前,并相信它会完成任务。随着这些系统变得更加复杂,它们也变得更加难以用 ***语义逻辑*** 来概括。最新一代的深度学习驱动的决策人工智能,如 Featurespace 的自动化深度行为网络,承诺了前所未有的决策准确性,但它们是以人类可理解的逻辑为代价的。鉴于对系统可能出轨的担忧,企业如何才能在一个新的复杂的人工智能系统中建立信任?它的未来取决于该系统的决策。
# 模型治理
在 20 世纪 80 年代,世界各地的银行都是简单机器学习系统的早期采用者。他们能够在这些系统中建立一定程度的信任,部分是因为逻辑可以由人类设计和审查。这个审查过程被称为**模型治理**,今天的银行通常会雇佣多个模型治理团队来独立审查在业务中部署的模型。这个过程如此重要,以至于美联储颁布了规定,概述了这个过程应该是什么样子。模型治理围绕两个过程——部署前的**审计**,以及部署后的**监控**模型。
典型审计通常关注三件事:
1.检查编码在特性中的逻辑的充分性和相关性(例如:使用这些特性是明智的吗?)
2.检查所学习的分类器,自省其所学习的逻辑,并评估其在验证数据上的性能。通常按交易的不同子群体进行细分。
3.检查产生分类器的训练方案。这包括检查用于训练和测试的数据(以确保其正确标记并代表真实世界的数据),然后审查实验结果支持数据科学家所做建模决策的证据(这些决策包括分类器算法选择、超参数选择和选择作为输入的特征)。
总而言之,审计的一部分是使用结果分析对建模决策进行统计评估,而另一部分是对逻辑进行定性评估。对于银行已经发展到可以使用的初级第一代机器学习系统,逻辑评估一直很简单,数学上也很好。最常见的简单分类器之一是一种被称为*逻辑回归*的算法。该分类器将未知结果的比值比的对数建模为提供给模型的 ***n*** 特征的加权和:

这通常与一组特征 ***x1…xn*** 一起使用,这些特征本身是编码人类设计逻辑的二进制决策规则。例如,数据科学家可能会设计他们的一个功能,作为一项规则来评估交易是否代表帐户在半夜在电子商务网站上的异常高的花费:

为了建立一个模型,数据科学家会定义数百个这样的规则。他们将使用领域知识、直觉和来自探索性数据分析的洞察力,实现用于捕捉欺诈的逻辑和用于批准真实支出的逻辑。然后,通过逻辑回归计算特征权重。拟合的权重有一个明智而有意义的解释:权重代表回归者认为交易将被证明是欺诈的可能性有多大(或有多大),假设规则中的条件为真。例如:
如果规则的拟合权重是 0.693(2 的自然对数),那么回归变量对交易的怀疑是两倍,因为规则中编码了逻辑因子。
权重正好为 0(1 的自然对数)意味着回归变量已经学会忽略属性,因为它认为它们是不相关的。
在这种方法下,决策规则包含可以被人类审计员解析和验证的逻辑思想,分配给规则的权重准确地告诉他们如何使用该信息。如果他们不喜欢某些信息的使用方式,他们可以直接将权重归零。从这个意义上说,分类器是可组合的,规则可以独立地添加或删除。权重告诉审计员该模型的聚合逻辑,对于该交易为真的特定规则的权重告诉分析师该模型做出的每个预测背后的确切逻辑。
尽管这些模型很容易被信任,但它们也是初级的,不再提供让银行具有竞争力所需的保护水平。例如,在 2010 年[【2】](#_ftn2)期间,每年被骗的英国账户数量增加了 55%,从 180 万增加到 280 万。银行信任了几十年的系统现在被 21 世纪欺诈的数量和复杂性所淹没,刺激行业采用目前已知的机器学习最强大的分支的算法,*深度学习*。
# 深度学习的时代
在大多数机器学习领域,设计良好的深度学习算法已经成为最先进的技术。然而,深度学习取得成功的关键是磨练架构和组件,以充分利用数据中的结构和相关性。对于事务流,这意味着不要孤立地考虑每个事务,而是要考虑导致当前事件的一系列操作中的所有相关信息。在这个序列中,最重要的是付款人当前的意图。
与这种设置最相似的是自然语言处理,其中动作序列是句子中使用的单词。在我们的例子中,句子是客户的账户历史,单词是金融交易。因此,像这样处理序列信息的深度学习算法必须以两种方式之一运行。他们或者以变压器[【3】](#_ftn3)或卷积网络的方式读取并操作序列中的一些连续子集,或者他们读取单个事件作为输入,并利用具有内置本地存储器的神经细胞来记住序列中先前示例的相关信息。后一类称为递归神经网络,包括 lst ms[【4】](#_ftn4)、GRUs[【5】](#_ftn5)和 Featurespace 全新的自动化深度行为网络架构等架构。
然而,深度学习算法的一个副作用是,它们失去了直观的可解释性。这些决定背后的逻辑超出了人类的解释能力。逻辑回归算法可能已经训练了数百个关于人类工程逻辑特征的参数,而深度学习的参数以十亿计,并且这些算法通常对原始未处理的事件数据进行操作。对逻辑特征工程步骤没有明确的要求——特征可以由架构中较低的神经层学习——它们学习的内部特征是复杂的。它们从网络中数以百万计的连接到前几层的相互影响中获取价值。
传统的模型治理过程依赖于对人类逻辑的审计和常识的应用,面对这样的复杂性,这些过程完全失败了。
# 建立信任
鉴于这些决策系统的成功运作关系重大,如果不建立高度的信任,企业就无法运营深度学习模型。如果模型治理框架现代化,并与深度学习模型的复杂性保持一致,那么这种信任程度就可以实现。有多种方法可以在一个不透明的模型中系统地建立信任,无法将决策简化为简单的逻辑并不意味着我们必须闭上眼睛,希望算法奏效。
当组织决定是否部署新系统时,有四个主要的信任和治理问题:
1.**模型还能更好吗**?这反映了一种焦虑,即模型开发人员本可以做得更好。正确的最关键的元素是确保训练数据和它的标记机制是真实世界的代表。在此之后,围绕模型开发的问题对于深度学习模型来说更容易明确回答,因为不依赖于数据科学家的特征工程的创造力或领域理解。取而代之的是,将特征工程融入损失函数优化的过程中,因此算法不断迭代,直到找到最佳特征。还有其他可以质疑的建模选择(数据准备、数据选择、模型超参数、架构选择等),但与功能发明不同,这些选择是从定义的选项列表中系统选择的,每个选择的价值都由实验结果证明。Auto-ML 方法甚至可以自动执行这种搜索,同时为决策生成透明的审计跟踪。
2.**模型公平吗?**这反映了对模型歧视受保护属性的担忧。对于深度学习模型来说,更难直观地确定这一点,因为逻辑无法手动检查禁止的推理。虽然人类偏见确实不会通过特征工程蔓延到模型设计中,但由于训练数据中虚假的或不具代表性的相关性,现实世界中过度(或不足)代表人群的有偏见的选择机制,或影响我们训练数据标签的有偏见的标签机制,偏见仍然会蔓延。即使模型对受保护的属性视而不见,它仍然可能通过在数据中找到代表受保护属性的信号而导致歧视性的结果。这是深度学习模型的治理框架必须解决的问题。
3.**每个预测都合理吗?**这反映了一种焦虑,即一些预测是不合理的,依赖于虚假的相关性,或者对训练数据中的稀疏区域进行了糟糕的外推。治理过程必须通过为模型在生产中做出的每个预测提供逻辑解释来解决这个问题。对于深度学习,逻辑解释很难,因为逻辑太复杂了。治理还必须围绕每个预测的预期质量提供透明性,即:模型在多大程度上基于大量证据工作,而在多大程度上基于推断工作。必须持续评估此类措施,以控制训练和服务数据之间的系统性偏差,这种偏差往往会随着时间的推移而增长。这些是模型治理必须解决的挑战。
4.**预测稳定吗?**这反映了一种焦虑,即面对数据中的真实世界影响,如事件激增、生产数据中断或数据处理的边缘情况,模型可能会偏离轨道并严重失败。这些都是合理的顾虑,最好通过了解可能的影响以及模型对这些影响的反应来解决。这意味着在离线实验中模拟这些效应,探测模型对它们的敏感性,并观察模型的恢复时间。这些信息将为模型操作员提供一份操作手册,帮助他们在出现这些问题时快速、恰当地做出反应。
# 治理现代化
我们上面讨论的信任问题为现有模型治理框架内的深度学习带来了 3 个未回答的核心挑战,每个挑战都需要新的策略来提供模型用户正确需要的信任级别。这些挑战是:
1.如何估计我们在一个特定的预测中应该具有的置信度?
2.如何解释导致特定决策的逻辑。
3.如何保证公平?
这些挑战可以通过对模型有效性进行严格和持续的统计测试来加强我们的监控工作,或者通过将支持算法整合到我们的建模堆栈中来克服。让我们从关注改进治理测试制度开始。
# 适合度
对机器学习模型的严格测试意味着理解模型在其预测中的优势和劣势,包括在开始时和在持续的基础上,因为这些优势由于概念漂移而演变。建立信任的最直接方法是校准模型的预测,这将分数转化为概率,然后对模型的概率进行拟合优度测试。下面的例子展示了这种分析所产生的视觉化。

***图 1:*** *对信息特征的拟合优度分析(用直方图均衡化宁滨计算)。该特征在低值时显示出适度过度拟合的迹象,因为在测试集(右)中的预测和观察之间存在系统偏差,而在训练集(左)中则没有。在> 1 的最右边的容器也引入了复杂的结构,这需要分类器学习更多的非线性,因此应该理解这种结构的原因和含义。作者图片。*
针对拒绝验证集的模型分数被聚合到离散桶内的分数分布中,这里由落入特定范围内的特征值来定义。在该桶中,分数分布通过位置参数(平均分数)和比例参数(分数相对于平均值的标准偏差)进行总结。然后,可以将平均值及其不确定性与观察到的欺诈发生率进行比较——一个拟合良好的模型应该在平均值和观察到的发生率之间没有统计上的显著偏差。偏差的出现意味着模型预测不好,不应该被信任。在模型开发过程中,这些图有助于提高模型性能。它们允许我们清楚地查看每个特征值对分数的依赖性。如果观察到偏差,可能是由过度拟合(在这种情况下,观察到的偏差在训练集的预测中不存在)或欠拟合(在这种情况下,观察到的偏差在训练和测试数据中都存在)引起的。如果一个桶内的标准偏差很大,这告诉我们该特征在很大程度上被该桶内的模型忽略(即:其他特征支配分数)。
出于治理的目的,当在持续的基础上应用于模型开发之后产生的分数时,这些分析变得特别强大。它们可以有效地检测模型退化,并识别模型退化的特征空间区域。存储桶可以由我们知道的输入数据样本的任何属性来定义,不管它是否被模型使用。这意味着我们可以监控受保护属性的适合度,或者我们在推理时不知道的属性。最有力的是,我们可以将桶定义为神经网络中神经元激活空间的高粒度集群。虽然我们可能无法解释这些分类的意义,但我们使用包含预测的分类的观察拟合优度作为代理,获得了每个预测的持续更新的拟合优度度量。对于每个聚类,偏差是对模型得分准确性的估计,标准差给出了对整个聚类得分函数的梯度和结构的估计。
拟合优度分析是使用严格测试使不透明模型变得更加透明的强大而直观的方法,也是解决现代模型治理的第一个挑战的有效方法。这种思维方式正在人工智能行业获得更大的牵引力,有时会以稍微不同的形式出现,例如信任分数[【6】](#_ftn6)。
# 公平
拟合优度方法还可以帮助应对公平保证的挑战——我们将能够不断测试预测的模型决策*(公平的三种流行衡量标准之一[【7】](#_ftn7))。这意味着我们不断地断言,模型的预测必须无偏差地反映原始数据中观察到的结果趋势。然而,充分性测试不能防止对受保护属性的不同影响——对大多数人来说,这是公平的最直观的伦理定义。*
*在公平的其他两个定义中,不同的影响被认为是不同的。*
*有了 ***独立性*** 保证,我们要求对于受保护属性的不同可能值,模型预测没有差异。这意味着二元分类器的分数分布应该是相同的,不管受保护属性的值如何。*
*利用 ***分离*** 保证,我们要求对于受保护属性的不同值,给定底层类的样本没有差异。对于欺诈预测,这意味着欺诈和真实示例应该分别接收对于受保护属性的所有值都相同的分数分布,但是对于每个受保护属性,所有示例的组合分数分布可能不同。这相当于要求受保护属性的每个值的 ROC 曲线必须对齐-该模型同样擅长于分离所有受保护属性值的类。*
**
****图 2:*** *在这些简单的例子中,左边的模型满足独立性保证——A、B 和 c 组的分数分布是相同的。但是,它不满足充分性保证——A 和 c 组的分数相对于基本事实是有偏差的。相反,右边校准良好的模型满足充分性保证,因为所有组的分数分布相对于基本事实是无偏的。作者图片。**
*分离的一个变体是 ***机会均等*** 的概念,它要求无风险类别的分数分布对于每个受保护的属性值都是相等的——“好客户”不应因为其受保护的属性而被阻止。*
*这些定义可以在持续的基础上直接测试。如果发现差异,可以通过校准分数[【8】](#_ftn8)来强制独立或分离。独立性也可以在训练时“嵌入”,要么通过清除不需要的相关性的训练数据,例如使用可变公平自动编码器[【9】](#_ftn9),要么通过惩罚分类器损失函数中的依赖性。*
*需要注意的一点是,这三种对公平的定义是相互排斥的。当我们谈论构建一个“公平”的分类器时,我们明确地选择了要观察的公平的定义。这种选择在行业中还没有标准化。*
# *说明*
*持续测试可能会解决接受复杂人工智能的大多数挑战,但仍然需要总结每个预测的决策逻辑——这是我们治理挑战的第三个。像这样的解释,如果准确的话,可以通过揭示特定结果背后的逻辑,在模型开发过程中防止糟糕的逻辑。当错误背后的推理能够被理解时,它会建立更大的信任。当我们能够自己判断决策者的错误是由糟糕的逻辑造成的,还是仅仅因为运气不好,我们就会信任他们。*
*有几种算法可以将分数归属于输入要素。SHAP[【10】](#_ftn10),基于博弈论的 Shapley 值,特别受树型模型的欢迎。尽管该算法通常随输入特征的数量呈指数缩放(使得该方法对于几乎所有真实模型都不可行),但 TreeSHAP 变体具有与输入特征的对数缩放[【11】](#_ftn11)。一种解释方法是为深度学习模型配备“解释器模型”(explainer model),这是一种基于树的集成模型,使用工程特征来预测深度学习模型产生的分数。来自深度模型的得分函数通常比潜在的地面真相更容易拟合[【12】](#_ftn12),SHAP 对这些工程特征的归因是有意义的,因为这些特征被设计成对人类有意义的编码信号。*
*然而,我们可以做得更好。像综合梯度[【13】](#_ftn13)或分层相关性传播[【14】](#_ftn14)这样的方法可以将神经网络得分直接归因于输入数据。在图像识别中,当归因于原始输入数据(在这种情况下,图像像素)时,这些方法是成功的,产生指示像素重要性的热图。人类分析师可以以有意义和有用的方式解释这些像素属性。对于欺诈分析,这意味着直接归因于原始输入交易流中的字段,生成客户交易历史的热图。*
*热图不同于传统的解释方法,如 SHAP 或逻辑回归权重,因为它们不归因于人类设计的封装可解释概念的特征。相反,他们归因于原始数据。这意味着用户必须根据具体情况解释逻辑。人类已经被训练识别复杂的模式,并从交易历史中构建有意义的叙述,因此这种方法将充分支持对个体预测的彻底分析。*
**
****图 4:*** *字段重要性信用卡交易序列的热图,用于解释分配给最终交易的高分。在本例中,多种因素在起作用—在不尝试地址验证(AVS 匹配)的高风险商家处花费了大量资金。此外,交易之前紧接着是潜在的低价值测试交易,这是一种标准的欺诈类型。图片作者。**
# *结论*
*为了在不透明的模型中建立信任,我们不仅需要证明其设计决策合理的审计跟踪,我们还需要继续相信该模型在其遇到的所有场景中都得到了很好的校准,继续相信决策是公平的,以及为个人预测可视化模式识别过程的方法。这些先决条件中的三个归结为统计测试。斯坦福大学机器学习教授、Coursera 和谷歌大脑的联合创始人吴恩达认为,通过类比我们在医疗保健领域的信任机制,测试是信任人工智能的充分基础:*
****“我们很多人都愿意服用生化效果无人完全了解的药用药物。我们信任这些药物,因为它们已经通过了随机临床试验,并获得了 FDA 的批准。同样,[不透明的]人工智能算法可能会通过严格的测试获得我们的信任。”——吴恩达。***<#_ftn16>*
**如果我们可以彻底测试深度学习模型,我们可以在不同条件下将透明度引入模型的结果,我们可以了解该结果的可靠性。这使我们处于一个知情的位置,我们可以负责任地使用不透明的模型,因为我们有一个在现实世界场景中信任这些模型的基础。要做到这一点,测试必须对这些场景应用预测质量的相关统计度量,以便我们了解模型在哪里有效,以及它是否公平。此外,这种测试必须是持续的——整合到我们的模型监控中,以确保我们的信任在现实世界不断变化的条件下是稳健的。模型解释通过增加单个预测的透明度在这里起到了支持作用,这对于模型预测不正确的情况尤其重要。我们可以接受模特不走运,但我们不能接受她们不明智。**
**深度学习将在未来几年彻底改变欺诈预测,提供欺诈团队渴望和消费者期待的增强保护级别。为了消费者,我们有责任提升我们的治理框架,将这些模型投入生产。**
**[【1】](#_ftnref1)p . Parkinson,[SR 11–7:模型风险管理指南](https://www.federalreserve.gov/supervisionreg/srletters/sr1107.htm) (2011)美国联邦储备系统董事会**
**[【2】](#_ftnref2)[欺诈—事实 2021,支付行业欺诈的权威概述](https://www.ukfinance.org.uk/system/files/Fraud%20The%20Facts%202021-%20FINAL.pdf) (2021)英国金融**
**[【3】](#_ftnref3)a . vas Wani,N. Shazeer,N. Parmar,J. Uszkoreit,L. Jones,A. Gomez,L. Kaiser 和 I. Polosukhin,[注意力是你所需要的一切](https://papers.nips.cc/paper/2017/file/3f5ee243547dee91fbd053c1c4a845aa-Paper.pdf) (2017)谷歌大脑、谷歌研究院和多伦多大学**
**s . hoch Reiter 和 J. Schmidhuber,[长短期记忆](https://www.researchgate.net/publication/13853244_Long_Short-term_Memory) (1997)神经计算,麻省理工学院出版社**
**[【5】](#_ftnref5)k . Cho,b . merrinboer,C. Gulcehre,D. Bahdanau,F. Bougares,H. Schwenk 和 Y. Bengio,[使用 RNN 编码器-解码器学习短语表示用于统计机器翻译](https://arxiv.org/pdf/1406.1078.pdf) (2014)蒙特利尔大学,雅各布大学,缅因大学**
**[【6】](#_ftnref6)a . Wong,X. Wang 和 A. Hryniowski,[我们能真正信任你们多少?走向简单、可解释的深度神经网络信任量化指标](https://arxiv.org/abs/2009.05835) (2020)康奈尔大学**
**[【7】](#_ftnref7)s . Barocas,M. Hardt 和 A. Narayanan,[机器学习中的公平:限制与机遇](https://fairmlbook.org/pdf/fairmlbook.pdf) (2020)康奈尔大学、加州大学伯克利分校和普林斯顿大学**
**[【8】](#_ftnref8)m . Hardt,E. Price 和 N. Srebro,[监督学习中的机会平等](https://papers.nips.cc/paper/2016/file/9d2682367c3935defcb1f9e247a97c0d-Paper.pdf) (2016)谷歌、UT Austin 和 TTI-芝加哥**
**[【9】](#_ftnref9)c . Louizos,K. Swersky,Y. Li,M. Welling 和 R. Zemel,[变分公平自动编码器](https://arxiv.org/abs/1511.00830v6) (2016)阿姆斯特丹大学、多伦多大学、加拿大高等研究院和加州大学欧文分校**
**[【10】](#_ftnref10)s . Lundberg 和 S. Lee,[解释模型预测的统一方法](https://papers.nips.cc/paper/2017/file/8a20a8621978632d76c43dfd28b67767-Paper.pdf) (2017)华盛顿大学**
**[【11】](#_ftnref11)s . Lundberg,G. Erion 和 S. Lee,[树集合的一致个性化特征归属](https://arxiv.org/abs/1802.03888) (2018)华盛顿大学**
**[【12】](#_ftnref12)l . Ba 和 R. Caruana,[深网真的需要深吗?](http://datascienceassn.org/sites/default/files/Do%20Deep%20Nets%20Really%20Need%20to%20be%20Deep.pdf) (2013)多伦多大学和微软研究院**
**[【13】](#_ftnref13)m . Sundararajan,A. Taly 和 Q. Yan,[深度网络的公理化归因](https://arxiv.org/pdf/1703.01365.pdf) (2017)斯坦福大学和谷歌**
**[【14】](#_ftnref14)s . Bach,A. Binder,G. Montavon,F. Klauschen,K. Müller 和 W. Samek 等人[关于通过逐层相关性传播对非线性分类器决策的逐像素解释](https://www.semanticscholar.org/paper/On-Pixel-Wise-Explanations-for-Non-Linear-Decisions-Bach-Binder/17a273bbd4448083b01b5a9389b3c37f5425aac0) (2015) PLoS ONE**
**[【15】](#_ftnref16)a . Ng,[亲爱的朋友们](https://blog.deeplearning.ai/blog/google-ai-explains-itself-neural-net-fights-bias-ai-demoralizes-champions-solar-power-heats-up) (2019) deeplearning.ai**
# 使用 ONNX 将 GPT-尼奥(及其他)投入生产
> 原文:<https://towardsdatascience.com/putting-gpt-neo-and-others-into-production-using-onnx-1204541e8ff2?source=collection_archive---------19----------------------->
## 了解如何使用 ONNX 将 torch 和 tensorflow 模型投入生产。将推理速度提高 2.5 倍。

Marc-Olivier Jodoin 在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍摄的照片
**注*:全笔记本可用*** [***此处***](https://github.com/oborchers/Medium_Repo/blob/master/Putting%20GPT-Neo%20into%20Production%20using%C2%A0ONNX/ONNX-Export.ipynb) ***。***
**更新*:如果你在很远的将来(截至 2021 年 4 月)读到这篇文章,那么这篇文章的底层代码可能会因为底层库的变化而被弃用。***
## 介绍
使用变压器已经成为最新 NLP 应用的新标准。想想 BERT 或 GPT3,我们可以有把握地得出结论,几乎所有的 NLP 应用都从类似变压器的模型中受益匪浅。然而,这些模型通常部署起来非常昂贵,并且需要特殊的硬件来运行。在本文中,您将了解什么是 ONNX,以及如何将 torch 和 tensorflow transformers 模型移植到 ONNX。您还将学习如何定制 torch 实现,以及如何在以后导出它。具体来说,我们将了解:
1. 简单导出[Bert-base-nli-stsb-mean-tokens](https://huggingface.co/sentence-transformers/bert-base-nli-stsb-mean-tokens)
2. 自定义导出[Bert-base-nli-stsb-mean-tokens](https://huggingface.co/sentence-transformers/bert-base-nli-stsb-mean-tokens)
3. 用 ORT-CustomOps 导出[通用语句编码器](https://tfhub.dev/google/universal-sentence-encoder-large/5)
4. 试图用 1.3B 参数导出[GPT-尼奥](https://huggingface.co/EleutherAI/gpt-neo-1.3B)
## ONNX 是什么?
当我很久以前开始使用变形金刚时,我的第一次体验是使用[伯特即服务](https://github.com/hanxiao/bert-as-service)。虽然 BaaS 仍然是一个不错的库,但现在在 GPU 上部署自己的模型并为其提供一个小的 REST API 已经相当简单了。通常,这将通过一个或多个框架来完成,如 torch 或 tensorflow。但是这在实践中有严重的局限性。
这就是 ONNX 发挥作用的地方。笔**笔**笔【欧洲】笔**笔**网络**笔**交换的目标是提供不同参与者之间的互操作性。互操作性意味着:
1. 跨框架共享模型(例如,torch 到 tensorflow)
2. 跨各种硬件(例如,CPU、GPU、FPGA 等)共享模型
这对社区有好处。还有你的血压。尝试在同一个 GPU 上使用两个不同的框架部署模型。这是一种痛苦。
(你好黑暗我的老朋友……)
在幕后,ONNX 提供了一种定制的文件格式,这是一种由节点组成的计算图,节点本身由基本操作符组成。ONNX 持有大量深度学习和机器学习相关的核心 op,还提供使用定制 op 的能力。引用他们的[主页](https://onnx.ai/about.html):
> ONNX 提供了可扩展计算图模型的定义,以及内置操作符和标准数据类型的定义。
> 每个计算数据流图被构造为形成非循环图的节点列表。节点有一个或多个输入和一个或多个输出。每个节点都是对一个操作员的调用。该图还有元数据来帮助记录其用途、作者等。
如果你想了解 ONNX 的更多信息,微软和 NVIDIA 有一个非常好的演示,你可以在这里找到。请记住,这个演示文稿来自 2019 年,两年内会有很多变化。
在开始使用 ONNX 之前,有三个主要组件与我们的目的相关:
* [ONNX](https://github.com/onnx/onnx) :提供图形格式和操作定义
* [ONNX 运行时](https://github.com/microsoft/onnxruntime):提供可用于在您的硬件上部署模型进行推理的运行时。它包含了 *ExecutionProviders* ,使我们能够使用各种方法加速操作,例如 CPU、Cuda 或 TensorRT。
* ONNX 运行时工具:提供对已经转换的 ONNX 变压器模型进行额外优化的功能。我们不会在这里使用它,但是请记住它是存在的!
## 预赛
要继续下去,您将需要相当多的库。我建议在开始之前构建自己的 Docker 映像,它支持最新的 NVIDIA 驱动程序,甚至可能支持 TensorRT。
从 NVIDIAs `[nvcr.io/nvidia/tensorrt:20.12-py3](https://docs.nvidia.com/deeplearning/tensorrt/container-release-notes/rel_20-12.html#rel_20-12)` image 开始是个好主意。根据您想走多远,您甚至可能想从头开始构建 ONNXRuntime(推荐)。这是我的 [Dockerfile](https://github.com/oborchers/Medium_Repo/blob/master/onnxruntime-issues/Dockerfile) 你可以用它工作。
这个脚本需要适应您的配置,可能不适合您。它已经在装有 V100 的容器上进行了测试。这个构建让您可以从 ONNX 运行时访问 CPU、CUDA、TensorRT 执行提供程序。我们也在使用最新开发版本的`transformers`库,即`4.5.0.dev0`来访问 get 尼奥。
## 1.简单出口
*注:完整笔记本可用* [***此处***](https://github.com/oborchers/Medium_Repo/blob/master/Putting%20GPT-Neo%20into%20Production%20using%C2%A0ONNX/ONNX-Export.ipynb) *。* 我们要看的第一个模型是来自句子变形金刚[库](https://github.com/UKPLab/sentence-transformers)的`bert-base-nli-stsb-mean-tokens`模型。该型号也可在[轮毂](https://huggingface.co/sentence-transformers/bert-base-nli-stsb-mean-tokens)上获得。它本质上是一个 BERT 模型,被训练来产生良好的句子嵌入,在相似性搜索中也表现良好。为了转换模型,让我们使用`convert_graph_to_onnx`中的变形金刚库中已经可用的方法(参见[这里的](https://github.com/huggingface/transformers/blob/master/src/transformers/convert_graph_to_onnx.py))。用于导出的代码如下所示:
接下来,我们只需要加载模型,创建一个推理会话。此外,我们传递一些会话选项、首选的执行提供程序,并加载导出的模型:
看起来不错!模型正在加载,一切都很好。如果比较速度的话,基本的`nlp`流水线形式`transformers`运行在编码`span="Hello my friends!"`的 *10ms* 左右。这模拟了在线推理,这可能是最常见的用例。另一边,ONNX 型号运行在 *2.8ms* 。这比 V100 增加了**2.5 倍**,只需几行代码,无需进一步优化。请记住,对于批量编码,这些值可能会有很大的不同。
理论上,您现在可以将模型从 [ONNX 运行时工具](https://github.com/microsoft/onnxruntime/tree/master/onnxruntime/python/tools/transformers)放到前面提到的优化器中。但是**注意**:如果您使用`use_gpu=True`运行优化器,那么请确保您安装了 ONNX 运行时**而没有 tensort,**因为如果启用了 tensort 执行提供程序,导出[将不会工作](https://github.com/microsoft/onnxruntime/issues/6080)。
如果仔细观察,您会发现打印语句中生成的形状是错误的。返回的是两个形状为(1,6,768)和(1,768)的数组的列表。理论上,我们期望返回的形状为(1,768 ),因为我们使用的是句子编码器。
这种行为是由于句子转换器库需要在令牌嵌入之上的管道中添加一个额外的平均池层。说实话,如果我们想要一个统一的部署框架,并且不要在事后摆弄 numpy 或 torch,那么事后添加层并不是一个优雅的解决方案,这违背了我们的目的。
在我们检查自定义导出之前,让我们先来看看基准:
* SentenceTransformer CUDA:每个循环 12.3 毫秒 1.4 毫秒(平均池化)
* ONNX CUDA (V100):每循环 2.21 毫秒 77 秒
* ONNX tensort(V100, [ExecutionProvider](https://github.com/microsoft/onnxruntime-openenclave/blob/openenclave-public/docs/execution_providers/TensorRT-ExecutionProvider.md) ):每个循环 3.86 ms 181 s
坦率地说,我们在这里看到的结果很奇怪。我已经在这里开了一个问题[,因为我无法从 TensorRT 得到任何加速。](https://github.com/microsoft/onnxruntime/issues/7230)
## 2.定制出口
添加自定义层需要我们理解所使用的`convert`函数内部发生了什么。剧透:这很简单(对 pytorch 来说)。 [convert](https://github.com/huggingface/transformers/blob/335c0ca35c159f88d73198bdac928e61a4d480c7/src/transformers/convert_graph_to_onnx.py#L277) 函数调用两个方便的函数,即`transformers.pipeline`对象上的`infer_shapes`和`ensure_valid_input`。之后,所有推断出的形状加上产生的`torch.nn.Module`对象被传递给`torch.onnx.export`函数。文档提供了一个关于如何正确使用导出功能的很好的例子。对于理解导出函数来说,最重要的是以下参数:
1. input_names:底层 torch 模型的`forward`函数的参数。必须按正确的顺序。
2. 输出名称:输出图层的名称。
3. dynamic_axes:定义哪些是动态的,以及它们以何种方式是动态的(将来会更有意义)。
4. 参数:将通过模型的一组示例输入。
这些参数是从上面提到的便利函数中导出的。所以让我们快速地把它们包起来,这样我们就可以随心所欲地摆弄它们了:
将`print_transformers_shape_inference`应用于感兴趣的 BERT 模型,我们得到以下形状:
Inferred shapes for sentence-transformers/bert-base-nli-stsb-mean-tokens
Input names: ['input_ids', 'token_type_ids', 'attention_mask']
Output names: ['output_0', 'output_1']
Dynamic Axes:
{
"attention_mask": {
"0": "batch",
"1": "sequence"
},
"input_ids": {
"0": "batch",
"1": "sequence"
},
"output_0": {
"0": "batch",
"1": "sequence"
},
"output_1": {
"0": "batch"
},
"token_type_ids": {
"0": "batch",
"1": "sequence"
}
}
Tokens:{
'input_ids': tensor([[ 101, 2023, 2003, 1037, 7099, 6434, 102]]),
'token_type_ids': tensor([[0, 0, 0, 0, 0, 0, 0]]),
'attention_mask': tensor([[1, 1, 1, 1, 1, 1, 1]])
}
Ordered input names: ['input_ids', 'attention_mask', 'token_type_ids']
Arguments: (
tensor([[ 101, 2023, 2003, 1037, 7099, 6434, 102]]),
tensor([[1, 1, 1, 1, 1, 1, 1]]),
tensor([[0, 0, 0, 0, 0, 0, 0]])
)
通过一些解释,这完全说得通。`output_0`引用`pooler_output`,`output_1`引用返回的`BaseModelOutputWithPoolingAndCrossAttention`的`last_hidden_state`。`input_ids`、`token_type_ids`和`attention_mask`都是动态的,并且是记号赋予器函数的输出。
让我们继续建立一个简单的 torch 模型,它继承了 BERT 模型。我们唯一增加的是令牌嵌入的加权求和以及防止零除误差的箝位。
最后检查一下,模型产生的输出与原始模型大致相同,我们可以继续了。
在导出我们的新模型之前,唯一要做的事情是修改我们之前导出的`dynamic_axes`和`output_names`。这是因为我们现在有了一个不同的输出层,它也是动态的(根据批处理大小)。我们可以使用身份层的名称来更好地识别输出层。
太好了!现在,我们已经准备好了新的 ONNX 模型,可以用它进行推理了。输出形状现在是(1,768)预期的形状,几乎等于原始模型,绝对公差为 1e-6。此外,新模型运行在*2.4 毫秒*,所以我们没有失去任何速度,并获得了一个适当的端到端模型。
很明显,这个过程可以根据你的喜好进行定制。也可以在此基础上训练自己的分类器,并以同样的方式将其添加到编码器中。
我们已经创建了前两个 ONNX 模型。干得好!让我们做点不同的事情。
## 3.使用 ort 自定义操作进行导出
这一部分特别关注[通用句子编码器 5](https://tfhub.dev/google/universal-sentence-encoder-large/5) ,这是一个我长期以来一直在使用的模型,我非常喜欢它。它速度快,性能好,而且相对较小。谢天谢地,有了`tf2onnx`T10 库。`tf2onnx`是一个从 tensorflow 模型生成 ONNX 文件的导出工具。由于使用 tensorflow 总是令人愉快的,我们不能直接导出模型,因为模型定义中包含了标记器。不幸的是,核心 ONNX 平台还不支持这些字符串操作。
幸运的是,ONNXRuntime CustomOps [库](https://github.com/microsoft/ort-customops)来帮忙了。该库也由 ONNX 团队维护,并为扩展 ONNX 基本功能的额外自定义操作提供支持。您将需要安装 CMake > 3.17.0,以便使用`pip install git+https://github.com/microsoft/ort-customops.git`编译和安装该文件。
安装完 CustomOps 库后,我们[将](https://tfhub.dev/google/universal-sentence-encoder-large/5)USE 下载到某个文件夹,并为`tf2onnx`库提供一个输出路径。除此之外,我们可以直接导出模型:
`tf2onnx`库提供了一些其他的强大功能。例如,`--signature_def`参数允许您部分导出具有多个签名的模型,例如[使用 v3](https://tfhub.dev/google/universal-sentence-encoder-qa/3) 进行 QA。点击查看参数[。](https://github.com/onnx/tensorflow-onnx/blob/master/tf2onnx/convert.py)
由于底层图形和额外 Ops 的不同,现在运行使用的推理略有不同。我们必须将自定义 Ops 库路径传递给 ONNX SessionOptions 对象。
我们剧目中的另一个模特:-)
## 4.试图导出[GPT-尼奥](https://huggingface.co/EleutherAI/gpt-neo-1.3B)
现在我们开始真正有趣的东西。GPT-尼奥刚刚在变形金刚图书馆发布。它本质上是 OpenAI 的 GPT3 架构的开源变体。[型号](https://github.com/EleutherAI/gpt-neo)有两种架构:1.3B 和 2.7B,代表内部参数的数量。这些型号可通过[型号中枢](https://huggingface.co/EleutherAI)获得。注意:从今天起你需要`transformers-4.5.0.dev0`,因为 GPT-尼奥不包括在当前的 Pypi 包中。
我们从复制本教程第 2 步中的简单导出开始。这可能有点奇怪,可能不适合您的环境,因为这一步丢弃了所有输出,除了`logits`。但是我们可以看到一些真实世界的数字,在真实硬件上的推理速度。截至发布日期(2021 年 4 月 5 日),变形金刚库提供的完整形状推断似乎没有达到预期效果,因此我们需要稍微调整一下。我们只在它周围包装一个自定义层,返回逻辑。加载模型需要 3 分钟,因为我们必须使用外部数据格式来补偿大的模型大小。再次运行之前的相同推理:
* 变形金刚 CUDA:每循环 114 毫秒 20 毫秒
* ONNX CUDA (V100):每循环 314 毫秒 4.15 毫秒
* ONNX TensorRT (V100, [ExecutionProvider](https://github.com/microsoft/onnxruntime-openenclave/blob/openenclave-public/docs/execution_providers/TensorRT-ExecutionProvider.md) ):初始化时出现异常:/workspace/ONNX runtime/ONNX runtime/core/providers/tensor rt/tensor rt _ execution _ provider . cc:777 subgraph collection _ t ONNX runtime::TensorrtExecutionProvider::getsupported list(subgraph collection _ t,int,int,const ONNX runtime::graph viewer&,bool *)const[ONNXRuntimeError]:1:FAIL:tensor rt input:67
好吧,所以,这是相当不满意的。我不想让您失望,但是我想在我们能够正确地导出模型之前,模型中还有更多的优化工作要做。对我来说,不清楚是什么导致了这个问题。但是,如果我们查看日志,我们可以看到正在发生的事情:
> 在 Op 类型的注册表中找不到 CUDA 内核:Pad 节点名:Pad_4368
> 在 Op 类型的注册表中找不到 CUDA 内核:Pad 节点名:Pad_3801
> 在 Op 类型的注册表中找不到 CUDA 内核:LessOrEqual 节点名:LessOrEqual_7094
> 强制回退到节点的 CPU 执行:Gather_5
> 强制回退到节点的 CPU 执行:Unsqueeze_17
> 强制回退到节点的 CPU 执行:Slice_37
> 强制
这些信息成千上万。我们总共有 10609 条这样的消息:

这里的关键是:导出到 ONNX 是一件好事。如果你的模型不使用那么多目前不支持的操作,那么它们中的许多运行在一个 CPU 上。虽然总的来说这不容易避免,但是优化一个模型从它的第一行代码开始。从一开始就记住你想如何优化它。与失踪的 Ops 生命相关的问题[在这里](https://github.com/microsoft/onnxruntime/issues/7238)。
## 结论
在本文中,我们深入探讨了 ONNX 以及如何从 pytorch 和 tensorflow 中导出模型。您现在可以直接从 pytorch 定制和导出模型。您还可以将 tensorflow 模型从带有自定义 ops 的检查点导出到 ONNX。此外,您还学会了沿途寻找特殊案例。
## 附加说明
本帖笔记本 [***此处***](https://github.com/oborchers/Medium_Repo/blob/master/Putting%20GPT-Neo%20into%20Production%20using%C2%A0ONNX/ONNX-Export.ipynb) 。
## 文学
1. ONNX 主页:[https://onnx.ai/about.html](https://onnx.ai/about.html)
# 将您的模型投入生产
> 原文:<https://towardsdatascience.com/putting-your-models-into-production-5ae3191722b9?source=collection_archive---------38----------------------->
## 使用 TensorFlow 服务将深度学习模型投入生产的指南

来源( [Unsplash](https://unsplash.com/) )
你已经辛苦工作了无数个小时,试图让你的模型恰到好处。您已经努力清理了数据,精心设计了特性,并尽最大能力调整了超参数。一切终于就绪,现在您可以向全世界展示您的模型了。只有一个问题:您的模型被困在您的本地机器中,无法访问外部世界。
这就是大多数机器学习模型的命运。事实上,大约 87%的产品从未投入生产。大量的资源(更不用说宣传了)投入到实际的模型构建过程中,但实际上,这仅占用数据科学家大约 14%的时间。一个经常被忽视的步骤是将这些模型实际部署到生产中。本文将关注服务于的 [Tensorflow,这是一个用于将 Tensorflow 模型部署到生产中的系统。](https://github.com/tensorflow/serving)
**张量流发球**
使用 TensorFlow 服务的最简单方法是使用 Docker 图像。Docker 是一个开发、运输和运行应用程序的平台;使得用很少的设置快速传递内容成为可能。如果您在本地机器上安装了 Docker,您可以通过在终端上运行以下命令来下载映像:
docker pull tensorflow/serving
否则可以去 [Docker 网站](https://docs.docker.com/get-docker/)获取。
由于我们不会关注模型构建过程,因此我们将使用 Keras 创建一个简单的序列模型,并在 MNIST 数据集上进行训练。
带有一个隐藏层的简单模型。
**模特培训**
定义模型后,让我们使用`sparse_categorical_crossentropy`作为我们的损失、`adam`作为我们的优化器、`accuracy`作为我们的度量来编译它。
编译模型。
最后,让我们在模型上调用`fit`方法来拟合我们的数据,对它进行 5 个时期的训练,并传入测试图像作为验证数据。
使模型符合数据。

模型训练输出。
训练完模型后,我们将把它保存到机器上指定的导出路径中。
保存训练好的模型。
上面我们创建了一个`MODEL_DIR`变量和一个版本号来跟踪我们的模型版本。如果我们想要在将来更新我们模型的版本,我们可以在保存它的时候改变版本号。在这种情况下,我们的模型将保存在名为`mnist_model/1`的目录中。如果我们检查这个目录,我们会看到一个`saved_model.pb`文件和一个`variables`文件夹。
`saved_model.pb`文件是一个 protobuf 文件,存储实际的 Tensorflow 模型。`variables`目录包含一个[训练检查点](https://www.tensorflow.org/guide/checkpoint),用于重新加载保存的模型。
**运行我们的 Docker 容器**
之后,我们将在机器上运行 docker 命令来服务模型。
用 docker 服务模型。
这里发生了很多事情,所以让我们进一步分解这个命令。
* **docker** -运行 docker 的命令。
* **运行** -在新容器中运行命令。
* **-p 8501:8501** -在特定端口上运行 docker 命令(在我们的例子中,容器上的端口 8501 将被映射到主机上的端口 8501)。
* **- mount type=bind,source=…,target=…** - 这将模型从我们的本地机器挂载到容器中。我们指定了一个`source`和一个`target`,前者是模型在本地机器上的绝对路径,后者是我们希望模型放在容器中的位置。
* **-e MODEL _ NAME**-**这里我们设置一个环境变量,当我们执行 [REST](https://en.wikipedia.org/wiki/Representational_state_transfer) 请求时,我们将使用它来调用我们的模型。模型名称可以是您想要的任何名称。**
* ****-t tensor flow/serving**-**这是我们指定想要使用的图像的地方。这里我们使用之前下载的`tensorflow/serving`图片。****
****运行该命令后,请务必等待,直到您在终端上看到如下消息:****
2021-04-13 02:25:32.041325: I tensorflow_serving/model_servers/server.cc:391] Exporting HTTP/REST API at:localhost:8501 ...
******做出推理请求******
****然后,带有我们模型的容器将在我们机器的端口 8501 上运行。此时,我们已经准备好发送对新数据进行预测的请求。我们可以编写一个简单的 python 脚本来发送 POST 请求并进行预测。****
****发送进行新预测的请求。****
****我们创建一个 JSON 变量,包含测试数据中的前 10 幅图像。在此之后,我们使用`requests`库发送 post 请求并获得数据预测。然后,我们将模型预测与地面实况标签进行比较,并查看模型是否按预期执行。我们已经使用 TensorFlow 服务成功部署了我们的模型!****
******结论******
****我们讨论了使用 TensorFlow 服务的模型部署。通过使用 Docker 映像,我们可以轻松地在容器中部署我们的模型,而不是在本地安装 TensorFlow 服务。一旦我们训练了我们的模型,让我们的容器启动并运行,我们就使用 python 脚本对新数据的预测进行 post 请求。****
****感谢您的阅读!****
****您可以通过以下渠道与我联系:****
* ****[中等](https://zito-relova.medium.com/)****
* ****[LinkedIn](https://www.linkedin.com/in/zrelova/) 。****
* ****[Github](https://github.com/zitorelova)****
* ****[卡格尔](https://www.kaggle.com/zitorelova)****
# PyCaret + SKORCH:使用最少的代码构建 PyTorch 神经网络
> 原文:<https://towardsdatascience.com/pycaret-skorch-build-pytorch-neural-networks-using-minimal-code-57079e197f33?source=collection_archive---------9----------------------->
## 用 Pycaret 构建 PyTorch 神经网络的低代码指南

照片由 [Danist Soh](https://unsplash.com/@danist07?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 在 [Unsplash](https://unsplash.com/s/photos/build?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 上拍摄
几乎在每个机器学习项目中,我们都训练和评估多个机器学习模型。这通常涉及编写多行导入、许多函数调用、打印语句来训练单个模型并比较模型间的结果。当用交叉验证循环比较不同的模型或者组装模型时,代码变得一塌糊涂。随着时间的推移,当我们从分类模型转移到回归模型时,情况会变得更加混乱,反之亦然。我们最终将代码片段从一个地方复制到另一个地方,造成混乱!我们可以通过导入 [PyCaret](https://pycaret.org/) 轻松避免这种混乱!
PyCaret 是一个低代码的机器库,它允许你在给定一个回归或分类问题的情况下,通过一个统一的 API 来创建、训练和测试 ML 模型。PyCaret 还提供了机器学习项目中的各种步骤,从数据准备到模型部署,只需最少的代码。它可以与遵循 Scikit-Learn API 的任何模型/库一起工作,如 Scikit-Learn Models、Xgboost、LightGBM、CatBoost 等。总而言之,该库是一个易于使用的生产力助推器,可以实现快速实验,并帮助您更多地关注手头的业务问题。
现在,如果您想将神经网络添加到您的待试模型列表中,该怎么办?,您需要用 PyTorch 之类的框架编写用于训练和测试循环的代码片段,将 NumPy 数组转换为张量,或者反过来让现有的东西工作或编写一套全新的评估函数。一个小的类型错误最终会改变你已经写好的代码的一部分,这可能会产生更多你没有预料到的问题。您最终会花更多的时间更新代码,而不是尝试不同的模型和解决问题。
如果您可以将相同的 PyCaret 与神经网络一起使用,只需进行非常小的更改,会怎么样?
是的,你没听错!SKORCH 让这一切成为可能!SKORCH 是 PyTorch 的 Scikit-Learn 包装器,它使得用类似 sklearn 的 API 训练神经网络成为可能,这正是 PyCaret 所期望的!
许多参考资料都在解释如何使用 [Pycaret 用一行代码构建 ML 模型](https://pycaret.readthedocs.io/en/latest/tutorials.html)!以及使用 [SKORCH 构建神经网络](https://skorch.readthedocs.io/en/latest/user/tutorials.html)的教程和示例。建议在进入本博客的下一部分之前浏览这些教程。这个包含代码的[笔记本](https://github.com/pranaymodukuru/pycaret-with-skorch/blob/main/pycaret-skorch-example.ipynb)可以并行引用。
在这篇博客中,我们将看到
* 如何使用 SKORCH 建立神经网络
* 如何用 PyCaret 训练神经网络
* 如何用 PyCaret 调整神经网络
在这些步骤中,我们还将看到如何使用 PyCaret 的 oneliners 将神经网络的性能与其他模型进行比较。
在本教程中,我们将使用“electrical_grid”数据集。这是一个包含 12 个输入特征和一个目标变量的二元分类问题。该数据集在 PyCaret 自带的内置数据集中可用,可以通过导入和调用 get_data 函数来访问,如下所示
from pycaret.datasets import get_data
data = get_data('electrical_grid')
笔记本中的输出应该是这样的,

PyCaret 的电网数据(图片由作者提供)
# 如何用 SKORCH 构建神经网络
## PyTorchModel
斯科奇与 PyTorch 模型合作;我们可以创建一个简单的模型,就像使用纯 PyTorch 时一样。我们将构建一个如图所示的三层 MLP,第一层有 12 个输入,因为数据中有 12 个要素,两个类都有两个输出。
import torch.nn as nnclass Net(nn.Module):
def init(self, num_inputs=12, num_units_d1=200, num_units_d2=100):
super(Net, self).init() self.dense0 = nn.Linear(num_inputs, num_units_d1)
self.nonlin = nn.ReLU()
self.dropout = nn.Dropout(0.5)
self.dense1 = nn.Linear(num_units_d1, num_units_d2)
self.output = nn.Linear(num_units_d2, 2)
self.softmax = nn.Softmax(dim=-1) def forward(self, X, **kwargs):
X = self.nonlin(self.dense0(X))
X = self.dropout(X)
X = self.nonlin(self.dense1(X))
X = self.softmax(self.output(X))
return X
## 斯科奇分类器
现在我们已经定义了网络架构,我们必须用 SKORCH 实例化一个 sklearn 兼容的神经网络,这是通过从 skorch 导入 NeuralNetClassifier 来完成的。我们正在进行分类工作。
from skorch import NeuralNetClassifiernet = NeuralNetClassifier(
module=Net,
max_epochs=30,
lr=0.1,
batch_size=32,
train_split=None
)
需要传递的最重要的参数是采用 nn 名称的模块。模块定义的,在这种情况下,我们传递 Net 给它。类似地,我们可以传递其他参数 max_epochs 来指示训练模型的次数,以及“lr”优化器的学习速率 batch_size。设置小批量大小。我们将坚持使用 Skorch 中使用的默认 SGD 优化器,并且可以将其更改为任何自定义优化器,如这里的[所解释的](https://skorch.readthedocs.io/en/latest/user/neuralnet.html#optimizer)。
Skorch 默认使用 5 重交叉验证。因此,每次分割有 80%的训练样本和 20%的验证样本。这可以通过传递 train_split=None 来禁用,我们将这样做,因为我们将使用 PyCaret 来训练模型,py caret 已经使用交叉验证来训练模型。
# 如何用 PyCaret 训练神经网络
## 数据帧转换器
现在我们已经初始化了一个 SKORCH NN 模型,我们可以用 PyCaret 来训练这个模型。这里我们必须记住的一件事是 PyCaret 天生适合熊猫。DataFrames,但是在 Skorch 模型中,它无法将数据直接传递给模型。(更多关于它的内容[这里](https://github.com/pycaret/pycaret/issues/700#issuecomment-757488589))因此,我们需要用一个 sko rch . helper . data frame transformer 构造一个 sklearn 管道 nn_pipe,将 PyCaret 传递的输入转换成除模型之外所需的格式。感谢[孵化器 Shokuhou](https://github.com/pycaret/pycaret/issues/700#issuecomment-879700610) 发现了这一点。
from skorch.helper import DataFrameTransformernn_pipe = Pipeline(
[
("transform", DataFrameTransformer()),
("net", net),
]
)
## PyCaret 设置
现在我们终于准备好了我们的模型,可以训练它了,因为我们已经决定使用 PyCaret 来训练模型,而不是 Skorch API。让我们快速了解一下 PyCaret。在使用 PyCaret 训练任何模型之前,我们需要通过调用 setup 函数来启动一个实验,该函数执行所有的预处理步骤,当然,可以借助传递给函数的参数来控制这些步骤。
from pycaret.classification import *target = "stabf"
clf1 = setup(data = data,
target = target,
train_size = 0.8,
fold = 5,
session_id = 123,
log_experiment = True,
experiment_name = 'electrical_grid_1',
silent = True)
我们传递数据、目标列名、train_size、交叉验证中使用的折叠数作为设置的主要参数。我们将 log_experiment 设置为 True,以跟踪 MLFlow 的实验,并设置一个 experiment_name,以便我们可以在稍后阶段返回并引用结果。此外,我们将把 silent 参数设置为 True,以避免设置阶段的“按 enter 继续”步骤。
## PyCaret 训练 ML 模型
为了训练一个 ml 模型,我们可以用我们想要训练的模型的缩写来调用 create_model 函数。在这种情况下,我们可以使用 create_model("rf ")来训练一个随机的森林分类器。
model = create_model("rf")
现在,PyCaret 使用默认参数实例化 RandomForestClassifier,并在五个折叠上训练模型,并打印结果,如下所示。

作者图片
经过训练的 RandomForestClassifier 现在存储在变量模型中,我们将在后面使用它。
## PyCaret 火车斯科奇模型
现在我们已经知道 PyCaret 是如何工作的,让我们来训练我们已经实例化的 Skorch 模型。Pycaret 的好处是 create_model 接受任何 Sklearn API 兼容的对象。因此,我们可以将上面创建的 nn_pipe 传递给 create_model 函数,如下所示。

作者图片
同样,create_model 采用 skorch 模型,在五折上训练模型,并打印结果。并且被训练的 skorch 模型现在被保存在变量 skorch_model 中。
## 比较模型
现在我们可以通过将它们作为一个列表传递给 PyCaret 中的 compare_models 函数来比较这两个模型。“比较模型”功能现在将在交叉验证折叠上训练两个模型,并记录平均 cv 分数,以在所需指标上比较模型。我们可以通过向 sort 参数传递一个字符串来设置所需的度量;让我们在例子中使用“AUC”。
best_model = compare_models(include=[skorch_model, rf_model], sort="AUC"

作者图片
# 如何用 SKORCH 调整神经网络
## 设置超参数网格
现在我们知道了如何训练神经网络,让我们看看如何调整超参数。在这种情况下,我们将调整隐藏密集层中的神经元数量、学习速率、优化器和历元数量。为此,我们需要创建一个自定义的超参数网格,如下所示。
custom_grid = {
'net__max_epochs':[20, 30],
'net__lr': [0.01, 0.05, 0.1],
'net__module__num_units_d1': [50, 100, 150],
'net__module__num_units_d2': [50, 100, 150],
'net__optimizer': [optim.Adam, optim.SGD, optim.RMSprop]
}
请注意,模块名和参数由双“__”分隔,因为这是 NeuralNetClassifier 期望传递参数的方式,以便它可以实例化所需的模块。同样,这里的“net”表示我们从 NeuralnetClassifier 创建的 Net 变量,“module”表示我们为 Pytorch 模型创建的 Net 模块。
如果想知道模型的参数列表,可以通过执行 estimator.get_params()来查看。按键()。其中,估计量是变量 skorch_model,在本例中是 skorch_model.get_params()。keys()打印以下输出,

作者图片
请注意,参数 net__module__num_units_d1/2 没有出现在打印输出中,因为它们不是 skorch_model 的参数,而是 net 的参数;因此,需要用 net__module__**对它们进行设置。
## 调谐
在设置了超参数网格之后,我们必须从 pycaret 调用 tune_model 函数,并传入模型和 custom_grid 作为调优的参数。在这里,我们也可以从 PyCaret 支持的各种调优算法/库中进行选择,这些算法/库在[文档](https://pycaret.readthedocs.io/en/latest/api/classification.html#pycaret.classification.tune_model)中列出。
tuned_skorch_model = tune_model(skorch_model, custom_grid=custom_grid)
就是这样!现在将开始模型调整。要有耐心;根据网络大小、数据集大小等,这可能需要一些时间。调整完成后,您将再次看到交叉验证分数,如下所示。

作者图片
tune_model()函数返回已调优的模型,该模型现在在 tuned_skorch_model 变量中可用。
## 比较模型
同样,我们可以通过将这些模型作为列表传递给 PyCaret 中的 compare_models 函数来比较它们。
best_model = compare_models(include=[tuned_skorch_model, skorch_model, rf_model], sort="AUC")

作者图片
不幸的是,没有将模型名称作为参数传递的选项。因此,tuned_skorch_model 和 skorch_model 都被命名为 NeuralNetClassifier(由 PyCaret 自动识别)。我假设第二行是优化的模型,第三行是未优化的模型。
此外,这篇博客主要展示了我们如何使用 Skorch 和 Pycaret 来用最少的代码构建 PyTorch 模型。它并不特别关注于提高模型性能。
**注:**
1. 在这里,compare_models 函数在 create_model 或 tuned_model 之后使用,但是 pycaret 建议将 compare_models 用作比较所有可用模型的第一步。但这里的目的是说明如何使用 Skorch+Pytorch。因此,在这种情况下,处理方式不同。一般来说,为了在训练后比较不同的模型,我们可以编写一个自定义的包装器函数。我们可以传递一个模型列表和要评估的数据来获得比较分数。
# MLFlow 仪表板
因为我们已经在 PyCaret 设置中将 log_experiment 设置为 True,所以所有的实验都用 MLFlow 记录,而不需要我们做任何事情!瞧啊。要查看所有实验,请在工作目录中打开一个终端,并键入 mlflow ui。通过点击终端中显示的 URL,您应该可以在浏览器中看到这一点。

作者图片
上图是我们在笔记本上运行的所有实验。
# 结论
我们看到了 Skorch 和 PyCaret 如何用最少的代码训练神经网络,并利用相同的 API 训练传统的 ML 模型和神经网络。此外,我们可以用一行代码比较所有模型的性能,并用一个参数保存所有保存到 MLFlow 中的指标!我强烈推荐阅读官方的 [PyCaret](https://pycaret.org/) 和 [Skorch](https://github.com/skorch-dev/skorch) 文档,了解更多关于如何使用这些神奇的库并使 ML 实验更容易!
# 参考
1. [https://pycaret.org/](https://pycaret.org/)
2. [https://www . analyticsvidhya . com/blog/2020/05/py caret-machine-learning-model-seconds/](https://www.analyticsvidhya.com/blog/2020/05/pycaret-machine-learning-model-seconds/)
3. [https://github.com/skorch-dev/skorch](https://github.com/skorch-dev/skorch)
4. [https://towards data science . com/s korch-py torch-models-trained-with-a-scikit-learn-wrapper-62b 9a 154623 e](/skorch-pytorch-models-trained-with-a-scikit-learn-wrapper-62b9a154623e)
# PyCaret 时序模块架构概述
> 原文:<https://towardsdatascience.com/pycaret-time-series-module-architecture-overview-57336a2f39c7?source=collection_archive---------19----------------------->
## 在引擎盖下寻找

在 PyCaret 时间序列模块的引擎盖下看——照片由 [Alison Ivansek](https://unsplash.com/@aliivan?utm_source=medium&utm_medium=referral) 在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍摄
# 📚概观
理解任何软件包的底层架构都有助于确保我们尽可能地使用它。这并不意味着一个人必须了解其中的每一行代码,但有时,只是有一个概述会有所帮助。
本文旨在提供对`pycaret`时间序列模块的架构概述,并展示在评估使用`pycaret`开发的模型时,这些信息可能会派上用场的例子。
# 📖建议的先前阅读
如果你还没有这样做,我推荐下面的短文。它讲述了`pycaret`如何使用基于回归的预测模型(我们将在本文后面讨论)
👉[用于时间序列预测的简化回归模型](https://github.com/pycaret/pycaret/discussions/1760)
# 📗体系结构
`pycaret`时间序列模块建立在`sktime`之上,后者是时间序列分析的统一框架。`sktime`的目标是做时间序列分析,就像`sklearn`做机器学习一样。如果你愿意,你可以在这里阅读更多关于它的内容,但是这并不是本文所必需的,因为我将给出一个快速的概述。
`sktime`提供了一个框架来:
1. 使用简化回归技术创建带有`sklearn`回归变量的时间序列模型(参见建议的上一篇阅读)。
2. 用类似于`sklearn`提供的转换创建模型管道。
3. 使用适配器连接到其他时序包(如`[statsmodels](https://github.com/alan-turing-institute/sktime/blob/v0.8.1/sktime/forecasting/base/adapters/_statsmodels.py#L17)`、`[pmdarima](https://github.com/alan-turing-institute/sktime/blob/v0.8.1/sktime/forecasting/base/adapters/_pmdarima.py#L14)`、`[tbats](https://github.com/alan-turing-institute/sktime/blob/4e06cb0231cdabb74bf88d0cb4f2b721fc863fe3/sktime/forecasting/base/adapters/_tbats.py#L18)`、`[prophet](https://github.com/alan-turing-institute/sktime/blob/v0.8.1/sktime/forecasting/base/adapters/_fbprophet.py#L19)`等)。
4. 允许用户使用[扩展模板](https://github.com/alan-turing-institute/sktime/blob/v0.8.1/extension_templates/forecasting.py)创建他们自己的预测模型。

PyCaret 时序模块:架构概述[图片由作者提供]
虽然用户可以直接使用`sktime`库来创建模型,但是管理工作流和模型比较过程仍然需要手动处理(类似于在`sklearn`中直接构建模型)。令人欣慰的是,`pycaret`通过将这些模型、管道和适配器包装在如下所示的方便的框架中,提供了一种用几行代码实现这一点的方便方法。
Create different types of models ----
ARIMA model from pmdarima
arima_model = exp.create_model("arima")
ETS and Exponential Smoothing models from statsmodels
ets_model = exp.create_model("ets")
exp_smooth_model = exp.create_model("exp_smooth")
Reduced Regression model using sklearn
Linear Regression
lr_model = exp.create_model("lr_cds_dt")
因此,当你在`pycaret`中创建一个时间序列模型时,你会得到一个`sktime`适配器、管道或者一个你自己开发的`sktime`兼容模型。
Check model types ----
print(type(arima_model)) # sktime pmdarima
adapter
print(type(ets_model)) # sktime statsmodels
adapter
print(type(exp_smooth_model)) # sktime statsmodels
adapter
print(type(lr_model)) # Your custom sktime compatible model

[图片由作者提供]
但是,人们可以从这些模型中提取出比肉眼看到的更多的信息。例如,如果您使用`pycaret`创建的模型名为`model`,那么通过调用`model._forecaster`可以轻松提取底层包装库模型、`sktime`管道或您的自定义`sktime`兼容模型。

模型对象和可用方法[作者图片]
Access internal models using _forecaster
----
print(type(arima_model._forecaster))
print(type(ets_model._forecaster))
print(type(exp_smooth_model._forecaster))
print(type(lr_model._forecaster))

[图片由作者提供]
从这一点开始,您可以使用本地库方法/函数或者`sktime`包装器来提取关于您的模型的有价值的信息。
What hyperparameters were used to train the model? ----
print(arima_model)
Access statistical fit properties using underlying pmdarima
arima_model._forecaster.summary()
Alternately, use sktime's convenient wrapper to do so ----
arima_model.summary()

ARIMA 模型参数[图片由作者提供]

ARIMA 模型统计摘要[图片由作者提供]
例如,上面的图像向我们展示了 ARIMA 模型是按照截距的要求建立的。拟合得出截距值为 5.798。我们将在另一篇文章中讨论这些统计细节(见建议的下一篇文章),但现在,只要知道这些信息可以很容易地访问。
类似地,我们可以使用类似于`sklearn`中的方法提取关于管道的信息。
sktime pipelines are similar to sklearn.
Access steps using named_steps
attribute
print(lr_model._forecaster.named_steps.keys())

模型管道步骤[图片由作者提供]
因此,这个模型实际上是一个包含 3 个步骤的管道——一个条件去理性化器,接着是去渲染器,然后是实际预测器。您可以通过调用`named_steps`来获得关于这些步骤的更多细节。例如,我们可以看到预测器实际上是使用`sklearn` `LinearRegression`的基于回归的模型。这就是我们在构建`lr_cds_dt`模型时所要求的(`lr`代表线性回归,`cds`代表条件反季节器,`dt`代表 Detrender)。
Details about the steps ----
pprint(lr_model._forecaster.named_steps)

模型管道细节[图片由作者提供]
# 🚀结论
虽然`pycaret`提供了一个低代码环境来创建和管理建模工作流,但是如果我们深入观察,还有更多的事情可以做。这篇文章只是触及了可能性的表面。在以后的文章中,我们将看看如何使用`pycaret`来理解底层模型的工作,比如 ARIMA。在那之前,如果你愿意在我的社交渠道上与我联系(我经常发布关于时间序列分析的文章),你可以在下面找到我。暂时就这样了。预测快乐!
🔗[领英](https://www.linkedin.com/in/guptanick/)
🐦[推特](https://twitter.com/guptanick13)
📘 [GitHub](https://github.com/ngupta23)
*喜欢这篇文章吗?成为* [***中等会员***](https://ngupta13.medium.com/membership) *继续* ***无限制学习*** *。如果您使用下面的链接,* ***,我将收取您的一部分会员费,您无需支付额外费用*** *。*
<https://ngupta13.medium.com/membership>
# 📗资源
* <https://nbviewer.ipython.org/github/ngupta23/medium_articles/blob/main/time_series/pycaret/pycaret_ts_architecture.ipynb>**【Jupyter Notebook】(可在 Google Colab 中打开)包含本文代码**
# **📖建议下一次阅读**
**[使用 PyCaret 的时间序列模块了解 ARIMA 模型—第一部分](https://medium.com/p/692e10ca02f2)**
**[向 PyCaret 添加自定义时间序列模型](https://github.com/pycaret/pycaret/discussions/1867)**
# Pydash:缺少 Python 实用程序的厨房水槽
> 原文:<https://towardsdatascience.com/pydash-a-bucket-of-missing-python-utilities-5d10365be4fc?source=collection_archive---------9----------------------->
## 以函数的方式使用 Python
# 动机
你有没有试过像这样展平嵌套数组?
如果您发现很难展平这样的嵌套数组,您会很高兴找到一个像这样的优雅解决方案:

作者图片
…或者在一行代码中获得深层嵌套字典的对象——如下所示。

作者图片
如果您正在寻找一个提供有用的工具来处理上述 Python 对象的库,pydash 将是您的首选库。
# pydash 是什么?
Pydash 是 Python 实用程序库的厨房水槽,用于以函数方式做“事情”。
要安装 pydash,请确保您的 Python 版本≥ 3.6,然后键入:
pip install pydash
从导入 pydash 开始:
# 目录
## 展平 Python 列表
您可以使用`py_.flatten`方法展平嵌套的 Python 列表:

作者图片
如果你的列表像下面这样嵌套很深怎么办?
这时`py_.flatten_deep`方法就派上用场了:

作者图片
## 将元素分成组
如果你能展平一个列表,你能把一个展平的列表变成一个嵌套的列表吗?是的,这可以用`py_.chunk`方法来完成:

作者图片
不错!列表中的元素被分成两个一组。
# 词典
## 省略字典的属性
要从字典中省略一个属性,我们可以使用`py_.omit`方法:

作者图片
## 获取嵌套字典的属性
你如何从`Walmart`中得到`apple`的`price`,也就是像下面这样的嵌套字典中的`in season`?
通常,您需要使用许多括号来获取这些信息:
[2, 4]
如果可以用点符号代替括号不是很好吗?这可以通过`py_.get`方法完成:

作者图片
酷!您也可以使用`[index]`获得数组中的元素:
2
# 词典列表
## 使用函数查找项目索引
要使用函数获取列表中元素的索引,请使用`py_.find_index`方法:

作者图片
## 查找具有匹配样式的对象
`py_.find_index`方法允许您获取匹配特定模式的对象的索引。但是,如果您想获得列表中的项目而不是索引,该怎么办呢?
这可以通过`py_.filter`方法完成:

作者图片
## 获取嵌套对象值
有时你的字典列表可以像下面这样嵌套。怎么才能得到`apple`的`taste`属性?
幸运的是,这可以通过`py_.map_`方法轻松实现:

作者图片
# 执行一个函数 n 次
您可以使用`py_.times`方法执行一个函数 n 次。这个方法是 for 循环的一个很好的替代方法。

作者图片
# 链接
## 皮达什的方法
有时,您可能希望对一个对象应用几种方法。不用写几行代码,可以一次应用所有方法吗?
这就是方法链派上用场的时候。要在对象中应用方法链接,使用`py_.chain`方法:

作者图片
请注意,运行上面的代码不会给我们提供值:
<pydash.chaining.Chain at 0x7f8d256b0dc0>
只有当我们将`.value()`添加到链的末尾时,才会计算出最终值:

作者图片
这叫[懒评](https://www.tutorialspoint.com/functional_programming/functional_programming_lazy_evaluation.htm)。惰性求值保留对表达式的求值,直到需要它的值,这样可以避免重复求值。
## 定制方法
如果你想用你自己的方法而不是 pydash 的方法,使用`map`方法:

作者图片
## 树立价值观
要用另一个值替换链的初始值,使用`plant`方法:

作者图片
酷!我们用相同的链条将`['apple', 'orange', 'grapes']`替换为`['apple', 'orange']`!
# 结论
恭喜你!您刚刚学习了如何使用 pydash 有效地操作 Python 对象。我希望这篇文章能为您的 Python 工具包添加一些更有用的工具。
pydash 提供了很多很酷的方法,我无法在本文中一一介绍。我鼓励你去看看 [pydash 的 API 参考](https://pydash.readthedocs.io/en/latest/api.html?highlight=find_index#pydash.arrays.find_index)。
在 Github repo 中,您可以随意使用本文的代码:
<https://github.com/khuyentran1401/Data-science/blob/master/python/pydash.ipynb>
我喜欢写一些基本的数据科学概念,并尝试不同的算法和数据科学工具。你可以在 LinkedIn 和 Twitter 上与我联系。
如果你想查看我写的所有文章的代码,请点击这里。在 Medium 上关注我,了解我的最新数据科学文章,例如:
</3-python-tricks-to-read-create-and-run-multiple-files-automatically-5221ebaad2ba> </python-clean-code-6-best-practices-to-make-your-python-functions-more-readable-7ea4c6171d60> </3-advance-techniques-to-effortlessly-import-and-execute-your-python-modules-ccdcba017b0c> </3-tools-to-track-and-visualize-the-execution-of-your-python-code-666a153e435e> [## 3 个跟踪和可视化 Python 代码执行的工具
towardsdatascience.com](/3-tools-to-track-and-visualize-the-execution-of-your-python-code-666a153e435e)
# PyData Global 2021:Python、数据科学和 Dask 爱好者的五大亮点
> 原文:<https://towardsdatascience.com/pydata-global-2021-top-5-highlights-for-the-python-data-science-and-dask-lover-f47f254f78d0?source=collection_archive---------39----------------------->

布莱恩·戈夫通过 unsplash.com 拍摄的图像
我不知道你怎么样,但是我在上周的 [PyData Global 2021](https://pydata.org/global2021/) 上玩得很开心!我把这些课程流式传输到我客厅的投影仪上,然后[通过推特发布了一些精彩的内容。](https://twitter.com/richardpelgrim)
为了更长时间地品味其中的乐趣,我列出了我最喜欢的课程和最重要的收获…给那些没有时间机器,无法同时参加多个课程的人😉
# 666 行 Beas(t)ley 汇编代码
大卫·比兹利(David Beazley)以其天才和幽默的独特结合,围绕蟒蛇的历史以及融入其基因的价值观和策略,编织了一个迷人的故事。他分享了一些无价的轶事,关于用 666 行汇编代码意外编程一台昵称为“野兽”的超级计算机,以及当你的办公拖车被闪电击中时会发生的神奇事情。在这里观看整个主旨[。](https://zoom.us/rec/share/AUJTje3O5L4v0iHyH87T_clsOZUP6EP3CJ_k2UKUcLRLsjUOwzETYJM4S0S8CXTI.1kORCztQWQ4kITd2?startTime=1635436806000)
我的收获:
* 蟒蛇请求原谅,而不是允许。
* 蟒蛇倾向于在外围活动……只是为了在观众最不期待的时候冲上舞台,抢走表演。
* 一个工具制造者的社区总是会比任何单一的工具更强大。
如果你像我一样是一个数据历史爱好者,我强烈建议你去看看马修·洛克林关于 Dask 历史的[视频。更多关于高性能和分布式计算的现状和未来,请查看](https://www.youtube.com/watch?v=5nJVg8j11h0)[这个博客](https://coiled.io/blog/the-state-of-distributed-computing-anaconda-webinar/)来了解马特和王蒙杰在《分布式计算的现状》中坦诚的对话。
# 非法菠萝比萨
将一个技术性很强的话题转变成一个吸引人的演讲不是一件容易的事。我认为弗朗切斯科·蒂西奥在他的演讲《用朱拜特笔记本了解阿帕奇·卡夫卡》中表现出色。除了清楚地解释阿帕奇卡夫卡是如何工作以及如何应用它之外,我还了解了一些关于意大利比萨饼礼仪的迷人事实。
我的收获:
* 永远不要(我的意思是永远不要)在意大利点夏威夷披萨,即使是在晚上外出后的凌晨 3 点。它们是非法的。有点…
* 用 Jupyter 笔记本的阿帕奇卡夫卡,感觉很流畅,很熟悉。
* 花时间精心制作一个有清晰、可联系的故事情节(最好穿插一些幽默)和有帮助的、视觉上吸引人的图表的演示文稿,以令人难忘的方式传达你的信息,确实是值得的。我不会忘记这个的!
# 无聊是新的浮华
随着围绕大数据、人工智能和 ML 的所有闪光和炒作,有时很容易忘记一些更“无聊”的实际东西,如数据格式和压缩算法,实际上对数据科学的成功同样至关重要。虽然调整 ML 模型的超参数很重要,但花时间优化数据输入的质量和格式也可以带来巨大的性能收益。
我的收获:
* 古老的咒语“垃圾进来,垃圾出去”仍然听起来非常正确。
* [将您的 CSV 文件转换成拼花文件](https://zoom.us/rec/share/x1AV6NkGektH8BH_jnLdmMz7tw8M46OkNwEQBKTRVQENyKZKTOq6q8g711uLbq3y.xp-adyJSoMuGJMiE?startTime=1635442234000)可以显著提高性能
* [新的 blosc2 压缩器](https://zoom.us/rec/share/dbDxZE4uqgHP9ZsHqBRZkgkq6tiGlTNXAlVp4xm476P_fXeNoGMgxAMWFVZPnOUE.03qp79KDcYdFUC0K?startTime=1635420705000)在数据压缩方面给你更多的灵活性
# 这都是视觉上的
今年有很多关于(互动)可视化的会议。我尤其喜欢 Nicolas Kruchten 对[为什么可视化很重要](https://zoom.us/rec/share/LTwQxPDBQUhePIwhPK_pWi84ShH1-uMdOc2mURZBm1yxC_MZ2JzbpLE3hxiB--jY.Adlw3HkwGryYxVis?startTime=1635424240000)和 4 个不同层次的交互性的高度概括。为了对 Python 中相互竞争的可视化库进行诚实的现场比较,我建议花一个小时观看 [Python Dashboarding 枪战和对决](https://zoom.us/rec/share/ailBm48NBppFXaNHvU2EsnSGf3XhR8Q9mzmhUnwqkwDXS_bRlYMRgnQvudc7UeY_.pAZHOxu7L2Yl6Fr0?startTime=1635430012000)。
# Dask 一方在这一点上很强!
当然,如果不提到来自 Dask 维护者以及来自商界和学术界的其他演讲者的强大 Dask 在第一届 PyData Global 上的存在,这个列表就不完整。
一些亮点:
* 来自 Arrowstreet Capital 的 April Rathe 用[“Dask:从 POC 到生产”](https://zoom.us/rec/share/LTwQxPDBQUhePIwhPK_pWi84ShH1-uMdOc2mURZBm1yxC_MZ2JzbpLE3hxiB--jY.Adlw3HkwGryYxVis?startTime=1635427879000)展示了她公司的旅程,令人印象深刻。我们喜欢她让整个团队改用 Dask 的方式🙂
* Coiled Lead OSS 工程师 James Bourbeau 在[“雪花& Dask:如何使用分布式读取功能扩展工作负载”](https://zoom.us/rec/share/bZPPtqNq4c8DMTDptHNnMamVn09gIkgTTPvVmdPOq62yc2i7e4cJwZ0byeC471Ft.qyq5H4W3mdHK1Spg?startTime=1635609671000)上展示了新的 Dask-雪花连接器。
* Brendan Collins 关于[“使用 Dask 和 Numba 进行空间分析”](https://zoom.us/rec/share/pdWvOlS5FcUZUUKk46r-1HUnAUmXSNhR_j9E7k8hpRfY-uw64ErGzgn4NWAAqX7I.HZiPDJEtBW41DCnw?startTime=1635447690000)的演讲深入到新的 Xarray-Spatial library 中,以进行基于栅格的地理空间分析。
* coiled Account Executive Gus Cavanaugh 提供了关于[“使用 Numba、Dask 和 Rapids 进行高性能计算”](https://zoom.us/rec/share/HZCezj59UGw88oLmpovnvXIqD2UFBI4louzeNE9VKDZvms66XwqoUZDBncVnPXB-.DyzbErAo6tJc7-CF?startTime=1635605232000)的初级教程。
* 查看由不少于 4 个核心 Dask 维护者举办的“大规模数据处理”研讨会,以获得对 Dask 的全面介绍和关于数据库未来的生动问答&。
# PyData Global 2021 的最大亮点是什么?
[发推特告诉我你对今年 PyData Global 的想法](https://twitter.com/richardpelgrim)——或者告诉我你是否认为这个亮点列表遗漏了什么!
*原载于 2021 年 11 月 1 日*[*https://coiled . io*](https://coiled.io/blog/pydata-global-2021-top-5-highlights-for-the-python-data-science-and-dask-lover/)*。*
# PyOD:用于异常检测的统一 Python 库
> 原文:<https://towardsdatascience.com/pyod-a-unified-python-library-for-anomaly-detection-3608ec1fe321?source=collection_archive---------8----------------------->
## 用于异常检测机器学习任务的 scikit-learn

照片由[安妮塔·里泰诺尔](https://www.flickr.com/photos/puliarfanita/)在 [flickr](https://www.flickr.com/photos/puliarfanita/24860619467) 拍摄
`[PyOD](https://pyod.readthedocs.io/en/latest/index.html)`是一个 Python 库,具有一套全面的可扩展、最先进的(SOTA)算法,用于检测多元数据中的异常数据点。这项任务通常被称为[异常检测](https://en.wikipedia.org/wiki/Anomaly_detection)或[异常检测](https://en.wikipedia.org/wiki/Anomaly_detection)。
> *异常值检测任务旨在识别偏离给定数据的“正常”或一般分布的罕见项目、事件或观察值。*
我最喜欢的定义:**异常是指让人怀疑它是由不同的数据生成机制生成的**
离群点检测的常见应用包括欺诈检测、数据错误检测、网络安全中的入侵检测以及机械中的故障检测。
# 为什么要使用特定的异常检测算法?
实际上,异常检测最好是作为一种无监督或半监督的任务,在这种情况下,您试图识别数据中的异常观察。
**离群值(异常数据)与内标值(正常数据)的标签通常不可用且难以获得。**即使标签确实存在,它们通常也是不够的。一种异常可能只有少数几种标记。其他类型的异常可能以前从未发生过,因此您无法训练监督算法来发现它们。
**“离群值”的定义可能是主观的。**什么被视为“异常”取决于应用,但它通常意味着数据错误或欺诈或犯罪活动。
专用的异常值检测算法提供了一种可靠地对大量未标记数据执行模式识别的方法。
<https://pub.towardsai.net/why-outlier-detection-is-hard-94386578be6c>
# **关键**特性
PyOD 拥有一套 30 多种检测算法,从隔离森林等经典算法到最新的深度学习方法,再到 [COPOD](/fast-accurate-anomaly-detection-based-on-copulas-copod-3133ce9041fa) ( [论文](https://arxiv.org/abs/2009.09463))等新兴算法。PyOD 算法已经得到很好的确立,在文献中被大量引用,并且非常有用。
PyOD 为所有算法、技术文档和示例提供了统一的 API,易于使用。
## 统一的 API
所有探测器都用`contamination`参数初始化。`contamination`是数据中异常值的预期比例,用于在模型拟合期间设置异常值阈值。默认情况下,`contamination=0.1`和必须在 0 和 0.5 之间。
探测器具有以下功能:
* `fit(data)`:安装探测器。使用训练数据计算任何必要的统计数据。与`scikit-learn`中的监督模型不同,`PyOD`中的拟合方法不需要目标标签的`y`参数。
* `decision_function(data)`:使用合适的检测器计算新数据的原始异常值。*(注意:一些探测器在没有预先安装的情况下仍然工作良好)*
* `predict(data)`:返回每个输入样本对应的二进制标签(离群/正常)。在底层,该函数将拟合步骤中生成的阈值应用于`decision_function`返回的异常分数。
* `predict_proba(data)`:使用标准化或统一化返回作为概率缩放的异常分数。
一旦安装,探测器包含属性`decision_scores_`、`labels_`和`threshold_`。
* `decision_scores_` —训练数据的异常值分数。分数越高,表明数据越不规则
* `threshold_` —根据拟合时初始化的`contamination`确定阈值。它根据`decision_scores_`中的`n_samples * contamination`最高分数设定。
* `labels_` —训练数据的二进制异常标签。0 表示观察值是内部值,1 表示外部值。它是通过将`threshold_`应用于`decision_scores_`而生成的。
## API 演示
这里有一个 API 的快速说明。
首先,加载或生成异常值检测数据。
这里,我使用 PyOD 的`generate_data`函数生成一个包含 200 个训练样本和 100 个测试样本的合成数据集。*正态样本由多元高斯分布生成;异常样本是使用均匀分布生成的。*
训练和测试数据集都有 5 个特征,10%的行被标记为异常。我给数据添加了一点随机噪声,使其更难完美区分正常点和异常点。
接下来,我对训练数据拟合 ABOD(基于角度的离群点检测器),并对测试数据进行评估。
对相同的数据测试不同的 PyOD 算法很容易。在下一段代码中,我将展示如何训练和评估 COPOD。
在合成数据集上,COPOD 比 ABOD 有更好的 ROC-AUC 精度分数。
## 关于异常检测器评估的两点注意事项
1. 如果没有任何基本事实标签(内层与外层),就无法测量检测机的性能。
2. [ROC-AUC](https://developers.google.com/machine-learning/crash-course/classification/roc-and-auc) 得分对于阈值选择是稳健的——如果 AUC 较高,则您的模型性能对您选择的阈值不太敏感。
# 离群点检测算法的类型
有几种异常值检测算法。这里介绍的分类法反映了 PyOD 分类法。
## 线性模型
异常值检测的线性模型使用线性模型,如 [PCA](https://en.wikipedia.org/wiki/Principal_component_analysis) 和[单类支持向量机](http://rvlasveld.github.io/blog/2013/07/12/introduction-to-one-class-support-vector-machines/)来分离正常和异常值观察值。
## 基于邻近的
用于离群点检测的基于邻近度的模型,例如 K- [最近邻居](https://scikit-learn.org/stable/modules/neighbors.html)和[局部离群因子](https://scikit-learn.org/stable/auto_examples/neighbors/plot_lof_outlier_detection.html?highlight=local%20outlier%20factor),使用数据点之间的距离来度量相似性。彼此高度接近的观察值更可能是正常的。距离较远的观测值更有可能是异常值
## 盖然论的
异常值检测的概率模型依赖于统计分布来发现异常值。概率检测器包括[中位数绝对偏差](https://en.wikipedia.org/wiki/Median_absolute_deviation)(MAD)[基于 Copula 的离群点检测](/fast-accurate-anomaly-detection-based-on-copulas-copod-3133ce9041fa) (COPOD),以及[基于角度的离群点检测](https://www.dbs.ifi.lmu.de/Publikationen/Papers/KDD2008.pdf) (ABOD)。
## 离群系综
离群值集成依赖于模型集成来隔离离群点。算法包括[隔离森林](https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.IsolationForest.html)(也有`scikit-learn`)和 [LODA](https://link.springer.com/content/pdf/10.1007%2Fs10994-015-5521-0.pdf) (轻型异常在线检测器)。
## 神经网络
神经网络也可以被训练来识别异常。
[自动编码器](https://en.wikipedia.org/wiki/Autoencoder)(以及变化的自动编码器)网络架构可以被训练来识别异常,而无需标记实例。自动编码器学习压缩和重建数据中的信息。重建误差然后被用作异常分数。
最近,已经提出了几种用于异常检测的 GAN 架构(例如 [MO_GAAL](https://pyod.readthedocs.io/en/latest/pyod.models.html#module-pyod.models.mo_gaal) )。
# 哪种算法最好?
没有一种检测算法能统治所有人。不同的算法会做得更好,这取决于任务。
## 算法基准
PyOD 针对来自 [ODDS](http://odds.cs.stonybrook.edu/#table1) 的 17 个异常检测基准数据集评估了其算法子集(10 种方法)。
我从最新基准测试结果中得出的结论在 PyOD [文档](https://pyod.readthedocs.io/en/latest/benchmark.html)中给出:
* **所有算法都快!**即使是最大的数据集,运行时间也不超过 1 分钟(最长的数据集有 49k 行;最宽的数据集有 274 列)。
* **在一个数据集上表现很好的算法可能在另一个数据集上表现很差**。例如,对于`vowels`数据集,ABOD 是第二好的检测器,但是对于`musk`数据集,它是最差的检测器。
## 定制探测器套装
构建更健壮的离群点检测模型(并且避免选择单个模型)的一种方法是将模型组合成定制的集成。这可以通过组合多个异常检测器的异常分数以及使用汇总分数对数据进行评分来完成。
PyOD 为`[pyod.models.combination](https://pyod.readthedocs.io/en/latest/pyod.models.html#module-pyod.models.combination)`模块中的模型组合提供了几个实用功能:
* `[average](https://pyod.readthedocs.io/en/latest/pyod.models.html#pyod.models.combination.average)` —对集合中检测器的得分进行平均
* `[maximization](https://pyod.readthedocs.io/en/latest/pyod.models.html#pyod.models.combination.maximization)` —集合中探测器的最高分
* `[aom](https://pyod.readthedocs.io/en/latest/pyod.models.html#pyod.models.combination.aom)`(最大值的平均值)—将基础检测器划分为子组,取每个子组的最大值,并对子组得分取平均值。
* `[moa](https://pyod.readthedocs.io/en/latest/pyod.models.html#pyod.models.combination.moa)`(平均值的最大值)—将基本检测器分成子组,取每个子组的平均分数,并返回最大子组分数。
* `[majority_vote](https://pyod.readthedocs.io/en/latest/pyod.models.html#pyod.models.combination.majority_vote)` —通过多数投票合并分数
* `[median](https://pyod.readthedocs.io/en/latest/pyod.models.html#pyod.models.combination.median)` —来自集合中检测器的中值分数
请注意,异常分数在合并之前必须标准化,因为检测器不会返回相同级别的异常分数。
点击[此处](https://pyod.readthedocs.io/en/latest/example.html)查看探测器组合指南。
# 想了解更多?
如果您有兴趣了解更多关于异常检测的信息,请查看 PyOD Github 知识库的[异常检测资源](https://github.com/yzhao062/anomaly-detection-resources)页面。这个页面非常全面,并且随着新研究的发布而不断更新。
如果你喜欢这篇文章,你可能也会喜欢这些类似的帖子。
</fast-accurate-anomaly-detection-based-on-copulas-copod-3133ce9041fa> [## 基于 COPOD 的快速准确异常检测
towardsdatascience.com](/fast-accurate-anomaly-detection-based-on-copulas-copod-3133ce9041fa) <https://pub.towardsai.net/an-in-depth-guide-to-local-outlier-factor-lof-for-outlier-detection-in-python-5a6f128e5871> <https://medium.com/geekculture/replace-outlier-detection-by-simple-statistics-with-ecod-f95a7d982f79> </sktime-a-unified-python-library-for-time-series-machine-learning-3c103c139a55>
## 不是中等会员?今天就加入!
# 参考
Zhao, Y., Nasrullah, Z. and Li, Z., 2019. PyOD: A Python Toolbox for Scalable Outlier Detection. Journal of machine learning research (JMLR), 20(96), pp.1-7.
【github.com】yzhao 062/pyod:(JMLR ' 19)一个用于可扩展异常值检测(异常检测)的 Python 工具箱
[欢迎使用 PyOD 文档!— pyod 0.8.9 文档](https://pyod.readthedocs.io/en/latest/index.html)
# PyPy 比 Python 快,但代价是什么?
> 原文:<https://towardsdatascience.com/pypy-is-faster-than-python-but-at-what-cost-12739bf2b8e9?source=collection_archive---------12----------------------->
## 学习机
## 提示:绝对不适合数据科学项目

美国宇航局在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 拍摄的照片
</run-your-python-code-as-fast-as-c-4ae49935a826>
在过去的几天里,上面链接的故事在“媒体趋势”排行榜上排名第一。当我第一次偶然发现它时,它只有不到 100 次鼓掌。现在它有成千上万个这样的问题,我觉得有必要把事情搞清楚。
这个故事的核心思想是使用 PyPy 而不是普通的 Python。这将用 PyPy 替换默认的 CPython 解释器,PyPy 据说比 Python 快得多,而且“没有任何改变”
然后作者给出了一个用 Python 和 PyPy 测量在一个循环内将 0 到 100,000,000 之间的整数相加所需时间的例子。这里,Python 需要 9.28 秒,而 PyPy 需要 0.22 秒。
但是作者忽略了一个重要的东西。
> 它不支持一些机器学习库。
考虑到 TDS 出版了它,这是一件奇怪的事情。
一些读者可能会兴奋地认为,如果 9.28 秒和 0.22 秒的比较成立,他们可以将机器学习模型的训练时间缩短 40 倍以上。
在现实中,这是他们大多数人都会经历的。

作者对文章的评论截图。
原因是大多数机器学习库使用 PyPy 不支持的 C API。
有些人请求对流行的 ML 库提供 PyPy 支持,但是他们似乎没有任何进展。下面是 2019 年开放的关于 PyTorch 的 PyPy 支持的 GitHub 问题。
<https://github.com/pytorch/pytorch/issues/17835>
这是 GitHub 的另一个问题,关于 2015 年 TensorFlow 的同一问题。
<https://github.com/tensorflow/tensorflow/issues/252>
Scikit-learn 在他们的 FAQ 中声明[他们对 PyPy 有“实验支持”,但当前最先进的机器学习模型很可能会使用 PyTorch、TensorFlow 甚至 Transformers 包,而不是 scikit-learn。](https://scikit-learn.org/stable/faq.html#do-you-support-pypy)
也就是说,我认为在您的数据科学项目中使用 PyPy 是不可行的,甚至是不可能的。
您必须检查您的项目使用的所有包是否都受 PyPy 支持。关于 Python 包与 PyPy 兼容性的完整列表,您可以访问下面的页面。
# 结论
目前,还不能使用 PyPy 作为 Python 数据科学项目的解释器。至少现在是这样。
很多机器学习库,如 Torch 和 TensorFlow,都依赖于与 C 库的兼容性,而 PyPy 在撰写本文时还不支持 C 库。
对我来说,我使用 Python 编写小脚本来实现自动化、抓取网站和机器学习。在所有用例中,我看不出 PyPy 在较小的任务中有什么不同,缺乏对机器学习库的支持绝对是一个障碍。
其他用例实际上可能受益于 PyPy,但是您需要在替换常规 Python 之前评估您的代码。使用 PyPy 意味着限制您的代码使用一组受支持的包,而不是 Python 可用的完整包。
回顾过去,PyPy 在几年前不支持熊猫和 NumPy,但是现在它支持它们。因此,将来有可能会有更多的库支持 PyPy 或被 PyPy 支持,但肯定不是在不久的将来。
感谢阅读!这里有一些我的其他故事,你可能会喜欢。
<https://medium.com/swlh/the-one-tool-that-every-machine-learning-engineer-should-master-68aecd920b0> </maybe-its-time-to-upgrade-your-terminal-experience-a9e12b2af77> </5-things-i-learned-from-my-first-time-working-with-big-data-a6193deb982f>
《学习机》是一系列关于我学到的东西的故事,我认为这些故事很有趣,值得分享。有时也是关于机器学习的基础。 [*获得定期更新*](https://chandraseta.medium.com/subscribe) *上新故事和* [*成为中等会员*](https://chandraseta.medium.com/membership) *阅读无限故事。*
<https://chandraseta.medium.com/membership>
# PyQt 和关系数据库:数据格式 2
> 原文:<https://towardsdatascience.com/pyqt-relational-databases-data-format-2-2203505f778?source=collection_archive---------21----------------------->
## 数据
## 用 PyQt 轻松呈现关系数据库数据。不需要 SQL 查询。

作者和 Unsplash 提供的图像
今天,我们将深入探讨数据格式化。
我们将讨论视图使用的条目数据角色,以向模型表明它需要哪种类型的数据。
再举几个数据格式的例子,并在最后给你一点奖励,这将有助于你理解表格数据格式。
## 本文是 PyQt &关系数据库系列的一部分:

斯特凡·梅洛
## PyQt &关系数据库系列
[View list](https://melo-stefan.medium.com/list/pyqt-relational-databases-series-9ded67b27631?source=post_page-----2203505f778--------------------------------)6 stories
## 表格演示
到目前为止我们学到了什么?很多。
然而,它可能看起来有点像,因为我们编写了相对较少的代码行。它适合于开发人员,因为它表明生产力的快速增长。
现在,你知道如何通过避免 SQL 查询知识来使你的生活变得更容易。没有编译任何 SQL 查询,您可以用关系数据库(在本例中是 SQLite 数据库)中的数据填充您的表。
并且您理解了*模型-视图-控制器*设计模式,并且您能够通过 PyQt 有效地使用它。
这些技能都是通过`QAbstractTableModel`进行模型定制支持的。
> 控制数据表示模型提供了使数据可读的无限可能性。
例如,我们已经指出了货币和精度,这是定价数据的两个基本属性。
## 项目数据角色
> 演示模型还能为我们做什么?
强烈推荐玩玩。你会学到很多,你会发现可能性是无穷无尽的。
最好的方法是从角色开始。角色代表一个`Qt.ItemDataRole`枚举。
模型中的每一项都有一组与之相关联的数据元素,每一项都有自己的角色。自定义模型应该返回这些类型的数据。
可以把它们想象成物品特征,基于这些特征,系统理解我们想要对物品做什么。
根据角色效果,有通用角色、描述外观和元数据的角色、可访问性角色和可用的用户角色。
今天我们将使用`DisplayRole`和`DecorationRole`。然而,当我们需要他们的时候,我们会再回来的。
## 更改“ID”和“日期”格式
让我们多学一点。
这个`DisplayRole`很有用。
在我们的示例中,我们将格式化 ID 号列并更改日期格式。我们开始吧!
很直白。我们将在子类`QAbstractTableModel`的`data()`方法中添加几行代码。
def data(self, index, role):
value = self._model.record(index.row()).value(index.column())
if role == Qt.ItemDataRole.DisplayRole:
if isinstance(value, int) and index.column() == 0:
return f'# {value}' if isinstance(value, int) and index.column() == 1:
return "${: ,.2f}".format(value)
if isinstance(value, str) and index.column() == 4:
date_object = date.fromisoformat(value)
return date_object.strftime('%x') return value
我们在上面添加了几行粗体字。在第一列中,我们添加了“#”符号。
对于日期列,我们需要一些导入:
from datetime import date
from time import strftime
因为我们的`value`只是一个纯文本(ISO 格式),所以我们创建了`date_object`。
我们需要它来方便格式转换。下面一行返回转换为本地格式的日期中的纯文本。
只添加了 7 行代码,表就变了,结果就在这里。

作者图片
## 装饰
正如承诺的,这是最后的一点奖金。
它展示了一种`DecorationRole`的力量。
有时候,用一些装饰让桌子看起来更有吸引力是很有用的。它显示了两种不同的情况。
如果您查看第一列,您会注意到我们在一个整数订单号前面添加了一个标签“#”符号。
> 在将数据写入模型的情况下,我们会将项目转换为字符串,这在将数据写入数据库时可能会出现问题。
为了避免这种不必要的转换,最好使用装饰。我们必须删除`DisplayRole`部分中第 0 列的整个`if`语句。
def data(self, index, role):
value = self._model.record(index.row()).value(index.column())
if role == Qt.ItemDataRole.DisplayRole: if isinstance(value, int) and index.column() == 1:
return "${: ,.2f}".format(value)
if isinstance(value, str) and index.column() == 4:
date_object = date.fromisoformat(value)
return date_object.strftime('%x') return value if role == Qt.ItemDataRole.DecorationRole:
if isinstance(value, int) and index.column() == 0:
return QtGui.QIcon('data/icons/hashtag_icon.png')
if isinstance(value, str) and index.column() == 4:
return QtGui.QIcon('data/icons/calendar.png')
然后我们只在`DecorationRole`部分实现两个`if`语句。
第一个用于第 0 列,添加 hashtag 符号装饰,另一个用于第 4 列,添加日历图标装饰。

作者图片
## 关系数据库数据表示
关注数据呈现总是很重要的。
PyQt 使得保持数据库源不变变变得容易,并且仍然提供了很多机会来使数据呈现对用户具有吸引力。
***如果你想阅读更多这样的内容,成为会员:***
<https://melo-stefan.medium.com/membership>
***或者你可以订阅我下面的简讯。谢谢你。***
# PyQt 和关系数据库—数据格式
> 原文:<https://towardsdatascience.com/pyqt-relational-databases-data-format-36907b427b8?source=collection_archive---------11----------------------->
## 数据
## PyQt 为关系数据库数据表示提供了一种方便的方式。

按作者排列图像,按像素排列背景
## 模型-视图设计模式
`QTableView`小部件是向用户呈现数据的一种方便灵活的方式。
此外,它可以有效地处理关系数据库,并且不用强调它完全符合模型-视图-控制器设计模式。
我们已经知道如何连接到数据库并从关系表中显示数据。
最有价值的好处是我们不需要 SQL 语言专业知识。众所周知,掌握 SQL 查询是一项长期任务。
## 为什么数据格式很重要
现在,让我们使我们的数据对人眼更有吸引力。
数据格式在数据科学中起着重要的作用。人们必须知道他们面对的是什么数据。没有正确的格式,数据就不能得到有效的利用。
## 本文是 PyQt &关系数据库系列的一部分:

斯特凡·梅洛
## PyQt &关系数据库系列
[View list](https://melo-stefan.medium.com/list/pyqt-relational-databases-series-9ded67b27631?source=post_page-----36907b427b8--------------------------------)6 stories
## PyQt 中的 MVC
*模型-视图-控制器* (MVC)是一种*软件设计模式*,通常用于开发用户界面,以分离内部信息表示(*模型*)和它们如何呈现(*视图*)之间的逻辑。
*控制器*接受输入并将其转化为模型或视图的指令。
PyQt 将视图和控制器合并成一个元素。当视图和控制器元素结合成一个时,结果就是*模型-视图*设计架构。
PyQt 有不同的模型类和各自的视图类。下表显示了匹配的模型基础。

## 自己展示你的数据
为了更好地控制数据的显示方式,我们将定制模型。
在模型-视图架构中,要编写一个*定制模型*,我们必须子类化一个适当的抽象模型。
对于我们的例子来说,`QAbstractTableModel`正是我们所需要的。然而,`QAbstractTableModel`是一个*抽象基类*,对于其他编程语言来说,它被称为*接口*。直接用的话就不行了。定制表格需要实现的最小方法是`data`和`rowCount`。
`data`方法返回表格数据。而`rowCount`方法必须返回数据源的维度。
## 修改前面的代码
为了从我们的`data`方法返回一些数据,我们需要修改我们已经拥有的代码。数据将来自我们的关系数据模型。否则,我们将失去关系表的功能。
因此,我们没有使用`view.setModel(model)`,而是使用了一个新的模型`view.setModel(presentation_model)`,它将从我们现有的关系表`model`中获取数据。修改后的代码如下所示:
class MainWindow(QMainWindow):
def init(self, parent = None):
super().init(parent) self.setWindowTitle('QTableView Example') # Setup the model
model = QSqlRelationalTableModel(self)
model.setTable('orders')
model.setRelation(2, QSqlRelation('customers', 'CustomerID', 'Customer'))
model.setRelation(3, QSqlRelation('products', 'ProductID', 'Product'))
model.setRelation(1, QSqlRelation('products', 'ProductID', 'Price'))
model.select() # Create the presentation model, which gets data from relational table model
presentation_model = MyTableModel(model) # Setup the view
view = QTableView(self)
view.setModel(presentation_model)
view.resizeColumnsToContents()
self.setCentralWidget(view)
## 展示模型
但是这个神秘的`MyTableModel`班是什么?它从哪里来?这是我们新的`QAbstractTableModel`子类。让我们创造它。
class MyTableModel(QAbstractTableModel):
def init(self, model):
super().init(model) self._model = model # Create the data method
def data(self, index, role):
if role == Qt.ItemDataRole.DisplayRole:
value = self._model.record(index.row()).value(index.column())
if isinstance(value, int) and index.column() == 1:
# Format the currency value
return "${: ,.2f}".format(value)
return value # Create the headerData method
def headerData(self, **section**: int, **orientation**: Qt.Orientation, **role**: int):
if role == Qt.ItemDataRole.DisplayRole and orientation == Qt.Orientation.Horizontal:
return self._model.headerData(section, orientation, role=role) # Create the rowCount method
def rowCount(self, parent: QModelIndex) -> int:
return self._model.rowCount() # Create the columnCount method
def columnCount(self, parent: QModelIndex) -> int:
return self._model.columnCount()
让我们来评论一些重要的细节:
* 一定不要忘记导入几个模块:
`from PyQt6.QtCore import QAbstractTableModel, QModelIndex, Qt`
* `__init__`构造函数只接受一个参数`model`。
我们将它存储在`self._model`中,以便在我们的方法中访问它。
* `data`方法首先寻找显示`role`。当找到它时,我们得到`value`并检查需要格式化的列。最后,该方法为演示返回一个格式化的`value`。
* `headerData`方法寻找`role`和`orientation`。当方向为`horizontal`时,返回可用的表头。因为我们没有定义行标题,所以行标题是隐藏的。
* 尽管我们已经创建了`rowCount`和`columnCount`方法,但它们目前什么也做不了。但是,我们稍后会用到它们。
## 结论
只要对代码稍加更新,我们就可以用我们喜欢的任何方式格式化数据。结果如下:

接下来,我们将看看其他一些格式,并开始修改数据。
***如果你想阅读更多这样的内容,成为会员:***
<https://melo-stefan.medium.com/membership>
***或者你可以订阅我下面的简讯。谢谢你。***
# PyQt &关系数据库
> 原文:<https://towardsdatascience.com/pyqt-relational-databases-e6bdbed7ebdf?source=collection_archive---------13----------------------->
## 易于使用的全功能部件,用于处理关系数据库数据。

照片由[西格蒙德](https://unsplash.com/@sigmund?utm_source=medium&utm_medium=referral)在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍摄
Python 是一种简单易学、功能强大的编程语言。
您可以获得复杂的输出,并且与其他语言相比,您需要编写的代码行要少得多。
然而,当涉及到 GUI 应用程序开发时,您可能会遇到困难。尤其是当你需要处理巨大的数据集时。
本文是 PyQt &关系数据库系列的一部分:

[斯特凡·梅洛](https://melo-stefan.medium.com/?source=post_page-----e6bdbed7ebdf--------------------------------)
## PyQt &关系数据库系列
[View list](https://melo-stefan.medium.com/list/pyqt-relational-databases-series-9ded67b27631?source=post_page-----e6bdbed7ebdf--------------------------------)6 stories
***如果你想阅读更多这样的内容,成为会员:***
<https://melo-stefan.medium.com/membership>
***或者你可以订阅我下面的简讯。谢谢你。***
## 为什么选择 PyQt?
python 中 GUI 的第一个问题是选择合适的框架。
对于数据库应用,我强烈推荐 PyQt。它保存了大量的样板代码,并且它的数据库支持是广泛的。
为此,我已经尽可能避免第三方模块导入。这种方法最重要的优点是在包更新时与代码无关。
因此,维护工作大大减少。除了 python 的内部模块之外,这里唯一需要的包是 PyQt。
注意:我假设您熟悉
* *PyQt 安装*
* *框架的基本用法*
* *SQLite 数据库*
* *关系数据库基础*
## 关系数据库
理解关系数据库至少由两个表组成是很重要的,这是最简单的情况。为了避免我们的例子看起来无聊,我们将有三个表。
***关系数据库*** 根据特定标准将数据分组到表格中。一些表可以相互关联;它们是联系在一起的。
这些链接基于*共享的*数据。与其挑战定义,不如让我们看一个相关表的例子。
## 我们的例子
为了保持简单,我们将使用 SQLite 数据库。这个决定背后有多种理由。
SQLite 是一个快速、小型、独立、高度可靠、功能全面的 SQL 数据库引擎。
数据库存储在单个磁盘文件中。任何人都可以出于任何目的访问它,`sqlite3`模块是 python 的内置模块。
我们用一些数据创建主表“订单”:

表格“订单”
现在,我们有了一个基本的*订单预订系统*来玩。这个表很难看,因为它包含了非人类可读的值。是的,的确!
尽管`OrderID`、`Customer_id`和`Product_id`值有意义——它们是 id,但是在`Price`列和`Product_id`列中有相同的奇怪值。
这不是意外。表`orders`中的`Price`值不是实际价格。是对`products`表中`Product_id`的引用。每当`Product_id`改变时,将`Product_id`值赋给`Price`是开发者的责任。
我们还有`customers`和`products`表格,如下所示。这些表格保存了`orders`表格所需的信息(值)。

表“客户”

表“产品”
## 应用程序设置
让我们现在开始编码。首先,我们导入不可避免的模块。
import sys
from PyQt6.QtWidgets import QApplication, QMainWindow, QMessageBox, QTableView
from PyQt6.QtSql import QSqlDatabase, QSqlRelation, QSqlRelationalTableModel
这里我想说明一下,我用的是 PyQt6。然而,该代码将与 PyQt5 一起工作,没有任何问题。
接下来,我们将包含运行 PyQt 应用程序所需的典型代码片段。没什么特别的。
if name == "main":
app = QApplication(sys.argv)
form = MainWindow()
form.show()
app.exec()
这里我们创建了`app`,PyQt `QApplication`类的一个实例,`form`是`MainWindow`类的一个实例;我们稍后会创建它。
我们确保表单会显示出来,并执行`app`。
接下来,我们将创建一个返回布尔值的函数`createConnection()`。
该函数将处理数据库连接,并在一切正常且数据库连接稳定时返回`True`。
# Type hint for return value
def createConnection() -> bool:
# SQLite type database connection instance
con = QSqlDatabase.addDatabase('QSQLITE')
# Connect to the database file
con.setDatabaseName('testDB.db')
# Show message box when there is a connection issue
if not con.open():
QMessageBox.critical(
None,
'QTableView Example - Error!',
'Database Error: %s' % con.lastError().databaseText(),
)
return False
return True
我们不希望应用程序在没有正确的数据库连接的情况下启动。
验证链接的最佳时机是在 GUI 完全启动之前。所以我们必须修改上面的应用程序初始化代码。
if name == "main":
app = QApplication(sys.argv)
if not createConnection():
sys.exit(1)
form = MainWindow()
form.show()
app.exec()
## 创建图形用户界面
是时候为方便的数据呈现编写我们想要的 GUI 了。
我们已经准备好了应用程序的主窗体(窗口)。你可以在上面的`form = MainWindow()`中看到,这个`MainWindow`类的实例名为`form`。尽管如此,我们还是没有`MainWindow`级!
我们现在就做点什么。这是最吸引人的部分。
我现在可以透露,我们将使用`QTableView`小部件。它有我们需要的魔力。
# Inherit from QMainWindow
class MainWindow(QMainWindow):
def init(self, parent = None):
super().init(parent)
# Set the window title
self.setWindowTitle('QTable Example')
# Create the model
model = QSqlRelationalTableModel(self)
# Set the table to display
model.setTable('orders')
# Set relations for related columns to be displayed
model.setRelation(1, QSqlRelation('products', 'ProductID', 'Price'))
model.setRelation(2, QSqlRelation('customers', 'CustomerID', 'Customer'))
model.setRelation(3, QSqlRelation('products', 'ProductID', 'Product'))
model.select() # Setup the view
# Create the view = a table widget
view = QTableView(self)
# Set the data model for table widget
view.setModel(model)
# Adjust column widths to their content
view.resizeColumnsToContents()
# Add the widget to main window
self.setCentralWidget(view)
## SqlRelationalTableModel
上面的代码显示了整个应用程序的 GUI 设置。注释解释了每一步的细节。
然而,我想澄清和分析模型的`setRelation()`方法和`QSqlRelation`对象的使用。
让我们看看我们在这里做了什么,除了链接到 GUI 本身的行。我们将关注我们的数据模型。
PyQt 包括几个我们可以有效使用的模型对象。在视图中,我们使用关系表;`SqlRelationalTableModel`类完全适合。我们将使用的模型完成了所有我们需要的艰苦工作。
`setRelation()`方法需要两个参数:`column`和`relation`。该方法指定`column`(外键)的行为由`relation`对象定义。
让我们以第一种情况为例:
model.setTable(‘orders’)
model.setRelation(1, QSqlRelation('products', 'ProductID', 'Price'))
行为是`orders`表中的列 1 映射到`products`表中的字段`ProductID`,视图应该向用户显示`products` ' `Price`字段。
## 结论
使用 QSqlRelationalTableModel,我们很好地避免了 SQL 语言知识和其他用于构造复杂 SQL 查询的样板代码。

作者图片
接下来,我们将使我们的表格小部件更吸引人。
# 机场数据的 PySpark 分析
> 原文:<https://towardsdatascience.com/pyspark-analysis-on-airport-data-6d10aaf9cbb1?source=collection_archive---------15----------------------->
## 使用大数据平台 PySpark 和 Google Collaboratory 笔记本分析机场和航班数据

在 [Unsplash](https://unsplash.com/s/photos/big-data?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 上由 [Carlos Muza](https://unsplash.com/@kmuza?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 拍摄的照片
大数据是大量复杂数据集和数据量的集合,包括海量数据、数据管理功能、社交媒体分析和实时数据。大数据是指无法通过简化的处理方法处理的数据,因为要处理的数据非常庞大。大数据也被定义为处理大量数据的系统,以非常短的延迟实时产生结果。Apache Spark 是一个统一的分析引擎,主要用于大规模处理。 [Apache Spark](https://spark.apache.org/) 的一些主要优势是处理速度、易用性、平台通用性以及可以与之集成的工具包。
PySpark 是在 Apache Spark 上工作的 Python API。使用 [PySpark](https://www.gangboard.com/blog/what-is-pyspark#:~:text=PySpark%20is%20the%20Python%20API%20written%20in%20python%20to%20support%20Apache%20Spark.&text=Apache%20Spark%20is%20written%20in,in%20parallel%20and%20batch%20systems.) 的主要优势是它可以很容易地与 Java、R 和 Scala 集成。数据科学家可以利用 RDD 以更高的速度在分布式集群上工作,以部分方式存储数据。磁盘持久化和强大的缓存有助于进一步提高处理速度。在本文中,我们讨论了如何在 Google Collaboratory 笔记本上使用 [PySpark](https://www.gangboard.com/blog/what-is-pyspark#:~:text=PySpark%20is%20the%20Python%20API%20written%20in%20python%20to%20support%20Apache%20Spark.&text=Apache%20Spark%20is%20written%20in,in%20parallel%20and%20batch%20systems.) 来分析包含大约 360 万行和 15 个特征的巨大数据集。
<https://github.com/ojaashampiholi/Airport_Analysis_PySpark>
# 在 Google Colab 上安装和初始化 PySpark
1. 下载 JRE 无头版到笔记本。
!apt-get install openjdk-8-jdk-headless -qq > /dev/null
2.下载 Spark with Hadoop 安装 zip 文件并解压缩以备后用。
!wget -q https://downloads.apache.org/spark/spark-3.0.2/spark-3.0.2-bin-hadoop2.7.tgz
!tar xf spark-3.0.2-bin-hadoop2.7.tgz
3.设置 Javahome 和 Sparkhome 变量。
import os
os.environ["JAVA_HOME"] = "/usr/lib/jvm/java-8-openjdk-amd64" os.environ["SPARK_HOME"] = "/content/spark-3.0.2-bin-hadoop2.7"
4.安装并初始化 findspark 库。
!pip install -q findsparkimport findspark
findspark.find()
findspark.init()
5.创建 Spark 和 SQLContext 会话。
from pyspark.sql import SparkSession
spark = SparkSession.builder
.master("local")
.appName("Colab")
.config('spark.ui.port', '4050')
.getOrCreate()
from pyspark.sql import SQLContext
sqlContext = SQLContext(spark)
spark
以上步骤可以在[笔记本](https://github.com/ojaashampiholi/Airport_Analysis_PySpark/blob/main/Airport_Analysis_PySpark.ipynb)中找到。一旦我们完成了必要的安装并建立了分析环境,数据就是开源的,可以在 Kaggle 上找到,标题是[美国机场数据集](https://www.kaggle.com/flashgordon/usa-airport-dataset)。该项目使用截至 2021 年 3 月 31 日可在 link 上获得的数据集。下载后,我们可以将 CSV 数据读取到 pyspark,并注册它的表格形式,以便与 SQLContext 一起使用。
df = spark.read.csv("./Airports2.csv", header=True, inferSchema=True)df.registerTempTable('df')
# 初步分析和见解
任何数据分析师对数据集进行初步分析的第一步都是计算行数、数据方案和要素的统计描述。
df.printSchema()

[**数据模式**](https://github.com/ojaashampiholi/Airport_Analysis_PySpark/blob/main/query_images/schema.JPG)
df.describe().show()

[**统计分析**](https://github.com/ojaashampiholi/Airport_Analysis_PySpark/blob/main/query_images/describe.JPG)
完成上述步骤是为了获得对数据的基本见解,它们帮助数据分析师确定需要应用于数据的预处理步骤,以将数据转换为正确的格式。为了更好地理解数据,我们可以看一下前几行数据。Spark 转换和动作可用于数据的子集化。
## 数据子集
df.select("Origin_airport","Destination_airport","Passengers","Seats").show(15)

[**数据子集**](https://github.com/ojaashampiholi/Airport_Analysis_PySpark/blob/main/query_images/subset.JPG)
## 数据集合
airportAgg_DF = df.groupBy("Origin_airport").agg(F.sum("Passengers"))
airportAgg_DF.show(10)

[**汇总数据**](https://github.com/ojaashampiholi/Airport_Analysis_PySpark/blob/main/query_images/aggregate.JPG)
# 研究问题和 SQL 解决方案
完成所有初步分析后,我们将继续回答一些问题,这些问题可以使用 SQLContext 用 SQL 查询来表达。下面简要讨论了研究问题及其相应的答案。以下问题的代码解决方案可以在这里找到。
## **查找航班起飞次数最多的机场**

我们可以看到,在航班起飞方面,受欢迎的机场依次是芝加哥奥黑尔机场、哈兹菲尔德-杰克逊亚特兰大机场、达拉斯/沃斯堡机场和洛杉矶国际机场。
## **找到到达旅客人数最多的机场**

我们可以看到,就到达旅客人数而言,受欢迎的机场依次是亚特兰大哈兹菲尔德-杰克逊机场、芝加哥奥黑尔机场、达拉斯/沃斯堡机场和洛杉矶国际机场。
## **寻找航班流量最多的机场**

我们可以看到,就航班数量而言,受欢迎的机场依次是芝加哥奥黑尔机场、哈兹菲尔德-杰克逊亚特兰大机场、达拉斯/沃斯堡机场和洛杉矶国际机场。
## **找到客流量最多的机场**

我们可以看到,就乘客数量而言,受欢迎的机场依次是达拉斯/沃思堡机场、哈兹菲尔德-杰克逊亚特兰大机场、芝加哥奥黑尔机场和洛杉矶国际机场。
## **查找最受欢迎路线的占用率**

热门航班的上座率在 48%到 71%之间,平均为 60%。这意味着尽管有许多航班在机场之间运行,但大多数航班在客运方面效率不高。此外,减少和重新安排一些航班以提高客座率可以帮助航空公司降低燃料成本,同时也有助于通过减少碳足迹来保护环境。
## **查找长途旅行的航班数量**

我们可以从输出中观察到,在大多数情况下,长途飞行的频率较低。然而,有趣的是,有许多航班在夏威夷—檀香山(檀香山国际机场)和纽约(约翰·肯尼迪国际机场和纽瓦克自由国际机场)之间运行,即使两地之间的距离相当远。
## **查找航班最多的航线的平均距离**

我们可以看到,除了少数例外情况,中距离航线(100-300 英里)的航班数量最多。即使机场之间的距离是 725 英里,芝加哥(ORD)和纽约(LGA EWR)之间的航线仍然非常繁忙。我们在这里看到的另一个有趣的例子是,即使距离约为 720 英里,亚特兰大(ATL)和达拉斯/沃斯堡(DFW)之间的航班也是一项受欢迎的服务。
# **未来工作**
输出表可以保存为单独的 CSV 文件,并可用于使用 Python 或 Tableau 可视化数据,以创建交互式仪表盘,从而帮助人们选择航班并相应地计划他们的假期。一个应用程序可以包含一个使用 Apache AirFlow 的工作流,该工作流每天刷新数据并更新已经在仪表板上创建的图表。
# Pyspark 安装指南
> 原文:<https://towardsdatascience.com/pyspark-installation-guide-140aee9aab87?source=collection_archive---------47----------------------->
## 设置 Pyspark 的全面指南

照片由[seli̇m·阿尔达·埃里希马兹](https://unsplash.com/@selimarda?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)在 [Unsplash](https://unsplash.com/s/photos/pipeline?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 上拍摄
如果您需要为数据科学应用程序设置 PySpark 环境的完整指南,请留下来;PySpark 功能,以及有待探索的最佳平台。
# Pyspark 是什么?
Pyspark 是一种健壮的语言,如果您想创建更具可伸缩性的管道和分析,就必须考虑学习这种语言。据数据工程师 Chris Min 称,Pyspark 基本上支持用 Python 编写 spark 应用程序,并以分布式方式高效处理数据。Python 不仅是一门伟大的语言,还是一个一体化的生态系统,可以执行探索性的数据分析,为数据平台创建 ETL,并构建 ML 管道。你也可以说 PySpark 不亚于一个完整的库,可以在单个/集群机器上用于大量的大型数据处理,此外,它甚至不需要 Python 中的线程或多处理模块就可以处理所有这些并行处理。
# Spark 是数据工程的真正交易

照片由[丹尼尔·史密斯](https://unsplash.com/@smudgern6?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)在 [Unsplash](https://unsplash.com/s/photos/engineering?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 上拍摄
根据[国际数据科学与分析杂志](https://rdcu.be/clqD9)的报道,Spark 作为一个通用集群计算框架的出现是真实的,它拥有 Python、Scala 和 Java 语言集成的 API。它令人印象深刻的高级内存编程模型和用于结构化数据处理的库、可伸缩 ML 和图形分析增加了它在数据科学行业中的功能。事实上,不可否认的是,在数据处理的某一点上,用 Pandas 进行扩展是很困难的。成为一名数据工程师需要处理大量的大型数据,如果你精通 Spark,这并不是什么大不了的事情。
# 数据科学家为什么要学 spark?

[西格蒙德](https://unsplash.com/@sigmund?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)在 [Unsplash](https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 上拍照
作为一名数据科学家,学习 Spark 可以改变游戏规则。对于大型数据处理,Spark 比 Pandas 好得多,但在使用上没有太大的不同,所以改用它不是什么大问题,当您在数据工程操作中获得真正的好处时也是如此。Spark 有各种问题的解决方案,它是一个完整的库集合,可以非常有效地执行逻辑。Spark 确保您获得非常干净和高效的操作体验,在某种程度上甚至比 Pandas 更好,尤其是在处理大型数据集时。Spark 以其高效的高性能分析和用户友好的结构为您所用。
# 探索 Pyspark 设置的所有选项
我也有这篇文章的视频版本,如果你感兴趣的话,可以在我的 youtube 频道上看这个视频
[Pyspark 安装指南](https://youtu.be/Ql_jfk3UnHE)作者 [Anuj Syal](https://www.youtube.com/channel/UCO8XsgcjqArk_mAd1VGBMfg?sub_confirmation=1)
下面是一组您可以考虑用来建立 PySpark 生态系统的各种选项。下面列出了您可以考虑的所有最佳平台:
# 在 Ubuntu 上本地设置 Spark 和 Python
* 安装 Java
sudo apt install openjdk-8-jdk
* 从`[https://spark.apache.org/downloads.html](https://spark.apache.org/downloads.html)` linux 版本下载 spark
* 设置环境变量`sudo nano /etc/environment`
JAVA_HOME="/usr/lib/jvm/java-8-openjdk-amd64"
Save and exit
* 测试`echo $JAVA_HOME`并查看路径以确认安装
* 打开 bashrc `sudo nano ~/.bashrc`,在文件末尾添加`source /etc/environment`
* 这应该可以在 ubuntu 上建立 Java 环境
* 安装 spark,在步骤 2 中下载 spark 后,用以下命令安装
cd Downloads
sudo tar -zxvf spark-3.1.2-bin-hadoop3.2.tgz
* 为 spark `sudo nano ~/.bashrc`配置环境,并添加以下内容
export SPARK_HOME=~/Downloads/spark-3.1 .2-bin-hadoop3.2 export PATH=\(PATH:\)SPARK_HOME/bin export PATH=\(PATH:~/anaconda3/bin export PYTHONPATH=\)SPARK_HOME/python:\(PYTHONPATH export PYSPARK_DRIVER_PYTHON="jupyter" export PYSPARK_DRIVER_PYTHON_OPTS="notebook" export PYSPARK_PYTHON=python3 export PATH=\)PATH:$JAVA_HOME/jre/bin
* 保存并退出
* 测试端子中的类型`pyspark`
**没有 ubuntu?使用 VirtualBox**
使用 VirtualBox 在本地安装 Ubuntu。VirtualBox 基本上可以让你在自己的物理计算机上构建一台虚拟计算机。您可以探索 VirtualBox 来设置 Spark 和 Python:(大约 20-30 分钟)
* 从下载 [Virtualbox](https://www.virtualbox.org/wiki/Downloads) 开始。

*来自 Virtualbox 下载的截图*
* 下载 Ubuntu ISO [图片](https://ubuntu.com/download/desktop/thank-you?version=20.04.2.0&architecture=amd64)
* 在 VirtualBox 中,点击新建并设置 ubuntu 64 位环境
* 传入所需的 CPU 内核、内存和存储
* 指向下载的 ubuntu 映像
# 在 Mac 上本地设置 Spark 和 Python
* 确保安装并更新了家酿软件,如果没有,请点击此[链接](https://brew.sh/)或键入终端
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
* 打开终端并安装 Java
brew install java
to check if java installed?
brew info java
* 安装 scala
brew install scala
* 安装 Spark
brew install apache-spark
* 安装 python
brew install python3
* 设置环境 bashrc 打开文件`sudo nano .bashrc`
* 添加以下环境变量
java
export JAVA_HOME=/Library/java/JavaVirtualMachines/adoptopenjdk-8.jdk/contents/Home/
export JRE_HOME=/Library/java/JavaVirtualMachines/openjdk-13.jdk/contents/Home/jre/
spark
export SPARK_HOME=/usr/local/Cellar/apache-spark/2.4.4/libexec
export PATH=/usr/local/Cellar/apache-spark/2.4.4/bin:$PATH
pyspark
export PYSPARK_PYTHON=/usr/local/bin/python3
export PYSPARK_DRIVER_PYTHON=jupyter
export PYSPARK_DRIVER_PYTHON_OPTS='notebook'
* 这将配置 pyspark 设置,以测试终端中的类型`pyspark`
# 用 docker 和 jupyter 笔记本在本地设置(我的首选方法)
**docker 是什么?**
Docker 是一个开发、发布和运行应用程序的开放平台。想了解更多关于 docker 的知识,可以查看这个[链接](https://docs.docker.com/get-started/overview/)
用 docker 和 jupyter notebook 设置 Spark 是一项非常简单的任务,只需要几个步骤就可以帮助 PySpark 在 Jupyter Notebook 上运行。遵循下面提到的步骤:
* 安装[对接器](https://docs.docker.com/get-docker/)
* 通过 [jupyter](https://hub.docker.com/u/jupyter) 使用预先存在的 docker 映像“[jupyter/pyspark-notebook](https://hub.docker.com/r/jupyter/pyspark-notebook)
* 拉图像
docker pull jupyter/pyspark-notebook
* 码头运行
docker run -d -p 8888:8888 jupyter/pyspark-notebook:latest
* 到 [localhost:8888](http://localhost:8888/) 新建一个笔记本,用`import pyspark`运行 cell
# 数据块设置
Databricks 是一个统一的分析平台,基本上在云中有 Spark 集群,管理得非常好。这是一个易于使用的环境,鼓励用户在完全集成的工作空间中学习、协作和工作。任何 spark 代码都可以轻松调度,没有任何麻烦,因为数据块本身就支持 pyspark
* 开始[创建一个 databricks 帐户](https://databricks.com/try-databricks)(这通常由 databricks 管理员完成)。并将其链接到您首选的云提供商。欲了解更多信息,请查看此[视频](https://www.youtube.com/watch?v=3fqfWYBXj2A)
[数据砖块教程](https://youtu.be/3fqfWYBXj2A)作者[阿努杰·塞亚尔](https://www.youtube.com/channel/UCO8XsgcjqArk_mAd1VGBMfg?sub_confirmation=1)
* 您必须从创建 Databricks 集群开始。
* 通过`import pyspark`创建一个数据块笔记本并进行测试
# AWS EC2 上的 Spark 和 Python
Amazon EC2 是 AWS 提供的虚拟机,这些虚拟机预装了操作系统软件 ami,但其余的依赖项需要单独安装。
* 转到 AWS 控制台和 EC2
* 选择 Ubuntu AMI
* 遵循选项 1 中的步骤
**避免这样做,使用其他选项**
# AWS Sagemaker 笔记本电脑上的 Pyspark
Amazon SageMaker 于 2017 年推出,是一个基于云的机器学习平台,它是完全管理的,并将您的环境从开发、培训和部署中分离出来,让您可以单独扩展它们,同时帮助您优化您的支出和时间。点击几个按钮就可以轻松旋转 Sagemaker 笔记本。Amazon sage maker Notebook instance 是一个运行 Jupyter Notebook 环境的机器学习(ML)计算实例。它带有预配置的 Conda 环境,如 python2、python3、PyTorch、TensorFlow 等
* 登录到您的 aws 控制台并转到 Sagemaker
* 点击笔记本,左侧为笔记本实例
* 点击创建笔记本实例,给它一个名字,并选择所需的配置
* 选择实例类型,也许从小型的 ml.t2.medium 开始,也许以后可以构建一个强大的实例
* 单击“创建”并等待几分钟,然后单击“打开 jupyterlab”转到笔记本
* 创建一个新笔记本,并编写以下代码片段来运行 pyspark
import sagemaker_pyspark
from pyspark.sql import SparkSession, DataFrame
classpath = ":".join(sagemaker_pyspark.classpath_jars()) spark = SparkSession.builder.config( "spark.driver.extraClassPath", classpath ).getOrCreate()
* 如果你有兴趣了解更多关于 Sagemaker 的信息,一定要看看我之前的[视频](https://youtu.be/95332cm5ROo),[11 分钟内 Sagemaker](https://youtu.be/95332cm5ROo)
[AWS Sagemaker 在 11 分钟内](https://youtu.be/95332cm5ROo)由 [Anuj Syal](https://www.youtube.com/channel/UCO8XsgcjqArk_mAd1VGBMfg?sub_confirmation=1)
# AWS EMR 集群设置
亚马逊 EMR 可能是运行 Spark 的最佳地点之一,它可以帮助你非常容易地创建 Spark 集群,因为它配备了各种功能,如亚马逊 S3 连接,这使它变得非常快和超级方便。此外,与 EC2 现货市场和 EMR 的集成操作管理了扩展。
准确地说,EMR 是一个管理良好的大数据服务,使数据科学家能够在使用 Python、Scala 和 Pyspark 编写的数据科学应用程序的工作中获得帮助。它为 Spark 确保了一个方便的集群设置,让数据科学家有一个开发和可视化的平台。
* 转到 AWS 控制台并搜索 EMR
* 点击创建一个集群
* 在常规配置中为其命名,在软件配置中选择 Spark 应用程序

* 在硬件配置中,选择实例类型,可以从 m1.medium 开始,并选择集群中的实例数量
* 在 Security 中,选择 EC2 密钥对,通常由管理员创建,如果没有,您可以按照右边的步骤创建编程访问密钥供集群使用

* 保留 rest 选项的默认值,并创建集群
* 之后,创建一个 EMR 笔记本,并选择新创建的集群来执行您的工作
# 结论:
Spark 是一个完整的分析引擎,可以帮助数据科学家进行冗长的数据处理操作,这些操作在熊猫身上是相当困难的。因此,学习 PySpark 这个健壮的库可以在数据工程师的工作过程中给予他们很大的帮助。现在您已经了解了各种平台,这些平台使您能够使用管理良好的云来建立 Spark 集群,您可以自己探索它们。
# 在 Linkedin 和 Twitter 上关注我
如果你对类似的内容感兴趣,点击 Medium 上的“关注”按钮,或者在 Twitter 和 Linkedin 上关注我
【https://anujsyal.com】最初发表于<https://anujsyal.com/pyspark-installation-guide>**。**
# PySpark 神经网络从零开始
> 原文:<https://towardsdatascience.com/pyspark-neural-network-from-scratch-8a19ebad3904?source=collection_archive---------8----------------------->
## [实践教程](https://towardsdatascience.com/tagged/hands-on-tutorials)
## 一个简单的教程,学习如何使用 PySpark 实现一个浅层神经网络(3 个完全连接的层)。

照片由[杰米街](https://unsplash.com/@jamie452?utm_source=medium&utm_medium=referral)在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 拍摄
# 序
本文不打算提供神经网络的数学解释,而只是解释如何使用 Python 中的 Spark (MapReduce)逻辑应用数学方程来运行它。为了简单起见,这个实现只使用 rdd(没有数据帧)。
同样,我假设您的机器上安装了 Spark,您可以运行 spark-submit 或 PySpark Jupyter-Notebook。
本教程中提供的所有代码都可以在这个 [GitHub](https://github.com/MarvinMartin24/PySpark-Neural-Network) 资源库中获得。
<https://github.com/MarvinMartin24/PySpark-Neural-Network>
为了以防万一,这里有一些资源可以设置您的机器来运行代码:
* 在 Mac 上用 Jupyter 笔记本安装[Spark](https://medium.com/@roshinijohri/spark-with-jupyter-notebook-on-macos-2-0-0-and-higher-c61b971b5007)
* 在 Linux 上用 Jupyter 笔记本安装[Spark](https://sicara.ai/blog/2017-05-02-get-started-pyspark-jupyter-notebook-3-minutes)
* 在 Windows 上安装[Spark with Jupyter Notebook](https://naomi-fridman.medium.com/install-pyspark-to-run-on-jupyter-notebook-on-windows-4ec2009de21f)
此外,在整篇文章中,我将根据我以前的一篇文章来解释三层神经网络背后的数学原理。我将提供的大多数数学公式都是在这里摘录和讨论的:
<https://medium.com/swlh/mathematics-behind-basic-feed-forward-neural-network-3-layers-python-from-scratch-df88085c8049>
# 为什么是火花?(而不是 Tensorflow 或 Pytorch)

作者在 imgflip.com 创建的 Gif
如果你已经熟悉深度学习,你可能已经遇到了 GPU/CPU 内存限制。当您试图提供太多资源无法处理的输入数据(通过大批量或大输入要素空间)时,通常会出现这种情况。
因此,根据前面的陈述,向您的模型提供几百千兆字节的输入几乎是不可能的。这对于主要使用 CPU 计算的机器学习算法(线性回归、SVM、逻辑回归、朴素贝叶斯等等)来说更是如此。
Spark 是处理大量数据的强大解决方案。它允许在计算机网络(通常称为集群)上分布计算。Spark 有助于在一个循环中多次分析一组数据的迭代算法的实现。Spark 广泛应用于机器学习项目。
您可能已经知道,TensorFlow 或 Pytorch 等著名的库一般用于构建神经网络。使用这些库的好处之一是 GPU 计算,它通过允许并行计算来加速训练。
Spark 的最新版本也允许使用 GPU,但在本文中,我们将只关注 CPU 计算(像神经网络的大多数 scratch 实现一样)以保持简单。本文提出了一个用于学习目的的实现,它不适合工业需求。
# 分类任务

MNIST 数据集样本,*作者图片*
对于本教程,我们将尝试解决众所周知的 MNIST 手写数字分类任务。MNIST 数据集有 60,000 个样本的训练集和 10,000 个样本的测试集。
每个图像都是手写数字(从 0 到 9),灰度为 28x28。我们 Spark 神经网络的目标是检测图像中的数字。
由于使用浅层全连接神经网络进行图像分类是一项相当困难的任务,因此我们将仅尝试对 0 和 1 的图像进行分类(以获得二元分类器)。在仅选择 0 和 1 图像之后,我们获得 12665 个训练样本和 2115 个测试样本。
# 模型架构
在这一节,我们将定义我们的神经网络的架构(例如,隐藏层的数量,每层神经元的数量,激活函数,损失函数)。为了避免复杂的数学或复杂的模块实现,本文提出了一个 3 层神经网络(只有 1 个隐藏层)。
该模型可以被看作如下:

*作者图片*
其中 **X** 为输入层(有 I 个神经元), **H** 为隐藏层(有 j 个神经元), **Y** 为输出层(有 k 个神经元)。这个模型有 4 个参数: **W(1),W(2),B(1),B(2)** 。
当我们使用 MNIST 图像作为输入数据时,我们有 **i =784** (28x28),那么 j 隐藏神经元的数量是任意选择的,这里我们可以使用 **j=64** ,最后输出神经元的数量 **k = 2** ,因为我们有 2 个可能的数字要预测(0 和 1)。

乙状结肠激活功能,*作者图片*
为了激活隐藏和输出神经元,我们将使用 sigmoid 激活函数。

MSE 损失函数,*作者图片*
最后,为了计算一批训练数据的训练误差,我们将使用一个称为均方误差(MSE)的损失函数。
# 数学
如前所述,本文不打算解释神经网络背后的数学,但这里有一个向前和向后传播公式的概述。更多详情,请看我之前在第一节提到的[文章](https://medium.com/swlh/mathematics-behind-basic-feed-forward-neural-network-3-layers-python-from-scratch-df88085c8049)。

前向(左)和后向(右)传播方程,*作者图片*
**符号**:
* e 是使用 MSE 对一批训练数据计算的误差。
* f 和 f’分别是激活函数和激活函数的导数(sigmoid 和 sigmoid 素数)。
* H^和 h 分别是激活前后的隐藏神经元。
* Y^和 y 分别是激活前后的输出神经元。
* y 是模型的预测值
* Y*是标签(期望的输出)
在训练期间,我们使用**小型批量梯度下降**更新模型参数 W(1)、W(2)、B(1)、B(2),使用以下等式:

*作者图片*
Alpha 是学习率(我们将使用 0.1),在代码实现中,我们通过训练数据批次的大小来标准化梯度。
*注意:与库不同,本文的实现不允许在不改变数学公式或实现的情况下添加额外的层。*
# 基于 RDDs 的神经网络训练
以通常的 Python 方式只使用 Numpy 来应用下面的等式是非常简单的,它只需要 Numpy 矢量化运算,但是当涉及到将计算分布到几台机器时,我们就不能再使用这种逻辑了。
## 弹性分布式数据集(RDD)
使用 Spark 的数据集使用 **RDDs** (弹性分布式数据集)存储。RDD 是跨集群节点划分和分布的元素“集合”。借助 RDD,Spark 可以执行迭代和交互式任务,同时保持集群的可伸缩性和容错能力。
我们的 MNIST 数据集通常会作为 rdd 加载(用于训练集和测试集)。rdd 是使用键值对来构造的。因此,在我们的实现中,我们可以将 **train_rdd** 和 **test_rdd** 定义如下:
## MapReduce
这些 rdd 使用两个主要操作来处理:**映射**和**归约**操作。
**→地图操作**
**映射**操作将分批分解计算。群集的驱动程序会将这些批处理中的每一个发送到群集中的不同计算机(取决于群集的资源和配置)。通过这种方式,群集中的每台计算机都能够计算分配给它的数据块的向前和向后传播。
通过查看数学部分的公式,我们可以看到,要计算给定输入的向后传播,我们需要一些向前传播的数据。
通过这种实现,我们在等式中观察到的链式法则可以通过连续执行映射操作来实现:

*作者图片*
在上图中,每个箭头代表一个映射转换。这些映射操作是对来自集群中不同计算机的每个输入图像并行执行的。这意味着,如果我们想要增加我们的训练集,我们只需要扩大群集中的资源,就能够计算所有训练图像的梯度。
为了阐明这一步,下面是另一个提供更多粒度的图表:

*作者图片*
我们可以看到,我们的原始元组**(图像,标签)**一次又一次地进化映射操作,以最终获得成本、精度以及相对于每个单个训练图像的参数的误差梯度。
注意 **E** 是使用 MSE 计算的单个输入图像的误差。同样地, **Acc** (精度)在这个阶段是表示对同一单个图像的预测成功的布尔值(0 表示错误,1 表示正确预测)。
**→减少操作**
一旦在聚类上计算了前向和后向传播,就应该对结果进行汇总,以获得平均成本、平均精度以及成本在参数上的平均梯度。
这种聚合在训练数据集上执行,并产生如下定义的单个输出元组:

*作者图片*
使用 Spark,这种聚合可以通过使用应用于最终 RDD(由贴图产生)的不同方法来实现。在本文提出的实现中,我们使用了**。reduce()** 分别对所有元组的每个元素求和。其他方法比如。聚合()或。treeAggregate()也广泛用于这种操作(事实上,它们甚至可以比简单的 reduce 方法更加优化)。
最后一个“归约”操作(被认为是 RDD 操作)生成一个包含 6 个项目的 python 列表(成本、B2 的梯度、W2 的梯度、B1 的梯度、W1 的梯度、精度)。所得到的平均梯度可以直接用于使用梯度下降来更新参数,并且可以在训练结束时存储成本和精度用于可视化目的。
# **代码示例**
本节提供了说明上述理论解释的代码。大多数数学公式都嵌入在函数中,以使代码更容易阅读。
算法的核心位于单个 FOR 循环内部(负责历元的迭代)。我们可以看到 8 个**映射**操作,随后是单个**减少**操作(第 95 行到第 105 行),允许计算小批量梯度,从而计算梯度下降。
请注意,小批量是使用**创建的。样品()**法。该方法返回 **RDD** (train_rdd)的采样子集, **False** 参数表示给定子集不会被多次采样, **0.7** 表示整个训练集的 70%被随机采样以构成小批量(70%相当大,意味着梯度几乎是在训练集上计算的,例如,全批量梯度下降)。训练集越大,这个比率应该越低。
# 培训结果
运行上面这段代码后,我们得到了有希望的结果:

*作者图片*
正如您在上面的图中看到的,我们的成本缓慢但稳定地下降,而准确性在训练数据集的各个时期成比例地增加。
是时候在测试数据集(存储在 **test_rdd** 中)上测试我们的模型了。要做到这一点,非常简单,因为我们现在有了参数 W(1),W(2),B(1),B(2)。代码如下所示:
在第 7 行,使用**。map,**我们简单地将运行前向传播的 predict()函数应用于所有测试示例,并使用**。减少**我们汇总指标(真阳性、假阳性、假阴性、真阴性)。
我们获得了以下指标:

*作者图片*
都在 90%以上,说明我们成功训练了我们的模型!
来自 GIPHY 捕获的 Gif
# 丰富
这个实现是为了学习,熟悉 PySpark 和 MapReduce。所以很多方面都可以改进。
**可能的 Spark 改进:** →使用分区,使用 mapPartitions 或 mapPartitionWithIndex 在小批量上运行计算。这种做法会少一些不恰当的洗牌(主要是由于。样本)。
→使用树聚合在分区上进行更优化的聚合(小批量)
→减少映射操作的数量(因为在底层,当一个动作被触发时,映射操作被合并)。
**可能的神经网络改进:** →使用更模块化的方法更容易地添加层(创建类似 PyTorch 或 Keras 的顺序模型)。
→使用更先进的技术(正则化技术、用于图像分类的 CNN、更好的优化器等等)。
# 结论
神经网络本身可能很难理解,即使使用简单的 python。能够将其扩展到像 Spark 这样更具可扩展性的系统是一个伟大的项目,可以帮助更好地理解其背后的复杂概念。
了解这些技术可能面临的限制也很重要,尤其是在扩展方面。
请随时在评论区与我分享您的反馈和想法💻🔥💻。
谢谢大家!
# 谷歌 Colab 101 上的 PySpark
> 原文:<https://towardsdatascience.com/pyspark-on-google-colab-101-d31830b238be?source=collection_archive---------8----------------------->
## 使用 Google Colab 的 PySpark 初学者实践指南

克里斯里德在 [Unsplash](https://unsplash.com/s/photos/python-code?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 上的照片
Apache Spark 是一个用于数据处理的闪电般快速的框架,可以在大规模数据集上执行超快速的处理任务。它还可以独立地或与其他分布式计算工具协作,将数据处理任务分布在多个设备上。
PySpark 是使用 Python 编程语言访问 Spark 的接口。PySpark 是用 python 开发的 API,用于 Spark 编程和用 Python 风格编写 spark 应用程序,尽管底层执行模型对于所有 API 语言都是相同的。
Google 的 Colab 是一个基于 Jupyter Notebook 的非常强大的工具。由于它运行在谷歌服务器上,我们不需要在我们的系统中本地安装任何东西,无论是 Spark 还是任何深度学习模型。
在本文中,我们将看到如何在 Google 协作笔记本中运行 PySpark。我们还将执行大多数数据科学问题中常见的一些基本数据探索任务。所以,让我们开始吧!
*注意——我假设你已经熟悉 Python、Spark 和 Google Colab 的基础知识。*
# 在 Colab 建立 PySpark
Spark 是用 Scala 编程语言编写的,需要 Java 虚拟机(JVM)才能运行。所以,我们的首要任务是下载 Java。
!apt-get install openjdk-8-jdk-headless -qq > /dev/null
接下来,我们将用 **Hadoop 2.7** 下载并解压 **Apache Spark** 进行安装。
*注意——对于本文,我正在下载 Spark 的****3 . 1 . 2****版本,这是目前最新的稳定版本。如果这一步失败了,那么很可能 spark 的新版本已经取代了它。所以,上网查查他们的最新版本,然后用那个代替。*
!wget -q https://www-us.apache.org/dist/spark/spark-3.1.2/spark-3.1.2-bin-hadoop2.7.tgz!tar xf spark-3.1.2-bin-hadoop2.7.tgz
现在,是时候设置“环境”路径了。
import os
os.environ["JAVA_HOME"] = "/usr/lib/jvm/java-8-openjdk-amd64"
os.environ["SPARK_HOME"] = "/content/spark-3.1.2-bin-hadoop2.7"
然后我们需要安装并导入'[**find spark**](https://pypi.org/project/findspark/)**'**库,它将在系统上定位 Spark 并将其作为常规库导入。
!pip install -q findsparkimport findspark
findspark.init()
现在,我们可以从 pyspark.sql 导入 sparkSession 并创建一个 SparkSession,这是 Spark 的入口点。
from pyspark.sql import SparkSession
spark = SparkSession.builder
.master("local")
.appName("Colab")
.config('spark.ui.port', '4050')
.getOrCreate()
就是这样!现在让我们从 PySpark 开始吧!

迈克·范·登博斯在 [Unsplash](https://unsplash.com/s/photos/loading?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 上的照片
# 将数据加载到 PySpark
Spark 有多种模块可以读取不同格式的数据。它还自动确定每一列的数据类型,但是它必须检查一次。
对于本文,我在 Github 中创建了一个样本 JSON 数据集。您可以使用“**wget”**命令将文件直接下载到 Colab 中,如下所示:
!wget --continue https://raw.githubusercontent.com/GarvitArya/pyspark-demo/main/sample_books.json -O /tmp/sample_books.json
现在使用**读取**模块将该文件读入 Spark 数据帧。
df = spark.read.json("/tmp/sample_books.json")
现在是时候使用 PySpark dataframe 函数来研究我们的数据了。
# **使用 PySpark 进行探索性**数据分析
## 让我们来看看它的模式:
在对数据集进行任何切片之前,我们应该首先了解它的所有列及其数据类型。
df.printSchema()Sample Output: root
|-- author: string (nullable = true)
|-- edition: string (nullable = true)
|-- price: double (nullable = true)
|-- title: string (nullable = true)
|-- year_written: long (nullable = true)
## 给我看一些样品:
df.show(4,False)Sample Output: +----------------+---------------+-----+--------------+------------+
|author |edition |price|title |year_written|
+----------------+---------------+-----+--------------+------------+
|Tolstoy, Leo |Penguin |12.7 |War and Peace |1865 |
|Tolstoy, Leo |Penguin |13.5 |Anna Karenina |1875 |
|Woolf, Virginia |Harcourt Brace |25.0 |Mrs. Dalloway |1925 |
|Dickens, Charles|Random House |5.75 |Bleak House |1870 |
+----------------+---------------+-----+--------------+------------+
## 数据集有多大:
df.count()Sample Output: 13
## 选择几个感兴趣的列:
df.select(“title”, “price”, “year_written”).show(5)Sample Output:
+----------------+-----+------------+
| title|price|year_written|
+----------------+-----+------------+
|Northanger Abbey| 18.2| 1814|
| War and Peace| 12.7| 1865|
| Anna Karenina| 13.5| 1875|
| Mrs. Dalloway| 25.0| 1925|
| The Hours|12.35| 1999|
+----------------+-----+------------+
## 过滤数据集:
# Get books that are written after 1950 & cost greater than $10df_filtered = df.filter("year_written > 1950 AND price > 10 AND title IS NOT NULL")df_filtered.select("title", "price", "year_written").show(50, False)Sample Output: +-----------------------------+-----+------------+
|title |price|year_written|
+-----------------------------+-----+------------+
|The Hours |12.35|1999 |
|Harry Potter |19.95|2000 |
|One Hundred Years of Solitude|14.0 |1967 |
+-----------------------------+-----+------------+# Get books that have Harry Porter in their titledf_filtered.select("title", "year_written").filter("title LIKE '%Harry Potter%'").distinct().show(20, False)Sample Output: +------------+------------+
|title |year_written|
+------------+------------+
|Harry Potter|2000 |
+------------+------------+
## 使用 Pyspark SQL 函数:
from pyspark.sql.functions import max# Find the costliest book maxValue = df_filtered.agg(max("price")).collect()[0][0]
print("maxValue: ",maxValue)df_filtered.select("title","price").filter(df.price == maxValue).show(20, False)Sample Output:
maxValue: 29.0
+-----------------------------+------+
|title |price |
+-----------------------------+------+
|A Room of One's Own |29.0 |
+-----------------------------+------+
# 结束注释
我希望你和我写这篇文章时一样喜欢在 Colab 与 PySpark 一起工作!您可以在我的 Github 资源库-[https://github.com/GarvitArya/pyspark-demo](https://github.com/GarvitArya/pyspark-demo)找到这个完整的工作示例 Colab 文件。
▶️ *请在下面的评论中提出任何问题/疑问或分享任何建议。*
▶️ *如果你喜欢这篇文章,那么请考虑关注我&把它也分享给你的朋友:)*
▶️ *可以联系我在—*[*LinkedIn*](https://www.linkedin.com/in/garvitarya/)*|*[*Twitter*](https://twitter.com/garvitishere)*|*[*github*](https://github.com/GarvitArya/)*|*[*insta gram*](https://www.instagram.com/garvitarya/)*|*[*Facebook*](https://www.facebook.com/garvitishere)
**
*在 [Unsplash](https://unsplash.com/s/photos/thank-you?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 上由[Courtney hedge](https://unsplash.com/@cmhedger?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)拍摄的照片*
# 用于机器学习的 PyTest 一个简单的基于示例的教程
> 原文:<https://towardsdatascience.com/pytest-for-machine-learning-a-simple-example-based-tutorial-a3df3c58cf8?source=collection_archive---------3----------------------->
## 我们展示了如何使用 PyTest 为一个简单的机器学习模块编写有效的测试函数

图片来源:作者来自 Pixabay [source-1](https://pixabay.com/vectors/check-mark-check-ok-approved-done-42622/) 和 [source-2](https://pixabay.com/vectors/chemistry-test-tube-laboratory-4209556/)
# 介绍
为什么数据科学和机器学习需要软件测试?
这篇文章中有许多精彩的观点,说明了为什么测试可以提高所有商业和行业的任何软件产品/服务的交付、性能和长期盈利能力。
<https://www.westagilelabs.com/blog/why-is-software-testing-and-qa-important-for-any-business/>
今天,站在 2021 年,我们看到数据科学和机器学习工具、平台和服务越来越多地嵌入到几乎所有行业中,包括医疗保健、金融、制造、零售、娱乐、运输…
机器学习正在触及或将要触及我们生活的几乎所有方面。自然,我们应该期待这些服务背后有一个高质量和健壮的软件框架,它可以预测我最喜欢的餐馆,或者当我在一个新城市迷路时为我提供可靠的指导。**只有当我知道幕后的软件是用成熟可靠的方法测试过的,我才会对这些看起来神奇的服务产生信任**。
在许多情况下,这些新型服务的发展速度甚至高于传统软件产品。加速产品的开发往往是以牺牲质量为代价的。一个好的软件测试策略可以帮助抵消这种权衡。
因此,确保这些新工具经过测试,以获得它们向最终客户承诺的重要特性和功能——可伸缩性、准确性、适应性等,这一点极其重要。
> 只有当我知道幕后的软件是用一种经过验证的、健壮的方法测试过的时候,我才会对这些服务产生信任,这些服务看起来很神奇
# PyTest——什么和为什么?
PyTest 是什么?
据其[网站](https://docs.pytest.org/en/6.2.x/)介绍,“ *pytest 是一款成熟的全功能 Python 测试工具,可以帮助你编写更好的程序。*”
Python 在其安装中提供了一个名为`unittest`的内置测试模块,但是它有一些缺点。这个 GitHub repo 以一种漂亮而直观的方式总结了这两者之间的一些测试场景,
互联网上有很多方便的学习和练习 Pytest 的资源。这里是入门教程,
<https://semaphoreci.com/community/tutorials/testing-python-applications-with-pytest>
而更全面稍微高级一点(处理设计模式)的是下面这个,
<https://realpython.com/pytest-python-testing/>
然而,**我觉得缺乏高度专注于使用 PyTest 进行机器学习模块的专用教程,并带有清晰的示例**。这是我第一次尝试解决这个问题。
我建议读者在深入研究本文之前,先用简单的 Python 程序和函数复习一下 PyTest 的基本用法,因为本文假设您已经基本熟悉了 PyTest。
那我们就开始吧。
# Scikit-learn 模型培训功能的 Pytest 模块
这是一篇基于示例的文章,我能想到的最典型的例子是用 Pytest 测试一个基于 scikit-learn 的 ML 训练函数。这是任何 Python ML 从业者迟早都极有可能遇到的场景。
这个例子的 GitHub repo 在这里: [**PyTest ML repo**](https://github.com/tirthajyoti/Machine-Learning-with-Python/tree/master/Pytest) 。
这是我们将要探索的想法,

图片来源:作者自制(自带)。Scikit-learn logo 来源:[维基百科(BSD)](https://commons.wikimedia.org/wiki/File:Scikit_learn_logo_small.svg) ,Pytest [版权来源](https://docs.pytest.org/en/stable/license.html)
## 如何基于此回购运行 Pytest
* 安装 pytest `pip install pytest`
* 从 [Github repo](https://github.com/tirthajyoti/Machine-Learning-with-Python/tree/master/Pytest) 中复制/克隆两个 Python 脚本
* `linear_model.py`有一个使用 scikit-learn 训练简单线性回归模型的函数。注意,它有基本的断言测试和`try-except`构造来处理潜在的输入错误。
* `test_linear_model.py`文件是测试模块,作为 Pytest 程序的输入。
* 在你的终端上运行`pytest test_linear_model.py -v`来运行测试。您应该会看到如下所示的内容,

## 这是什么意思?
* 终端消息(如上)表明运行了 9 个测试(对应于`test_linear_model.py`模块中的 9 个函数),并且全部通过。
* 它还显示了测试运行的顺序(这是因为您在运行`pytest`命令时在命令行中包含了`- v`参数)。Pytest 允许您随机化测试序列,但是这个讨论将在另一天进行。
## 我的主 ML 函数看起来怎么样?
让我们看看我们正在测试的核心 ML 函数。这是一个简单的函数,用 scikit-learn 库训练一个线性回归模型,并返回 R 分数。它还将训练好的模型保存在本地文件系统上。
我将分两部分向您展示该功能,因为我希望您注意第一部分中的许多`assert`语句。

如果你盯着代码看足够长的时间,你会很容易意识到**函数已经内置了许多类型检查和错误捕捉代码**。
如果函数已经在测试它的传入数据类型和范围,那么我们用 Pytest 检查什么呢?
**PyTest 不检查你的数据。它检查你的代码/程序**。
您仍然必须在主函数中编写各种数据和输入检查代码,以确保数据完整性,并处理 ML 函数部署在实际生产系统中的奇怪情况。
最重要的是,在开发过程中,您希望编写 Pytest 模块来查看您编写的函数是否按预期运行。这包括它自身的错误检查行为。
在这个例子中,我们在原来的`train_linear_model`函数中写了一串`assert`语句,并将它们包装在一个完整的`try...except`块中。**测试代码仅检查我们是否为那些错误的输入情况返回了** `**AssertionType**` **错误**,以及是否为最终用户打印了正确的错误消息。
为了强调这一点,让我以局部的方式重复上面的总体方案,

图片来源:作者制作(自有)
所以,像 ***错误处理*** 或 ***矢量形状检查*** 这样的事情只是你的函数的整体设计模式的一部分。Pytest 将它们视为与其他 ML 特定模式相同,如 ***数据预处理*** 或 ***模型拟合*** 。
在这种情况下,这里是函数的其余部分——这里没有太多断言,只是常规的 ML 内容。您可以在此函数中使用两个 Numpy 数组,它将进行内部测试/训练分割,用训练集拟合模型,在测试集上评估模型并返回分数。
> PyTest 不会检查您的数据。它检查你的代码/程序。你仍然必须在主函数中编写各种数据和输入检查代码,以确保数据完整性和处理奇怪的情况…

## ML 函数捕捉错误的例子
尽管这不是一个直接的 Pytest 主题,但是看到这些断言语句在运行中捕捉错误会很有帮助。这里是一些数据和一个正常的执行,

这里有几个在生产系统中出现的例子,可以用这个函数来处理。

# 现在,让我们看看 Pytest 模块
如果你看一下 [Pytest 模块](https://github.com/tirthajyoti/Machine-Learning-with-Python/blob/master/Pytest/test_linear_model.py),首先映入你眼帘的是庞大的代码!**为了测试一个 42 行的模块,我们必须编写将近 300 行代码!**

这很正常。您想要测试的特性和功能越多,您的测试套件就会越长。这同样适用于测试更多的*情况*或*拐角情况*。
可以详细过一下[码](https://github.com/tirthajyoti/Machine-Learning-with-Python/blob/master/Pytest/test_linear_model.py)。我将只提到一些突出的特点,
* 注意,`test_linear_model.py`是如何包含 9 个名字以 `**test...**`开头的**函数的。那些包含实际的测试代码。**
* 它还有几个**数据构造函数**,它们的名字不是以`test...`开头,它们被 Pytest 忽略了。但是我们间接需要它们来进行测试。`random_data_constructor`甚至使用一个`noise_mag`参数来控制噪声的大小,以测试线性回归算法的预期行为。请参考`test_noise_impact`功能。

* 注意,我们**需要导入各种库来测试各种东西**,例如,我们导入了像`joblib`、`os`、`sklearn`、`numpy`这样的库,当然,还有来自`linear_model`模块的`train_linear_model`函数。

* 请注意测试函数的**清晰而独特的名称,例如`test_model_return_object()`只检查从`train_linear_model`函数返回的对象,或者`test_model_save_load()`检查保存的模型是否可以正确加载(但不尝试做出预测或任何事情)。总是写简短明了的测试函数,并且只关注一个焦点**。

* 为了检查预测,即训练的模型是否真的工作,我们有`test_loaded_model_works()`功能,其中**使用无噪声的固定数据发生器**(与其他情况相比,我们可以使用有随机噪声的随机数据发生器)。它传递固定的`X`和`y`数据,加载训练好的模型,检查 R ( [回归系数](https://www.geeksforgeeks.org/python-coefficient-of-determination-r2-score/))分数是否完全等于 1.0(对于没有噪声的固定数据集为真),然后将模型预测与原始地面真实值`y`向量进行比较。

* 注意,前面提到的测试函数**是如何使用一个特殊的 Numpy 测试函数** `**np.testing.assert_allclose**` **代替常规的** `**assert**` **语句**。这是为了避免与模型数据(即 Numpy 数组和涉及线性代数运算的预测算法)相关的任何潜在的数值精度问题。
* `test_model_works_data_range_sign_change`函数**测试线性回归估计器**的预期行为,即无论数据范围如何,回归分数仍将为 1.0(按 10e-9 或 10e+9 缩放数据)。如果数据以某种方式改变符号,它也会改变预期的行为。

* `test_additive_invariance`函数测试线性回归估计量的加性不变性。类似地,**你应该考虑你的最大似然估计器的特殊属性,并为它们编写定制的测试**。

* 看一下`test_wrong_input_raises_assertion`函数,当输入各种类型的错误输入参数时,**测试原始建模函数是否产生正确的异常和消息。**

* 最后,`test_raised_exception`函数**使用一个特殊的 Pytest 特性-`[pytest.raises](https://docs.pytest.org/en/reorganize-docs/new-docs/user/pytest_raises.html)`[上下文管理器](https://docs.pytest.org/en/reorganize-docs/new-docs/user/pytest_raises.html)在运行时测试其他异常类型的出现(即不同于** `**AssertionError**` **可能由函数模块**中的 `**assert**` **语句引发)。**

> 您想要测试的特性和功能越多,您的测试套件就会越长。这同样适用于测试更多的*情况*或*角落情况*。
# 摘要
在本文中,我们展示了如何为简单的线性回归函数模块编写 PyTest 模块/测试套件。我们谈到了编写测试的一些一般原则,如数据类型和输入向量形状检查,以及机器学习任务的特定方面,如线性回归估计器在数据规模变化下应如何表现。
Pytest 是一个轻量级且易于使用的实用程序,但它提供了足够的灵活性来在测试套件中构建强大的设计模式。您可以利用这一点,使您的数据科学和机器学习包更加健壮和高效。
*喜欢这篇文章吗?成为* [***中等会员***](https://medium.com/@tirthajyoti/membership) *继续* ***无限制学习*** *。如果你使用下面的链接,* ***我会收到你的一部分会员费,而不会给你带来额外的费用*** *。*
<https://medium.com/@tirthajyoti/membership>
# Python 3.10 五项新功能和注意事项
> 原文:<https://towardsdatascience.com/python-3-10-five-new-features-and-considerations-f775c9432887?source=collection_archive---------2----------------------->

来自[皮克斯拜](https://pixabay.com/?utm_source=link-attribution&utm_medium=referral&utm_campaign=image&utm_content=6706894)的[乔舒亚·沃罗尼耶基](https://pixabay.com/users/joshuaworoniecki-12734309/?utm_source=link-attribution&utm_medium=referral&utm_campaign=image&utm_content=6706894)的图片
## 不仅仅是列表,还有例子和注意事项。
日前,Python 3.10 终于发布了。网上已经有很多文章甚至在它发布之前就已经发表了。然而,我发现大多数都只是列出了新的特性,没有太多的讨论。因此,在我的文章中,我将尝试给出一些关于如何使用这些新特性的例子。还有,我就用我个人的观点来讨论一下。
这些变化我就不一一列举了。如果你想要所有新特性的列表,官方文档总是最好的地方。
<https://www.python.org/downloads/release/python-3100/>
我不会只关注特性,我还会建议如何获得 Python 的这个新版本,这样我们都可以开始玩了。既然它已经发布了,我可以使用我最喜欢的 Python 环境管理工具包——Anaconda 来下载它。如果你还没拿到,可以免费下载。
<https://www.anaconda.com/products/individual>
安装后,我们可以使用最新版本 3.10 创建一个新的 Python 虚拟环境。
conda create --name py310 python=3.10
恭喜你!已经安装了最新的 Python。
# 1.允许将联合类型写成 X | Y

图片来自 [Pixabay](https://pixabay.com/?utm_source=link-attribution&utm_medium=referral&utm_campaign=image&utm_content=2156174)
第一个重要特性是联合类型语法。具体来说,我们可以在两个或多个类型之间使用一个管道来表示“不是这个类型就是那个类型”。
这种语法最常见的用法是检查对象的类型。我们现在可以像下面这样编写代码,而不是像`isinstance(x, int) or isinstance(x, str)`那样编写多个条件。
isinstance(x, int | str)
请参见下面的示例。

我首先想到的是,我们可以使用这个语法来编写一些健壮的条件。例如,一个变量应该是一个整数,但也可以是“无”,这也是有效的,那么我们可以这样写条件。
isinstance(x, int | None)

除此之外还有函数签名。现在,我们可以用这个语法来表示一个参数可以有多种类型。例如,我们想编写这样一个函数,将传递给它的参数加 1。参数可以是整数或字符串,返回值应该与原始类型相同。
def plus_1(number: int | str):
if isinstance(number, int):
return number + 1
else:
number = int(number)
return str(number + 1)
我们可以验证它是否如预期的那样工作。

# 2.向 zip()添加可选的长度检查

来自 [Pixabay](https://pixabay.com/?utm_source=link-attribution&utm_medium=referral&utm_campaign=image&utm_content=4615782) 的 [Stjepan Zlodi](https://pixabay.com/users/ylodi-9215071/?utm_source=link-attribution&utm_medium=referral&utm_campaign=image&utm_content=4615782) 的图片
在我之前的一篇文章中已经对`zip()`函数进行了深入的讨论。这里也提到了 Python 3.10 中的这个新特性。这是 Python 中一个非常强大和有用的函数。如果不是太了解,强烈推荐看我的文章。
</fasten-python-objects-using-zip-88a1e7a68c7>
简而言之,`zip()`函数将从两个容器(比如一个列表)中逐个获取项目,并将两个容器中相应位置的项目放在一个元组中。看看下面的例子。
list(
zip([1, 2, 3], ['a', 'b', 'c'])
)

默认情况下,如果我们提供两个不同长度的列表,长列表中的额外项目将被忽略。这也被称为“木桶效应”。
list(
zip([1, 2, 3], ['a', 'b', 'c', 'd'])
)

在上面的例子中,第二个列表中的项目`'d'`被忽略。
然而,有时我们可能没有意识到有些项目被忽略了,因为函数会自动忽略。因此,在 Python 3.10 中,引入了一个名为“strict”的新标志,以强制两个列表具有相同的长度。
list(
zip([1, 2, 3], ['a', 'b', 'c', 'd'], strict=True)
)
如果我们显式设置`strict=True`,当长度不同时会抛出异常。

# 3.现在正式允许带括号的上下文管理器

图片来自 [Pixabay](https://pixabay.com/?utm_source=link-attribution&utm_medium=referral&utm_campaign=image&utm_content=15712) 的 [PublicDomainPictures](https://pixabay.com/users/publicdomainpictures-14/?utm_source=link-attribution&utm_medium=referral&utm_campaign=image&utm_content=15712)
在以前的版本中,在一个“with”语句中包含多个子句有点痛苦。例如,如果我们想在一个“with”语句中打开两个文件,我们必须将它们写在一行中。
with open('sample_data/mnist_test.csv') as test, open('sample_data/mnist_train_small.csv') as train:
pass
在笔记本里,是这样的。

当我们在`open()`函数中有非常长的参数时,这将是非常痛苦的。
有一个解决方案可以让我们的代码更好,那就是添加一个反斜杠,这样我们就可以把它们分成多行。
with open('sample_data/mnist_test.csv') as test,
open('sample_data/mnist_train_small.csv') as train:
pass
它看起来像这样。

这肯定是更好,但仍然有点丑陋的反斜线。
现在,在新版本中,我们可以将放入一对括号中,这样代码会更整洁,可读性更好。
with (open('1.txt') as first_file,
open('2.txt') as second_file):

就我个人而言,我希望将代码编写如下,我认为这样可读性会更好,尤其是当我们有两个以上的项目时。
with (
open('1.txt') as first_file,
open('2.txt') as second_file,
open('3.txt') as third_file,
open('4.txt') as fourth_file
):

# 4.新语法:匹配…大小写…

图片由[埃尔默·盖斯勒](https://pixabay.com/users/pxel_photographer-17831348/?utm_source=link-attribution&utm_medium=referral&utm_campaign=image&utm_content=6363378)从[皮克斯拜](https://pixabay.com/?utm_source=link-attribution&utm_medium=referral&utm_campaign=image&utm_content=6363378)拍摄
Python 中的“switch … case …”语法颇具争议。就连 Python 之父 Guido 也不支持在 Python 中加入这样的语法。然而,它仍然在这个新版本中发布,我实际上确信这个语法是好的。
请注意,人们通常说“switch case ”,因为这在其他编程语言中也很常用。然而,在 Python 中,关键字是“匹配…大小写…”。
例如,我们可以根据 HTTP 代码返回不同的消息。
def http_error(status):
match status:
case 400:
return "Bad request"
case 401:
return "Unauthorized"
case 403:
return "Forbidden"
case 404:
return "Not found"
case _:
return "Unknown status code"

在这个例子中,保守的开发人员可能仍然会争辩说,它实际上并没有为提高代码的可读性提供任何好处。我们可以用 if-else 重写这个函数,也不算太差。
def http_error(status):
if status == 400:
return "Bad request"
elif status == 401:
return "Unauthorized"
elif status == 403:
return "Forbidden"
elif status == 404:
return "Not found"
else:
return "Unknown status code"
事实上,对于这样一个通用用例,匹配用例语法并没有太大的区别。然而,在我看来,新的关键字“match”表明了这种语法的核心逻辑和目的。当我们想为一个表达式“匹配”一些模式时,这意味着让我们的生活更容易。
例如,我们想写一个函数从一个元组中返回主机 URL、端口号和协议类型。然而,元组只能有两个项目(主机和端口)。在这种情况下,我们希望返回一个默认协议“https”。如果元组已经提供了协议,我们将需要返回它。
使用 if-else 语法,我们必须编写如下代码。
def get_url(info):
if isinstance(info, tuple) and len(info) == 2:
host, port = info
protocol = "http"
elif isinstance(info, tuple) and len(info) == 3:
host, port, protocol = info
return host, port, protocol

实际上也不算太糟。然而,如果我们考虑我们试图匹配的两种模式,我们可以使用新的语法重写代码,如下所示。
def get_url(info):
match info:
case host, port:
protocol = 'http'
case host, port, protocol:
pass
return host, port, protocol

哇!非常整洁干净,可读性比 if-else 版本好很多。这就是我对我们应该如何使用这个新语法的想法!
# 5.增强型错误消息

图片由[安德鲁·马丁](https://pixabay.com/users/aitoff-388338/?utm_source=link-attribution&utm_medium=referral&utm_campaign=image&utm_content=1951987)来自 [Pixabay](https://pixabay.com/?utm_source=link-attribution&utm_medium=referral&utm_campaign=image&utm_content=1951987)
以前 Python 的错误信息有时候并不理想。例如,下面的代码缺少右大括号。
my_dict = {'name': 'Chris', 'age': 33

错误消息将简单地告诉我们解析代码时没有成功。
在新版本中,这些错误信息在很大程度上得到了改进。它会直接告诉我们为什么解析代码失败。

一些错误消息让开发者感到温暖,太:D 了

# 摘要

来自 [Pixabay](https://pixabay.com/?utm_source=link-attribution&utm_medium=referral&utm_campaign=image&utm_content=6679843) 的 [yamabon](https://pixabay.com/users/yamabon-11501053/?utm_source=link-attribution&utm_medium=referral&utm_campaign=image&utm_content=6679843) 图片
在这篇文章中,我介绍了几天前发布的 Python 3.10 中最重要的 5 个新特性。希望这将是有帮助的,并展示了这些新功能以及我提供的例子。
<https://medium.com/@qiuyujx/membership>
如果你觉得我的文章有帮助,请考虑加入 Medium 会员来支持我和成千上万的其他作者!(点击上面的链接)
# Python Altair 将过滤、分组和合并结合到一个数据可视化中
> 原文:<https://towardsdatascience.com/python-altair-combines-filtering-grouping-and-merging-into-a-single-data-visualization-13e7e8306b6c?source=collection_archive---------30----------------------->
## 探索性数据分析的完整工具

照片由[艾萨克·史密斯](https://unsplash.com/@isaacmsmith?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)在 [Unsplash](https://unsplash.com/s/photos/chart?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 拍摄
Altair 是 Python 的统计数据可视化库。它为创建静态和交互式可视化提供了简单易懂的语法。
我认为 Altair 与其他常见的数据可视化库的区别在于,它将数据分析组件无缝集成到可视化中。因此,它是一个非常实用的数据探索工具。
在本文中,我们将逐步了解如何创建包含过滤、分组和合并操作的可视化。最后,我们将创建一个信息丰富的图,可用作探索性数据分析过程的一部分。
我们首先生成由两个数据帧组成的模拟数据。第一个包含餐馆订单,另一个包含订单中商品的价格。
import libraries
import numpy as np
import pandas as pd
import altair as alt
import random# mock data
orders = pd.DataFrame({
"order_id": np.arange(1,101),
"item": np.random.randint(1, 50, size=100),
"qty": np.random.randint(1, 10, size=100),
"tip": (np.random.random(100) * 10).round(2)
})prices = pd.DataFrame({
"item": np.arange(1,51),
"price": (np.random.random(50) * 50).round(2)
})order_type = ["lunch", "dinner"] * 50
random.shuffle(order_type)
orders["order_type"] = order_type
这些值使用 numpy 函数生成。然后,我们使用 100 个商品的列表添加订单类型。Python 的 random 模块下的 shuffle 函数是用来随机化列表的。
让我们看一下刚刚创建的数据框。

订单数据框架

价格数据框架
您可能会认为我们可以在订单表中输入商品价格。我们以这种方式设计数据帧,以便能够使用合并操作。此外,它稍微演示了关系数据库的概念。
让我们首先创建一个简单的情节来介绍 Altair 语法的结构。
alt.Chart(orders).mark_circle(size=50).encode(
x="qty", y="tip", color="order_type"
).properties(
title = "Tip vs Quantity"
)

(图片由作者提供)
我们首先将数据传递给一个顶级图表对象。数据可以是 Pandas 数据框的形式,也可以是指向 json 或 csv 文件的 URL 字符串。然后,选择可视化的类型(例如`mark_circle`、`mark_line`等)。
`encode`函数指定在给定的数据框中绘制什么。因此,我们在编码函数中写的任何东西都必须链接到数据帧。最后,我们使用 properties 函数指定绘图的某些属性。
考虑这样一种情况,我们需要创建价格和小费值的散点图。它们在不同的数据帧中。一种方法是合并两个数据框,并在散点图中使用这两列数据。
Altair 提供了一种更实用的方法。它允许在不同的数据框中查找列。直觉和熊猫的合并功能一样。
alt.Chart(orders).mark_circle(size=50).encode(
x="tip", y="price:Q", color="order_type"
).transform_lookup(
lookup="item",
from_=alt.LookupData(data=prices, key="item", fields=["price"])
).properties(
title = "Price vs Tip"
)

(图片由作者提供)
`transform_lookup`功能类似熊猫的`merge`功能。用于匹配观察值的列(即行)被传递给`lookup`参数。`fields`参数用于从其他数据框中选择所需的列。
我们还可以将过滤组件集成到我们的绘图中。我们把价格超过 10 美元的数据点标出来。
alt.Chart(orders).mark_circle(size=50).encode(
x="tip", y="price:Q", color="order_type"
).transform_lookup(
lookup="item",
from_=alt.LookupData(data=prices, key="item", fields=["price"])
).transform_filter(
alt.FieldGTPredicate(field='price', gt=10)
).properties(
title = "Price vs Tip"
)

(图片由作者提供)
`transform_filter`功能用于过滤。`FieldGTPredicate`处理“大于”条件。Altair 还为“等于”、“小于”、“范围”等其他条件提供了谓词。
除了过滤和合并,Altair 还允许在绘图前对数据点进行分组。例如,我们可以创建一个条形图,显示每种订单类型的平均价格。此外,我们可以为价格低于 20 美元的商品执行此操作。
alt.Chart(orders).mark_bar().encode(
y="order_type", x="avg_price:Q"
).transform_lookup(
lookup="item",
from_=alt.LookupData(data=prices, key="item", fields=["price"])
).transform_filter(
alt.FieldLTPredicate(field='price', lt=20)
).transform_aggregate(
avg_price = "mean(price)", groupby = ["order_type"]
).properties(
height=200, width=300
)

(图片由作者提供)
让我们详细说明每一步:
* `transform_lookup`:从价格数据框中查找价格。
* `transform_filter`:过滤 20 美元以下的价格。
* `transform_aggregate`:按订单类型对价格进行分组,并计算平均值。
确保将聚合列的名称传递给`encode`函数。
## 结论
筛选、合并和分组是探索性数据分析过程中必不可少的。Altair 允许在创建数据可视化的同时执行所有这些操作。在这个意义上,Altair 也可以被认为是一个数据分析工具。
我们在文章中做的例子可能看起来不那么有用,但是它们清楚地解释了这些组件是如何在可视化中使用的。
感谢您的阅读。如果您有任何反馈,请告诉我。
# 面向初学者的 Python 和 Apache Cassandra
> 原文:<https://towardsdatascience.com/python-and-apache-cassandra-for-beginners-d9682f2f43c1?source=collection_archive---------10----------------------->
## [实践教程](https://towardsdatascience.com/tagged/hands-on-tutorials)
## 了解如何将 Python 连接到 Cassandra,并使用 Datastax ODBC 驱动程序插入数据,以及使用 Astra web 控制台查询数据。
Python 是使用最广泛的编程语言之一,拥有庞大的支持社区,而 Cassandra 是最受欢迎的 NoSQL 数据库之一,传统上用于 web 应用程序存储或依赖于快速数据检索的以数据为中心的应用程序。总之,它们在各行各业和学术界的许多应用中都能找到。
本 Cassandra Python 教程面向 Python 和 Cassandra 初学者。你可以在整篇文章中看到的代码样本可以在这个 [Github 库](https://github.com/andyadamides/python-cassandra-intro)中公开获得。我们将指导您设置 Python 和 DataStax Astra,这是一个托管的 Cassandra as-a-Service 应用程序,可以在任何云上免费运行。我们将向您展示如何将 Python 连接到 Cassandra,如何使用 Datastax ODBC 驱动程序插入数据,以及如何使用 Astra web 控制台通过 CQL 控制台查询存储在 Cassandra 中的数据。即将发布的 Python SDK for Astra 将支持针对给定 Astra 数据库实例的 REST、GraphQL 和无模式 JSON 文档 API 的 API 访问,这将在下一篇文章中讨论。

# Python 入门
Python 在过去十年中广受欢迎,这是有充分理由的。简单地说,它是一种可读性很强、简单易用的编程语言。它也很容易上手,但同时它可以用于各种不同的应用程序和不同的领域。存在多个 Python 库,允许解决诸如字符串操作、实现机器学习算法、[、面向对象](https://en.wikipedia.org/wiki/Object-oriented_programming)编程和数据工程目的等问题,我们将在本教程中重点介绍这些问题。
# 安装 Python
Python 有两个主要版本,Python 2 和 Python 3。前者不再接收更新,也不再受支持,而后者是最新版本,也是我们将使用的版本。无论您使用什么操作系统,安装 Python 都很容易。
在你的电脑上安装 Python 的最好方法是通过官方的 Python 页面。根据你使用的操作系统,从官方[页面](https://www.python.org/downloads/release/python-394/)选择合适的安装程序。
要验证 Python 是否已正确安装在您的计算机上,请打开命令行窗口并执行以下操作:
python --version
这将返回您刚刚安装的版本:
Python 3.8.2
如果你正确地安装了 Python 并且得到了一个**命令没有找到**或者类似的错误信息,这很可能意味着 Python 没有被正确地添加到你的操作系统的 PATH 变量中;所以一定要仔细检查 Python 的安装路径是否是该路径的一部分。
# 安装 Python 依赖项
像任何其他编程语言一样,Python 也使用单独的实用程序来安装包。Python 内置的包管理器叫做 **pip。**除了来自 pip,其他流行的 Python 包管理器包括[**virtualenv**](https://pypi.org/project/virtualenv/)**和 [**conda**](https://www.anaconda.com/products/individual) **,**但是为了这个 Cassandra Python 教程的目的,我们将使用 **pip。****
**每一个使用 **pip,**的 Python 项目通常会在资源库的根目录下有一个名为 **requirements.txt** 的文件,就像我们在 [Github 项目中有 requirements.txt 一样。这个文件的内容非常简单;每一行都由包的名称组成,可选地后跟该包的特定版本:](https://github.com/andyadamides/python-cassandra-intro/blob/main/requirements.txt)**
cassandra-driver3.25.0
numpy1.19.3
astrapy0.0.2
simplejson3.17.2
**要安装所需的包,包括 Cassandra Python 库 **cassandra-driver** 和 **astrapy** ,只需在命令行中导航到根目录并执行:**
pip install -r requirements.txt
**后台发生的事情是 **pip** 将从默认和公共 Python 包索引 [PyPi](https://pypi.org/) 中获取这些包。您可以查看每个包的 PyPi 主页,包括可用版本、进一步的文档、每个包的 Github 库的链接以及如何使用每个包的示例。例如,这是[数字](https://pypi.org/project/numpy/)的页面,这是[卡珊德拉驱动](https://pypi.org/project/cassandra-driver/)的页面。**
**这就完成了我们 Python 项目的依赖项的安装。**
# **卡珊德拉是什么?**
**Cassandra 是领先的开源 NoSQL 分布式数据库管理系统(DBMS)。与 Oracle 或 SQL Server 等传统 SQL DBMS 不同,NoSQL 数据库遵循不同的存储模型。在 SQL 系统中,数据组织在由固定模式驱动的表和列中,而在 Cassandra 中,固定模式并不强制实施,允许同一表(或 Cassandra 中通常称为表的列族)中有动态数量的列,并且能够处理和存储非结构化数据。除此之外,用于与 Cassandra 交互的语言是传统 SQL 的变体(和子集),称为 Cassandra 查询语言(CQL)。**
**90%的财富 100 强公司都在使用 Cassandra。快速增长的事实可以用其丰富的功能集来解释,这些功能对大量数据特别有益。其分布式体系结构可确保超快的写入性能和数据查询的快速检索,无单点故障,从而实现 100%的高可用性,并显著缩短上市时间,因为 Cassandra 集群的部署、管理和维护非常简单。**
# **建立卡珊德拉数据库**
**Cassandra 是开源的,这意味着它可以免费使用。根据您使用的操作系统,您可以在本地下载 Cassandra 及其依赖项,并进行配置和安装。Kubernetes 用户也可以使用 Docker 图像,但所有这些过程可能会令人厌倦,尤其是对于第一次使用的用户。**
**开始使用 Cassandra 的最佳方式是通过一个可从网上获得的托管 Cassandra 数据库。Datastax Astra 是一个由 Apache Cassandra 支持的无服务器数据库即服务,只需几次点击即可启动,有一个慷慨的免费层,可在主要的云提供商(亚马逊网络服务,Azure 或谷歌云)中使用。**
**官方 Astra 指南提供了创建 Astra 服务所需的所有信息;你需要注册一个账户,然后选择更多的细节,比如选择一个云提供商和命名你的数据库。**
# **使用 astrapy 生成 JSON 并插入到 Astra 中**
**使用 React、Angular 或 Vue 作为前端? [**astrapy**](https://pypi.org/project/astrapy/) 是一个方便的 Cassandra Python 库,它在 Datastax Astra 的 REST API 上创建了一个无模式的、面向 JSON 文档的 API。在这个例子中,我们将使用一个令牌(而不是客户端密码)向 Astra 认证,生成一个伪 JSON 文档,并发出一个 PUT REST 调用,将 JSON 插入到 Astra 集合中。**
**在浏览代码之前,确保你已经安装了 **astrapy** 和 [simplejson](https://pypi.org/project/simplejson/) 库。你可以通过执行 **pip 冻结来检查。**如果你没有它们,用**pip install-r requirements.txt .**从项目根目录下的 requirements . txt 文件中安装它们**
**首先在代码中,我们将得到一个 Astra HTTP 客户端。在此之前,我们需要生成一个应用程序令牌,并总共导出五个环境变量。导航至 Datastax Astra 主页,点击数据库名称:**
****
**然后,在数据库名称下方,点击**连接**:**
****
**这一次,使用 API 选项保持在**连接上:****
****
**注意您需要在页面右侧导出的 5 个环境变量中的 3 个:**
* ****ASTRA_DB_ID****
* ****ASTRA_DB_REGION****
* ****ASTRA_DB_KEYSPACE****
**对于**ASTRA _ DB _ APPLICATION _ TOKEN**环境变量,让我们生成**连接凭证**。点击显示“**此处”**的地方:**
****
**将弹出一个新页面,选择角色(选择**读/写用户**)并点击**生成令牌**:**
****
**一旦你这样做,你会得到一个窗口,所有的细节:**
****
**确保将其他(客户 Id、客户机密)信息保存在您可以参考的地方,因为我们稍后会用到它们。导出**ASTRA _ DB _ APPLICATION _ TOKEN**环境变量,该变量等于在此步骤中生成的令牌..**
**为第五个也是最后一个环境变量 **ASTRA_DB_COLLECTION 填入您喜欢的任何名称。**在我这里这个变量等于 **demo_book。****
**一旦根据所使用的操作系统导出了五个环境变量,就可以开始执行 Python 脚本了。第一部分将使用令牌身份验证来验证 Datastax Astra:**
**一旦我们有了 HTTP 客户机对象,它就被传递给下一个方法,在这个方法中,我们创建一个 JSON 文档并将其插入到 Astra 集合中:**
**要执行 Python 脚本,请键入**Python JSON _ document _ API . py**并按回车键。**
**最后,我们可以通过发出一个 **curl** 命令来确认文档已经成功插入,并通过如下命令行检索它:**
**这个命令将返回所有插入的文档,在我们的例子中:**
# **使用驱动程序建立从 Python 到 Astra 的连接**
**Cassandra Python[**Cassandra-driver**](https://pypi.org/project/cassandra-driver/)使得在 Datastax Astra 中认证和插入表格数据变得容易。到目前为止,我们已经确保安装了 Python,并且有了一个 Astra 无服务器实例,我们可以使用一个无模式的、面向 JSON 文档的 API 来处理它。让我们继续使用 cassandra-driver 作为 Astra Python SDK 中文档 API 的模式驱动替代,与我们的 Astra 数据库进行实际交互。**
**在开始编码之前,我们需要了解配置从 Python 到 Astra 的连接的先决条件。在 Astra 仪表板页面上,点击页面左侧的数据库名称:**
****
**然后,在数据库名称的正下方,点击**连接**:**
****
**从驱动列表中选择 **Python** :**
****
**现在,您会看到页面右侧弹出了一个详细的步骤指南。首先,我们将确保下载**包**,并将其存储在我们的本地存储中。我们将在后面的代码中引用这一点。在页面右侧,点击**下载捆绑包**按钮,然后点击**安全连接捆绑包**弹出按钮**
****
**这将下载从浏览器配置的默认下载目录中的包。这通常位于您的主目录中,包的名称遵循特定的命名格式**secure-connect-<database-name>。例如,在我们的例子中,它被命名为**secure-connect-Cassandra-pythondemo . zip。******
# **如何在 Astra 中创建 Cassandra 表**
**在本节中,我们将使用 Python 生成一个虚构的[时间序列](https://en.wikipedia.org/wiki/Time_series)数据集,并使用 Datastax Python ODBC/JDBC 驱动程序将数据插入到我们的 Astra 数据库中。**
**首先,我们将创建 Astra 表来保存我们的数据。导航至 Astra 仪表板页面,点击页面左侧的数据库名称:**
****
**点击 Cassandra 查询语言(CQL)控制台:**
****
**从 [Github repo](https://github.com/andyadamides/python-cassandra-intro) 中复制**intro/demo _ readings . SQL**并粘贴到 CQL 控制台上,然后点击回车:**
**这就完成了 Astra 表的创建。正如您在上面的 [DDL](https://en.wikipedia.org/wiki/Data_definition_language) 脚本中所看到的,这个 timeseries 数据集由浮点值度量(*值*)组成,它是在连续的时间间隔(*值 _ts* )中为一个虚构的硬件对(*设备 _id* 和*时间序列 _id* )捕获的,同时还包括记录被捕获时的时间戳(*发布 _ts* )。**
# **用 Python 在 Cassandra 中插入数据**
**创建 Cassandra 表之后,让我们导航到 **Python** 。首先,确保 **git 在您的本地文件系统中克隆**项目:**
git clone git@github.com:andyadamides/python-cassandra-intro.git
***注意*:如果您没有安装 git,请按照本 [Github 指南](https://github.com/git-guides/install-git)进行安装。**
**该程序由一个名为 **main.py** 的 Python 脚本组成。入口点位于脚本的最后两行。Python 解释器通过设计知道从脚本的这一部分开始执行:**
if name == “main”:
main()
****main()** 方法执行两个高级任务,它建立与 Astra 数据库的连接,然后插入已经生成的数据:**
def main():
“””The main routine.”””
session = getDBSession()
generateDataEntrypoint(session)
**建立与 Astra 数据库的连接发生在 **getDBSession()** 方法中:**
**在这一步,确保填写正确的连接 Astra 的详细信息。特别要确保**导出**Python 代码期望的三个环境变量,以便安全成功地建立到 Astra 的连接:**
* **ASTRA_PATH_TO_SECURE_BUNDLE**
* **ASTRA_CLIENT_ID**
* **阿斯特拉 _ 客户 _ 秘密**
****注:** ASTRA_CLIENT_ID 和 ASTRA_CLIENT_SECRET 是在上面的部分生成的。**
**找到上一步中下载的包 zip,并将其复制到一个目录中,该目录可以配置为 ASTRA_PATH_TO_SECURE_BUNDLE 环境变量的一部分:**
> **cloud_config= {**
>
> **' SECURE _ connect _ BUNDLE ':OS . environ . get(' ASTRA _ PATH _ TO _ SECURE _ BUNDLE ')**
>
> **}**
**例如,如果将安全包 zip 放在 Python 项目的根目录中,ASTRA_PATH_TO_SECURE_BUNDLE 环境变量的值需要等于'**../secure-connect-Cassandra-pythondemo . zip**,项目的根目录将包括以下文件和文件夹:**
****
**同样,用上一步中的值设置 ASTRA_CLIENT_ID 和 ASTRA_CLIENT_SECRET 环境变量。**
**一旦建立了连接,我们就通过传递在上一步的 **getDBSession()** 方法中生成的 Astra **session** 对象,在**generateDataEntrypoint(session)**方法中生成和插入数据。**
****注意:**拥有[硬编码的](https://en.wikipedia.org/wiki/Hard_coding)秘密是**而不是**推荐的最佳实践;请确保不要在生产中使用它,并以安全第一的心态设计您的应用程序。在本文中,我们不会讨论安全获取机密的最佳实践。**
**当我们生成虚构的数据时,我们利用[**numpy**](https://numpy.org/)**和[**random**](https://docs.python.org/3/library/random.html)**Python 库来帮助创建一个基于任意范围的 id 列表,然后随机选取一个 id:******
******我们还用两个循环包围了我们的生成脚本,并且我们根据两个静态变量配置了运行次数。例如,以下脚本将生成 2 个时间序列,每个序列有 10 行(基于 **timeseries_to_generate** 和 **number_of_rows** 变量):******
****将数据插入 Astra 发生在 **generateInsertData(t_id,number_of_rows,session)** 方法中:****
****首先,我们用**读数**和**设备标识**变量准备虚拟数据。对于这些变量,我们正在创建另外两个 **numpy** 任意列表,方法与我们之前创建**时间序列 id**的方法相同。我们还引入了一个名为[**datetime**](https://docs.python.org/3/library/datetime.html)**的新 Python 模块,在其中我们用来创建 **value_ts** 和 **t_pub_ts** 变量。******
****在上述变量初始化之后,我们正在用 **insert_query** 变量为 Astra 准备 insert 语句。一个 [PreparedStatement](https://docs.datastax.com/en/developer/python-driver/3.9/api/cassandra/query/#cassandra.query.PreparedStatement) 对于在一个应用程序中多次执行的查询特别有用,这符合我们的用例。如您所见,我们将在下面的 for 循环中调用 **insert_query** 与 **number_of_rows** 变量一样多的次数,将数据插入到我们在上一步中创建的表中:****
session.execute(insert_query, [device_id, t_id, value_ts, t_pub_ts, round(random.choice(readings),2)])
******session . execute(insert _ query,data)** 函数调用有效地使用了我们在上一步中创建的 Astra 数据库会话。它的第一个参数是 **insert_query** 预准备语句,第二个参数是一个类似于**数组的对象**,它包含要插入数据库表的实际数据。****
****在 Astra 中执行 Python 脚本并检查结果****
****让我们继续执行 Python 脚本。****
******注意:**在尝试执行这个脚本之前,确保 [**git 克隆**](https://github.com/git-guides/git-clone) 回购以及遵循本文开头列出的所有先决条件(安装 Python、设置 Astra、在 Python 中设置 Astra 捆绑包和客户端 id/secret)。****
****打开命令提示符,导航至 Python 脚本文件的位置。确保配置要生成的数据数量( **timeseries_to_generate** 和 **number_of_rows** 变量)。然后,执行:****
python main.py
****根据您汇总的配置,您将获得类似于以下内容的输出:****
Processing:130483SuccessSuccessSuccessSuccessSuccessSuccessSuccessSuccessSuccessSuccessProcessing:176990SuccessSuccessSuccessSuccessSuccessSuccessSuccessSuccessSuccessSuccess
****以上表明 Python 程序的执行是成功的。为了检查数据是否已经被插入到 Astra 中,让我们转到 Astra 控制台并执行一个 **CQL** 脚本。****
****在 Astra 控制台中,导航至 CQL 控制台选项卡:****
********
****键入以下 CQL 查询并点击**回车**:****
select * from cassandra_pythondemo.demo_readings;
****这将产生以下结果集:****
********
****Python 中的插入脚本非常有效,我们已经成功地在 Astra Cassandra 数据库中插入了数据。****
# ****结论****
****我们现在已经成功构建了一个连接到 Astra 的 Python 脚本,Astra 是一个由 Apache Cassandra 支持的无服务器数据库即服务。我们展示了如何浏览 Astra 网站以创建新的 Cassandra 表,如何通过 CQL 控制台执行查询,如何使用多个 Python 库(如 numpy 和 datetime)在 Python 中生成数据,以及如何使用 Python cassandra-driver 配置与 Astra 的连接,并使用准备好的语句插入数据。我们还使用 astrapy Cassandra Python 库与 Astra Document API 进行交互,以插入和检索 JSON 数据。****
****了解更多信息:****
* ****[一张照片](https://pypi.org/project/astra-python/)****
* ****【Astra 的 Python 驱动****
****这个 Cassandra Python 教程的代码可以在这里找到:【https://github.com/andyadamides/python-cassandra-intro】T5****
# 命令行上的 Python 和数据科学片段
> 原文:<https://towardsdatascience.com/python-and-data-science-snippets-on-the-command-line-2673d5d9e55d?source=collection_archive---------23----------------------->
## 在一行代码中访问有用的 Python 片段
# 动机
[Data Science Simplified](https://mathdatasimplified.com/) 是我创建的一个网站,它将每天的 Python 和数据科学技巧发送到你的邮箱。这个小贴士可以让你在 1 分钟内获得有用的知识,并继续你的一天。

作者图片
但是,有时您可能想在需要时搜索某些提示,而不去网站。如果您能像下面这样在命令行上搜索和查看代码片段,那不是很好吗?

作者 GIF
这就是 python-snippet 派上用场的时候。在本文中,您将了解什么是 python-snippet 以及我用来创建这个方便工具的库。
# 什么是 python-snippet?
[python-snippet](https://github.com/khuyentran1401/python_snippet) 是一款 python 工具,让你在命令行上轻松获得 Data Science 简化版的代码片段。
要安装 python-snippet,请键入:
python3 -m pip install --user python-snippet.
确保您的 Python 版本> = 3.6.2。
python3 --version
要检查安装是否成功,请键入:
python-snippet

作者图片
# 基于类别搜索帖子
您可以通过键入以下内容来基于类别搜索帖子:
python-snippet search-category
python-snippet 会根据你选择的类别推荐相关的文章。

作者 GIF
选择一篇文章后,您应该会看到原始文章的链接和打印在终端上的代码片段,如下所示:

作者图片
如果您想使用代码,也可以将代码片段保存到当前目录。
# 基于字符串模式搜索帖子
您可以将此命令视为终端上的搜索栏。

作者图片
要基于字符串模式搜索帖子,请键入:
python-snippet search-posts
例如,输入`python-snippet search-posts pandas`会给出包含字符串`pandas`的搜索结果。

作者 GIF
与前面的命令类似,在选择输出后,一个代码片段将被打印在终端上,并可以保存到您的当前目录中。

作者图片
点击这里可以找到所有代码片段的完整列表[。](https://khuyentran1401.github.io/Python-data-science-code-snippet/)
# 我是如何创建这个工具的?
这个工具很方便,但也很容易创建。如果你想创建一个类似的工具,这里是我用来创建 python-snippet 的库。
## 数据
我使用[请求](https://docs.python-requests.org/en/master/)从我的 GitHub 页面获取 HTML,并使用 [Beautiful Soup](https://www.crummy.com/software/BeautifulSoup/bs4/doc/) 提取相关元素,比如我页面上的表格行。

作者图片
## 问题
为了提示用户选择特定的类别和帖子,我使用了 questionary。
[Questionary](https://github.com/tmbo/questionary/tree/94f5386da93852b436524318314de19702e1c73d) 是一个 Python 库,可以在一行代码中构建漂亮的命令行用户提示。要给用户一个可供选择的项目列表,请使用`questionary.select(question, choices).ask()`:

作者 GIF
## 命令
为了创建像`python-snippet search-category`和`python-snippet search-posts`这样的命令,我使用了 [Typer](https://typer.tiangolo.com/) 。
Typer 是一个基于 Python 的类型提示构建命令行界面(CLI)应用程序的库。使用 Typer,您可以在一行代码中创建命令行界面,如下所示:

作者图片
你可以在这里找到关于 Typer 的综合教程:
</typer-build-powerful-clis-in-one-line-of-code-using-python-321d9aef3be8>
## 用语法在终端上打印代码
像下面这样没有语法突出显示的代码很无聊。

作者图片
为了在终端上打印带有语法突出显示的代码,我使用了丰富的。
Rich 是一个 Python 库,用于终端中丰富的文本和漂亮的格式。下面是如何使用 Rich 打印带有语法高亮的代码。

作者图片
现在代码在终端上看起来更好了!
你可以在这里找到 Rich 的完整教程:
</rich-generate-rich-and-beautiful-text-in-the-terminal-with-python-541f39abf32e>
## 出版
最后,为了发布我的包,我使用了诗歌。[poems](https://python-poetry.org/docs/)是一个 Python 库,它使得管理依赖关系和打包 Python 项目变得简单高效。
使用诗歌,您可以通过以下 5 个命令将您的包发布到 PyPI:
* `poetry init`
* `poetry shell`
* `poetry add <package>`
* `poetry build`
* `poetry publish`
在这里找到完整的诗歌教程:
</how-to-effortlessly-publish-your-python-package-to-pypi-using-poetry-44b305362f9f>
# 结论
恭喜你!您刚刚学习了如何使用 python-snippet 从终端访问 Data Science Simplified 中的代码片段。我希望这个工具能让你不用离开终端就能获得有用的提示。
您还可以使用您在本文中学到的库来创建一个适合您需要的命令行工具。
随意发挥,并在这里叉这篇文章的源代码:
<https://github.com/khuyentran1401/Data-science/tree/master/applications/python_snippet_tutorial>
我喜欢写一些基本的数据科学概念,并尝试不同的算法和数据科学工具。你可以在 LinkedIn 和 T2 Twitter 上与我联系。
星[这个回购](https://github.com/khuyentran1401/Data-science)如果你想检查我写的所有文章的代码。在 Medium 上关注我,了解我的最新数据科学文章,例如:
</introduction-to-datapane-a-python-library-to-build-interactive-reports-4593fd3cb9c8> </how-to-create-mathematical-animations-like-3blue1brown-using-python-f571fb9da3d1> </3-tools-to-track-and-visualize-the-execution-of-your-python-code-666a153e435e> [## 3 个跟踪和可视化 Python 代码执行的工具
towardsdatascience.com](/3-tools-to-track-and-visualize-the-execution-of-your-python-code-666a153e435e) </introduction-to-dvc-data-version-control-tool-for-machine-learning-projects-7cb49c229fe0>
# Python 和模块搜索路径
> 原文:<https://towardsdatascience.com/python-and-the-module-search-path-e71ae7a7e65f?source=collection_archive---------9----------------------->

克里斯·里德在 [Unsplash](https://unsplash.com/s/photos/code-system-path?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 上拍摄的照片
## python 如何知道要导入哪些包,在哪里可以找到它们,以及现代工具(conda、pyenv、them)如何让这一切变得简单
之前我们已经研究过各种工具(conda,pyenv)如何操作`$PATH`变量,这样当你输入`python`时,你想要的 python 版本(在给定的环境中)就是你得到的 python 版本。对于导入 python 标准默认函数库来说,这一切都很好,但是:
* 当我们想从 PyPI 安装第三方软件包时(比如 numpy,pandas,scipy)
* 当我们想在本地安装我们自己的包/模块时——无论是开发还是在为 PyPI 构建我们自己的包之前进行测试
python 如何知道:
* 在哪里寻找这些包
* 如果我们有两个同名的,如何区分它们的优先级
为此,你一输入`python`,它就会构建‘模块搜索路径’。
## 输入`python`会发生什么?
所以我们已经知道,当你输入`python`时,你的操作系统会从上到下查找第一个名为‘python’的可执行文件的路径(不管它实际上是不是 pyenv shims 指出的 python 可执行文件)。然后,一旦找到它,它就执行它。但是这有什么用呢?接下来是什么?在知道如何定位我们的包的背景下,我们关心它如何构造`sys.path`变量。
类似于`$PATH` , `[sys.path](https://docs.python.org/3/library/sys.html#sys.path)`是一个内部 python 版本,类似于操作系统如何自顶向下遍历`$PATH`来匹配可执行文件,python 遍历`sys.path`来匹配包和模块。
</python-the-system-path-and-how-conda-and-pyenv-manipulate-it-234f8e8bbc3e>
## `sys.path`是如何确定和设定的?
这里有一篇很棒的文章介绍了这一点的复杂性,尽管这看起来是一个简单的任务,但有许多困难需要克服。在“现代 python 设置”中,我们有幸使用以下一项或多项:
* pyenv
* 康达
* 诗意
这个过程在很大程度上被简化了,因为我们并不真正关心像 T0 和 T1 这样的东西,因为它们往往是空白的。
我们来看一个例子。我使用的是`pyenv`,就像之前一样,我们可以验证我们运行的是哪个版本的 python:
pyenv version
3.9.7 (set by /Users/jamisonm/.pyenv/version)
鉴于我们已经在上一篇文章中讨论了[和](https://markjamison03.medium.com/python-the-system-path-and-how-conda-and-pyenv-manipulate-it-234f8e8bbc3e)pyenv 实际上是如何查找和设置可执行文件的,我们知道我们在这个实例中使用了`/Users/jamisonm/.pyenv/versions/3.9.7/bin/python`作为 python 可执行文件。那么现在会发生什么呢?
**找到这个版本的 python 被执行的位置**
因此,操作系统执行 python 可执行程序,然后被执行的程序向操作系统询问其位置。假设没有打嗝,这将被设置在变量`sys.executable` = `/Users/jamisonm/.pyenv/versions/3.9.7/bin/python`中。
**设置 sys.prefix 和 sys.exec_prefix**
接下来,python 设置前缀和 exec_prefix,它们看起来通常是相同的。如果我们的`sys.executable`变量设置为`/Users/jamisonm/.pyenv/versions/3.9.7/bin/python`,那么`sys.prefix` = `sys.exec_prefix` = `/Users/jamisonm/.pyenv/versions/3.9.7/`。然而,如果一个`pyvenv.cfg`文件存在于`sys.executable`之上的目录中,那么它被读取,并且`sys.prefix`和`sys.exec_prefix`都被设置到那个目录——这是由[这个函数控制的,这里是](https://github.com/python/cpython/blob/32f55d1a5de66f9a86964fc0655d7a006a9d90b9/Lib/site.py#L495)。
**使用 sys.prefix 和 sys.exec_prefix 设置 sys . path**
Python 现在导入模块[站点](https://docs.python.org/3/library/site.html#module-site)并运行其[主函数](https://github.com/python/cpython/blob/32f55d1a5de66f9a86964fc0655d7a006a9d90b9/Lib/site.py#L587)。根据文件[这里的](https://docs.python.org/3/library/site.html#module-site),这样做如下:
*“它从头部和尾部开始构建多达四个目录。对于 head 部分,它使用 sys.prefix 和 sys.exec _ prefix 跳过空磁头。对于尾部,它使用空字符串,然后是 lib/site-packages(在 Windows 上)或 lib/pythonX。Y/site-packages(在 Unix 和 macOS 上)。对于每个不同的头尾组合,它会查看它是否引用现有目录,如果是,则将其添加到 sys.path 中,并检查配置文件的新添加路径。*
实际上,这会将以下内容添加到 sys path 中:
/Users/jamisonm/.pyenv/versions/3.9.7/lib/python39.zip
/Users/jamisonm/.pyenv/versions/3.9.7/lib/python3.9
/Users/jamisonm/.pyenv/versions/3.9.7/lib/python3.9/lib-dynload
/Users/jamisonm/.pyenv/versions/3.9.7/lib/python3.9/site-packages
仅此而已。现在我们有了我们的`sys.path`变量。就像使用`$PATH`和操作系统一样,python 从上到下遍历`sys.path`变量,寻找匹配的模块和包名,当找到一个匹配的模块和包名时,它就执行那段 python 代码。与`$PATH`的唯一区别是 python 在遍历`sys.path`之前首先检查本地调用目录以寻找匹配的模块(下面将详细介绍)。

图片来自[https://xkcd.com/1987/](https://xkcd.com/1987/)
## 什么是“站点包”?
这是你从 PyPI 下载的所有第三方软件包的去处(通过 pip,conda,poetics 等)。一旦我们以这种方式看待事情,管理一个环境并确保所有依赖关系都在正确的位置的想法就变得非常琐碎。一旦我们有了 python 可执行文件的正确位置,我们只需确保遵守规则,并将其放入相应的`site-packages`文件夹中。当我们在那个环境中键入`python`时:
* 操作系统遍历`$PATH`变量,找到并执行 python 的正确版本
* 然后,操作系统将这个位置交给 python 可执行文件,并初始化类似于`sys.executable`的东西
* python 还基于上面的公式创建了`sys.path`变量,指定在相应的`site-packages`文件夹中查找包
## 在本地安装一个包怎么样?
如果我们想用特定版本的 python 和相关的依赖项(我们可以从 PyPI 中获取)来建立一个环境,上面的方法非常有用。但是那些不在 PyPI 上的东西呢?或者,如果我们从 PyPI 中取出一个包并修改它(可能是为了改进它)然后想要使用它呢?
对此,python 有另一种解决方案— **。pth 文件**。从 python 文档[这里](https://docs.python.org/3/library/site.html#module-site):
*“路径配置文件是一个文件,其名称的格式为 name.pth,并且存在于上述四个目录之一中;它的内容是要添加到 sys.path 中的附加项(每行一项)。不存在的项永远不会添加到 sys.path 中,并且不会检查该项是否引用了目录而不是文件。任何项都不会被多次添加到 sys.path 中。空行和以#开头的行将被跳过。执行以 import(后跟空格或制表符)开头的行。*
因此,为了在本地编写一些代码,并确保当我们在某个地方启动 python 时,我们可以导入我们需要的代码:
* 将代码写在名为`module_name.py`的文件中
* 在`sys.path`(通常是`site-packages`)的某个目录中添加一个名为`module_name.pth`的文件,该文件包含我们的模块所在的目录
**这正是当您运行类似** `**poetry install**` **或** `**pip install . -e**`的程序时所发生的事情——一个. pth 文件被添加到`site-packages`文件夹中,该文件夹将该位置添加到`sys.path`变量中。
让我们用一个测试模块来测试一下。在一个新目录中创建一个名为 test 的文件夹。在我的主目录中,我们有`mkdir ~/test`。然后,我们可以将以下文件添加到名为`datetime.py`的目录中(故意命名为与 python 标准库中的`datetime.py`文件冲突)。总而言之,我们有以下几点:
mkdir ~/test
cd test
touch datetime.py
然后将以下内容添加到`datetime.py`:
def hello_world():
print('hello world')
return
最后,我们需要将我们的`test.pth`文件添加到我们当前环境的`site-packages`目录中。如上所述,使用 pyenv(在我使用 python 版本 3.9.7 的全局设置中),我的目录位于`/Users/jamisonm/.pyenv/versions/3.9.7/lib/python3.9/site-packages`。然后,我们可以添加以下内容:
cd /Users/jamisonm/.pyenv/versions/3.9.7/lib/python3.9/site-packages
echo "/Users/jamisonm/test" > test.pth
其中最后一个命令是一个命令行程序,创建一个包含我们想要的目录的文本文件。现在,如果我们启动 python 并运行 sys.path,我们会看到以下内容:
import sys
print('\n'.join(sys.path))
/Users/jamisonm/.pyenv/versions/3.9.7/lib/python39.zip
/Users/jamisonm/.pyenv/versions/3.9.7/lib/python3.9
/Users/jamisonm/.pyenv/versions/3.9.7/lib/python3.9/lib-dynload
/Users/jamisonm/.pyenv/versions/3.9.7/lib/python3.9/site-packages
/Users/jamisonm/test
增加了底部条目。这是因为当 python 扫描`sys.path`中的前 4 个位置时,它发现了一个`.pth`文件(我们创建的`test.pth`文件),并相应地将其添加到最后的`sys.path`。重要的是,这意味着:
* python 将在`/Users/jamisonm/test`中查找之前,首先在其他位置搜索匹配的包或模块名
* 在此之前,python 实际上在当前目录中搜索匹配项
这意味着,如果我们从`/Users/jamisonm/test`开始运行以下命令,我们会看到:
import datetime
datetime.date.today()
Traceback (most recent call last):
File "
AttributeError: module 'datetime' has no attribute 'date'
因为我们在`/Users/jamisonm/test`中定义的`datetime.py`模块优先于其余的`sys.path`——包括在`/Users/jamisonm/.pyenv/versions/3.9.7/lib/python3.9`中定义的 python 标准库模块`datetime.py`。但是,如果我们移出`test`目录并重复,我们会看到:
import datetime
datetime.date.today()
datetime.date(2021, 11, 5)
import sys
print('\n'.join(sys.path))
/Users/jamisonm/.pyenv/versions/3.9.7/lib/python39.zip
/Users/jamisonm/.pyenv/versions/3.9.7/lib/python3.9
/Users/jamisonm/.pyenv/versions/3.9.7/lib/python3.9/lib-dynload
/Users/jamisonm/.pyenv/versions/3.9.7/lib/python3.9/site-packages
/Users/jamisonm/test
即 python 首先找到标准库`datetime.py`模块(它包含`datetime.date.today()`函数),即使`/Users/jamisonm/test`仍然在我们的`sys.path`中。
## 结论
类似于当您通过从上到下遍历您的`$PATH`变量来键入`python`时,您的操作系统如何确定运行哪个 python 版本,python 创建并搜索模块搜索路径(主要通过创建`sys.path`变量)来定位已安装的包,并且如果我们有名称冲突,哪个应该优先。
一旦我们以这种方式理解了事情,环境和依赖性管理的主题就变得容易了。通过消除设置这些变量的人工工作,让我们的生活变得更轻松的工具非常有用,但基本过程不一定非常复杂:
* 操作系统找到要使用的正确 python 版本
* 使用这些信息,python 定义了`sys.path`来创建模块搜索路径
* 。pth 文件可以添加到这些位置之一,以将本地模块的位置添加到[模块搜索路径](https://docs.python.org/3/tutorial/modules.html#the-module-search-path)的末尾
像 conda 和 pyenv+poem 这样的工具就是这样做的——它们通过创建、更改和管理这些 python 可执行文件、相应的站点包目录和任何其他文件,使我们能够轻松地掌握这些步骤。pth 文件添加到它们中以简化本地开发。
# Python 在项目评估中的应用:从数据上传到分析
> 原文:<https://towardsdatascience.com/python-applied-to-project-evaluations-from-data-upload-to-analysis-1090647c7832?source=collection_archive---------23----------------------->
## 这里介绍的 Python 片段应用于评估研究的一组数据框架,举例说明了关键的分析步骤和可复制的编码解决方案,可用于其他评估

来源于 [Pixabay](https://pixabay.com/photos/data-computer-internet-online-www-2899901/)
# 编程语言与社会科学相遇
Python 为计算科学提供了广泛的应用。另一方面,在任何形式的研究中,确定最适当的方法、变量和伦理标准仍然是个人的责任。某种[希波克拉底誓言](https://en.wikipedia.org/wiki/Hippocratic_Oath)应该让研究人员承担责任(提到[就是数据科学家](/hippocratic-oath-for-data-scientists-407d2db15a78)),这意味着每个数据点都应该遵循坚实的理论或假设,有助于从多个科学的定性角度进行充分的三角测量。在社会、卫生和人道主义部门进行干预时尤其如此,因为背景驱动因素相互关联,对每个人都有不可预测的影响。这是大画面和小画面之间的舞蹈。
由于任何适用于社会和行为变化的指标都带有一大堆与可比性和结论性相关的问题,人们应该小心地庆祝一种可以快速得出结论和一般性发现的编程语言。记住这一点,对定性和定量证据进行三角测量的努力可以受益于像 Python 这样的面向对象语言来处理和分析结构化和非结构化证据。已经有许多数据科学专家和组织在这些领域努力改进实践的[例子](https://www.datakind.org/chapters/datakind-uk),这是一个非常令人鼓舞的趋势。

由作者组合来自[源 1、](http://www.sump-challenges.eu/content/monitoring-and-evaluation) [源 2](https://upload.wikimedia.org/wikipedia/commons/c/c3/Python-logo-notext.svghttps://upload.wikimedia.org/wikipedia/commons/c/c3/Python-logo-notext.svg) 的图像
## 目的
我觉得有必要开始根据我的实践经验撰写一系列文章,这样我就可以为其他人提供有用的代码片段。我将依靠来自现实世界评估的证据,并提出一步一步的描述。尽管一些度量标准是特定于特定领域的,但是每个例子的目的都是概述一个过程和可复制的代码,这些代码也可以用于其他领域——这取决于变量的类型和模型需求。这也是为了就更有效的编码解决方案和我的自学进行对话,这样我就可以在学习和分享的同时成长。
## 启动的先决条件
1.访问支持笔记本电脑的平台,该平台允许您复制粘贴代码并查看相关输出
2。检查你的数据是否在。csv 格式,并且证据是按照系统的列-行结构排列的(特别是如果是手工的话)
3。确保你的机器上安装了最新版本的 Python,并且相关包的安装已经提前完成
4。对数据分析和逻辑思维的基本理解以及对问题和提问的开放态度:)
# 步骤 1:上传和快速清理
第一步是上传相关的包,这些包提供了将 Python 进一步提升为可解释性资产的能力:通过集合访问和编程语言的共同创造,这是真正的突破。如果安装了所有相关的包,这是一个相对容易的步骤。代码中见:[熊猫](https://pandas.pydata.org/)、 [numpy](https://numpy.org/) 、 [seaborn](https://seaborn.pydata.org/) 、 [scipy](https://www.scipy.org/) 和 [matplotlib](https://matplotlib.org/) 。所有这些在数据科学应用中都非常重要,它们都有不同的用途,但有很大的互补性。
上传完这些包后,就该上传数据帧了。当然,如果我们谈论的是成千上万的数据帧,这个片段的适用性就很低。然而,如果我们在同一个干预领域只有一些重复的数据收集,那么我们可以将它们视为不同的实体。 **info** 命令给我关于条目数量、数据类型和内存使用的信息。已经有三个非常有趣的方面需要考虑。在这种情况下,3 个数据帧(dfBase、dfMid、dfEnd)被上传,对应于跨越超过 1000 个回答者的大样本的连续顺序的一个数据收集周期。
一旦上传了数据帧,快速清理通常包括移除所有没有条目的行或看起来重复的行。对于数值变量,频繁的数据质量检查是用较低和较高的五分位数值替换极端异常值。从 Pythonic 的角度来看,离群值的建议片段相当简洁,但是数据清理可以更加复杂,特别是我们为缺失值提供的含义。这是数据科学中最大的难题之一,尤其是在涉及[预测分析时。](https://www.researchgate.net/publication/333075475_Missing_Data_Problem_in_Predictive_Analytics)本身就是一个话题。
# 步骤 2:定位变量并创建虚拟模型
在深入分析之前可以采取的其他相关行动是,如果出于推理目的需要,可以创建虚拟模型。一个最常见的哑元是位置,因为它们作为独立的条件,它们可以被视为独立的事件,并作为控制因素包含在回归中。这种情况下的关键命令是`pd.get_dummies.`
另一项行动是通过`value_counts(dropna=False)`中的分类规范验证是否有任何相关变量`is_unique`条目或频率。我发现另一个非常有用的命令是`columns.get_loc("Name Variable")`,因为它帮助我定位数据帧中变量的位置。有了变量的位置,提取/分割证据子集变得更加容易。
# 步骤 3:关键指标的分析
就个人而言,这是我更喜欢的步骤,因为此时分析变得更加迭代,对变量行为的理解更加具体。我们应该把任何形式的分析都视为进化,从而产生新的假设和问题,进一步丰富模型或使计算结果更加准确。下面的代码片段提供了不同层次的规范。例如,我希望调整我的视觉输出`plt.rcParams`的大小,并确定我需要可视化多少情节`fig, ax = plt.subplots(nrows=1, ncols=1)`。一旦定义了这些布局细节,通过连接`pd.concat`相关变量进行比较,我就可以获得相关结果指标的`boxplot`值分布。我们可以清楚地看到从基线到终点的进展,这对应于这一特定指标的改进。
另一种分析信息的方法是了解关键指标在一个数据框架中的分布和相关性。在这种情况下,我考虑的是终点的[食物消耗分数](https://inddex.nutrition.tufts.edu/data4diets/indicator/food-consumption-score-fcs)和结果 1 和 2。这两个指标是由知识、态度和实践( [KAP](https://www.spring-nutrition.org/publications/tool-summaries/kap-survey-model-knowledge-attitudes-and-practices) )的各种子成分组成的复合指标。我使用命令`rename`确保视觉输出清晰,使用`next_dffull_label = iter(Outcomes).__next__`为我的`ax`图的`annotate`创建标签。其次,在`corrdot`和`corrfunc`函数中,我定义了[皮尔逊相关系数](https://en.wikipedia.org/wiki/Pearson_correlation_coefficient)到`stats.pearsonr`的标签和 p 值的参数。
第二步,我用命令`sns.PairGrid`激活 seaborn,这个命令应用于不包含任何零值的结果数据帧。`map_lower`、`map_diag`、`map_upper`、`map_upper`的各种参数的功能是将图形的各种元素定位在预定位置。`fig.subplots_adjust`在`axes.flatten`、`set_xlabel`和`set_ylabel`的基础上进一步布局调整。修改这些参数将告知标签和空间方面的最终视觉输出。结果是一个视觉输出,它指定了关键的相关性(例如饮食多样性和食物消费之间的相关性),并概述了每个值的分布及其相关性。它可以提供一个初始的图像来识别要包含在推理模型中的变量。
# 步骤 4:切片和分解分析
根据所考虑的指标,如果结果值是综合的,则有不同的分解级别。在这种情况下,我们可以提示一个`InteractiveShell`来查看值如何跨越相关的度量从基线发展到终点。在这种情况下,对知识、态度和做法的变化的分类是通过对每个方面的相关组成部分求和并对结果进行平均来呈现的。对相同值的进一步分解是通过使用分类规范过滤数据帧并应用总和的平均值来完成的。该值可以是`mean`、`median`或`sd`偏差,这完全取决于分析的目的。
# 最后的考虑
你在这篇博客中看到的是一系列片段,可以指导你用几个步骤来准备数据框架和分析关键指标。每个步骤的复杂程度可能会因数据帧的数量、形状、大小和长度而有很大差异。这些方面决定了将证据转换成可用于生成视觉或计算输出的格式的一组决策和方法。一般来说,社会科学依赖于数据框架,这些数据框架相对较小,但有时是手动生成的,并且富含需要清理和特殊处理的字符串(定性)变量( [NLP](https://journals.sagepub.com/doi/full/10.1177/1609406919887021#:~:text=Qualitative%20data%2Danalysis%20methods%20provide,algorithms%20to%20analyze%20textual%20data.) )。在自然科学的背景下,数值矩阵形式的数据帧更为常见,信息的迭代可能会很频繁,因此特定算法对于解决计算问题变得很有必要。
尽管这种明显的分歧根植于教育的结构方式,但当今世界正迫使我们采取整体解决方案,克服学科孤岛。编程提供了一个桥梁,从一个超专业的学科语言到一个更跨学科的方法,旨在发展综合知识和研究方法。跨学科研究的一个[定义](https://research.ncsu.edu/rdo/2020/08/the-difference-between-multidisciplinary-interdisciplinary-and-convergence-research/)是:
> “由团队或个人进行的一种研究模式,它整合了来自两个或更多学科或专业知识主体的信息、数据、技术、工具、观点、概念和/或理论,以促进基本理解或解决其解决方案超出单一学科或研究实践领域范围的问题。”
Python 是一种编程语言,通过[编译方法](https://nedbatchelder.com/blog/201803/is_python_interpreted_or_compiled_yes.html)来实现更高程度的解释。任何二进制逻辑从 a 代码(字节码)到可以跨领域应用的复杂编程语言的演变,确实描述了我们这个时代的一个巨大机遇。对这些抽象语言的访问越来越多地激励专家在分析信息时模糊自然描述和形式描述之间的界限。向相似的语言靠拢将使来自不同学科的个人能够一起工作,从多个、综合、三角化的角度和技术来看待基于研究的证据。
# Python Assert 语句—你需要知道的一切在 5 分钟内解释完毕
> 原文:<https://towardsdatascience.com/python-assert-statement-everything-you-need-to-know-explained-in-5-minutes-4b13c39c8391?source=collection_archive---------15----------------------->
## 原因、时间和方式—立即学习 Python 中的 assert 语句。

[Jason Briscoe](https://unsplash.com/@jsnbrsc?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 在 [Unsplash](https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 上的照片
作为一种解释型和动态类型的语言,Python 有着难以预料和难以跟踪的错误的坏名声。这份声明可能会让你少一两次紧张。这是一种捕捉不应该在早期发生的事情的常用方法,最有可能发生在函数的开始。
这是一个简单的布尔表达式,用于检查条件是否返回 true。如果是这样,什么都不会发生。如果没有,脚本会用一条可选消息引发一个`AssertionError`。
请记住,断言不能代替异常处理。你应该使用`try except`块来防止在某个时候必然会发生的任何错误。对不应该发生的事情使用断言。
不想看书?看看我关于这个主题的视频:
# 如何使用 assert?
正如您所想象的,Python 内置了`assert`关键字。它后面必须跟一个条件,如果条件的计算结果为 false,您可以选择打印一条消息。
下面是语法:
assert
例如,如果梅根未成年,她就不能进入俱乐部:
assert age > 18, 'Go home, Megan.'
让我们暂时离开 Megan,关注一个更简单的例子。我们有一个设置为 1 的变量`x`。让我们断言它,看看会发生什么:
x = 1
assert x == 1
以下是输出结果:

图片 1 —断言基础知识(1)(图片由作者提供)
实际上什么也没发生。这是因为条件评估为真,所以没有引发错误。让我们稍微修改一下代码:
x = 1
assert x == 2
这次的输出有所不同:

图片 2-断言基础(2)(图片由作者提供)
您得到了一个`AssertionError`,因为条件评估为假— `x`是 1,所以它不可能是 2。控制台输出没有提供任何解释,这可能不是您想要的。您可以指定当`AssertionError`出现时打印的错误信息:
x = 1
assert x == 2, 'X should be 1'
以下是输出结果:

图片 3-断言基础(3)(图片由作者提供)
*简单,对吧?*应该是。实际上,你更有可能在函数的开头写`assert`语句。让我们看看接下来该怎么做。
# Python 函数中的断言
让我们声明一个简单的 Python 函数。它将接受一个列表作为参数,对其求和,然后以浮点形式返回总和。有两个问题需要尽早发现:
1. **列表是否传递给函数?**你可以在 Python 中使用类型提示来指示应该传递哪种数据类型,但是没有人阻止你传递字典而不是列表。暗示只是暗示。
2. **列表是空的吗?显然,你不能对一个空列表求和。嗯,调用`sum([])`返回 0,但这不是你想要的。**
这就是为什么您将有两个断言语句,后面是一段用于汇总列表元素的代码。如果这两个条件都不为真,Python 解释器将不会执行该代码:
def sum_list(lst: list) -> float:
assert type(lst) == list, 'Param lst
must be of type list!'
assert len(lst), 'The input list is empty!'
total = 0
for item in lst:
total += item
return total
现在让我们试着调用`sum_list()`并传入一个字典:
lst_sum = sum_list(lst={'a': 1, 'b': 4})
print(lst_sum)
事情是这样的:

图 4 —函数中的断言(1)(作者图片)
正如您所希望的——没有向函数提供列表,因此将一个适当的断言错误打印到控制台。
*传一个空单怎么办?*
lst_sum = sum_list(lst=[])
print(lst_sum)

图 5 —函数中的断言(2)(图片由作者提供)
你仍然得到了`AssertionError`,但是这一次有了不同的消息。
最后,让我们通过提供一系列数字来验证该函数是否正常工作:
lst_sum = sum_list([1.11, 2.21, 3.14])
print(lst_sum)

图 6 —函数中的断言(3)(图片由作者提供)
现在你知道了,它像广告宣传的那样工作。
# 如何忽略运行时的断言?
`assert`语句的特别之处在于,您可以在运行脚本时忽略它们,而无需修改代码。
以下是如何正常运行 Python 脚本:
python3 script.py
如果您有`asserts`语句并且想忽略它们,只需在命令中添加`-O`:
python3 -O script.py
这里有一个我们的`assertion.py`文件的例子,里面有下面的代码(你之前已经声明了`sum_list()`函数):
lst_sum = sum_list(lst={'a': 1, 'b': 4})
print(lst_sum)

图 7 —忽略运行时的断言(作者图片)
如您所见,引发了`TypeError`,因为传递的是字典而不是列表。
# 应该使用断言语句吗?
我以前已经说过了,但是我要再重复一次——断言不能代替`try except`块。对于不可避免会发生的错误,您应该总是使用`try except`。
尽管如此,`assert`语句至少有两个优点。
`**assert**` **比** `**if**`更紧凑,可读性更强。写`assert <condition>, <message>`比写`if <condition>: <message>`容易。至少它会为您节省一行代码。此外,通过写`assert`,你知道你在检查不应该发生的情况。`if`只是一个一般性的检查。
`**assert**` **语句在运行时可以忽略**。你已经看到如何做到这一点,所以我不会重复自己。
总而言之,`assert`编写起来更快,也更容易与常规的条件检查区分开来。好好使用它们,但不要把它们作为异常处理的唯一方法。
*你们觉得断言语句怎么样?它们是你的 Python 项目的一部分吗,还是你经常使用 if 和* `*try except*` *?*。请在下面的评论区告诉我。
*喜欢这篇文章吗?成为* [*中等会员*](https://medium.com/@radecicdario/membership) *继续无限制学习。如果你使用下面的链接,我会收到你的一部分会员费,不需要你额外付费。*
<https://medium.com/@radecicdario/membership>
# 保持联系
* 注册我的[简讯](https://mailchi.mp/46a3d2989d9b/bdssubscribe)
* 在 YouTube[上订阅](https://www.youtube.com/c/BetterDataScience)
* 在 LinkedIn 上连接
# Python 初学者突破(数据类型)
> 原文:<https://towardsdatascience.com/python-beginner-breakthroughs-data-types-b2a9f8b85836?source=collection_archive---------43----------------------->
## 在开始构建数据科学项目之前,编码是必须的,在 Python 中,理解数据类型的关键属性和差异将使学习编码更加顺利。

马库斯·斯皮斯克在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上的照片
学习为数据科学编码可能会令人望而生畏,尤其是如果你以前没有任何编码或编程语言的经验和/或理解。更糟糕的是,如果大多数人像我一样,那么真正理解像数据类型和结构这样的简单概念,可能是你不去做的事情,而是继续跋涉到更重要的事情,比如一头扎进 TensorFlow、Keras 和 Scikit-Learn。这种方法的愚蠢之处在于,每当你遇到问题并在执行代码时出现类型错误时,你很可能会复制整个错误语句并将其粘贴到 stackoverflow.com 中,希望得到一个简单的解释。花些时间熟悉并理解类型和结构的基础知识,可以帮助您处理在遇到更复杂的代码时会遇到的所有棘手的细微差别。
本文将介绍各种数据类型的基础知识,并希望以这样一种方式来解释它,使那些很少或没有编码背景的人容易理解和消化,以及那些对该主题进行了很好的复习的人。正如前面提到的理解,数据类型所代表的内容背后的关键方面以及它们的关键属性/特性将有助于理解在您的代码中处理数据的方式。
## 运算符和比较:
要注意的关键点是,运算符和比较允许您定义逻辑语句来执行、运算和/或比较两个不同的变量。布尔运算符包括:*、 ***或*** 、 ***not。****
**
*如何使用运算符及其结果的表格*
*此外,比较操作允许您在对象或变量之间创建条件逻辑语句。下表详细介绍了 Python 中可用的大多数比较操作。*
**
*比较运算符的类型和含义*
**
*扬·安东宁·科拉尔在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍摄的照片*
# *数据类别:*
*数据类型可以分为几个关键类别:*、 ***【序列】*** 、 ***文本*** 、 ***集合*** 和 ***映射*** (注意:还有一些其他的,但本文的重点将是提到的那些通常最常用的)。**
****
**苏珊·霍尔特·辛普森在 Unsplash[上的照片](https://unsplash.com?utm_source=medium&utm_medium=referral)**
## **数字类型:**
**顾名思义,Python 中的数字类型通常以数字形式表示。三种数值类型是:**
* ****int (integer) —** 整数就像它们的数学表示一样,代表整数(或整数)。这意味着没有小数部分。这意味着整数也有无限的精度,所以当你对整数进行计算时,结果就是精确的答案。**
* ****float —** 或有时被称为浮点数(或 C 等语言中的 double)用带小数的数字表示。关于浮点数要注意的一个关键点是,因为它们有固定的小数位数(或精度),所以你可以解决问题并有浮点错误。这意味着,假设你有一个精确到小数点后 10 位的数字,如果你执行一系列的运算,你可能会得到与真实值的微小差异。跟踪您的精度以及您希望如何表示它们是确保数据按照您的期望进行处理的关键。**
* ****复数—** 顾名思义,它代表既有实部又有虚部的复数。**
**下面是可以应用于数值类型的常见操作列表。**
****
**常见数字运算符**
****
**照片由 [Arisa Chattasa](https://unsplash.com/@golfarisa?utm_source=medium&utm_medium=referral) 在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍摄**
## **序列类型:**
**序列类型表示一种内部可以包含多个值的数据类型。各种序列类型可以通过其携带数据的属性来区分。序列的另一个关键特性是它们是可迭代的,这意味着您可以处理序列中的每一项。**
* ****列表**—是最常见的序列数据类型之一。列表是可以包含其他数据类型的各种值的对象。这意味着您可以在一个列表中构建一个整数、浮点数、字符串(或文本)或它们的组合的列表。关于列表需要注意的一点是,它们是可变的,这意味着列表中的值和项数可以改变。密切关注列表上正在执行的流程对于确保维护数据的完整性至关重要。列表通常也表示为方括号内的项目,即[列表项目 1、项目 2、项目 3 等]。在我之前写的一篇文章中可以找到一种使用列表理解快速建立列表的方法([列表理解](/python-beginner-breakthroughs-list-comprehensions-4090f26b9f50)**
* ****元组** —与列表非常相似的序列类型是元组。列表和元组之间的关键区别在于元组是不可变的,这意味着它们不能被改变。这对于存储可以表示为常量并在算法中使用的项非常有用,而不用担心元组中的值会受到影响。元组通常用括号表示(元组项目 1、项目 2 等)。**
* ****范围** —范围项目是一种特殊类型的序列,通常有助于定义(顾名思义)要查看的范围或间隔。通常,当定义一个范围时,您可以通过记录您要调查的*开始*(从哪里开始)、*停止*(从哪里结束)和*步骤*(每个项目之间的实例数)来定义另一个序列数据类型的范围。当定义如何在一个具有更复杂逻辑的序列中循环时,这些就很方便了。**
****
**常见序列类型运算符**
****
**由[拉斐尔·沙勒](https://unsplash.com/@raphaelphotoch?utm_source=medium&utm_medium=referral)在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍摄的照片**
## **字符串(文本)类型:**
**字符串代表了我们在书写中使用的字符。对于那些希望深入研究数据科学中的 NLP 的人来说,处理和理解 Python 中字符串的工作方式是至关重要的。字符串被表示为一个类对象,有许多方法可以应用于它们。这基本上意味着当您导入和/或创建字符串数据类型时,可以应用特定于字符串数据的附加方法(过程)。还要注意的是,字符串是不可变的序列。字符串可以用单引号或双引号来表示,比如:“字符串项”或“字符串项”,两者是等价的。**
## **设置类型:**
**集合是 Python 中的一种数据类型,它包含无序且不同的项目。这意味着集合不能包含任何重复项。集合对于确定成员测试(是另一个项目中的一个项目)、从其他序列类型中删除重复项以及执行数学运算(如交集、并集、差集和对称差集)非常有用。由于集合是无序的,所以无论何时调用集合,它都不会为其中的数据保留索引或位置。这意味着对集合中第一个项目的两次连续调用会产生两个不同的项目,尽管理论上它是从同一个对象中提取的。因此,许多序列行为对集合不起作用,如切片和索引。在集合类型中,有两种主要的集合类型:集合和冷冻集合。关键的区别在于,集合是可变的(创建后可以更改),而 frozensets 是不可变的,这使得它们是可散列的(可以转换为一个在其生命周期中保持不变的散列值)。集合通常用圆括号表示,比如:(set item1,item2 等等)。**
****
**照片由[皮斯特亨](https://unsplash.com/@pisitheng?utm_source=medium&utm_medium=referral)在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍摄**
## **映射类型:**
**字典,简称 dict,是非常强大的映射类型,允许在字典中保存的两个条目之间建立某种关系连接。字典由键和值对组成。一种很好的思考方式是,对于一个给定的键,有一个层次或结构,其中可以有一组值。字典是可变的,所以它们可以在创建后改变,键/值可以由各种类型组成,比如列表、集合和元组。字典可以通过多种方式构建:迭代,通过字典理解(类似于列表理解逻辑),以及通过初始化和添加单个条目。在学习 Python 之前,我从未遇到过像 dictionaries 这样的类型,但是在熟悉它之后,它所包含的效用是巨大的。嵌套字典是使其更加强大的另一个很好的例子,对于字典中的键/值对,值实际上是嵌套在其中的另一个字典。嵌套字典的一个例子是马尔可夫链概率,比如猜测天气。关键字可以是各种天气条件(即晴天、阴天、雨天),值将代表新的字典,关键字是未来天气状态,值是未来天气状态发生的概率。下面的代码块将详细说明这一点。**
{('sunny'): {'sunny': 0.3333333333333333, 'cloudy': 0.3333333333333333, 'rainy': 0.3333333333333333}, ('rainy'): {'rainy': 1.0}, ('cloudy'): {'sunny': 0.5, 'rainy': 0.5}}
**如何理解这一点是,对于第一个键或状态,“sunny”表示“sunny”状态的值的嵌套字典表示未来状态和概率。所以如果一天是晴天,第二天是晴天的概率是 0.33,是阴天的概率是 0.33,是雨天的概率是 0.33。对于多雨的状态,唯一的未来状态和概率。如果你仔细想想,那是相当强大的。注意:我可能会在将来做一个更完整的字典综述。**
****
**本文中提到的 Python 数据类型的基本属性**
## **包裹**
**理解各种数据类型以及如何正确使用它们将使项目的编码部分变得容易得多。您也将很好地理解从哪里开始故障诊断,因为当您遇到类型错误时,您可以理解它在说什么。像往常一样,如果你有任何事情想提出来,请不要犹豫,如果你有一些你希望在未来看到的话题,请在下面的回复中注明。**
**Python(以及您使用的任何其他库)中的一个很好的资源是 Python 文档,可以在这里找到: [Python 3.9.1 文档](https://docs.python.org/3/)**
# Python 初学者突破(词典)
> 原文:<https://towardsdatascience.com/python-beginner-breakthroughs-dictionaries-469597c96455?source=collection_archive---------28----------------------->
## 对于数据科学工作来说,一种非常通用的数据类型是 dictionary,它提供了大量的实用工具,允许在自身内部建立内置的关系和层次结构。

照片由[皮斯特亨](https://unsplash.com/@pisitheng?utm_source=medium&utm_medium=referral)在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍摄
我不得不承认,在学习 Python 和计算机科学之前,我根本不知道这种数据类型的存在。当我最终接触到它并看到它的巨大潜力时,字典很快就成为了我因其结构和多功能性而钦佩的数据类型之一。本文旨在快速概述什么是字典,如何使用字典的一般规则,使用字典的实际方法,然后提供一些快速假设的例子,说明在哪里可以使用字典。
在其他编程语言中通常被称为“关联存储器”或“关联数组”,字典的关键原则是通过父子关系映射两个变量。在这种关系中,语法上称为键的父节点和称为值的子节点表示数据类型中的键-值对。该字典最常见的用法是拥有一组可以被调用的键,然后生成与该键相关联的匹配值。
下图是代表键和值对的美国一些州和城市的字典的基本示例。请注意,对于每个值,都有一个与之关联的独特的键值对。

用一些美国州(作为关键字)和城市(作为关键字的值)来可视化表示字典。作者图片
## 词典规则:
使用词典时要注意一些基本规则,这将使使用词典的学习曲线不那么陡峭。
* 字典键可以是任何不可变的类型,比如字符串、数字或元组(注意:包含不可变对象的元组不可作为键接受)
* 字典本身是可变的,这意味着您可以在创建时对条目进行更改(添加/删除/修改)
* 字典中不能存在重复的键,因为字典本身是跨键索引的。*如果你给一个已经存在的键赋值,你将覆盖先前的值*。
* 因为字典是跨关键字索引的,所以不能使用 list(基于序列的)命令,例如。追加&。延伸或切割按键
* 项目的顺序被保留(因为索引是跨键完成的),新项目被添加到末尾。删除项目时也是如此。
* 字典可以嵌套,以提供两个独立项目(或字典)之间的额外关系
# 如何…使用字典:
## 建立字典
可以使用 dict()函数添加条目,如下所示,其中定义的键和值对几乎就像一个元组。
d = dict([
(
(
.
.
.
(
])
另一个建立字典的有效方法是使用理解。我有另一篇文章,讨论了列表理解逻辑和结构,这些相同的想法可以应用到字典中。你可以在这里找到列表理解文章[。](/python-beginner-breakthroughs-list-comprehensions-4090f26b9f50)
可以想象,就像列表一样,有多种方法来构建列表,比如遍历序列或范围。
## **遍历字典**
您可以用各种方式遍历字典,特定的方法将允许您访问或调用所需的键、值或键值对。最简单的方法是简单地使用字典的 for 循环进行迭代,如下所示。
a_dict = {'a': '1', 'b': '2', 'c': '3'}
for key in a_dict:
print(key)
...
a
b
c
Python 字典类型内置了 3 个特别强大的方法,可以根据您想要从字典中提取的内容进行有针对性的迭代。这些方法是:。keys(),。values(),。项目()
## 你想要钥匙吗?
* 。keys()方法只返回字典的键,下面是一个使用它的例子。注意,这类似于遍历字典
a_dict = {'a': '1', 'b': '2', 'c': '3'}
for key in a_dict.keys()
print(key)
...
a
b
c
## **所以你希望键和值成对出现?**
* 。items()方法返回表示为元组的键值对的视图对象。请注意下面方法的使用以及作为元组对(key,value)的结果输出。请注意第二个例子中的语法。
.items() Example 1
a_dict = {'a': '1', 'b': '2', 'c': '3'}
for item in a_dict.items()
print(item)
...
(a,1)
(b,2)
(c,3)# .items() Example 2
a_dict = {'a': '1', 'b': '2', 'c': '3'}
for keys, value in a_dict.items()
print(key, value)
...
(a,1)
(b,2)
(c,3)
## 所以你只想要值?
* 最后一个特定于字典的方法用于调用字典的值,它是。values()方法。类似地,注意方法调用和输出
a_dict = {'a': '1', 'b': '2', 'c': '3'}
for value in a_dict.values()
print(value)
...
1
2
3
## 如何筛选字典?
具有适当方法的条件逻辑可结合使用,以根据所述条件构建所需的新字典。在下面的示例中,基于与值相关的条件过滤新字典。
a_dict = {'a': '1', 'b': '2', 'c': '3'}
new_dict = {}
for key, value in a_dict.items():
if value <= 2:
new_dict[key] = value
new_dict
## 想根据字典条目做一些计算?
利用适当的方法和条件逻辑的组合将允许您基于字典中包含的条目执行计算。下面是一个玩具及其价格字典的例子,使用方法和条件很容易找出字典中所有项目的总价值。
toy_price_dict = {'toy car': '100.00', 'toy boat': '200.00', 'toy train': '300.00'}
total_cost = 0
for value in toy_price_dict.values():
total_cost += value
total_cost
600.0
当然,这是一个非常简单的例子,可以进行更复杂的计算,但这是你如何做的基本结构。
## 如何给字典排序?
使用内置的 sorted()函数对字典进行排序很容易。排序可以通过对键或值进行排序来完成,如下例所示。在字典中排序的第二个例子是另一种方式,但是使用一个助手函数来定义排序所依据的键。另外,请注意,在第二个示例中,您可以通过将“reverse”参数设置为 True 来颠倒排序顺序。
Sorting by keys example
toy_price_dict = {'toy car': '100.00', 'toy boat': '200.00', 'toy train': '300.00'}
for key in sorted(toy_price_dict):
print(key, '->', toy_price_dict[key])
toy boat -> 200.0
toy car -> 100.0
toy train -> 300.0#Sorting by values example 1
toy_price_dict = {'toy car': '100.00', 'toy boat': '50.00', 'toy train': '120.00'}
for value in sorted(toy_price_dict):
print(values)
120
100
50#Sorting by values example 2
toy_price_dict = {'toy car': '100.00', 'toy boat': '50.00', 'toy train': '120.00'}
def by_value(item):
return item[1]
for k, v in sorted(toy_price_dict.items(), key=by_value, reverse=True):
print(k, '->', v) ('toy boat', '->', 50.00)
('toy car', '->', 100.00)
('toy train', '->', 120.00)
## 整理你的字典
利用嵌套字典更加强大和有用。除了别的以外,嵌套字典可以作为未来概率的地图。这方面的一个例子如下所示:
next_day_weather = {('sunny'): {'sunny': 0.3333333333333333, 'cloudy': 0.3333333333333333, 'rainy': 0.3333333333333333}, ('rainy'): {'rainy': 1.0}, ('cloudy'): {'sunny': 0.5, 'rainy': 0.5}}
您可以在这里看到,无论您用什么方法预测未来状态的概率,嵌套字典都可以将未来状态作为键,将值作为概率。下图直观地演示了这一点。

代码块中嵌套字典的可视化表示形式。作者图片
希望这篇文章介绍了字典的用途和使用方式,并能帮你省去一些搜索如何完成与字典相关的简单任务的麻烦。如果你有任何问题或意见,或想看一些具体的东西,让我知道作为回应。谢了。
# Python 初学者突破(错误处理)
> 原文:<https://towardsdatascience.com/python-beginner-breakthroughs-error-handling-b04f85b4b543?source=collection_archive---------38----------------------->
## [入门](https://towardsdatascience.com/tagged/getting-started)
## 知道如何解释运行代码时收到的错误,可以让您立即诊断和解决问题!

[西格蒙德](https://unsplash.com/@sigmund?utm_source=medium&utm_medium=referral)在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍照
对于学习编程语言的人来说,理解语法是理解如何编写和实现代码的核心。一旦你开始理解语法和代码结构,关键的下一步是理解当你的代码不运行时你可能遇到的错误。彻底了解错误是什么以及为什么会出现错误,可以更快地诊断和排除错误。当我试图自学 Python 时,最初当我遇到错误时,我会将它复制并粘贴到 stackoverflow.com 的黑洞中,并在阅读其他人的问题时迷失。直到我参加了一门正规的大学编程课程,了解了各种错误类型及其与我的代码问题的关系,我才变得不那么依赖“谷歌搜索答案”。
## 避免危机
当遇到错误时,有一个更系统的方法来处理错误,这使得错误处理的故障排除部分变得容易得多。随着经验的积累和对 Python 语法的更好理解,您将学会快速识别您所得到的错误类型、根本原因以及如何修复它们。你最不想做的事情就是进入危机模式,在这种模式下,你很快就被时间限制住了,并且被错误所困扰。快速吸一口气,用一个清单来帮助解决这个问题,这就是我们在这篇文章中试图帮助你组装的东西。

马库斯·温克勒在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍摄的照片
## 故障排除流程
1. 确定错误类型。
2. 请阅读描述,了解引发该错误/异常的详细原因。参考 Python 文档和/或可能受错误影响的库文档是一个很好的做法。大多数文档会详细说明函数输入或调用输出的要求,以及它的类型和任何其他可能需要的要求。
3. 确定错误是否属于以下类型:a)语法/打字错误或 b)与流程/逻辑相关。
4. 如果错误与语法有关,请查看代码中错误发生的位置(通常回溯会突出显示错误的行和位置)以进行修复。
5. 如果错误与流程/逻辑相关,请检查错误类型和描述,然后查看错误发生的位置以及您要执行的代码的流程。很多时候,更好的方法是回顾一下您在代码的特定部分中试图完成的过程,看看您是否满足该功能或代码行的要求。
6. 一旦被诊断,返回并修复错误。
## 回溯剖析
下面我复制了一个简单的语法错误追溯,并会分解如何解释/理解它:

PyCharm IDE 中一个回溯的屏幕截图。作者图片
1. 错误类型—导致文件无法正确运行的特定错误
2.错误描述—证明所发生的错误或异常的描述的文本的简短描述。
3.错误位置——根据错误的类型(通常是语法错误),它将表示程序确定某些东西应该或不应该在哪里(即,缺少符号、圆括号、方括号等)
4.错误行位置——和上面一样,有时代码中有问题的行会被突出显示
5.错误追溯—现在,为了编译和运行代码,命令被发送,其他文件在后台运行,而您可能不知道,有时这真的很有帮助,如果您有更高阶的函数,和/或从其他文件调用函数,追溯可以让您深入了解当您点击 run 时在哪里启动了什么。
常见错误类型:

常见的典型错误类型表以及解释这些错误的简短描述。作者图片
## 包裹
为了更好地解决这些讨厌的错误问题,分解错误追溯,了解您收到的错误是什么,并确定它是快速修复/打字错误类型的问题,还是可能需要您重新考虑您试图完成什么的流程/逻辑问题。在一个完美的世界里,你越少依赖像 stackoverflow.com 和其他问题型论坛这样的网站,你就会过得越好。学习编程的一些最难的部分就像学习一门新语言,你希望能够在所有情况下都流利,而不是一直跑回到谷歌翻译。对错误有一个系统的方法会让你成为一个故障排除高手!
如果你对这篇文章有任何问题,请告诉我。此外,如果你想了解或解释一些事情,请回复!
# Python 初学者突破(函数)
> 原文:<https://towardsdatascience.com/python-beginner-breakthroughs-functions-2de3a6db77ac?source=collection_archive---------19----------------------->
## Python 编码的核心和灵魂…学习在 Python 中创建干净、简单、易读的函数是无价的。

沙哈达特·拉赫曼在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍摄的照片
我认为有人会说函数编程结构可能是编码中最重要的概念之一。功能的概念是一种抽象,允许用户能够简单地知道功能做什么以及生成输出所需的输入。这允许包含该函数的一小部分代码块充当进入该函数的数据的自动处理器,该处理器可由匹配输入的所需数据类型的所有输入重用。可以构建函数来过滤输入数据、执行数据转换、准备和显示绘图和可视化。本质上,函数功能允许将代码模块化为执行特定任务的更小的代码片段,并与其他函数组合来执行更复杂的任务。函数的效用是无限的,但是如果不知道如何从概念上设计、构建和记录一个函数,它很容易成为一个穷尽的任务。
一个函数如何工作的基本概念非常简单,但是理解如何为一个函数开发一个高质量的工作流是使它清晰、简单、易读和易懂的关键。我将重点介绍构建函数的基础知识、开发优秀函数的概念,以及如何用 docstrings 正确地记录函数。
## 基本函数语法
def arithmetic_mean(mean_data):
"""
This function will calculate the arithmetic mean of the list of the numbers inputted
Input: num_list - A list of numbers in either integer or float type
Output: The arithmetic mean of the list of numbers
""" if not mean_data:
return None
elif mean_data is not None:
add = sum(mean_data)
avg = add/len(mean_data)
return avg
功能的组成部分:
* 第 1 行:“def”是函数的定义调用,后面是函数名,括号中是函数的输入或参数。在上面的示例中,名称是“算术平均值”,输入是“平均值数据”。请注意,一个函数可以有多个用逗号分隔的输入。
* 第 2–8 行:docstring 是函数的注释部分(这是" "(或三撇号)" " " "内的所有项目),用于解释函数的作用以及输入和输出。
* 第 9–13 行:这些行是函数被调用时将执行的语句。
* 第 14 行:函数的返回调用,或者函数被调用后的输出会是什么。

[斯科特·格雷厄姆](https://unsplash.com/@homajob?utm_source=medium&utm_medium=referral)在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍照
## 功能开发和建设:
开发和构建一个函数的一个关键方面是确定它需要做什么,考虑构造它的最佳或最简单的方式,然后获取一组需求并用 Python 编码。在我学习编码的早期,我完全弄错了,看到一个问题,我需要构建一个函数,然后直接开始编码。这绝对不是处理问题的正确方法。你将花费无数的时间试图处理一行你认为可以解决问题的代码,老实说,用这种方法可能会使问题变得更加复杂。
理想的开始方式是拿起笔和纸,离开电脑,依次写出这个函数需要做什么。确定输入是什么,它们将如何被处理,然后输出应该全部手写出来。这个过程称为构建伪代码。您可能在笔记本上写的速记可能足以让您进入下一步编码。
对于一个更正式的方法和正确的函数文档,接下来需要做的是开发 ***伪代码*** 。简而言之,伪代码是一种在函数中表示代码的方式,这种方式与特定的编程语言无关,并正式表示函数中的进程。此外,特定于某种编程语言的数据类型(如列表、字典和其他结构)分别被更一般化的术语(如序列或映射)所替代。下面是我为上面的算术平均值函数写的伪代码的例子。请注意,它比您在家中进行的标准项目需要的格式要正式得多,但是对于更多的技术或研究目的,以下格式可能是必要的。

算术平均值函数的伪代码。作者图片
## 函数返回
函数的最后一行通常保留给作为函数输出返回的对象。在大多数情况下,赋值变量用于返回该对象的值。在算术平均值函数的情况下,返回是变量‘avg ’,它将产生‘avg’变量的值,在这种情况下是算术平均值。需要记住的重要一点是,可以对返回进行定制,以提供各种输出,比如前面提到的变量,还有字符串。此外,像函数的输入一样,可以分配多个输出来返回。它们和输入一样,会在返回行中用逗号分隔出来。需要记住的一件重要事情是,返回多个输出的函数的输出序列将与它们编写脚本的方式保持一致。请参见下面的示例:
def function(input):
return output1, output2, output3t = function(some_input)
print(t)output1, output2, output3
请注意,上面的函数返回三个变量,如果您将该函数赋给一个变量并打印它,输出的顺序将与输入中脚本的顺序相同。在这个函数将被用于馈送其他函数和/或您需要单独调用返回的输出的情况下,了解这一点很重要。
总结:
希望这篇文章列出了关于如何构建一个函数的基础知识,以及你需要注意的关于函数语法的关键事项。此外,我想重申的要点是,远离计算机的功能开发是使编码成为简单练习的关键部分,而不是试图同时解决过程和语法问题。如果您有任何反馈,请回复和/或联系我们!谢了。
# Python 初学者突破(Lambda 函数)
> 原文:<https://towardsdatascience.com/python-beginner-breakthroughs-lambda-functions-cf932befb4a4?source=collection_archive---------38----------------------->
## 当函数不是函数时

[粘土银行](https://unsplash.com/@claybanks?utm_source=medium&utm_medium=referral)在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 拍摄的照片
Lambda 函数是 Python 世界中非常强大的工具,它弥补了是否需要正式定义函数的差距。我认为 Python 中的 lambda 函数是一张王牌,可以帮助您快速、轻松地实现一些东西,而不需要定义函数的形式和任何额外的工作。记住 ***“能力越大,责任越大”*** ,知道什么时候可以,什么时候不应该使用 lambda 函数同样重要。

照片由[本·怀特](https://unsplash.com/@benwhitephotography?utm_source=medium&utm_medium=referral)在 [Unsplash](https://unsplash.com/?utm_source=medium&utm_medium=referral) 上拍摄
# “A-Ha”突破:Lambda 函数
如果您已经很好地掌握了在 Python 中导航语法和理解一些核心方法和函数如何工作的方法,那么学习 lambda 函数是什么和如何工作的可能有助于缓解一些在处理“简单函数”时不必要的困难问题。在函数中构建所有的代码总是一个好的做法,但是有时你想让事情变得非常简单,并且不想经历定义一个新函数的形式…这就是 lambda 函数的用武之地。Lambda 函数允许你声明一个带有参数和表达式的匿名函数。对于简单的例程操作,与构建标准函数相比,lambda 函数可能是一个理想的选择。
细节中的魔鬼是 lambda 函数的过度使用或不当使用可能会导致在进行故障排除时出现令人头疼的问题,因为您可能需要上下搜索代码来找到错误和/或对代码进行更改/更新。Lambda 函数应该在适当的时候谨慎使用。
## 基本 Lambda 函数形式和语法

lambda 函数部分的分解
1. 要赋值的变量—这对于如何实现 lambda 函数是可选的,但是您可以将其赋值给一个变量,并使用该变量进行进一步的计算和操作
2. 函数调用——只需使用 lambda 来实现函数
3. 函数的参数——可以有多个参数,但通常不超过两个是 lambda 函数的一个好的经验法则。如果您需要更多的参数,您可能需要重新考虑构造一个标准函数
4. 表达式——Lambda 函数旨在用一行代码编写(因此它们可以帮助简化短小的代码)
5. IFFE 参数赋值— IFFE(立即调用函数表达式)参数用作首字母缩写词,表示以这些值作为参数来运行函数。这一部分是可选的,因为您可以像传统函数一样调用 lambda 函数并分配参数值,如下面的下一个代码块所示。
你可以在上面的图片中看到,执行 lambda 函数的单行代码将得到 5,就像下面用不同的方法实现的结果一样。
x = (lambda x, y: x + y)
z = x(2,3)
print(z)
...
5

迈克·彼得鲁奇在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上的照片
## 实现 lambda 函数的不同方法:
由于 lambda 函数允许您构建一个单行函数并实现它的简单本质,您可以通过各种不同的方式使用它们来使您的代码更具功能性。一种方法是使用λ**来设置排序键**,如下所示。
rank_list = sorted(score_mean.items(),
reverse=True,
key=lambda x: x[1])
因此,在名为“rank_list”的新列表中,我们使用 lambda 函数根据字典值的第二个元素 score_mean 进行排序。
另一种可能的使用方法是作为一个简单的函数应用于如下的迭代。
distance = (lambda x, y:
(1 - np.dot(x, y) / (np.linalg.norm(x) * np.linalg.norm(y))))
...for idx, vector_1 in enumerate(projected_vector):
article_distance[articles[idx]] = distance(vector_1, vector_2)
在上面的代码块中,我们使用 lambda 来捕获余弦相似性计算,然后应用它来查找两个向量之间的距离。我们对循环中的每一项都这样做,并捕捉距离以便以后操作。

乔治·霍夫曼在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上的照片
## 兰姆达斯的限制
如前所述,使用 lambdas 应该谨慎,考虑清楚和必要性,以确保良好的代码审查,并促进未来的更新。尽管 lambda 函数的行为类似于普通函数,但在直接进入 lambda 函数之前,您应该考虑一些限制:
* 因为 lambda 函数是快速的一行程序,所以普通的 python 语句不能用在表达式中。
* 由于函数本质上在技术上是匿名的,所以它们不一定与名称绑定。
* 由于前面提到的项目,复杂性有限:一个语句,一行,可能不(或不应该)包括太多的参数
## 包裹
兰姆达斯是你包里非常有用的工具。它们允许在您的代码中实现快速简单的功能,但是在它们能做什么方面有一些限制。想想你什么时候和为什么要使用它们,为什么一个普通的函数可能不是最佳的。正如我在开始时提到的“能力越大,责任越大!” 同样适用于兰达斯。如果您有任何问题、意见或反馈,请告诉我!谢了。
# Python 初学者突破(python 风格)
> 原文:<https://towardsdatascience.com/python-beginner-breakthroughs-pythonic-style-dbf543e5e117?source=collection_archive---------26----------------------->
## 正如已经说过的,代码被阅读的次数远远多于它被编写的次数。在编写代码时养成良好的格式化习惯几乎和让它工作一样重要。

乔丹·昆斯在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上的照片
代码的主要功能是工作,其次是如果你打算不止一次地使用它,让它可以被解释。养成良好的习惯并遵循 PEP-8 中推荐的通用格式惯例([见这里](https://www.python.org/dev/peps/pep-0008/))是标准化代码外观的一种简单快捷的方法。PEP-8 (Python 增强提案#8)是作为一种尝试和创建通用实践模板的手段而创建的,目的是增强可读性和标准化。在本文中,我将重点介绍一些您可以对代码进行的更改,以使其更实用、更时尚。
# 代码布局
## 刻痕
PEP-8 提供了关于何时以及如何正确使用缩进的广泛指南。我将强调两个对于新手程序员来说可能不太直观的例子。
* 制表符或空格——Python 3 解决了混合制表符和空格的问题,但是理论上,*空格是缩进的首选方法*,但是如果制表符已经在现有代码中被一致地使用,它也是合适的。
* 最大行长度— *将任何一行长而复杂的代码限制在 79 个字符以内*。这更多是为了美观,也是为了让你的 IDE 的其他窗口的屏幕空间最大化。*文档字符串和注释应限制在 72 个字符以内*。
* 与操作符相关的换行符——这实际上会使理解代码变得简单得多,一旦你看到并比较了不同之处,但是换行符应该插在操作符之前,请看下面的代码块。第一个例子是当换行符在前,第二个是在后。第二种方法使得从视觉角度理解表达式简单得多,因为它类似于手写算术问题。
total_cost = (retail price +
total_taxes +
shipping_handling -
discount)# Preferred option
total_cost = (retail price
+ total_taxes
+ shipping_handling
- discount)
## 进口
尽可能地使用绝对导入,以使代码更具可读性,并使读者能够理解哪个模块来自哪个库。如果第二部分不太重要或者使用了库中的公共模块,那么只从库中导入模块是可以的。
* 一次仅导入一个库。(唯一的例外是在一行中调用导入库中的多个模块,如下所示)
Good only calling a single library
import statistics#exception for multiple modules
from scipy import optimize, stats
## 空白
正如 PEP-8 中所写的那样,过多的空白会让你看起来很不舒服,而且还会让你不得不花更多的时间去修改。
1. 紧接着左括号:
2.在尾随普通括号和右括号之间
3.在逗号、分号或冒号之前
4.请注意,在顺序数据类型的切片中,冒号的作用类似于一个运算符。你应该用相等的空间对待冒号(见下面的例子)
5.紧接在函数调用的左括号之前
6.将工作分配与另一个工作分配对齐的附加空格
1. spam(ham[1], {eggs: 2}) # Good
spam( ham[ 1 ], { eggs: 2 } ) # Bad
2. foo = (0,) # Good
bar = (0, ) # Not so good
3. if x == 4: print x, y; x, y = y, x # Thumbs up!
if x == 4 : print x , y ; x , y = y , x # Ewww!
4. ham[1:9], ham[1:9:3], ham[:9:3], ham[1::3], ham[1:9:] # Good
ham[lower+offset : upper+offset] # Good too
ham[1: 9], ham[1 :9], ham[1:9 :3] # Ouch
ham[lower : : upper] # Eyes hurting
5. spam(1) # Good
spam (1) # Why??
6. x = 1
y = 2
long_variable = 3 # Good
x = 1 # Excessive spaces in code block
y = 2
long_variable = 3
空白的关键是只在需要的时候使用它们。请注意上面不必要的空白造成了多少额外的空间。不要走极端,确保代码的清晰和流畅不被打断。

在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上由 [Austin Distel](https://unsplash.com/@austindistel?utm_source=medium&utm_medium=referral) 拍摄的照片
**评论**
我个人反对在代码中使用大量的注释,除非你是为了让别人从中学习而写的。变量赋值、函数开发以及代码中的其他一切都应该具有可读性,以便有编码经验的人理解正在发生的事情。这并不是说不应该有任何注释,而是(像空格一样)在必要时使用它们。例如,当代码的后续读者可能不清楚某些内容时。
* 随着代码的修改更新注释(必须!!!)
* 为了可读性,注释应该是完整的句子
* 块注释中的段落应该用#开头的一行隔开
* 应该避免行内注释,因为它们通常被认为是不必要的和分散注意力的。
**命名风格**
当您在代码中为项目指定名称时,以下示例是普遍接受的形式:
b (single lowercase letter)
B (single uppercase letter)
lowercase
lower_case_with_underscores
UPPERCASE
UPPER_CASE_WITH_UNDERSCORES
CapitalizedWords (or CapWords, CamelCase5, StudlyCaps)
mixedCase (differs from CapitalizedWords by initial lowercase character!)
Capitalized_Words_With_Underscores (ugly!)
## 加载项
有各种各样的插件可以安装到您的 ide 环境中,这些插件将根据 PEP-8 和其他格式标准提出建议和/或自动更正您的代码。我使用的一个插件叫做 PyLint,但是还有许多其他类似的插件。
希望这个关于编码风格的小提示能让你的代码更简洁,更重要的是更易读!非常感谢您的任何评论或反馈,如果您有任何想要探索的内容,请对本文做出回应。
# Python 初学者突破(顶级 Python 内置函数)
> 原文:<https://towardsdatascience.com/python-beginner-breakthroughs-top-python-built-in-functions-19a6d978035b?source=collection_archive---------24----------------------->
## 为什么要重新发明轮子?Python 有许多内置函数,可以做一些你目前正在思考的事情

serjan midili 在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍摄的照片
Python 本身有许多内置函数,每个想熟悉这种编程语言的人都应该知道。在本文中,我将重点介绍最重要的几个,您可以用它们来开发将为您的机器学习或数据科学项目提供动力的代码。你是否经常发现自己在编码,想做一些看似简单的事情,但当你开始尝试并把它打出来时,你会想‘肯定有更好的方法’。如果您遇到了这种情况,而且这个概念实际上非常简单,那么可能有一种更简单的方法,Python 内置函数。下面的列表没有特定的重要性顺序,但是我想强调 Python 必须提供的一些最常用的内置函数。
Python 的一个优点是,正如函数的命名一样,它们的名字很大程度上暗示了它的功能。下面我将函数分成三组:基本、数据类型相关和高级

照片由[猎人哈利](https://unsplash.com/@hnhmarketing?utm_source=medium&utm_medium=referral)在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 拍摄
# 基本功能:
## abs()
顾名思义,abs()函数返回一个数字的绝对值。输入可以是各种数字输入。
## 任何()
any()函数的工作原理是,如果项目是 iterable,则返回 True/False。这可以用来确定数据是否来自序列/可迭代类型。
## len()
这可能是 python 中最常用的函数之一,len()函数返回对象中元素的数量。因此,如果你的列表中有 5 个条目,len()函数将返回 5。
Examples of Basic Functions
abs() function
x = -24
abs(x)
24
any() function
y = [0, 1, 2, 3, 4]
any(y)
True
len() function
len(y)
5
## 最大()
另一个真正常用的函数,顾名思义,返回 iterable 的 max 元素(如果你只提供一个参数)。您还可以提供另外两个仅包含关键字的参数:key 和 default。使用像 max(list,key=len)这样的 max()函数将返回列表中最大长度(或最长)的元素。
## 最小值()
min 函数执行的功能与 max()函数相同,但方向相反。类似地,还有一些附加的仅关键字参数可以用来使函数更加可定制。
## 打印()
每个人都在 Hello World 的第一天练习中学习了这个函数,它允许用户在文本流中显示项目。
max() function
l = [1, 4, 7, 21, 35]
m = [[1, 2, 3], [1, 2], [0, 3, 2, 1]]
max(l)
35
max(m, key=len)
[0, 3, 2, 1]
min() function
l = [1, 4, 7, 21, 35]
m = [[1, 2, 3], [1, 2], [0, 3, 2, 1]]
min(l)
1
min(m, key=len)
[1, 2]
print() function
x = 'hello world'
print(x)
hello world
## 圆形()
根据您希望如何表示数字,round()函数可以将数字舍入到指定的小数点(带有附加参数)
## 总和()
最后一个基本函数是 sum(),顾名思义,它将接受一个 iterable 并将元素相加。您还可以添加一个额外的参数来指定起点。
round() function
x = 24.3452
round(x)
24
round(x, ndigits=2)
24.35
sum() function
z = [1, 3, 5, 10, 51]
sum(z)
70

马库斯·斯皮斯克在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上的照片
# 高级功能:
## 枚举()
enumerate()函数是一个非常有用的函数,它有助于获取一个序列(比如一个列表)并获取所有相应索引和元素的元组对。您可以使用附加的 start 参数定义第一个元组元素(编号)的起始编号。注意 enumerate()的输出是一个 enumerate 对象,您需要将它转换为列表或其他类型才能看到它。
## 过滤器()
顾名思义,filter()函数将根据函数参数是否为 true 来筛选 iterable。参数是 function(将应用于迭代器的函数)和 iterable(可以是任何 iterable 或序列类型)。注意,如果过滤器参数很简单并且在整个代码中不可重用,那么处理它的一个常见方法是使用 lambda 函数。
enumerate() function
k = ['first', 'second', 'third', 'fourth']
z = enumerate(k)
print(list(z))
[(0, 'first'), (1, 'second'), (2, 'third'), (3, 'fourth')]
filter() function
p = [1, 2, 3, 4, 5]
q = filter(lambda x: x % 2 == 0, p)
print(list(q))
[2, 4]
## 地图()
map()函数为 iterable 中的所有项计算给定函数,并输出相应的迭代器。例如,本质上,一个列表整数将用一个函数来计算,该函数将对该项求平方,并产生一个包含所有平方整数值的新列表。
## 已排序()
与许多更高级的函数一样,这个函数是帮助以所需格式表示序列类型数据的关键。sorted()函数正如其名所示,对数据进行排序。还有一些额外的参数可以用来构建一个更高级的排序逻辑。您提供一个键作为排序依据,并定义 reverse = True 或 False,以反转排序的方向。请注意,该函数适用于数字和字符串类型的数据
## zip()
使用 zip()函数可以处理多种序列类型并希望将它们组合在一起。就像你想象的衣服上有拉链一样,它有两个可重复项,并以拉链的方式将它们结合起来。它的方法是提供两个可迭代对象作为输入,然后返回一个元组迭代器。需要注意的一点是,你应该小心不同长度的可重复项,因为较长的项可能会产生问题。一件有趣的事情是,您也可以通过使用函数调用 zip(*zipped_item)来解压缩。
map() function
z = [0 , 2, 5, 21]
y = map(lambda x: x ** 2, z)
print(list(z))
[0, 4, 25, 441]
sorted function
a = ['r', 'd', 'a', 'b', 'z']
print(sorted(a))
['a', 'b', 'd', 'r', 'z']
print(sorted(a, reverse=True))
['z', 'r', 'd', 'b', 'a']
zip() function
number_list = [1, 2, 3]
str_list = ['one', 'two', 'three']
zipped = list(zip(number_list, str_list))
print(zipped)
[(1, 'one'), (2, 'two'), (3, 'three')]
unzipped = list(zip(*zipped))
print(unzipped)
[(1, 2, 3), ('one', 'two', 'three')]

弗兰基·查马基在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍摄的照片
# 相关数据类型:
## 类型()
type()函数最简单的形式是返回提供给它的对象的类型。使用类型()的 3 个参数创建一个新的类型对象。参数是 name(一个类名)、bases(一个逐项列出基类的元组)和 dict(一个包含类主体定义的名称空间的字典)。
## int()
int()函数返回所提供的数字或字符串的整数对象。请注意,对于浮点数,它将向零截断。
## str()
与 int()类似,当给定一个对象作为输入时,str()函数返回一个字符串对象。
## 字典()
dict()函数根据提供的输入创建一个新的字典。
type() function
x = 'string'
type(x)
str
x = 2.14
type(x)
float
int() function
p = 2.21
int(p)
2
str() function
z = 21
str(z)
'21'
dict() function
z = dict()
print(z)
{}
note there are many ways to build dictionaries
**总结**
Python 中有大量已经构建的函数,可以执行一些基本的数据操作。通过理解和了解它们,你可以简单地调用这些函数中的一个,而不是编写必要的程序。一如既往,如果您有任何问题或意见,请随时回复或评论!谢谢
# 超越初级阶段的 Python
> 原文:<https://towardsdatascience.com/python-beyond-beginner-stage-good-practices-and-tools-75ddd55b445d?source=collection_archive---------16----------------------->
## 帮助您更上一层楼的工具和最佳实践

图片来源:[undraw.co](http://undraw.co)
所以你已经开始用 Python 了。你对语法、控制流和操作符了如指掌。您可能已经熟悉了一些特殊的 Python 特性,比如 list 和 dict comprehension、lambda 函数、inline if、for..以此类推。你可能也用过类和生成器。
我想分享一些特性、工具、库和良好的实践,你可以将它们包含在你的开发流程中,以提高效率,使你的代码更加健壮。
# 排除故障
学习 python 调试器真的有回报。
只需在您希望程序暂停的地方插入`import pdb; pdb.set_trace()`,然后您可以键入`!my_var`来查看变量并执行任何语句,如`!my_function(my_var)`您可以通过键入`s`来单步执行代码(步骤)。
使用`ipdb`作为基于 ipython 的`pdb`的替代,以获得更友好的用户体验和多行代码支持。
*如果您希望代码在发生错误(异常)*时暂停,而不插入 set_trace 断点,将`python my_program.py`更改为`python -m pdb -c continue my_program.py`,您将在第一个未捕获的异常处得到 pdb 提示。如果代码中没有出现异常,您可以使用`u`跳出调用堆栈。
**如果你使用的是 jupyter** **notebook** 的话,你很幸运继承了 ipython 的特性。如果您的单元格引发了异常,创建一个新的单元格并键入`%debug`来附加调试器。谷歌工程-colab 和 vs 代码 jupyter 笔记本扩展太。
**如果你正在使用 vs 代码**,你可以使用 python 插件的调试功能,这是上帝派来调试多处理和多线程代码的——允许你单独检查每个进程,并在代码运行时给你创建断点*的自由。*
**如果你正在使用 python 的命令行控制台**,我建议你使用带有`%debug`神奇功能支持的`ipython`来替代`python`。
# 测试
每个人通常都测试自己的工作。他们至少运行一次他们的程序,看看它是否给出了期望的输出。
有了经验,你可能会意识到,用不同的输入场景或环境条件来检查你的程序,对于避免以后错误破坏你的软件是很重要的。
您可以手动完成,但是当您的代码变得更加复杂,或者您的经理想要测试用例通过的证明时,您会想要考虑自动化和记录测试流程。
对于初学者来说,您可以在您的`my_code.py`旁边创建一个名为`test_my_code.py`的文件,并用`assert`语句
创建一个函数`test_my_func()`来检查各种输入情况下的输出。
您可以调用`test_my_code.py`的`__main__`块中的函数并通过命令行运行它,或者更好地使用`pytest`及其丰富的功能框架来运行`pytest test_my_code.py`,它调用带有`test`前缀的文件中的所有函数。我也在我的主要项目中使用`unittest`,它是一个 python 内置库。
一旦您的代码开始与其他昂贵而复杂的组件进行交互,您将需要单独测试您的代码组件(如函数和类)。为此,您需要暂时删除其他组件,并用固定输出替换它们。这叫猴子打补丁。
在`pytest`中,您可以使用 [monkeypatch](https://docs.pytest.org/en/stable/monkeypatch.html) ,在`unittest`中有`[mock](https://docs.python.org/3/library/unittest.mock.html)`模块。
这些测试框架的其他用例是:
* 检查所有的测试用例,即使有一个失败。计算错误的总数。
* 使用`==`比较两个字典、嵌套列表、floats(考虑精度)以及其他一些不能简单比较的对象类型。
* 检查传递给模拟对象的参数和它被调用的次数以及每次调用的参数。
* 断言应该在代码块中引发异常。
* 并行运行测试
# 开发—使用打字
键入是指定您希望变量包含的数据的“类型”。虽然它不会影响 Python 的执行,但它有助于代码开发。举个例子:
def get_marks(name: str) -> float:
return random.rand() * 100
它说 get_marks 接受一个字符串(在变量`name`中)并返回一个浮点数。怎么又有用了?
**原因 1** 假设我对`random`库不熟悉,想用函数检查艾伯特的分数是否等于玛丽的分数。我可能会假设返回类型是一个整数,并使用`==`操作符,由于涉及到精度问题,将其用于 float 是不安全的。
理由二有助于提高可读性。代码审查者会感谢你的。使用你图书馆的人会喜欢你的。
**原因 3** 型跳棋。你可以使用像`mypy`这样的库,它使用类型检查来确定某些操作是否与对象的类型不兼容。比如 strings 有`.tolower()`方法但是 int 没有。在大型代码库中,运行`mypy`比用所有测试用例运行程序来找出这样一个 bug 要容易得多。
**原因 4** 运行时数据验证器。
示例 1:您正在通过 web 服务一个应用程序,并希望自动验证“post”数据的类型。对`fastapi`库使用类型提示。它返回适当的状态代码,生成模式和 swagger 文档。
示例 2:一个类中的变量只能接受某些数据类型,而你不想将其他数据类型传递给`__init__`?使用`pydantic`来验证在其 dataclass 中传递的每个值。
# 架构—使用数据类
> Python 5 将只有这些[数据类],没有'普通'类。奥利弗·拉塞尔
尽管 Oliver 是以开玩笑的方式说的,但是数据类的用处不能被夸大。除了在`__init__`中初始化变量,你可以做以下事情:
from dataclasses import dataclass
@dataclass
class Food:
name: str
carbs_percent: int
fiber_percent: int
@property
def net_carbs(self):
return self.carbs_percent + self.fiber_percentFood("Apple", 40, fiber_percent=5).net_carbs
returns 45
有几个实现数据类的框架。除了内置的`dataclass`库之外,还有`attrs`和`pydantic`,它们都有各自的缺点和优点。我推荐具有数据验证特性的`pydantic`。
您可以使用`dataclasses.as_dict`函数轻松地将数据类转换为字典,这对于序列化来说很方便,并且可以通过使用字典解包`**my_dict`或外部工具(如支持数据类内部数据类的`dacite.from_dict`)将其转换回来。三个库中的每一个对于`as_dict`和`from_dict`具有相同的功能。
使用 dataclasses 的一个副作用是,它迫使您键入变量,这在 Python 中是一个非常好的编码实践。
要指定变量的默认值,在默认值不可变的情况下直接指定,或使用`dataclasses.field`类似如下:
from dataclasses import field
@dataclass
class Food:
name: str = ''
subitems: list = field(default_factory=list)
如果您直接分配`subitems: list = []`,那么您对一个实例如`Food().subitems.append(3)`所做的任何更改都将改变所有`Food`实例的值。这就是为什么这是不允许的,而且`dataclasses`会在定义中提出一个例外。**然而,如果赋值不是默认类型(list、dict 等),它不会检测赋值是否可变。)**所以需要小心。例如,不要做以下事情
USE DEFAULT FACTORY INSTEAD OF THE FOLLOWING
@dataclass
class Animal:
name: str = ''
@dataclass
class Store:
animal: Animal = Animal("dog")store1 = Store()
store1.animal.name = "cat"store2 = Store()
print(store2.animal.name) # prints "cat"
改为做`animal: Animal = field(default_factory=lambda: Animal("dog"))`
# 架构—使用命名元组
如果你想在 python 中返回多个变量,你通常要做如下的事情
def get_time_and_place() -> Tuple[datetime, str]:
return datetime.now(), 'Combodia'
请注意,当您返回多个变量时,返回类型是一个元组。元组可以像下面这样解包
`mytime, myplace = get_time_and_place()`
当返回值的数量变得很多时,跟踪`myplace`是在元组的第 5 个索引还是第 6 个索引变得更加困难。您必须仔细阅读函数的 docstring,以确保您以正确的顺序解包返回值。键入函数返回值变得繁琐,比如`-> Tuple[int, str, str, List[int]]`。
这些问题可以通过使用`typing.NamedTuple`或`collections.namedtuple`来解决,我推荐使用打字。因其用户友好的特性而得名。
class TimeAndPlace(typing.NamedTuple):
time: datetime
place: str = 'Madagascar'def get_time_and_place() -> TimeAndPlace:
output = TimeAndPlace(datetime.now())
return output
现在,您获得了一个以名称作为属性的元组,其索引和解包特性保持不变。
mytime, myplace = TimeAndPlace(dattetime.now())
TimeAndPlace(dattetime.now())[1] # returns Madagascar
您可以在这里使用 dataclasses,但是会丢失元组的属性。您不能索引或解包。你也失去了不变性,所以不能作为字典中的一个键。
# 架构—使用枚举
from enum import Enumclass Label(Enum):
dog='dog'
cat='cat'
Enum 是一种数据结构,当某个特定变量有不同的值时,可以使用它。这解决了一个常见的问题,即使用字符串存储变量的类,然后当值为`cat`并且语句评估为 False 时,错误地使用了`label == 'cats'`。
如果您写`label == Label.cats`,Enum 通过引发 AttributeError 来防止这种情况发生。它还能让 pylint 和 mypy 这样的 linters 检测到错误。
如果你使用 IDE,比如 jupyter notebook 或者 VS-code with Pylance,你会得到自动完成功能,防止你一开始就写`Label.cats`。
如果您从一个文本文件中获得标签字符串输入,那么您可以按照以下方式使用 Enum 进行数据验证
label_text: str = open("my_label.txt").read()
label = Label(label_text)
如果 my_label.txt 是`cats`而不是`cat`,上面的代码会引发 ValueError,否则会将字符串转换为适当的属性。
`Label.cat.value`返回可用于序列化和保存的“cat”字符串。
如果在其中一个值中使用 json.dumps 和 Enum,将会引发异常。在[这个堆栈中有解决方法。在我看来,最好的方法是使用 IntEnum,它也是 python `int`的子类,这样你就可以直接使用`0 == Label.cat`,而不是先将你的变量显式转换成 Enum。缺点是你失去了序列化的可读性。](https://stackoverflow.com/questions/24481852/serialising-an-enum-member-to-json)
# 架构—使用路径库
内置的`pathlib`实现了操作和验证文件路径的面向对象的方法。考虑以下情况:
**遍历目录并读取文件**
使用操作系统
import os
for maybe_file in os.listdir(my_dir):
if not os.path.isfile(maybe_file): continue
with open(os.path.join(my_dir, file)) as f:
print(f.read())
使用 pathlib
from pathlib import Path
for file in Path(my_dir).iterdir():
if not file.is_file(): continue
with file.open() as f:
print(f.read())
这是一种更干净的方法。关键是通过传递一个类似于代表路径的对象`./`的字符串来实例化的`Path`对象。对象有`is_file()`、`exists()`、`name`、`glob('*.jpeg')`等方法和属性
最引人注目的是使用`/`操作符构建路径的方式。考虑:
os.path.join(new_dir, os.path.basename(file))
/home/ubuntu/project/x.jpeg
相对
new_dir / file.name
Path(/home/ubuntu/project/x.jpeg)
# 林挺
linter 可以帮助你在不运行程序的情况下发现许多类型的错误。有时有一些类型的错误你的测试用例会错过,但是 linter 可能会捕捉到。如果你使用任何像 VS 代码一样的 IDE,你可以在每次保存代码时找到错误。
例如,您可以知道是否拼错了变量或函数名,是否使用了不存在的类属性,是否传递的参数比函数预期的多,或者是否多次传递参数。如果你有类型注释,linters 将捕捉所有类型的不兼容性。
如果代码中有类似于捕获一般异常或未使用变量的味道,它也会发出警告。它还建议你做重构和其他在 PEP 和其他地方推荐的最佳实践。
我推荐同时使用`pylint`和`mypy`,这可以通过在 VS 代码设置(JSON)中设置以下标志在 VS 代码中实现
"python.linting.mypyEnabled": true,
"python.linting.pylintEnabled": true,
在自动化构建管道中运行测试之前,您可以使用 pylint 来检查失败。您可以检查该命令返回的退出代码,以查看错误级别(致命/错误/警告…)[https://pypi.org/project/pylint-exit/](https://pypi.org/project/pylint-exit/)
# 可读性—样式和格式
1. 好的库对每个函数、类、类方法和模块都有文档字符串。我推荐 google 风格的 docstrings[https://sphinxcontrib-Napoleon . readthe docs . io/en/latest/example _ Google . html](https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html)
2. 代码布局基于 https://pep8.org/`PEP8`T21
3. 使用手动格式化和自动格式化的组合,如`yapf`或`autopep8`(还有其他的你可以试试)。
# 结论
* 使用`pdb`、vs code python extension、`ipython` magic commands 这样的工具,会减少调试的痛苦。
* 在你的代码设计中包括:类型提示、`dataclasses`、`enum`、`pathlib`和`namedtuples`。这些将使您的代码更具可伸缩性、可读性、易用性和可扩展性。
* 像`pytest`和`unittest`这样的测试框架将帮助你以编程方式编写测试。
* 像`mypy`、`pylint`、`pylance` (vs 代码)这样的 Linter 提高了代码质量。
* 遵循代码风格指南 PEP8 和`docstring`指南,以提高可读性和文档。
# Python Bites:操纵文本文件
> 原文:<https://towardsdatascience.com/python-bites-manipulating-text-files-511d1257d399?source=collection_archive---------9----------------------->
## 读取、写入和追加

照片由[帕特里克·福尔](https://unsplash.com/@patrickian4?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)在 [Unsplash](https://unsplash.com/s/photos/food-bite?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 拍摄
文本文件通常用于存储数据。因此,它们在数据科学系统中是必不可少的。Python 提供了多种功能和方法来处理文本文件。
创建文本文件有许多选项。因为这里的重点是操作文本文件,而不是创建文本文件,所以我们不会涵盖所有的内容。让我们用一种你可以在 jupyter 笔记本上使用的方法。
%%writefile myfile.txt
Python is awesome
This is the second line
This is the last line
我们现在在当前工作目录中有一个名为“myfile.txt”的文本文件。为了阅读文本文件的内容,我们首先需要打开它。
f = open("myfile.txt")
我们现在可以读取“myfile.txt”的内容了。
f.read()
'Python is awesome\nThis is the second line\nThis is the last line'
read 函数,顾名思义,读取文件的内容。您可能已经注意到,新行用“\n”表示。
使用 readlines 函数可以逐行读取文件内容。它返回一个列表,其中每一行都包含一个项目。
f.readlines()
[]
我们的 readlines 函数返回一个空列表,这有点问题。原因是一旦你读取一个文件,光标会一直移动到文件的末尾。下一次尝试读取同一文件时,光标找不到任何要读取的内容,因为它已经位于文件的末尾。
解决方法是将光标设置回初始位置。然后,我们将能够再次读取该文件。
f.seek(0)
0f.readlines()
['Python is awesome\n', 'This is the second line\n', 'This is the last line']
一旦内容被读取,我们应该关闭文件。否则,我们可能会遇到意想不到的结果。
f.close()
如果您试图在文件关闭后读取它,Python 会抛出一个值错误,指示对关闭的文件进行了 I/O 操作。
我们也可以写入一个现有的文件。模式参数用于选择操作类型。默认值为“r ”,代表读取。因此,在读取文件时,我们不需要指定模式参数的值。
如果要覆盖内容,模式设置为“w”。也可以通过 append 选项(' a ')向现有文件添加新内容。
我们还将查看演示如何覆盖文件或添加新内容的示例。在进入这一部分之前,我想介绍一种打开文件的新方法。
每次阅读文件时都要关闭文件,这听起来可能是一个乏味的操作。幸运的是,Python 提供了一种更实用的读写文件的方式。
with open("myfile.txt", mode='r') as f:
content = f.read()print(content)
Python is awesome
This is the second line
This is the last line
我们不再需要显式调用文件的 close 方法。让我们给我们的文件添加一个新文件。我们应该选择追加选项。
with open("myfile.txt", mode='a') as f:
f.write("\nTHIS IS THE NEW LINE")
这是新的内容。
with open("myfile.txt", mode='r') as f:
updated_content = f.read()print(updated_content)
Python is awesome
This is the second line
This is the last line
THIS IS THE NEW LINE
写模式覆盖文件。因此,它删除文件中已经存在的内容,并写入新内容。
with open("myfile.txt", mode='w') as f:
f.write("THIS WILL OVERWRİTE THE EXISTING LINES")
让我们读新内容。
with open("myfile.txt", mode='r') as f:
updated_content = f.read()print(updated_content)
THIS WILL OVERWRİTE THE EXISTING LINES
mode 参数也接受允许多个操作的参数。例如,“r+”打开一个文件进行读写。
with open("myfile.txt", mode='r+') as f:
content = f.read()
f.write("\nHere is a new line")
f.seek(0)
updated_content = f.read()
我们打开文件并阅读其内容。然后我们写新的一行,读取更新的内容。
print(content)
THIS WILL OVERWRİTE THE EXISTING LINESprint(updated_content)
THIS WILL OVERWRİTE THE EXISTING LINES
Here is a new line
## 结论
我们做了几个简单的例子来展示 Python 如何处理文本文件的读写。当然,还有更多。这篇文章可以被认为是一个介绍,它将使你熟悉操作。
全面了解基础知识总是很重要的。通过积累基础知识,你将获得高级技能。
感谢您的阅读。如果您有任何反馈,请告诉我。
# Python 但是很奇怪
> 原文:<https://towardsdatascience.com/python-but-its-weird-f90b45220f86?source=collection_archive---------17----------------------->
## **质疑你的 Python 技能的代码片段**

由[vini cius“amnx”Amano](https://unsplash.com/@viniciusamano?utm_source=medium&utm_medium=referral)在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍摄
我们都爱 Python!长期使用这种语言后,如果深入概念,你会对这种语言的模块化感到惊讶。根据我的经验,我发现使用 Python、一点 PowerBI 和基于它的 SQL 的组合很容易实现我的大部分数据分析工作。
但是在这么多的特性中,有一些非常奇怪的 Python 概念和代码,您在编程过程中可能会遇到,也可能不会遇到。也许它被跳过了,或者你假设它对 Python 来说是显式正确的,没有任何解释。让我举一些例子。
# 递增/递减运算符在 Python 中有效吗?

沃洛季米尔·赫里先科在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上的照片
在像 C/C++/Java/Go 这样的编程语言中,我们有增量(++)和减量运算符(-),它们将变量的值更新 1。这些是在循环中迭代时常用的。
如果您尝试在 Python 中使用这些操作符,您将得到以下结果:
a = 10
print(++a)
10
print(--a)
10
print(a++)
SyntaxError: invalid syntax
print(a--)
SyntaxError: invalid syntax
后递增/递减返回语法错误,因为 Python 语法中没有定义这种类型的语法。但是前递增/递减运算符呢?即使这些操作符返回概念上错误的答案,我们如何得到它们的结果?
实际上,双加号(++)被视为+(+a),它被解析回(a ),这就是它没有引发任何错误的原因。而是返回“a”的原始值。同样的解释也适用于(- -a)。
# 是否正确使用了 round()函数?

照片由 [Varun Gaba](https://unsplash.com/@varunkgaba?utm_source=medium&utm_medium=referral) 在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍摄
尝试在不使用 Python IDE 的情况下评估以下内容:
print(round(12.5) - round(11.5))
很可能,您会将值 12.5 舍入为 13,将 11.5 舍入为 12,并将最终答案舍入为 1。这个答案在 Python 3.x 中是错误的!
Python 的 3.x round 函数是使用银行家或收敛舍入实现的,这与 5 的边缘情况不同。大于 5 的值向上舍入和小于 5 的值向下舍入的通常规则保持不变。对于 5 的边缘情况,它将数字舍入到最接近的偶数。
这意味着 12.5 将被舍入到 12,因为它是最接近的偶数,而 11.5 也将被舍入到 12,导致最终答案为 0。所以,在这种情况下使用 round 函数!
# 海象操作员行为

杰伊·鲁泽斯基在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上的照片
随着 Python 3.8 的发布,一个新的运营商进入了人们的视线,walrus operator。根据官方文档,它是一种赋值表达式,用于循环、表达式和更多实例中。它允许您在同一个表达式中赋值和返回值。
所有可能的实例都在官方版本中讨论过,你可以在 [PEP 572](https://www.python.org/dev/peps/pep-0572) 上找到。我在网上发现的一个有趣的用例将在下面讨论,在这里我输入数字,直到输入为-1。
在一般情况下,代码如下所示:
但是,使用 walrus 运算符,它将被简化为:
如果您试图在程序中使用 walrus 操作符作为赋值操作符,您将会得到一个语法错误:
a := func(x)
SyntaxError: invalid syntax
是因为海象“无粘性赋值表达式”被限制在顶级。通过将表达式括在括号中,仍然可以使用 walrus 运算符赋值。这将是一个有效的表达式,但不建议使用。
(a := func(45))
# 循环引用

马特·西摩在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍摄的照片
这个概念真的让我大吃一惊,我都不知道 Python 语言里有没有这样的东西!
假设您将一个列表声明为:
some_list = some_list[0] = [0, 1]
1. 这里,已经完成了三项任务。在这种情况下,先取最左边和最右边,然后取剩余的赋值。因此,some_list 变量保存值[0,1]。
2. 现在有趣的部分,some_list[0]将被分配给[0,1]。您可能认为它应该抛出一个错误,但是由于 some_list 变量已经被赋值,所以它不会引发任何错误。
3. 但是,我们也知道[0,1]首先被指定为 some_list。
4. 这意味着 some_list[0]只想引用自身。即使赋予了自我价值,它也会再次指向自身。
5. 这就产生了无限引用,称为循环引用。
6. Python 通过将这个无限引用值指定为省略号(…)来处理这个问题。是啊!如果您打印出 some_list 变量,您将看到这个值。
[[...], 1]
即使在我们的例子中尝试打印 some_list[0],也会得到如上所示的相同值。它将保持相同的价值为任何数量的水平!你可以尝试另一个非常著名的复杂例子:
a, b = a[b] = {}, 5
print(a)
这里也是,最初,a 和 b 被分配各自的值为{}和 b。对于 a[b],它将 b 值 5 分配为字典的键,但是该值指向正在更新的字典 a。因此,key 5 将保存一组省略号和值 5。
{5: ({…}, 5)}
# 结论
Python 中有很多值得关注的东西。其中一些被设计成以定义的方式发生,其他的只是由社区发现的复活节彩蛋。诸如字符串实习、GIL 限制、基于等价的字典键散列、“is”操作符用法等等。您可以通过访问这个 [GitHub 资源库](https://github.com/satwikkansal/wtfpython)查看整个列表,它是本文中讨论的所有内容的主要来源。
我希望你喜欢这篇文章,如果你喜欢,一定要表示你的支持。如果有任何疑问、疑问或潜在的机会,您可以通过 [LinkedIn](https://www.linkedin.com/in/kaustubh-gupta/) 、 [Twitter](https://twitter.com/Kaustubh1828) 或 [GitHub](https://github.com/kaustubhgupta) 联系我。
**热门文章—**
* **100k 观点俱乐部**
</building-android-apps-with-python-part-1-603820bebde8>
* **50K 观点俱乐部**
</3-ways-to-convert-python-app-into-apk-77f4c9cd55af> </run-python-code-on-websites-exploring-brython-83c43fb7ac5f>
**参考文献—**
1. [https://github.com/satwikkansal/wtfpython](https://github.com/satwikkansal/wtfpython)
2. [https://www.python.org/dev/peps/pep-0572/](https://www.python.org/dev/peps/pep-0572/)
# Python ChainMap:将多个字典视为一个
> 原文:<https://towardsdatascience.com/python-chainmap-treat-multiple-dictionaries-as-one-8cae028807e7?source=collection_archive---------13----------------------->
## **ChainMap** 的实际使用案例,为初学者提供简单易懂的示例

由[布兰登·莫温克尔](https://unsplash.com/@bmowinkel?utm_source=medium&utm_medium=referral)在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍摄的照片
你可能以前从未听说过 **ChainMap** 。 **ChainMap** 是 Python **集合模块**中提供的另一个鲜为人知的**数据容器**。
在本文中,我将尝试解释 **ChainMap** 及其用例。一旦你读完它,你将有望理解 **ChainMap** 如何帮助解决你在编码时可能遇到的一些具体问题。
# **什么是 ChainMap?**
简而言之,它允许您将多个字典视为一个。它通过对多个字典对象提供一个单一的、可更新的视图来对多个字典进行分组。
假设我们的图书馆里有一些书,我们想把它们存储在一个带有作者-书名对的字典对象中。我们可以将具有相似风格的书籍分组并存储在单独的字典对象中,而不是将它们都存储在单个字典对象中。然后,它们可以包含在一个**链图**对象中。
from collections import ChainMapthriller = {'L.Foley': 'The Guest List', 'T.Fisher ': 'The Wives'}
romance = {'E.Henry': 'Beach Read', 'A.Hall': 'Boyfriend Material'}
science_fiction = {'M.Wells': 'Network Effect', 'K.Liu': 'The Hidden Girl'}my_library = ChainMap(thriller, romance, science_fiction)str(my_library)Output: ChainMap({'L.Foley': 'The Guest List', 'T.Fisher ': 'The Wives'}, {'E.Henry': 'Beach Read', 'A.Hall': 'Boyfriend Material'}, {'M.Wells': 'Network Effect', 'K.Liu': 'The Hidden Girl'})
**ChainMap** 支持所有标准的 **dictionary 方法**,这样我们可以查询 **ChainMap** 对象,或者我们可以 **get()** , **pop()** ,**update()**chain map 的项目,就像我们对常规 dictionary 对象所做的那样。
print(my_library['E.Henry'])
print(my_library.get('L.Foley'))
print(my_library.keys())Output:
Beach Read
The Guest List
KeysView(ChainMap({'L.Foley': 'The Guest List', 'T.Fisher ': 'The Wives'}, {'E.Henry': 'Beach Read', 'A.Hall': 'Boyfriend Material'}, {'M.Wells': 'Network Effect', 'K.Liu': 'The Hidden Girl'}))
当查找时,它按照从第一个到最后一个的顺序搜索。在我们的例子中,当我们用一个关键字在**我的 _ 库**中搜索时,它从 ***惊悚*** 字典开始,然后继续**浪漫**和**科幻**直到找到一个关键字。通过考虑我们可以定义的顺序,这使得一次搜索多个字典变得容易。
from collections import ChainMapthriller = {'Genre': 'thriller', 'L.Foley': 'The Guest List', 'T.Fisher ': 'The Wives'}romance = {'Genre': 'romance','E.Henry': 'Beach Read', 'A.Hall': 'Boyfriend Material'}science_fiction = {'Genre': 'science_fiction','M.Wells': 'Network Effect', 'K.Liu': 'The Hidden Girl'}my_library = ChainMap(thriller, romance, science_fiction)
print(my_library['Genre'])Output: thriller
Chainmap 通过引用存储字典。如果其中一个更新,链图内容也会更新。
from collections import ChainMapthriller = {'Genre': 'thriller', 'L.Foley': 'The Guest List', 'T.Fisher ': 'The Wives'}romance = {'Genre': 'romance','E.Henry': 'Beach Read', 'A.Hall': 'Boyfriend Material'}science_fiction = {'Genre': 'science_fiction','M.Wells': 'Network Effect', 'K.Liu': 'The Hidden Girl'}my_library = ChainMap(thriller, romance, science_fiction)print(my_library['Genre'])thriller['Genre'] = 'thriller and crime'print(my_library['Genre'])Output:
thriller
thriller and crime
# **有什么帮助?**
**Chainmap** 比使用标准**字典**对象有三大优势;
它为多个映射对象提供了一个可更新的视图。这样你就可以一次搜索多个字典。
**它包含更多信息**,因为它具有分层结构,提供关于映射优先级的信息。当你搜索一个键值时,它从第一个映射开始搜索,并按照你将映射放入 **Chainmap** 对象的顺序继续搜索。
**如果您在应用程序中使用的映射足够大,访问起来足够昂贵,或者经常改变,它会提供更好的性能**。根据映射的大小,使用 **Chainmap 对象**而不是 o **rdinary 字典**可以提高应用程序的性能。
# 关键要点和结论
在这篇短文中,我解释了什么是链图对象,以及我们何时应该在 Python 中使用它们。关键要点是:
* Chainmap 通过对多个字典对象提供一个可更新的视图来对多个字典进行分组。
* Chainmap 有一个分层结构,它提供了关于映射优先级的信息。
* 如果您在应用程序中使用的映射足够大,访问起来足够昂贵,或者经常改变,那么它会提供更好的性能。
我希望你已经发现这篇文章很有用,并且**你将开始在你自己的代码**中使用 Chainmap 数据类型。
# 用于熊猫统计的 Python 类
> 原文:<https://towardsdatascience.com/python-classes-for-statistics-with-pandas-69360f9f1db9?source=collection_archive---------11----------------------->
## 用于从数据中生成统计见解的自定义 Python 类

马克斯·菲舍尔在[像素](https://www.pexels.com/photo/teacher-asking-a-question-to-the-class-5212345/)上拍摄的照片
在计算机编程中,类是用户定义的数据类型的蓝图。类是根据属性(数据)和方法(函数)来定义的。这些数据结构是组织数据和方法的一种很好的方式,这样它们在将来很容易重用和扩展。在本文中,我们将定义一个 python 类,它将允许我们生成简单的汇总统计数据,并对数据执行一些 EDA。
我们开始吧!
出于我们的目的,我们将使用 *FIFA 19* 数据集,可以在这里[找到](https://www.kaggle.com/karangadiya/fifa19)。
首先,让我们导入熊猫包:
import pandas as pd
接下来,让我们将显示列和行的最大数量设置为“None”:
pd.set_option('display.max_columns', None)
pd.set_option('display.max_rows', None)
现在,让我们读入数据:
df = pd.read_csv('fifa_data.csv')
接下来,我们将打印前五行数据,以了解列类型及其值(列结果被截断):
print(df.head())

首先,让我们定义一个名为“Summary”的简单类,它读入数据并在初始化时显示前五行:
class Summary:
def init(self, data):
pd.set_option('display.max_columns', None)
pd.set_option('display.max_rows', None)
self.df = pd.read_csv(data)
print(self.df.head())
如果我们用 data = 'fifa_data.csv '调用我们的 Summary 类的实例,我们得到:
Summary('fifa_data.csv')

让我们修改这个类,这样我们就有了一个可以调用来显示前五行的方法:
class Summary:
def init(self, data):
pd.set_option('display.max_columns', None)
pd.set_option('display.max_rows', None)
self.df = pd.read_csv(data)
def print_head(self):
print(self.df.head())
现在,如果我们定义一个实例,我们可以在实例上调用“print_head”方法,它将显示前五行:
data = Summary('fifa_data.csv')
data.print_head()
我们还可以修改我们的“print_head”方法,使它可以打印多于默认的五行。
class Summary:
def init(self, data):
pd.set_option('display.max_columns', None)
pd.set_option('display.max_rows', None)
self.df = pd.read_csv(data)
def print_head(self, rows):
print(self.df.head(rows))
现在,如果我们调用“行数”为 10 的打印头 get(我截断了显示的列数:):
data = Summary('fifa_data.csv')
data.print_head(10)

让我们定义一个函数来获取数据的列名:
class Summary:
...
def get_columns(self):
print(list(self.df.columns))
如果我们调用“get_columns ”,我们会得到:
data.get_columns()

我们还可以定义一个打印列数和行数的方法:
class Summary:
...
def get_dim(self):
print('Rows:', len(self.df))
print('Columns:', len(list(self.df.columns)))
如果我们对“数据”调用“get_dim”:
data.get_dim()

我们看到我们的数据有 18,207 行和 88 列。现在让我们定义一些能给我们一些统计见解的函数。Pandas 数据框有一个“describe()”方法,为我们提供一些基本的统计信息。这包括计数、平均值、标准差、最小值、最大值和四分位数:
class Summary:
...
def get_stats(self):
print(self.df.describe())
现在让我们调用' get_stats '。下面显示了“特殊”、“国际声誉”、“弱脚”、“技能移动”的统计数据:
data. get_stats()

由于数据包含 88 列,一次检查所有这些列的汇总统计数据可能有点令人畏惧。我们可以定义一个函数来计算特定列的平均值:
class Summary:
...
def get_mean(self, column):
print(f"Mean {column}:", self.df[column].mean())
让我们在“年龄”列上调用这个方法:
data.get_mean('Age')

我们可以对标准差做同样的处理:
class Summary:
...
def get_standard_dev(self, column):
print(f"STD {column}:", self.df[column].std())...data.get_standard_dev('Age')

对于分类列,我们可以使用 collections 模块中的“Counter”方法定义一种方法来显示分类值的分布。让我们将此应用于“国籍”列:
...
from collections import Counter
class Summary:
...
def get_counter(self, column):
print(dict(Counter(self.df[column])))
...
data.get_counter('Nationality')

我们可以定义一个函数来显示特定数字列的直方图。让我们将此应用于“年龄”列:
...
import matplotlib.pyplot as plt
import seaborn as sns
class Summary:
...
def get_hist(self, column):
sns.set()
self.df[column].hist(bins=100)
plt.title(f"{column} Histogram")
plt.show()
...
data.get_hist('Age')

接下来,我们可以使用箱线图来可视化分类列中不同分类值之间的数值分布:
class Summary:
...
def get_boxplot_of_categories(self, categorical_column, numerical_column, limit):
keys = []
for i in dict(Counter(self.df[categorical_column].values).most_common(limit)):
keys.append(i)
df_new = self.df[self.df[categorical_column].isin(keys)]
sns.set()
sns.boxplot(x = df_new[categorical_column], y = df_new[numerical_column])plt.show()
让我们为 5 个最常见的民族的“年龄”生成箱线图:
data.get_boxplot_of_categories(df, 'Nationality', 'Age', 5)

最后,我们可以定义一个函数来显示特定数字列的热图:
class Summary:
...
def get_heatmap(self, columns):
df_new = self.df[columns]
sns.heatmap(df_new.corr())
plt.title(f"Heatmap of {columns}")
让我们将此应用于“年龄”、“总体”、“潜力”、“特殊”:
data.get_heatmap(['Age', 'Overall', 'Potential', 'Special'])

这个类的好处在于它是一个通用的蓝图,可以重新应用于任何“csv”文件。您需要做的只是用文件名初始化实例:
data = Summary('new_file.csv')
使用“get_head”和“get_column”后,您将学习列名和类型,并能够开始应用我们在本文中使用的统计和可视化方法。我就讲到这里,但是我鼓励你自己动手处理数据和编写代码。
## 结论
总之,在这篇文章中,我们展示了如何使用 python 类通过 Pandas 库生成简单的汇总统计数据。首先,我们展示了一些基本操作,比如如何获得第一组行、列和行的长度,以及使用 describe 方法的简单统计。这些方法允许您快速获得对数据类型及其值的粗略理解。此外,我们构建了定制的统计函数,允许我们分析特定的列。当我们有太多的列需要快速分析时,这很有用。我们还展示了一些可视化数据中数值分布的方法,让我们了解哪些值接近平均值,哪些值是异常值。最后,我们讨论了如何定义一种方法来生成相关值的热图。这有助于我们想象变量是如何相互依赖的。这对于机器学习中的特征工程和模型构建非常有用。我希望你觉得这篇文章有用/有趣。这篇文章中的代码可以在 [Github](https://github.com/spierre91/medium_code/blob/master/classes_in_python/EDA_class.py) 上找到。感谢您的阅读!
# Python 干净代码:让 Python 函数更具可读性的 6 个最佳实践
> 原文:<https://towardsdatascience.com/python-clean-code-6-best-practices-to-make-your-python-functions-more-readable-7ea4c6171d60?source=collection_archive---------2----------------------->
## 停止编写需要三分钟以上才能理解的 Python 函数

图为[创意交流](https://unsplash.com/@thecreative_exchange?utm_source=medium&utm_medium=referral)在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral)
# 动机
你有没有看过自己一个月前写的函数,发现 3 分钟很难理解?如果是这样的话,是时候重构你的代码了。如果你花了超过 3 分钟来理解你的代码,想象一下你的队友需要多长时间来理解你的代码。
如果你希望你的代码是可重用的,你希望它是可读的。编写干净的代码对于与不同角色的其他团队成员合作的数据科学家来说尤其重要。
您希望您的 Python 函数:
* 变小
* 做一件事
* 包含具有相同抽象级别的代码
* 少于 4 个参数
* 没有重复
* 使用描述性名称
这些实践将使您的函数更具可读性,更容易发现错误。
受到 Robert C. Martin 的《干净的代码:敏捷软件工艺手册》一书的启发,我决定写一篇关于如何用 Python 为数据科学家编写干净代码的文章。
在本文中,我将向您展示如何利用上面提到的 6 种实践来编写更好的 Python 函数。
# 开始
我们先来看看下面的函数`load_data`。
函数`load_data`试图从 Google Drive 下载数据并提取数据。即使这个函数有很多评论,3 分钟也很难理解这个函数是做什么的。这是因为:
* 这个函数太长了
* 该函数试图做多件事
* 函数中的代码处于多个抽象层次。
* 该函数有 3 个以上的参数
* 有多处重复
* 函数名不是描述性的
我们将通过使用上面提到的 6 个实践来重构这段代码
# 小的
函数应该很小,因为更容易知道函数做什么。多小才算小?一个函数中的代码应该很少超过 20 行。可以小到如下图。函数的缩进级别不应大于一或二。
# 做一个任务
一个函数应该只完成一个任务,而不是多个任务。函数`load_data`尝试执行多项任务,例如下载数据、解压缩数据、获取包含训练和测试数据的文件名,以及从每个文件中提取文本。
因此,它应该被分成如下多个功能
而且每个功能应该只做一件事:
函数`download_zip_data_from_google_drive`只从 Google Drive 下载一个 zip 文件,其他什么都不做。
# 一个抽象层次
函数`extract_texts_from_multiple_files`中的代码与函数处于不同的抽象层次。
> 抽象层次是观察或编程一个系统的复杂程度。级别越高,细节越少。级别越低,越详细。— PCMag
这就是为什么:
* 功能`extract_texts_from_multiple_files`是一个高层次的抽象。
* 代码`list_of_text_in_one_file =[r.text for r in ET.parse(join(path_to_file, file_name)).getroot()[0]]`处于一个较低的抽象层次。
为了使函数中的代码处于相同的抽象级别,我们可以将低级代码放入另一个函数中。
现在,代码`extract_texts_from_each_file(path_to_file, file)`处于一个高抽象层次,与函数`extract_texts_from_multiple_files`处于同一抽象层次。
# 复制
下面的代码有重复。用于获取训练数据的代码部分与用于获取测试数据的代码部分非常相似。
我们应该避免重复,因为:
* 这是多余的
* 如果我们对一段代码做了更改,我们需要记住对另一段代码做同样的更改。如果我们忘记这样做,我们会在代码中引入错误。
我们可以通过将重复的代码放入函数中来消除重复。
由于从训练文件中提取文本的代码和从测试文件中提取文本的代码是相似的,我们将重复的代码放入函数`extract_tests_from_multiple_files`中。这个函数可以从训练或测试文件中提取文本。
# 描述性名称
> 一个长的描述性的名字比一个短的神秘的名字更好。一个长的描述性名称比一个长的描述性注释更好。—罗伯特·马丁的《干净的代码》
用户可以通过查看其名称来了解函数`extract_texts_from_multiple_files`的作用。
不要害怕写长名字。**写长名字比写模糊名字好。**如果你试图通过编写类似`get_texts`的代码来缩短你的代码,其他人不看源代码就很难理解这个函数到底做了什么。
如果函数的描述性名称太长,如`download_file_from_ Google_drive_and_extract_text_from_that_file`。这是一个很好的迹象,表明你的函数正在做多件事情,你应该把它分成更小的函数。
# 少于 4 个参数
一个函数的参数不应超过 3 个,因为这表明该函数正在执行多项任务。测试一个超过 3 种不同变量组合的函数也很困难。
例如,函数`load_data`有 4 个参数:`url`、`output_path`、`path_train`和`path_test` 。所以我们可能会猜测它试图同时做多件事:
* 使用`url`下载数据
* 在`output_path`保存
* 提取`output_path`中的训练和测试文件,并保存到`path_train`、`path_test`
如果一个函数有 3 个以上的参数,考虑把它变成一个类。
例如,我们可以将`load_data`分成 3 个不同的函数:
由于函数`download_zip_data_from_google_drive`、`unzip_data`和`get_train_test_docs`都试图实现一个目标:获取数据,我们可以将它们放入一个名为`DataGetter`的类中。
*旁注:在上面的代码中,我使用* `*staticmethod*` *作为一些方法的装饰器,因为这些方法不使用任何类属性或类方法。更多关于这些方法的* [*在这里*](https://realpython.com/instance-class-and-static-methods-demystified/) *。*
正如我们所看到的,上面的函数都没有超过 3 个参数!尽管使用类的代码比使用函数的代码更长,但是可读性更好!我们也确切地知道每段代码做什么。
# 我怎么写这样一个函数?
开始写代码的时候不要试图做到完美。从写下符合你想法的复杂代码开始。然后随着代码的增长,问问你自己你的函数是否违反了上面提到的任何实践。如果是,重构它。[测试一下](/pytest-for-data-scientists-2990319e55e6)。然后继续下一个功能。
# 结论
恭喜你!您刚刚学习了 6 种编写可读和可测试函数的最佳实践。由于每个函数执行一项任务,这将使您更容易测试您的函数,并确保它们在发生更改时通过单元测试。
如果你能让你的队友毫不费力地理解你的代码,他们会很乐意在其他任务中重用你的代码。
这篇文章的源代码可以在[这里](https://github.com/khuyentran1401/Data-science/tree/master/python/good_functions)找到。
我喜欢写一些基本的数据科学概念,并尝试不同的算法和数据科学工具。你可以通过 [LinkedIn](https://www.linkedin.com/in/khuyen-tran-1ab926151/) 和 [Twitter](https://twitter.com/KhuyenTran16) 与我联系。
如果你想查看我写的所有文章的代码,请点击这里。在 Medium 上关注我,了解我的最新数据科学文章,例如:
</pytest-for-data-scientists-2990319e55e6> </stop-using-print-to-debug-in-python-use-icecream-instead-79e17b963fcc> </3-python-tricks-to-read-create-and-run-multiple-files-automatically-5221ebaad2ba> </how-to-get-a-notification-when-your-training-is-complete-with-python-2d39679d5f0f>
# 参考
马丁,R. C. (2009 年)。干净的代码:敏捷软件技术手册。上马鞍河:普伦蒂斯霍尔。
# 使用 Git 预提交挂钩简化 Python 代码格式化
> 原文:<https://towardsdatascience.com/python-code-formatting-made-simple-with-git-pre-commit-hooks-9233268cdf64?source=collection_archive---------12----------------------->
## 编写每个人都会爱上的代码

使用 Black、Isort 和 Autoflake 的预提交钩子自动格式化 python 代码。——[黑暗女王](https://unsplash.com/@thdrkqwn?utm_source=medium&utm_medium=referral)在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上的照片
Python 因其优雅的语法而成为世界上最流行的编程语言。但是仅仅这样并不能保证代码清晰可读。
Python 社区已经发展到创建标准,使不同程序员创建的代码库看起来像是同一个人开发的。后来,像 Black 这样的包被创建来自动格式化代码库。然而,问题只解决了一半。Git [预提交](https://pre-commit.com/)钩子完成剩下的工作。
什么是预提交挂钩?
预提交钩子是在 git 提交之前自动运行的有用的 git 脚本。如果预提交挂钩失败,git 推送将被中止,并且取决于您如何设置它,您的 CI 软件也可能失败或根本不触发。
请注意,在设置提交前挂钩之前,请确保您具备以下条件。
* 您需要 git 版本> = v.0.99(您可以使用 git — version 检查这一点)
* 你需要安装 Python(因为它是 git 钩子使用的语言。)
# 安装提交前挂钩
您可以使用单个 pip 命令轻松安装预提交包。但是要将它附加到您的项目中,您还需要一个文件。
pip install pre-commit
*。pre-commit-config.yaml* 文件包含项目所需的所有配置。您可以在这里告诉预提交在每次提交之前需要执行什么操作,并在需要时覆盖它们的默认值。
以下命令将生成一个示例配置文件。
pre-commit sample-config > .pre-commit-config.yaml
下面是一个示例配置,它在每次提交更改之前对 requirements.txt 文件进行排序。将它放在项目目录的根目录下,或者编辑使用上一步生成的目录。
我们已经安装了预提交包,并配置了它应该如何工作。现在,我们可以使用下面的命令对这个存储库启用预提交挂钩。
厉害!现在将下面的 requirements.txt 文件添加到您的项目中,并进行提交。请参见预提交钩子的作用。
在提交之前,git 将从存储库中下载指令,并使用需求修复模块来清理文件。生成的文件将如下所示。
# 黑色带有预提交挂钩,可以自动格式化 Python 代码。
[Black](https://github.com/psf/black) 是一个流行的包,帮助 Python 开发者维护一个干净的代码库。
大多数代码编辑器都有键盘快捷键,您可以将其绑定到黑色,这样您就可以随时清理代码。例如,Linux 上的 VSCode 使用 Ctrl + Shift + I,在第一次使用该快捷键时,VScode 会提示使用哪个代码格式化程序。您可以选择黑色(或 autopep8)来启用它。
但是,如果按快捷键困扰你,你可以把它放在预提交钩子上。下面的代码片段可以达到这个目的。
请注意,这比以前的设置更多。这里,除了使用黑色,我们还覆盖了它的默认值。我们使用 args 选项配置 black 来设置 120 个字符的最大行长度。
让我们看看 git 提交钩子如何与 black 一起工作,Black 的文档中给出了一个例子。用以下内容创建一个 python 文件(名称不重要,只要是. py 文件即可)。
提交后的上述文件将如下所示。这个相比上一个更标准。它很容易阅读,代码审查人员也喜欢这样看。
# 配置预提交挂钩来查找本地存储库
有时,您希望从本地安装包运行预提交挂钩。让我们尝试使用本地安装的 [isort 包](https://github.com/PyCQA/isort)来对 python 导入进行排序。
您可以使用以下命令安装 isort。
pip install isort
现在编辑。pre-commit-config.yaml 文件并插入下面的代码片段。
要了解这一点,请创建一个包含多个导入的 python 文件。这里有一个样本。
提交后,相同的文件将如下所示。
# 使用 Black、Isort 和 Autoflake 预提交钩子来获得更整洁的 python 代码库。
这是我在几乎所有项目中使用的预提交钩子模板。我们已经在这个列表中讨论了两个钩子,Black 和 Isort。 [Autoflake](https://github.com/myint/autoflake) 是另一个有用的钩子,可以移除未使用的变量、空白和导入。
由于该模板使用本地包,请确保您已经安装了它们。您可以运行下面的命令一次性安装它们,并设置对 git 存储库的预提交。
pip install isort autoflake black pre-commit
pre-commit install
# 最后的想法
Git 预提交在很多方面都是革命性的。它们主要在 CI/CD 管道中用于触发活动。另一个主要用例是将它们用于自动代码格式化。
对于不同的人来说,格式良好的代码易于阅读和理解,因为它遵循社区中共享的通用准则。Python 有一个名为 PEP8 的标准,Black、Isort 和 Autoflake 等工具可以帮助开发人员自动化这个标准化过程。
然而,记住这一点并每次手动使用该工具可能会很麻烦。预提交钩子快速地把它放到代码审查清单中,并在每次提交前自动运行它。
在这篇文章中,我们讨论了如何从远程存储库和本地安装包中使用预提交钩子。
我希望你会喜欢它。
> 朋友,谢谢你的阅读。看来你和我有许多共同的兴趣。 ***跟我打招呼*** *上* [*领英*](https://www.linkedin.com/in/thuwarakesh/)*[*推特*](https://twitter.com/Thuwarakesh)*[*中*](https://thuwarakesh.medium.com/subscribe) *。我会为你打破僵局。***
**还不是中等会员?请使用此链接 [**成为会员**](https://thuwarakesh.medium.com/membership) 因为我为你免费推荐赚取佣金。**
# Python 可比金融数据包
> 原文:<https://towardsdatascience.com/python-comparables-financial-data-package-ee8863fe7bb1?source=collection_archive---------33----------------------->
## 利用 Aswath Damodaran 提供的全面可比数据

***注来自《走向数据科学》的编辑:*** *虽然我们允许独立作者根据我们的* [*规则和指导方针*](/questions-96667b06af5) *发表文章,但我们不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的* [*读者术语*](/readers-terms-b5d780a700a4) *。*
著名价值投资家乔尔·格林布拉特在他的《特殊情况下的投资》一书中,用了一个有趣的故事来强调使用可比数据来识别低价出售的错误定价资产的好处。
> “[……][T]姻亲遵循一个非常简单的策略。无论他们找到一件漂亮的古董家具标本[……]还是一幅印象派画作,他们在购买前只会问自己一个问题。是否有类似的家具或绘画作品最近在拍卖会上以远高于潜在购买价格的价格售出[……]”—乔尔·格林布拉特[1]
正如格林布拉特继续解释的那样,这种方法也适用于股票市场,在股票市场,将一家公司与同行进行比较是至关重要的。只有通过观察一家公司相对于同类公司的表现,投资者才能确定他们看到的是一家真正管理良好的公司,还是仅仅是有利的整体市场环境的受益者(正如巴菲特所说:“水涨船高[…]”)。
因此,任何投资决策都应该基于对可比公司数据的分析。此类可比数据的一个重要来源是“估价学院院长”NYU 教授的网站。在他的网站上,Damodaran 提供了大量不同指标的详细数据集,作为公司估值模型的重要输入。虽然这些数据集可以免费查找并以 CSV 文件的形式下载,但将这些数据集集成到 Python 估值模型中的唯一方法是通过网络搜集数据。
为了简化从 Damodaran 网站检索数据的任务,我创建了一个简单的 Python 包。这个库以可访问的 pandas 数据帧和系列的形式返回 Damodaran 的数据集,使得在定制 Python 脚本中实现数据变得更加容易。
# 安装和导入
可以使用简单的 pip install 命令安装 compdata 包:
$ pip install compdata
随后,投资者可以导入 comp_data 模块来访问市场和行业类:
from compdata import comp_data
# 市场等级
为了访问一般的市场数据,如历史回报率或税率,该软件包允许投资者创建一个市场类的实例。随后,可以使用各种类方法来检索包含数据的数据帧。
market = comp_data.Market()
macro_df = market.get_macro()
print(macro_df)
上述代码产生以下数据帧:

输出数据帧
# 行业类别
对于更具体的可比数据,投资者可以创建行业类的实例,将相应行业的名称作为参数传递。为了识别足够的数据,行业名称必须与 Damodaran 使用的术语之一相匹配,这些术语在 industry_name_list 中提供。与 Market 类非常相似,可以使用各种方法来检索数据。
print(comp_data.industry_name_list)>> ['Aerospace/Defense', ..., 'Utility (Water)', 'Total Market', 'Total Market (without financials)]aerospace = comp_data.Industry('Aerospace/Defense')
holdings = aerospace.get_holdings()
print(holdings)
以上脚本返回以下熊猫系列:

输出系列
# 结束语
我想指出的是,compdata 包只是 Damodaran 工作的一个小贡献和补充。所有收集和整合数据的功劳显然都归于阿斯瓦特·达莫达兰。他提供的数据只能以他允许的方式使用。
以下是 compdata GitHub 存储库的链接:
# 放弃
上面提供的代码只是将 Python 编程应用于金融领域的一个练习。本文和 compdata 程序包代码中包含的信息不应用于投资决策。
[1]格林布拉特,J. (2010 年)。*你可以成为股市天才*。试金石。【http://pages.stern.nyu.edu/~adamodar/】
# 解释数据科学的 Python 核心概念(带代码)
> 原文:<https://towardsdatascience.com/python-core-concepts-for-data-science-explained-with-code-decfff497674?source=collection_archive---------12----------------------->
## 每个数据科学家都应该知道的 Python 概念

照片由[米米·蒂安](https://unsplash.com/@mimithian?utm_source=medium&utm_medium=referral)在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍摄
Python 提供了大量用于数据科学的库,如 Pandas、Numpy 和 Scikit-learn。但是,立即学习这些库并跳过基础知识并不好。
如果你想学习数据科学的 Python,你应该先掌握 Python 的核心概念。拥有坚实的 Python 基础将有助于您避免常见错误和不良实践。这样一来,学习数据科学中使用的 Python 库就容易多了。
在本指南中,我们将看到每个数据科学家都应该知道的一些必备的 Python 概念。在本文的最后,您会发现一个 PDF 版本的 Python for Data Science 备忘单(见下面目录中的第 9 节)
Table of Contents 1. Python Attributes vs Methods
2. Python Data Types
3. Python Variables
4. Python Lists
5. Python Dictionary
6. Python If Statement
7. Python For Loop
8. Python Function
9. Python for Data Science Cheat Sheet (Free PDF)
# 1.Python 属性与方法
我说不清我初学 Python 的时候,交替使用“属性”和“方法”这两个词有多久了。
当你学习像 Pandas 这样的库时,你会频繁地调用属性和方法,所以知道它们之间的区别是有好处的。
* **属性**:属性是存储在类中的变量。即与对象相关联的值。使用带点的表达式按名称引用它。对于一个对象`Foo`,你称一个属性`bar`为`Foo.bar`
* **方法**:方法是定义在类体内的函数。对于一个对象`Foo`,你调用一个方法`baz`作为`Foo.baz()`
当谈到数据科学时,我们不经常使用类,所以如果您不太理解定义,请不要担心。现在,你可以记住这些符号。
在本指南中,我们将会看到很多 Python 方法的实际例子。
*不想看书?改看我的视频:*
# 2.Python 数据类型
Python 中的每个值都是一个对象。一个对象有不同的数据类型。以下是 Python 中最常见的数据类型。
1. String (str):一个字符串代表一系列字符。在 Python 中,引号内的任何内容(单引号或双引号)都是字符串。
2. 布尔值:真/假值
3. Integer (int):可以不用小数部分书写的数字。
4. Float (float):包含浮点小数点的数字。
在接下来的章节中,我们将会看到列表和字典等其他数据类型的细节。
要检查对象的数据类型,使用`type`功能。
IN [0]: type(1)
OUT[0]: intIN [1]: type(2.3)
OUT[1]: floatIN [2]: type("Hello World")
OUT[2]: str
## 字符串方法
我们可以像在 Microsoft Excel 中一样对字符串应用不同的函数。然而,在 Python 中我们使用方法。方法是“属于”一个对象的函数。为了调用一个方法,我们在对象后面使用`.`符号。
让我们来看一些改变文本大小写的字符串方法。
IN [3]: "Hello World".upper()
OUT[3]: HELLO WORLDIN [4]: "Hello World".lower()
OUT[4]: hello worldIN [5]: "hello world".title()
OUT[5]: Hello World
# 3.Python 变量
变量帮助我们存储数据值。在 Python 中,我们经常处理数据,因此变量对于正确管理这些数据非常有用。为了给变量赋值,我们使用了`=`符号。
让我们创建一条“我正在学习 Python”的消息,并将其存储在一个名为`message_1`的变量中。
message_1 = "I'm learning Python"
我们可以创造尽可能多的变量。只要确保给新变量赋予不同的名字。让我们创建一个新的信息,说“这很有趣!”并将其存储在一个名为`message_2`的变量中。
message_2 = "and it's fun!"
## **字符串串联**
我们可以通过使用`+`操作符将`message_1`和`message_2`放在一起。这称为字符串串联。
IN [6]: message_1 + message_2
OUT[6]: "I'm learning Pythonand it's fun!"
我们需要添加一个空格`“ ”`使文本可读。这一次,让我们将这个新消息存储在一个名为`message`的新变量中
message = message_1 + ' ' + message_2IN [7]: message
OUT[7]: "I'm learning Python and it's fun!"
现在你可以改变写在`message_1`或`message_2`中的信息,这也将改变我们新的`message`变量。
# 4.Python 列表
在 Python 中,列表用于在单个变量中存储多个项目。列表是有序且可变的容器。在 Python 中,我们把可以改变值的对象称为“可变的”。也就是说,列表中的元素可以改变它们的值。
为了创建一个列表,我们必须引入方括号`[]`中的元素,用逗号分隔。
考虑以下名为“countries”的列表,其中包含了世界上人口最多的一些国家。
countries = ['United States', 'India', 'China', 'Brazil']
请记住,列表可以有不同类型的元素和重复的元素。
## 列表索引
通过索引,我们可以根据列表项或元素的位置来获取它们。列表中的每个项目都有一个索引(在列表中的位置)。Python 使用从零开始的索引。也就是说,第一个元素(“美国”)的索引为 0,第二个元素(“印度”)的索引为 1,依此类推。
为了通过索引访问一个元素,我们需要使用方括号`[]`。
IN [8]: countries[0]
OUT[8]: United StatesIN [9]: countries[1]
OUT[9]: IndiaIN [10]: countries[3]
OUT[10]: Brazil
也有负索引帮助我们从列表的最后一个位置开始获取元素,所以我们不使用从 0 及以上的索引,而是使用从-1 及以下的索引。
让我们得到列表的最后一个元素,但是现在使用负索引。
IN [11]: countries[-1]
OUT[11]: Brazil
## 限幅
切片意味着访问列表的一部分。切片是列表元素的子集。切片符号具有以下形式:
list_name[start:stop]
其中“start”表示第一个元素的索引,stop 表示要停止的元素(不包括在切片中)。
让我们看一些例子:
IN [12]: countries[0:3]
OUT[12]: ['United States', 'India', 'China']IN [13]: countries[1:]
OUT[13]: ['India', 'China', 'Brazil']IN [14]: countries[:2]
OUT[14]: ['United States', 'India']
## 列出操作和方法
你可以用列表做很多事情。您可以添加新元素、删除任何元素、更新值等等。在这。部分,我们将看到最常见的列表操作和方法。
**向列表添加元素** 有不同的方法可以帮助我们向列表添加新元素。让我们使用`countries`列表测试它们(我们将在每个测试中定义`countries`列表,以相同数量的元素开始)
append(): Adds a new element at the end of a list
countries = ['United States', 'India', 'China', 'Brazil']
IN [15]: countries.append('Canada')
IN [16]: countries
OUT[16]: ['United States', 'India', 'China']# insert(): Adds a new element at a specific position
countries = ['United States', 'India', 'China', 'Brazil']
IN [17]: countries.insert(0,'Canada')
IN [18]: countries
OUT[18]: ['Canada', 'United States', 'India', 'China', 'Brazil']
我们甚至可以使用`+`来连接列表。
countries_2 = ['UK', 'Germany', 'Austria']IN [19]: countries + countries_2
OUT[19]:
['Canada',
'United States',
'India',
'China',
'Brazil',
'UK',
'Germany',
'Austria']
我们可以把列表`countries`和`countries_2`放在另一个列表中,这叫做嵌套列表。
nested_list = [countries, countries_2]IN [20]: nested_list
OUT[20]:
[['Canada', 'United States', 'India', 'China', 'Brazil'],
['UK', 'Germany', 'Austria']]
**移除元素** 有不同的方法可以帮助我们从列表中移除元素。让我们用我们创建的第一个`countries`列表来测试它们。
remove(): Removes the first matching value.
IN [21]: countries.remove('United States')
IN [22]: countries
OUT[22]: ['India', 'China', 'Brazil']# pop(): Removes an item at a specific index and then returns it.
IN [23]: countries.pop(0)
IN [24]: countries
OUT[24]: ['China', 'Brazil']# del: Removes an item at a specific index
IN [25]: del countries[0]
IN [26]: countries
OUT[26]: ['Brazil']
**排序列表**
我们可以很容易地使用`.sort()`方法对列表进行排序。让我们创建一个名为`numbers` 的新列表,然后从最小到最大的数字对其进行排序。
numbers = [4, 3, 10, 7, 1, 2]
IN [27]: numbers.sort()
IN [28]: numbers
OUT[28]: [1, 2, 3, 4, 7, 10]
我们可以在`.sort()`方法中添加`reverse`参数来控制顺序。如果我们希望它是后代,我们设置`reverse=True`。
numbers.sort(reverse=True)
**更新列表中的值**
为了更新列表中的值,我们使用索引来定位我们想要更新的元素,然后使用`=`符号将其设置为新值。
numbers[0] = 1000
IN [29]: numbers
OUT[29]: [1000, 2, 3, 4, 7, 10]
**复制列表**
每当我们需要复制一个已有的列表时,我们可以使用以下选项:
option 1: [:]
new_list = countries[:]# option 2: .copy()
new_list_2 = countries.copy()
`[:]`来源于我们之前学过的刀法。在这种情况下,我们既没有设置开始值,也没有设置停止值,因此选择了整个列表。另一方面,`.copy()`方法显式地复制了一个列表。
请注意,使用下面的代码复制一个对象不会像我们使用前面的选项那样创建一个独立的副本。
new_list_3 = countries
# 5.Python 词典
在 Python 中,字典帮助我们将数据存储在`key:value` 对中(一对称为一个项目)。和列表一样,字典也是可变的,但是和列表不同,字典是通过键访问的,不允许使用重复的键。
字典有以下形式:
my_dict = {'key1':'value1', 'key2':'value2'}
让我们用一些关于我的信息来创建我们的第一本字典。
my_data = {'name':'Frank', 'age':26}
让我们看看如何在字典中访问一个值以及一些常用的方法。
my_data['name'] # returns value that corresponds to "name" key
my_data.keys() # returns all keys of the dictionary
my_data.values() # returns all values of the dictionary
my_data.items() # returns the pairs key-value of a dictionary
## 添加和更新元素
类似于列表,我们可以在字典中添加和更新值。让我们从添加一个新的表示我的身高的对键值开始。
my_data['height']=1.7
运行上面的代码后,字典获得了一个新条目。
IN [30]: my_data
OUT[30]: {'name': 'Frank', 'age': 26, 'height': 1.7}
如果我添加的数据不正确,我们可以用`.update()`方法更新它,甚至添加更多的数据。
让我们更新我的身高,并添加一个新的关键。
my_data.update({'height':1.8,'languages':['English', 'Spanish']})
现在我的字典有以下条目:
{'name': 'Frank',
'age': 26,
'height': 1.8,
'languages': ['English', 'Spanish']}
## 复制字典并删除元素
我们可以使用。方法来制作字典的副本。
new_dict = my_data.copy()
`new_dict`字典将拥有与原来的`my_data` 字典相同的条目。
我们可以使用列表中使用的相同方法来删除字典条目。
pop(): Removes an item by specifying its key
IN [35]: my_data.pop('height')
IN [36]: my_data
OUT[36]: {'name': 'Frank', 'age': 26, 'languages': ['English', 'Spanish']}# del: Removes an item with a specific key
IN [37]: del my_data['languages']
IN [38]: my_data
OUT[38]: {'name': 'Frank', 'age': 26}# clear(): Removes all items in a dictionary
IN [39]: my_data.clear()
IN [40]: my_data
OUT[40]: []
# 6.如果语句
if 语句是 Python 中最常用的语句之一。这是一个条件语句,用来决定一个语句(或语句块)是否被执行。
下面是 if 语句的语法:
if
elif
...
else:
让我们通过一个简单的例子来看看 if 语句的实际应用。下面的代码将根据我们在一个`age`变量中引入的值输出一条消息。
age = 18if age>=18:
print("You're an adult!")
elif age>=13:
print("You're a teenager!")
else:
print("You're a kid")
下面的代码是说“如果年龄等于或大于 18 岁,你是成年人;如果年龄在 13 到 17 岁之间,你是青少年;如果年龄小于 13 岁,你就是个孩子”
但这还不是全部!我们可以使用带有关键字`in`的`if`语句来验证一个元素是否在列表中。
让我们核实一下中国是否在我们的“国家”列表中
countries = ['United States', 'India',
'China', 'Brazil']if 'China' in countries:
print("Country is in list")
else:
print("Not in list")
在本例中,输出将是“国家在列表中”
# 7.For 循环
for 循环是 Python 中经常使用的循环。它允许我们遍历一个 iterable 对象(例如,列表、字典)并对每个元素执行相同的操作。
下面是 for 循环的语法:
for in :
让我们遍历我们的`countries`列表并打印所有的元素。
for country in countries:
print(country)United States
India
China
Brazil
我们可以一起使用 for 循环和 if 语句来只对某些元素执行操作。例如,让我们遍历同一个列表,但现在只打印元素“美国”
for country in countries:
if country == "United States":
print(country)United States
## 枚举循环中的元素
有时候我们需要在一个循环中枚举元素。我们可以用 for 循环来实现。
for i, element in enumerate():
让我们循环并枚举`countries`列表中的元素。
for i, country in enumerate(countries):
print(i)
print(country)0
United States
1
India
2
China
3
Brazil
## 遍历字典元素
我们可以在遍历字典时分别获得键和项。我们只需要使用`.items()`方法。
for key, value in my_dict.items():
让我们遍历`my_data`字典并获取键和值。
for key, value in my_data.items():
print(key)
print(value)
# 8.Python 函数
Python 有很多内置函数,可以帮助我们执行特定的任务。我们来看看其中最常见的。
len() -> calculates the length of any iterable object
max() -> returns the item with the highest value in an iterable
min() -> returns the item with the lowest value in an iterable
type() -> returns the type of an object
range() -> returns a sequence of numbers
我们也可以创建自己的函数。下面是用于创建函数的语法:
def function():
return
看起来很简单,对吗?让我们创建一个对两个值`a`和`b`求和的函数。
def sum_values(a,b):
x = a+b
return x
每次我们调用函数`sum_values`时,我们都会对`a`和`b`的值求和。
IN []: sum_values(2,3)
OUT[]: 5
就是这样!在学习任何数据科学库之前,这些都是您需要了解的 Python 核心概念。
# 用于数据科学的 Python 备忘单
[**与 3k 以上的人一起加入我的电子邮件列表,获取我在所有教程中使用的 Python for Data Science 备忘单(免费 PDF)**](https://frankandrade.ck.page/bd063ff2d3)
如果你喜欢阅读这样的故事,并想支持我成为一名作家,可以考虑报名成为一名媒体成员。每月 5 美元,让您可以无限制地访问数以千计的 Python 指南和数据科学文章。如果你使用[我的链接](https://frank-andrade.medium.com/membership)注册,我会赚一小笔佣金,不需要你额外付费。
<https://frank-andrade.medium.com/membership>
# Python 计数器:学习用 Python 计算对象的最简单方法
> 原文:<https://towardsdatascience.com/python-counter-learn-the-easiest-way-of-counting-objects-in-python-9165d15ea893?source=collection_archive---------9----------------------->
## 为初学者提供易于理解的示例

[斯蒂夫·约翰森](https://unsplash.com/@steve_j?utm_source=medium&utm_medium=referral)在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍照
开发应用程序时,您可能需要完成一些任务,包括对容器对象中的元素进行计数。您可能需要计算文本中的词频,或者您可能想知道列表对象中最常用的数字。对于这种情况,Python 在其**集合模块**中提供了一个方便的工具,一个名为 **Counter()** 的内置类。
在这篇短文中,我将尝试用简单易懂的例子向初学者解释如何使用**计数器**类。
# **Python 中的计数器类**
你可以在下面的[**docs.python.org**](https://docs.python.org/3/library/collections.html#collections.Counter)找到计数器类的解释
> 一个`[Counter](https://docs.python.org/3/library/collections.html#collections.Counter)`是一个`[dict](https://docs.python.org/3/library/stdtypes.html#dict)`子类,用于计数可散列对象。它是一个集合,其中的元素存储为字典键,它们的计数存储为字典值。计数可以是任何整数值,包括零或负计数。`[Counter](https://docs.python.org/3/library/collections.html#collections.Counter)`类类似于其他语言中的包或多集。
简而言之, **Counter** 接受一个 iterable 对象(比如一个字符串、一个列表或一个 dict 对象),并给出对象计数,只要这些对象是可散列的。
在下面的代码片段中,我创建了一个列表、一个字符串和一个 dictionary 对象。当它们被传入**计数器类**时,它返回一个字典,其中键是元素,它们的计数是返回的字典的值。
from collections import Countera_list = [1,2,2,3,4,4,4]
a_string = 'data'
a_dict = {'a': 5, 'b':3, 'c':5, 'd':5, 'e':1 }#counting objects in a list
c_list = Counter(a_list)#counting characters in a string
c_string = Counter(a_string)#counting values in a dictionary
c_dict = Counter(a_dict.values())print (c_list)
print (c_string)
print (c_dict)Output:
Counter({4: 3, 2: 2, 1: 1, 3: 1})
Counter({'a': 2, 'd': 1, 't': 1})
Counter({5: 3, 3: 1, 1: 1})
# **对不可清洗的对象使用计数器类**
请注意,如果容器对象中的元素是不可哈希的类型,比如 list 对象,就会出现错误。
from collections import Countera_list = [1,2,[5,6,7,7],2,3,4,4,4]#counting objects in a list
c_list = Counter(a_list)print (c_list)Output: TypeError: unhashable type: 'list'
解决方法是在将对象传递给**计数器类之前,将任何列表元素转换成**元组**。请看下面你如何用一行代码实现它;**
from collections import Countera_list = [1,2,[5,6,7,7],2,3,4,4,4]#counting objects in a list which has unhashable list object c_list = Counter(tuple(item) if type(item) is list else item for item in a_list)print (c_list)Output: Counter({4: 3, 2: 2, 1: 1, (5, 6, 7, 7): 1, 3: 1})
由于列表元素被转换为元组对象,现在 Counter 类工作时没有错误消息。
# 计数器类中的方法
**Counter()** 是**字典类**的子类,因此**计数器对象**支持字典中可用的方法。除此之外,还有另外三种方法是计数器类独有的。
## 寻找最常见的元素
为了找到集合对象中最常见的元素,可以使用`**most_common**` ([ *n* ])方法。
`**elements**`()方法返回一个遍历元素的迭代器,每个元素重复它的计数。
使用`**subtract**`([*iterable-or-mapping*])方法,您可以从 iterable 或 dictionary 对象中减去元素计数。
from collections import Countera_text = 'When developing your application, you may need to overcome some tasks involving counting the elements in a container object. You may need to count word frequencies in a text or you may want to know the most frequently used number in a list object.'a_text_splited = a_text.split()
a_counter = Counter(a_text_splited)
most_occur = a_counter.most_common(5)print(most_occur)
print(sorted(a_counter.elements()))Output: [('may', 3), ('to', 3), ('in', 3), ('a', 3), ('you', 2)]['When', 'You', 'a', 'a', 'a', 'application,', 'container', 'count', 'counting', 'developing', 'elements', 'frequencies', 'frequently', 'in', 'in', 'in', 'involving', 'know', 'list', 'may', 'may', 'may', 'most', 'need', 'need', 'number', 'object.', 'object.', 'or', 'overcome', 'some', 'tasks', 'text', 'the', 'the', 'to', 'to', 'to', 'used', 'want', 'word', 'you', 'you', 'your']
# 关键要点和结论
在这篇短文中,我解释了 Python **Counter() class** 以及它如何帮助您对 iterable 中的对象进行计数。关键要点是:
* 计数器接受一个可迭代对象(比如一个字符串,一个列表,或者一个字典对象),只要对象是可哈希的,就给出对象的计数。
* 使用**计数器类**时,如果容器对象中的元素是不可销毁的类型,比如 list 对象,就会出现错误。解决方法是在将对象传递给**计数器类之前,将任何列表元素转换成**元组**。**
* 为了找到一个集合对象中最常见的元素,可以使用`**most_common**` ([ *n* ])方法。
我希望你已经发现这篇文章很有用,并且**你将开始在你自己的代码中使用 Python 计数器类**。
# 安全地测试并应用对数据库的更改:Alembic 入门
> 原文:<https://towardsdatascience.com/python-database-migrations-for-beginners-getting-started-with-alembic-84e4a73a2cca?source=collection_archive---------6----------------------->
## 使用这个简单的 Python 工具对数据库进行版本控制

迁移就像蓝图;它们是建立你的数据库的说明(图片由[jeshouts](https://www.pexels.com/@jeshoots-com-147458)在[像素](https://www.pexels.com/photo/floor-plan-on-table-834892/)上提供)
如果您没有在数据库中处理迁移,那么您就错过了。数据库迁移在代码中建立了数据库的结构和变更历史,并为我们提供了安全地应用和恢复数据库变更的能力。
在这篇文章中,我们将看看一个 Alembic 一个 Python 工具,使迁移工作变得非常简单。有了它,我们将创建与数据库无关的迁移(这意味着您可以在任何数据库上执行它们。这些迁移是用代码定义的,这意味着我们可以应用类似 Git 的版本控制;跟踪变更,拥有单一的事实来源,以及与多个开发人员协作的能力。当您阅读完本文后,您将能够:
* 用代码定义对数据库结构的更改
* 版本控制变更
* 应用和恢复迁移
* 将迁移应用到多个数据库(例如,在开发数据库上进行测试,然后将最终结构迁移到生产数据库)
* 不仅要创建所需的表,还要添加索引和关联
为了向你展示给同事留下深刻印象所需的所有特征,我定义了 5 个步骤:
1. 安装和设置
2. 创建迁移
3. 执行和撤消迁移
4. 将更改应用到现有数据库
5. 将新的迁移应用到不同的数据库
# 1.安装和设置
我们将使用一个名为 Alembic 的 Python 包。这个包使得创建数据库迁移变得很容易。
## 装置
安装很简单。打开一个终端,导航到您的项目目录并执行下面的代码。这将安装并初始化 Alembic。注意,在根目录下创建了一个名为`/alembic`的文件夹。
*不熟悉使用终端?查看* ***下面的文章***
</terminals-consoles-command-line-for-absolute-beginners-de7853c7f5e8>
pip install alembic
alembic init alembic
接下来,我们将建立一个数据库连接。Alembic 必须知道要连接到哪个数据库。通常我们会通过调整 sqlalchemy.url 在`/alembic.ini`中指定这个值,但是最好覆盖这个值。打开`/alembic/env.py`,找到`config = context.config`。在此下方插入以下内容:
***保护您的凭证安全:建议从环境变量中加载连接字符串。*** *如果您将此项目上传到公共存储库,任何人都可以看到您的凭据。使用环境变量,您可以通过将所有机密信息(如密码和连接字符串)放在一个文件中来保护您的凭证安全,我们可以* `*.gitignore*` *。在下面的文章中看看这是如何工作的:*
</keep-your-code-secure-by-using-environment-variables-and-env-files-4688a70ea286>
让我们通过在终端中运行`alembic current`来测试我们与的连接。这显示了我们的数据库目前的状态。如果您看到类似下面的文字,说明您已经成功连接到数据库。
INFO [alembic.runtime.migration] Context impl PostgresqlImpl.
INFO [alembic.runtime.migration] Will assume transactional DDL.
# 2.创建迁移
执行下面的代码来创建您的第一个迁移:
`alembic revison -m "create test schema"`
这将在`/alembic/versions`中创建一个新文件。这个文件是我们实际的迁移,它包含两个函数:
1。升级
2。使降低
在第一个函数中,我们将放入想要执行的代码,例如创建一个模式。在第二个函数中,我们将在第一个函数中放置撤销操作的代码,例如删除模式:
在升级中,我们告诉 alembic 执行一些 SQL 来创建一个模式。
在降级中,我们放置指令来撤销这一点,删除模式。
很清楚,对吧?让我们创建一些表格!我们将创建三个表:学生、班级和学生 _ 班级。显然,学生和班级将是存储学生和班级信息的表。Student_classes 将一个或多个学生 id 连接到一个或多个班级 id。这是因为一个学生可以有多个班级,而一个班级可以有多个学生:
**学生表**
正如你所看到的,我们使用了`op.create_table`函数来创建一个表格。首先我们将指定表名,然后指定所有列。最后,我们指定模式名。如果您只想使用默认模式,可以省略。还要注意,我们添加了像主键和“可空”这样的约束。降级功能再次撤销升级。
**阶级表** 这里我们只看升级功能:
student_classes 表
在这个表中我们需要做一些花哨的东西。因为该表的存在只是为了将学生绑定到班级,反之亦然,所以我们可以定义更多的功能:
您可以看到,在上面的代码中,我们用 ondelete="CASCADE "标志定义了 foreignKeys。这意味着如果一个班级或一个学生从他们的表中被删除(一个学生毕业或一个班级被终止),那么这些删除会级联到 student_class 表中;删除相关记录。这使我们的数据保持整洁。

我们的数据库正在建设中(图片由[斯科特·布莱克](https://unsplash.com/@sunburned_surveyor)在 [Unsplash](https://unsplash.com/photos/x-ghf9LjrVg) 上提供)
# 3.执行和撤消迁移
现在我们已经定义了所有的表和关系,是时候将它们反映到数据库中了。在您的终端中只需执行:
`alembic upgrade head`
这告诉 alembic 升级所有脚本,直到像 Git 中的 head。
之后,我们可以调用`alembic history`来显示如下内容:
(venv) C:\rnd\med_alembic>alembic history
a3e1bb14f43b -> a46099cad4d5 (head), student_classes
058a1e623c60 -> a3e1bb14f43b, create classes table
73d31032477a -> 058a1e623c60, create students table
Revision ID: 058a1e623c60
Revises: 73d31032477a
Create Date: 2021–10–22 12:58:23.303590
-> 73d31032477a, create test schema
```
这显示了我们执行的所有迁移,前面有一个代码。我们可以用这个代码来降级像:
alembic downgrade 058a1e623c60
这将撤消所有迁移,直到创建“创建学生表”迁移。
返回一个迁移:alembic downgrade -1
(数字,不是 L)
恢复所有迁移:alembic downgrade base
重置数据库
如果你仍然在测试和开发数据库,你可以很容易地重置。假设您插入了一些数据,并在一次迁移中发现了一个错误;可能是错误的列名。只需调整升级函数中的错误并调用:
alembic downgrade base && alembic upgrade head
这将在一条语句中重置您的整个数据库!虽然这对您的开发数据库非常方便,但我不建议在您的生产数据库上执行此命令,因为所有数据都将丢失
4.将更改应用到现有表格
有时您需要对现有的表格进行调整;重命名列、调整数据类型或添加索引或约束。使用 Alembic,您可以通过首先在开发数据库上开发它们来设计、测试和验证这些更改。一旦您对一切都满意了,您就可以插入一个不同的.env
文件,并将所有新的更改迁移到生产数据库!下面是一些如何做到这一点的例子。
添加约束
假设这些表已经投入生产,但是缺少了一些东西。首先,我们要向 classes 表中的 name 列添加一个惟一约束;我们不能有多个同名的类。我们用
alembic revision -m “create unique constraint classes.name”
创建一个新的迁移
然后添加以下内容:
就这么简单!
添加索引
下一个问题:我们的学生表增长了很多。我们来加个索引吧!
alembic revision -m “add index on students.name
下面的代码将创建索引。
重命名列和更改数据类型
使用 alter_column 函数可以更改重命名列、更改数据类型和调整某些约束(可为 null 等):
这会将列名从“name”更改为“student_name ”,并将从字符串()更改为最大长度为 100 个字符的字符串。
请注意,降级再次撤销了我们所有的更改。
别忘了检查一下alembic upgrade head
前后的区别!
![]()
我们的数据库被迁移了!(图片由 Ubay Seid 在 Unsplash 上拍摄)
5.将新的迁移应用到不同的数据库
不,我们已经在 Postgres dev 数据库上测试了所有迁移,现在是时候将所有更改迁移到我们的生产数据库了。为此,我们只需连接到生产数据库并执行迁移。最好的方法是使用一个安全保存所有连接信息的文件。
还记得第 1 部分的/alembic/env.py
文件吗?这是我们定义数据库连接字符串的地方。我们所要做的就是将不同的.env
文件插入到 Alembic 中。然后,我们可以从环境中加载连接字符串的各个部分。在这种情况下,我将使用python-dotenv
包来加载.env
文件,并创建如下所示的连接字符串。更多关于如何使用 env 文件这里 。
db_user = os.environ.get("DB_USER")
db_pass = os.environ.get("DB_PASS")
db_host = os.environ.get("DB_HOST")
db_name = os.environ.get("DB_NAME")
db_type = os.environ.get("DB_TYPE")
config.set_main_option('sqlalchemy.url', f'{db_type}://{db_user}:{db_pass}@{db_host}/{db_name}')
结论
数据库迁移为我们提供了安全测试和应用数据库更改的能力。因为我们写出了代码中的所有更改,所以我们可以将它们包含到我们的版本控制中,这提供了更多的安全性。总而言之,使用 Alembic 简单、安全,是设计、测试和更改数据库的好主意。我希望已经阐明了这些迁移是如何工作的,以及如何使用 Alembic。
如果你有建议/澄清,请评论,以便我可以改进这篇文章。同时,看看我的其他关于各种编程相关主题的文章:
- 用 FastAPI 用 5 行代码创建一个快速自动记录、可维护且易于使用的 Python API
- 从 Python 到 SQL——安全、轻松、快速地升级
- 创建并发布你自己的 Python 包
- 创建您的定制私有 Python 包,您可以从您的 Git 库 PIP 安装该包
- 面向绝对初学者的虚拟环境——什么是虚拟环境以及如何创建虚拟环境(+示例)
- 通过简单升级,显著提高数据库插入速度
编码快乐!
—迈克
页(page 的缩写)学生:比如我正在做的事情?跟着我!
python:OOP 中的装饰者
关于类方法、静态方法和属性装饰器的指南
![]()
作者图片
面向对象的编程范例在 60 年代和 70 年代流行起来,出现在 Lisp 和 Smalltalk 等语言中。这些特性也被添加到现有的语言中,如 Ada、Fortran 和 Pascal。
Python 是一种面向对象的编程语言,尽管它不支持强封装。
Python 中面向对象编程的介绍性主题——更一般地说——包括定义类、创建对象、实例变量、继承的基础知识,甚至可能包括一些特殊的方法,如__str__
。但是当我们有一个高级的外观时,我们可以谈论像装饰器的使用,或者写一个定制的new
方法,元类,和多重继承。
在这篇文章中,我们将首先讨论什么是装饰器,然后讨论 classmethods 和 staticmethods 以及属性装饰器。
Classmethods、staticmethods 和 property 是描述符的例子。这些是实现__get__
、__set__
或__delete__
方法的对象。
但是,这是另一篇文章的主题。
目录
我们将在本文中讨论以下内容:
**- what are decorators?
- classmethods
- staticmethods
- @property**
一个例子
让我们来看一个简单的例子:一个学生类。
目前,这个类有两个变量:
- 名字
- 年龄
- 得分
当提供了这两个属性时,我们将添加一个简单的__init__
方法来实例化一个对象。
我们将在整篇文章中对此进行修改。
装修工
装饰器是为原始函数(或类)提供增强功能的函数(或类),而程序员不必修改它们的结构。
一个简单的例子?
假设我们想在我们的Student
类中添加一个方法,该方法获取学生的分数和总分,然后返回一个百分比。
![]()
get_percent 函数—作者使用 draw.io 生成的图像
我们的百分比函数可以这样定义:
让我们定义我们的装饰者,创造性地命名为record_decorator
。它将一个函数作为输入,并输出另一个函数(本例中为wrapper
)。
![]()
我们的装饰者——作者使用 draw.io 绘制的图像
wrapper
功能:
- 接受我们的两个参数
score
和total
- 调用传递给
grade_decorator
的函数对象
- 然后计算与分数相对应的等级。
- 最后,它返回计算出的百分比和分数。
![]()
如何应用装饰器——作者使用 draw.io 绘制的图像
我们可以这样实现我们的装饰器。
现在,为了改进get_percent
函数,只需在我们的函数上方使用带有修饰名称的@
符号,它的定义与之前完全相同。
要使用它,我们不需要修改 call 语句。执行此操作:
**get_percent(25, 100)**
返回
**25.0, D**
基本上发生的事情是,当我们应用装饰器时,函数get_percent
被替换为wrapper
。
我们将把get_percent
方法放在Student
类中,把装饰器放在类外。由于get_percent
是一个实例方法,我们给它添加了一个self
参数。
装饰者在课堂上是如何使用的?
我们将看到在类中使用的三个流行的装饰器和它们的用例。
![]()
我们的 A 类中的各种方法—图片由作者使用 draw.io
分类方法
先说实例方法。实例方法是那些被对象调用的方法,因此被传递关于该对象的信息。按照惯例,这是通过self
参数完成的,当调用该方法时,对象的信息通过self
隐式传递。
例如,我们可以在我们的类中添加一个方法来计算学生的分数和百分比(使用 get_percent 方法),并生成一个包含学生姓名、百分比和分数的字符串形式的报告。
来到类方法,这种类型的函数在类上被调用,因此,它需要一个类被传递给它。按照惯例,这是通过cls
参数完成的。我们还将@classmethod
装饰器添加到我们的类方法中。
它看起来像这样:
class A:
def instance_method(self):
return self **@classmethod
def class_method(cls):
return cls**A.class_method()
类方法的用例
由于类方法处理的是类,而不是实例,所以它们可以被用作工厂模式、的一部分,在工厂模式中,对象是基于某些参数返回的。
例如,在 pandas 中创建数据帧有多种方法。方法有:from_records()
、from_dict()
等。它们都返回一个 dataframe 对象。尽管它们的实际实现相当复杂,但它们基本上是像字典一样,对其进行操作,然后在解析数据后返回 dataframe 对象。
回到我们的例子,让我们定义一些方法来创建我们的Student
类的实例。
- 由两个独立的自变量:例如,
、20 和 85
- 由逗号分隔的字符串:例如,“
,20,85”。
- 通过元组:例如,(
,20,85)
要在 Java 中实现这一点,我们可以简单地重载我们的构造函数:
在 python 中,一种干净的方法是通过类方法:
我们还定义了__str__
方法,这样我们就可以直接打印一个 Student 对象,看看它是否被正确实例化了。我们的类现在看起来像这样:
现在,为了测试这一点,让我们创建三个Student
对象,每个对象来自不同类型的数据。
从上面的__str__
方法的定义来看,输出完全符合预期:
Name: John Score: 25 Total : 100
Name: Jack Score: 60 Total : 100
Name: Jill Score: 125 Total : 200
静态方法
静态方法不关心实例,像实例方法一样。它不需要向它隐式传递一个类。
静态方法是放置在类中的常规方法。可以使用类和对象来调用它。我们使用@staticmethod
装饰器来实现这些方法。
一个简单的例子:
class A:
def instance_method(self):
return self @classmethod
def class_method(cls):
return cls **@staticmethod
def static_method():
return**a = A()a.static_method()
A.static_method()
静态方法的用例
为什么这会有用?为什么不把这样的函数放在类的外面呢?
当将函数放在类中更有意义时,使用静态方法代替常规函数。例如,放置只处理类或其对象的实用方法是一个好主意,因为这些方法不会被其他任何人使用。
回到我们的例子,我们可以让我们的get_percent
方法成为静态的,因为它服务于一个通用的目的,不需要绑定到我们的对象。为此,我们可以简单地在get_percent
方法之上添加@staticmethod
。
财产
属性装饰器提供了访问( getter )、修改( setter )和删除( deleter )对象属性的方法。
![]()
房产装饰者——作者使用 draw.io 绘制的图片
getter 和 setter 方法
让我们从 getter 和 setter 方法开始。这些方法分别用于访问和修改私有实例。在 Java 中,我们会这样做:
现在,无论何时访问或修改这个值,都可以使用这些方法。因为变量x
是私有的,所以它不能在JavaClass
之外被访问。
在 python 中,没有private
关键字。我们在变量前加上一个 dunder( __
)来表明它是私有的,不应该被直接访问或修改。
在变量名前加一个__
会把那个变量名从varname
修改成_Classname__varname
,所以像print(obj.varname)
和obj.varname = 5
这样的直接访问和修改是行不通的。尽管如此,这并不是很强,因为您可以用修改后的表单直接替换varname
,以直接进行修改。
让我们以下面的例子来理解这一点:
添加 getter 和 setter 方法
以我们的学生类为例,让我们通过在变量名前添加一个 __ 来使 score 属性成为“private”。
如果我们像 Java 一样直接添加get_score
和set_score
,主要问题是,如果我们想对现有代码这样做,我们必须将每个访问从:
**print("Score: " + str(student1.score))**
**student1.score = 100**
对此:
**print(student1.get_score())
student1.set_score(100)**
这就是@property
装饰师的用武之地。您可以使用此功能简单地定义getter
、setter
和deleter
方法。
我们的类现在看起来像这样:
要使 **score**
属性只读,只需去掉 setter 方法即可。
然后,当我们更新score
时,我们得到以下错误:
Traceback (most recent call last):
File "main.py", line 16, in <module>
student.score = 10
**AttributeError: can't set attribute**
删除器方法
deleter
方法允许您使用del
函数删除受保护的或私有的属性。使用与前面相同的示例,如果我们直接尝试删除 score 属性,我们会得到以下错误:
student = Student("Tom", 50, 100)
del student.scoreThis gives:
Traceback (most recent call last):
File "<string>", line 17, in <module>
**AttributeError: can't delete attribute**
但是当我们添加一个 deleter 方法时,我们可以删除我们的私有变量score
。
该属性现在已成功删除。打印出score
的值给出“对象没有属性……”,因为我们删除了它。
Traceback (most recent call last):
File "<string>", line 23, in <module>
File "<string>", line 9, in x
**AttributeError: 'PythonClass' object has no attribute '__score'**
属性装饰器的用例
当定义数据验证方法时,属性装饰器是非常有用的,比如当决定一个要赋值的值是否有效并且不会导致代码中的问题时。
另一个用例是当想要以特定方式显示信息时。回到我们的例子,如果我们想显示一个学生的名字为“Student Name: <name>”
,而不仅仅是<name>
,我们可以从 name 属性的属性 getter 返回第一个字符串:
现在,任何时候我们访问name
,都会得到一个格式化的结果。
student = Student("Bob", 350, 500)
**print(student.name)**
输出:
**Student Name: Bob**
**property**
装饰器也可以用于记录变更。****
例如,在一个setter
方法中,您可以添加代码来记录变量的更新。
现在,每当调用 setter 时,也就是变量被修改时,就会记录更改。假设鲍勃的数学考试有一个总错误,他最后多得了 50 分。
student = Student("Bob", 350, 500)
print(student.score) **student.score = 400** print(student.score)
上面给出了以下输出,并显示了记录的更改:
70.0 %
**INFO:root:Setting new value...**
80.0 %
最后,我们的类看起来像这样:
注意#1:你应该在类的什么地方定义装饰器?
你可以在很多地方定义装饰器:在类的外部,在一个单独的类中,或者甚至在一个内部类中(相对于我们使用装饰器的类)。在这个例子中,我们简单地在Student
类之外定义了grade_decorator
。尽管这样做是可行的,但装饰器现在与我们的类没有任何关系,这可能是我们不喜欢的。
关于这一点的更详细的讨论,请查看这个帖子:
注意#2:除了 Java 中的构造函数重载之外,还有其他选项来模拟我们讨论的方法吗(比如,from_str 或 from_tuple)?
除了重载构造函数,我们还可以利用 java 中的静态工厂方法。我们可以定义一个像from_str
这样的静态方法,从传递给它的字符串中提取关键信息,然后返回一个对象。****
结论
面向对象编程是一个非常重要的学习和使用范例。不管你是否需要在你的下一个项目中使用这里讨论的主题,非常好地了解基础知识是必要的。与更基本的概念(如继承或类和对象的基本实现)相比,本文中的主题并不经常被使用,它们是建立在这些基础上的。无论如何,我希望这篇文章能让你了解 Python OOP 中的其他方法(除了实例方法)和属性装饰器。
Python Deques:了解 Deques 的设计目的
为初学者提供易于理解的示例
![]()
利瓦伊·琼斯在 Unsplash 上的照片
Python 中有很多内置的数据类型。根据不同的用例,每种数据类型都有优缺点。因此,了解每种数据类型的优缺点是开发高效应用程序的关键。
在本文中,我将尝试解释 Python 中的 deque 数据类型。
什么是德奎?它们是为什么而设计的?
Deque 是双端队列的简称,这意味着它有效地支持从两端添加和移除项目。它们旨在高效地执行这些特定任务。
队列与列表、堆栈和队列没有太大的不同。在某种程度上, deques 结合了list、stack和 queues 的特点。
那么这些数据类型之间有什么区别;
- 列表数据类型使用连续的内存块,其中的元素彼此相邻存储。因此,随机访问列表中的元素很快,因为 Python 知道在哪里查看。
- 同时,当内存块不足以存储附加项时,列出需要分配附加内存块的对象,在这种情况下会降低追加操作的性能。
- 另一方面, deques 中的元素存储在它们自己的内存块中。因此,我们可以更有效地删除或添加元素。
- 对包含在一个队列中的元素的随机访问需要 Python 循环整个对象,这与相同操作在列表对象中的 O(1)常数时间复杂度相比效率不是很高。
- deques 中的元素是双重链接的,因此每个元素都知道相邻项的位置。这带来了增量内存需求,因为元素包含两个以上的整数来存储相邻项的位置。
让我们看看 deques 的运行情况;
**import sys, time
from collections import deque**a_deque = **deque**([3,4,5,6])
a_list = [3,4,5,6]print(sys.getsizeof(a_deque))
print(sys.getsizeof(a_list))start_time = time.time()
for item in range(100000000):
a_list.append(item)
end_time = time.time()
print("**Append time for LIST:** ", end_time - start_time)start_time = time.time()
for item in range(100000000):
a_deque.append(item)
end_time = time.time()
print("**Append time for DEQUE:** ", end_time - start_time)start_time = time.time()
for item in a_list:
aa = a_list[200]
end_time = time.time()
print("**Lookup time for LIST:** ", end_time - start_time)start_time = time.time()
for item in a_deque:
aa = a_deque[200]
end_time = time.time()
print("**Lookup time for DEQUE:** ", end_time - start_time)**Output:**632 (size of deque object in bytes)
88 (size of deque object in bytes)Append time for LIST: 13.975143671035767
Append time for DEQUE: 11.940298795700073
Lookup time for LIST: 8.094613075256348
Lookup time for DEQUE: 8.974030017852783
什么时候用德克?
何时使用 deques
- 如果需要在线性序列的末尾删除或添加元素,那么使用 deques 。它们在这些操作上提供了 O(1) 恒定的时间效率。
何时不使用 deques
- 如果你经常需要随机访问时间复杂度为 O(n) 的元素,不要使用队列(执行操作所需的时间与对象的大小成正比)。列表在这种情况下是更好的选择,因为它们提供了 O(1) 的时间复杂度。
- 如果你有内存限制,不要使用队列。
关键要点和结论
在这篇短文中,我解释了在 Python 中何时使用dequee数据类型。关键要点是:
- Deque 是双端队列的简称,这意味着它有效地支持从两端添加和移除项目。它们旨在高效地执行这些特定任务。
- 如果需要在线性序列的末尾删除或添加元素,那么使用 deques 。它们为这些操作提供了恒定的时间效率。
- 如果经常需要随机访问容器对象中的元素,不要使用 deques 。
我希望你已经发现这篇文章很有用,并且你将开始在你自己的代码中使用 deque 数据类型。
Python 字典可以改进
![]()
第三方图书馆带来更多便利
只要我们使用 Python,字典就是我们几乎无法避免使用的数据结构。我认为 Python 字典在灵活性方面已经足够好了。毕竟,Python 语言本身就是以动态著称的。
但是,如果您也像我一样出于某些目的使用 JavaScript,您可能会熟悉使用“dot”从字典中获取对象值。本文将从一个例子开始,展示 Munch 试图解决的不便之处。然后,我将介绍这个库的其他特性。
原始 Python 字典
![]()
照片由 DariuszSankowski 在 Pixabay 上拍摄
我假设大多数像您一样的开发人员已经熟悉 Python 字典了。这是一个为演示目的而创建的示例词典。
my_profile = {
'name': 'Chris',
'age': 33,
'address': {
'country': 'Australia',
'state': 'Victoria'
}
}
如果我们想得到“名称”的值,只需做my_profile['name']
。
![]()
如果我们想获得“country”的值,我们可以将方括号链接起来,因为它嵌套在对象“address”内部。
![]()
老实说,没什么大问题。如果我们坚持要找一个,那就是键必须是一个字符串。除非我们定义一个合适的类并将my_profile
实例化为一个实例,否则我们必须使用 string 来获取值。
Munch 就是这样一个绕过这个问题的库。
使用蒙克字典
![]()
Munch 是在 PyPI 中注册的,所以我们可以使用 pip 轻松获得它。
pip install munch
一旦我们得到了库,就需要从模块中导入类Munch
。
from munch import Munch
然后,我们可以如下定义我们的字典。
my_profile = Munch({
'name': 'Chris',
'age': 33,
'address': Munch({
'country': 'Australia',
'state': 'Victoria'
})
})
然后,Munch 字典和 Python 字典之间的主要区别是前者支持“点”语法来获取值。
![]()
在上面的例子中,我们不必使用字符串来获取值。好处是它更加可靠和方便,因为大多数 IDE 工具都有自动完成按键的功能。
![]()
如果你不确定为什么使用可访问属性比字符串更可靠,请参考我以前的一篇文章。
如何在 Python 中少犯“错误”
[## 如何在 Python 中少犯“错误”
towardsdatascience.com](/how-to-make-fewer-mistakes-in-python-6925619ce87e)
构建蒙克词典
![]()
您可能已经注意到,在前面的例子中,我们从 Python 字典创建了 Munch 字典。当然,如果我们已经有一个字典,这是很方便的,我们只是想使用一些来自 Munch 的特性。
然而,有时我们可能想在字典不存在的时候建立一个字典。在这种情况下,我们不必首先构建一个 Python 字典,然后将其转换为 Munch。我们可以使用点语法从头开始构建字典。这非常类似于 JavaScript。
例如,我们想重新构建我们在前面的例子中使用的字典。
my_new_profile = Munch()
这个可以当做my_new_profile = {}
,但是我们从一开始就有 Munch 的特性。所以我们可以做以下事情来添加键值对。
my_new_profile.name = 'Chris'
my_new_profile.age = 33my_new_profile.address = Munch()
my_new_profile.address.country = "Australia"
my_new_profile.address.state = "Victoria"
my_new_profile
将与my_profile
相同。
![]()
蒙克还是字典的一个子类
![]()
事实上,Munch 类是 Python 字典的一个子类。
![]()
上面的例子表明我们创建的my_new_profile
既是蒙克又是dict
。了解到这一点,可以很有信心的说,Munch 也将继承 Python Dictionary 的所有原创特性。
例如,我们仍然可以使用字符串来设置键值对或获取值。如果键是变量,我们可能仍然需要这个特性。
my_new_profile['programming_language'] = 'Python'
my_new_profile['programming_language']
![]()
当然,Python 字典的所有功能在这里也将可用,比如获取一个 Munch 字典的所有键。
my_new_profile.keys()
![]()
序列化是 Python 字典最重要的特性之一。很多时候,我们可能想要在 Python 字典和 JSON 字符串之间进行转换。显然,通过使用 Munch,我们从未失去这一功能。
# Serialisation
import jsonprofile_json = json.dumps(my_new_profile)
print(profile_json)
![]()
默认值
![]()
现在,让我们看看 Munch 还提供了哪些很酷的功能。默认值是其中之一。
关于 Python 字典,我们需要知道的一个重要提示是关键错误。
![]()
当担心按键错误时,建议使用get()
功能。使用此功能时,我们可以设置默认值。
my_profile.get('programming_language', 'undefied')
![]()
这还不错,但是我们每次使用get()
函数时都必须传递默认值。有了蒙克字典,我们只需要做一次。
from munch import DefaultMunchmy_profile = DefaultMunch('undefined', {
'name': 'Chris',
'age': 33
})
如图所示,这一次我们需要导入DefaultMunch
类。当我们定义字典时,当我们试图获取一个不存在的键时,第一个对象将是默认值。让我们试一试。
my_profile.whatever_not_exists
![]()
当密钥不存在时,将返回预定义的默认值。如果确实存在,则正确返回该值。
查询时添加关键字
![]()
照片由 AlbanyColley 在 Pixabay 上拍摄
Munch 库的另一个小块糖是默认的工厂类。当我们试图从一个键中获取一个值时,如果这个键不存在,它将被自动添加到 Munch 字典中。
我们需要用DefaultFactoryMunch
类定义一个 Munch 字典。
from munch import DefaultFactoryMunchmy_profile = DefaultFactoryMunch(list, {
'name': 'Chris',
'age': 33
})
请注意,默认值需要在DefaultFactoryMunch
的实例中定义。当访问不存在的键时,将使用该默认值。
假设我们正在查询 Munch 字典。如果键不返回任何值,我们希望给键增加值。
if not my_profile.languages:
my_profile.languages.append('Python')print(my_profile.languages)
![]()
它保证我的个人资料必须有 Python:)
摘要
![]()
在本文中,我介绍了名为 Munch 的库。它完全继承了 Python 字典,因此它支持我们熟悉的所有原始功能。此外,它支持“点语法”。这比传递一个字符串来获取一个键值更方便、更可靠。除此之外,当我们希望从字典中访问一个不存在的键时,它还提供了一个更好的解决方案。
https://medium.com/@qiuyujx/membership
如果你觉得我的文章有帮助,请考虑加入 Medium 会员来支持我和成千上万的其他作者!(点击上面的链接)
Python Excel 与 Xlwings 的集成
用 Python Xlwings 为您的 Excel 插上翅膀
![]()
学习如何用 Xlwings 飞行(巴伐利亚天空,图片由作者提供)
动机:
我们经常进行令人印象深刻的 Python 分析,但我们的同事所要求的只是在 Excel 中获得最终结果。我们的许多同事不想使用 Jupyter Notebook、Python 脚本之类的东西。他们只想坚持使用他们钟爱的电子表格工具(比如微软的 Excel)。在“面向非 Python 爱好者的 Excel-Python 应用”中,我们学习了如何为那些同事构建一个完整的 Excel-Python 应用。在本帖中,我们将更深入地考察 Xlwings 。Xlwings 让我们能够使用 Excel 电子表格作为 GUI(图形用户界面)来开发交互式应用程序,而 Python 则在后台完成这项工作,通过单击 Excel 中的按钮或 UDF(用户定义的函数)来调用。在这个故事的结尾,你将拥有一个漂亮的 Excel KPI 仪表板模板 :
![]()
非常感谢最伟大的 CEO 之一(首席 Excel 官): Chandoo ,这一切都是从你那里学来的!
..其从数据库连接接收数据,使用 Pandas 数据帧和 Numpy 数组计算总设备效率(OEE)。我们还将讨论为什么我们坚持 Excel 的可视化部分,而不是使用 Xlwings 的图表和静态图片的可能性。
解决方案:
正如我们许多人已经体验过的,与 Numpy 数组和 Pandas 数据框架相比,Excel 的电子表格网格使用类似的逻辑。由于这一点,Xlwings 的核心特性之一是使读取和写入 Excel 数据变得极其方便。
导入包和数据
在我们检查之前,我们需要确保我们已经安装了 xlwings。如果您尚未安装,请现在安装,例如通过 pip 安装:
pip install xlwings
现在我们可以导入 Xlwings 并调用 Xlwings 的 Book 函数来访问我们的 input.xlsx 文件。然后我们使用 Xlwings 的视图功能来证明上面关于其可用性的陈述:
import xlwings as xw
import pandas as pd
df = pd.read_excel('input.xlsx')
xw.view(df)
![]()
这就是我们的制造业 KPI 在 Xlwings 看来的样子
如果您想从 SQL Server 数据库中导入数据,可以这样做:
import pandas as pd
import pyodbc
sql_conn = pyodbc.connect(‘Driver={SQL Server};’
‘Server=MsSqlServerName;’
‘Database=DatabaseName;’
‘Trusted_Connection=NO;UID=USER;PWD=PASSWORD)
query = “SELECT * from dbo.input“
df = pd.read_sql(query, sql_conn)
与熊猫相反,Xlwings 打开这个 Excel 文件(。查看,这样我们可以直接在 Excel 电子表格中查看数据框。该函数支持所有常见的 Python 对象(数字、列表、字典、字符串、元组、数组和数据帧)。默认情况下,视图打开一个新工作簿并将该对象粘贴到第一个工作表的单元格 A1 中。由于 Excel 的 autofit,甚至列宽也会自动调整。查看功能在检查大数据框时非常方便(比在 Juypter 笔记本单元格中直接浏览大数据框更方便,尤其是在交叉检查大量计算时)。
关于书本和床单的一些单词
最高粒度的工作簿、工作表、范围或特定的单元格是我们在 Excel 中处理的最重要的组件。因此,现在让我们做一些与这些概念相关的基本任务。
我们通过调用 Xlwings 的 Book 函数来访问我们的 input.xlsx 文件:
wb=xw.Book(r”C:\users\input.xlsx”)
wb.name
![]()
调用 wb.name,我们可以将 Input.xlsx 作为输出。
wb.sheets
![]()
Xlwings 自动将输入选项卡检测为唯一填写的表单。我们也可以直接从这本书里调用这一页:
sht = wb.sheets[‘Input’]
我们也可以使用索引来代替工作表的名称。与 Python 类似,Xlwings 从零开始索引:
sht= wb.sheets[0]
sht.name
![]()
输入工作表名称,因为工作表索引 0 是 Excel 中的工作表 1。
下一步,我们可以使用 range 作为工作表对象的属性:
sht.range(“A1”)
![]()
以上范围仅激活 A1 单元格。但是我们希望看到从单元格 A1 开始的完整表格,不管这个表格包含多少行和多少列。因此,我们必须提供 DataFrame 类作为 options 方法中的 convert 参数。此外,我们使用 header 1 表示 yes 和 table expand 这样的选项。默认情况下,数据帧应同时具有标题和索引:
sht.range(“A1”).options(pd.DataFrame, header=1, index=False, expand=’table’).value
![]()
我们收到了完整的表格,但没有明确说明完整的范围。
range 对象的 value 属性接受并返回单个单元格的标量和二维范围的嵌套列表。上面的扩展表选项比以元组样式硬定义范围对象要方便和灵活得多:
sht.range(“A1:AI253”).value
![]()
数据帧通常比元组更容易处理。
我们还可以通过索引和切片工作表对象来排列对象。用 A1 表示法做这件事感觉更直观,使用整数索引让 Excel 感觉有点像 Numpy 数组:
sht[“A1:AI253”]
![]()
sht["A1:AI256"] [:, 10]
![]()
计算 OEE
从我们对 Excel 对象的小小探索中回来,现在让我们回到我们想要计算 OEE 的输入数据:
dfInput = sht.range(‘A1’).options(pd.DataFrame, header=1, index=False, expand=’table’).value
出于培训目的,我们将这个简单的 OEE 计算添加到我们的数据框架中:
dfInput[‘OEECalc’]=(dfInput[‘Availability’]+dfInput[‘Performance’] + dfInput[‘Quality’]) /3
下一步,我们将打开 KPI 仪表板模板,我们希望将其他工作簿中的数据转移到该模板中:
wbNew=xw.Book(r”C:\users\KPIDashboard_Xlwings.xlsx”)
更准确地说,我们希望将表格的内容复制到选项卡 InputNew 中,从单元格 B4 开始(您可能会问为什么是 B4?仅供学习之用):
shtout = wbNew.sheets[‘InputNew’]
shtout.range(‘B4’).options(pd.DataFrame, index=False).value = dfInput
关于 Xlwings 绘图的几点思考
多亏了 Xlwings,我们可以将数据从一个 Excel 文件复制到另一个文件中,包括一个 OEE 计算。Xlwings 还支持使用 Matplotlib 绘图,我们现在可以使用它来绘制 KPI。但事实上,我们决定坚持使用 Excel 的仪表板功能来显示我们的 KPI 输入数据。这是因为在当前状态下,Excel 的图表和图像功能远远优于 Xlwings。得益于 Excel 函数,我们还使该 OPS KPI Excel 模板非常动态且易于调整,尤其是对于我们无法使用 Python 的同事而言:
![]()
通过单击圆圈对 KPI 进行排序
![]()
如果不应显示任何目标,则取消勾选。
![]()
搜索特定产品在所有 KPI 中的分布。
![]()
绘制相互对立的 KPI。
![]()
统计过程控制(SPC)仍然没有过时。
![]()
最终,箱线图通常被严重低估。
Xlwings Excel 加载项
我们现在能够在 Jupyter 笔记本中启动读取(从 Excel)、计算和写入(返回 Excel)过程。到目前为止,没什么特别的。但多亏了 Xlwings,我们现在可以在 Excel 中添加 VBA 宏,这样我们就可以在 Excel 中启动 Jupyter 笔记本了。基本概念是简单地将 Jupyter 笔记本代码编写到主函数中,导出为 Python 文件。py)并从 Excel VBA 编辑器中调用 Python 脚本。好了,现在我们谈了。
首先,让我们确保我们已经安装了插件。否则,您必须在您的终端中安装 xlwings addin:
xlwings addin install
![]()
现在,当您打开 Excel 时,您将看到新的加载项:
![]()
然后,您必须在 Excel 中激活 Xlwing 插件,方法是转到您的插件设置:
![]()
单击加载项以激活 Xlwings 加载项
然后,单击搜索按钮并转到您的加载项文件夹,例如:C:\ Users \ AppData \ Roaming \ Microsoft \ AddIns。选择 Xlwings.xlam ,一切就绪:
![]()
从现在开始,你可以直接从 Excel 中调用任何 Python 脚本。您只需将这个子程序添加到您的 VBA 模块中:
Sub SampleCall()
mymodule = Left(ThisWorkbook.Name, (InStrRev(ThisWorkbook.Name, ".", -1, vbTextCompare) - 1))
runpython ("import " & mymodule & ";" & mymodule & ".main()")
End Sub
![]()
如果 Python 和 Excel 具有相同的名称,您的 VBA 子例程应该与此类似。
请将您的 Jupyter 笔记本修改为一个 def main:
import pandas as pd
import xlwings as xw
def main():
wb=xw.Book(r”C:\users\input.xlsx”)
wbNew=xw.Book(r”C:\users\KPIDashboard_Xlwings.xlsm”)
# more specifically, we want to work with the sheet Input
sht = wb.sheets[‘Input’]
# and to be even more precise, we are only using the cells which belong to the table’s range starting with cell A1
dfInput = sht.range(‘A1’).options(pd.DataFrame, header=1, index=False, expand=’table’).value
# for training purpose, we add an easy OEE calculation to our dataframe
dfInput[‘OEECalc’]=(dfInput[‘Availability’]+dfInput[‘Performance’] + dfInput[‘Quality’]) /3
shtout = wbNew.sheets[‘InputNew’]
shtout.range(‘B4’).options(pd.DataFrame, index=False).value = dfInput
[@xw](http://twitter.com/xw).func
def hello(name):
return f”Hello {name}!”
if __name__ == “__main__”:
xw.Book(“input.xlsx”).set_mock_caller()
main()
..并将这个 Jupyter 笔记本下载为 Python(只需点击 Jupyter 笔记本菜单中的“文件”,选择“下载为 py”)文件。请将其命名为与您的 Excel 文件相同的名称,并确保 Excel 文件与您的 Jupyter 笔记本位于同一文件夹中,以便正常工作:
![]()
祝贺你!
我们现在有一个很好的 Excel-Python OPS KPI 模板,它从数据库中获取数据,使用 Pandas 计算 OEE,并使用动态 Excel 图表功能。都直接出 Excel 了。我们将在另一篇文章中更详细地讨论制造业 KPI 的定义(链接将尽快添加)。你可以在 my github 上找到 Jupyter 笔记本和 Python,以及 Excel 插件和 KPI 仪表盘。
非常感谢您的阅读!希望这篇文章对你有帮助。请随时在 LinkedIn 、 Twitter 或工作室与我联系。
https://jesko-rehberg.medium.com/membership
最初发表在我的网站 DAR-Analytics 。
Python 工厂提供可伸缩、可重用和优雅的代码
通过将行为(在工厂超类中定义)与对象创建(在子类中)分离,创建灵活、可扩展的代码
![]()
缺席 84 /AdobeStock
在现实生活中的工厂里,相同或相似物品的生产不是单独完成的,而是在装配线上进行的。类似地,工厂设计模式允许您创建不依赖于特定产品的软件,而是可以在许多类似的应用程序中重用。工厂模式是著名的 Gof(四人帮)设计模式之一[1]。这是一种创造性的设计模式,它将对象创建封装在一个名为工厂的类中。这种模式通过从系统中分离出如何、何时以及由谁创建对象来提高系统的灵活性[6]。
当您需要创建新类型的对象时,工厂特别有用。如果对象创建分散在整个代码中,那么您将不得不花费时间在代码中找到对象类型起作用的确切位置[2]。用软件工程的术语来说,这意味着你有大量的技术债务要处理。在机器学习中尤其如此,如[4]中所解释的。机器学习会因数据依赖性、数据漂移和隐藏的反馈循环而导致巨大的维护成本。最近,通过创建功能商店【3】【5】,工厂找到了进入机器学习的途径。特性存储可以是在线的,也可以是离线的,它们的主要目的是在数据转换应用于不同的特性时对其进行抽象,并以这种方式实现更快的开发、更好的协作、高效的模型部署和扩展[7]。
1.0 Python 中工厂实现的示例
现在让我们看一个如何在 Python 中使用工厂的具体例子。让我们假设,我们想为一个刚从大学毕业、在一家银行找到第一份工作——定量金融分析师——的儿子打造一个新衣柜。鉴于他精通 Python,他决定实现一个虚拟衣柜索引。我们将帮助他为每套服装分配一个“正式等级”,因为他完全有能力穿着 t 恤和破洞牛仔裤出现在重要会议上。
![]()
斯维塔/AdobeStock
如下图所示,虚拟衣柜索引从创建两个抽象的工厂开始,一个是鞋类(鞋类,另一个是服装(男装)。这些是抽象类,它们只用于模板的目的。与 Java 不同,Python 没有固有的抽象类类型。相反,它提供了一个名为 abc 的模块,该模块提供了抽象类基础设施。男装类的 colort 和 colorb 变量代表上衣(衬衫、毛衣等)的颜色。)和下(裤)分别。注意我们给鞋子和顶部和底部的颜色分配了一个默认值“棕色”(总是一个安全的颜色)。
现在让我们通过定义具体的类 Boots 和 Loafers 来实现两种类型的鞋。我们从鞋类类继承,然后覆盖它的 shoespecs() 方法来实现每个具体类的特定动作。
类似地,我们创建了两个具体的类,分别继承自men swath、sweater andurdoropants、和buttondownshirtandresspants。每个具体的类都覆盖了抽象类方法 clothespecs() ,以便为每个样式指定细节。请注意, clothespecs() 方法接受一个鞋类对象,以便将鞋类的颜色与裤子的颜色相匹配。
抽象类装下面是一个工厂,提供衣服和鞋子的选择方法,choose _ clothis()和 choose_shoes() 分别是、,并且有一个实例属性, formality 。
下面的具体类light color _ ColdAndCasualDay继承了抽象类 Outfit 并有以下方法:
(A)指定形式和名称变量的构造函数。形式变量继承自装备。
(b)类装备的choose _ ways()方法的覆盖形式,返回一个SweaterAndCorduroyPants对象。
(c)一个被覆盖的 choose_shoes() 方法的形式,该方法返回一个 Boots 对象。
正如我们所看到的,当我们在类的层次结构中向上移动时,事情变得更加具体,而较低级别的类,如鞋类、男装和服装、是 不可知的子类,它们的对象是在哪里创建的,以及它们是如何聚集的 。为下面的dark color_WarmSemiFormalDay类定义了类似的结构,该类定义了为衣服返回一个buttondownshirtandresspants对象,为鞋类返回一个Loafers对象的方法。
对比抽象工厂,到目前为止我们看到,dark color _ WarmSemiFormalDay和light color _ ColdAndCasualDay是具体 工厂。它们如何作为对象的工厂在下面的类 Look 的定义中说明。 类 Look 是负责对象创建的编排类。 下面我们可以看到,它的构造函数以一个工厂作为实参。在我们有限的衣柜里,工厂可以是一个浅色 _ 冷色和浅色或深色 _ 暖色半正式的物件。当我们创建一个类 Look 的对象时,工厂的choose _ ways()和 choose _ shoes()被调用,它们分别给实例变量 clo 和 sho 赋值。然后这些变量被用在 all-important dress()方法中,该方法将所有东西放在一起,创建完整的外观 !由于策略性地放置了对象创建封装和多态,这是可伸缩的、可重用的代码。
在下面的代码片段中,我们可以看到我们的对象创建和对 dress() 方法的调用。注意,我们检查两个对象是否相等,或者对象 g2 是否大于对象 g1 。在上面的 Look 类的定义中,我们将 eq 运算符的输出定义为两个对象的手续的相等比较的输出。正如我们已经讨论过的,我们崭露头角的财务分析师对正式风格一无所知,所以我们需要以一种特定的方式告诉他(正式评级),这种方式在正式程度上被认为是等同的。所以,3 是最高正式(西装),0 是无正式 (T 恤和球鞋)。下面的等于运算返回假,更大的运算返回真,因为 g2 比 g1 (1)具有更高的形式等级(2)。
最后,在下面的代码片段中,我们导入当前的虚拟衣柜,它作为一个字典存在于一个文本文件中,并将我们创建的两个新外观附加到它,以服装对象的名称作为键,以正式度作为值。
你可以看到完整的代码,作为一个木星笔记本在我的 github 目录:https://github.com/theomitsa/Python-factories
感谢您的阅读!
参考
- Gamma,e .,Helm,r .,Johnson,和 r .,J. Vlissides,设计模式:可重用面向对象软件的元素,Addison-Wesley Professional,1994 年 10 月。
- 埃凯尔,b .等人, Python 3 模式、配方和习惯用法,阅读文档(电子书)
- 飞,a .,特征存储:一个数据科学工厂的组件, 媒介:走向数据科学,2019 年 9 月。
- Sculley,d .等人, 机器学习:技术债务的高息信用卡 ,机器学习的软件工程,NIPS 会议研讨会,2014 年。
- Taifi, ML 特色店:一次休闲之旅 1/3 ,中型,2020 年 4 月。
- Bishop, C# 3.0 设计模式,奥赖利媒体,第 336 页,2007 年。
- Hirschtein,a ., 什么是特征库,为什么它们对扩展数据科学至关重要? ,中:走向数据科学,2020 年 4 月。
艺术 Python:使用 Python 设计个性化报价图像
通过机器学习自动生成报价图像
![]()
本文将向您展示如何创建报价图像,而不使用任何图形设计软件或程序。还有,你不用花时间找报价;机器会为你选择一个。自动化应用的领域很多,平面设计也是其中之一。随着超级强大的 GPU 的发展,计算机科学家开始使用这种能力来获得乐趣。我相信你已经听说过 OpenAI,以及如何用一句话来描述它就能设计出东西。机器的智能会完成剩下的工作。这个项目叫做 DALL-E。
在今天的文章中,我们将创建一个在图片上添加引号的程序,我们所要做的就是选择一张图片并选择一个关键字。如果你准备好了,让我们开始吧!
目录
- 第一步——库
- 第二步—报价生成
- 步骤 3 —选择一幅图像
- 步骤 4 —在图像上渲染报价
- 最后一步——导出结果
步骤 1 —库
首先,让我们谈谈图书馆。我们将在这个项目中使用两个主要的库,它们被称为: Pillow 和Quote。Pillow 是一个知名的图像编辑/处理库。Quote 是 Goodreads Quote API 的 python 包装器。以下是这两个库的官方文档链接:
那么,我们如何安装它们呢?简单。
pip install pillow quote
我们准备好了包裹。现在,让我们开始编程。我将使用 Jupyter 笔记本作为我的编码环境。更容易在步骤内来回移动。
如果你想进一步了解枕头库和一些实际应用,这里有一篇我为你找到的颇有见地的文章:用 Python 进行图像处理作者是詹姆斯·布里格斯。
第 2 步—报价生成
让我们从将报价模块导入我们的程序开始。
from quote import quote
我们将使用报价模块从 API 获取随机报价。我们甚至可以传递关键字;这将返回符合我们请求的报价选择。决定权在你。
我会把我的关键词设为生活。让我们看看会得到什么样的报价。顺便说一下,limit 参数是我们选择想要得到多少报价的地方。
keyword = 'Life'result = quote(keyword, limit=1)print(result)
![]()
图片由作者提供。
如您所见,返回的列表项是一个字典。我们有几个键和值。关键词是作者、书和引用。我们可以分别访问它们,如下所示:
result[0]['quote']result[0]['author']
![]()
图片作者。
我们的报价准备好了!是时候选择一张我们想要添加引用的图片了。
步骤 3 —选择一幅图像
这将是最容易的一步。我们要做的就是选择一个图像。你可以在 Pexel 上找到很棒的股票图片。确保选择一张合适的图片。否则,对于图像来说,文本可能看起来很大。我在这个项目中使用的价值观可能不是对每个人都是最好的。请随意相应地调整它们。
这是我挑选的图片,一个干净的背景图片:
![]()
照片由 Pexels 的 Cátia Matos 拍摄
第 4 步—在图像上呈现报价
这一步我们会做很多事情。对于这一步,我们必须从之前安装的 Pillow 库中导入几个模块。
from PIL import Image, ImageFont, ImageDraw
背景图像
酷!现在让我们继续导入我们选择的图像。ImageDraw 方法将图像转换为可编辑的格式。
bg_image = Image.open("image_leaves.jpg")quote_image = ImageDraw.Draw(bg_image)
文本属性
现在,让我们定义想要使用的文本字体。我用 Roboto 字体,字体大小为 52。我已经从谷歌字体网站下载了这种字体。我们还将报价和作者值赋给新的变量。
quote_font = ImageFont.truetype('Roboto_Slab/RobotoSlab-VariableFont_wght.ttf', 52)quote_text = result[0]['quote']
图像上的文本
棘手的部分来了,就是将引用的文本放入图像中。因为完整的报价在一行中,所以我们需要把它分成不同的行来更好地工作。我尝试在不使用任何库的情况下进行这种拆分,下面是我所做的。
要做的第一件事是用逗号分隔完整的引号。但是你看下面,有一条线还是那么长。
![]()
然后,我决定运行一个 for 循环来按句点进行拆分。在进行拆分的同时,另一个循环是在图像上呈现文本。所以 2 合 1 解决方案适合长行和逐行呈现文本。Offset 用于在行之间添加一些空白像素。
offset = 0
for i in quote_text:
i = i.split(".")
for ix in i:
quote_image.text((15,15+offset), ix, (216, 233, 237), font=quote_font)
offset += 100
让我带您看一下这段代码的核心部分,它将文本呈现在图像上:
- (15,15+offset):文本开始的 x 和 y 点。在枕头中,轴系统是笛卡尔轴系统。所以,左上角是(0,0)点。
- ix:是文本行。
- (216,233,237):文本的颜色。如果想改变每行的文本颜色,也可以给这些可更新的变量。
- font=quote_font:传递我们之前导入的字体信息。
最后,让我们在底部加上作者的名字。这条线不在循环内。
quote_image.text((15,15+offset), quote_author, (216, 233, 237), font=quote_font)
最后一步—导出结果
最后一步来了!你准备好看到结果了吗?让我向你展示它的样子:
bg_image.save("marilyn_quote_on_image.jpg")
![]()
完美!我觉得这个很有意思!我们已经创建了一个程序,可以生成个性化的报价图像,而无需进行任何报价搜索或使用图形设计软件。所有这些都是由几行代码组成的。感谢 Goodreads 的报价 API 和 Pillow python 图像处理库。在这个项目中,我演示了一个例子;您甚至可以在几秒钟内自动创建数百个报价图像。让我知道你的想法!
我希望你今天学到了新东西。如果您喜欢这篇文章,请随意查看我的其他动手编程文章。快乐编码,保持创造力!
为您的艺术项目提供 Python 实践
Python for Art:使用 TensorFlow 2 的快速神经风格转换
用神经网络在几毫秒内创建迷人的照片
![]()
来自 Pexels 的 Frank Cone 摄影
在这篇文章中,我将向你展示如何用快速神经风格转换来使你的照片风格化。神经风格转移是一个很好的方法,可以在几秒钟内将你的普通快照变成艺术品。感谢我们在 TensorFlow 的朋友,他们为我们创建并训练了模块,以便我们可以快速应用神经网络。阅读本实践教程后,您将有一些在项目中使用 TensorFlow 模块的实践。如果你准备好了,让我们开始吧!
目录
- 简介
- 步骤 1 —库
- 步骤 2 —功能
- 步骤 3 —原稿和样式图像
- 步骤 4 —任意图像风格化
- 最后一步——导出结果
介绍
先从理解他们话中的张量流和神经风格转移开始。然后我们将进入应用部分。
张量流
“tensor flow 是机器学习的端到端开源平台。它有一个全面、灵活的工具、库和社区资源的生态系统,让研究人员推动 ML 的最新发展,开发人员轻松构建和部署 ML 驱动的应用程序。”
参考:【https://www.tensorflow.org】T21
神经类型转移
“神经样式转移是一种优化技术,用于获取两个图像—一个内容图像和一个样式参考图像(如著名画家的作品)—并将它们混合在一起,以便输出图像看起来像内容图像,但以样式参考图像的样式“绘制”
参考:https://www . tensor flow . org/tutorials/generative/style _ transfer
在我们继续之前,我想分享一篇由博士研究员 Matthew Stewart 写的关于神经 styler 转移的文章。你将对神经类型转移的工作原理有一个很好的幕后理解。
步骤 1 —库
首先,我们必须为这个项目安装几个库。这些库可以列举如下:Matplotlib、Numpy、TensorFlow、TensorFlow Hub 和 PIL。
- Matplotlib 用于可视化我们 Jupyter 笔记本中的图像。如果我们想记录这个过程,它还允许我们保存图形。
- Numpy 是一个需要安装的科学库。它是在后台工作的顶级库之一。因为图像是由像素组成的,并且是以多维数组的形式收集的,所以 Numpy 库可以帮助我们处理这种数组。
- TensorFlow 是由谷歌开发者和软件工程师开发的机器学习生态系统。许多大公司都在使用 TensorFlow 开展业务。
- TensorFlow Hub 是一个训练有素的机器学习模型库,可随时进行微调,并可部署在任何地方。它是张量流伞的一部分。
- PIL 是一个图像处理库。非常有用的库,尤其是在从事计算机视觉项目时。它是作为枕头安装的。导出最终结果时,我们将使用 PIL。
我们可以使用 python 包管理器 pip 在一行中安装所有这些包和库。在您的终端窗口中运行以下行:
pip install matplotlib numpy TensorFlow tensorflow_hub pillow
安装过程结束后,我们可以继续创建新的 Jupyter 笔记本。然后将它们导入程序中。
from matplotlib import gridspec
import matplotlib.pylab as plt
import numpy as np
import tensorflow as tf
import tensorflow_hub as tf_hub
import PIL
第 2 步—功能
在这一步,我们将定义两个函数。我将在下面添加每个功能的简短描述。
加载图像
在这个函数中,我们将图像解码成三个不同的通道。这就像进入事物的像素级。然后我们裁剪图像的中心。最后,我们正在调整图像的大小,以便我们的内容和样式图像在大小上匹配。
def load_image(image_path, image_size=(512, 256)):
img = tf.io.decode_image(
tf.io.read_file(image_path),
channels=3, dtype=tf.float32)[tf.newaxis, ...]
img = tf.image.resize(img, image_size, preserve_aspect_ratio=True)
return img
可视化图像
在这个函数中,正如你从它的名字可以理解的那样,我们以网格的顺序来可视化这些图。
def visualize(images, titles=('',)):
noi = len(images)
image_sizes = [image.shape[1] for image in images]
w = (image_sizes[0] * 6) // 320
plt.figure(figsize=(w * noi, w))
grid_look = gridspec.GridSpec(1, noi, width_ratios=image_sizes)
for i in range(noi):
plt.subplot(grid_look[i])
plt.imshow(images[i][0], aspect='equal')
plt.axis('off')
plt.title(titles[i])
plt.savefig("final.jpg") plt.show()
步骤 3-原始和样式图像
这将是一个简单的步骤。我们将选取两幅图像,然后将它们导入我们的程序。我们将使用之前定义的 load_image 函数来导入图像。
以下是我选择的图片的原始版本:
![]()
左:来自 Pexels 的 Furknsaglam 拍摄的照片。右:照片由 Pexels 的埃伯哈德拍摄
原始图像(左图)是我们想要风格化的主要图像。样式图像(右图)是我们在转换样式时将用作参考的图像。
original_image = load_image("original_image.jpg")style_image = load_image("style_image.jpg")
我们已经将图像加载到程序中。现在,让我们用我们的风格图像来训练我们的神经网络模型。我们将使用 avg_pool 方法。
style_image = tf.nn.avg_pool(style_image, ksize=[3,3], strides=[1,1], padding='VALID')visualize([original_image, style_image], ['Original Image', 'Style Image'])
![]()
作者图片
步骤 4 —任意图像风格化
我们将定义并加载我们希望在此步骤中使用的 TensorFlow hub 模型。我们将使用的模型被称为任意图像风格化。加载模型有不同的方式,一种是通过 url 加载,另一种是下载模型文件夹。
对于这个项目,我更喜欢下载模型并从我的本地驱动器加载它。这样,程序就不必每次运行代码时都下载模型。
stylize_model = tf_hub.load('tf_model')
tf_model 是下载的模型的文件夹名称。这里的是任意图像风格化模型的下载链接。该文件夹将如下所示:
![]()
作者图片
现在,让我们使用我们的原始和样式图像运行模型。
results = stylize_model(tf.constant(original_image), tf.constant(style_image))stylized_photo = results[0]
最后一步—导出结果
快到了!在最后一步中,我们将可视化最终结果,以查看快照之前和之后的情况。我们将调用在函数步骤中定义的可视化函数。
visualize([original_image, style_image, stylized_image], titles=['Original Image', 'Style Image', 'Stylized Image'])
![]()
作者图片
这难道不令人着迷吗?正如你所看到的,我们在原始镜头中的角色和这幅画融合得非常好。神经风格的转换无疑赋予了照片更多的深度和情感。让我知道你的想法。
为什么不与世界分享这个惊人的作品。为了与我们的朋友分享最终的作品,我会告诉你如何导出它。
以下是导出编辑过的图像的功能:
def export_image(tf_img):
tf_img = tf_img*255
tf_img = np.array(tf_img, dtype=np.uint8)
if np.ndim(tf_img)>3:
assert tf_img.shape[0] == 1
img = tf_img[0]
return PIL.Image.fromarray(img)
现在,让我们调用 export_image 函数。
export_image(stylized_photo).save("my_stylized_photo.png")
![]()
作者图片
结论
恭喜你。我们使用 TensorFlow 和神经网络创建了一个独特的艺术品。编程不仅仅是解决问题。我们也可以把它用于像这样迷人的艺术项目。这些项目对我练习新技能帮助很大。在构建机器学习/深度学习项目方面,TensorFlow 是其中的佼佼者。
希望你喜欢读这篇文章,并在今天学到一些新的东西。从事动手编程项目是提高编码技能的最佳方式。如果您在执行代码时有任何问题,请随时联系我。
面向您的动手机器学习项目
- 使用 Python 构建语音情感识别器
- 用代码把你的照片变成艺术草图
面向初学者的 Python 控制流
你想学习一些关于 fi-else 条件或循环的知识吗?不要再看了!
最近,我开始撰写面向 Python 编程初学者的系列文章。我们已经讨论了与开始您的 Python 之旅相关的主题,上次我们与 T2 操作符一起工作。今天我们将继续我们的旅程,并解决控制流的话题。
或者像 Jake VanderPlas 在“Python的旋风之旅”中所说:“ 控制流是编程中真正遇到困难的地方。
我决定把这篇文章分成几部分,以便于学习。这些部分如下:
- 介绍
- 条件句或条件语句
- 循环
-用于
-而
- 带中断和继续的控制循环
-中断
-继续
- 结论
快乐阅读!😃
![]()
介绍
首先,什么是控制流?简单来说,它是执行某些操作的顺序。我们举一个简单的例子,说我们要测量外面的空气温度。我们需要做什么?首先我们拿着温度计,然后我们打开门,我们走到外面,我们关上门,我们在阴影中找到一个地方,我们测量温度,我们写下来,等等。
类似的,在编程中,如果我们要做一个特定的任务或者操作,就需要做简单的更小的步骤。这些步骤可以例如包括决策或给定次数的任务重复。
假设我们想要一个脚本,它根据某种标准以不同的方式执行,例如,如果我们测量的空气温度是 3°C,它会打印出“外面很冷”,但如果是 21°C,它会打印出“外面很热”。在这种情况下,检查一些条件,并根据条件执行一个任务(打印出某个语句)。
或者,假设我们想将每个工作日测得的气温从摄氏度转换为华氏度。然后,我们需要一个脚本,它获取每个工作日的测量温度,并执行以下公式:
( C * 1.8) + 32 = F
这意味着我们做一个相似的任务已知的次数,准确地说是 7 次。我们取 7 个数,每个数乘以 1.8,再加上 32。
现在让我们更详细地看一些例子,并提供代码。🙂
条件句或条件语句
条件语句,或者通常称为 if-then 语句,允许我们根据某种条件(布尔条件)执行一段代码。
Python 中的条件语句有:
- 简单 if
- 如果-否则
- 嵌套 if
- if-elif-else
应用条件语句的关键字(符号)是 i f、elif、else 和冒号(😃。重要的是在冒号后缩进新的一行。
简单的 if 语句:
让我们想象一下最简单的情况。
![]()
按作者
如果 if 语句表达式的计算结果为 True ,则执行该语句后面的缩进代码。如果表达式评估为 False ,则跳过 if 语句后的缩进代码,程序执行与 if 语句缩进相同级别的下一行代码。
我们声明变量 x 为 10。我们检查 x 是否大于 5,因为该语句为真 (10 > 5),所以 print 命令得到执行,并且语句“ X 大于 5!"被打印出来。
这个说法不成立的情况呢?然后,这个脚本将什么也不做。
![]()
按作者
如果我们想要一个脚本在语句为 False 时做一些事情,我们需要稍微修改一下。
if-else 语句
如上所述,这个语句允许我们给脚本增加第二种可能性,当条件为假时该怎么办。让我们想象一下这个案例。
![]()
按作者
如果表达式计算结果为真,则执行 if 语句的缩进代码。紧跟在 else 之后的缩进代码仅在表达式评估为 False 时执行。要标记 else 块的结尾,代码必须非预期到与起始 if 行相同的级别。
![]()
按作者
上面的代码检查输入的温度是否在 15 以上,如果语句为真,它打印出我们不需要夹克,但是如果空气温度在 15 以下,脚本建议带一件夹克。
现在我们知道,如果气温超过 15 摄氏度,我们就不需要夹克,但应该穿什么衬衫,短袖还是长袖?为了用我们的脚本回答这个问题,我们需要使用一个嵌套的 if 语句。
嵌套 if 语句
基本上这是一个 if 语句放在另一个 if 语句中。让我们把它形象化,以便更好地理解。
![]()
按作者
像以前一样,代码检查空气温度是否高于 15°C,如果语句为真,则表明我们不需要夹克。接下来,它会检查是否低于或等于 20 摄氏度,如果是真的,它会建议穿长袖衬衫。如果那是假,那么它比 20°C 暖和,它建议一件短袖衬衫。
![]()
按作者
这个例子也可以用一个 if-elif-else 语句来解决。让我们看看怎么做。
if-elif-else 语句
elif 关键字可以被认为是 else if ,如果我们想要在 if 和 else 之间进行更清晰的划分,我们就使用它。Python elif 语句允许在初始 if 语句之后继续执行检查。一个 elif 语句不同于 else 语句,因为提供了另一个表达式来检查,就像最初的 if 语句一样。
![]()
按作者
如果表达式为真,则执行 elif 后面的缩进代码。如果表达式评估为 False ,代码可以继续执行可选的 else 语句。在初始 if 之后,可以使用多个 elif 语句来执行一系列检查。一旦 elif 表达式评估为真,则不再执行 elif 或 else 语句。
![]()
按作者
代码首先检查是否空气温度低于 15°C,如果为真,则建议带一件夹克。如果为假,则检查是否低于或等于 20°C,如果为真,则建议穿长袖衬衫。如果两个语句都是假,则执行否则语句,并建议穿短袖衬衫。
环
或通常称为重复语句,用于重复指令块。在 Python 中,有两种类型的循环:
- 对于循环
- while 循环
对于循环
For 循环在我们迭代一系列数据时使用,如列表、元组、字典、字符串等。因为我们知道一个列表或字符串中有多少个元素,所以当我们迭代已知(或期望)次数时,就使用它们。应用 for 循环的关键字是 for 和 中的。
![]()
按作者
这个可视化展示了一个简单的循环工作流的。我们初始化一个序列(列表、字符串、元组……),循环检查该项是否是最后一项,如果为真,循环停止,如果为假,代码被执行,循环在序列中的下一项重复。直到代码执行完最后一项,循环才会结束。
让我们看一个例子。我们将用华氏温度计算一周的摄氏温度测量值。因此,我们声明一个 python 字典,它将工作日保存为键,将测得的每日气温保存为值。
![]()
按作者
当我们循环遍历字典时,我们使用。items()方法,该方法返回 k(键)和 v(值)。接下来,我们创建一个名为 temp_f 的新变量,它保存以 f 为单位的计算温度。这个方法将给定的变量转换成字符串,并允许我们在句子中使用它。我们必须在句子中想要的位置使用花括号{}。
"在{}日,气温是华氏{:.2f} . "
在句子(字符串)之后,注意引号,我们应用。format()方法。我们在句子中提供了想要打印出来的变量,在本例中,是 k(工作日)和 temp_f(计算出的华氏温度)。“ :.2f ”表示我们将计算出的值(float)转换成一个字符串,小数点后有两位小数。查看官方网站了解更多信息。
。格式(k,temp_f)
关于循环的伟大之处在于,我们需要这样做一次,但是循环将在每个工作日重复并执行想要的任务。
while 循环
在 Python 中, while 循环用于迭代,直到满足某个条件。基本上,只要条件保持为真,循环就会执行多次。当 if 变为 False 时,循环停止。让我们用一个简单的可视化来阐明这个说法。
![]()
按作者
一个简单的 while 循环如下工作。参数被求值,根据表达式,代码被执行,直到参数被求值为真。当它变为 False 时,循环停止。
非常重要:在 while 循环的每一步,我们都需要改变参数,以避免无限循环。
让我们看一个计算天数的例子。
![]()
按作者
我们从第一天开始。检查参数(变量 day 的值)是否小于或等于 8,如果为真,则打印输出。打印之后,我们将变量 day 的值加 1,以避免无限循环。如果我们没有在每个循环中增加一天,变量 day 将保持等于 1,因此小于 8,循环将是无限的。
用中断和继续控制循环
break 和 continue 语句帮助我们微调循环及其执行。
- break 语句完全跳出循环
- continue 语句跳过当前循环的剩余部分,转到下一次迭代
它们都可以在 for 和 while 循环中使用。
break 语句
![]()
按作者
break 语句用于我们想要将从循环中断开的情况,即使条件没有变为 False 或者我们已经迭代了整个序列。同样,如果使用了中断,则任何后续的 else 程序块都不会被执行。
让我们举一个简单的例子,看看我们一周内的气温测量值。假设我们想要停止打印温度值,如果空气温度等于或高于 18°c。
代码打印出温度值,当我们到达星期五,温度为 18.9°C 时,循环中断并停止。
![]()
按作者
代码打印出温度值,当我们到达星期五,温度为 18.9°C 时,循环中断并停止。
连续语句
![]()
按作者
继续语句有点类似于中断语句,但是不是中断循环,而是开始下一次迭代。左边的形象化帮助我们获得更清晰的理解。
假设有这样一种情况,我们希望打印出温度,但不包括低于 15°c 的温度。这意味着,我们打印出除星期四以外的所有温度。对于这样的任务,我们将使用 continue 语句。因此,我们遍历字典,当温度低于 15°C 时,continue 语句被激活,它避免执行 print 语句,而是跳到下一次迭代。让我们来看看代码。
![]()
按作者
continue 语句非常适合于丢弃或排除任务,我们的目标是避免一个值、一组值或某种情况。
结论
控制流是与编程相关的最重要的主题之一,它当然不同于有用的代码和无用的代码。从简单的数据探索到“最先进的”神经网络,我们到处都在处理某种控制流。
有许多与控制流相关的例子,但是通过这些简单的例子,我们看到了 Python 提供的可能性。我的想法是让例子尽可能简单。最好总是从简单的开始,然后朝着更复杂的例子努力。
随着我们继续我们的旅程,我们将会遇到更复杂的例子,并看到更多有趣的与控制流相关的方法以及我们到目前为止学到的所有东西。
对于这篇文章或我在媒体上的其他文章有任何问题或建议,欢迎通过 LinkedIn 联系我。
感谢您抽出时间,干杯!🙂
初学 Python 熊猫
友好的熊猫帮助我们处理电子表格数据
在上一篇文章中,我们看到了 Numpy 在处理大量数字数据时如何让我们的生活变得更加轻松。但是有时候,还会涉及到其他类型的数据,而且 Numpy 并不总是最好的解决方案。在这样的情况下, 熊猫 就派上了用场。它有许多应用的可能性,但我的首选情况是读取文件,而不仅仅是数字,即。csv 或。xlsx 文件,包含某种观察结果(数字数据),也包含关于这些观察结果的文本信息或元数据。
这篇文章的结构如下:
- 介绍
- 熊猫系列和熊猫数据框
- 读取数据
- 检查数据
- 选择数据
- 简单的情节
- 结论
享受阅读吧!🙂
![]()
介绍
熊猫是什么?Pandas 是一个基于 Numpy 的非常受欢迎的库,用于处理表格数据、数据操作和分析。Pandas 最好的一点可能是它将数据存储为包含行和列的 Python 对象,与存储在 Excel 文件中的数据非常相似。此外,这样我们可以很容易地可视化我们的数据,使我们的工作比处理列表或字典形式的数据容易得多。此外,优于 Numpy 的优点是它处理多种数据类型(即字符串),而不仅仅是数字数据,尽管我需要提到缺点,这使它与 Numpy 相比速度较慢。
熊猫怎么装?
嗯,如果使用 Anaconda,Pandas 是预装在基本环境中的。然而,通常情况下,为您的新项目创建新环境是一个好的实践。要在新环境中安装 Pandas,我们需要激活该环境,然后输入:
康达安装熊猫
如果正在使用 pip,可以通过键入以下命令来安装 Pandas:
pip 安装熊猫
如何进口熊猫?
当导入某些库时,包括 Pandas,我们遵循一个惯例,基本上这意味着我们使用已建立的库的缩写。对于熊猫,我们用“ pd ”。
进口熊猫作为 pd
熊猫系列和熊猫数据框
在我们开始讨论 Pandas 的一些工具之前,我们需要提到 Pandas 用来存储数据的两个数据结构,Pandas 系列和 Pandas 数据框架。可以把 Pandas 系列想象成一个有 1 列的 Excel 电子表格,外加一个索引列,或者更好,如果你熟悉 Numpy,可以想象成一个一维数组。
想象在一个点位置的每日气象观测,例如两周的风速。让我们看一个例子。
![]()
作者图片
我们使用 pd。Series() 命令,并提供一个列表,在本例中是一个长度为 14 的 floats 列表。打印出来后,我们还会得到序列中每个元素的索引。我们还可以从一维 Numpy 数组创建一个序列。为了提高效率,我将使用同样的风速表。
尝试打印出新创建的系列,并与之前的结果进行比较。
你现在可能会想,如果我们有多个系列会怎么样,如果我们也有降水测量,那么 Pandas 会把我们的数据存储在数据帧中。我很确定,你明白了,一个数据帧只是多个系列的容器。可以把它想象成一个包含多个列和一个索引列的 Excel 电子表格,或者一个 N 维的 Numpy 数组。让我们看一个例子。
为了创建数据帧,我们使用 pd。DataFrame() 命令,并提供一个字典,其中键表示我们的列名,值是之前创建的 Numpy 数组(我们的数据)。
![]()
作者图片
看起来有点类似于 N 维数组。每个 Pandas DataFrame 由以下组件组成:索引、列和数据(值)。索引是每行的标签(“名称”),而每列也有它们的标签(“名称”)。索引总是从零(0) 开始,直到 n-1 ,其中 n 是行数。
![]()
作者图片
这个数据帧只包含浮点(数字),但是允许任何数据类型(整数、字符串、布尔值..),尽管如果在一个列中组合多种数据类型,必须小心。
为了避免意外行为,我们还可以如下指定列数据类型:
也可以使用 pd。Series() 命令用一个字典,如果你想命名创建的系列。
创建系列和数据框有更多的选择和可能性,你可以在官方熊猫网站上找到。但是在处理数据科学或数据分析任务时,我们经常需要处理大型数据集,这些数据集以逗号分隔值的形式存储。csv)或 Excel 电子表格(。xlsx)。让我们看看如何将这样的文件读入熊猫数据帧。
读取数据
要读取一个. csv 文件,我们使用 pd.read_csv() 命令。当文件被正确格式化后,Pandas 会计算出分隔符、列名、数据类型等。都存在于我们的数据中。所有提到的都可以被提供,并且更多的参数可以被添加到 pd.read_csv() ,以扩大读取不同格式文件的可能性。此外,为每个(或一些)列提供 dtype 参数,显著提高了读取文件的速度,但也大大降低了内存使用,在这篇精彩文章中可以找到更多信息。
为了了解 pd.read_csv() 如何工作,我创建了一个数据集,其中包含 2018 年全年的一些气象观测数据,我们将把这些数据作为熊猫数据帧读入并进行处理。为了可视化新创建的数据帧,Pandas 提供了一些方便的方法, df.head() 和 df.tail() (df 代表我们的数据帧的名称,在我们的例子中是 weather_data)。
在这里,有几件事情是重要的:我们需要确保我们的 test_data 文件在 s ame 文件夹中,就像我们的 Jupyter notebook (或者。py 文件),其次我们要正确拼写文件的名称,并提供扩展名。csv 。
![]()
作者图片
。head() 方法打印出前 5 行(默认情况下,我们可以通过在括号中输入任意整数来改变),而。tail() 打印出最后 5 行(也是默认的)。
以与…相同的方式。csv 文件,我们也可以读取 Excel 文件。
weather _ excel = PD . read _ excel(" test _ data . xlsx ")
有时会出现错误,“ XLRDError:Excel xlsx 文件;不支持 ”,那么确保将 openpyxl 库安装到您选择的环境中,并使用命令:
weather _ excel = PD . read _ excel(" test _ data . xlsx ",engine="openpyxl ")
我使用了一个读取逗号分隔值文件或 Excel 文件的基本示例,但是请确保您检查了官方文档()。csv 此处为,而为。xlsx 这里)来找出我们可以用这两种方法使用的所有论点。另外,你可以看看我以前的一篇关于用熊猫打开和处理格式不太好的文件的文章。
检查数据
在我们成功读取数据后,我们需要更仔细地查看我们的数据帧。我们已经打印了前 5 行和后 5 行,但是剩下的呢?类似地,检查数据帧的形状总是一个好的做法,就像我们对 Numpy ndarrays 所做的那样。因此,我们使用 df.shape 方法,该方法返回数据帧的行数和列数。
![]()
作者图片
数据帧有 365 行和 5 列,因为 2018 年有 365 天,我们有每天的观察值。此外,我们有一个日期栏,和 4 个不同的观察。
通过使用 df.info ()方法,我们可以获得关于数据帧的更多信息,该方法产生列的长度、数量和数据类型,以及内存中的大小。
![]()
作者图片
在水文(和气象)任务中,我们经常对数据集的统计参数感兴趣,Pandas 为这个问题提供了一个解决方案。它提供了最常用的统计参数,如方法,平均值,中位数,最小值,最大值,偏斜度等。我们来看几个例子。
![]()
作者图片
![]()
作者图片
![]()
作者图片
有趣的是, df.max() 也用日期计算对象类型列。由于我们没有将日期转换为 datetime 格式,因此最大值不是 12/31/2018,而是一个字符串的最大值,即 9,所以是 9/9/2018。为了将日期转换成 datetime 格式(Python 随后将其评估为实际日期),我们使用了 pd.to_datetime() 函数,如下所示:
现在,在我们将日期转换为 datetime 格式之前,尝试打印出 dataframe 的前 5 行,并与前面的示例进行比较。此外,我们可以看到列的数据类型发生了变化。
![]()
作者图片
现在让我们更进一步,学习从我们的 weather_data 数据框架中选择数据的某些部分。
选择数据
为了从数据帧中选择数据,我们有几个选项,例如使用方括号[] 、。loc[] 或。iloc[] 功能。当然还有更多的可能性,但这三种应该可以满足我们的大部分需求。
方括号
使用方括号,我们可以选择一列(返回熊猫系列)或多列(返回熊猫数据框架)。假设我们只对降水数据感兴趣。
![]()
作者图片
或者如前所述,我们还可以在方括号内提供列名列表,然后返回新的 DataFrame。
![]()
作者图片
也可以通过给方括号提供索引来选择数据,但这种方法不常使用。
。位置[]
的。锁定方法工作方式略有不同。这意味着通过索引标签来选择数据,而且,数据类型取决于我们所处理的数据帧的索引数据类型。我们如下使用它:
![]()
作者图片
它也适用于具有其他类型索引(字符串、日期时间等)的数据帧。).
仅选择一个索引标签,返回一个系列。选择两个标签或一个子集,返回一个新的数据帧。
![]()
作者图片
请注意,当提供多个标签时,我们需要将它们存储在一个列表中(因此有两个方括号)。还有,。loc 包括最后的值,例如与索引一个列表相反。
带。loc 我们也可以选择多行多列!
![]()
作者图片
所以,。loc 只能用于标签(整数,字符串,日期时间…),它可以选择行,但也可以选择列,我们可以提供单个标签,子集或列表的选择。
。iloc[]
。iloc 方法的工作方式与。loc 方法,但是它只支持行的整数位置(无论 DataFrame 的索引是什么数据类型,)。iloc 正在通过整数位置选择数据)。仅使用一个值(索引)将返回一个序列,而使用多个索引或一个子集将返回一个数据帧。
![]()
作者图片
。iloc 也可用于同时选择列,但方式与行相同,通过它们的整数位置。让我们来看看。
![]()
作者图片
所以在上面的例子中,我们选择了第 7、第 8、第 9 和第 10 行,以及第 1 到第 3 列。(行和列的计数都从零开始)
简单的情节
获得数据的统计数据并知道如何选择数据帧的某些部分是很好的,但看到数据集的一些图形解释当然更好。所以,作为数据探索的最后一步,让我们来看看熊猫的简单情节。对于这个例子,让我们画出 2018 年 1 月的风速。
![]()
作者图片
我们也可以绘制多个列,现在让我们看看 2018 年的夏天有多暖(或冷)。
![]()
作者图片
值得一提的是,那些情节,可以通过其他方式选择数据,你能想到任何吗?自己试试吧。
降水量大多被绘制成柱状图。因为对于这个位置,秋天在大多数情况下都是多雨的,让我们通过将 9 月和 10 月的降雨量绘制成柱状图来看看 2018 年的降雨量。
![]()
作者图片
我们并没有绘制成条形图,但是“step”参数是一个很好的变通方法,可以得到像条形图一样的图形,而不会弄乱标签。🙂
结论
今天我们学习了一些基本的熊猫函数,这些函数在处理类似电子表格的数据时是必要的。我再怎么强调也不为过,还有几十个函数和方法,所有这些都无法涵盖和展示,但是你可以在官方的熊猫文档中找到它们。此外,当您使用自己的数据时,您将会遇到许多自己的数据。
但是作为第一步,为了熟悉我们的朋友熊猫,你一定会发现上面的建议很有用。
对于这篇文章或我在媒体上的其他文章有任何问题或建议,欢迎通过 LinkedIn 联系我。
感谢您抽出时间,干杯!🙂
用于数据科学的 Python 用于 Plotly Dash 交互式可视化的引导程序
将引导程序添加到您的交互式仪表盘
![]()
威廉·艾文在 Unsplash 上的照片
问候数据从业者。
欢迎来到 2021 年,这里的新趋势是围绕着机器学习。大多数雇主,甚至是数据科学家自己,都沉浸在部署新的机器学习模型的想法中,这种模型可以为他们预测几乎所有的事情。
Gif by 师叔运气
如果你曾经在这个领域工作过,你会理解现在的大多数组织甚至没有足够的基础去冒险进入机器学习,但是他们仍然想要。
他们甚至没有抓挠—
- 正确的数据仓库实践
- 完全了解所拥有的数据
- 利用云工具进行机器学习
也就是说,数据可视化将永远是数据探索阶段的必需品。没有可视化的帮助,没有人能正确理解数据。谢天谢地,今天我们有许多图书馆来帮助我们。这里有几个。
因此,作为一名数据科学家,你能够通过干净和漂亮 的可视化来传达你的发现是非常重要的。毫无疑问,它会让你鹤立鸡群。
破折号
![]()
纽约石油天然气由 Dash App 图库
你听说过剧情破折号吗?
达什是这个街区的新成员。我在这里写了一下。
Dash 是新的开源 Python 库,让你无需了解任何 HTML、CSS 或 Javascript 就能构建令人惊叹的数据可视化应用。
一切都在 Python 中,给已经了解 Python 的数据科学家带来了真正的美好时光。最重要的是,快,美,好学。
Dash 还可以通过编程实时更新其仪表盘,无需任何交互。
它真正处于部署和自动化您的仪表板的最前沿,没有需要管理部署、移动响应和 SRE(站点可靠性工程)带来的其他东西的麻烦。
那么你还在等什么,现在就开始行动吧!
Gif 来自 Reddit
在本文中,我们将深入探讨如何通过在 Dash 中实现 Bootstrap 来美化 Dash 应用。它提供了非常有用的工具,如导航条、按钮、表单,以及更多。
但是首先,对于那些不知道的人来说,到底什么是' Bootstrap '?
引导程序
![]()
对于那些没有网络开发知识的人来说,HTML 和 CSS 构成了网页的基本前端框架。我肯定你以前在什么地方听说过他们。
每个网站都需要 HTML 和 CSS,它们是构建网站的基础。
当谈到为桌面和移动优化网站时,事情变得很混乱。根据谷歌的数据,超过一半的网络流量来自手机。这种情况催生了一个时代,网络开发者开始更加重视移动网站和应用。
这就是 Bootstrap 的用武之地。
Bootstrap 是目前开发移动响应网站最流行的 CSS 框架。
根据 BuiltWith 和 w3tech , Bootstrap 在 CSS 框架领域排名第一,占全球所有网站的大约 26.8% ,其中包括网飞和 Udemy 等热门网站。
它使前端开发更快更容易,节省了您在优化所有形状的设备时的工作。
bootstrap 的惊人特性之一是网格系统。默认情况下,Bootstrap 将您的布局分成 12 列,可随您使用的任何设备进行扩展,使其成为一个移动响应框架。
![]()
它还有预定义的类,允许你轻松开发有用的特性,比如按钮、导航条和表单。
这会节省你几个小时的工作时间。
现在,我们回到 Dash。
先决条件
Dash 可视化基于 Plotly。如果你已经和相处得很好,那会有很大帮助。
要做到这一点,你还需要在使用熊猫进行数据转换方面有坚实的基础。我已经写了许多关于以上所有主题的指南,在继续之前,请随意修改它们。
**
破折号
你也了解基本的破折号破折号概念比如—
- Dash 的布局由核心组件和 HTML 组件组成
- 基本的 HTML 概念,如 Div 和容器**
进口
安装
pip install dash
pip install dash-bootstrap-components
使用 Jupyter 笔记本
import dash
import dash_core_components as dcc
import dash_html_components as html
import plotly.graph_objs as go
import pandas as pd
import dash_bootstrap_components as dbc
from dash.dependencies import Input, Output
组织的引导
无论出于什么原因,如果您正在构建一个仪表板,以适合用户的方式设计和组织它是很重要的。Dash 本身并没有为了组织的目的而优化,我们可以使用引导来实现。
网格系统 欢迎来到网格。
Gif by 乐高
Bootstrap 使用网格系统来组织你网站上的元素。
在 Bootstrap 中,你的网站是由行和列组成的。
您可以根据需要将每行拆分或组合成不同数量的列。
![]()
截图来自getbootstrap.com
在纯 HTML+ CSS 中是什么样子的:
<div class="container">
<div class="row">
<div class="col-sm">
One of three columns
</div>
<div class="col-sm">
One of three columns
</div>
<div class="col-sm">
One of three columns
</div>
</div>
</div>
Dash bootstrap 中是什么样子的:
row = html.Div(
[
dbc.Row(
[
dbc.Col(html.Div("One of three columns")),
dbc.Col(html.Div("One of three columns")),
dbc.Col(html.Div("One of three columns")),
],no_gutters=True,
),
]
)
上面的两个代码块产生相同的东西,即一行,默认高度为分成 3 列,列之间没有任何间隔。
然后,您可以向这些列中添加任何元素,如破折号图表、破折号按钮和导航条,以便正确地组织它们。
调整高度和宽度的重要提示—
您可以调整行高和列宽
让我们试试那个。
![]()
作者图片
app.layout = html.Div(
[
dbc.Row(dbc.Col(html.Div("A single, half-width column"), width=6,style={"border":"2px black solid"})), dbc.Row(
dbc.Col(html.Div("An automatically sized column"), width="auto",style={"border":"2px black solid"})
), dbc.Row(
[
dbc.Col(html.Div("One of three columns"), width=3,style={"border":"2px black solid"}),
dbc.Col(html.Div("One of three columns"),style={"border":"2px black solid"}),
dbc.Col(html.Div("One of three columns"), width=3,style={"border":"2px black solid",'height':'10rem'}),
]
),],className = 'container')
通过在列中设置宽度和高度参数,我们可以完全控制如何组织页面上的元素。
通过使用 dash bootstrap,您还可以使用 className 参数将类分配给所有组件。注意我是如何将容器类分配给主 Div 的,只需在最后一行指定它。
什么是类?
类是你给元素起的名字,用来给它们分配某些设计元素。您可以在单独的 CSS 文件中定义这些类。
假设您创建了一个名为“BIG”的类,并为其分配了 size =“2px”。现在你可以通过声明 className = 'BIG '来轻松地将这个类分配给你想要的任意多个元素。在设计你的元素时,这是一种更有效的方式。
有了这个,让我们在实际的可视化上尝试一下。
![]()
作者录音
app.layout = html.Div([
dbc.Row([
dbc.Col(scatterplot1, width=4),
dbc.Col(scatterplot2, width=8),
])
],className = 'container'
)
使用上面的代码块,我们已经预先定义了 2 个散点图。
第一个散点图被设置为第二个散点图宽度的一半。
这对于正确规划和显示图表非常有用。从我的经验来看,如果用户觉得看起来不干净,你的图表往往会被忽视。
人们忽视那些忽视人的设计。—弗兰克·奇梅罗,设计师
导航栏
既然我们已经了解了基础知识,让我们来看看更有趣的东西。在你的仪表盘上设置一个功能齐全的导航条怎么样?
导航条
让我们建立一个简单的导航栏,让我们能够轻松地导航到特定的页面。导航条反应灵敏,持续停留在页面顶部,功能齐全。
![]()
作者录音
PLOTLY_LOGO = "[https://images.plot.ly/logo/new-branding/plotly-logomark.png](https://images.plot.ly/logo/new-branding/plotly-logomark.png)"
# defining navbar items
home_button = dbc.NavItem(dbc.NavLink('Home',href="#home", external_link=True,className='navlinks'))
statistic_button = dbc.NavItem(dbc.NavLink('Statistics',href="#statistics", external_link=True,className='navlinks'))
trend_button = dbc.NavItem(dbc.NavLink('Trend',href="#trend", external_link=True,className='navlinks'))
news_button = dbc.NavItem(dbc.NavLink('News',href="#news", external_link=True,className='navlinks'))navbar = dbc.Navbar(
dbc.Container(
[
html.A(
dbc.Row(
[
dbc.Col(html.Img(src=PLOTLY_LOGO,className = 'logo',height="30px")),
],
align="center",
no_gutters=True,
),
href="#home",
),
dbc.NavbarToggler(id="navbar-toggler"),
dbc.Collapse(dbc.Nav([home_button,statistic_button,trend_button,news_button],className='ml-auto work-sans', navbar=True), id="navbar-collapse", navbar=True),
],
),
color="rgb(42,62,66)",
dark=True,
style = {'background-color':'#191919'},
className = 'navbar-change',
expand= 'lg'
)
首先,我们定义放置在导航栏上的所有按钮。这些按钮是在 NavLink 中定义的,同时将它与我们希望它带我们去的任何页面名称相关联。例如,统计按钮上有 href = '# 统计'。
然后我们可以设置 HTML。Header(id= 'Statistics') 在页面的标题上,点击那个按钮会把我们带到这里。
当我们点击按钮时,您可以在屏幕的左下角清楚地看到这种情况。
所有的设计都是通过颜色、样式和类名参数完成的。
现在剩下要做的就是简单地将所有按钮作为列表传入 dbc.NavBar。****
你可以在这里阅读所有关于 dash 引导组件的信息。
(电视机的)超大屏幕
![]()
截图自 unbounce.com
如今大屏幕经常被用来把你的注意力吸引到特色内容。你肯定会无意中发现它。在本节中,我们将尝试使用 Dash Bootstrap 生成一个超大屏幕。
大屏幕是登陆页面的完美选择。
登录页面是用户访问您的仪表板/网站时登录的页面。这是他们看到的第一个东西,所以一定要留下好印象。
让我们试一试。
![]()
作者截图
jumbotron = dbc.Jumbotron(
[
html.H1("Revenue Dashboard", className="display-3"),
html.P(
"A deep dive into revenue for the year, segmented by verticals.",
className="lead",
),
html.Hr(className="my-2"),
html.P(
"Data is updated every day at 12pm."
),
html.P(dbc.Button("Overview", color="primary"), className="lead"),
]
)
一个相对简单的着陆大屏幕,包括一个标题、描述和一个将用户带到页面主要内容的按钮。注意我们是如何使用 className 来设计这里的许多元素的。
我在这里只使用了默认的类名。你可以在官方 CSS 类名这里找到它们。
我希望这已经阐明了 dash bootstrap 有多有用。
有了这个,你完全可以用 Python 设计你的仪表盘,同时用熊猫和plottly绘制你的图形。
文章的完整代码:
结论
![]()
恭喜你, 你现在可以完全用 Python 高效地设计自己的仪表盘了。
在本文中,您已经了解了:
- 网格系统
- 使用 Bootstrap 组织/设计
- 导航栏
- (电视机的)超大屏幕
你还有很多工作要做。
现在去数据从业者,
建立一些东西并与我分享,
与世界分享。
如果你遇到困难,请随时联系我。
你可以在下面找到我的信息。
在你走之前
我们的数据之旅还没有结束。我正在撰写更多关于数据行业的故事、文章和指南。你绝对可以期待更多这样的帖子。与此同时,请随意查看我的其他文章,暂时填补您对数据的渴望。
像往常一样,我引用一句话作为结束。
目标是将数据转化为信息,将信息转化为洞察力。
—卡莉·菲奥莉娜
订阅我的时事通讯,保持联系。
也可以通过 我的链接 注册一个中等会员来支持我。你将能够从我和其他不可思议的作家那里读到无限量的故事!
我正在撰写更多关于数据行业的故事、文章和指南。你绝对可以期待更多这样的帖子。与此同时,可以随时查看我的其他 文章 来暂时填补你对数据的饥渴。
感谢 的阅读!如果你想与我取得联系,请随时联系我在 nickmydata@gmail.com 或我的 领英简介 。也可以在我的Github中查看之前写的代码。
Python 数据科学备忘单(2021)
2021 年初学 Python 的绝对基础
![]()
阿什·爱德华兹拍摄于 Unsplash 的照片
Python 已经成为 2021 年执行数据科学最流行的计算语言。但在你能够制作令人震惊的深度学习和机器学习模型之前,你需要先了解 Python 的基础知识和不同类型的对象。
查看下面的不同部分,了解各种类型的对象及其功能。
变量和数据类型
Python 中的变量用于存储值,在这里您将学习如何将变量赋给特定的值,如何更改该值,以及在 Python 中转换为不同的数据类型。
变量赋值
>>> x=5
>>> x
5
变量计算
变量可以加、减、乘、除等。
x = 5 for all functions below
- 两个变量的和
>>> x+2
7
- 两个变量相减
>>> x-2
3
- 两个变量的乘法
>>> x*2
10
- 变量的指数运算
>>> x**2
25
- 变量的余数
>>> x%2
1
- 变量的除法
>>> x/2
2.5
数据类型的转换
Python 提供了将变量从一种数据类型转换成另一种数据类型的能力。
- 字符串变量
>>> str(x)
'5'
- 变量到整数
>>> int(x)
5
- 浮动变量
>>> float(x)
5.0
- 布尔型变量
>>> bool()
True
列表
list 是 Python 中的一种数据结构,它是可变元素的有序序列。列表中的每个元素或值称为一个项目。
以下代码中的我的列表和我的列表 2 在本节中用作列表示例。
>>> a = 'is'
>>> b = 'nice'
>>> my_list = ['my','list',a,b]['my', 'list', 'is', 'nice']>>> my_list2 = [[4,5,6],[3,4,5,6]][[4, 5, 6],[3, 4, 5, 6]]
选择列表元素
从列表或项目中选择特定项目的不同方式。
- 选择索引 1 处的项目
>>> my_list[1]
'list'
- 选择倒数第三项
>>> my_list[-3]
'list'
- 选择索引 1 和 2 处的项目
>>> my_list[1:3]
['list', 'is']
- 选择索引 0 之后的项目
>>> my_list[1:]
['list', 'is', 'nice']
- 选择索引 3 之前的项目
>>> my_list[:3]
['my', 'list', 'is']
- 复制我的列表(_ u)
>>> my_list[:]
['my', 'list', 'is', 'nice']
- my_list[list][itemsOfList]
>>> my_list2[1][0]
3
>>> my_list2[1][:2]
[3, 4]
列表操作
因为列表是可变的,所以你可以将列表相加或者相乘。
- 添加列表
>>> my_list + my_list
['my', 'list', 'is', 'nice', 'my', 'list', 'is', 'nice']
- 乘法列表
>>> my_list*2
['my', 'list', 'is', 'nice', 'my', 'list', 'is', 'nice']
列出方法
向列表中添加项目、删除项目、计数项目等各种功能。
- 获取项目的索引
#Gets the index at of item 'a' in the list
>>> my_list.index(a)
2
- 清点物品
#Counts how mnay times 'a' occurs in the list
>>> my_list.count(a)
1
- 将项目添加到列表中
#Adds 'T' to the end of the list
>>> my_list.append('T')
['my', 'list', 'is', 'nice', 'T']
- 移除项目
#Removes T from the list
>>> my_list.remove('T')
['my', 'list', 'is', 'nice']
- 删除项目。v2
#Removes the first value from the list
>>> del(my_list[0:1])
['list', 'is', 'nice']
- 删除项目. v3
#Removes the last item from the list
>>> my_list.pop(-1)
['my', 'list', 'is']
- 颠倒列表
#Reverses the entire list
>>> my_list.reverse()
['nice', 'is', 'list', 'my']
- 插入一个项目
#Inserts item 'T' at index 0 in the list
>>> my_list.insert(0,'T')
['T', 'my', 'list', 'is', 'nice']
- 对项目进行排序
#Sorts the list alphabetically
>>> my_list.sort()
['is', 'list', 'my', 'nice']
用线串
Python 中的字符串是一系列字符。字符串是不可变的。这意味着一旦定义,就不能更改。
在本节中,下面代码中的 my_string 被用作字符串的示例。
>>> my_string = 'thisStringIsAwesome'
>>> my_string
'thisStringIsAwesome'
选择字符串元素
从字符串或值中选择特定值的不同方法。
- 选择索引 3 处的项目
>>> my_string[3]
's'
- 选择索引 4 到 8 处的项目
>>> my_string[4:9]
'Strin'
字符串操作
你可以用不同的方式把字符串加在一起。
- 添加字符串
>>> my_string + 'Innit'
'thisStringIsAwesomeInnit'
- 乘法字符串
>>> my_string*2
'thisStringIsAwesomethisStringIsAwesome'
- 成串
>>> 'm' in my_string
True
字符串方法
改变字符串外观或从字符串中提取统计数据的各种函数。
- 字符串转换为大写
#Converts all values from the string to uppercase
>>> my_string.upper()
'THISSTRINGISAWESOME'
- 字符串转换为小写
##Converts all values from the string to lowercase
>>> my_string.lower()
'thisstringisawesome'
- 计数字符串元素
#Counts how mnay times 'w' appears in the string
>>> my_string.count('w')
1
- 替换字符串元素
#Replaces all values of 'e' in the string with 'i'
>>> my_string.replace('e','i')
'thisStringIsAwisomi'
- 去除空白
#Removes any unecessary spaces from the string
>>> my_string.strip()
'thisStringIsAwesome'
NumPy 数组
NumPy 数组是由相同类型的值组成的网格。维数是数组的秩;数组的形状是一组整数,给出了数组在每个维度上的大小。
下面代码中的 my_array 和 my_2darray 在本节中被用作 NumPy 数组的例子。
>>> my_list = [1,2,3,4]
>>> my_array = np.array(my_list)
>>> my_2darray = np.array([[1,2,3],[4,5,6]])
选择 NumPy 数组元素
从数组中选择特定值的不同方法。
- 选择索引 1 处的项目
>>> my_array[1]
2
- 选择索引 0 和 1 处的项目
>>> my_array[0:2]
array([1, 2])
- my _ 2d array[行,列]
>>> my_2darray[:,0]
array([1, 4])
NumPy 数组操作
这些操作向您展示了如何将两个数组相加,将一个值乘以一个数组,并对该数组进行比较。
- 添加列表
>>> my_array + np.array([5,6,7,8])
array([ 6, 8, 10, 12])
- 乘法数组
>>> my_array*2
array([2, 4, 6, 8])
- 大于或小于
>>> my_array > 3
array([False, False, False, True])
NumPy 数组方法
各种函数,增加一个数组的值,删除一个值,计算一个数组的平均值和中值,等等。
- 获取数组的维数
>>> my_array.shape
(4,)
- 将项目追加到数组
#Adds an arrasy to the end of another array
>>> np.append(other_array)
- 在数组中插入项目
#Inserts the value of 5 into index 1 of the array
>>> np.insert(my_array,1,5)
array([1, 5, 2, 3, 4])
- 删除数组中的项目
#Removes the value in index 1 from the array
>>> np.delete(my_array,[1])
array([1, 3, 4])
- 阵列的平均值
>>> np.mean(my_array)
2.5
- 数组的中值
>>> np.median(my_array)
2.5
- 标准偏差
>>> np.std(my_array)
1.118033988749895
图书馆
像 NumPy 这样的各种库需要先导入 Python,然后才能使用该库的任何函数。
- 导入库
>>> import nump as np
- 选择性进口
>>> from math import pi
现在和可预见的将来,Python 都是编程和数据科学的霸主。但是在接受创建复杂模型和项目的挑战之前,您需要先学习 Python 的基础知识。
开始的时候使用这个备忘单作为指南,需要的时候再回头看,你将会很快成为一名专业的 Python 程序员。
与 1k+人一起加入我的电子邮件列表,免费获得完整的 Python for Data Science 备忘单小册子。
用于数据科学的 Python 如何在 Youtube 上获得更多的浏览
用数据科学解决 YouTube 的攀升
![]()
亚历山大·沙托夫在 Unsplash 上拍摄的照片
问候数据从业者,
欢迎回到每周阅读的用数据科学解决现实世界的问题。就在队列中,当今千禧一代面临的第一世界最优先的问题之一是—
不知道怎么在 Youtube 上炸。
不用担心,数据可以解决一切,
而且我们要解决 YouTube 的问题。今天。
油管(国外视频网站)
世界著名的视频分享网站。YouTube 在 YouTube 上发布视频赚了大钱,这不是什么秘密。如果你生活在岩石下,大型 YouTubers 每年可以赚几百万,而每周只需要上传一个视频。
这进一步导致每个人都想分一杯羹。
gif 由 andersonpaak 提供
每个人和他们的狗现在都是优步,希望赚很多钱。
然而,这些人在游戏中姗姗来迟,由于平台已经饱和,他们正在努力为自己的视频增加流量。
在 YouTube 上,更多的浏览量意味着更多的钱。因此,人们开始问这样的问题—
- 什么样的视频标题吸引更多的观看?
- 哪个视频类别吸引更多的观看?
- 如何优化我的视频标签来吸引更多的浏览量?
你很幸运,我们今天会回答你的问题。
在这篇文章中,我们将对来自美国的一些 YouTube 视频数据进行一些探索性数据分析,提取我们能得到的任何见解。
先决条件
我们今天要表演的 EDA 主要使用了 Plotly 。理解构建简单的 Plotly 图的基础很重要。我在这里 ,写了关于构建基本 Plotly 图的内容,涵盖了构建任何类型的图时需要理解的概念。
为了使用 Plotly,您经常需要使用 Pandas 来转换数据。我会保护你的。我碰巧也写了一本关于熊猫 的指南。
一旦你理解了和和熊猫,你就为执行几乎任何类型的探索性数据分析做好了充分准备。我会把这两个库称为我的数据分析和可视化的面包和黄油。
进口
使用 Jupyter 笔记本电脑…
# To manipulate data, NLP and visualizations
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
import os
from subprocess import check_output# Standard plotly imports
import plotly as py
import plotly.tools as tls
from plotly.offline import iplot, init_notebook_mode
import plotly.graph_objs as go
import cufflinks# Using plotly + cufflinks in offline mode
init_notebook_mode(connected=True)
cufflinks.go_offline(connected=True)# To interactive buttons
import ipywidgets as widgets
from ipywidgets import interact, interact_manualimport warnings
warnings.filterwarnings("ignore")#nlp
import string
import re #for regex
import nltk
from nltk.corpus import stopwords
import spacy
from nltk import pos_tag
from nltk.stem.wordnet import WordNetLemmatizer
from nltk.tokenize import word_tokenize
from nltk.tokenize import TweetTokenizerfrom wordcloud import WordCloud, STOPWORDS
现在这是一个很大的进口,但我保证他们是必要的。
数据集源
![]()
作者截图
数据集从Kaggle.com导入。它被命名为趋势 YouTube 视频统计,它拥有来自许多国家的数据。为了这个宠物项目的目的,我们将只分析来自美国的数据。
注意:数据是通过 YouTube API 提取的。因此,您可以提取特定国家和不同时间段的数据。来源中的所有信息。
导入数据集
df = pd.read_csv(filepath)
在这个探索性数据分析中,我们的目标是—
- 更好地理解数据集
- 了解视图是否因视频类别而异
- 了解视频标题的质量是否影响其观看
让我们从理解数据中的变量开始。
#Looking for Null values and the types of our data
df.info()
df.head()
![]()
作者图片
![]()
作者图片
查看这些列,我们将重点关注这个项目中的视图。让我们找出视图的分布。
df[['views']].iplot(kind="histogram",
bins=50, theme="white",
histnorm='probability',
title="Distribuition of Views",
xTitle='Distribution',
yTitle='Probability')
![]()
作者录音
单纯从这张图表上,我们可以看出大多数视频的浏览量都不到500 万次。也有很多离群值,这些可能是病毒式传播的视频。让我们看看没有异常值的分布。
def RemoveOutliers(df_num):
# calculating mean and std of the array
data_mean, data_std = np.mean(df_num), np.std(df_num)
# setting the threshold value
threshold = data_std * 3
#setting lower and upper limit
lower, upper = data_mean - threshold, data_mean + threshold
# array without outlier values
outliers_removed = [x for x in df_num if x > lower and x < upper]
return outliers_removeddf['removed_views_outliers'] = RemoveOutliers(df['views'])
df['removed_views_outliers'].iplot(kind="histogram",
bins=100, theme="white",
histnorm='probability',
title= "Distribution of Views Without Outliers",
xTitle='Distribution',
yTitle='Probability')
![]()
作者录音
几乎有 40%的视频浏览量不到 20 万次,超过 20 万次之后,获得浏览量只会越来越难。
视频类别
众所周知,某些类别会比其他类别获得更多的浏览量。让我们探索一下可用的视频类别,以及每个类别中发布视频的频率。
rename_channels = {1:'Film/Animation', 2:'Cars/Vehicles', 10:'Music', 15:'Pets/Animals',
17:'Sport',19:'Travel/Events',20: 'Gaming',22:'People/Blogs',23:'Comedy',
24:'Entertainment',25:'News/Politics',26:'How to/Style',27:'Education',
28:'Science/Technology',29:'Non Profits/Activism'}
df['category_name'] = np.nan
df['category_name'] = df['category_id'].map(rename_channels)percent_category = round(df["category_name"].value_counts(), 2)
categories = round(df["category_name"].value_counts() / len(df["category_name"]) * 100,2)labels = list(categories.index)
values = list(categories.values)trace1 = go.Pie(labels=labels,
values=values,
marker=dict(colors=['red']),
text=(percent_category.index.map(str)),
hovertext=(percent_category.values.astype(str)))layout = go.Layout(title="Views by Category",
legend=dict(orientation="h"));fig = go.Figure(data=[trace1], layout=layout)
iplot(fig)
![]()
作者录音
看起来拥有最多视频的前三个类别是—
- 娱乐
- 音乐
- 如何/风格
如果我们要发布这些类别中的任何一个,这对我们了解我们的竞争对手是很重要的。在音乐和娱乐类别中,大多数视频是由名人或 T2 娱乐公司发布的,而不是 YouTubers。因此,他们视频输出的质量和数量超过我们任何人都是有道理的。
我们知道这些类别拥有最多的视频,但它们拥有最多的浏览量吗?
trace1 = go.Bar(x=df.groupby(['category_name'])['views'].sum().index,
y=df.groupby(['category_name'])['views'].sum().values,
name='Total Views by Category', visible=True)trace2 = go.Bar(x=df.groupby(['category_name'])['channel_title'].nunique().index,
y=df.groupby(['category_name'])['channel_title'].nunique().values,
name='Unique Channels by Category', visible=False)data = [trace1, trace2]updatemenus = list([
dict(active=-1,
showactive=True,
buttons=list([
dict(
label = 'Total Views by Category',
method = 'update',
args = [{'visible': [True, False, False]},
{'title': 'Sum of views by Category'}]),
dict(
label = 'Total Channels by Category',
method = 'update',
args = [{'visible': [False, True, False]},
{'title': 'Total Channels by Category'}]),]),
)
])layout = dict(title='Different Metrics by each category <br> Sum of views by Category',
showlegend=False,
updatemenus=updatemenus)fig = dict(data=data, layout=layout)iplot(fig)
![]()
作者录音
音乐和娱乐类别确实拥有最多的视图和频道。一个有趣的事实是,即使音乐类别中发布的视频越来越多,娱乐类别中的频道也比音乐更多。这意味着娱乐品类比音乐略饱和。
关于饱和度这个话题,我们再深入探讨一下。
#saturation metrics by category
views_list = np.array(df.groupby(['category_name'])['views'].sum().tolist())
channels_list = np.array(df.groupby(['category_name'])['channel_title'].nunique().tolist())
videos_list = np.array(df.groupby(['category_name'])['title'].nunique().tolist())views_by_videos = views_list/videos_list
views_by_channels = views_list/channels_list
我们创造新的衡量标准—
- 浏览量 _by_videos
- 浏览频道
按类别查看每个视频和频道的浏览量。通过这样做,我们可以大致判断某个类别的饱和程度。
当我们以 YouTuber 起家时,我们不想进入一个饱和的视频类别。除非你有极不公平的优势,否则进入更欢迎新手的类别将是明智的。想象一下在音乐上和‘Justin Bieber’这样的人竞争,不是什么好结局。
让我们在图上显示饱和度指标。
trace1 = go.Bar(x=df.groupby(['category_name'])['title'].nunique().index,
y=views_by_videos,
name='Saturation - Views per Video', visible=True)trace2 = go.Bar(x=df.groupby(['category_name'])['title'].nunique().index,
y=views_by_channels,
name='Saturation - Views per Channel', visible=True)data = [trace1,trace2]updatemenus = list([
dict(active=-1,
showactive=True,
buttons=list([
dict(
label = 'Saturation - Views per Video',
method = 'update',
args = [{'visible': [True, False]},
{'title': 'Saturation - Views per Video'}]),
dict(
label = 'Saturation - Views per Channel',
method = 'update',
args = [{'visible': [False, True]},
{'title': 'Saturation - Views per Channel'}]),
]),
)
])layout = dict(title='*(Select from Dropdown)* Saturation Metrics by Category',
showlegend=False,
updatemenus=updatemenus)fig = dict(data=data, layout=layout)
iplot(fig)
![]()
作者录音
从剧情可以看出,音乐依然是每视频浏览量和每频道浏览量的领头羊。然而,排在第二位的是非盈利/行动主义类别,该类别在每视频浏览量和每频道浏览量上都位居第二。看前面的图表,非盈利/行动主义中的视频和频道数量都是最低的。
这意味着该类别的视频和频道最少,但每个视频和频道的浏览量最高。因此,这将是一个理想的参赛类别。
如果让我选一个,我会选电影/动画类别。从数据来看,它没有太多的视频和频道,但每个视频的浏览量和每个频道的浏览量都非常高。此外,动画是我感兴趣的领域,所以这是一个优势。
视频标题
现在我们已经选择了一个类别,是时候探索视频标题了。正如许多 YouTubers 用户提到的,视频标题和缩略图很重要。
你可以有一个非常好的视频。没有一个好的视频标题和缩略图,没有人会点击它——包括所有的 YouTuber
因此,让我们确保我们的视频标题得到优化,以获得更多的意见。
让我们先来看看标题和标签中的字数,
以及它是如何影响我们的观点的。
#Unique word count
df['count_unique_word']=df["title"].apply(lambda x: len(set(str(x).split())))
df['count_unique_word_tags']=df["tags"].apply(lambda x: len(set(str(x).split())))df[['count_unique_word_tags','count_unique_word']].iplot(kind="histogram",bins=50, theme="white", histnorm='probability', title="Distribuitions of Word Count in Title and Tags",
xTitle='Distribuition',
yTitle='Probability')
![]()
作者录音
看字数的分布。对于视频标题,看起来大多数视频标题都有 5 到 10 个单词。对于视频标签来说,大部分视频标签都少于 30 个字。
我们可以以此为参考。让我们看看它是如何影响视图的。
# Dataframe for unique word count for video titles
df3 = df[['count_unique_word','views']]
df3 = df3.groupby('count_unique_word').mean().reset_index()# Dataframe for unique word count for video tags
df4 = df[['count_unique_word_tags','views']]
df4 = df4.groupby('count_unique_word_tags').mean().reset_index()trace1 = go.Bar(x=df3['count_unique_word'],y=df3['views'],name = 'Views vs Video Title Word Count',visible = True)
trace2 = go.Bar(x=df4['count_unique_word_tags'],y=df4['views'],name = 'Views vs Video Tags Word Count',visible = True)data=[trace1,trace2]updatemenus = list([
dict(active=-1,
showactive=True,
buttons=list([
dict(
label = 'Views vs Video Title Word Count',
method = 'update',
args = [{'visible': [True, False]},
{'title': 'Views vs Video Title Word Count'}]),
dict(
label = 'Views vs Video Tags Word Count',
method = 'update',
args = [{'visible': [False, True]},
{'title': 'Views vs Video Tags Word Count'}]),
]),
)
])layout = dict(title="*(Select from Dropdown)* Views vs Word Count",
showlegend=False,
updatemenus=updatemenus)
fig = dict(data=data, layout=layout)
iplot(fig)
![]()
作者录音
有意思。对于视频标题字数,包含 2 个单词的标题获得最多的浏览量,之后,每增加一个单词,浏览量也会增加,直到 11 个单词,浏览量开始持平。这将建议我们的视频标题长度为 2 个单词,或介于 5 至 10 个单词之间。
对于视频标签字数来说,25 个或更少的视频有着几乎相同的浏览量。浏览量在 26 到 34 个单词标签之间开始达到峰值,之后浏览量开始下降。
相当有趣的东西。
视频标题/标签内容
现在我们已经大致知道了视频标题和标签的长度,我们想知道应该在里面放什么样的文字。通过探索文字内容,我们可以大致看出其他 Youtubers 在他们的视频中放了什么样的文字。我们可以用词云图把这些词清晰地形象化。
plt.rcParams['font.size']= 15
plt.rcParams['savefig.dpi']= 100
plt.rcParams['figure.subplot.bottom']= .1
stopwords = set(stopwords.words("english"))plt.figure(figsize = (15,15))
wordcloud = WordCloud(
background_color='black',
stopwords=stopwords,
max_words=1000,
max_font_size=120,
random_state=42
).generate(str(df['title']))print(wordcloud)
fig = plt.figure(1)
plt.imshow(wordcloud)
plt.title("WORD CLOUD - VIDEO TITLE")
plt.axis('off')
plt.show()
![]()
作者截图
plt.figure(figsize = (15,15))
wordcloud = WordCloud(
background_color='black',
stopwords=stopwords,
max_words=1000,
max_font_size=120,
random_state=42
).generate(str(df['tags']))print(wordcloud)
fig = plt.figure(1)
plt.imshow(wordcloud)
plt.title("WORD CLOUD - TAGS")
plt.axis('off')
plt.show()
![]()
作者截图
对于视频标题,我们可以看到被提到最多的词是' WANT '和' TALK '。这些词出现得最多是有原因的,我们将不得不进一步探索以更好地理解这些数据。
对于视频标签,我们可以看到提到最多的词是'惊艳'、末世'、一生'、周'。这些都是很普通的词。同样,为了理解这些数据,我们必须进行更深入的分析。
项目的代码
作者要点
结论
![]()
马修·斯特恩在 Unsplash 上的照片
如果你已经读到这里,你一定很想得到 T21 在 Youtube 上的钱。如果是这样的话,你只需要在你的 Youtube 之旅中遵循这些指示,你就可以开始生活了。
- 在电影/动画类别中创建视频
- 视频标题为 2 个字或 5 到 10 个字
- 有 26 到 34 个视频标签
- 视频标题中包含最多的是“想要”和“说”——找出原因
- 神奇,最后,生活和一周是视频标签中包含最多的-找出原因
如果你们还没有达成共识,这些数据显然只是作为参考,并不能证明能给你带来更多的浏览量。这是对来自美国的一小部分 YouTube 数据的探索性分析,这意味着它不会准确地显示包括美国在内的任何地方的观看行为。
我希望这对你和我来说至少是有趣的。
下期再见!
订阅我的时事通讯,保持联系。
我们的数据之旅还没有结束。我正在撰写更多关于数据行业的故事、文章和指南。你绝对可以期待更多这样的帖子。与此同时,你可以随时查看我的其他 文章 来暂时填补你对数据的渴望。
像往常一样,我引用一句话作为结束。
数据是新的科学。大数据掌握着答案。~ VMware 首席执行官帕特·基尔辛格
感谢 阅读!如果你想与我取得联系,请随时通过 nickmydata@gmail.com 或我的 LinkedIn 个人资料 联系我。您也可以在我的Github中查看之前的撰写代码。
Python For-Else 和 While-Else 用 4 个真实的例子清楚地解释了
编程,PYTHON
获得清晰的理解,并学习 4 个可以使用它的用例!
![]()
图片由来自 Pixabay 的 Dwi Rizki Tirtasujana 提供,作者修改
你知道在 Python 中for
和while
循环有一个else
子句吗?为什么我们会有它?这是什么意思?它是如何工作的?在你的日常工作中,你可以在哪里使用它?来,让我们一起探索!
理解 For-Else 和 While-Else
对于我们许多人来说,**else**
这个术语会造成混淆,因为if-else
语句中的else
有意义,但是else
表示循环?奇怪!有那么一会儿,不要试图去理解else
这个词。试想 Python 通过其循环为您提供了一个额外的特性。让我们看看它是如何工作的。
句法
带有可选else
子句的for
循环:
**for** *variable_name* **in** *iterable***:**
#stmts in the loop
.
.
.
**else**:
#stmts in else clause
.
.
.
带有可选else
子句的while
循环:
**while** *condition:*
#stmts in the loop
.
.
.
**else**:
#stmts in else clause
.
.
.
- 如果循环被
break
语句终止,则else
子句不会被执行。
- 如果循环没有命中
break
语句,那么else
子句将在循环完成所有迭代后执行一次(也就是说,在循环正常完成后)。
![]()
Python 中按作者排序的图像-For-Else 和 While-Else
Raymond Hettinger ,Python 先驱在他的一个演讲中提到,在 Python 中,总是在循环的*else*
子句旁边注释*#no break*
。如果循环的*else*
被称为 nobreak ,那么就不会有混淆。
简化的真实世界示例
你在想,这有什么意义?这对我有什么用?来,让我们探索一些对你有用的有趣的应用。
a)搜索
传统方法
传统上,在搜索程序中使用标志来标记是否找到了某个值。如果您必须搜索更多的值,那么维护许多标志变量会增加复杂性。
#*************************Traditionally*************************
#set flag = 0
#loop the items in the data
#if the item was found in the loop then set flag = 1
#display search result by verifying the flag value
#***************************************************************
Python 中更好的方法
但是使用 Python,您可以轻松地编写一个搜索程序,而不必使用任何标志。您可以使用一个break
语句和循环的else
子句来轻松完成这项工作。下面给出了一个简单的搜索示例。
作者代码:使用 for-else Python 进行搜索
b)检查限值
传统方法
传统上,标志变量用于检查数据是否突破了给定的限制。
#*************************Traditionally*************************
#set flag = 1
#loop the items in the data
#if any item breaches the given limits then set flag = 0
#display the validation result by verifying the flag value
#***************************************************************
Python 中更好的方法
但是使用 Python,您可以使用break
语句和循环的else
子句轻松验证这一点。不需要标志。看看下面的例子。
作者代码:使用 for-else Python 检查限制
c)嵌套循环
传统方法
更古老的语言有**GOTO**
语句来改变程序中的执行行。这种方法很棘手,因此更容易犯人为错误。如果你有嵌套循环和一些GOTO
语句,那么只会增加更多的复杂性和风险。
#************************Older Languages************************
#outer_label: outer loop
# inner loop
# if a condition was met, GOTO outer_label
#***************************************************************
Python 中更好的方法
但是在 Python 中,没有GOTO
语句,这是一件好事。事实上,这也是 Python 引入break
和 loop 的else
语句的原因之一——取代GOTO
语句。因此,让程序员更容易和更安全地使用它们。
- 当我们不得不管理嵌套循环时,我们可以很容易地使用一个
break
语句从一个内部循环中脱离出来,并获得到外部循环的执行行。
- 如果您需要检查内部循环是否正常完成了所有的迭代而没有遇到 break 语句,您可以使用循环的
else
子句。
请记住,在 Python 中,您不能使用单个break
语句来中断多个嵌套循环。一个break
语句只能中断包含它的一个循环。
为了清楚地理解这一点,请看下面的例子。
作者编写的代码:带有 Else 子句的嵌套循环示例 Python
d)与异常处理一起使用
如果您想在循环引发异常时终止它,那么您可以在except
块中使用break
语句。此外,如果你想在循环正常完成(也就是说,完成了所有的迭代而没有中断)的情况下执行一些语句,那么你可以在循环的else
子句中编写这些语句。通过下面的例子可以更好地理解它。
作者编写的代码:带有异常处理的循环 Else 子句示例
外卖:
- 循环的
else
子句(for
/ while
)只有在循环完全完成执行而没有命中 break 语句时才会执行(换句话说,循环已经正常完成)。
- 循环的 else 子句中的语句将在循环正常完成后执行一次。****
- 如何让困惑远离?把循环的 else 子句想象成不中断。总是在循环的
else
子句旁边注释#no break
,这是 Python 先驱 Raymond Hettinger 建议的提高代码可读性的最佳实践。
4 。你能在哪里使用它?
- 进行搜索(不使用标志,而是使用
break
和循环的else
)。
- 检查限制/边界(不使用标志,使用
break
和循环的else
)。
- 管理嵌套循环(当您需要根据内部循环是正常执行还是命中 break 语句来采取行动时)。
- 您可以将它与异常处理一起使用(当您需要
break
on exception 并仅在循环正常完成时执行某些语句时)。
****5。一个警告:总是验证循环的else
子句的缩进,检查它是否与循环关键字(for
/ while
)正确对齐(缩进)。错误地,如果一个循环的else
子句与一个if
语句对齐(缩进),那么代码将会中断。
有了这些,我希望你对 Python 中的 for-else 和 while-else 有信心。您还熟悉了它的一些应用程序。跟我重复一遍“不再迷茫:)”!
如果您遇到了在-else** 和 while-else 中的任何其他应用(使用),请在下面评论。**
最初发布于 2021 年 2 月 12 日python 简化 。
阅读更多关于 Python 和数据科学的有趣文章, 订阅 到我的博客pythonsimplified.com。** 你也可以通过LinkedIn**联系我。****
参考
**https://stackoverflow.com/questions/9979970/why-does-python-use-else-after-for-and-while-loops https://www.udemy.com/course/python-3-deep-dive-part-1/ **
面向 Excel 用户的 Python 第 1 部分
开始使用 pandas 处理数据
![]()
这是一个教程系列的第一部分,面向所有想开始使用 python 而不是 Excel 的人,例如,自动化任务或提高速度和可伸缩性。或者只是出于好奇。
当出现以下情况时,您处于正确的位置:
- 您可以使用 Excel 合并各种工作表或表格(使用诸如 Index、Match、VLookup
- 使用简单的数学运算,如求和与平均
- 使用条件聚合函数(例如,每个类别的交易总和)或数据透视表
这篇博文不会涵盖您需要了解的关于转储 Excel 的所有内容,这也不是本系列的目标。第 1 部分旨在帮助您从 Excel 转向 Python/Pandas。
虽然 Excel 有它自己的位置,而且非常快速和方便,但我希望这个博客文章系列能够帮助您探索除 Excel 之外的其他数据处理方式。
你可以在 Jupyter 笔记本上找到这篇博文,以及我们在 HOSD 指导 GitHub repo 中使用的数据。
本教程的目标
我们将像在 Excel 中一样探索和组合不同的销售数据表。这些表格是:
- 命令
- 订单 _ 行
- 制品
一会儿会有更多关于数据的细节。
要求
虽然 Python 的一些基本经验会有所帮助,但你不必成为一名程序员或数据科学家来遵循本教程。理想情况下,你应该听说过熊猫,并亲自花了一些时间与 python 打交道。只要能让自己感到舒适,不气馁就行了。试着跟上并阅读我们没有涉及的内容。你可以随时回到这里。
1.形势
![]()
来源:pix abayhttps://pix abay . com/photos/office-startup-business-home-office-594132/
想象一下,你刚开始在一家大型巴西在线零售商的销售部门工作。这是你的第一天。你设置好你的电脑,从你公司的咖啡师那里得到一杯很棒的双份浓缩咖啡。休息室里的旧咖啡机,你已经准备好开始了!
不幸的是,现在是夏天,你团队中的每个人都在度假。这让你有很多问题和时间自己探索公司数据。太好了!
你脑中的一些问题是:
- 🏬以交易数量衡量,这家公司实际上有多大?
- 💵按年收入计算的公司规模?
- 👗每个类别我们提供多少产品?
- 📦我们的交付有多成功?
您知道您可以简单地通过使用标准公式和一个数据透视表来使用和组合 Excel 中的三个表 Orders、Order_lines 和 Products 来回答这些问题。
但是因为没有人在你身边,你开始一份新工作标志着你职业生涯的一个新时代,所以这次你想做得不同。为什么不试试 LinkedIn 上人人都在谈论的这个叫做 python 的很酷的东西呢?好啊,我们走吧!
2.开始—数据
本教程中使用的数据是 Olist 的巴西电子商务公共数据集的改编和简化版本。
在 Excel 中,你可以做类似于打开一个新的 Excel 文档,进入数据>获取数据>从文本,然后搜索。csv 文件,并单击文本导入向导(选择分隔符、文件编码、数据格式等。)或者您只需双击。csv 文件,希望 Excel 会自己正确地解决所有问题(哎哟!).
![]()
但是现在我们在 python 的土地上,事情有点不同(但是不会更难!).
使用。csv 文件,我们将使用包熊猫将其作为 dataframe(表格的别称)读取。我们将导入的表保存在具有适当名称的变量中。数据帧名称通常以 df 结尾。
# setup notebook and load tables
import pandas as pd
orders_df = pd.read_csv("data/Orders.csv")
order_lines_df = pd.read_csv("data/Order_lines.csv")
products_df = pd.read_csv("data/Products.csv")
太好了!我们刚刚将三个表加载到我们的 python 程序中。
我们有吗?
与 Excel 不同,导入的数据不会立即显示。如果你不明确说出你想看什么,Python 是相当沉默的。
在您使用 pandas 的 read_csv()方法导入 csv 文件并将其保存为 something_df 之后,我们可以使用 something_df.head(5) 显示前 5 行。
这样,我们可以简单地看到表的样子以及我们正在处理哪些列。让我们为我们的三张桌子做那件事。
订单将陈述所有交易、交易发生的时间以及订单是否已成功履行。每个订单都有一个 order_id(每个订单唯一)、customer_id(每个客户唯一)、order_status 和购买日期。
# Preview of the Orders table
orders_df.head(5)
![]()
但是,orders_df 不包含单个订单的内容。为此,我们需要查看一下 Order_lines 表,并使用订单 id 连接这两个表。在 order_lines_df 中,每一行指一个交易项目,包括数量、价格和运费值。
# Preview of Order_lines table
order_lines_df.head(5)
![]()
产品类别还不是这个表的一部分,所以我们需要将产品表(products_df)与 order_lines_df 结合起来,以防我们想要分析类别级别的数据。目前,products_df 中只有两列,产品 id 和相关的产品类别。
# Preview of Products table
products_df.head(5)
![]()
3.用数据回答我们的问题
![]()
来源:pix abayhttps://pix abay . com/photos/audit-chart-graph-hand-writing-3229739/
现在我们已经把所有的东西都载入了笔记本,我们可以开始用数据来回答我们的问题了。
3.1 🏬以交易数量衡量,这家公司实际上有多大?
因为我们在销售部门工作,所以当我们在这里谈论公司规模时,我们指的是交易数量。为此,我们可以从使用 python 的 len()方法计算 order_df 中的行数开始。这类似于单击 Excel 中的列并在屏幕底部读取计数。
len(orders_df)99441
或者,我们可以对 order_id 列使用 pandas pd.unique()方法,该方法将返回 order_id 的唯一值。在这种情况下,应该没有什么不同,因为订单 id 本质上是唯一的。
但是,如果我们对有重复项的列(如 customer_id)使用这种方法,那么我们对整个表使用 len()的结果将是不正确的。因此,让我们正确地使用 pd.unique()来获取唯一的订单 id,然后对其使用 len()。
注意:可以使用 table_df["column_name"]来访问表中的列
len(pd.unique(orders_df["order_id"]))99441
起初,我们对大量的交易感到惊讶,但随后我们注意到两件事:
- 并非所有订单都已交付。我们应该只查看使用 order_status 成功交付的订单。
- 这些交易发生在 2016–2018 的三个年。我们应该看看每年的交易数量。
我们先把所有没有发货成功的订单过滤掉。为了找出有多少 order_status 选项,我们将在 Excel 中单击该列的过滤器。
![]()
Excel 只向我们显示唯一的值,这很方便,但是正如我们刚刚了解到的,我们可以通过在 order_status 列上使用 pd.unique()来达到同样的目的。
pd.unique(orders_df["order_status"])array(['delivered', 'invoiced', 'shipped', 'processing', 'unavailable',
'canceled', 'created', 'approved'], dtype=object)
在 Excel 中,我们只需取消选择除“已交付”之外的所有选项来过滤我们的表格。Excel 将检查每一行,只显示 order_status 等于“已交付”的行。
我们可以告诉 python 也这样做。如果我们的计算机是一个人,我们会告诉她:请以 order_df 表为例,只显示 order_df 表中“order_status”列等于“delivered”的行。幸运的是,我们的计算机不是人,需要更少的文本来做同样的事情。
翻译成 python 就是:
orders_df[orders_df["order_status"]=="delivered"]
![]()
我们应该将过滤后的结果保存到另一个数据帧中,以便进一步使用该表。
delivered_orders_df = orders_df[orders_df["order_status"]=="delivered"]
接下来,我们希望对每年的交易进行细分。我们将把 order_purchase_timestamp 列转换成一个日期时间列。然后我们将从中提取年份,并保存到一个名为“year”的新列中。
这类似于使用 Excel 的 Year()公式。
![]()
# the data type of this column is not yet datetime
delivered_orders_df["order_purchase_timestamp"].dtypesdtype('O')# convert to datetime
delivered_orders_df["order_purchase_timestamp"] = pd.to_datetime(delivered_orders_df["order_purchase_timestamp"])
delivered_orders_df["order_purchase_timestamp"].dtypesdtype('<M8[ns]')# extract the year from datetime and save to new column
delivered_orders_df["order_year"] = delivered_orders_df["order_purchase_timestamp"].dt.year# show the new table
delivered_orders_df.head(5)
![]()
太好了!现在,我们拥有了统计每年交付订单所需的一切。在 Excel 中,我们会按年份过滤,对每一行进行计数,并将其写入一个新的汇总表。
更高级的 Excel 用户可以跳过这一手动任务,使用以年份为列的数据透视表,并计算 order_id 的值。
![]()
我们可以通过使用 pandas groupby 和 agg 函数来实现同样的功能。对于这样一个简单的任务来说,语法可能看起来令人生畏,但是这个函数是非常通用和强大的,这是 Excel 的 pivot 梦寐以求的。
首先,我们选择要聚合的表(what),然后定义要聚合的列(where),接着定义聚合函数(how)。
delivered_orders_df.groupby(by=["order_year"], as_index=False).agg({'order_id':'count'})
![]()
3.2 💵按年收入计算的公司规模?
在交易数量旁边,我们还想找出收入。如上所述,我们需要通过 order_id 作为键将 delivered_order_df 与 order_lines_df 结合起来。此外,我们注意到 order_lines 表有关于订单商品数量和价格的信息,但还没有收入的信息。
因此,这是我们需要做的:
- 用 order_item * price 的值在 order_lines_df 中创建一个列“line_revenue”
- 创建一个汇总表,将每个订单的 line_revenue 汇总为 order_revenue
- 将聚合表与 delivered_orders_df 合并,并合计每年的 order_revenue
在 Excel 中,我们会创建一个新列,使用一个数据透视表,使用类似 VLOOKUP 的东西将数据透视表与其他过滤表合并,在结果表上构建另一个数据透视表…
我们不要那样做。这不是一个复杂的任务,但是我们已经明白为什么我们应该切换到 Python 来完成这样的任务。一旦你习惯了语法和过程,一切都会变得更容易和更容易实现。
order_lines_df.head()
![]()
# create new column and save the result of order_item_id * price
order_lines_df["line_revenue"] = order_lines_df["order_item_id"] * order_lines_df["price"]
order_lines_df.head()
![]()
# aggregate table and sum up line_revenue to get the total order value per order id
order_lines_agg_df = order_lines_df.groupby(by=["order_id"], as_index=False).agg({"line_revenue":"sum"})
order_lines_agg_df.head(5)
![]()
delivered_orders_df.head(5)
![]()
现在我们想合并上面的两个表,order_lines_agg_df 和 delivered_orders_df。就结果而言,我们所做的类似于 Excel 的 VLOOKUP()公式,但是我们使用 order_id 作为键来合并两个表。python 代码非常简单,几乎像散文一样。
之后,我们重复上面在计算每年的交易数量时所做的事情,但这次我们对 order_revenue 求和。
# merge the two tables to get revenue per order
delivered_orders_merged_df = pd.merge(left=delivered_orders_df, right=order_lines_agg_df, how="left", on="order_id")
delivered_orders_merged_df.head(5)
![]()
revenue_per_year_df = delivered_orders_merged_df.groupby(by=["order_year"], as_index=False).agg({"order_revenue":"sum"})
revenue_per_year_df.columns = ["order_year", "total_revenue"]
revenue_per_year_df
![]()
这看起来不对,对吧?不完全是。这被称为科学记数法,其中非常大或非常小的数字显示为计算结果。
4.649029e+4 表示 4.649029 * 10⁴表示 4.649029 * 10000 表示 46490.29。您可以使用以下代码片段更改 Jupyter 笔记本显示数字的方式。
pd.set_option('display.float_format', lambda x: '%.2f' % x)# display float numbers up to second decimal point
revenue_per_year_df
![]()
太好了!看起来我们公司很有活力。使用这种方法,我们可以计算各种各样的东西。比如每年的客户数量和平均订单价值。交易与客户的比率可能也很有意思,因为当一家公司的收入仅基于少数高价值客户时,该公司可能会面临风险。如果他们转向我们的竞争对手呢?
一旦你完成本教程,这些都是很好的调查问题。但首先让我们回到我们的其他问题。
3.3 👗每个类别我们提供多少产品?
既然您已经知道了如何计算一个列的唯一值,现在这将变得非常容易。上面,我们计算了 orders_df 中唯一 order _ ids 的数量。现在,我们必须计算 products_df 中的 product_id,以大致了解我们的产品范围,即我们提供多少不同的产品。
products_df.head()
![]()
# count unique products in our database
len(pd.unique(products_df["product_id"]))32951
由于我们还想知道每个类别的产品数量,我们复制上面的方法,计算每年的交易数量。我们现在将使用“产品类别名称”而不是“订单年份”,并且使用“产品标识”而不是“订单标识”来计数。
# count products per category
products_per_category_df = products_df.groupby(by=["product_category_name"], as_index=False).agg({"product_id":"count"})
products_per_category_df.columns = ["product_category_name", "product_id_count"]
products_per_category_df.head(5)
![]()
# number of categories
len(products_per_category_df)73
哇,我们提供 73 个类别的近 33,000 种产品?就产品数量而言,前 5 大类别是什么?
像在 Excel 中一样,我们只需按 product_id_count 对表进行排序。
# sort by product_id_count descending
products_per_category_df.sort_values(by=["product_id_count"], ascending=False).head(5)
![]()
这可能是你发现在巴西公司工作时懂葡萄牙语很有帮助的地方…
3.4 📦我们的交付有多成功?
在 3.1 中,我们创建了一个只包含成功交付订单的新表。如果我们计算该表的唯一订单 id 数,并将该数除以未过滤的 orders_df 表中的唯一订单 id 数,那么我们就可以得到成功交付的百分比。
unique_orderids_total = len(pd.unique(orders_df["order_id"]))
unique_orderids_delivered = len(pd.unique(delivered_orders_df["order_id"]))
# calculate ratio and round to second decimal point
round(unique_orderids_delivered/unique_orderids_total,2)0.97
97%的交付成功率是我们公司值得骄傲的事情。
4.摘要
现在,由于 pandas,我们了解到使用 python 是 Excel 的真正替代方法,我们有点忘乎所以,根据来自三个原始表的聚合数据计算了各种 KPI。快速看一下手表就能知道我们的胃已经知道了什么:该吃午饭了!
但是在我们去公司之前。文鼎机器,我们想保存我们所做的,并将其发送给我们的同事。我们如何做到这一点?
我们可以简单地将数据帧导出为 csv 文件,并将它们发送给那些喜欢使用 Excel 的人。
# Export dataframe as csv for Excel
products_per_category_df.to_csv("output/products_per_category_df.csv", index=False)
今天,我们学习了一些基本但强大的函数,这些函数允许我们用 Python 代替 Excel 工作。这里有一个总结。
- pd.read_csv() 可以用来导入。作为数据帧的 csv 文件
- table_df.head() 向我们展示了该表的预览输出
- 为了访问 table_df 的一列,我们键入table _ df[" column _ name "]
- len(PD . unique(table _ df[" column _ name "])给出该列中唯一值的数量
- 我们可以将两列相乘,并使用table _ df[" new "]= table _ df[" col 1 "] table _ df[" col 2 "]*将结果保存在新列中
- 除了 Excel 的数据透视表,我们还可以汇总每个类别的值,例如,使用table _ df . group by(by =[" category "],as_index=False)计算每年的交易次数或合计每个订单的收入。agg({"col3":"sum"})
- 除了在 Excel 中使用 VLOOKUP(),我们还可以使用 pd.merge(left=table_1_df,right=table_2_df,how="left ",on="key_column")
我建议你继续下去,进一步研究这个数据集或你自己的数据集。每当你不知道如何做某件事时,最好的方法是查看熊猫文档或谷歌你的特定问题(“来自字符串的 python 日期时间”或“python 数据透视表”),看看其他人是如何做的。只是不要气馁,继续学习!
![]()
开始总是艰难的,但坚持下去,你会比任何 Excel 专业人员更有效、更快。如果你需要一些关于数据分析和数据科学是否可以成为你的职业道路的一般性指导,我们 HOSD 辅导公司提供免费的一对一辅导。
只需在我们的网站上点击一下,您就可以预约我们的导师。不要害羞:)
此外,我们想宣布,本教程和许多其他教程将在会议结束时变成一个带有问答环节的在线研讨会。要了解更多关于我们的产品或与我们联系,请务必前往我们的 HOSD LinkedIn 页面。
坚持学习!
乔纳斯
HOSD 创始人兼数据分析师导师