用 Matplotlib 实现 Python 中的条形图竞赛
~在大约不到 50 行的代码中
Bar Chart Race animation showing the 10 biggest cities in the world
条形图比赛已经有一段时间了。今年,他们席卷了社交媒体。它始于马特·纳瓦拉的推特,这条推特被点击了 1000 万次。然后, John Burn-Murdoch 使用 d3.js 创建了可复制的笔记本,其他人开始创建他们的种族。然后,fluorescent studio 发布了面向非程序员的赛程图。从那以后,数百场比赛在互联网上被分享。
与 Matplotlib 竞赛
我想知道——用 Jupyter 和 Matplotlib 用 Python 重新制作 JBM 的版本有多容易?事实证明,在不到 50 行代码中,您可以使用 Matplotlib 在 Python 中合理地重新创建可重用的条形图比赛。
这是我们想要创造的。
让我们编码
现在,您已经看到了输出,我们将逐步构建它。
的默认风格是为许多常见的情况设计的,但对我们的审美来说绝不是最佳的。因此,我们的大部分代码将进入样式(轴,文本,颜色,刻度等)
在您的Jupyter
笔记本中,导入相关库。
用pandas
阅读城市人口数据集。我们只需要 4 列来与'name', 'group', 'year', 'value'
一起工作。典型地,一个name
映射到一个group
(城市到洲/国家),每个year
有一个value
。
数据转换
我们很有兴趣看到前 10 名value
是给定的year
。使用熊猫变换,我们将得到最高的10
值。
基本图表
现在,让我们绘制一个基本的条形图。我们首先创建一个图形和一个轴。然后,我们使用ax.barh(x, y)
绘制水平条形图。
注意,最高的横条在底部。我们需要扭转这种局面。
颜色,标签
接下来,让我们基于组添加值、组标签和颜色。我们将使用colors
和group_lk
来给条形添加颜色。
group_lk
是name
和group
值之间的映射。
现在,我们只剩下设计图表的样式了。
波兰式
为了方便起见,让我们将代码移到draw_barchart
函数中。
我们需要以下项目的风格:
- 文本:更新字体大小、颜色、方向
- 轴:移动 X 轴到顶部,添加颜色和字幕
- 网格:在横条后添加线条
- 格式:逗号分隔的值和轴标记
- 添加标题、致谢名单、装订线空间
- 移除:方框、y 轴标签
我们将为此再添加十几行代码。
我们现在有一个相同的图表。
动画种族
为了制作比赛的动画,我们将使用matplotlib.animation
中的[FuncAnimation](https://matplotlib.org/3.1.1/api/_as_gen/matplotlib.animation.FuncAnimation.html)
。[FuncAnimatio](https://matplotlib.org/3.1.1/api/_as_gen/matplotlib.animation.FuncAnimation.html)
通过反复调用一个函数(在画布上绘制)来创建动画。在我们的例子中,这个函数将是draw_barchart
。我们也使用frames
,这个参数接受你想要运行draw_barchart
的值——我们将从year
1968 运行到 2018。
现在,我们有了它,用 matplotlib 在笔记本中进行条形图竞赛。
您可以将animator
对象保存为视频/gif 或在笔记本中播放。
奖金:xkcd 式!
将 matplotlib 图转换成 xkcd 风格的图非常容易。您可以使用plt.xkcd
简单地打开xkcd
草图风格绘图模式。
这里是动画 xkcd 风格的条形图比赛。
自己动手
比赛动画的完整代码在这里,你也可以在谷歌实验室上玩它。尝试更改数据集、颜色并分享您的比赛。
Matplotlib 是一个巨大的库——能够调整绘图的每个方面是非常强大的,但对于高度定制的图表来说,这可能很复杂/耗时。至少,对于这些条形图比赛,这是相当快!
Base R 脚本练习:将 RIS 文件转换为格式良好的 XML 文档。
介绍
这篇文章的目的是分享我遇到的一个脚本问题。我没有太多应对这类挑战的经验,所以我认为这是一个分享和寻求反馈的好机会。
挑战
编写一个脚本,将任何。RIS 文件转换成格式良好的 XML 文档。
- 包括假设和详细注释。
- 该脚本不得有任何基础语言之外的依赖。
- 该脚本应该能够防止意外误用。
- 每条记录都应该放在一个文章标签中。
虽然我知道其他语言可能更适合这些类型的过程,但我还是选择用 R 编写脚本来探索这个过程。
第一部分-理解。RIS 和。XML 格式
RIS(研究信息系统)
ris 文件格式是一种标记格式,是为标准化书目信息而创建的。例如,生命科学和生物医学信息的免费书目数据库 MEDLINE 使用 ris 格式进行引用。
让我们看看它是什么样子的:
如你所见,基本结构是:每一行包含一个标记和一个用“-”分隔的值。每个 RIS 记录以“TY”标签(引用类型)开始,以 ER 标签(引用结束)结束。这里有更多关于 ris 格式的信息。
XML(扩展标记语言)
XML 本质上就像任何其他标记语言(HTML)一样,通过使用 来注释文档。然而,XML 是可扩展的,这意味着用户可以创建他/她自己的标签(也就是说,你可以用代替
)。这显示了 XML 的主要优势。标签可以更好地描述它们所包含的信息。当试图做除了在 web 浏览器中显示该信息之外的任何事情时,这是有利的。由于其简单性和标准化,XML 在数据交换和存储方面也很流行。这里有更多关于 XML 的内容。
让我们快速看一个例子:
现在让我们来看看格式约束,这样我就可以确保文件的格式正确。这些是主要的限制因素:
- XML 文档可以以 XML 声明开始(即)
- 任何特殊字符(“<> &”)都不能出现,除非是在执行其角色时。
- 开始标签、结束标签被正确地嵌套
- 标记名区分大小写;开始标记和结束标记必须完全匹配。
- 标记名不能包含任何字符!"#$%&'()*+,/;<=>?@[]^`{|}~,也不是空格字符,不能以“-”、“”开头或一个数字。
- 单个根元素包含所有其他元素。
更多关于结构良好的 XML 文档和约束的信息。
第二部分-剧本
假设:
RIS 文件遵循标准 RIS 标签格式,包括:
- 每张 RIS 唱片都以“唱片结束”标签结尾:“呃-”
- 每个 RIS 标签和值由“-”分隔
- 每个 RIS 标签和值都在各自的行上
# naming function and function argumentsris2xml <- function(x = "", root = "root_element", save = FALSE, file_name = "new_file"){# read lines
ris2 <- readLines(x, encoding = "UTF-8")# seperating articles
df <- data.frame(article = NA, ris = ris2, stringsAsFactors = FALSE)
end_of_records <- rev(grep(pattern = '^ER -', ris2))
article = length(end_of_records)
for(i in end_of_records){
df$article[1:i] <- article
article = article - 1
}# removing ER lines and empty Lines
df <- df[-end_of_records,]
df <- df[grep(pattern = '\\S', df$ris ),]# splitting tags and values
split <- strsplit(df$ris, split = " -")
df$tags <- sapply(split, "[[", 1)
df$values <- sapply(split, "[[", 2)# trim any extra white space
df$tags <- trimws(df$tags, which = "both")
df$values <- trimws(df$values, which = "both")
# xml special character restraints
df$values <- gsub('<', '<', df$values)
df$values <- gsub('>', '>', df$values)
df$values <- gsub('&', '&', df$values)
df$values <- gsub("'", ''', df$values)
df$values <- gsub('"', '"', df$values)######## Function for finishing steps after tag constraints #########
finish <- function(){
# putting values in tags
for(i in 1:nrow(df)){
df$tagged[i] <- paste0('<', df$tags[i], '>', df$value[i], '</', df$tags[i], '>', collapse = "" )
}# adding article tags and I2E tag
document <- data.frame(article= unique(df$article), body = NA)# article tags
for(i in document$article){
vect <- unlist(df[df$article == i, "tagged"])
temp <- paste0(vect, collapse = "")
document[document$article == i, "body"] <- paste0("<Article>", temp, '</Article>', collapse = "")
}# adding root tag & xml tag
body <- paste0(document$body, collapse = "")
document_final <- paste0('<?xml version="1.0"?>','<', root, '>',body,'</', root ,'>', collapse = "")
# return xml document
return(document_final)
# save file if user chose to
if(save == TRUE){
write(document_final, file = paste0(file_name, ".xml"))
}
}
######################################################################
# Enforcing XML tag constraints#finding invalid tags
invalid <- grep("[\\[\\!\"#\\$%&'\\(\\)\\*\\+,/;<=>\\?@\\[\\\\\\]\\^`\\{\\|\\}~]|(^\\d)|(^-)|(^\\.)", df$tags, perl = TRUE)# if there are invalid tags: print warning and print the invalid tags
if(length(invalid) > 0){
print('WARNING: there may be invalid tag names. please check to make sure the tags follow the xml constraints')
print("The following tags are invalid: ")
print(df$tags[invalid])# give the user the option to re-write the tags
re_write <- readline(prompt = "to re-write these tags please do so in order and seperated by a comma: ")
re_write <- unlist(strsplit(x = re_write, split = ","))
re_write <- trimws(re_write, which = "both")# check user inputs for validity
re_invalid <- grep("[\\[\\!\"#\\$%&'\\(\\)\\*\\+,/;<=>\\?@\\[\\\\\\]\\^`\\{\\|\\}~]|(^\\d)|(^-)|(^\\.)", re_write, perl = TRUE)# make sure the number of inputs match & inputs meet contsraints before finishing script
if((length(re_write) == length(invalid)) & (length(re_invalid) == 0)){
df[invalid, "tags"] <- re_write
# constraints are met so finish script
finish()
# if contsraints not met, print error and exit.
}else{
print("Error: re-writing tags not valid. Please try function again")
}
}else{
# constraints are met so finish script
finish()
}}
参数:
- x = " ":要转换的文件
- root = "root_element ":将保存文档其余部分的根标签
- save = FALSE : T/F 如果用户想要保存转换后的文件。
- file_name = "new_file ":保存转换后文件的文件名。
结论
仅此而已。至于测试,我通过一些 xml 验证器运行输出文件,并在 R 中使用一些 XML 解析器来确保格式正确。我已经能想到我还可以增加几个步骤,比如在继续之前检查 RIS 的文件以确保它符合假设。我知道这个脚本远非完美,但这是一种我从未有过的用 R 工作的有趣方式。我喜欢听想法!感谢阅读。
使用 NLTK 的基本情感分析
“你最不满意的顾客是你最大的学习来源.”—比尔·盖茨
那么客户怎么说呢?
在今天的背景下,事实证明很多。社交媒体打开了顾客意见的闸门,现在顾客意见大量自由流动,供企业分析。今天,使用机器学习的公司能够以文本或音频的形式提取这些意见,然后以前所未有的规模分析它们背后的情绪。情感分析,观点挖掘,随你怎么说,如果你有产品/服务要卖,你就需要在上面。
当以电子方式捕捉时,客户情绪——超越事实的表达,传达情绪、观点和情感——具有巨大的商业价值。我们谈论的是客户、潜在客户、患者、投票者和意见领袖的声音。”——塞思·格里姆斯
从媒体上的用户评论到分析股票价格,情感分析已经成为几乎所有行业中无处不在的工具。例如,下图显示了易贝的股价走势,情绪指数是基于对提到易贝的推文的分析而创建的。
Figure 1: eBay Stock Prices Vs. Sentdex Moving Average
什么是情感分析?
Techopedia 对情感分析的定义如下:
情感分析是一种数据挖掘类型,通过自然语言处理(NLP)、计算语言学和文本分析来测量人们的意见倾向,这些技术用于从网络中提取和分析主观信息——主要是社交媒体和类似的来源。所分析的数据量化了公众对某些产品、人物或想法的情绪或反应,并揭示了信息的上下文极性。情感分析也称为意见挖掘。
有两种广泛的情感分析方法。
纯统计数据:
这类算法将文本视为单词包(BOW),其中单词的顺序和上下文被忽略。原始文本被过滤掉,只剩下被认为带有感情色彩的词。对于这个博客,我将尝试这种方法。这种模型没有利用对特定语言的理解,而仅仅使用统计测量来对文本进行分类。
统计学和语言学的结合:
这些算法试图结合语法原则、各种自然语言处理技术和统计数据来训练机器真正“理解”语言。
基于分析生成的输出类型,情感分析也可以大致分为两类。
分类/极性——那段文字是“积极的”、“中性的”还是“消极的?”在这个过程中,你试图给一段文字贴上积极或消极或中性的标签。
标量/程度 — 给出一个预定义等级的分数,范围从高度肯定到高度否定。例如,下图显示了基于各种选举候选人推文的情绪分析。在这种情况下,情绪是以标量形式来测量的。
Figure 2: How Twitter Feels about The 2016 Election Candidates
在我的数据科学训练营期间,我尝试使用 NLTK 库构建一个基本的情绪分析工具。我找到了一个漂亮的 youtube 教程,并按照列出的步骤学习如何进行基本的情绪分析。虽然该教程侧重于分析 Twitter 情绪,但我想看看我能否将电影评论分为正面或负面。我发现了一个包含 25000 条 IMDB 评论的标签数据集。txt 文件分为两个文件夹,用于负面和正面评论。
流程:
我在我的 Jupyter 笔记本上导入了以下库,并从它们各自的文件夹中读取了正面和负面的评论。
制作单词包(BOW): 对于我们的单词包(BOW),从技术上来说,我们可以包含所有独特的单词。然而,在我们的分析中包括所有独特的单词在计算上可能是昂贵的,甚至是不必要的。例如,评论中女演员的名字不会给出关于评论情绪的任何信息。所以,我们需要变得聪明,选择信息最丰富的单词。
对于这个小范围的项目,也是在教程的指导下,我只从特征中选择了形容词,基于这样一个假设,即形容词是积极和消极情绪的高度信息。对于每一篇评论,我都去掉了标点符号,标记了字符串,去掉了停用词。请查看我的另一个博客,了解如何使用 NLTK 执行这些基本的预处理任务。
接下来,为了获得所有形容词的列表,我执行了词类(在上面提到的我的博客中也讨论过)标记,并创建了我们的 BOW 或在这种情况下的形容词包。我称这个列表为“所有单词”,它还需要另一轮过滤。
接下来,为了挑选信息最丰富的形容词,我使用 nltk 创建了 all_words 中单词的频率分布。FreqDist()方法。我列出了所有单词中出现频率最高的 5000 个形容词。至此,all_words 已经准备好作为我们最后的弓了。
**为每个评论创建特征:**对于每个评论,我创建了一个元组。元组的第一个元素是一个字典,其中的关键字是 BOW 的 5000 个单词中的每一个,如果该单词出现在评论中,则每个关键字的值为 True,否则为 False。第二个元素是该标签的标签,“pos”表示正面评价,“neg”表示负面评价。
#example of a tuple feature set for a given review
({'great': True, 'excellent': False, 'horrible': False}, 'pos')
然后,我将元组列表(从这里开始称为 feature_set)分成训练集(20,000)和测试集(5,000)
有趣的部分:机器学习!!!
既然我已经准备好了我的特性、训练和测试集,我的下一步就是尝试一个普通的基础模型。对于我的基本模型,我使用了 NLTK 的朴素贝叶斯分类器模块。该模型的准确率为 84.36%。这对于一个基本模型来说是非常好的,考虑到训练数据的规模,这并不奇怪。右图显示了未归一化和归一化预测的混淆矩阵。
上图(左图)显示了该模型中 15 个最具信息性的特征。与它们相关的比率显示了每一个相应的单词在一类文本中出现的频率比其他单词多多少。这些比率被称为似然比。例如,“糟糕”这个词在负面评价中出现的几率是正面评价的 13 倍。
为了进一步评估该模型,我使用 sci-kit learn 计算了 f1_score,并创建了一个混淆矩阵。f1 _ 得分为 84.36%。归一化混淆矩阵显示,该模型正确预测了 83%的正面评论和 85%的负面评论。
接下来,我尝试在训练集上训练其他分类算法,以找到得分最高的模型。我用了,多项式朴素贝叶斯,伯努利朴素贝叶斯,逻辑回归,随机梯度下降和支持向量分类器。NLTK 有一个名为 SklearnClassifier 的内置 Scikit 学习模块。这个 SklearnClassifer 可以继承通过 Scikit Learn 导入的任何模型的属性。您所要做的就是使用特定的 Scikit 学习模块作为参数来启动 NLTK SkleanClassifier。
不同车型的 f1 分数如下所示。他们的表现多少有些相似。然而,这两个朴素贝叶斯模型表现稍好。
MNB: 0.845,BNB: 0.8447999,对数规:0.835,新币:0.8024,SVC: 0.7808
最后一步:建立一个集合模型
接下来,我想看看所有这些模型的预测能力是否结合在一起,也就是说,我们能否达到更好的分数?合乎逻辑的下一步是建立一个集合模型。集合模型结合了上述每个模型对每个评论的预测(投票),并使用多数投票作为最终预测。
为了避免不得不重新训练模型(因为每个模型需要大约 8 到 12 分钟来训练),我使用 pickle 存储了所有的模型。Pickle 是一个非常有用的 python 模块,它允许您在关闭内核之前保留可能需要很长时间创建的 python 对象。
为了构建集成模型,我构建了用分类器列表初始化的 EnsembleClassifier 类。重要的是,使用奇数个分类器作为集成的一部分,以避免出现平局。该类有两个主要的方法, classify :返回预测的标签, confidence :返回预测的置信度。这个度的衡量方法是(获胜票数)/总票数。
接下来,我使用 pickle 加载了所有的模型,初始化了一个集合模型对象,并将测试集中的特性列表输入到模型中。如下所示的集合模型的 f1 分数是 85%。比我们之前使用原始朴素贝叶斯模型获得的最高 f1 分数 84.5%略有增加。
同样的类也可以用来对一个评论进行实时分类。下面的函数接收单个评论,为该评论创建一个特征集,然后使用集成方法给出一个预测。输出为您提供了一个标签以及对该标签的信任度。
为了证明这一点,我从烂番茄中收集了一些关于惊奇队长的评论。我有意选取了两个不那么极端的评论和两个非常极端的评论来观察模型的表现。结果证明这个模型做得很好!该模型对不太对立的评论 text_a 和 text_c 不太确定,但是以高得多的置信度识别出对立的 text_b 和 text_d。
就是这样!
你可以在我的 GitHub 库和我用作指南的原始网页中找到这个项目的示例代码。在我的 Github 中,我已经包含了一个 live_classifier.py 文件和我训练过的模型作为 pickled 文件。一旦你克隆了回购,你就可以直接在你的终端上运行这个 live_classifier.py 文件,然后使用情感()函数对你在网上找到的任何评论进行分类。更好的是,自己补复习!
我现在对探测文本中的讽刺或挖苦感兴趣。讽刺是微妙的,甚至人类也很难理解。仅仅是为了它,我引用了我最喜欢的作家库尔特·冯内古特的一句话来测试集合模型,他以讽刺作品而闻名。事情是这样的:
“一切都很美好,没有任何伤害”——库尔特·冯内古特
如果我之前没有提到他的工作性质,我猜大多数人会认为这句话有积极的意义。然而,考虑到引用的上下文,人们可能会认为这个引用有更深的损失情绪。我的非常简单的情绪分析模型将该报价标注为 100%肯定*。*这并不奇怪,因为该模型一开始就没有被训练识别讽刺。
我想以下面这段关于情感分析的细微差别及其可靠性的引言来结束我的演讲。
情感分析会 100%准确或接近准确吗?
可能不会,但这并不意味着是一件坏事。这并不是因为人们不够聪明,无法最终制造出真正理解语言的计算机。相反,这真的是不可能的,因为很少 80%的人同意文本的情感。——http://sentdex.com/sentiment-analysis/
来源:
报价 1—http://breakthrough analysis . com/2012/09/10/typesofsentimental analysis/
图 1— Ebay 股票价格—http://send ex . com/how-accurate-is-sensition-analysis-for-stocks/
图 2——推特对 2016 年大选候选人的感受——https://modern data . plot . ly/elections-analysis-in-r-python-and-gg plot 2-9-charts-from-4-countries/
灵感—https://python programming . net/情操-分析-模块-nltk-教程/
每个数据分析师都应该知道的使用 Python 的基本数据分析技术,第二部分。
基本分析技巧
迈向经验丰富的分析师的第一步
这是基础数据分析技术系列的后续,每个数据分析师都应该知道。对于那些还没有读过第一部分的人,可以在这里找到它。
在第一部分中,我们介绍了初级数据分析师应该掌握的第一套技术,我们回顾了这些技术:基本过滤、多条件过滤、聚合和连接。
在这一部分中,我们将在第一部分的基础上,引入一组新的技术:
- 转换数据类型
- 透视数据
- 创建条件列
- 多功能聚合
我们将再次使用两个 Python 库进行分析,它们是:[**pandas**](http://pandas.pydata.org/pandas-docs/stable/)
& [**numpy**](https://docs.scipy.org/doc/numpy/reference/)**.**
如果您还没有安装这两个包,请使用下面的代码来安装它们:
蟒蛇:
**conda install -c anaconda numpy****conda install -c anaconda pandas**
PIP:
**pip install pandas****pip install numpy**
熊猫简介 dtypes 。
pandas 中的每一列(系列)都有一个分配给它的类型,这是指存储在该列中的数据类型。
熊猫最基本的类型如下:
- 对象,即文本数据,通常被称为
**string.**
- 浮点数,是具有小数精度的数(分数),通常称为浮点数。
- 整数,均为整数。
为什么在每一列中使用正确的类型至关重要?因为它将定义您可以对数据执行哪些操作。
例如,每当一个列被定义为**string**
时,您就不能对它们执行计算。例如,我们可以创建一个 pandas DataFrame,它有一个包含数字的 values 列,但是作为 object dtype。
如果我们用下面的代码检查这个数据帧的数据类型:
**print(df.types)**
我们得到以下输出:
dtypes
正如我们所看到的,我们的值列位于字符串形式的对象 dtype 中。因此,如果我们试图对这些值进行计算,我们将得到一个错误或错误的输出:
例如,取这些值的平方:
**df[‘Value’] ** 2**
会给出一个类型错误:
乘以 2 不会产生错误,但会给出错误的输出:
它不会引发错误的原因是,在 Python 中,我们可以通过相乘来连接字符串:
1.转换数据类型
因此,为了防止这些错误或错误的输出,我们必须将列的类型转换为正确的类型。在熊猫身上,我们有[**Series.astype**](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.Series.astype.html)
的方法。
此方法将您要转换成的 dtype 作为参数:
**df[‘Value’].astype(int)**
将该列转换为**int32**
:
**重要提示:*这不是【原地】*。所以我们没有覆盖数据帧中的原始列。为此,我们需要以下代码:
**df[‘Value’] = df[‘Value’].astype(int)**
如果我们再次检查数据帧的数据类型,我们可以看到转换正确进行:
1.1 转换日期时间
datetime 的转换不同于 pandas 中的其他 dtypes,因为您有许多不同的格式来存储日期:
- 29–04–2019,格式:日-月-年
- 29/04/2019 18:23:09,格式:日/月/年时:分:秒
- 04–29–2019,格式:年-月-日
这些只是许多可能性中的几种,还有一些格式会变得相当复杂。我们不会在这些系列中讨论这些,但是我们会在我的另一个系列中讨论,这个系列叫做用 Python 处理时间序列,它将很快出版。
在 pandas 中,有几种方法可以将对象类型转换为日期时间,现在我们来看两种最常用的方法:
[**pandas.to_datetime**](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.to_datetime.html)
[**Series.astype**](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.Series.astype.html)
在我们深入一些例子之前,我们必须首先用一个 datetime 列扩展我们的数据帧,我们可以用一个表示日期的字符串列表来做到这一点:
**df[‘Date’] = [‘2019–04–23’, ‘2019–04–24’, ‘2019–04–25’, ‘2019–04–26’]**
这给了我们下面的数据框架:
如果我们看看**print(df.dtypes)**
的产量。我们看到我们的**Date**
列是对象类型(字符串)。
方法 1: pd.to_datetime
**to_datetime**
函数接受一列作为输入,所以在我们的例子中是**df['Date']**
。总代码如下所示:
方法 2: Series.astype
在前面的例子中,我们使用了**astype**
将字符串转换成整数。这次我们想要的是 numpy 模块中的日期时间类型**datetime64[ns]**
,在这里可以找到。所以我们可以将它传递给我们的**astype**
方法,并将我们的字符串转换成日期时间:
2.透视数据
有时,需要创建特定的概视图,并以特定的方式汇总数据,以便提高可读性,此外,由于数据是聚合的,因此更容易看到有价值的信息。
旋转是上述场景中经常使用的技术之一。它使您能够聚合数据,同时转换数据,以便索引和列被原始数据帧中两列的分类数据所替换。
source: pandas docs
说够了,让我们在熊猫身上试试这个方法。首先,我们需要一个新的数据框架,这是我们自己创造的:
正如我们在数据框架中看到的,每个公司的每个部门都重复了三次,同样也重复了三次。我们可以在熊猫身上用[**pivot_table**](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.pivot_table.html)
的方法汇总这个数据。
该方法采用了许多参数,您可以在文档中找到这些参数,但是现在我们将研究其中的三个参数:
- **索引:**要设置为索引的列
- 列:对于每个唯一值,将被转换为新的列的列。
- **值:**哪一列将是每个单元格中的值。
因此,在我们的示例中,它将如下所示:
3。创建条件列
处理数据时,可能需要根据表中已有的列向表中添加更多信息。这些列被称为条件列,因为它们的信息依赖于其他列。
如果我们看一下关于公司和部门规模的表格:
我们可能想要区分大的部门和小的部门,并在名为 size_indicator 的新列中对其进行定义。
现在我们把大部门的门槛定在 100。因此,如果我们用 伪代码 写出我们的逻辑,它将如下所示:
**if Employee >= 100, then "Big"
else "Small"**
在 Python 中,我们可以以多种方式应用这种逻辑,我们将看看两种最常用的方法:
- numpy 在哪
- 列表理解
3.1 带有 numpy.where 的条件列
因为我们要使用 numpy 模块,所以我们必须首先导入这个模块:
**import numpy as np**
在 numpy 中有一个**.where**
方法,工作方式如下:
**np.where(condition, value if true, value if false)**
所以在我们的例子中我们必须传递下面的方法:
- 条件:
**df['Employees'] >= 100**
- 值为真:
**"Big"**
- 假值:
**"Small"**
因此,这将导致以下结果:
3.2 列表理解的条件列
列表理解是一种方法,与创建列表时使用和 if 循环的相比,这种方法可以显著提高代码速度。
and if 循环示例:
相同的列表创建,但是具有列表理解:
回到我们的数据,我们可以对 list comprehension 应用相同的逻辑来创建我们的 Size_indicator 列:
4.多功能聚合
在第一部分的中,我们已经用熊猫的 groupby 函数查看了聚合数据。我们能够在应用 groupby 时应用单个函数,例如组的max
或mean
。
在 pandas 中,当应用 groupby 来创建更广泛的概览时,您必须能够应用多种功能。
这可以通过使用 聚合 方法来实现。我们可以向该方法传递一个字典,其中包含我们想要应用的列名和函数:
如上所述,我们按公司分组,取员工的总和和平均值以及尺码指标的计数*。*
这就是第二部分的:每个数据分析师都应该知道的使用 Python 的基本数据分析技术。
你可以在我的 GitHub 上以 Jupyter 笔记本的形式找到这篇文章的代码:链接
对于任何问题或其他讨论,请随时评论!
期待第三部分,在那里我们会看到一些新的技术!
每个数据分析师都应该知道的基本数据分析技术,使用 Python。
基本分析技巧
迈向经验丰富的分析师的第一步
在我作为数据分析师的日常工作中,我会看到各种各样的数据和来自客户的各种各样的分析请求。我注意到的是,你在大多数项目中需要的某些基本技术,与你正在从事的项目类型无关。我相信每个数据分析师/科学家都应该对这些技术有很好的理解。因此,本文的目标是带领读者了解这些技术,并在基础层面上解释这些技术。
这些是我们将浏览和讨论的主题:
- 基本过滤
- 多条件过滤
- 聚合
- 连接
对于我们的分析,我们将利用 Python 中的[**pandas**](https://pandas.pydata.org/)
库。因此,如果您还没有安装这个库,请在您的命令提示符下使用以下代码之一来安装 pandas:
# If you use Anaconda, type the following in anaconda prompt
conda install -c anaconda pandas# If you use pip, type the following in command prompt
pip install pandas
此外,我假设你已经对 Python 和 pandas 库有了基本的了解。但是不要担心,如果你还没有接触到上述任何一个,我们将从头到尾检查一遍。
我们的数据集
为了能够通过提到的技术,我们需要数据。我们可以导入一个**csv**
文件或者一个**excel**
文件,但是现在我们保持简单,仅仅创建一个关于熊猫的小数据集。
下面的代码将生成一个熊猫数据帧:
这给了我们下面的数据框架:
DataFrame 1
从上面可以看出,它包含 ID、值和日期。
1.基本过滤
现在我们加载了 pandas 模块并创建了一个数据集,我们可以从第一项技术开始。当您想要基于列中的值获得数据的子集时,我们讨论的是过滤数据。
在熊猫身上,我们有多种方法做到这一点,现在我们来看看最常见的几种:
- 使用带方括号
**[]**
的布尔索引 - 通过
**.loc**
使用布尔索引
所以用方括号过滤如下:
pandas 中过滤背后的逻辑是将条件传递给方括号中的数据帧:
*df[condition]*
并给出了以下输出:
Filtering with square brackets
用过滤。loc* 看起来非常相似:***
正如预期的那样,它给出了相同的输出,因为我们应用了相同的滤波器
Filtering with .loc
首选用哪个?对于基本过滤器,正如我们在上面看到的,没有区别或偏好,它归结为你喜欢什么代码语法明智。但是当你想对你的数据进行更高级的选择时,**.loc**
提供了更复杂的选择和切片。但现在不用担心这个。
2.带条件过滤
我们应用了我们的第一个过滤器,它非常简单。但是,假设您想要应用一个具有多个条件的过滤器。我们如何在熊猫身上做到这一点?为此,我们研究了 Python 操作符。
2.1&运算符 例如,您要过滤所有**ID**
等于 C1、**Value**
大于 100 的行。
为了应用这个过滤器,我们必须用**&**
操作符链接两个条件。看起来像下面这样:
并将返回以下输出:
Filtering with and operator
正如预期的那样,我们返回了一行,因为只有这一行符合我们在过滤器中设置的条件。
2.2 |操作符
Python 中的**|**
运算符代表**or**
,如果满足其中一个条件就会返回**True**
。
我们可以通过应用以下过滤器来显示这一点:给我们所有**date**
晚于 2019–04–10或* **Value**
大于 100 的行。***
在 Python 代码中,这将如下所示:
并将返回以下输出:
Filtering with or operator
正如所料,返回的所有行的值都大于 100 或的日期在 2019-04-10 之后。
3.协议
有时需要聚合数据,以便创建特定的概视图或进行一些计算。在熊猫中,我们用**groupby**
来表示这一点。
那么 groupby 到底是什么?如果我们引用熊猫文献的话:
***“分组依据”指的是涉及以下一个或多个步骤的过程:
根据某些标准将数据分成组。
将功能独立应用于每组。
将结果组合成一个数据结构。
因此,基本上它是根据一些指标对你的数据进行分组,使你能够对这些分组采取一些行动。
3.1 Groupby #1:获取总和
让我们来看一个例子。假设我们想根据**ID**
得到每组的总数**value**
。这就像 Python 代码中的以下内容:
这将为我们提供以下输出:
Aggregation with sum
如果我们再看一下数据帧,我们会发现这是正确的:
Original dataframe
例如,ID A1 的总值是**100 + 120 = 220**
,这是正确的。
3.2 Groupby #2:获取最高日期
熊猫提供了一个大范围的功能,您可以在使用 groupby 后在您的组上使用。再来看一个。例如,我们可以通过使用.max()
函数获得每组的最高日期。
看起来会像这样:
并且会给我们以下输出:
Aggregation with max date
4.连接
连接是基于一个公共列以并排的方式组合两个数据帧。大多数时候这些列被称为**key columns**
。
术语**join**
源于数据库语言 SQL,之所以需要它是因为 SQL 数据库的数据建模大部分是通过使用关系建模来完成的。
连接有很多种类型,您的输出将基于您执行的连接类型。因为这是入门教程,我们就看最常见的:**inner join**
。在本系列的后面部分,我们将研究更复杂的连接。
内连接来源于**venn diagrams**
,代表两个集合的内(交集)部分。因此,当我们将它转换成我们的数据时,一个内部连接返回出现在两个数据帧中的行。**
4.1 我们的数据集
因为我们想要合并两个数据帧,所以我们将创建新数据。这两个假想数据集代表客户主表和订单表。
使用以下代码,我们创建了两个新的数据帧:
它们看起来如下:
dfA: Customers master
dfB: Orders
因此,我们可以对这些新数据进行的一个逻辑分析是,获取 orders 表中每个订单旁边的客户的姓名和城市。这是一个典型的**join**
问题,按行匹配两个数据帧,并用更多的列丰富数据。在这种情况下,我们的键列是**Customer_ID**
。
在熊猫中,我们使用[**merge**](https://pandas.pydata.org/pandas-docs/stable/user_guide/merging.html)
的方法进行接合。我们将向该方法传递以下参数:
- 您要连接哪个数据框架(dfA,dfB)。
- 什么是关键列(Customer_ID)。
- 要执行的联接类型(内部)。
我们可以在 merge 方法中使用比上面列出的更多的参数,但是现在这些已经足够了。
在 pandas 中,我们要执行的合并看起来像下面这样:
并且输出如我们所料,在每个对应的**customer_ID**
旁边添加了 name 和 city 列。
Inner join
这就是本部分的:每个数据分析师都应该知道的基本数据分析技术,使用 Python。
你可以在我的 GitHub 上以 Jupyter 笔记本的形式找到这篇文章的代码:链接
如果这篇文章对你有用,请考虑给这篇文章点个赞,并与朋友和/或同事分享。
对于任何问题或其他讨论,请随时发表评论。
在这里找到第二部分,我们在这里进行了更深入的探讨。
xarray 的基本数据结构
Photo by Faris Mohammed on Unsplash
Xarray 是一个 python 包,用于处理带标签的多维数组,包括高级分析和可视化功能。Xarray 深受熊猫的启发,在内部使用熊猫。虽然 pandas 是处理表格数据的一个很好的工具,但是当数据维数较高时,它可能会变得有些笨拙。Pandas 的主要数据结构是 Series(用于一维数据)和 DataFrame(用于二维数据)。它曾经有面板(用于三维数据),但在版本 0.25.0 中删除了。
假设读者熟悉熊猫,如果你不知道熊猫是什么,你应该在 xarray 之前查看一下。
为什么我应该使用 ND 数组?
在许多科学领域,需要将一个数据点映射到各种属性(称为坐标),例如,您希望将某个温度测量值映射到纬度、经度、高度和时间。这是 4 维!
在 python 中,使用 ND 数组的基本包是 NumPy 。Xarray 有一些内置特性,使得使用 ND 数组比 NumPy 更容易:
- xarray 不使用轴标签,而是使用命名维度,这使得选择数据和对维度应用操作变得容易。
- NumPy 数组只能有一种数据类型,而 xarray 可以在 ND 数组中保存异构数据。这也使得 NaN 操作更容易。
- 使用
obj.attrs
跟踪对象上的任意元数据。
数据结构
Xarray 有两种数据结构:
- 数据数组—用于单个数据变量
- 数据集—多个数据数组(数据变量)的容器
根据 CF 惯例,在数据变量和坐标之间有区别。Xarray 遵循这些约定,但是它主要是语义性的,您不必遵循它。我是这样看的:一个数据变量是感兴趣的数据,一个坐标是描述感兴趣的数据的标签。例如,纬度、经度和时间是坐标,而温度是数据变量。这是因为我们对测量温度感兴趣,其余的都是描述测量结果(数据点)。在 xarray 文档中,他们说:
坐标表示常量/固定量/独立量,不同于属于数据的变化量/测量量/相关量。
O 凯,来看看一些代码吧!
# customary imports
import numpy as np
import pandas as pd
import xarray as xr
首先,我们将创建一些玩具温度数据来玩:
我们生成了一个随机温度值的数组,以及坐标纬度和经度(二维)的数组。首先,让我们看看如何在 pandas 中表示这些数据:
df = pd.DataFrame({"temperature":temperature, "lat":lat, "lon":lon})
df
Output of the above cell
我们将从这些数据中创建一个 DataArray ,让我们来看看四种方法:
- 来自熊猫系列
- 来自一个熊猫的数据框架
- 使用 DataArray 构造函数
- 使用带有投影坐标的 DataArray 构造函数
从序列创建数据数组
我们将创建一个熊猫系列,然后是一个数据数组。由于我们希望在数据中表示两个维度,因此我们将创建一个具有两级多索引的序列:
idx = pd.MultiIndex.from_arrays(arrays=[lat,lon], names=["lat","lon"])s = pd.Series(data=temperature, index=idx)
s# use from_series method
da = xr.DataArray.from_series(s)
da
Output of the above cell
这是数据数组打印出来的样子。
从数据帧创建数据数组
我们可以为 DataArray 构造函数提供一个熊猫数据帧。它会将数据框的索引视为第一维,将列视为第二维。如果索引或列有多个级别,xarray 将创建嵌套维度。
由于我们希望纬度和经度作为我们的维度,实现这一点的最聪明的方法是使用纬度作为索引,经度作为列来透视我们的数据框:
df_pv = df.pivot(index="lat", columns="lon")# drop first level of columns as it's not necessary
df_pv = df_pv.droplevel(0, axis=1)df_pv
Output of the above cell
数据透视后,我们可以通过为 DataArray 构造函数提供透视数据框来轻松创建 DataArray:
da = xr.DataArray(data=df_pv)
da
Output of the above cell
使用构造函数创建 DataArray
我们已经看到了从 pandas 对象创建 DataArray 的两种方法。现在,让我们看看如何手动创建数据数组。因为我们希望在数据中表示二维,所以数据应该以二维数组的形式出现,这样我们就可以将它直接传递给 DataArray 构造函数。
我们将使用透视数据框中的数据,然后我们需要明确指定坐标和维度:
Output of the above cell
这里需要注意的重要一点是,坐标数组必须是一维的,并且具有它们所代表的维度的长度。我们有一个(4,4)形状的数据数组,所以我们为构造函数提供了两个坐标数组。每个都是一维的,长度为 4。
使用带有投影坐标的构造函数创建 DataArray
我们将检查用投影坐标创建 DataArray 的最后一种方法。它们在某些情况下可能是有用的,但是它们有一个缺点,即坐标没有明确的可解释性。使用它们的最大的优势是我们可以传递给 DataArray 构造函数相同形状的数组,用于数据和坐标,而不必考虑旋转我们的数据。
在我们的例子中,我们有温度数据,我们有两个维度:纬度和经度,所以我们可以在一个任意形状的(不需要旋转)的二维数组中表示我们的数据,然后为构造函数提供两个相同形状的坐标数组来表示纬度和经度:
下面,请注意在 DataArray 构造函数中指定坐标的方式。这是一个字典,它的键是坐标的名称,它的值是元组,它们的第一项是维度列表,第二项是坐标值。
da = xr.DataArray(data=temperature,
coords={"lat": (["x","y"], lat),
"lon": (["x","y"], lon)},
dims=["x","y"])da
Output of the above cell
注意它说 x 和 y 是没有坐标的维度。还要注意,lat 和 lon 旁边没有星号,因为它们是无量纲坐标。
三维
现在让我们创建另一个维度!让我们创建 2 天的温度数据,不是 1 天而是 2 天!
像以前一样,每天我们都需要一个二维(纬度和经度)温度值数组。为了表示 2 天的数据,我们希望将每日数组堆叠在一起,形成一个三维数组:
现在,我们将数据传递给 DataArray 构造函数,并使用投影坐标:
da = xr.DataArray(data=temperature_3d,
coords={"lat": (["x","y"], lat),
"lon": (["x","y"], lon),
"day": ["day1","day2"]},
dims=["x","y","day"])da
Output of the above cell
我们也可以使用一个具有 3 级多索引的 pandas 系列来创建同样的东西。要创建一个系列,我们需要将数据扁平化,这意味着使其一维化:
# make data 1-dimensional
temperature_1d = temperature_3d.flatten("F")
lat = lat.flatten()
lon = lon.flatten()day = ["day1","day2"]
现在,我们将创建一个具有 3 级多索引的系列:
Output of the above cell
最后,我们将使用 from_series 方法创建一个 DataArray:
da = xr.DataArray.from_series(s)
da
Output of the above cell
资料组
到目前为止,我们只处理了温度数据。让我们添加压力数据:
现在我们将创建一个数据集(不是一个数据数组),以温度和压力作为*数据变量。*对于投影坐标, data_vars 参数和 coords 参数都需要一个类似于 DataArray 的 coords 参数的字典:
Output of the above cell
我们还可以为每个数据变量创建一个数据数组,然后从数据数组创建一个数据集*。*让我们使用 from_series 方法为温度和压力创建两个数据数组,就像我们对三维情况所做的那样:
现在,我们将使用这两个数据数组创建一个数据集:
ds = xr.Dataset(data_vars={"temperature": da_temperature, "pressure": da_pressure})ds
结论
这是对 xarray 数据结构的快速介绍。xarray 还有更多的功能,比如索引、选择和分析数据。我不会在这里涉及这些,因为我想保持这个教程简单。我鼓励你看一看 xarray docs,并尝试使用它。希望这篇文章对你有用!
ETF 的基本数据争论和可视化
概观
正如前一篇文章所承诺的,我们最终会完成一些编码示例。在选择一个与你分享的话题时,我实际上是从进行深度工作中抽出 10 分钟的休息时间得到的灵感——这是一种来自加州新港*【1 }*的生产力技巧。对于那些需要复习的人来说,深入的工作是:
一种在不受干扰的环境中进行的工作,这种工作可以集中你的注意力,创造出其他人很难复制的更大价值。
我个人会努力 40-50 分钟,然后需要 10-15 分钟的休息。其中一次休息时,我的同事们在谈论市场和比特币。boom——这是本文的下一个主题。今天,我们将讨论一些基本的时序数据争论和 Python 中的可视化。然后我们会用均线交易策略来结束它。
现在只是为了记录,我不是说这个策略是好的或任何东西,但只是想告诉你在一些数据操作和可视化之后你能做什么。管理方面的问题解决了,让我们开始吧!
时间序列和数据争论的术语
学习新事物时,我总是喜欢从头开始。这样,我们都在同一页上,以后可以构建更复杂的模型。基本上,我宁愿解释得多一点也不愿意解释得不够多。就我个人而言,我总是讨厌被弄糊涂,当有人做了一个巨大的逻辑跳跃。
时间序列的定义:时间序列是一种在一段时间内获取的数据类型,已知数据点彼此相关*【2】*。时间序列的例子包括股票价格、马丘比丘的每日温度、小海龟跑进海洋的速度等。
数据角力的定义:数据角力是将原始数据转化为更干净的数据,使模型更好的过程*【3】*。例如,你从 Twitter 上获得大量的 tweet 数据。将原始文本数据放在机器学习 NLP 模型中不会有太好的效果。但是如果你删除不必要的单词,清理特殊字符,为输入和输出构建数据,等等。,那么你就可能有一个像样的模型,可以预测或分类。
基本时序数据争论和可视化
在 Python 中,标准的做法是首先导入包,然后为它们指定快捷方式以节省时间。比如熊猫从现在开始就要叫 pd 了。
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
现在,让我们将 NASDAQ etf 从 csv 导入到一个 dataframe 中(就像 Excel 中的工作表一样)【4】。
qqq = pd.DataFrame.from_csv(‘QQQ Historical Data.csv’)print(qqq.head())
检查数据是什么样的总是好的,这样你就对你正在分析的东西有了更好的感觉。以下是股票和 etf 数据的常见格式:
Price Open High Low Vol. Change %
Date
2014-12-31 103.25 104.50 104.91 103.10 24.37M -1.03%
2015-01-02 102.94 103.76 104.20 102.44 31.31M -0.30%
2015-01-05 101.43 102.49 102.61 101.14 36.52M -1.47%
2015-01-06 100.07 101.58 101.75 99.62 66.21M -1.34%
2015-01-07 101.36 100.73 101.60 100.48 37.58M 1.29%print(qqq.shape)
shape 命令向我们显示了数据帧中的行数和列数,按此顺序排列。
(1127, 6)print(qqq.describe())
Describe 向我们显示了一般的汇总统计数据。
Open High Low Price
count 506.000000 506.000000 506.000000 506.000000
mean 102.880198 103.857688 101.770731 102.864565
std 17.100460 17.089441 17.049718 17.081374
min 74.040000 74.830000 72.000000 74.050000
25% 86.740000 87.715000 85.620000 86.730000
50% 105.260000 106.300000 104.040000 105.075000
75% 117.812500 118.677500 116.722500 117.745000
max 133.500000 133.500000 132.220000 133.280000
loc vs . iloc
好了,这里有一个小命令总是让我困惑,当我第一次开始学习 Python 的时候。当我们使用 loc 时,它得到一个带标签的行,而 iloc 得到一个带索引的数据点。以下是每种方法的示例:
print(qqq_2015.loc[‘2015–03–16’])Price 106.7
Open 105.73
High 106.74
Low 105.62
Vol. 25.67M
Change % 1.29%
Name: 2015-03-16 00:00:00, dtype: objectprint(qqq.iloc[0, 0])103.25
以下是如何获取特定时间序列的示例代码,在本例中是 QQQ etf 的 2015 年全年。
qqq_2015 = qqq.loc[‘2015–01–01’:’2015–12–31']
python 中的绘图非常简单。您可以告诉它您想要的绘图大小、要绘制的时间序列以及何时显示,如下所示:
plt.figure(figsize=(10, 8))
qqq[‘Price’].plot()
plt.show()
要在 dataframe 中创建一个新列,可以使用与创建新变量类似的模式。下面我们将创建一个获取差价和每日回报的新专栏。做这两件事的目的是看我们的交易策略一天能赚多少或亏多少。
qqq[‘PriceDiff’] = qqq[‘Price’].shift(-1) — qqq[‘Price’]qqq[‘Return’] = qqq[‘PriceDiff’] /qqq[‘Price’]
交易策略的另一部分是告诉算法价格的方向。有了这个形态,策略就能知道 ETF 或股票是涨还是跌。由此,我们可以告诉策略下一步该做什么——即交易价格上涨(多头)或下跌(空头)。
qqq[‘Direction’] = [1 if qqq[‘PriceDiff’].loc[ei] > 0 else 0 for ei in qqq.index ]print(‘Price difference on {} is {}. direction is {}’.format(‘2015–01–05’, qqq[‘PriceDiff’].loc[‘2015–01–05’], qqq[‘Direction’].loc[‘2015–01–05’]))
做均线策略的最后一个要素是做均线,可以用。滚动()和。平均值()。在这里,我们将使用 50 移动平均线,它显示了过去 50 天的平均收盘价。下面是带有情节的代码:
qqq[‘ma50’] = qqq[‘Price’].rolling(50).mean()
plt.figure(figsize=(10, 8))
qqq[‘ma50’].loc[‘2015–01–01’:’2015–12–31'].plot(label=’MA50')
qqq[‘Price’].loc[‘2015–01–01’:’2015–12–31'].plot(label=’Price’)
plt.legend()
plt.show()
建立移动平均线交易策略
我们拥有交易策略的所有要素。从上面的代码添加,我们可以制定一个 50 简单移动平均线(SMA)和 100 简单移动平均线(SMA)的交叉策略。一旦 50 日均线超过 100 日均线,我们将交易 etf 将走高。一旦 50 日均线跌破 100 日均线,我们将关闭我们的多头或根本不交易。
qqq[‘MA50’] = qqq[‘Price’].rolling(50).mean()
qqq[‘MA100’] = qqq[‘Price’].rolling(100).mean()
qqq = qqq.dropna()
下面的代码设置了我们购买的股票数量,并允许我们绘制交易期间的回报图:
qqq[‘Shares’] = [1 if qqq.loc[ei, ‘MA50’]>qqq.loc[ei, ‘MA100’] else 0 for ei in qqq.index]qqq[‘Close1’] = qqq[‘Price’].shift(-1)
qqq[‘Profit’] = [qqq.loc[ei, ‘Close1’] — qqq.loc[ei, ‘Price’] if qqq.loc[ei, ‘Shares’]==1 else 0 for ei in qqq.index]
qqq[‘Profit’].plot()
plt.axhline(y=0, color=’red’)
第一次看到这个,很难判断我们是否盈利。另一种方式来看待它是从我们的股票交易中获得的价格。cumsum()代码—创建一个新列并绘制它:
qqq[‘wealth’] = qqq[‘Profit’].cumsum()qqq[‘we alth’].plot()
plt.title(‘Total money you win is {}’.format(qqq.loc[qqq.index[-2], ‘wealth’]))
上面的图显示了我们从交易 1 份 QQQ etf 中获得了多少钱,因此我们交易 1 份获得了 48.03 美元(43.12%的总回报)。43.12%的回报率并不差,但与同期仅买入并持有 etf 而不交易相比(总回报率为 68.57%),这有点糟糕。
结论
恭喜你,你刚刚经历了一些数据争论,可视化,并建立了一个移动平均线交易策略!
图片取自随机的 YouTube 个人资料【5】
当你坐下来想想你学了些什么,那真的很多。您学习了如何从 CSV 导入数据,制作数据框架,查看行,查看数据点,创建新列,一些统计函数,并绘制出简单的移动平均线交易策略。我知道我在数据科学方面比在财务方面解释得更多。如果有什么不清楚的,在下面评论或者给我发消息!
在以后的文章中,我们将结合数据科学的商业思想,进行更多的编码演练。
特别感谢万博士,我修改了他的一个课程的想法,以构建本文*【6】*。
免责声明:本文陈述的所有内容均为我个人观点,不代表任何雇主。投资有很大的风险。在做出任何投资决定之前,请咨询您的财务专家。最后,这篇文章包含附属链接,谢谢你的支持。
[1] C .纽波特,深度工作:在纷乱的世界中专注成功的规则 (2016),大中央出版社
[2] NIST,时间序列分析导论,https://www . ITL . NIST . gov/div 898/handbook/PMC/section 4/pm C4 . htm
[3] Trifacta,什么是数据角力?,https://www.trifacta.com/data-wrangling/
www.investing.com,景顺 QQQ 信托历史数据,https://www . investing . com/ETFs/power shares-QQ QQ-Historical-Data
[5]https://www.youtube.com/channel/UCkAqayQ4Q8XD6CwWSxD6DbA 的 TT·克尔,YouTube 个人资料,
[6] X .万,构建简单交易策略,https://www . coursera . org/learn/python-statistics-financial-analysis
基本集成学习(随机森林,AdaBoost,梯度推进)-一步一步解释
前三种基于树的集成学习算法的逐步解释。
我们都这样做。在我们做出任何重大决定之前,我们会询问人们的意见,比如我们的朋友、家人,甚至我们的狗/猫,以防止我们有偏见😕或者不理智😍。
模型也是这样。单个模型遭受偏差或方差是很常见的,这就是为什么我们需要集成学习。
E nsemble 学习,一般来说,是一种基于许多不同模型进行预测的模型。通过组合单独的模型,集合模型趋向于更加灵活🤸♀️(偏差较小)和 data-sensitive🧘♀️(方差较小)。
两种最流行的集成方法是打包和助推。
- **装袋:**并行训练一堆个体模型。每个模型由数据的随机子集训练
- Boosting: 按顺序训练一堆个体模型。每个单独的模型都从前一个模型所犯的错误中学习。
有了对什么是集成学习的基本了解,让我们来种一些“树”🎄。
以下内容将逐步解释随机森林、AdaBoost 和梯度增强,以及它们在 Python Sklearn 中的实现。
随机森林
andom forest 是一个集合模型,使用 bagging 作为集合方法,决策树作为个体模型。
让我们仔细看看这个魔法🔮的随机性:
步骤 1: 从训练集中选择 n 个(例如 1000 个)随机子集
第二步:训练 n 个(例如 1000 个)决策树
- 一个随机子集用于训练一个决策树
- 每个决策树的最佳分割基于随机的特征子集(例如,总共 10 个特征,从 10 个特征中随机选择 5 个进行分割)
步骤 3: 每棵单独的树独立地预测测试集中的记录/候选者。
第四步:进行最终预测
对于测试集中的每个候选人,随机森林使用具有多数票的类(例如猫或狗)作为该候选人的最终预测。
当然,我们的 1000 棵树就是这里的议会。
自适应增强
AdaBoost 是一个增强集成模型,特别适合决策树。Boosting 模型的关键是从以前的错误中学习,例如错误分类数据点。
通过增加错误分类数据点的权重,daBoost 从错误中学习。
让我们举例说明AdaBoost 如何适应。
步骤 0: 初始化数据点的权重。如果训练集有 100 个数据点,那么每个点的初始权重应该是 1/100 = 0.01。
第一步:训练决策树
第二步:计算决策树的加权错误率(e) 。加权误差率(e) 是指所有预测中有多少是错误的,你可以根据数据点的权重来区别对待错误的预测。权重、越高,在(e)的计算过程中,相应的误差将被加权。
步骤 3: 计算该决策树在集合中的权重
这棵树的权重=学习率* log( (1 — e) / e)
- 树的加权错误率越高,😫在随后的投票中,给予该树的决策权越少
- 树的加权错误率越低,😃在以后的投票中,该树将被给予更高的决策权
步骤 4: 更新错误分类点的权重
每个数据点的权重=
- 如果模型得到的数据点正确,重量保持不变
- 如果模型得到的这个数据点是错误的,那么这个点的新权重=旧权重* np.exp(这棵树的权重)
注意:树的权重越高(该树执行的越准确),该树的误分类数据点将获得越多的提升(重要性)。在所有错误分类的点被更新之后,数据点的权重被归一化。
第五步:重复第一步(直到达到我们设定的训练树的数量)
第六步:进行最终预测
AdaBoost 通过将(每棵树的)权重相加乘以(每棵树的)预测来进行新的预测。显然,权重越高的树对最终决策的影响力越大。
梯度推进
梯度推进是另一种推进模式。记住,推进模型的关键是从以前的错误中学习。
radient Boosting 直接从错误——残差中学习,而不是更新数据点的权重。
下面举例说明梯度提升是如何学习的。
步骤 1: T 雨决策树
步骤 2: 应用刚刚训练好的决策树进行预测
第三步:计算该决策树的残差,将残差保存为新的 y
第四步:重复第一步(直到达到我们设定的训练树数)
第五步:进行最终预测
梯度推进通过简单地将(所有树的)预测相加来进行新的预测。
Python sk learn 中的实现
下面是 Python Sklearn 中上述三种方法的简单实现。
**# Load Library** from sklearn.datasets import make_moons
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier,AdaBoostClassifier,GradientBoostingClassifier**# Step1: Create data set**
X, y = make_moons(n_samples=10000, noise=.5, random_state=0)**# Step2: Split the training test set**
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)**# Step 3: Fit a Decision Tree model as comparison**
clf = DecisionTreeClassifier()
clf.fit(X_train, y_train)
y_pred = clf.predict(X_test)
accuracy_score(y_test, y_pred)**OUTPUT: 0.756****# Step 4: Fit a Random Forest model, " compared to "Decision Tree model, accuracy go up by 5%** clf = RandomForestClassifier(n_estimators=100, max_features="auto",random_state=0)
clf.fit(X_train, y_train)
y_pred = clf.predict(X_test)
accuracy_score(y_test, y_pred)**OUTPUT: 0.797****# Step 5: Fit a AdaBoost model, " compared to "Decision Tree model, accuracy go up by 10%**
clf = AdaBoostClassifier(n_estimators=100)
clf.fit(X_train, y_train)
y_pred = clf.predict(X_test)
accuracy_score(y_test, y_pred)**OUTPUT:0.833****# Step 6: Fit a Gradient Boosting model, " compared to "Decision Tree model, accuracy go up by 10%** clf = GradientBoostingClassifier(n_estimators=100)
clf.fit(X_train, y_train)
y_pred = clf.predict(X_test)
accuracy_score(y_test, y_pred)**OUTPUT:0.834*****Note: Parameter - n_estimators stands for how many tree we want to grow***
总的来说,集成学习是非常强大的,不仅可以用于分类问题,也可以用于回归。在这篇博客中,我只使用决策树作为集成方法中的个体模型,但是其他的个体模型(线性模型,SVM 等。)也可以应用在打包或增强集合中,以获得更好的性能。
这个博客的代码也可以在我的 GitHub 链接中找到。
请在下面留下任何评论、问题或建议。谢谢大家!
实现更高效机器学习的基本特征工程
Photo by Lenny Kuhne on Unsplash
如果我问你一个问题,比如机器学习难吗,你可能会说是的。但是机器学习并不是一件难事。因为您可能需要的每一个算法都是预先实现的,并以包的形式提供(主要在 python 库中)。您可以导入您想要的任何库,然后开始构建模型。但是几乎所有这些算法都是对数值数据或二进制数据进行操作的。所以你不能直接输入一个特征集就指望算法能起作用。尤其是当数据集中有非数字数据时,机器学习模型不会像预期的那样工作,并且准确率非常低。我们必须清理这些非数字数据,提取有用的数字嵌入。清洁不仅如此。我们必须处理缺失值、共线特征、零重要特征等等。但这还不够。我们必须从清理后的特征中为机器学习模型导出更有意义的特征。您可以在本文后面找到这些主题的更多细节。所有这些都被称为特征工程。特征工程是机器学习中最重要也是最困难的任务。
处理非数字数据
首先,您必须将非数字特征转换成数字格式。有两种类型的字符串数据,即分类数据和连续数据。
分类特征
分类数据/特征是具有有限数量标签或类别的变量。根据性质,分类数据可以分为序数数据和名词性数据。当值有自然顺序时,属性就是序数属性。例如,功能关键级别可以是低、中或高,并且有一定的顺序。当我们不能推导出任何类别之间的顺序或关系时,它被称为一个名义特征。例如,一个国家的省或区没有顺序。然而,这是一个分类属性,因为一个国家有固定数量的省或区。
将分类数据转换成数字格式的流行技术很少。即使对于像决策树和随机森林这样可以处理分类数据的算法,编码也是有意义的。
1.标签编码
标签编码将非数字标签转换为数字标签。对于有序分类数据来说,这种编码类型更好,如果数据集中的分类要素数量巨大,您可能会经常使用标签编码。因为像热编码这样的编码技术会导致高内存消耗。您可以轻松地从 python scikit 学习库中导入标签编码器。但是,标签编码会使 ml 模型误解数据中存在某种排序。为了克服这个问题,我们可以使用一热编码。
2.一个热编码
在一个热编码中,我们为分类属性的每个类创建虚拟列。对于每个伪属性,类的存在表示为 1,不存在表示为 0。所以这些数字用 1 和 0 来表示。我们使用一个热编码和名义数据。您可以很容易地从 python scikit 学习库中导入一个 hot 编码器。
3.二进制编码
在二进制编码中,首先将类转换成数字格式,然后再转换成相应的二进制字符串。然后,二进制数字被分成单独的列。
4.张量流特征列
特征列将输入数据与您的机器学习模型联系起来。Tensorflow 提供了一个包含九个函数的模块来处理特征列。下面列出了这些功能。从 tensorflow 官方文档中可以找到很好的解释(点此)。
- 数字列
- 桶形塔
- 分类标识列
- 分类词汇栏(词汇文件)
- 分类词汇栏(词汇列表)
- 散列列
- 交叉柱
- 指示器列
- 嵌入柱
连续特征
在数学中,如果一个变量可以取最小值和最大值之间的任何值,那么它被称为连续变量。同样的事情也适用于这里。但是,如果变量没有最小值和最大值,那么我们可能会遇到麻烦,因为变量可能会为机器学习模型创建稀疏特征。稀疏特征对机器学习模型没有任何意义,在我看来,最好是摆脱它们。但问题是从数据集中删除特征会降低 ml 算法的准确性。因此,我们应该尽一切可能将该特性转化为有用的格式。如果没有选项,那么最好放弃该功能,因为它使机器学习模型比使用它更准确。
在某些情况下,我们也许能够从字符串数据中导出更有用的数字特征。例如,考虑一个带有 ip 地址的特性列。作为一个特性,ip 地址是字符串数据。我们可以通过将 ip 地址分成四个二进制八位数并应用简单的数学运算来将其转换成整数。这个整数数据可以被输入到一个 ml 模型中,并期望它能很好地工作。但是我们可以通过从 ip 获取经度和纬度来获得一个更有用的特性。您可以使用免费的 API 或数据库或任何其他技术来将 ip 转换为相关坐标。在我看来,我们或许可以通过获取地理位置坐标,而不是简单地将 ip 转换为整数,来为机器学习模型导出更有意义的特征。
正如您可能理解的那样,处理连续变量是一项有点困难的任务。然而,有一些流行的技术来处理连续变量。下面列出的只是其中的一些,你可能会发现更多的技巧。
1.变量宁滨
宁滨将连续的数字变量分组。基本上,这样做是为了发现难以识别的变量模式。例如,年龄列表可以根据取值范围划分为多个区间。然而,最重要的是箱子的大小。你可以根据你的假设决定一个最佳的箱子大小。
2.正常化
规范化用于将数字列的值更改为通用的比例。在数学中,你可能会发现正态分布的数据很容易阅读和解释。许多机器学习算法假设其数据点呈正态分布。特别是在 K-means 这样的聚类技术中,归一化非常有帮助。一种常用的归一化技术是获取 z 值。
3.理解业务逻辑
在某些情况下,数据本身并不能说明任何模式。但是通过理解业务逻辑或领域可以帮助你找出更有意义的特性。这就是为什么你可能经常在一个机器学习小组中找到一个或多个领域专家。他们非常了解业务领域及其运作方式,能够从现有特征中创造出更有意义的特征,从而为机器学习模型衍生出可识别的模式。这种技术不仅适用于连续数据,也适用于其他类型的数据。
特征选择
在处理完非数字特征之后,你必须进行特征选择。特征选择是选择属性的过程,这些属性可以使 ml 模型更加精确,并消除不相关的属性。下面列出了一些最常见的特征选择技术。
1.缺失值处理
当我们收集真实世界的数据时,可能会有缺失值。这导致数据集的列中包含缺失值。缺失值可能导致机器学习模型识别数据点之间的错误模式,并给出错误和不太准确的预测。通过将数据集读取为 pandas 数据框,您可以很容易地发现数据集是否有缺失值。熊猫图书馆有很多内置的方法来轻松处理 csv 数据。下面列出了一些处理缺失值的技巧。
一.删除
这是处理缺失值的最基本的方法。基本思想是删除任何缺失值超过指定阈值的列。然而,删除列将减少机器学习模型的特征数量,并导致模型不太准确。在我看来,保留数据比删除数据更好。
二。使用回填或前填
该方法将为缺失值分别传播下一个或上一个值。但是,如果下一个或上一个值也是 NaN 或不可用,则 NaN 值将保持不变。
三。常量值插补
该方法将使用一个全局常量来填充所有缺失的值。当尝试和预测缺失值没有意义时,可以使用常量值插补。主要的缺点是 ml 模型的性能会下降。
四。均值、中值和众数插补
我们可以用列的平均值或中值来填充缺失值,而不是用单个常数来填充缺失值。这种方法单独处理每个变量,忽略与其他变量的任何关系。
动词 (verb 的缩写)使用预测模型
有时,我们使用机器学习模型通过识别属性之间的关系来预测缺失值。通过考虑上述技术,这是一种更加有效的填补缺失值的方法。但是准确性完全取决于你的预测模型有多好。
2.共线特征
共线特征是高度依赖于另一个特征的特征。由于高方差和较低的模型可解释性,共线特征导致综合性能下降。基于特定的相关系数来识别相关水平**。我们移除相关系数大于指定阈值的特征。但是,我们应该从两个相关的特征中只移除一个特征,因为移除特征会降低机器学习模型的准确性。有两个著名的系数,皮尔逊相关系数和斯皮尔曼相关系数。这两者都可以从 scipy python 库中导入。皮尔逊相关可用于具有线性关系的连续变量。Spearman 相关可用于具有非线性关系的变量或有序分类变量。我们可以使用 python seaborn 热图轻松地可视化变量之间的相关性。**
3.零/低重要特性
零重要和低重要特征对于机器学习模型没有任何意义。可以使用梯度推进机器学习模型(GBM)来计算特征的重要性。您可以移除不太重要的特征,因为它们对于 ml 模型进行预测并不重要。然而,这种方法只适用于有非确定性标记数据的监督模型,如果我们要使用基于树的方法进行预测。但更重要的是,你应该试着从这些特性中衍生出新的有意义的特性。可能存在这样的情况,即使特征本身不太重要,也有可能导出更有意义的特征。
4.单一独特价值特征
这里,我们删除了在整个数据集中具有单一唯一值的列,因为机器学习模型无法从这些列中得出任何结论。
结论
我简要描述了一些流行的基本特征工程技术,包括处理非数字数据和特征选择技术。使用 python scikit learn 库和 pandas 库可以轻松实现上述所有技术。但是特征工程是一个非常广泛的话题,还有更多的内容要涵盖。特征工程中最重要的技术之一是从现有特征中派生出更有意义的新特征。几乎所有机器学习模型的成功都取决于我们如何向我们的模型呈现数据。我们可以通过获取现有特征之间的比率、取平均值或中值、交叉特征等来导出新特征。例如,如果我们正在为一个分类问题进行特征工程,新的特征应该以一种模型可以容易地区分数据点之间的聚类的方式出现。
最后但同样重要的是,有许多方法可以实现自动化特征工程。自动化特征工程旨在从现有特征中自动创建许多候选特征。从这些生成的特征中,我们可以选择最好的来训练我们的模型。Featuretools 是一个用于自动化特征工程的开源 python 框架。
创建时间序列预测的基本原则
解释创建时间序列预测的基本步骤。
Photo by Adrian Schwarz on Unsplash
我们被随处可见的模式所包围,人们可以注意到与天气有关的四季模式;指交通流量的高峰时间模式;在你的心跳中,在股票市场的股票中,也在某些产品的销售周期中。
分析时间序列数据对于检查这些模式和创建未来预测非常有用。创建这些预测有几种方法,在这篇文章中,我将介绍最基本和传统的方法。
所有代码都是用 Python 写的,另外,任何附加信息都可以在我的 Github 上看到。
那么让我们开始评论分析时间序列的初始条件:
平稳序列
平稳时间序列的统计特性,如均值、方差和自相关,在一段时间内相对恒定。因此,非平稳序列是其统计特性随时间而变化的序列。
在开始任何预测建模之前,有必要验证这些统计属性是否恒定,我将在下面解释这些要点:
- 常数平均值
- 恒定方差
- 自相关的
常数平均值
平稳序列有一个相对恒定的平均值,没有看涨或看跌趋势。有一个恒定的平均值,周围有小的变化,更容易推断未来。
在有些情况下,方差相对于平均值较小,使用它来预测未来可能是一个很好的指标,下面的图表显示了相对恒定的平均值与方差之间的关系:
在这种情况下,如果序列不是稳定的,对未来的预测将是无效的,因为平均值周围的变化会显著偏离,如下图所示:
在上面的图表中,很明显有一个看涨的趋势,均值在逐渐上升。在这种情况下,如果用平均值来预测未来,误差会很大,因为预测价格总是低于实际价格。
恒定方差
当序列具有恒定的方差时,我们对相对于平均值的标准方差有一个概念,当方差不恒定时(如下图),预测在某些时期可能会有较大的误差,这些时期将是不可预测的,预计方差将随着时间的推移保持不变,包括在未来。
为了减少方差影响,可以应用对数变换。在这种情况下,也可以使用指数变换,如 Box-Cox 方法,或使用通货膨胀调整。
自相关序列
当两个变量在一段时间内相对于标准偏差具有相似的变化时,你可以说这些变量是相关的,例如,当体重随着心脏疾病而增加时,体重越大,心脏问题的发生率就越高。在这种情况下,相关性为正,图表看起来如下所示:
负相关的一个例子是:对工作安全措施的投资越大,与工作相关的事故就越少。
以下是几个具有相关级别的散点图示例:
source: wikipedia
当主题为自相关时,表示某些之前的时段与当前时段存在相关性,具有这种相关性的时段被命名为*,例如,在一个每小时都有测量值的序列中,今天 12:00 的温度与 24 小时前 12:00 的温度非常相似。如果你比较这 24 宫时间范围内的温度变化,将会有一个自相关,在这种情况下,我们将有一个与第 24 宫时间范围的自相关。*
自相关是用单个变量创建预测的条件,因为如果没有相关性,就不能使用过去的值来预测未来,当有多个变量时,就可以验证因变量和自变量的滞后之间是否存在相关性。
如果一个序列不具有自相关性,那么它就是一个具有随机和不可预测序列的序列,进行预测的最佳方法通常是使用前一天的值。下面我会用更详细的图表和解释。
从这里我将分析 Esalq 的每周含水乙醇价格(这是在巴西谈判含水乙醇的价格参考),数据可以在这里下载。
价格以每立方米巴西雷亚尔(BRL/m3)为单位。
在开始任何分析之前,让我们在训练和测试集上分割数据
在培训和测试的基础上划分数据
当我们要创建时间序列预测模型时,将数据分成两部分至关重要:
训练集:这些数据将是定义模型系数/参数的主要依据;
测试集:这些数据将被分离出来,不会被模型看到,以测试模型是否工作(一般情况下,这些值与一个向前移动的方法进行比较,最后测量平均误差)。
测试集的大小通常约为总样本的 20%,尽管该百分比取决于您拥有的样本大小以及您希望提前多长时间进行预测。理想情况下,测试集应该至少与所需的最大预测范围一样大。
与其他预测方法不同,例如不受时间影响的分类和回归,在时间序列中我们不能用来自数据任何部分的随机样本来划分训练和测试数据,我们必须遵循序列的时间标准,其中训练数据应该总是在测试数据之前。
在这个 Esalq 含水价格的例子中,我们有 856 周,我们将使用前 700 周作为训练集,最后 156 周(3 年~ 18%)作为测试集:
从现在开始,我们将只使用训练集来进行研究,测试集将只用于验证我们将做出的预测。
每个时间序列都可以分解成 3 个部分:趋势、季节性和残差,这是从序列中去除前两个部分后剩余的部分,低于这些部分的分离:
很明显,该系列呈上升趋势,峰值出现在每年的年末和年初,最小值出现在 4 月和 9 月(巴西中南部甘蔗压榨开始)。
然而,它表明使用统计测试来确认序列是否是平稳的,我们将使用两个测试:T4 迪基-富勒测试和 KPSS 测试。
首先,我们将使用 Dickey-Fuller 检验,我将使用基数为 5%的 P 值,也就是说,如果 P 值低于这 5%,这意味着该序列在统计上是平稳的。
此外,还有模型的统计测试,其中这些值可以与 1%、5%和 10%的临界值进行比较,如果统计测试低于某个选定的临界值,序列将是稳定的:
在这种情况下,Dickey-Fuller 检验表明序列不是平稳的(P 值 36%和临界值 5%小于统计检验)。
现在,我们将使用 KPSS 检验来分析该序列,与迪基-富勒检验不同,KPSS 检验已经假设该序列是平稳的,只有当 P 值小于 5%或统计检验小于某个值时才不会是平稳的:
KPSS 检验证实了迪基-富勒检验,也表明序列不是平稳的,因为 P 值为 1%,统计检验高于任何临界值。
接下来,我将演示将数列转化为平稳数列的方法。
将该系列转变为静止的
区别
差分用于移除趋势信号并减少方差,它只是周期 T 的值与前一周期 T-1 的值的差。
为了更容易理解,下面我们只得到乙醇价格的一小部分,以便更好地可视化,请注意,从 2005 年 5 月到 2006 年 5 月中旬,价格开始上涨,这些价格每周上涨,累积起来形成一个上升趋势,在这种情况下,我们有一个非平稳序列。
在进行第一次微分时(如下图),我们移除了系列的累积效应,仅显示了整个系列中期间 T 相对于期间 T-1 的变化,因此,如果 3 天前的价格是 BRL 800.00,并更改为 BRL 850.00,微分的值将是 BRL 50.00,如果今天的值是 BRL 860.00,则差值将是-BRL 10.00。
通常只需要一次微分就可以将一个数列转化为平稳数列,但是如果有必要,可以进行第二次微分,在这种情况下,将对第一次微分的值进行微分(很少有超过两次微分的情况)。
使用相同的例子,为了进行第二次微分,我们必须对 T 减去 T-1 进行微分:BRL 2.9—BRL 5.5 =—BRL 2.6,以此类推。
让我们做一下 Dickey-fuller 检验,看看这个级数在第一次微分时是否是平稳的:
在这种情况下,我们确认序列是平稳的,P 值为零,当我们比较统计测试的值时,它远低于临界值。
在下一个示例中,我们将尝试使用通货膨胀调整将序列转换为平稳序列。
通货膨胀调整
价格是相对于交易时间而言的,2002 年乙醇的价格是 BRL 680.00,如果这种产品的价格在今天以这个价格交易,肯定会有许多工厂关闭,因为这是一个非常低的价格。
为了使系列平稳,我将使用 IPCA 指数(巴西 CPI 指数)根据当前值调整整个系列,该指数从培训期结束(2016 年 4 月)一直累积到研究开始,数据来源在巴西地理统计局网站上。
现在让我们看看级数是如何变得,以及它是否变得平稳。
可以看出,上升趋势已经消失,只剩下季节性振荡,迪基-富勒测试也证实了该系列现在是平稳的。
出于好奇,请看下图,图中显示了相对于原始系列的通货膨胀调整后的价格。
减少方差
对数
对数通常用于将具有指数增长值的序列转换为更具线性增长的序列,在本例中,我们将使用自然对数(NL),其中底数为 2.718,这种对数广泛用于经济模型中。
转换为 NL 的值的差异大约等于原始系列值的百分比变化,这是减少不同价格系列差异的有效基础,请参见下面的示例:
*If we have a product that had a price increase in 2000 and went from BRL 50.00 to 52.50, some years later (2019) the price was already BRL 100.00 and changed to BRL 105.00, the absolute difference between prices is BRL 2.50 and BRL 5.00 respectively, however the percentage difference of both is 5%.*
当我们在这些价格中使用 LN 时,我们得到:NL (52,50)-NL(50,00) = 3,96–3,912 = 0,048 或 4.8%,以同样的方式在第二个价格序列中使用 LN,我们得到:NL(105)-NL(100)= 4.654–4.605 = 0.049 或 4.9%。
在这个例子中,我们可以通过将几乎所有东西放在同一个基础上来减少值的变化。
下面同一个例子:
Result: The percentage variation of the first example is 4.9 and the second is 4.9
下表比较了 X 的百分比变化值与 NL (X)的变化值:
让我们绘制原始序列和 NL 变换序列之间的对比图:
Box-Cox 变换(幂变换)
BOX COX 变换也是一种变换级数的方法,λ(λ)值是用于变换级数的参数。
简而言之,该函数是几个指数变换函数的结合点,在这里我们搜索变换级数的最佳λ值,使其具有更接近正态高斯分布的分布。使用此变换的一个条件是序列只有正值,公式为:
下面,我将绘制原始序列及其分布,然后绘制具有最佳λ值的变换序列及其新分布,为了找到λ值,我们将使用库 Scipy 的函数 boxcox ,它生成变换序列和理想λ:
下面是一个交互式图表,您可以在其中更改 lambda 值并检查图表中的更改:
此工具通常用于提高模型的性能,因为它使模型更符合正态分布,请记住,在完成模型的预测后,您必须返回到原始基数,根据下面的公式对变换进行反演:
寻找相关的滞后
要想预测,单变量序列必须具有自相关性,也就是说,当前周期必须基于更早的周期进行解释(一个滞后)。
由于该系列具有周周期,1 年大约是 52 周,我将使用显示 60 个滞后周期的自相关函数来验证当前周期与这些滞后的相关性。
分析上面的自相关图表,似乎所有的滞后都可以用于创建对未来事件的预测,因为它们具有接近 1 的正相关性,并且它们也在置信区间之外,但是该特征是非平稳序列。
另一个非常重要的功能是部分自相关功能,其中先前滞后对当前周期的影响被移除,并且仅在当前周期上被分析的滞后的影响被保留,例如:第四个滞后的部分自相关将移除第一、第二和第三个滞后的影响。
在部分自相关图下面:
可以看出,几乎没有滞后对当前周期产生影响,但是如前所述,没有微分的序列不是稳定的,我们现在将绘制这两个函数与具有一个微分的序列的关系,以了解其工作原理:
自相关图发生了显著变化,表明该序列仅在第一个滞后期具有显著相关性,在第 26 个月(半年)左右具有负相关的季节性效应。
要创建预测,我们必须注意一个关于寻找相关滞后的极其重要的细节,这种相关性背后有一个原因是很重要的,因为如果没有逻辑原因,这可能只是一个机会,当您包含更多数据时,这种相关性可能会消失。
另一个要点是,自相关和部分自相关图对异常值非常敏感,因此分析时间序列本身并与两个自相关图进行比较非常重要。
在这个例子中,第一个滞后与当前周期具有高相关性,因为前一周的价格历史上没有显著变化,在相同的情况下,第 26 个滞后呈现负相关性,表明与当前周期相反的趋势,这可能是由于一年中供应和需求的不同周期。
由于通货膨胀调整后的序列已经稳定,我们将使用它来创建我们的预测,在调整后的序列的自相关和部分自相关图下方:
我们将只使用前两个滞后作为自回归序列的预测值。
要了解更多信息,杜克大学教授罗伯特·瑙的网站是与该主题相关最好的网站之一。
评估模型的指标
为了分析预测值是否接近当前值,必须测量误差,这种情况下的误差(或残差)基本上是 Yreal YpredYreal Ypred。
评估训练数据中的错误以验证模型是否具有良好的断言性,并通过检查测试数据中的错误(模型“看不到”的数据)来验证模型。
当您将训练数据与测试数据进行比较时,检查错误对于验证您的模型是过拟合还是欠拟合非常重要。
以下是用于评估时序模型的关键指标:
平均预测误差—(偏差)
它只不过是被评估系列误差的平均值,值可以是正的也可以是负的。这一指标表明模型倾向于做出高于真实值(负误差)或低于真实值(正误差)的预测,因此也可以说平均预测误差是模型的偏差。
平均绝对误差
这个度量非常类似于上面提到的预测的平均误差,唯一的区别是将负值误差转换为正值,然后计算平均值。
此指标广泛用于时间序列,因为在某些情况下,负误差可以抵消正误差,从而表明模型是准确的,但在 MAE 的情况下,这种情况不会发生,因为此指标显示了预测值与实际值的差距,无论是高于还是低于实际值,请参见下面的情况:
Result: The error of each model value looks like this: [-4 -2 0 2 4]
The MFE error was 0.0, the MAE error was 2.4
MSE——均方误差
该指标对较大的误差给予更大的权重,因为每个误差值都是平方的,然后计算平均值。因此,该指标对异常值非常敏感,并对具有更大误差的预测给予很大权重。
与 MAE 和 MFE 不同,MSE 值采用二次单位,而不是模型的单位。
RMSE —均方根误差
此指标是 MSE 的平方根,其中误差返回到模型的测量单位(BRL/立方米),它在时间序列中非常常用,因为它对源于平方过程的较大误差更敏感。
MAPE —平均绝对百分比误差
这是另一个有趣的指标,通常用在管理报告中,因为误差是以百分比来衡量的,所以产品 X 的误差可以与产品 y 的误差进行比较。
此指标的计算采用误差的绝对值除以当前价格,然后计算平均值:
让我们创建一个函数,用几个评估指标来评估训练和测试数据的错误:
检查残值
根据所选的度量标准创建模型并检查误差值是不够的,您还必须分析残差本身的特征,因为在有些情况下,模型无法捕获做出良好预测所必需的信息,从而导致用于改进预测的信息出现错误。
为了验证该残差,我们将检查:
- 当前值与预测值(顺序图);
- 残差与预测值(离差图):
分析此图非常重要,因为在其中我们可以检查模式,这些模式可以告诉我们模型中是否需要一些修改,理想情况是误差沿着预测序列线性分布。
- 【QQ 剧情】 【残(散图)】:
总结一下,这张图显示了残差在理论上的分布,遵循高斯分布,而实际上是如何分布的。
- 残差自相关(顺序图):
其中不应有超出置信界限的值,或者模型将信息排除在模型之外。
我们需要创建另一个函数来绘制这些图形:
最基本的预测方法
从现在开始,我们将创建一些含水乙醇的价格预测模型,下面是我们将为每个模型遵循的步骤:
- 根据训练数据创建预测,然后根据测试数据进行验证;
- 根据上述指标检查每个模型的误差;
- 用剩余比较值绘制模型。
让我们看看模特们:
天真的方法:
进行预测的最简单方法是使用前期的值,这是在某些情况下可以做到的最佳方法,与其他预测方法相比,这种方法的误差较低。
一般来说,这种方法不能很好地预测未来的许多时期,因为相对于实际值,误差往往会增加。
许多人也使用这种方法作为基线,试图改进更复杂的模型。
下面我们将使用训练和测试数据进行模拟:
QQ 图显示,存在一些比理论上应该的更大的(上下)残差,这些是所谓的异常值,并且在第一、第六和第七滞后中仍然存在显著的自相关,这可以用于改进模型。
同样,我们现在将在测试数据中进行预测。预测系列的第一个值将是训练数据的最后一个值,然后这些值将由测试的当前值逐步更新,依此类推:
RMSE 和梅误差与训练数据相似,QQ 图的残差更符合理论上的情况,这可能是由于与训练数据相比样本值较少。
在比较残差与预测值的图表中,注意到当价格增加时,误差的绝对值有增加的趋势,也许对数调整会减少这种误差扩大,最终确定残差相关图表明仍有改进的空间,因为在第一个滞后中有很强的相关性,其中可能会添加基于第一个滞后的回归以改进预测。下一个模型是简单平均值:
简单平均值:
另一种进行预测的方法是使用序列平均值,通常这种形式的预测适用于值在平均值附近波动、方差恒定且没有上升或下降趋势的情况,但是也可以使用更好的方法,其中可以使用季节模式进行预测。
该模型使用数据开始时的平均值,直到前一个分析周期,并且每天扩展,直到数据结束,最后,趋势是直线,我们现在将比较该模型与第一个模型的误差:
在测试数据中,我将继续使用从训练数据开始的平均值,并使用将添加到测试数据中的值来扩展平均值:
简单均值模型未能捕捉到序列的相关信息,这可以从实际与预测图以及相关和残差与预测图中看出。
简单移动平均线:
移动平均值是为给定期间(例如 5 天)计算的平均值,并且是移动的,并且总是使用该特定期间进行计算,在这种情况下,我们将总是使用过去 5 天的平均值来预测第二天的值。
误差低于简单平均值,但仍高于简单模型,低于测试模型:
与训练数据类似,移动平均线模型优于简单平均线模型,但是它们还没有从简单模型中获益。
预测在两个滞后中具有自相关,并且误差相对于预测值具有非常高的方差。
指数移动平均线:
上面描述的简单移动平均模型具有平等对待最后 X 个观察值并完全忽略所有先前观察值的特性。直觉上,过去的数据应该逐渐被忽略,例如,最近的观察结果理论上应该比第二最近的稍微重要一点,第二最近的应该比第三最近的稍微重要一点,以此类推,指数移动平均线(EMM) 模型就是这样做的。
由于α(α)是一个介于 0 和 1 之间的常数,我们将使用以下公式计算预测值:
当预测的第一个值是相应的当前值时,其他值将被更新为实际值与前一时段的预测值之差的 α 倍。当 α 为零时,我们有一个基于第一个预测值的常数,当 α 为 1 时,我们有一个采用简单方法的模型,因为结果是前一个真实周期的值。
下图是几个 α 值的图表:
EMM 预报的平均数据周期为 1 / α 。比如当 α = 0.5 时,滞后相当于 2 个周期;当 α = 0.2 时,滞后 5 个周期;当 α = 0.1 时,滞后为 10 个周期,以此类推。
在这个模型中,我们将任意使用 0.50 的 α ,但是您可以执行网格搜索来寻找 α ,这减少了训练和验证中的误差,我们将看到它看起来是什么样子:
该模型的误差类似于移动平均线的误差,但是,我们必须在测试库中验证该模型:
在验证数据中,迄今为止的误差是我们已经训练的模型中第二好的,但是残差的图形特征与 5 天移动平均模型的图形非常相似。
自回归:
自回归模型基本上是具有显著相关滞后的线性回归,其中自相关和偏自相关图表应首先绘制以验证是否有任何相关。
以下是训练序列的自相关和偏自相关图表,显示了具有显著相关性的两个滞后的自回归模型的特征:
下面,我们将基于训练数据创建模型,在获得模型的系数后,我们将它们乘以测试数据所执行的值:
与我们训练的所有其他模型相比,此模型中的误差最低,现在让我们使用其系数来逐步预测训练数据:
请注意,在测试数据中,误差没有保持稳定,甚至比简单模型更差,请注意,在图表中,预测值几乎总是低于当前值,偏差测量显示实际值比预测值高 BRL 50.19,可能调整训练模型中的一些参数,这种差异会减小。
为了改进这些模型,你可以应用一些转换,比如本文中解释的那些,你也可以添加外部变量作为预测源,但是,这是另一篇文章的主题。
最终考虑
每个时间序列模型都有自己的特点,应该单独分析,以便我们可以提取尽可能多的信息,做出良好的预测,减少未来的不确定性。
检查平稳性、转换数据、在训练数据中创建模型、验证测试数据以及检查残差是创建良好的时间序列预测的关键步骤。
另见我关于 ARIMA 车型的帖子。
我希望你喜欢这篇文章,如果你有任何问题或你需要的信息,不要犹豫,通过我的 LinkedIn 联系我,也可以在这里评论。
基本 SQL 决定:我最喜欢的漫威角色
P.S 致敬斯坦·李(1922–2018)
介绍
作为一个被同龄男孩包围的年轻女孩,我们最喜欢的游戏之一是重演一部超级英雄电影(与其说是重演,不如说是改写),并选择我想扮演的超级角色。当时,我的选择非常主观,取决于哪个角色在我的记忆中仍然清晰,我的心情如何,以及我认为谁可以“假设”消除我玩伴的角色。
最近在回忆“那些美好的旧时光”时,我开始思考如何才能找出我最喜欢的漫威角色应该是谁。我认为“统计学和 SQL 可以解决这个难题”。
我决定引导我内心的*“歇洛克·福尔摩斯”和*结构来完成这个任务,这就是我想到的。
项目流程
- 阐明我的目标
- 找到数据
- 将数据插入数据库并清理数据
- 重新明确我的目标,并想出该用什么方法
- 操作:分析
- 获得结果,总结并思考可以改进的地方
客观澄清
这可能是(有争议的)任何分析中最重要的部分,尤其是如果你不想迷失在数据中,无休止地兜圈子。我的主要目的是用一个足够好的理由,而不是感情或“一时冲动”的时刻,来找出我最喜欢的超级角色应该是谁,也就是说,从统计学上来说做出选择**。**
数据
通过确定一个目标,我对我的数据应该包括什么有了一个清晰的想法:英雄的名字和跨越所有英雄的能力,或者可以使用 z 分数标准化的评级能力。
我决定只关注漫威的角色,因为我更喜欢他们。没有阴影的意图
我终于从一个名为 Mentrasto 的 Github 概要文件中找到了一个非常结构化的数据,它符合我的清单。这些数据由 26 个超级字符和 14 个特征组成,它们可以分为两类
- 个人详细信息
*ID*
:每个字符的唯一条目号name
:漫威人物的名字popularity
:基于排名的每个角色的受欢迎程度alignment
:区分角色是好(英雄)、坏(反派)还是只是中立gender
:如果一个角色是男是女height_m
:字符高度,单位为米weight_kg
:字符的重量,单位为千克hometown
:每个字符的起源
2。能力类别&评级
intelligence
: 智力等级从 1 到 7。 1 个是哑巴,7 个是天才strength
:强度等级从 1 到 7。 1 个是哑巴,7 个是聪明人speed
: 从 1 到 7 的速度等级。 1 傻 7 聪明durability
: 耐久性等级从 1 到 7。1 个是哑巴,7 个是天才energy_projection
: 能量投射技能等级 1-7。 1 傻 7 聪明- 战斗技能等级从 1 到 7。 1 傻 7 聪明
数据库存储
第三步是通过创建一个表并插入值来将我的数据存储在数据库中。我决定在这个项目中使用 SQLite,因为它是一个小型数据。
尽管数据源已经准备好被创建为数据库,但是您需要做的只是使用CREATE TABLE
子句和INSERT INTO
子句,您就可以开始分析了。这就是我的意思。
要创建表,请使用
或者就我而言
若要插入值,请使用
对我来说
你可以观看这个视频来了解如何使用 SQLite 的要点。
我决定使用SELECT *
子句来看看我的数据在 SQL 中是什么样子
重新阐明目标
有这么多计划外的专栏可用,我需要再次澄清我的目标,即统计出谁是我最喜欢的角色。
这意味着我唯一需要关注的个人信息是名字**。**
因为所有可用的英雄都基于 6 种能力被同等地评级,所以没有必要标准化数据。由于目标更多的是描述性的,而不是推断性的,所以想到的一个常见的趋势测量是平均值。
操作:分析
我直接进入主要任务,得到每个技能的平均值,计算每个英雄超过每个技能平均值的次数,然后选择最高的次数。
我做了WITH
子句、集合函数和CASE
语句。这基本上是一个包含大量CASE
语句的完整查询。
这总结了我所有的分析。
但是让我带您浏览一下每个查询组件
使用CASE
语句获得所有能力的平均值
然后合计从新列中得到的分数,使用ORDER
子句对分数进行排序,并得到最上面的字符。您可以在下面找到用于捕获它的查询
瞧啊。您需要做的就是使用一个WITH
语句将两个查询组合起来。
结果呢?
胭脂,x 战警的女成员赢了。
Disclaimer: Originated from https://www.marvel.com/comics/discover/1018/rogue
老实说,这并没有让我感到惊讶,因为胭脂这个角色可以吸收任何变种人的能力并用它来对付他们,同时让他们虚弱甚至死亡。
现在我可以自信地扮演胭脂…任何一天
最终想法
虽然这是一个非常酷的发现,但我意识到了一些事情。
- 这是一个非常小的数据库。一个更大的数据库,有更多的漫威人物和更多的判断能力,可能会得出完全不同的结果。下一步是获取更多的数据。
请随时从 漫威数据库 获取更多数据
所使用的代码也可以应用于更大的数据。但是,这将取决于基于大小的优化(查询运行的速度)。
你可以在我的 Github 这里 查看我的代码
- 所用测量值的选择(平均值或均值)可能会受到异常值的影响。下一步是检查定量列中的异常值,如果有的话,使用中值度量。
奖金
我开玩笑地决定根据他们的身体质量指数(身体质量指数)对所有角色进行分类,因为数据是可用的,并且已经被转换成适当的维度。目的是看有多少人肥胖、超重、健康或瘦。这是我在下面得到的。
同样,我的数据中的所有角色都肥胖也不足为奇,特别是因为这些数据似乎是来自真实的卡通角色的细节,而不是扮演每个角色的人。因此,大多数漫画都是对人类的漫画,带有一些夸张的身体部位。
_____________________________ 结尾 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
像我一样,任何人都可以学习成为一名数据分析师,如果你想了解我的下一个项目或我学习的最新进展,请随时注册我的 简讯
每位数据科学家的基本统计数据
Photo by Ruthson Zimmerman on Unsplash
你对自己的估计有多少信心?!
从一个给定的数据集中计算一个变量的平均值可能很容易,但同样难以理解的是从中找出意义。你刚刚计算的平均值,实际上是多少?是人口平均数?但是等等!你只是带了一个代表整个人口的样本。记住,在统计学家的世界里,绝对没有什么是确定的。所以,是的,你刚刚计算的平均值有一些误差。那个错误是什么?回答这个问题需要你知道你想把这个意思解释为什么。
这篇博客将带你详细了解这些概念,并解释样本数据平均值的各种解释方式以及每种情况下的误差。博客中谈到的所有概念最后都以三个要点的形式进行了总结。
我将借助一个数据集来讨论这些概念,这个数据集可以扩展到任何场景中的任何数据集。
Sample Dataset
我们有一份奥斯汀公寓的清单,包括面积和租金。现在,除非我们拥有的数据涵盖了奥斯汀的每一套公寓,否则这将被称为未来的样本。这确实是真实的情况,几乎总是这样,我们只能接触到我们感兴趣的全部人群中的一个样本。
群体是每个感兴趣的数据点的集合。随机样本是从总体中随机收集的数据点的子集。
我们的公寓群有一批面积为 A1 的公寓,另一批面积为 A2 的公寓,等等。假设我们能得到的数据是从这个群体中随机抽取的样本,这个群体的平均租金是 1。
让我们解释一下样本的意思…
这个样本数据的平均租金可以用两种方式解释。1 是一个很好的估计-
a) 奥斯汀公寓全部人口的平均租金。也就是说,如果你相信你的样本是随机产生的,那么总体均值可以很好地近似为样本均值。
b) 从所有奥斯汀公寓的人口中随机选择的公寓。具体来说,如果你碰巧从整个城市中随机挑选了一套公寓,并想进行计算猜测——对其租金进行估计,你最好的选择是你可以获得的样本数据的平均值。
当谈到评估时,一个直接的后续问题是…
我们对自己的估计有多少信心?
在继续讨论误差范围和置信区间之前,我们将试图了解 1,2,3…之间的关系。这将有助于理解这个概念。
根据示例数据,假设我们的人口平均值(通常称为真实平均值)是 500 美元(实际上我们不知道,因为我们无法访问整个人口)。很有可能随机抽取的样本的平均值 I(我们可以从现有数据中计算出来)接近 500 美元。但是肯定会有一些随机的样本,在这些样本中你碰巧选择了所有租金高的公寓或者所有租金低的公寓。对于这样的样品,我会远离 500 美元。看起来我们可以有一个 I 的分布。
中心极限定理表明,如果观察值足够大,假设样本均值正态分布在真实总体均值附近是安全的。经验法则将“足够大”定义为 n=30。
除了这个正态分布,我们还需要了解另一个分布的特征。
单个公寓的租金在样本均值附近的分布不必是正态的。
一旦我们有了数据,我们需要做一些工作来弄清楚这个分布是什么样子的。但是我们知道的是,我们人口中的单个公寓将遵循与我们的随机样本关于样本均值相似的关于人口均值的分布。
牢记这些要点,让我们现在继续前进。
计算误差幅度和置信区间
开始时,我们对样本均值有两种解释。我们对这些估计有多少信心?
a)作为总体均值估计的样本均值:
从上面讨论的分布中,我们知道这些样本平均值与总体平均值相差多少。
误差幅度将是样本均值*总体(非个人公寓租金)*的标准偏差。
但是等等!我们只有一个样本!理论拯救了我们。数学证明-
样本均值的标准偏差=样本点的标准偏差/sqrt(观察值数量)
直觉上,这是有意义的,因为样本均值的标准差必须小于单个公寓的标准差!(随机)样本均值将围绕总体均值杂乱分布,我们可以预期它们非常接近于相同值,因此标准差很小。然而,样本中的单个公寓将表现出更多的变化,导致更高的标准偏差。现在,sqrt(n)的确切因子必须通过数学推导得出。
假设样本均值的标准差为 x. 下一个问题是,我们对真实总体均值在这个误差范围内的置信度是多少?
利用样本均值正态分布于总体均值的事实,我们可以 68%确信总体均值位于样本均值的 x 范围内。
b)样本均值作为个人公寓租金的估计值
既然我们说我们所取的样本是随机的,我们就可以用这个样本分布来近似我们总体的性质。因此,人口中单个公寓租金的标准差将与样本公寓租金的标准差相同(即 x ) 。
因此,误差幅度就是租金的样本标准差。
对于实际租金将在这个误差范围内的置信度,如果租金是正态分布的,那么我们可以预期 68%的置信度。但是,由于个人租金关于平均值的分布不一定是正态的,所以需要做一些工作来计算置信区间(这将因每种分布类型而异)。一个简单的方法是计算样本均值 x 范围内的样本租金数,然后除以样本数据点数。
所有信息总结如下—
- 样本均值是对 a .总体均值和 b. 单个数据点的良好估计。
- 当使用样本均值作为前者时,我们可以有 68%的把握认为真实均值在我们的误差范围内(使用正态分布的特征)。这里的误差幅度是样本均值分布相对于总体均值的标准偏差。这可以通过使用样本点相对于样本平均值的标准偏差来计算。
- 当使用样本平均值作为单个数据点的估计值时,样本的标准偏差可用作误差范围。由于单个点关于其平均值的分布不一定是正态分布,置信区间将随手头的每个数据集而变化。
我要感谢托马斯·萨格教授在课堂上讲授这些概念。我尽力通过这个博客来传递我的学习成果。希望这有助于澄清围绕样本均值解释的混乱。如果你有任何问题,请在评论中告诉我!
数据科学产品管理基础:ML 工作流
ML 理论难,应用更难!
过去几年,我一直在将数据科学应用于商业的不同方面。一些用例是内部机器学习(ML)工具、分析报告、数据管道、预测 API 以及最近的端到端 ML 产品。
我有过成功和失败的 ML 产品的公平分享。甚至有关于 ML 产品恐怖故事的报道,其中开发的解决方案最终未能解决它们应该解决的问题。在很大程度上,可以通过适当地管理 ML 产品来填补这一空白,以确保它最终对用户真正有用。
我很快了解到,管理 ML 产品很困难,因为机器学习(ML)工作流程中的不同步骤涉及到复杂性和不确定性(列举如下):
- 相关文献综述(及其他资源)
- 数据采集&处理
- 模型训练,实验,&评估
- 部署
考虑到 ML 工作流程中的困难和我们的资源限制(例如,时间、人员、存储和计算),我们如何确保我们的每个步骤的方法将最大化 ML 模型对整个 ML 产品的价值贡献?
在这篇博文中,我的目标是给出这些步骤的概述,同时举例说明一些可预见的挑战和我发现在优化 ML 工作流中有用的框架。请注意,在给定可用资源的情况下,当 ML 工作流创建的 ML 模型对整个产品的附加值最高时,我们认为 ML 工作流是优化的。
此外,这篇文章是为至少对机器学习概念、工具和工作流有高度理解的读者准备的;但是,如果您不熟悉我提到的一些概念,请不要担心。我经常链接到解释这些概念的文章,即使对那些我不理解的文章,快速的谷歌搜索也能给出高水平的理解。
我们开始吧!
1.相关文献(和其他资源)的回顾
ML 工作流程的相关文献回顾(RRL)步骤包括阅读现有方法、数据集和其他可能与我们将用于模型的方法相关的资源。这为我们的方法提供了一组可能的设计选择。这些设计选择的例子有
- ML 算法
- 神经网络架构
- 超参数值(例如隐藏层数)
- 优化者
- 特征工程
- 自然语言处理中的文本预处理方法
- 汇集方法
- 数据集
- 用于部署的云服务
大多数设计选择取自相关文献,但也有可能一些组件是“从零开始”创建的,这通常是因为项目的某些方面可能与相关文献中的任何内容都不同。例如,如果被训练的模型旨在根据一个人的在线购物来预测其收入,并且没有关于该主题的论文/博客发表,那么您的团队可能必须生成他们自己的关于特征工程的设计选择。
另一个相关的产品管理(PM)问题是在开始模型开发之前决定给 RRL 分配多少时间。以我的经验来看,花 1-2 周的时间关注 RRL 是合适的。在某些情况下,让一组成员专注于 RRL,而另一组成员专注于进行基于 RRL 发现的实验也是有意义的。后者对于更大的团队更可行。
RRL 还应该告知我们可供我们使用的资源。从这里开始,我们必须了解这些可以在多大程度上减少工作量,甚至消除我们工作流程中某些部分的需要。
例如,预训练模型的存在可能会加快训练时间,同时需要更少的训练数据,这意味着我们可以分配更少的时间来进行模型训练和数据集生成(在监督模型的情况下)。在语义搜索的上下文中,我们甚至可以通过使用由预训练的 BERT 模型生成的特征来完全消除模型训练的需要。
对于带标签的数据集形式的可用资源,确定标签是否与我们希望模型实现的目标一致对我们来说很重要。例如,如果我们想要训练一个模型,它可以在没有足够大的客户服务数据集的情况下识别客户服务电子邮件的情绪,那么 IMDB 评论数据集是否与训练该模型相关?我们可以假设这个问题的答案,但如果我们能找到一种方法来测试在 IMDB 评论上训练的模型是否能正确检测客户服务电子邮件上下文中的情绪,那就更好了。我们将在下一节更详细地讨论数据。
总之,RRL 步骤告诉我们现有的方法和可用的资源,这将有助于设计和规划我们的 ML 工作流程。
2.数据收集和处理
这一步包括获取数据并将其处理成一种状态,在这种状态下,可以将数据输入 ML 模型进行训练或评估。此步骤的方法主要取决于七个因素:
- 数据的大小
- 运行查询所需的计算能力
- 可利用的资源
- 数据库文档的质量
- 数据的清洁度
- 数据源
- 地面实况数据的可用性
从上面可以看出,数据的大小(1)和所需的计算能力(2)会影响提取和处理数据所需的资源量。特别是当可用资源(3)有限时(例如,来自内部数据中心的固定资源),这意味着对于具有复杂查询的大型数据集,数据拉取更慢且更昂贵,这证明需要额外的查询优化工作和数据采样。在 hive 查询的情况下,这可能意味着一次在几个分区上运行优化的查询来进行初始数据提取。此外,值得注意的是,使用云计算平台(例如 GCP 和 AWS)可以缓解存储和计算方面的资源稀缺问题,因为这些平台允许您“按需”使用资源,而无需遵守物理数据中心的限制。
虽然数据的整洁性(5)可能会增加数据处理的资源需求,但数据库文档的质量(4)不会影响所需的资源。但是,这两个因素都会影响编写从必要的数据库中提取数据所需的查询所需的时间。一个巨大的瓶颈通常来自于不知道哪些表包含您需要的确切字段。一个记录良好的数据库会使找到正确的字段变得容易,而一个记录不良的数据库会大大减慢你的工作流程。类似地,不干净的数据会降低您的速度,因为需要额外的(通常非常繁琐的)步骤来将字段转换成处理所需的格式。
你的数据来源(6)也有相关的风险。在数据源来自公共 API 或网站(通过 web crawler)的情况下,还存在可用性、可维护性和合法性的问题。就 API 而言,曾经“免费”使用的东西很有可能以后需要通过“订阅”按月付费。另一个问题是,当这些资源的所有者突然对您在一段时间内可以获取的数据量施加速率限制时(例如,每天 5 次请求)。
对于被爬取的网站,维护成为一个大问题,因为网络爬虫通常对被爬取的网站中的任何变化非常敏感。此外,如果被抓取的网站基于其条款&条件明确禁止抓取,网络抓取可能会有法律问题,所以在进行任何网络抓取之前最好先检查一下。由于这些原因,当你的 ML 产品需要从公共 API 和网站获取时,警惕这些风险是很重要的。尽早了解这些情况会让你做好准备,为事情变糟做好潜在的替代方案。
最后,用于模型评估的地面真实数据(7)的可用性将决定我们是否必须设计一种方法来标记数据。通常,标记数据有两个可能的来源:1)外部方,2)用户。根据建模任务的难度,来自外部方的“高质量”标签数据的成本会有所不同。对于技术任务(例如医学期刊的信息提取),只有领域专家用户能够给出高质量的标签。对于简单的任务(例如狗对猫的图像分类),领域专家不需要有高质量的标签。那么我们为什么不直接让领域专家用户一直标记数据呢?因为它们通常更贵(想象一下按小时付费给医生来标记数据)。换句话说,随着建模任务变得越来越复杂,雇人标记数据的成本也变得越来越高。
一个聪明的解决办法是让用户为你创建你的基本事实数据。这可以通过在 ML 产品中嵌入“标签”来实现,同时最大限度地减少摩擦,甚至将标签过程转变为增值活动。一个很好的例子是脸书给用户“标记”朋友的能力,让他们知道他们在照片中。通过标记您的朋友,您正在“标记”他们的图像数据集。用户受益是因为他们可以和朋友联系,而脸书受益是因为他们得到了“免费”标签的数据。大家都赢了!
我计划在未来的博客文章中进一步讨论将评估嵌入到你的人工智能产品中的话题。
3.模型培训、实验和评估
现在我们来看 ML 工作流程中最著名的部分:实际训练模型和试验不同的方法!
您可能读过一些文章,这些文章谈到训练模型只占数据科学家工作的 20%。这是非常准确的,但是从 PM 的角度来看,重要的是要理解要考虑的主要事情是:
- 计划的实验
- 培训运行时间
- 精确度定义
- 准确度基线
在你开始训练你的模型之前,你应该已经对基于你的团队的假设(由 RRL 步骤的发现支持)你计划运行的实验有一些想法。列出这些步骤,并根据模型的预期改进(就准确性而言)和所需的增量时间对它们进行排序。预期改进最高、所需时间最短的实验应该在最上面。
此外,区分需要重新训练模型的实验和不需要重新训练的实验也很重要。例如,在模型被训练用于文本分类之后,假设输入文本的格式仍然一致,人们仍然可以试验不同的文本预处理方法。对于不需要“再训练”的实验,时间成本通常要低得多,因为训练运行时间占实验时间成本的大部分。这意味着许多非再培训实验可以被视为团队可以快速执行的“速赢”。
关于非训练实验要注意的一点是,它们通常是那种你只有在已经有一些训练好的模型之后才能做的实验(例如,模型集合需要已经训练好的模型)。正因为如此,我通常把实验期的最后一段时间分配给快速的非训练实验,而训练时间较长的实验则在实验期的最开始进行。
Training runtime (2)只是讨论一个版本的模型在数据集上训练需要多长时间。这很重要,因为训练一个模型(对于需要训练的实验)需要多长时间将决定你可以用不同版本的模型做多少次实验,给定你为你的项目承诺的时间表。
这意味着你很可能没有足够的时间来完成你计划进行的所有实验。无论如何,应该首先进行排名较高的实验;然而,该团队也可以通过减少执行实验所需的时间来增加实验的数量。一个简单的方法是减少训练时间。
那么我们如何控制训练运行时间呢?通常,这可以通过简化模型(例如,减少神经网络的层数)或减少数据集的大小(例如,从数据集中选择示例或要素的子集)来控制。我采用的一种非常有效的简单方法是从一个模型的最简单版本开始,使用最小的合理数据样本(即运行时间最快的版本)。然后,你可以逐渐增加模型的复杂性和样本量,直到你达到你的目标训练运行时间——允许你进行所有计划的实验的运行时间。跟踪模型复杂性和样本大小的每个级别的准确性和运行时间也很重要,这样您就可以利用这一点来了解与增加的复杂性和样本大小相关的时间权衡。
Tradeoff between Training Runtime and Sample Size + Model Complexity
减少培训运行时间的另一种方法是升级您可以访问的硬件资源(例如 GPU 和 CPU)。幸运的是,云计算平台允许人们定制不同价位的可用资源。因为升级的成本是完全记录在案的,所以升级决策是由升级成本和培训运行时的预期加速决定的。为了充分利用这些资源,许多训练算法实现通过并行化(例如 pytorch、tensorflow、scikit-learn)来加速操作。
最后,我们必须定义模型的准确性度量(3 ),我们将使用它来评估每个实验的结果。要记住的重要一点是,准确性指标应该是模型对整个 ML 产品的价值贡献的度量。因此,你需要对你的 ML 产品试图解决的问题有一个深刻的理解,并从那里,回到模型正在执行的特定任务,并确定它帮助产品解决问题的确切方式。
一个常见的错误是只关注准确性度量,因为这是特定任务的常用度量。事实上,确保模型的准确性度量与其对产品的价值贡献一致是非常重要的:否则,更好的执行模型不会转化为更好的产品,在这种情况下,实验将是徒劳的。
例如,在 Airbnb 的情况下,主机偏好通过主机将接受预订请求的概率来建模。鉴于接受概率较高的预订首先显示给客人,这也意味着更多的预订将被接受,这将转化为 Airbnb 的收入提升。模型越精确,接受率越高,收入也越高。
一旦价值度量被确定,团队现在需要一个基线性能(4)来决定模型是否“成功”以及成功的程度。我设定基线性能的一个框架是理解性能水平,这将使模型对 ML 产品真正“有用”。这可能是一件很难做到的事情,但它可能是有用的,因为它:1)迫使我们思考模型应该如何为用户提供价值,2)给我们一个初始的目标性能,我们可以用它来评估有用性,3)激励团队与用户进行实际的对话,以理解模型在为他们提供价值中的作用。
从长远来看,通过运行 A/B 测试来比较不同版本的模型对实际产品价值指标(如收入、签约、订单)的影响,可以更经验性地得出有用性。Airbnb 是一家拥有数据科学文化的公司的一个很好的例子,这种文化通过 A/B 测试使用实验来评估模型。这确保了他们的模型确实在改进产品。
4.部署
部署是 ML 工作流中的最后一步,该方法主要取决于:1)预测是实时需要还是周期性批量需要,以及 2)延迟要求。
如果实时预测是必要的,那么模型可能会被部署为一个 API,可以通过向这个 API 发送请求来检索预测。这通常使用在单独实例(例如 AWS ec2)上运行的 flask 或 django 等应用框架或在云上运行预测脚本的 FaaS 平台(例如 AWS Lambda)来完成。
如果需要以特定的频率(例如每天、每月)批量进行预测,那么定期对新数据运行预测脚本就足够了。对于这种情况,像 crontab(内置于 Linux 中)和其他自动化框架(例如 Airflow、Luigi)这样的工具就足够了。由于广泛记载的原因,我更喜欢使用气流进行批量部署。
对于延迟需求,它基本上回答了这个问题:模型需要多快才能做出预测?通常,应用程序会有服务级别协议(SLA ),将预测延迟保持在某个标准。
例如,当您打开中型应用程序时,他们会向您显示一个文章推荐列表,该推荐列表可能是 1)“预测的”并每天存储一次,在您打开应用程序时向您显示(批量部署),或者 2)在您打开应用程序时“预测的”(实时部署)。自然地,第一个只需要预测 SLA 足够快,以便用户在早上打开应用程序时(例如,从每天午夜起的 5 个小时)将看到更新的列表。其次,SLA 必须更加严格,因为预测是在用户打开应用程序时发生的(例如,从打开应用程序起 1 秒钟),用户不希望等待太久才能看到他们推荐的文章。
从上图可以明显看出,延迟更可能是实时部署的一个问题。那么,我们如何将延迟降低到可接受的水平(SLA),假设目前还没有达到这个水平?例如,想象一下打开应用程序后,预测需要 5 秒而不是 1 秒的 SLA。
这就是机器学习工程(MLE)的用武之地。MLE 涉及使用特殊技术(例如并行化)来加速部署的机器学习模型的运行时间,目标是达到设定的 SLA。ML 工程师的工作是将 5 秒预测运行时间降低到目标 1 秒预测运行时间。
结论
至此,你应该对 ML 工作流程有了一个总体的了解,以及在设计每个步骤的方法时需要考虑的一些不同的事情。请务必注意,在每个冲刺阶段结束时,应与您的数据科学团队详细讨论每一步,以便在给定所有可用信息的情况下,计划的方法在任何时间点都是最佳方法。
此外,我认为,在成为数据科学产品经理之前,拥有数据科学方面的全面经验具有相当大的优势。这是因为设置积压工作、确定任务优先级、估计时间表以及沟通 ML 产品如何工作和提供价值需要深入了解 ML 工作流程以及与每个步骤相关的不确定性。即使仅仅从优先化任务和制定时间表的角度来看,理解 ML 工作流也意味着了解随着项目的进展新信息的含义。
我计划在以后的博客文章中对数据科学产品管理的其他方面进行更详细的讨论(例如,设置待办事项、区分任务优先级、估计时间表、使 ML 模型目标与产品的价值主张保持一致、将评估集成到 ML 产品中、管理 ML 的新利益相关者)。如果你对从哪些开始有任何建议,请随时评论*、或通过、*、推特、 linkedin 或电子邮件(*lorenzo.ampil@gmail.com)*联系我。敬请期待!
初学者 BASH 基础。
了解一些最有用的 BASH 命令及其提供的实用程序。
今天大多数计算机都不是由电力驱动的。相反,它们似乎是由鼠标的“跳动”提供动力的:威廉·肖特
你有没有注意到电影中的超级书呆子黑客如何通过凶猛地键入一些命令并盯着黑色背景的绿色屏幕,轻松地渗透到最安全的银行并抢劫它们?这个人是如何始终如一地获取所有密码,并在任何地方控制隐藏的摄像头,只需在键盘上敲几下。嗯,我不确定电影制作者是怎么得到这个的,但我猜这是他们告诉我们,命令行是一个强大的工具,尽管没有授权所有这些黑客攻击和访问!!荒谬。
很多时候,初学者非常习惯于使用基于 GUI 的界面,以至于他们往往会忽略命令行界面(CLI)的功能。当我们需要将大约十万个文件复制到一个文件夹中时,鼠标真的很方便,但是如果要重命名所有这些文件或者根据它们的扩展名将它们分开会怎么样呢?因为图形用户界面是不可编程的,所以我们要花很长时间用命令行来重命名或分隔它们,然而,我们可以用几行代码很快实现这一点。
对于各种开发人员来说,Unix shell 是一个非常强大的工具。本文旨在快速介绍从 UNIX 操作系统开始的基础知识。
一种多用户的计算机操作系统
除了基于 WINDOWS 的操作系统,今天的大多数操作系统都是建立在 UNIX 之上的。其中包括许多 Linux 发行版、macOS、iOS、Android 等等。仅仅浏览一下基于 UNIX 的操作系统的系谱就足以突出 UNIX 的重要性,这也是它在业界被广泛采用的原因。事实上,许多数据和计算系统的后端,包括像脸书和谷歌这样的行业巨头,都大量使用 UNIX。
The UNIX Family Tree. Source: WIkipedia
壳
Shell 是在计算机上运行程序的命令行界面。用户在提示符下键入一串命令,shell 为用户运行程序,然后显示输出。这些命令可以由用户直接输入,也可以从称为 shell 脚本或 shell 程序的文件中读取。
外壳类型
UNIX 系统通常提供多种 shell 类型。一些常见的是:
然而,在本文中,我们将仅限于 Bash shell。然而,我们鼓励你阅读和尝试其他 shell,尤其是 zsh shell ,因为在最新的 MacOS 中,名为 Catalina, zsh 将取代 bash shell。所以现在了解一下是个好主意。
末端的
终端是一个用于与外壳交互的程序。它只是 Shell 和运行在其中的其他命令行程序的接口。这类似于网络浏览器是网站的接口。下面是 Mac 上典型终端的样子:
A Typical Mac Terminal
Mac 和 Linux 都有各自版本的终端。Windows 也有一个内置的命令 shell,但是它是基于 MS-DOS 命令行的,而不是基于 UNIX 的。因此,让我们看看如何在 Windows 上安装一个 shell 和一个终端程序,其工作方式与 Mac 和 Linux 上的相同。
在 WINDOWS 上安装
- Windows Linux 子系统(WSL)
它是 Windows 10 中新的 Linux 兼容系统。WSL 允许开发人员直接在 Windows 上运行 GNU/Linux 环境——包括大多数命令行工具、实用程序和应用程序,无需修改,没有虚拟机的开销。你可以在这里阅读更多关于它的安装和特性。
- Git Bash
Git Bash 是我们将在本文中使用的东西。从这里下载 Git 到你的 Windows 电脑上,用所有默认设置安装。最后你得到的是一个终端窗口,就像下面这样。
A Windows Git Bash
探索终端
每当我们打开终端窗口时,我们都会看到上次登录的凭证和一个 Shell 提示符。每当 shell 准备好接受输入时,就会出现 Shell 提示符。根据发行版的不同,它的外观可能会稍有不同,但大多数情况下,它显示为username@machinename
后跟一个$符号。
如果您不想要这些信息,您可以使用 PS1 定制您的 shell 提示符。
终端现在只会在提示符下显示$
。但是,这只是暂时的,一旦终端关闭,它将恢复到原来的设置。
入门指南
为了掌握一些窍门,让我们尝试几个简单的命令:
- echo: 返回您在 shell 提示符下键入的任何内容,类似于 Python 中的
Print
。
- **日期:**显示当前时间和日期。
- 日历:显示当月的日历。
- 清除端子 :
Ctrl-L
或clear
清除端子
基本 Bash 命令
bash 命令是 bash 可以独立执行的最小代码单元。这些命令告诉 bash 我们需要它做什么。Bash 通常接受来自用户的单个命令,并在命令执行后返回给用户。
工作目录
显示当前工作目录
pwd
代表print working directory
,它指向当前工作目录,即 shell 当前正在查看的目录。这也是 shell 命令查找数据文件的默认位置。
目录类似于文件夹,但是在 Shell 中,我们将坚持使用目录这个名称。UNIX 文件层次结构有一个树形结构。为了到达一个特定的文件夹或文件,我们需要遍历这个树结构中的某些路径。路径在斜线(/
)字符的帮助下分隔上述结构的每个节点。
导航目录
像ls
和cd
这样的命令用于导航和组织文件。
限位开关(Limit Switch)
ls
代表一个list
,它列出了一个目录的内容。通常从查看我们的主目录开始。这意味着如果我们单独打印ls
,它将总是打印当前目录的内容,在我的例子中是/Users/parul.
My Home directory represented in the shell and the GUI interface.
参数
当与ls
命令一起使用时,参数和选项打开一些特殊功能。
**ls** <folder>
:查看特定文件夹的内容。**ls -a**
: 用于列出文件夹中所有隐藏的文件**ls -l**
: 打印出更长更详细的文件列表。ls -l
也可以与目录名一起使用,列出该特定目录下的文件。**ls ~**
: 波浪号(~)是表示主目录的快捷方式。所以,不管我们进入哪个目录,ls ~
总是会列出主目录。
通配符
shell 还允许我们用星号(*)表示的模式来匹配文件名。它充当通配符来替换给定模式中的任何其他字符。例如,如果我们列出*.txt
,它将列出所有扩展名为.txt
的文件。让我们试着列出所有的。演示文件夹中的 py 文件:
激光唱片
cd
代表Change Directory
并将活动目录更改为指定的路径。在我们cd
进入一个目录后,可以用ls
命令查看那个目录的内容。让我们来看看使用该命令的一些方法:
**cd <Directory>**
:将当前目录切换到所需目录。让我们导航到位于Demo
目录中的test
目录,并使用ls
命令查看其内容。注意,我们也可以使用分号(;)在同一行上写两个命令。
**cd ..**
:返回父目录。**cd**
:返回主目录
组织文件
有一些命令可以让我们在 shell 内部移动、删除、创建和复制文件。
mkdir
mkdir
代表Make directory
,用于新建一个目录或文件夹。
平均变化
mv
代表Move
,它将一个或多个文件或目录从一个地方移动到另一个地方。我们需要指定要移动的内容,即源和要移动到的位置,即目的地。
让我们在名为PythonFiles
的演示文件夹中创建一个新目录,并使用上面的两个命令将所有.py
文件从演示文件夹移动到其中。
触控
touch
命令用于创建新的空文件。它还用于更改现有文件和目录的时间戳。下面是我们如何在演示文件夹中创建一个名为foo.txt
的文件。
空间
rm
代表Remove
,它删除文件或目录。**默认情况下,它不会删除目录,**但是如果在一个目录中用作rm -r *
,那么该目录中的每个目录和文件都会被删除。
现在让我们删除之前创建的foo.txt
文件。
删除目录
rmdir
代表remove directory
,用于从文件系统中删除空的目录。让我们删除我们不久前创建的PythonFiles
文件夹。
注意到…/表示父目录。
查看文件
这是果壳的另一个方面,超级有用。有些命令可以帮助我们查看文件的内容,这样我们就可以对它们进行操作。
猫
cat
代表concatenate
,它读取一个文件并输出其内容。它可以读取任意数量的文件,因此得名 concatenate。在我们的演示文件夹中有一些文本文件,让我们使用 cat 来查看它们的内容。
要查看多个文件,请在cat
命令后输入两个文件名:
$ cat Names.txt fruits.txt
较少的
cat
命令在屏幕上显示文件的内容。当文件内容较少时,这没有问题,但当文件很大时,这就成了一个问题。从下面的例子中可以看出,该命令在终端以非常高的速度弹出所有内容,我们无法理解文件的所有内容。幸运的是,有一个叫做less
的命令可以让我们查看内容,一次一屏。
$ less babynames.txt
有一些选项可以使用:
Spacebar
:进入下一个屏幕
b
:转到上一屏幕
/
:搜索特定单词
q
:退出
男人
man
命令显示手册页,这是许多 Linux 和大多数 Unix 操作系统默认内置的用户手册。
man bash
:显示整本手册
man <keyword>
eg man ls
给出关于 ls 命令的信息。
管道和过滤器
管道运算符“|”(竖线)是一种将一个命令的输出作为另一个命令的输入发送的方法。
command1 | command2
当命令将其输出发送到管道时,该输出的接收端是另一个命令,而不是文件。下图显示了wc
命令如何统计cat
命令显示的文件内容。
在某种程度上,wc
是一个接收输入并以某种方式转换这些输入的命令。这样的命令被称为过滤器,并且被放置在 Unix 管道之后。
过滤
现在让我们看看一些常用的过滤器命令。我们将使用一个名为baby names . txt的文件,其中包含大约 1000 个婴儿的名字,以及一个包含一些水果名字的 fruits.txt 文件。
- grep 或
global regular expression print
搜索具有给定字符串的行或在给定的输入流中寻找模式。以下命令将读取所有文件,并输出包含单词“Tom”的所有行
但这是一个庞大的列表,我们不可能理解所有这些刚刚在终端爆炸的数据。让我们看看如何使用管道操作符来理解它。
- wc 是的简称
word count.
它读取文件列表并生成一个或多个以下统计数据:换行计数、字数计数和字节计数。让我们将上述grep
命令的输出输入到wc
中,以计算包含单词“Tom”的行数
- 排序过滤器按字母或数字顺序对行进行排序
cat
命令首先读取文件fruits.txt
的内容,然后对其进行排序。
- uniq 代表
unique
,它给出了输入流中唯一的行数。
需要注意的是uniq
不能检测重复的条目,除非它们是相邻的。因此,在使用 sort 命令之前,我们已经对文件进行了排序。或者,也可以用sort -u
代替uniq
。
管道对于执行一些复杂的任务非常方便,因为几个命令可以放在一个管道中。
进一步阅读
命令行工具是对工具箱的一个很好的补充。最初,对于初学者来说,它可能是令人生畏的,但是一旦他们掌握了它的窍门,它真正的优势和好处就可以实现了。这篇文章是让一个开始,只触及表面。网上有大量有用的资源可以用来探索和学习关于 shell 的细节。以下是一些推荐的资源: