TowardsDataScience 2023 博客中文翻译(八)
你可能已经看到大多数程序员将作为一个占位符,用于在执行过程中未使用或不需要的变量。然而,大多数人不知道的是,默认情况下,Python 将你最后一次执行的结果赋值给变量。x: int = 5在本文中,我们已经介绍了五个强大的 Python 库,它们可以用来加快和增强项目的探索性数据分析阶段。这些库从简单的图形到利用自然语言处理技术与数据进行交互都有涉及。我强烈建议你查看这些库并探索它们的功能。你永远
每个数据科学家都应该避免的 5 个错误
原文:
towardsdatascience.com/5-mistakes-every-data-scientist-should-avoid-7e3523f6a9ec
照片由 Lucas George Wendt 提供,来源于 Unsplash
通过避免这些陷阱来提升你的数据科学水平
·发表在 Towards Data Science ·阅读时间 7 分钟·2023 年 1 月 12 日
–
数据科学是一个广阔的领域,需要大量的经验和知识才能掌握。在我们成为更好的数据科学家的过程中,定期自我审视、从错误中学习并避免无意中犯的错误是至关重要的。
在这篇博客中,我们将讨论我作为数据科学家期间遇到的 5 个陷阱。
1. 辛普森悖论
这是数据科学中一个相对被低估的领域。辛普森悖论发生在我们合并组数据时,局部数据模式变得不可观察。这可以通过一个例子来更好地解释:
图片来自作者
我们有一个关于某打车应用流失客户的数据集。我们注意到,对于区域 1,客户的里程越多,流失的可能性越大。对于区域 2,客户的里程越少,流失的可能性越大。最后,对于区域 3,无论客户的里程是多少,流失的可能性几乎相等。在上图中,当我们将这三个区域合并时,我们会看到什么?它就变成了一大堆数据,没有明显的模式。
这可能听起来像是一个棘手的问题。但我们可能需要从最简单的角度来解决它。检测和可能解决辛普森悖论的一些方法包括:
-
我们不应仅仅在整个数据集上应用统计分析(预处理或模型评估)。相反,我们应该将数据集拆分,分别在不同的切片——数值变量的区间、分类变量、类别和标签等——上运行相同的分析。这可以帮助我们识别在整个数据集上可能不显著的局部数据模式。
-
当我们注意到辛普森悖论时,解决这一问题的一种方法是为这一样本组训练一个不同的模型,假设它们足够重要。可以添加启发式规则来确定应该使用哪个模型。
-
解决辛普森悖论的另一种可能方法是在特征工程过程中引入交互项。在上面的例子中,我们可以在面积和里程之间创建一个特征交叉,以放大这两个特征与流失概率之间的不同局部关系。
2. 选择偏差
垃圾进,垃圾出。
这对于任何分析功能都是正确的。我们必须更加重视数据质量的重要性。衡量数据质量的一个标准是数据集是否准确代表了总体。
选择偏差的一个例子可能是一个机器学习模型评估新治疗焦虑的有效性,该模型训练的数据来自于大多数患者不患有该病的研究组。虽然建立治疗的基线是重要的,但如果没有包含足够的焦虑患者数据点,就会显著削弱模型的鲁棒性和泛化能力。
为了避免选择偏差,我们应始终考虑以下几点:
-
目标人群是什么?
-
从表现角度来看,我们的训练集与目标人群有多大的不同?
-
我们的数据是如何收集的?我们是否可能低估或高估了某些群体?
-
在考虑人群由哪些群体组成时,我们是否遗漏了其他身份或参数?
当有疑问时,务必花时间审查数据及其收集策略。
3. 混杂变量
我们的模型可能没有达到预期效果的一个原因可能是我们遗漏了一些与我们的自变量(X)和因变量(y)相关的额外变量(混杂变量)。这意味着,我们不是让模型更容易地找到混杂变量与因变量之间的关联,而是让模型通过特征集与因变量之间的传递关系进行学习。
一个例子可能是分析某人患肺癌的概率。可能发现患这种致命疾病的男性比例很高。但除非我们包含吸烟习惯、家庭空气质量、职业接触毒素和癌症家族史等数据点,否则这可能不是一个非常准确的结论。
作者提供的图片
一旦我们开始包含混杂变量,我们的模型应该会有所改善,因为我们提供了与因变量有更直接关系的特征。我们也应该对我们的特征对因变量的重要性得出更准确的结论。
一般来说,我们应该不断提醒自己,相关性并不等于因果关系。当我们怀疑是否遗漏了任何混杂变量时,以下问题可能会有所帮助:
-
我们是否期望排名最重要的特征对因变量有如此大的影响?
-
专家如何解释重要特征与因变量之间的互动?
-
假设我们对自变量和因变量之间关系的逻辑解释涉及推理步骤(例如,更多男性吸烟,因此肺癌风险更高)。我们可以将中间变量纳入我们的特征集吗?
4. 异方差性
异方差性。除了这个词发音困难外,它还是一个数据科学家在未察觉时可能面临的致命陷阱。大多数时间序列分析方法在数据方差随时间保持稳定时效果最佳。问题在于,当数据的波动性随时间剧烈变化时。
作者提供的图片
即使手头的数据不是时间序列,也并不意味着我们可以摆脱异方差性。想象一下,你在拟合体重和身高之间的回归模型。较低年龄组的收入水平应该集中在较低的范围内。随着年龄的增加,收入应该以一个依赖于多个因素(如公司、职位变动、职业变化、经济情况等)的速率增加,从而导致收入分布更加稀疏。这意味着即使我们可能拥有较高的 R 平方值,随着年龄的增加,我们的模型也会变得不那么准确。
作者提供的图片
好消息是:我们可以通过提出这些问题来提醒自己:
-
错误项是否与所用特征中的任何一个相关?
-
错误项的分布是否会随着所用特征的变化而变化?
-
我们是否可以进行任何变换(如对数变换)或重采样(如自助法)以调整方差的规模?
5. 多重共线性
另一个常见的陷阱是多重共线性。这是一个自变量彼此高度相关的情况。乍一看,这可能是一个相对次要的问题。但它会使我们的解决方案不必要地复杂甚至不稳定。
比如我们想建立一个模型来估计市场营销活动的成功。使用的特征包括 Facebook 广告、Instagram 广告、TikTok 广告和 Google 广告的点击率,以及总体转化率。无论使用哪个平台,精心策划的广告都应该期望有不错的点击率,因此点击率相关的特征应具有合理的相关性。假设你加入了这家公司,你对老板的第一印象是将模型复杂度减少至少 100% 同时保持更稳定的结果。你刚刚为你的晋升做了一个有力的证明。
我们如何准确发现和解决类似问题?以下是我过去亲自使用的一些建议:
-
相关性矩阵:不言自明,用于发现线性相关的特征
-
降维:PCA、t-SNE 和 UMAP 等技术可以作为减少列数的有前途的方法,同时保留数据集中大部分的方差
-
移除/聚合变量:在上述示例中,可以使用加权平均列来替代所有彼此高度相关的列。
-
正则化:可以使用 Lasso(L1)或 Ridge(L2)等技术来克服多重共线性
结论
这里有 5 个我个人之前曾经陷入的陷阱。
不要止步于此
像任何领域一样,数据科学是一个需要不断打磨思维和获取新知识的深坑,以便从人群中脱颖而出。
如果你喜欢这篇文章,你还可以通过下面的我的推荐链接订阅 Medium 来支持我。这是一个我找到许多有趣阅读的平台。即使你完全不打算订阅,你也可以通过点赞支持我和我的创作。
[## 使用我的推荐链接加入 Medium — Louis Chan
阅读 Louis Chan 的每一个故事(以及 Medium 上其他成千上万的作者)。你的会员费用直接支持……
最后但绝对重要的是,如果我遗漏了或误解了任何关键点,请随时在 LinkedIn 上发表评论或发私信给我。让我们共同保持知识流动,并在这个领域中不断进步!
[## Louis Chan — 领先 GCP 数据与 ML 工程师 — 副总监 — KPMG 英国 | LinkedIn
一个雄心勃勃、好奇且富有创造力的个体,坚信知识领域之间的相互联系以及……
5 个更多超棒的 Python 隐藏功能 — 第二部分
原文:
towardsdatascience.com/5-more-awesome-python-hidden-features-part-2-160a533c212b
PYTHON | PROGRAMMING | FEATURES
探索一些强大的功能,以释放 Python 的全部潜力
·发布在 Towards Data Science ·5 分钟阅读·2023 年 3 月 22 日
–
图片由 Joshua Reddekopp 提供,来源于 Unsplash
Python 是一种强大且稳健的语言 —— 使 Python 脱颖而出的一个原因是它的多功能性和动态性。Python 以其酷炫的‘一行代码’而闻名。毫无疑问,每当我们看到一行优美的 Pythonic 代码时,都会感到兴奋或好奇。
在我们之前的帖子中,我们讨论了 Python 编程语言中的 5 个超棒的隐藏功能。你可以在这里找到那篇文章:
利用这些酷炫的隐藏 Python 功能,将你的编码技能提升到一个新的水平
towardsdatascience.com
我收到了很多积极的反馈,有些人甚至联系我请求更多这样的强大 Python 技巧。
那么,来看看这 5 个酷炫的隐藏功能,它们可以让你在用 Python 编程时变得更加厉害!
隐藏功能 1: 列表步进
列表步进用于选择列表的不同部分,甚至可以选择间隔不同的项。语法如下:
list[start:end:step]
-
start: 包括的第一个元素的索引
-
end: 排除的第一个元素的索引
-
step: 每次迭代之间我们跳过的元素数量
假设我们有一个包含数字 0–9 的列表,我们想要返回只有偶数的部分。我们可以这样做:
my_list: list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
even_numbers: list = my_list[::2] # [0, 2, 4, 6, 8]
在这里,我们不指定起始和结束索引,因此,Python 将起始索引视为第一个元素,结束索引视为最后一个元素(即整个列表)。然后我们指定步长为 2。因此,Python 将从第一个元素开始并返回它(即 0)。Python 然后移动 2 步(移动到 1,然后移动到 2),并返回结果(即 2)。这个过程会重复,直到遍历完整个列表。
使用列表步进的另一个强大技巧是能够使用负索引反转列表。
my_list: list = [1, 2, 3, 4, 5]
reversed_list: list = my_list[::-1] # [5, 4, 3, 2, 1]
隐藏功能 2:链式比较操作符
在编程中,我们经常需要作为逻辑流程的一部分评估多个比较。
假设我们有一个变量 x
,我们想确保 x
大于 1 但小于 10。我们通常会这样做:
x: int = 5
condition1: bool = x > 1 # check if x is greater than 1
condition2: bool = x < 10 # check if x is smaller than 10
print(condition1 and condition2) # True
在 Python 中,我们可以按如下方式链式比较:
x: int = 5
print(1 < x < 10) # True
print(10 < x < 20) # False
我们也可以这样做:
x: int = 5
print(5 == x > 4) # True
print(x < 10 < x*10 < 100) # True
隐藏功能 3:复数/虚数
如果你学习过数学,你对复数的概念应该很熟悉。对于那些没有学习过且感兴趣的人,你可以阅读以下文章作为这个话题的良好介绍。
复数 复数是实数和虚数的组合 实数是数字…
Python 中一个有趣的功能是大多数人不知道的,它完全支持复数。在数学中,我们通常使用符号 i
来表示复数。在 Python 中,我们使用 j
或调用 complex()
函数。
# Creating complex numbers
z1 = 2 + 3j
z2 = complex(4, -2) # (4 -2j)
# Accessing real and imaginary parts
print(z1.real) # 2.0
print(z1.imag) # 3.0
# Arithmetic with complex numbers
z3 = z1 + z2 # (6+1j)
z4 = z1 * z2 # (14+8j)
z5 = z1 / z2 # (0.1+0.8j)
# Conjugate of a complex number
z6 = z1.conjugate() # (2-3j)
隐藏功能 4:使用 _ 访问最后一个结果
你可能已经看到大多数程序员将 _
作为一个占位符,用于在执行过程中未使用或不需要的变量。
然而,大多数人不知道的是,默认情况下,Python 将你最后一次执行的结果赋值给变量 _
。
x: int = 5
y: int = 99
x + y # 104
print(_) # 104
隐藏功能 5:参数解包
假设我们有一个任意的函数:
def my_sum(a, b, c):
return a + b + c
我们有一个包含 3 个数字的列表,我们希望将其传递给我们的函数。我们通常写:
my_list = [1, 2, 3]
result = my_sum(my_list[0], my_list[1], my_list[2])
print(result) # 6
在 Python 中,我们可以这样做:
result = my_sum(*my_list)
print(result) # 6
*
将解包整个列表,并将每个项作为参数传递给函数。随后,我们也可以使用 **
来解包字典。
# Example of dictionary argument unpacking
def my_func(a, b, c):
print(f"a={a}, b={b}, c={c}")
my_dict = {'a': 1, 'b': 2, 'c': 3}
my_func(**my_dict)
额外功能:import antigravity
Python 中散布着几个隐藏的彩蛋。尝试 import antigravity
😁
你喜欢这篇文章吗?每月 $5,你可以成为会员,解锁对 Medium 的无限访问权限。你将直接支持我和你在 Medium 上的所有其他喜爱作者。非常感谢!
[## 使用我的推荐链接加入 Medium - David Farrugia
获得对我所有⚡优质⚡内容的独家访问权限,并在 Medium 上无限制浏览。通过请我喝杯咖啡来支持我的工作…
也许你还可以考虑订阅我的邮件列表,以便在我发布新内容时收到通知。这是免费的 😃
[## 当 David Farrugia 发布新内容时,获取电子邮件。
当 David Farrugia 发布新内容时,获取电子邮件。通过注册,如果你还没有 Medium 账户,你将创建一个…
想要联系我吗?
我很想听听你对这个话题或任何有关 AI 和数据的想法。
如果你希望联系我,可以通过 davidfarrugia53@gmail.com 给我发邮件。
5 个你需要了解的强大 Python 库,用于增强你的 EDA 过程
利用 Python 的强大功能探索和理解你的数据
·发布于 走向数据科学 ·阅读时间 10 分钟·2023 年 2 月 15 日
–
图片由 Gerd Altmann 提供,来源于 Pixabay
在运行机器学习模型之前,确保数据质量良好是至关重要的。如果我们将质量差的数据输入这些模型,可能会得到意想不到或不愿意看到的结果。然而,对数据进行准备工作并尝试理解你拥有的或缺失的内容是非常耗时的。通常,这个过程可能会消耗项目可用时间的 90%。
如果你在 Python 中进行探索性数据分析(EDA),你会知道一些常见的库,比如 pandas、matplotlib 和 seaborn。它们都是很棒的库,但每个库都有自己的细微差别,这可能需要时间去学习或记住。
近年来,出现了一些强大的低代码 Python 库,使数据探索和分析阶段的项目变得更快、更容易。
在这篇文章中,我将向你介绍这五个 Python 库,它们将提升你的数据分析工作流程。所有这些库都可以在 Jupyter notebook 环境中运行。
1. YData Profiling(之前称为 Pandas Profiling)
YData Profiling 库,前身为 Pandas Profiling,允许你基于 pandas dataframe 创建详细的报告。它非常易于导航,并提供有关各个变量、缺失数据分析、数据相关性和交互的信息。
YData Profiling 的一个小问题是处理较大数据集的能力,这可能会导致报告生成变慢。
如何使用 YData Profiling 库
可以通过终端使用 pip 安装 YData Profiling:
pip install ydata-profiling
在库安装到你的 Python 环境后,我们可以简单地从库中导入 ProfileReport
模块,并与 pandas 一起使用。Pandas 用于从 CSV 文件或其他格式加载数据。
import pandas as pd
from ydata_profiling import ProfileReport
df = pd.read_csv('Data/Xeek_Well_15-9-15.csv')
ProfileReport(df)
一旦数据被读取,我们可以将 dataframe 传递给 ProfileReport
,报告将开始生成。
生成报告所需的时间将取决于数据集的大小。数据集越大,生成时间就越长。
报告生成后,你可以开始滚动查看报告,如下所示。
选择的数据集的 Ydata Profile 报告。图片来源于作者。
我们可以深入探讨数据集中的每个变量,查看数据完整性、统计数据和数据类型的信息。
查看数据集内数字变量的关键统计数据。图片来源于作者。
我们还可以创建数据完整性的可视化。这让我们理解缺失的数据以及缺失情况如何在不同变量之间相关联。
通过 YData Profiling 报告的各种视图识别缺失值。图片来源于作者。
你可以在下面的文章中探索更多 Pandas Profiling(改名之前)的功能。
[## Pandas Profiling — Python 中的简易探索性数据分析]
使用 Pandas Profiling 库进行快速有效的 EDA
towardsdatascience.com](/pandas-profiling-easy-exploratory-data-analysis-in-python-65d6d0e23650?source=post_page-----f0100d563c16--------------------------------)
2. D-Tale
D-Tale 将你的 Pandas dataframe 提升到一个全新的水平。这个强大且快速的库使得与数据交互、进行基本分析甚至编辑变得非常简单。
我最近才发现这个库,但它已成为我探索数据时的首选库之一。
如果你想在下载之前尝试这个库,库作者提供了一个 实时示例。
如何使用 D-Tale
可以通过终端使用 pip 安装 D-Tale:
pip install dtale
然后可以与 pandas 一起导入,如下所示。一旦 pandas 读取了数据,结果数据框可以传递给 dtale.show()
import pandas as pd
import dtale
df = pd.read_csv('Data/Xeek_Well_15-9-15.csv')
dtale.show(df)
稍等片刻,D-Tale 交互式表格将出现,显示数据框中的所有数据。
D-Tale 配备了大量功能,允许你审查数据、可视化其完整性、编辑数据等。
当我们查看个别变量时,比如数据集中的 DTC 列,我们可以使用直方图可视化其分布:
D-Tale 的 Describe 模块中的交互式直方图。图片由作者提供。
还可以查看数据在分类变量中的分布情况:
通过岩性或地质形成等类别轻松可视化数据。图片由作者提供。
如果你想探索更多 D-Tale 的功能,可以在我下面的文章中找到更多信息:
## D-Tale 用于快速和轻松的井日志数据探索性数据分析
使用 D-Tale Python 库加速探索性数据分析工作流
towardsdatascience.com
3. SweetViz
SweetViz 是另一个低代码的交互式数据可视化和探索库。通过几行代码,我们可以创建一个交互式 HTML 文件来探索数据。
如何使用 SweetViz
Sweetviz 可以通过终端使用 pip 安装:
pip install sweetviz
安装完成后,我们可以将其导入到笔记本中,并使用 pandas 加载数据。
import sweetviz as sv
import pandas as pd
df = pd.read_csv('Data/Xeek_Well_15-9-15.csv')
然后我们需要调用两行代码来生成报告:
report = sv.analyze(df)
report.show_html()
这将打开一个新的浏览器标签页,显示以下设置。
SweetViz — 一个快速而强大的 EDA Python 库。图片由作者提供。
浏览器标签页打开后,你可以浏览数据框中的每个变量,查看每个变量的关键统计数据和完整性。当你点击任何变量时,如果数据是数值型数据,将打开数据分布的直方图;如果是分类数据,将显示值的计数。
此外,它还会显示该变量与数据集中其他变量之间的关系,具体以数字表示。
如果你想直观地查看,可以点击仪表板顶部的 Associations 按钮,打开一个图形相关性图。在下图中,我们可以看到一个混合的方形和圆圈,分别代表分类变量和数值变量。
方块/圆圈的大小代表关系的强度,颜色代表 Pearson 相关系数值。这应该是我迄今为止在 Python 中见过的最好的变量关系可视化之一。
使用 SweetViz 生成的变量关联。图片由作者提供。
我发现这个库的一个小问题是你需要一个宽屏幕才能查看所有水平内容而不需要滚动。然而,不要让这阻碍你利用这个库带给你 EDA 的强大功能。
4. Missingno
如果你对使用轻量级库来探索数据的完整性感兴趣,那么 missingno 是你应该在 EDA 工具箱中考虑的一个。
这是一个 Python 库,它提供了一系列可视化工具,用于理解 pandas dataframe 中缺失数据的存在及其分布。该库提供了一小部分图表(条形图、矩阵图、热图或树状图)来可视化你的 dataframe 中哪些列包含缺失值,以及缺失程度在变量之间的关系。
如何使用 MissingNo
Missingno 可以通过终端使用 pip 安装:
pip install missingno
一旦安装了库,我们可以将其与 pandas 一起导入,并将数据加载到 dataframe 中。
import pandas as pd
import missingno as msno
df = pd.read_csv('xeek_train_subset.csv')
然后,我们可以从可用的图表中调用我们想要的图表:
msno.bar(df)
msno.matrix(df)
msno.denrogram(df)
msno.heatmap(df)
missingno 库中的四个主要图表。图片由作者提供。
上述四种图表为我们提供了以下洞见:
-
数据框中每一列的完整性 —
msno.bar()
-
缺失数据出现的位置 —
msno.matrix()
-
缺失值之间的相关性 —
msno.heatmap()
和msno.dendrogram()
这个库的优点在于图表简洁易懂,并且可以快速直接地融入到报告中。
要了解更多关于这些图表的信息,我推荐深入阅读下面的文章。
使用 missingno Python 库识别和可视化机器学习前的缺失数据
使用岩石物理测井数据的示例
towardsdatascience.com
5. 草图
Sketch 是一个非常新的(截至 2023 年 2 月)库,利用 AI 的力量帮助你通过自然语言问题直接在 Jupyter 中理解你的 Pandas 数据框。你还可以使用它生成示例代码,例如如何绘制数据框中的 x 和 y,然后使用该代码生成所需的图表。
该库大多是自包含的,它使用机器学习算法来理解你的问题与数据集之间的关系。有一个函数依赖于 OpenAI 的 API,但这并不影响库的使用。
Sketch 具有很大的潜力,特别是如果你希望为客户提供一个对 Python 编码知识要求非常有限的界面的话。
如何使用 Sketch
可以通过终端使用 pip 安装 Sketch:
pip install sketch
然后我们将 pandas 和 sketch 导入到我们的笔记本中,接着从 CSV 文件中加载数据。
import sketch
import pandas as pd
df = pd.read_csv('Data/Xeek_Well_15-9-15.csv')
一旦导入了 Sketch,我们的数据框将有三个新方法可用。
第一个方法是 .ask
,它允许你用自然语言提问关于数据框的内容。
df.sketch.ask('What are the max values of each numerical column?')
这将返回数据框中每个数值列的最大值的以下行。
当询问 Sketch 返回每列最大值时的响应。图像由作者提供。
我们还可以询问数据框的完整性:
df.sketch.ask('How complete is the data?')
它将返回以下响应,以人类编写的形式而不是表格或图形。
当询问 Sketch 关于数据框的完整性时的响应。图像由作者提供。
非常令人印象深刻。但这还不是全部。
我们甚至可以查询库关于如何使用 .sketch.howto()
绘制数据框中的数据
df.sketch.howto("""How do I plot RHOB against DEPTH_MD
using a line plot and have the line coloured in red?""")
它将返回一个代码片段,说明如何执行:
从 sketch.howto 函数返回的代码片段。图像由作者提供。
运行时,将返回以下图表:
从 sketch python 库返回的代码生成的图表。图像由作者提供。
Sketch 的第三个选项是 .apply
方法,它需要一个 OpenAI API 才能运行。当我们想要从现有特征创建新特征或生成新特征时,这个功能非常有用。到目前为止,我还没有探索这个选项,但希望在不久的将来能够进行。
有关 Sketch 的更多细节,请查看以下文章:
[## Sketch:一个有前景的 AI 库,用于直接在 Jupyter 中帮助处理 Pandas 数据框
在 Jupyter Notebook 中利用 AI 的力量
摘要
在本文中,我们已经介绍了五个强大的 Python 库,它们可以用来加快和增强项目的探索性数据分析阶段。这些库从简单的图形到利用自然语言处理技术与数据进行交互都有涉及。
我强烈建议你查看这些库并探索它们的功能。你永远不知道,也许你会发现你新的最爱 Python 库。
感谢阅读。在你离开之前,你应该订阅我的内容,将我的文章直接发送到你的收件箱。 你可以在这里做到这一点!或者,你也可以 注册我的通讯 以便将额外的内容直接免费发送到你的收件箱。
其次,你可以通过注册会员来获得完整的 Medium 体验,同时支持成千上万的其他作者和我。它只需每月 $5,你将能够完全访问所有精彩的 Medium 文章,并有机会通过你的写作赚钱。
如果你通过 我的链接, 你将直接支持我,你的费用也不会增加。如果你这样做了,非常感谢你的支持。
本文使用的数据集是 Xeek 和 FORCE 2020 机器学习竞赛的一部分训练数据集的子集(Bormann et al., 2020)。它在挪威政府的 NOLD 2.0 许可证下发布,详细信息可以在这里找到:挪威开放政府数据许可证(NLOD)2.0。完整数据集可以通过这里访问。
数据集的完整参考是:
Bormann, Peter, Aursand, Peder, Dilib, Fahad, Manral, Surrender, & Dischington, Peter. (2020). FORCE 2020 油井记录和岩性数据集用于机器学习竞赛 [数据集]。Zenodo. doi.org/10.5281/zenodo.4351156
5 个区分资深开发者和初级开发者的 Python 技巧
原文:
towardsdatascience.com/5-python-tricks-that-distinguish-senior-developers-from-juniors-826d57ab3940
通过对 Advent of Code 难题解决方法的差异进行说明
·发表于Towards Data Science ·阅读时间 6 分钟·2023 年 1 月 16 日
–
由Afif Ramdhasuma拍摄的照片,来源于Unsplash
自 2015 年起,每年 12 月 1 日,Advent of Code开始。正如他们网站上所描述的,Advent of Code(以下简称 AoC)是
一个降临节日历,包含各种技能水平的小编程难题,可以用任何编程语言解决。人们将其用作面试 准备、公司培训、大学 课程作业、练习 问题、速度竞赛或相互挑战。
在这篇文章中,我们将探讨五种高级解决常见编码问题的方法,而不是初级方法。每个编码问题都来源于 AoC 难题,许多问题在 AoC 和其他编码挑战及评估中反复出现,比如在求职面试中。
为了阐明这些概念,我不会详细讲解完整的 AoC 难题解决方案,而是只专注于某个具体难题的一小部分,在这个难题中,资深开发者和初级开发者的差异非常明显。
1. 有效地使用推导式和分割读取文件
在 Day1 中,需要读取几个数字块。每个块之间由一个空行分隔(因此实际上是 '\n’
)。
输入和期望输出
# INPUT
10
20
30
50
60
70
# DESIRED OUTPUT
[[10, 20, 30], [50, 60 70]]
初级开发者方法: 使用 if-else 语句的循环
numbers = []
with open("file.txt") as f:
group = []
for line in f:
if line == "\n":
numbers.append(group)
group = []
else:
group.append(int(line.rstrip()))
# append the last group because if line == "\n" will not be True for
# the last group
numbers.append(group)
高级开发者方法: 利用列表推导式和 .split()
with open("file.txt") as f:
# split input into groups based on empty lines
groups = f.read().rstrip().split("\n\n")
# convert all the values in the groups into integers
nums = [list(map(int, (group.split()))) for group in groups]
使用列表推导式,我们可以将之前的 10 行代码压缩成两行,而不会显著损失(如果有的话)可理解性或可读性,并且性能有所提升(列表推导式比常规循环更快)。对于那些未曾见过 map
的人,map
将一个函数(第一个参数)应用于第二个参数中的可迭代对象。在这种特定情况下,它将 int()
应用到列表中的每个值,使每个项目变成整数。有关 map
的更多信息,请点击 这里。
2. 使用 Enum 代替 if-elif-else
在 Day2 中,挑战围绕一个 石头-剪刀-布 游戏展开。不同选择的形状(石头、纸张或剪刀)会得到不同的点数:1 (X),2 (Y) 和 3 (Z)。以下是解决此问题的两种方法。
输入和期望输出
# INPUT
X
Y
Z
# DESIRED OUTPUT
1
2
3
初级开发者方法: if-elif-else
def points_per_shape(shape: str) -> int:
if shape == 'X':
return 1
elif shape == 'Y':
return 2
elif shape == 'Z':
return 3
else:
raise ValueError('Invalid shape')
高级开发者方法: Enum
from enum import Enum
class ShapePoints(Enum):
X = 1
Y = 2
Z = 3
def points_per_shape(shape: str) -> int:
return ShapePoints[shape].value
当然,在这个例子中,简单的方法并不是 那 糟糕,但使用 [Enum](https://docs.python.org/3/library/enum.html)
会导致更简洁和可读性更高的代码。特别是当选项更多时,简单的 if-elif-else 方法会变得越来越差,而使用 Enum
则相对容易保持概览。有关 Enum
的更多信息,请点击 这里。
3. 使用查找表代替字典
在 Day3 中,字母具有不同的值。小写字母 a-z 的值为 1 到 26,大写字母 A-Z 的值为 27 到 52。由于可能的值很多,像上述那样使用 Enum
会导致很多行代码。这里更实用的方法是使用 查找表:
# INPUT
c
Z
a
...
# DESIRED OUPUT
3
52
1
...
初级开发者方法: 创建一个全局字典
letters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
letter_dict = dict()
for value, letter in enumerate(letters, start=1):
letter_dict[letter] = value
def letter_value(ltr: str) -> int:
return letter_dict[ltr]
高级开发者方法: 使用字符串作为查找表
def letter_value(ltr: str) -> int
return 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.index(ltr) + 1
使用字符串的 .index()
方法,我们可以获得索引,因此 letters.index('c')+1
将得到期望的值 3。没有必要将值存储在字典中,因为索引 就是 值。为了避免 +1
,你可以在字符串的开头添加一个空格字符,使得 a
的索引从 1 开始。然而,这取决于你是否希望返回空格的值为 0 还是错误。
正如你现在可能想到的,是的,我们也可以使用查找表来解决 石头、剪刀、布 任务:
def points_per_shape(shape: str) -> int:
return 'XYZ'.index(shape) + 1
4. 高级切片
在 Day5 中,需要从行中读取字母(见下方输入)。每个字母在第四个索引上,从索引 1 开始。现在,几乎所有 Python 程序员都熟悉使用例如 list_[10:20]
的字符串和列表切片。但很多人不知道,你可以使用例如 list_[10:20:2]
来定义步长为 2。在 Day5(以及许多其他编码场景中),这可以节省你大量不必要的复杂代码:
# INPUT
[D]
[N] [C]
[Z] [M] [P]
# DESIRED OUTPUT
[' D ', 'NC', 'ZMP']
初级开发者方法: 使用 range
和索引的双重循环
letters = []
with open('input.txt') as f:
for line in f:
row = ''
for index in range(1, len(line), 4):
row += line[index]
letters.append(row)
高级开发者方法: 使用高级切片方法
with open('input.txt') as f:
letters = [line[1::4] for line in f]
5. 使用类属性存储类实例
在 Day11 中描述了一种猴子互相传递物品的情况。为了简化,我们假设它们只是互相传递香蕉。每只猴子可以被表示为一个 Python class
的实例,其 id
和香蕉数量作为实例属性。然而,有很多猴子,它们需要能够互相交互。存储所有猴子并让它们能够互相交互的一个技巧是定义一个包含所有 Monkey
实例的字典,将其作为 Monkey
类的类属性。使用 Monkey.monkeys[id]
,你可以访问所有现有的猴子,而不需要 Monkies
类或外部字典:
class Monkey:
monkeys: dict = dict()
def __init__(self, id: int):
self.id = id
self.bananas = 3
Monkey.monkeys[id] = self
def pass_banana(self, to_id: int):
Monkey.monkeys[to_id].bananas += 1
self.bananas -= 1
Monkey(1)
Monkey(2)
Monkey.monkeys[1].pass_banana(to_id=2)
print(Monkey.monkeys[1].bananas)
2
print(Monkey.monkeys[2].bananas)
4
6. 自我文档化表达式(额外奖励)
这个技巧几乎在每次编写 Python 程序时都适用。与其在 f-string 中定义你正在打印的内容(例如
print(f"x = {x}")
你可以使用 print(f"{x = }”)
来打印值,并指定你正在打印的内容。
# INPUT
x = 10 * 2
y = 3 * 7
max(x,y)
# DESIRED OUTPUT
x = 20
y = 21
max(x,y) = 21
初级开发者方法:
print(f"x = {x}")
print(f"y = {y}")
print(f"max(x,y) = {max(x,y)}")
高级开发者方法:
print(f"{x = }")
print(f"{y = }")
print(f"{max(x,y) = }")
总结
我们已经探讨了 5 个 Python 技巧,这些技巧区分了高级开发者和初级开发者。当然,单单应用这些技巧并不能让人瞬间晋升为高级开发者。然而,通过分析这两者之间风格和模式的差异,你可以学习高级开发者与初级开发者在处理编码问题时的方法差异,并开始内化这些方法,以便最终自己成为高级开发者!
如果你喜欢这篇文章并希望了解更多关于高级 Python 方法的信息,请务必阅读我关于如何找到内容以提升自己成为更高级开发者的另一篇文章!
## 如何通过向这些专业人士学习提升你的 Python 技能
避免停留在 Python 编程的初级水平
towardsdatascience.com
资源
-
AoC Reddit(提示、解决方案和讨论)
每个数据科学家都应该刻在脑海中的 5 个问题
我是否在解决正确的问题?
·发表于 Towards Data Science ·阅读时间 6 分钟·2023 年 12 月 22 日
–
由 Tingey Injury Law Firm 提供的照片,来源于 Unsplash
尽管有所有的数学和编程,数据科学不仅仅是分析数据和建立模型。归根结底,数据科学的关键目标是解决问题。
然而,问题在于,大多数数据科学项目开始时,我们很少有明确的问题。在这些情况下,数据科学家的角色不是拥有所有答案,而是提出正确的问题。
在本文中,我将详细介绍每个数据科学家都应该刻在脑海中的 5 个问题,使问题发现成为第二天性。
锤子时刻
当我在研究生院开始我的数据科学旅程时,我对这一学科有一个天真的看法。也就是说,我过于专注于学习工具和技术(例如 LSTM、SHAP、VAE、SOM、SQL 等)
虽然技术基础对于成为成功的数据科学家是必要的,但过于关注工具会产生“锤子问题”(即当你有一把非常好的锤子时,一切看起来都像钉子)。
这通常会导致一些智力刺激但实际无用的项目。
问题 > 技术
我的观点直到毕业并加入一家大型企业的数据科学团队时才完全成熟,在那里我能够向那些比我早几年(如果不是几十年)的人学习。
关键的教训是专注于问题而非技术的重要性。这意味着在编写任何一行代码之前,深入理解业务问题。
作为数据科学家,我们通常不会解决自己的问题,我们通过与客户和利益相关者的对话来获得这些理解。正确把握这一点很重要,因为如果你做不好,你可能会花费大量时间(和金钱)解决错误的问题。这就是“问题发现”问题派上用场的地方。
5 个问题发现问题
6 个月前,我离开了我的企业数据科学工作,成为了一名独立的数据科学顾问(为了资助我的创业项目)。从那时起,我对破解这些早期阶段的“发现”对话产生了强烈的兴趣。
我提高这一技能的方法有两个方面。首先,采访经验丰富的数据自由职业者,了解他们的最佳实践(我采访了 10 位)。其次,尽可能多地进行发现性通话(我进行了大约 25 次)。
这里列出的问题是我之前提到的所有经验的总结。虽然这绝不是一个完整的列表,但这些是我发现自己反复询问的问题。
1)你想解决什么问题?
虽然(理论上)这应该是这份清单上的唯一问题,但(不幸的是)实际情况并非如此。很多时候,客户并不明确自己需要解决的问题(如果他们明确,可能就不会找咨询师了)。即使他们明确了,我通常也需要跟上步伐,以更好地理解业务背景。
这个问题在两种情况下都很有帮助,因为(理想情况下)客户的回答会引发后续问题,从而让我更深入地了解他们的世界。例如,他们可能会说:“我们尝试过使用 OpenAI 创建一个自定义聊天机器人,但效果不好。”
对此我可能会问:“这个聊天机器人是用来做什么的?” 或者 “是什么让你觉得结果不好?”。
2)为什么……?
“什么”的自然后续问题是“为什么”。这是你可以问客户的最强有力的问题之一。然而,它也可能是最难提出的问题之一。
“为什么”问题往往会让人感到防备,因此拥有多种表述这种问题的方式会很有帮助。这里有一些例子:
-
这对你的业务(你的团队)为什么重要?
-
你为什么现在想要解决这个问题?
-
解决这个问题对你的业务意味着什么?
-
这如何与业务的更大目标相适应?
-
你为什么想用人工智能来解决这个问题?
这个问题(或其任何变体)是一种非常有效的方式来获得客户的背景信息,这应该(再次)激发后续问题。
为了继续之前的例子,客户可能会说,“我们有几个支持票据,想要自动分类为 3 个优先级别,我们认为 AI 聊天机器人是解决这个问题的好方法。” 这为之前的“我们尝试制作了一个定制聊天机器人”的回应提供了更多背景。
“我们在做什么?”和“我们为什么要这样做?”是商业中最基本的两个问题。因此,擅长提出“什么”和“为什么”可以让你走得更远。
3) 你的梦想结果是什么?
我喜欢这个问题,因为它(有效地)结合了“什么”和“为什么”的问题。这使客户可以以直接问到时可能无法传达的方式谈论他们对项目的愿景。
学习新事物时,我通常需要多次尝试才能真正理解。同样,我发现为了真正找到客户问题的根源,我需要在对话中以不同的方式问“什么”和“为什么”几次。
这让人联想到**丰田的“5 个为什么”**方法,用于找出问题的根本原因。尽管这是在制造业背景下开发的,但同样适用于数据科学中的问题解决。
这里两个相关的问题是:成功的标准是什么? 和 我们如何衡量它? 这些比“梦想结果”要更具实用性,但有助于从询问“什么和为什么”过渡到“怎么做?”
4) 到目前为止你尝试了什么?
这个问题开启了解决方案的路径。它通过揭示项目的更多技术细节来实现这一点。
例如,这通常让我对谁将编写代码有个很好的了解。如果他们已经构建了一些基本的 POC,那么客户(及其团队)可能会承担大部分工作。如果他们从头开始,那可能是我或我网络中的分包商。
在这个客户还没有做任何事情的第二种情况下,可以提出一些其他问题。
-
现有的解决方案是什么?
-
你现在是如何解决这个问题的?
-
其他人是如何解决类似问题的?
5) 为什么是我?
我从大师级谈判专家Chris Voss那里获得了这个问题,他将其框架化为揭示人们动机的有效方法。
通常,这会引发有关是什么导致他们找到你的额外背景信息,以及他们****如何看待你融入项目,这对定义下一步非常有帮助。
然而,有时,人们对这个问题没有很好的答案,这可能表明他们实际上并不想与你合作,并且隐藏了他们的真实意图(例如,他们想要免费的咨询或竞争性报价)。
一个关键的教训
过去几个月对我来说一个重要的教训是学习这些问题(即深深地记在脑海中)但随后忘记它们。
目标是让这些问题在你与人交谈的过程中自然地在脑海中形成。虽然这需要经历不少尴尬时刻,但只有通过实践才能够发展起来的(别担心,我也在不断练习)。
让我受益的是以学习为目标进入这些对话。这意味着保持好奇心、提问和倾听(比说话)更多。
结论
虽然数据科学需要技术技能,但如果没有对问题的清晰理解,这些技能无法提供太多价值。这就是为什么发展识别和理解客户业务问题所需的沟通技能至关重要。
在这里,我分享了发现问题的 5 个基本问题。虽然这不是一个完整的列表,但我希望对承担更多客户面对面角色的数据从业者有所帮助。
如果你有任何补充,请留下评论 😁。
这可能会让你感到不舒服,但对你有好处
[towardsdatascience.com
资源
社交媒体: YouTube 🎥 | LinkedIn | Twitter
支持: 请我喝杯咖啡 ☕️
shawhin.medium.com/subscribe?source=post_page-----3948e215750f--------------------------------
[## 免费获取我写的每一篇新故事
免费获取我写的每一篇新故事 P.S. 我不会与任何人分享你的电子邮件。通过注册,你将创建一个…
提升您的 MLflow 模型实验的 5 个快速技巧
使用 MLflow Python API 来驱动更好的模型开发
·
关注 发表在 Towards Data Science ·7 分钟阅读·2023 年 3 月 13 日
–
MLflow 是加速机器学习模型开发过程的绝佳工具,其强大的实验组件使数据科学家能够记录最佳算法和参数组合,并迅速迭代模型开发。
本博客旨在展示如何充分利用 MLflow 实验。我们将重点介绍start_run()
及其参数,这些参数可以增强您的实验过程。此外,我们还将介绍search_runs()
函数,它提供了您实验历史的广泛视图,并在分析中提供更大的灵活性。
如果你是 MLflow 的新手,我建议在进入此博客之前,查看一下 MLflow 网站、文档、一些博客文章或教程视频。
照片由Adrien Converse提供,来源于Unsplash
mlflow.start_run()
这些技巧大多数是start_run()
函数的参数。我们调用此函数来启动实验运行,它成为活动运行,我们可以在其中记录参数、指标和其他信息。
这是我在 MLflow 中使用最频繁的函数,它为用户提供了最即时的价值。
1. run_id
run_id
是一个特定于每个实验运行的 UUID。一旦运行启动,就无法覆盖诸如模型类型或参数值等属性。然而,你可以使用run_id
来回溯记录额外的值,如指标、标签或描述。
# Start MLflow run for this experiment
# End any existing runs
mlflow.end_run()
with mlflow.start_run() as run:
# Turn autolog on to save model artifacts, requirements, etc.
mlflow.autolog(log_models=True)
print(run.info.run_id)
diabetes_X = diabetes.data
diabetes_y = diabetes.target
# Split data into test training sets, 3:1 ratio
(
diabetes_X_train,
diabetes_X_test,
diabetes_y_train,
diabetes_y_test,
) = train_test_split(diabetes_X, diabetes_y, test_size=0.25, random_state=42)
alpha = 0.9
solver = "cholesky"
regr = linear_model.Ridge(alpha=alpha, solver=solver)
regr.fit(diabetes_X_train, diabetes_y_train)
diabetes_y_pred = regr.predict(diabetes_X_test)
# Log desired metrics
mlflow.log_metric("mse", mean_squared_error(diabetes_y_test, diabetes_y_pred))
mlflow.log_metric(
"rmse", sqrt(mean_squared_error(diabetes_y_test, diabetes_y_pred))
)
在这种情况下,我们可能还希望记录此运行的决定系数(r²)值:
with mlflow.start_run(run_id="3fcf403e1566422493cd6e625693829d") as run:
mlflow.log_metric("r2", r2_score(diabetes_y_test, diabetes_y_pred))
run_id
可以通过从之前的运行中print(run.info.run_id)
提取,或通过查询mlflow.search_runs()
来获得,稍后会详细介绍。
2. experiment_id
在 MLflow 中,你可以通过几种不同的方式设置要记录的实验。第一个命令将所有后续运行的实验设置为“mlflow_sdk_test”。
mlflow.set_experiment("/mlflow_sdk_test")
这也可以通过experiment_id
参数在逐次运行中进行配置。
my_experiment = mlflow.set_experiment("/mlflow_sdk_test")
experiment_id = my_experiment.experiment_id
然后,这个值可以在传递给start_run()
时重复使用:
# End any existing runs
mlflow.end_run()
with mlflow.start_run(experiment_id=experiment_id):
# Turn autolog on to save model artifacts, requirements, etc.
mlflow.autolog(log_models=True)
print(run.info.run_id)
diabetes_X = diabetes.data
diabetes_y = diabetes.target
# Split data into test training sets, 3:1 ratio
(
diabetes_X_train,
diabetes_X_test,
diabetes_y_train,
diabetes_y_test,
) = train_test_split(diabetes_X, diabetes_y, test_size=0.25, random_state=42)
alpha = 0.8
solver = "cholesky"
regr = linear_model.Ridge(alpha=alpha, solver=solver)
regr.fit(diabetes_X_train, diabetes_y_train)
diabetes_y_pred = regr.predict(diabetes_X_test)
# Log desired metrics
mlflow.log_metric("mse", mean_squared_error(diabetes_y_test, diabetes_y_pred))
mlflow.log_metric(
"rmse", sqrt(mean_squared_error(diabetes_y_test, diabetes_y_pred))
)
mlflow.log_metric("r2", r2_score(diabetes_y_test, diabetes_y_pred))
3. run_name
当你指定运行名称时,你可以比依赖 MLflow 生成的默认名称更好地控制命名过程。这使你能够建立一致的实验运行命名约定,类似于你管理环境中其他资源的方式。
# Start MLflow run for this experiment
# End any existing runs
mlflow.end_run()
# Explicitly name runs
today = dt.today()
run_name = "Ridge Regression " + str(today)
with mlflow.start_run(run_name=run_name) as run:
# Turn autolog on to save model artifacts, requirements, etc.
mlflow.autolog(log_models=True)
print(run.info.run_id)
diabetes_X = diabetes.data
diabetes_y = diabetes.target
# Split data into test training sets, 3:1 ratio
(
diabetes_X_train,
diabetes_X_test,
diabetes_y_train,
diabetes_y_test,
) = train_test_split(diabetes_X, diabetes_y, test_size=0.25, random_state=42)
alpha = 0.5
solver = "cholesky"
regr = linear_model.Ridge(alpha=alpha, solver=solver)
regr.fit(diabetes_X_train, diabetes_y_train)
diabetes_y_pred = regr.predict(diabetes_X_test)
# Log desired metrics
mlflow.log_metric("mse", mean_squared_error(diabetes_y_test, diabetes_y_pred))
mlflow.log_metric(
"rmse", sqrt(mean_squared_error(diabetes_y_test, diabetes_y_pred))
)
mlflow.log_metric("r2", r2_score(diabetes_y_test, diabetes_y_pred))
但是,请注意run_name
在 MLflow 中并不是唯一约束。这意味着你可能会有多个实验(具有唯一的运行 ID)共享相同的名称。
MLflow 实验表视图——重复的运行名称:作者提供的图片
这意味着每次在with语句中执行新运行时,它将创建一个同名的新实验,而不是将细节附加到此运行中。
这使我们顺利过渡到下一个参数。
4. 嵌套
如果你使用过 scikit-learn 的GridSearchCV
函数来执行超参数优化,你可能对嵌套实验运行有所了解。
在 MLflow 中,嵌套实验看起来像下面这样:
MLflow 实验表视图——嵌套实验:作者提供的图片
注意,这里的指标是针对父运行保存的,它返回子运行记录的最佳值。子运行值本身是空白的。
虽然嵌套实验非常适合评估和记录参数组合以确定最佳模型,但它们也是组织工作的大型逻辑容器。通过分组实验,您可以将各个数据科学调查进行分隔,并保持实验页面的有序和整洁。
# End any existing runs
mlflow.end_run()
# Explicitly name runs
run_name = "Ridge Regression Nested"
with mlflow.start_run(run_name=run_name) as parent_run:
print(parent_run.info.run_id)
with mlflow.start_run(run_name="Child Run: alpha 0.1", nested=True):
# Turn autolog on to save model artifacts, requirements, etc.
mlflow.autolog(log_models=True)
diabetes_X = diabetes.data
diabetes_y = diabetes.target
# Split data into test training sets, 3:1 ratio
(
diabetes_X_train,
diabetes_X_test,
diabetes_y_train,
diabetes_y_test,
) = train_test_split(diabetes_X, diabetes_y, test_size=0.25, random_state=42)
alpha = 0.1
solver = "cholesky"
regr = linear_model.Ridge(alpha=alpha, solver=solver)
regr.fit(diabetes_X_train, diabetes_y_train)
diabetes_y_pred = regr.predict(diabetes_X_test)
# Log desired metrics
mlflow.log_metric("mse", mean_squared_error(diabetes_y_test, diabetes_y_pred))
mlflow.log_metric(
"rmse", sqrt(mean_squared_error(diabetes_y_test, diabetes_y_pred))
)
mlflow.log_metric("r2", r2_score(diabetes_y_test, diabetes_y_pred))
如果需要在此嵌套运行中进行添加,则在后续执行时指定父运行的run_id
作为参数,附加更多子运行。
# End any existing runs
mlflow.end_run()
with mlflow.start_run(run_id="61d34b13649c45699e7f05290935747c") as parent_run:
print(parent_run.info.run_id)
with mlflow.start_run(run_name="Child Run: alpha 0.2", nested=True):
# Turn autolog on to save model artifacts, requirements, etc.
mlflow.autolog(log_models=True)
diabetes_X = diabetes.data
diabetes_y = diabetes.target
# Split data into test training sets, 3:1 ratio
(
diabetes_X_train,
diabetes_X_test,
diabetes_y_train,
diabetes_y_test,
) = train_test_split(diabetes_X, diabetes_y, test_size=0.25, random_state=42)
alpha = 0.2
solver = "cholesky"
regr = linear_model.Ridge(alpha=alpha, solver=solver)
regr.fit(diabetes_X_train, diabetes_y_train)
diabetes_y_pred = regr.predict(diabetes_X_test)
# Log desired metrics
mlflow.log_metric("mse", mean_squared_error(diabetes_y_test, diabetes_y_pred))
mlflow.log_metric(
"rmse", sqrt(mean_squared_error(diabetes_y_test, diabetes_y_pred))
)
mlflow.log_metric("r2", r2_score(diabetes_y_test, diabetes_y_pred))
关于这种方法要注意的一点是,您的指标现在将记录在每个子运行中。
5. mlflow.search_runs()
这个技巧是使用 search_runs()
函数。
这个函数允许我们以编程方式查询实验 GUI,结果以易于理解和操作的表格格式返回。
在下面的示例中,我们可以从实验中的运行中选择特定字段,并将其加载到 Pandas DataFrame 中。请注意,可用的列远远超过实验 GUI 中提供的列!
# Create DataFrame of all runs in *current* experiment
df = mlflow.search_runs(order_by=["start_time DESC"])
# Print a list of the columns available
# print(list(df.columns))
# Create DataFrame with subset of columns
runs_df = df[
[
"run_id",
"experiment_id",
"status",
"start_time",
"metrics.mse",
"tags.mlflow.source.type",
"tags.mlflow.user",
"tags.estimator_name",
"tags.mlflow.rootRunId",
]
].copy()
runs_df.head()
由于这是一个 Pandas DataFrame,我们可以添加一些可能对分析有用的列:
# Feature engineering to create some additional columns
runs_df["start_date"] = runs_df["start_time"].dt.date
runs_df["is_nested_parent"] = runs_df[["run_id","tags.mlflow.rootRunId"]].apply(lambda x: 1 if x["run_id"] == x["tags.mlflow.rootRunId"] else 0, axis=1)
runs_df["is_nested_child"] = runs_df[["run_id","tags.mlflow.rootRunId"]].apply(lambda x: 1 if x["tags.mlflow.rootRunId"] is not None and x["run_id"] != x["tags.mlflow.rootRunId"]else 0, axis=1)
runs_df
如果我们想要汇总结果集以提供随时间变化的运行信息,我们可以使用:
pd.DataFrame(runs_df.groupby("start_date")["run_id"].count()).reset_index()
上述查询的输出:作者提供的图像
自动生成的 tags.estimator_name 字段允许我们查看每个算法的测试运行数量。
pd.DataFrame(runs_df.groupby("tags.estimator_name")["run_id"].count()).reset_index()
上述查询的输出:作者提供的图像
由于这是一个 DataFrame,我们可以将数据导出以满足任何报告要求,从而为可能没有访问工作区的用户提供所需的可见性,并在工作区之间进行比较。
结束语
这些只是如何扩展 MLflow 函数和参数在实验过程中的使用的一些示例,但在 Python API 中还有许多其他可用的函数和参数。
希望这篇文章能激发您探索一些可用的函数和参数,并查看它们是否能为您的模型开发过程带来好处。有关更多信息,请参考 API 文档,并尝试不同的配置以找到最适合您需求的配置。
如果您当前使用了我在本文中没有提到的任何函数或参数,请在评论中告诉我!
所有代码可以在我的 GitHub Repo 中找到。
作为数据专业人士展示你工作的 5 条建议
原文:
towardsdatascience.com/5-recommendations-to-show-your-work-as-data-professionals-a1c8f71c9de0
对于数据专业人士而言,工作展示的建议
·发表在Towards Data Science ·阅读时间 7 分钟·2023 年 1 月 9 日
–
照片由JIUNN-YIH LAU提供,来源于Unsplash
作为数据专业人士,你熟悉如何向领导、同事或认识的人展示你的工作。许多优秀的书籍(如《数据讲述》 作者科尔·努斯鲍默·克纳夫利克 和《数据故事:通过故事解释数据并激发行动》 作者南希·杜阿尔特) 涵盖了用数据讲故事的主题。完成工作只是旅程的一部分。你的工作也应该是你展示给世界的渠道。
你是否考虑过将你的工作展示给你不认识的人?比如在博客文章、YouTube、网络研讨会或会议上?
讲故事不应仅限于你熟悉的人。你可以揭示幕后故事,并更广泛地分享它们。网络上的人们会喜欢了解你如何通过用例分析、数据收集、模型构建、A/B 测试和业务影响来创造/开发一个优秀的产品/结果的故事。
互联网的信息量庞大,对于任何新数据专业人士来说,获得更广泛的受众变得更加困难。我将讨论为什么你应该首先展示你的工作,然后提供五个自我推广的技巧。
GitHub 不足以进行自我推广
许多数据专业人士认为 GitHub 账号足以让世界发现他们。今天“出色到无法忽视”并不高效于自我推广。
GitHub 个人资料页面显示了你的每日贡献、你参与的仓库以及你打开的 PR。你可能还会有一些关注者和项目上的星标。这表明你在积极地从事某些工作,但你的工作并不能自我说明。它需要一个故事,一些叙事能吸引人们。
仅仅分享你的 GitHub 账号在你和世界之间设置了障碍。对你工作感兴趣的人必须在混乱的 GitHub 上自己探索,才能知道那些提交做了什么。这使得想要了解更多的读者处于一个复杂的跟随位置。这过滤掉了你的潜在追随者。
大胆地展示你的工作,并提供更多指导。直接阅读 1000 行代码而没有内容可能对一些大师有效,但我们只能假设某些人具备与你相同的熟练程度。更好的方法是展示你的过程,将你的工作编织成一个故事,并提供完整的图景。
建议 1:思考过程,分享过程,并记录过程
我们应该关注过程而不是最终结果,并使用文档来跟踪过程。
詹姆斯·克利尔(James Clear)的类似观点:
目标对设定方向有帮助,但系统最适合于取得进展。当你花太多时间思考目标而不是设计系统时,会出现一些问题。——詹姆斯·克利尔 原子习惯
尽管最终结果很重要,但它仅仅是一个方向。过程在你达到目标时占据了更多的重量。
我曾经急于查看最终结果而快速推进过程。这留下了一些粗心的漏洞,或者最终结果需要纠正。当我回顾时,如果有人问我为了达到最终决定我会采取哪些步骤。我在一些问题上迷失了方向:
-
“我们是如何得出这个事实表的粒度的?”
-
“你为什么不能应用逻辑 A,而是使用逻辑 B?”
-
“这个组件一年前的设计决策是什么?”
当我忽视文档时,这些问题的答案变得模糊,因此我的一些想法消退,变得难以追踪。
每天拥有文档能激发更多的想法。当你的工作过程被记录下来时,你的大脑会重建和连接散落的点。下一步和之前的任何错误变得清晰。
人们也乐于从你的过程中学习。如果你为开源项目做出了贡献,谈谈这个项目以及你贡献的影响,甚至提供逐步的使用说明;截取屏幕截图,制作演示视频。如果你创建了一个 APP,分享你完成的过程,并为尚未完成的项目制作演示。你分享过程并为社区提供价值,人们会关注并订阅你的邮件列表。这是完美的自然流量!
隐藏过程,只展示最终产品可能会让世界感到惊讶。然而,你的最终产品需要被发现作为一个过程,分享中间结果给了你被发现的渠道和时间,所以要注意公开展示你工作进展的力量。
推荐 2**:不要害怕展示你的作品**
许多人害怕展示自己的作品。我是那些被恐惧思维困扰的人之一:我在这个领域不是专家,并且对展示自己所做的事情感到过于害羞。别人会怎么想我,他们会根据我分享的内容来评判我吗?恐惧在你和读者之间设置了障碍。
你必须能够展示自己的一部分。如果人们评判你,如果他们觉得他们了解你那是你必须接受的现实——尼尔·盖曼的写作建议
你需要尝试突破害羞的心理障碍。从你最熟悉的事情开始——你的日常工作。分享一些你希望自己早知道的信息,并为开始使用数据处理库的人编写一个教程。
你需要有更强的心态来展示你的作品。不要害怕批评。这样想:很多人没有时间留下反馈。那些留下评论的人表示他们关心你的作品。无论是赞美还是批评,这都是你工作受到关注的好迹象,你应该继续下去。
推荐 3**:创建一个域名来展示你的作品**
你的作品需要在互联网上有一个展示的地方。社交媒体的流行度经常变化。影响者必须遵循规则以适应算法。拥有一个域名可以让你有更多的自由来制定自己的规则并为读者挑选最佳内容。如果你的社交媒体流量减少了,你的影响力和工作也会消失。现在很少有人使用 Myspace,但没有人能保证 Facebook 或 Twitter 会持续多久。拥有和维护一个域名既安全又便宜,而不是长期依赖任何社交平台。
创建一个域名和建立一个网站很简单。主要是通过像 WordPress 和 Elementor 这样的工具变得更加容易,它们允许你通过拖放来创建网站而无需编写一行代码。
当你在你的网站上展示你的工作时,你应该通过社交媒体分享它。另一种分享的选项是像 Medium 这样的博客平台,其中包括像 Toward Data Science (TDS) 这样的出版物,拥有超过 60 万关注者。你可以利用 Medium 的自定义规范链接功能同时发布你的工作。
推荐 4**:专注于高质量内容,而不是货币化**
你在互联网上展示的工作可以实现货币化。实现货币化需要时间和精力。当你开始时,不要考虑货币化,也不要关注关注者的数量;花更少的时间在多个社交媒体上用出色的营销图片来为你的帖子建立品牌。出版物有一些规则要遵循,通常我花不到 3 分钟就能找到一张免费的库存图片,这应该足够了。
重点是 发布高质量的内容来展示你的工作。阅读更多经典数据书籍和学习 TDS 上发布的内容将为你的思维提供源泉。高质量的内容对于长期运行至关重要,因为 SEO 在发现你的工作中起着关键作用。为出版物撰写高质量的内容可以使你的文章在 SEO 优化上走捷径。
推荐 5**:教别人,从教学中学习**
教学是另一种学习方式。教学和学习是双向的。当你分享你的知识时,你会发现额外的视角、反馈和想法,这些都进一步帮助你学习更多。
当我三年前写下文章 Airflow 调度间隔 101 时,我想教人们 Airflow 调度器是如何工作的。在写作过程中,我对调度器的内部核心没有很好的理解。然后我阅读了 Airflow 调度器的源代码,这帮助我获得了比 Airflow 官方文档更好的洞察。这篇文章成为了我表现最佳的文章之一,因为我提供了更多背景,获得了超过 10 万次阅读和 1 千次点赞。
Airflow 调度间隔 101
Airflow 调度间隔可能是一个具有挑战性的概念,甚至对于那些在 Airflow 上工作的开发者来说也是如此……
towardsdatascience.com
有时我会查看一些 StackOverflow 上的问题,看看是否需要知道答案。当时,我在研究并得到我的答案被接受后提供了答案。这是学习新事物并回馈的机会。
最后的想法
作为数据专业人士,你的工作不应该是秘密。我有两个例子可以分享,展示了你的工作如何为我带来更多机会。
-
我曾经发布过Frontpage Slickdeals 数据分析与 Pandas 和 Plotly Express,并获得了 Slickdeals.com 的面试机会。
-
我在EMR 上的 Alluxio:Spark 任务的快速存储访问与共享上发布了另一篇文章。我有机会在纽约的一个本地 Meetup 活动上与创始人见面并讨论我的工作。
假如你想被发现并为你的工作和自己建立品牌。在互联网上展示你的工作是你作为数据专业人士应该充分利用的绝佳方式。
我希望这个故事对你有所帮助。这篇文章是我工程与数据科学故事的系列之一,目前包含以下内容:
数据工程与数据科学故事
查看列表53 篇故事!
你还可以订阅我的新文章或成为推荐的 Medium 会员,获得对 Medium 上所有故事的无限访问权限。
如有疑问/评论,请随时在此故事的评论区留言或通过Linkedin或Twitter直接联系我。
5 个迹象显示你的数据建模不佳
云时代的常见挑战
·
关注 发表在 Towards Data Science · 7 分钟阅读 · 2023 年 6 月 20 日
–
照片 由 Jan Antonin Kolar 在 Unsplash
随着过去十年云技术的扩展和廉价存储成本的降低,许多组织积累的数据量比以前想象的要大得多。许多云数据仓库提供商提供的按需付费模式(AWS,GCP,Azure)减少了前期资本资源的需求和数字基础设施的考虑。
好消息是,这最终使得数据科学工作对大多数组织来说更加可及。
坏消息是数据湖正变得越来越像数据沼泽。
什么是数据建模?它面临哪些挑战?
对数据工程师来说,向高层管理人员传达一个良好建模生态系统的价值往往是困难的。这是因为利益相关者能看到的只是展示出的 BI 工具和预测模型。然而,建模不良的数据从数据治理的角度来看会给分析团队带来重大挫折。这不可避免地会拖慢工作流程,引入重复任务,降低报告准确性,以及产生许多其他负面副作用。
定义“良好建模”数据是一个独立的话题。但你可以通过以下概念在你的数据仓库中考虑它:
-
存在一个清晰的模式来查找与业务实体相关的数据表。
-
使用了一个有意的/已知的建模技术,例如维度模型、实体关系模型、数据保险库等。
-
表格和字段命名约定是一致的,文档化良好,并且具有业务价值。
还应注意到,数据建模采用的是一种整体性和多系统的方法。它从你的 OLTP(在线事务处理)系统开始,这是数据最初被记录的地方。以下是一些例子:
-
CRM 系统如Salesforce
-
销售点系统如Stripe
-
电子商务平台如亚马逊
理想情况下,你的数据在通过源系统收集时应该被规范化到第三范式。然后,它应该被摄取到分析环境中,也就是一个 OLAP(在线分析处理)系统,在这里应用了分析建模技术。在本文的背景下,OLAP 系统与云数据仓库同义。但 OLAP 系统也可以包括像 SQL Server、MySQL 或 PostgreSQL 这样的独立托管工具。
尽管数据分析师和数据科学家只与 OLAP 系统互动,但组织的数据建模策略需要考虑 OLTP 和 OLAP,才能保持可持续性。
以下是说明您的分析环境建模不佳的 5 个关键迹象。
1.) 需要部落知识来理解数据的位置
为了使新分析师在入职时取得成功,他们需要一个明确的路线图,了解数据仓库中有哪些数据,数据来源于哪里,以及其业务背景是什么。然而,数据建模不佳的团队往往难以使新员工顺利上手,因为不理解新员工回答基本业务问题为何需要如此长时间。如果没有适当的指导,分析团队可能会经历高离职率,因为新成员没有获得成功所需的工具。
数据分析师和数据科学家应该专注于解决业务问题,而不是浪费时间寻找业务实体的位置。团队越快熟悉可用数据,仪表板和预测模型的完成速度就越快。这最终会提升团队的生产力。
如果只有少数分析师知道如何回答基本的业务问题,那就是一个问题。采用这种孤立的方式无法扩展,并且只会限制团队能够解决的问题数量。
照片 by Desola Lanre-Ologun on Unsplash
2.) 不同分析师为相同指标产生不同结果
如果没有单一的真实来源,不同团队成员很容易以不同的方式计算相同的指标。例如,收入是如何定义的?计算这一指标使用了哪些表?需要有一个明确的路径来定义业务逻辑,这一切都始于一个有意图的数据模型。
我曾在这样的环境中工作,那里有 3 个不同的表表示相同的业务实体,它们使用了不同的 SQL 逻辑来得出相似但不完全相同的记录输出。再加上管理不善的报告请求队列,就会导致两个不同的分析师用不同的结果回答相同的问题。这不仅导致利益相关者对数据失去信任,还需要跨团队进行繁琐且不必要的调解工作。
3.) 团队需要重用冗余的业务逻辑代码块
我见过团队使用 Google 表格记录 SQL CASE 语句,以概述具体的业务指标。这些语句往往很长且难以阅读。虽然这种做法试图在团队之间提供一致性,但问题在于它违反了组织内的 DRY(不要重复自己)原则。
对于许多面临此类问题的团队,使用像 DBT 这样的转换工具可以让分析工程师在一个地方定义业务逻辑,然后让分析师在多个地方引用它。
想象以下例子——如果你是一个电子商务公司,且有一种复杂的方式来计算页面浏览量(这是可以的),那么为什么要在你的 BI 工具中分发和重复这个业务逻辑呢?不仅在逻辑没有被逐字复制和粘贴的情况下风险较大,而且还浪费计算资源,而这通常是大多数云服务提供商的最大开支。
为了解决这个问题,考虑绘制出需要进行的常见聚合和业务逻辑,运行一个每天(或根据需要的频率)进行的转换作业,并将其写入一个表中。然后让你的 BI 层基于这个表运行。
4.) 你的数据仓库表现不佳
如上所述,建模不良的数据会引入冗余。但它也会产生不必要的复杂性。过多的计算资源是这的副产品,并且所有云数据仓库都有一定的定价阈值限制。一旦达到这个限制,执行新的查询可能会变得极其缓慢,甚至在某些情况下不可行。
任何数据工程师都会告诉你,单纯购买额外资源并不是解决这个问题的可持续方案。
长且复杂的查询不仅执行时间长,还会消耗环境中的可用资源。考虑一个需要执行涉及 20 个连接的查询的例子。在这种情况下,理想的解决方案非常少,因为它表明用于解决业务问题的数据没有以易于访问的格式存储。这么多的连接可能会计算开销很大,尤其是当相关表的数据量很大或 ON 子句涉及多个列时。如果你正在实施一个维度模型,你的团队可能需要考虑在这些场景下在数据库中创建一个新的事实表。
资源的衡量方式因所使用的云服务提供商而异,但它们都遵循使用固定数量虚拟 CPU 的相同概念。例如,BigQuery 使用槽位的概念,槽位实际上是执行查询时可用的计算资源数量。采用按需定价的组织在任何给定时刻可使用 2000 个槽位。因此,如果一个查询非常复杂并且占用的槽位超过了可用数量,其他查询将会排队,直到它们能够被执行。
5.) 你经常需要在 SQL 中硬编码值
硬编码的值通常是数据仓库中缺失所需数据的明显迹象。在维度模型的上下文中,这通常意味着需要创建一个新的维度表来提供额外的列。
扎克·奎因 写了一篇 文章,很好地阐述了这个概念,演示了如何通过查找表消除冗长的 CASE 语句。将这个示例放在维度模型的背景下——假设你的组织需要进行大量的地理空间分析。你有一个customer_dimension表,它提供了州的缩写,但你想将其显示为完整的州名。你可以写这样的代码:
SELECT
customer_id
, customer_name
, address
, city
, state AS state_abrevaition
, CASE
WHEN state = 'NJ' THEN 'New Jersey'
WHEN state = 'NY' THEN 'New York'
WHEN state = 'PA' THEN 'Pennsylvania'
..............
END AS state_full_name
, zip_code
FROM customer_dimension
但这种 CASE 语句并不可持续。如果我们想更详细地改进这个解决方案,我们需要将zip_code_dimension表连接到customer_dimension表。你会看到下面的zip_code_dimension表将为我们的分析提供更大的粒度。这个表可能看起来像这样:
作者拍摄
所以有了那个新表,想象一下能够运行这个查询:
SELECT
c.customer_id
, c.customer_name
, c.address
, c.state AS state_abreviation
, z.state_name
, c.zip_code
, z.county_name
, z.country_name
, z.timezone
, z.lat
, z.lng
FROM customer_dimension c
LEFT JOIN zip_code_dimension z
ON c.zip_code = z.zip_code
这不仅是一种更优雅和可读的查询方式来生成完整的州名,而且让我们能够回答更多的问题。通过zip_code_dimension,我们现在可以将该邮政编码的纬度和经度附加到地图可视化中,以更清晰的格式呈现。此外,还有一些其他维度字段,如果我们想在输出中包含它们(如国家、时区等),则需要数百行硬编码。
结论
如果你发现上述任何要点与你的环境相关,那么现在可能是时候全面审视你的数据管道,并了解分析团队在哪些方面需要填补空白。为了能够正确建模你的团队数据,你需要能够概念化相关的业务实体,并将其组织成有利于组织内常见问题的方式。没有一种通用的方法,但它需要对所有团队成员明确,并以一致的方式创建。
你已经成为高级 Pandas 用户的 5 个迹象
时候该获得荣誉了
·发表于 Towards Data Science ·阅读时间 10 分钟·2023 年 5 月 12 日
–
图片由 Barbara A Lane 提供,来自 Pixabay
介绍
你是否常常幻想 Pandas DataFrames 和 Series?你是否花费数小时进行复杂的操作和聚合,几乎没有注意到背部疼痛,一边想着“这真是太有趣了”?
你可能已经成为了一个高级 Pandas 用户而未曾意识到。加入这个少数派 Pandas 爱好者的俱乐部,接受你已经正式成为数据魔法师的事实。
那么,让我们来看看你是否属于这个俱乐部的五个迹象。
0. 了解何时抛弃 Pandas
当你刚开始学习数据分析时,Pandas 可能让你觉得它可以做所有事情。许多在线课程将 Pandas 推销为满足所有数据相关需求的一站式服务。
然而,随着经验的积累,你逐渐意识到 Pandas 有许多不足之处。你知道如何退一步问自己,“Pandas 在这里是最佳选择吗?”
有一些场景下,答案是一个大大的 NO。这些包括实时数据处理、大规模数据集处理、高性能计算和生产级数据管道。
1/ 对于实时数据处理,想象一下一个大炮,它以每小时 100 发的速度从某个过程发射实时数据片段。这些数据片段来得又快又猛,你必须在空中捕捉、处理并保存每一个。
温和地说,Pandas 会被这种级别的数据处理窒息。相反,你应该使用像 Apache Kafka 这样的库。
2/ 当涉及到大规模数据集时,Pandas 的创始人 Wes McKinney 有一个经验法则:
RAM 必须比数据集大小大 5-10 倍才能使 Pandas 达到最佳性能。
“足够简单”,如果是 2013 年你会这么说,但今天的数据集往往轻松打破这一规则。
3/ 高性能计算就像指挥一场交响乐。就像指挥需要协调许多不同音乐家的动作以创造和谐的表演一样,高性能计算任务需要协调和同步多个处理元素和线程以获得最佳结果。
至于 Pandas,它是单独运行的。
4/ 对于生产级的数据管道,可以把它们看作是供水系统。正如供水系统需要可靠、可扩展和可维护以确保稳定的清洁水源一样,数据管道也需要类似的特质。虽然 Pandas 可能负责清理和转换,但其他库应处理其余部分。
离开 Pandas 的毛茸茸的怀抱可能很困难,但如果它不够用,不要感到内疚去探索其他选项。
就个人而言,我最近对 Polars 产生了兴趣,这是一种用 Rust 编写的库,旨在从零开始解决 Pandas 的所有限制。
## 7 个简单步骤,从 Pandas 切换到闪电般快速的 Polars 并且永远不再回头
编辑描述
[towardsdatascience.com
你还可以与 datatable
这样的库进行混搭。这里是我经常用来在几分之一秒内加载大型 CSV 文件并在 Pandas 中执行分析的代码片段:
import datatable as dt
df = dt.fread("my_large_file.csv").to_pandas()
1. 速度需求
Pandas 是一个庞大的库,有许多不同的方法来执行相同的任务。然而,如果你是一个经验丰富的用户,你会知道在特定情况下哪个方法最有效。
例如,你对 apply
、applymap
、map
、iterrows
和 itertuples
等迭代函数之间的区别非常熟悉。你还了解在使用较慢的替代方案以获得更好的功能和使用最佳方案以获得最佳速度之间的权衡。
虽然有些人可能会称你为挑剔,但你会仔细使用 iloc
和 loc
,因为你知道 iloc
在索引行时更快,而 loc
在索引列时更快。
然而,当涉及到索引值时,你会避免使用这些访问器,因为你知道使用[query](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.query.html)
函数进行条件索引快上好几个数量级。
# DataFrame of stock prices
stocks_df = pd.DataFrame(
columns=['date', 'company', 'price', 'volume']
)
threshold = 1e5
# Rows where the average volume for a company
# is greater than some threshold
result = df.query(
'(volume.groupby(company).transform("mean") > @threshold)'
)
你也知道 replace
函数是 query
的好伙伴,用于替换值。
df.query('category == "electronics"').replace(
{"category": {"electronics": "electronics_new"}}, inplace=True
)
此外,你熟悉不同的文件格式,如 CSV、Parquets、Feathers 和 HDFs,并且会根据需要有意识地选择这些格式,而不是盲目地将所有内容倒入传统的 CSV 文件中。你知道,选择正确的格式可以帮助节省大量时间和内存资源。
除了文件格式,你还有一个强大的技巧——向量化!
与其将 DataFrames 视为普通的 数据框,你更倾向于将它们视为矩阵,将列视为向量。每当你发现自己想使用类似 apply
或 itertuples
的迭代函数时,你首先会看看是否可以利用向量化将一个函数同时应用于一列中的所有元素,而不是逐个应用。
此外,你更喜欢使用具有 .values
属性的底层 NumPy 数组,而不是 Pandas Series,因为你亲眼见证了使用 NumPy 数组进行向量化的速度要快得多。
## 如何提高 Pandas 速度并在毫秒内处理 1000 万行数据集
编辑描述
[towardsdatascience.com
当一切都失败时,你不会就此放弃。绝不。
你会转向 Cython 或 Numba 进行真正的计算密集型任务,因为你是专家。虽然大多数人学习了 Pandas 的基础知识,你却花费了几小时痛苦的时间来学习这两项技术。这就是你与众不同的地方。
好像这些还不够,你还认真阅读了 Pandas 用户指南的 提升性能 页面。
2. 如此多的数据类型
Pandas 提供了如此多的数据类型灵活性。你不满足于仅使用 float
、int
和 object
数据类型,你已经将以下两张图片作为你的桌面壁纸:
来源:pbpython.com/pandas_dtypes.html
BSD-3 许可协议。
来源:docs.scipy.org/doc/numpy-1.13.0/user/basics.types.html
。SciPy 文档。
你刻意选择尽可能小的数据类型,因为你知道它对 RAM 很友好。你知道 int8
比 int64
占用的内存少得多,浮点数也是如此。
你还像避瘟疫一样避免使用 object
数据类型,因为它是最糟糕的类型。
在读取数据文件之前,你会用 cat file_name.extension
查看其顶部几行,以决定你想为列使用哪些数据类型。然后,当使用 read_
* 函数时,你会为每列填写 dtype
参数,而不是让 Pandas 自行决定。
你还尽可能在 就地 执行数据处理。否则,你知道 Pandas 会生成 DataFrames 和 Series 的副本,浪费你的内存。此外,你对 pd.Categorical
和 chunksize
等类和参数有很好的掌握。
3. Pandas 的朋友们
如果有一件事能让 Pandas 成为数据分析库的霸主,那就是它与数据生态系统其他部分的集成。
例如,现在你一定意识到如何将 Pandas 的绘图后端从 Matplotlib 更改为 Plotly、HVPlot、holoviews、Bokeh 或 Altair。
是的,Matplotlib 是 Pandas 的好朋友,但偶尔你也想要像 Plotly 或 Altair 这样互动性强的工具。
import pandas as pd
import plotly.express as px
# Set the default plotting backend to Plotly
pd.options.plotting.backend = 'plotly'
说到后端,你也注意到 Pandas 在全新 2.0.0 版本中添加了一个完全支持的 PyArrow 实现,用于其 read_*
函数来加载数据文件。
import pandas as pd
pd.read_csv(file_name, engine='pyarrow')
当只有 NumPy 后端时,存在许多限制,比如对非数值数据类型支持不足、几乎完全忽略缺失值或不支持复杂数据结构(日期、时间戳、分类数据)。
在 2.0.0 版本之前,Pandas 一直在开发内部解决方案,但这些方案不如一些重度用户的期望。使用 PyArrow 后端,数据加载速度显著提高,并带来了 Apache Arrow 用户熟悉的一系列数据类型:
import pandas as pd
pd.read_csv(file_name, engine='pyarrow', dtype_engine='pyarrow')
另一个我相信你在 JupyterLab 中经常使用的 Pandas 酷炫功能是 DataFrames 的样式设置。
由于 Jupyter 项目如此出色,Pandas 开发者在 .style
属性下添加了一些 HTML/CSS 魔法,因此你可以以一种揭示额外洞察的方式装饰普通的 DataFrames。
df.sample(20, axis=1).describe().T.style.bar(
subset=["mean"], color="#205ff2"
).background_gradient(
subset=["std"], cmap="Reds"
).background_gradient(
subset=["50%"], cmap="coolwarm"
)
图片由作者提供。
4. 数据雕刻师
由于 Pandas 是一个数据分析和处理库,你是否能灵活地调整和转换数据集以适应你的目的,才是真正的高手标志。
尽管大多数在线课程提供了现成的、清理过的列格式数据,但野外的数据集有许多不同的形状和形式。例如,一种最令人讨厌的数据格式是基于行的(金融数据中非常常见):
import pandas as pd
# create example DataFrame
df = pd.DataFrame(
{
"Date": [
"2022-01-01",
"2022-01-02",
"2022-01-01",
"2022-01-02",
],
"Country": ["USA", "USA", "Canada", "Canada"],
"Value": [10, 15, 5, 8],
}
)
df
图片由作者提供
你必须能够将行格式转换为更有用的格式,如以下示例,使用 [pivot](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.pivot.html)
函数:
pivot_df = df.pivot(
index="Date",
columns="Country",
values="Value",
)
pivot_df
你也可能需要执行这个操作的相反操作,称为 melt。
下面是一个使用[melt](https://pandas.pydata.org/docs/reference/api/pandas.melt.html)
函数的 Pandas 示例,它将列数据转换为行数据格式:
df = pd.DataFrame(
{
"Date": ["2022-01-01", "2022-01-02", "2022-01-03"],
"AAPL": [100.0, 101.0, 99.0],
"GOOG": [200.0, 205.0, 195.0],
"MSFT": [50.0, 52.0, 48.0],
}
)
df
图片由作者提供
melted_df = pd.melt(
df, id_vars=["Date"], var_name="Stock", value_name="Price"
)
melted_df
图片由作者提供
这些函数可能很难理解,甚至更难应用。
还有类似的函数,如 pivot_table
,它创建一个数据透视表,可以对表中的每个值进行不同类型的聚合计算。
另一个函数是stack/unstack
,它可以压缩/展开 DataFrame 索引。crosstab
计算两个或多个因素的交叉表,默认情况下,计算因素的频率表,也可以计算其他汇总统计数据。
然后是groupby
。尽管这个函数的基本用法很简单,但其更高级的用例非常难以掌握。如果将 Pandas groupby
函数的内容制作成一个单独的库,它会比 Python 生态系统中的大多数库都要大。
# Group by a date column, use a monthly frequency
# and find the total revenue for `category`
grouped = df.groupby(['category', pd.Grouper(key='date', freq='M')])
monthly_revenue = grouped['revenue'].sum()
精确选择适合特定情况的函数是你成为真正的数据雕刻师的标志。
认识 Pandas 中最难的函数,第一部分
编辑描述
towardsdatascience.com
阅读第二部分和第三部分来了解本节提到的函数的详细情况。
结论
虽然文章的标题可能看起来像是一种戏谑的方式来识别高级 Pandas 用户,但我的目的是为那些希望提升数据分析技能的初学者提供一些指导。
通过突显高级用户的一些独特习惯,我想揭示这个多功能库的一些鲜为人知但强大的功能。
无论你是经验丰富的数据专家还是刚刚起步,通过识别高级用户的特征并采纳他们的一些技巧和窍门,你都可以将数据分析提升到一个新的水平。
希望这篇文章为你提供了一些娱乐和灵感,激励你探索 Pandas 的深度,成为数据处理的高手。感谢你的阅读!
喜欢这篇文章以及它那奇特的写作风格吗?想象一下能访问到数十篇类似的文章,全部由一位才华横溢、迷人且机智的作者(就是我,顺便说一下 😃)编写。
仅需 4.99 美元的会员资格,你不仅可以访问我的故事,还可以获得 Medium 上来自最优秀、最聪明的头脑的知识宝藏。如果你使用我的推荐链接,你将获得我超级 nova 的感激之情和一个虚拟的击掌以支持我的工作。
[## 通过我的推荐链接加入 Medium - Bex T.
获取对我所有 ⚡优质⚡ 内容以及 Medium 上所有内容的独家访问权限,无限制地支持我的工作,给我买一杯咖啡…
ibexorigin.medium.com](https://ibexorigin.medium.com/membership?source=post_page-----40b81b82d369--------------------------------)
图片由作者通过 Midjourney 提供。
你已经成为高级 Pythonista 的 5 个迹象,你可能都没意识到
时候到了,应该获得认可
·发表于 数据科学前沿 ·阅读时间 8 分钟·2023 年 2 月 13 日
–
图片由 Charles Thonney 提供,来源于 Pixabay
介绍
你已经编写 Python 代码一段时间了,编写脚本并解决各种问题。你认为自己已经很不错了,是不是?好吧,大家抓紧了,因为你可能已经成为一名高级 Pythonista,却未曾意识到!
从闭包到上下文管理器,我列出了一些高级 Python 特性,这些特性会让你感叹:“我一直在用这些!”
即使这些概念对你来说是新的,你也将拥有一个出色的检查清单,将你的技能提升到新的水平。
1. 作用域
高级 Python 编程的一个关键方面是对作用域概念的深入了解。
作用域定义了 Python 解释器在程序中查找名称的顺序。Python 的作用域遵循 LEGB 规则(本地、封闭、全局和内置作用域)。根据这一规则,当你访问一个名称(它可以是变量、函数或类)时,解释器会按顺序在 本地、封闭、全局 和 内置 作用域中查找。
让我们通过示例更好地理解每个级别。
示例 1 — 本地作用域
def func():
x = 10
print(x)
func() # 10
print(x) # Raises NameError, x is only defined within the scope of func()
在这里,x
仅在 本地 于 func
的作用域中定义。因此,它在脚本的其他地方无法访问。
示例 2 — 封闭作用域
def outer_func():
x = 20
def inner_func():
print(x)
inner_func()
outer_func() # 20
包含作用域是局部作用域和全局作用域之间的中介作用域。在上面的示例中,x
在outer_func
的局部作用域中。另一方面,x
相对于嵌套的inner_func
函数在包含作用域中。局部作用域总是具有对包含作用域的只读访问权限。
示例 3 — 全局作用域
x = 30
def func():
print(x)
func() # 30
在这里,x
和func
定义在全局作用域中,这意味着它们可以从当前脚本中的任何地方读取。
要在更小的作用域(局部和包含)中修改它们,应使用global
关键字访问它们:
def func2():
global x
x = 40
print(x)
func2() # 40
print(x) # 40
示例 4 — 内建作用域
内建作用域包括所有已经定义的库、类、函数和变量,这些都不需要显式的导入语句。Python 中的一些内建函数和变量的例子包括print
、len
、range
、str
、int
、float
等。
2. 函数闭包
对作用域的扎实掌握开启了另一个重要概念的大门——函数闭包。
默认情况下,函数执行完毕后,会返回到一个空白状态。这意味着其内存会被清除所有过去的参数。
def func(x):
return x ** 2
func(3)
9
print(x) # NameError
上面,我们将 3 的值分配给了x
,但函数在执行后忘记了它。如果我们不想让它忘记x
的值怎么办?
这就是函数闭包发挥作用的地方。通过在某个内函数的包含作用域中定义变量,你可以将其存储在内函数的内存中,即使函数返回后也能保持。
这是一个简单的示例函数,用于计算其执行次数:
def counter():
count = 0
def inner():
nonlocal count
count += 1
return count
return inner
# Return the inner function
counter = counter()
print(counter()) # 1
print(counter()) # 2
print(counter()) # 3
1
2
3
按照 Python 的所有规则,我们在第一次执行后应该已经丢失了counter
变量。但是,由于它在内函数的闭包中,它会一直存在直到你关闭会话:
counter.__closure__[0].cell_contents
3
3. 装饰器
函数闭包有比简单计数器更重要的应用。其中之一是创建装饰器。装饰器是一个嵌套函数,你可以将其添加到其他函数中以增强或甚至修改它们的行为。
例如,下面我们正在创建一个caching装饰器,它记住了函数的每一个位置参数和关键字参数的状态。
def stateful_function(func):
cache = {}
def inner(*args, **kwargs):
key = str(args) + str(kwargs)
if key not in cache:
cache[key] = func(*args, **kwargs)
return cache[key]
return inner
stateful_function
装饰器现在可以添加到可能会在相同参数上重复使用的计算密集型函数中。示例是以下递归斐波那契函数,它返回序列中的nth 数字:
%%time
@stateful_function
def fibonacci(n):
if n <= 0:
return 0
elif n == 1:
return 1
else:
return fibonacci(n-1) + fibonacci(n-2)
fibonacci(1000)
CPU times: user 1.53 ms, sys: 88 µs, total: 1.62 ms
Wall time: 1.62 ms
[OUT]:
43466557686937456435688527675040625802564660517371780402481729089536555417949051890403879840079255169295922593080322634775209689623239873322471161642996440906533187938298969649928516003704476137795166849228875
我们在不到一秒的时间里找到了斐波那契序列中的第 1000 个巨大的数字。下面是没有缓存装饰器的情况下,执行相同过程所需的时间:
%%time
def fibonacci(n):
if n <= 0:
return 0
elif n == 1:
return 1
else:
return fibonacci(n-1) + fibonacci(n-2)
fibonacci(40)
CPU times: user 21 s, sys: 0 ns, total: 21 s
Wall time: 21 s
[OUT]:
102334155
计算第 40 个数字花费了 21 秒。如果没有缓存,计算第 1000 个数字将需要几天时间。
你可以在我另一篇文章中了解如何创建自己的装饰器(包括作用域和闭包)的详细信息:
编辑描述
towardsdatascience.com
4. 生成器
生成器是 Python 中强大的构造,可以高效地处理大量数据。
假设你在某个软件崩溃后有一个 10GB 的日志文件。为了找出出了什么问题,你必须在 Python 中高效地筛选它。
做这件事的最糟糕方法是像下面这样读取整个文件:
with open("logs.txt", "r") as f:
contents = f.read()
print(contents)
由于你逐行处理日志,你不需要读取所有的 10GB,只需一次处理其中的块即可。这就是你可以使用生成器的地方:
def read_large_file(filename):
with open(filename) as f:
while True:
chunk = f.read(1024)
if not chunk:
break
yield chunk # Generators are defined with `yield` instead of `return`
for chunk in read_large_file("logs.txt"):
process(chunk) # Process the chunk
上面,我们定义了一个生成器,它一次仅迭代日志文件的 1024 行。因此,末尾的for
循环非常高效。在每次循环迭代中,内存中只有 1024 行文件内容。之前的块会被丢弃,而其余内容仅在需要时加载。
生成器的另一个特点是能够逐个生成元素,即使在循环之外,也可以使用next
函数。下面,我们定义了一个极速的函数来生成斐波那契数列。
要创建生成器,你需要调用一次函数,然后在结果对象上调用next
:
def fibonacci():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
fib = fibonacci()
type(fib)
generator
print(next(fib)) # 0
print(next(fib)) # 1
print(next(fib)) # 1
print(next(fib)) # 2
print(next(fib)) # 3
你可以阅读下面的文章来了解更多关于生成器的信息。
[## 如何在 Python 中使用生成器和 yield - Real Python
生成器函数是 PEP 255 引入的一种特殊函数,返回一个懒惰的迭代器。这些是……
realpython.com](https://realpython.com/introduction-to-python-generators/?source=post_page-----2b1dd7ef57f3--------------------------------)
5. 上下文管理器
你一定已经使用上下文管理器很长时间了。它们允许开发者高效管理资源,如文件、数据库和网络连接。它们自动打开和关闭资源,从而使代码干净且无错误。
但是,使用上下文管理器和自己编写的上下文管理器之间有很大的区别。做得对的话,它们可以让你在原有功能的基础上抽象出很多样板代码。
一个流行的自定义上下文管理器的例子是计时器:
import time
class TimerContextManager:
"""
Measure the time it takes to run
a block of code.
"""
def __enter__(self):
self.start = time.time()
def __exit__(self, type, value, traceback):
end = time.time()
print(f"The code took {end - self.start:.2f} seconds to execute.")
上面,我们定义了一个TimerContextManager
类,它将作为我们未来的上下文管理器。它的__enter__
方法定义了当我们使用with
关键字进入上下文时发生的事情。在这种情况下,我们启动了计时器。
在__exit__
中,我们退出上下文,停止计时器,并报告经过的时间。
with TimerContextManager():
# This code is timed
time.sleep(1)
The code took 1.00 seconds to execute.
这是一个更复杂的示例,它使资源锁定,以便每次只能由一个进程使用。
import threading
lock = threading.Lock()
class LockContextManager:
def __enter__(self):
lock.acquire()
def __exit__(self, type, value, traceback):
lock.release()
with LockContextManager():
# This code is executed with the lock acquired
# Only one process can be inside this block at a time
# The lock is automatically released when the with block ends, even if an error occurs
如果你想更温和地了解上下文管理器,请查看我关于这个话题的文章。
编辑描述
[towardsdatascience.com
如果你想深入了解并学习所有相关内容,这里还有一篇出色的 RealPython 文章。
## 上下文管理器与 Python 的 with 语句 - Real Python
在本教程中,你将学习:你在编程中常遇到的一个问题是如何正确管理外部…
结论
就这样,各位!你有多少次说过,“我知道这一点!”?即使不是很多次,你现在也知道了要学习的东西,以变得更高级。
不要害怕学习新事物带来的不适。只要记住,伟大的力量伴随着(我不说了!)更多挑战性的 bug 需要修复。但嘿,你现在是个高手了,小小的调试对你来说算什么?
感谢阅读!
喜欢这篇文章吗?让我们面对现实,它的奇特写作风格?想象一下能访问更多类似的文章,全部由一位才华横溢、迷人、机智的作者(顺便说一下,就是我 :))。
仅需 4.99 美元的会员费,你不仅可以访问我的故事,还能获取来自 Medium 上最优秀、最聪明头脑的知识宝藏。如果你使用 我的推荐链接,你将获得我超级 nova 的感激和虚拟击掌以支持我的工作。
获取我所有⚡高级⚡内容的独家访问权,并在 Medium 上畅享无限支持我的工作,通过购买我…
使数据管理成功的 5 项技能
在数据管理中生存并成功
·
关注 发表在 数据科学前沿 ·7 分钟阅读·2023 年 1 月 9 日
–
在我的行业生涯中,我一直从事各种数据相关的角色,如数据管理、数据工程和数据分析。我考虑了哪些方面做得好,哪些方面相当具有挑战性,以及人们和他们的技能(包括我自己!)如何影响数据项目的成功。根据这些经验,我整理了以下一些重要技能,这些技能使得一个数据经理变得出色!请注意,这不是基于实证研究,我在这里不是作为研究者写的——这个列表仅仅是一些个人观察的汇总——所以如果你有其他技能,请在评论中添加 😃
#1 建模
“一图胜千言”。这在数据管理中同样适用。每个数据项目都会遇到需要绘制表示现状或捕捉利益相关者不同视角的图像的情况。因此,请将模型视为数据项目中高效沟通的关键技术!目前有很多复杂的建模工具支持成熟的建模语言。而且第二点更为重要:请应用成熟的建模语言!你能犯的最大错误之一就是试图将数据项目的每个方面都放在一个混乱的 PowerPoint 图表中。你最终会得到一个既不是好的数据模型也不是好的过程模型的怪物。相反,要意识到你的数据项目有动态方面(→ 过程模型的候选者)和静态方面(数据模型或部署模型的候选者),它们必须能够契合,但不一定要在一个图表中。已有成熟的语言(如 BPMN、UML、陈氏模型)和工具(如 Visual Paradigm、Modelio、Enterprise Architect)支持符合语法的语言应用或回环工程。模型就像是物理建筑的施工图,所以确保你的计划是成熟的,以避免数据架构中由项目人员不同解读模型而造成的倾斜地板和开墙。在物联网时代,模糊数据模型对物理世界也有影响,。它们限制了比较数据分析的可能性或使得用例扩展变得困难。
#2 连接人们
在一个组织中,数据管理者的角色是复杂的。这个人通常既不是独立实施数据库的 IT 专家,也不是实际负责数据或流程的业务专家(这更属于数据管家(Data Steward)的职责)。那么,数据管理者(甚至数据管理部门)的真正价值是什么呢?在我看来,你需要一个在不同的数据利益相关者之间搭建桥梁的人,从方法论的层面进行连接。很容易找到那些自认为在特定业务领域、数据分析方法或 IT 工具方面是专家的人,但找到一个愿意将这些人连接起来并组织他们的能力的人则相当复杂,因为这在数据项目中往往是必需的。因此,我所指的是像网络建设、项目管理、利益相关者管理和变更管理这样的技能,这些技能是一步一步建立数据社区作为数据治理的支柱所必需的。没有这些人,数据管理者将失败!因此,在我看来,招聘人员在寻找数据管理者时,不仅应考察技术技能,还应考察这些人际技能。同样,如果你对数据管理者职位感兴趣,评估一下自己是否对超越技术话题的各种组织职责感到舒适。
#3 领域专长
我见过各种建立数据管理组织的方法。有的团队是更大业务部门的一部分,全球负责数据标准,或者是由小核心团队和其他部门的“虚线”数据管理者组成的联邦组织,或者数据管理团队是 IT 部门的一部分。不论组织的归属在哪里——我的经验表明,一个好的数据经理应具备基本的领域知识。如果一个人作为某个业务领域的数据经理,她/他应该了解价值链中的情况,关键业务问题是什么,存在哪些数据用于何种目的,哪些业务流程生成和消耗数据,它们如何相互连接。有了这些知识,数据经理在收到数据变更请求时,能总是多想一步。也许管道中还有其他请求可以合并,也许你对流程实施有自己的想法,也许你建议涉及其他利益相关者(→见 #2),也许有必须实施的政策(例如 DSGVO),也许你可以影响数据模型的可持续性……列表很长!假设数据经理最初有 IT 背景,创建令人满意的领域知识可能是一项具有挑战性的任务,因为它是行业、公司或甚至部门特定的,你不能仅仅通过听有声书来弥补这个差距。然而,保持冷静,并接受随着时间的推移会有学习曲线,但重要的是你愿意学习和吸收这些非 IT 知识。
#4 技术意识
将数据变更请求从业务相关方的期望转化为真正需要实施的内容是一个过程。在我所在的“家”行业——半导体行业中,业务专家通常也拥有先进的 IT 知识——这既是福也是祸 😃 一方面,你可以与所有相关方深入探讨技术问题;另一方面,他们倾向于提出 IT 解决方案建议而不是问题描述,这在 IT 专家中并不受欢迎,因为这在某种程度上绕过了他们的责任。除了受损的荣誉感之外,还有充分的理由解释为什么构建实施概念与需求分析是分开的。其中之一是没有“唯一”的解决方案,而是各种优缺点的选项。领域专长的重要性与对最新数据管理技术及其能力的意识同样重要。例如,并不是每一条主数据都必须手动输入,即使数据管理员期望如此。也许记录中的某些属性可以通过业务规则系统推导出来,或者通过其他系统的数据集成进行填写。或者,问题可以通过更改业务流程中的任务顺序来解决,这可以通过工作流得到支持和强制执行。此外,数据目录还有一些强大的功能,帮助数据管理者在其全面角色中——请查看我关于某个工具的系列文章以获取更多信息。特别是对于那些没有 IT 背景的数据管理者,这项技能可能具有挑战性。幸运的是,技术知识通常不是公司特有的,因此可以通过官方材料(视频、MOOC、书籍、文章等)来弥补。从教学的角度来看,我建议为初级数据管理者制定一个学习路径,以便概述和深入了解所选技术。
#5 精益思维
最后但同样重要的是,我在应用精益哲学方面有过良好的经验。“精益” 通常与制造业相关,但也可以用于数据管理。例如,你可以利用 “Poka Yoke” 原则来设计数据流程或图形用户界面,以通过设计避免某些数据质量问题。或者,你可以应用 “5S” 来清理数据架构,其中数据评估有助于判断你组织中的哪些数据对象是“浪费”,哪些是业务关键的 —— 我们已发表了一篇提出这种方法的研究论文。尤其是在较大的数据项目中,例如当你想将整个领域整合到主数据管理系统中时,组织 “Kaizen” 事件是值得的,在这种活动中,所有利益相关者以研讨会模式共同工作,塑造未来(概念上的)数据景观。另一个很好的方法是 “PDCA” ,它有助于迭代地改善数据质量 —— 还有更多精益方法我已经在数据项目中应用过且非常有用!在我看来,精益方法是获得可持续数据架构的关键。这种可持续性效应也可以在传统的精益管理应用中观察到,如在 这篇(德语)文章 中讨论的那样。价值链中的通用浪费类型也可以在数据架构中找到,例如运动(例如,数据维护的不必要的人工努力)和缺陷(例如,错误或缺失的数据限制了数据产品的有效性)。总的来说,这是一项可能被低估的技能,但却是进一步研究的绝佳主题!
结论
通过这个故事,我想分享我对数据经理最佳技能组合的看法。一个好的数据经理远不止是数据管理软件的技术专家。通常,数据管理计划的成功(或失败)取决于组织中的(缺失的)支持。因此,有一些非技术技能,如利益相关者管理或项目管理,特别是在更复杂的数据项目中,需要学习和应用。尽管如此,IT 知识也很重要,尤其是在通过精益方法发现数据架构中的浪费时。领域知识是你在大学里学不到的,而是通过在某一业务领域的实际工作逐渐获得的,但你也应该对这种知识保持开放,并且可以通过采访专家等方式积极影响你的学习曲线的坡度!别忘了在你的数据项目中应用已建立的建模技术!你组织中的人们越习惯于良好的模型,未来的数据项目沟通将变得越高效。
使用 Python 构建美丽条形图的 5 个步骤
原文:
towardsdatascience.com/5-steps-to-build-beautiful-bar-charts-with-python-3691d434117a
如何利用 Matplotlib 的全部功能讲述更具吸引力的故事
·发表在 Towards Data Science ·7 分钟阅读·2023 年 1 月 23 日
–
2008 年美国国内航班的平均航空延误 — 作者提供的图像
动机
当支持这些故事的图表清晰、自解释且视觉上令人愉悦时,讲述一个引人入胜的故事变得容易得多。
在许多情况下,内容和形式同样重要。
数据再好但表现不佳将不会引起应有的关注,而表现出色的数据即使质量较差也容易被忽视。
Matplotlib 使得使用现成函数绘制数据变得快捷而简单,但微调步骤则需要更多的努力。
我花了不少时间研究使用 Matplotlib 构建引人注目的图表的最佳实践,以便你不必花费时间。
在本文中,我重点介绍了条形图,并解释了如何将我在各处找到的知识片段拼接在一起,从而达到如下效果…
2008 年美国国内航班的平均航空延误 — 作者提供的图像
… 到那个:
2008 年美国国内航班的平均航空延误 — 作者提供的图像
#0 数据
为了说明方法论,我使用了一个关于 2008 年美国国内航班延误的公开数据集:
2008 年,“数据展览 2009:航空公司准时数据”,
doi.org/10.7910/DVN/HG7NV7
,哈佛数据集,V1公有领域 CC0 1.0
在导入了读取数据和构建图表所需的包后,我简单地按月份对数据进行了分组,并计算了平均延迟,使用了以下代码:
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib as mpl
from matplotlib.ticker import MaxNLocator
df = pd.read_csv('DelayedFlights.csv')
df = df[['Month', 'ArrDelay']] # Let's only keep the columns useful to us
df = df[~df['ArrDelay'].isnull()] # Get rid of cancelled and diverted flights
# Group by Month and get the mean
delay_by_month = df.groupby(['Month']).mean()['ArrDelay'].reset_index()
本文中用于构建不同版本条形图的数据集如下:
本文中使用的数据集 —— 图片由作者提供
#1 基础图表
坦白说,使用两行代码,你已经可以构建一个条形图并从中获得一些基本的见解。
虽然这个图表既不最美观,也不最实用,因为缺少关键信息,但你已经可以看出,12 月份旅行很可能会导致航班延误。
# Create the figure and axes objects, specify the size and the dots per inches
fig, ax = plt.subplots(figsize=(13.33,7.5), dpi = 96)
# Plot bars
bar1 = ax.bar(delay_by_month['Month'], delay_by_month['ArrDelay'], width=0.6)
Matplotlib 最基本的条形图 —— 图片由作者提供
#2 基本要素
让我们在图表中添加一些重要元素,以使其更易于观众阅读。
-
网格
为了提高可读性,图表的网格是必不可少的。它们的透明度设置为 0.5,以免过多干扰数据点。
-
X 轴和 Y 轴重新格式化
我主动添加了比实际需要更多的参数,以获得对微调可能性的更全面的视角。例如,x 轴不需要 major_formatter 和 major_locator 对象,因为我们只是设置标签,但如果读者的 x 轴包含其他数字,那么这些可能会派上用场。
-
条形标签
在每个条形上方添加条形标签,使得对接近数据点之间的比较更容易,并提供关于实际值的更多细节。
# Create the grid
ax.grid(which="major", axis='x', color='#DAD8D7', alpha=0.5, zorder=1)
ax.grid(which="major", axis='y', color='#DAD8D7', alpha=0.5, zorder=1)
# Reformat x-axis label and tick labels
ax.set_xlabel('', fontsize=12, labelpad=10) # No need for an axis label
ax.xaxis.set_label_position("bottom")
ax.xaxis.set_major_formatter(lambda s, i : f'{s:,.0f}')
ax.xaxis.set_major_locator(MaxNLocator(integer=True))
ax.xaxis.set_tick_params(pad=2, labelbottom=True, bottom=True, labelsize=12, labelrotation=0)
labels = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
ax.set_xticks(delay_by_month['Month'], labels) # Map integers numbers from the series to labels list
# Reformat y-axis
ax.set_ylabel('Delay (minutes)', fontsize=12, labelpad=10)
ax.yaxis.set_label_position("left")
ax.yaxis.set_major_formatter(lambda s, i : f'{s:,.0f}')
ax.yaxis.set_major_locator(MaxNLocator(integer=True))
ax.yaxis.set_tick_params(pad=2, labeltop=False, labelbottom=True, bottom=False, labelsize=12)
# Add label on top of each bar
ax.bar_label(bar1, labels=[f'{e:,.1f}' for e in delay_by_month['ArrDelay']], padding=3, color='black', fontsize=8)
在我们的图表中添加一些基本功能 —— 图片由作者提供
#3 专业外观
在我们的图表中添加更多功能会使其看起来更加专业。这些功能可以叠加在任何图表上(不仅仅是条形图),并且与我们在本文中使用的数据无关。
感谢下面的代码片段,这些调整将几乎不需要任何额外的努力来实现。作者的建议:保存并随意重用。
读者可以调整这些参数来创建自己的视觉身份。
-
坐标轴
坐标轴构成了图表周围可见的框架。它们被移除,除了右侧的坐标轴被设置得稍微粗一点。
-
顶部的红线和矩形
在标题上方添加了一条红线和一个矩形,以很好地将图表与上方的文本隔离开来。
-
标题和副标题
一个没有标题来介绍它的图表算什么呢?
副标题可以用来进一步解释内容,甚至提出初步结论。
-
来源 在所有生产的图表中必不可少。
-
边距调整
调整图表区域周围的边距,以确保充分利用所有可用空间。
-
白色背景
设置白色背景(默认透明)在通过电子邮件、Teams 或其他工具发送图表时会非常有用,因为透明背景可能会造成问题。
# Remove the spines
ax.spines[['top','left','bottom']].set_visible(False)
# Make the left spine thicker
ax.spines['right'].set_linewidth(1.1)
# Add in red line and rectangle on top
ax.plot([0.12, .9], [.98, .98], transform=fig.transFigure, clip_on=False, color='#E3120B', linewidth=.6)
ax.add_patch(plt.Rectangle((0.12,.98), 0.04, -0.02, facecolor='#E3120B', transform=fig.transFigure, clip_on=False, linewidth = 0))
# Add in title and subtitle
ax.text(x=0.12, y=.93, s="Average Airlines Delay per Month in 2008", transform=fig.transFigure, ha='left', fontsize=14, weight='bold', alpha=.8)
ax.text(x=0.12, y=.90, s="Difference in minutes between scheduled and actual arrival time averaged over each month", transform=fig.transFigure, ha='left', fontsize=12, alpha=.8)
# Set source text
ax.text(x=0.1, y=0.12, s="Source: Kaggle - Airlines Delay - https://www.kaggle.com/datasets/giovamata/airlinedelaycauses", transform=fig.transFigure, ha='left', fontsize=10, alpha=.7)
# Adjust the margins around the plot area
plt.subplots_adjust(left=None, bottom=0.2, right=None, top=0.85, wspace=None, hspace=None)
# Set a white background
fig.patch.set_facecolor('white')
我们的视觉身份应用到图表上,使其更整洁 —— 图片由作者提供
#4 色彩渐变
我们在上一节中留下的图表干净整洁,准备好被纳入演示中。虽然对条形颜色进行渐变以更好地可视化变化并非必需,但会增加一个吸引人的特性。
这个用例的在线文档可能不是最好的,但实际上用 Matplotlib 的LinearSegmentedColormap和Normalize函数实现起来并不太难。
# Colours - Choose the extreme colours of the colour map
colours = ["#2196f3", "#bbdefb"]
# Colormap - Build the colour maps
cmap = mpl.colors.LinearSegmentedColormap.from_list("colour_map", colours, N=256)
norm = mpl.colors.Normalize(delay_by_month['ArrDelay'].min(), delay_by_month['ArrDelay'].max()) # linearly normalizes data into the [0.0, 1.0] interval
# Plot bars
bar1 = ax.bar(delay_by_month['Month'], delay_by_month['ArrDelay'], color=cmap(norm(delay_by_month['ArrDelay'])), width=0.6, zorder=2)
仍然是相同的条形图,增加了额外的颜色特性——图片由作者提供
#5 最后修饰
为了实现文章开头介绍的最终结果,剩下的工作就是实现这几个额外的组件:
-
平均数据线
在图表上显示平均数据线是帮助观众快速了解情况的一种有效方式。
-
第二种颜色刻度
通过第二种颜色刻度,我们突出了高于平均水平(或任何阈值)的数据,以便在短时间内更容易理解可视化内容。
-
图例
当我们添加第二种颜色刻度时,我们在图表上引入了图例的需求。
# Find the average data point and split the series in 2
average = delay_by_month['ArrDelay'].mean()
below_average = delay_by_month[delay_by_month['ArrDelay']<average]
above_average = delay_by_month[delay_by_month['ArrDelay']>=average]
# Colours - Choose the extreme colours of the colour map
colors_high = ["#ff5a5f", "#c81d25"] # Extreme colours of the high scale
colors_low = ["#2196f3","#bbdefb"] # Extreme colours of the low scale
# Colormap - Build the colour maps
cmap_low = mpl.colors.LinearSegmentedColormap.from_list("low_map", colors_low, N=256)
cmap_high = mpl.colors.LinearSegmentedColormap.from_list("high_map", colors_high, N=256)
norm_low = mpl.colors.Normalize(below_average['ArrDelay'].min(), average) # linearly normalizes data into the [0.0, 1.0] interval
norm_high = mpl.colors.Normalize(average, above_average['ArrDelay'].max())
# Plot bars and average (horizontal) line
bar1 = ax.bar(below_average['Month'], below_average['ArrDelay'], color=cmap_low(norm_low(below_average['ArrDelay'])), width=0.6, label='Below Average', zorder=2)
bar2 = ax.bar(above_average['Month'], above_average['ArrDelay'], color=cmap_high(norm_high(above_average['ArrDelay'])), width=0.6, label='Above Average', zorder=2)
plt.axhline(y=average, color = 'grey', linewidth=3)
# Determine the y-limits of the plot
ymin, ymax = ax.get_ylim()
# Calculate a suitable y position for the text label
y_pos = average/ymax + 0.03
# Annotate the average line
ax.text(0.88, y_pos, f'Average = {average:.1f}', ha='right', va='center', transform=ax.transAxes, size=8, zorder=3)
# Add legend
ax.legend(loc="best", ncol=2, bbox_to_anchor=[1, 1.07], borderaxespad=0, frameon=False, fontsize=8)
最终产品:图表易于阅读——图片由作者提供
#6 最终思考
本文的目的是分享这里和那里的知识,以使用 Matplotlib 构建更具吸引力的条形图。我尽量使其尽可能实用,包含可重用的代码片段。
我相信还有其他调整是我没想到的。如果你有任何改进的建议,欢迎评论,让这篇文章对大家更有用!
本文仅关注于条形图,敬请期待更多内容!
感谢你阅读到文章的最后!
如果你有任何问题或意见,欢迎在下方留言,或者通过* LinkedIn 与我联系!
更多推荐
所有评论(0)