东京铁路系统外国人指南
Every day, millions of Japanese use public trains to commute in and around Tokyo. With the 2020 Olympics right around the corner, this number is expected to increase significantly with the sudden influx of millions of international spectators. What can data about Tokyo’s public transportation tell us about the world’s most efficient and complex system? And can we find ways to improve the system such that it can operate just as efficiently at much higher capacities?
东京地铁系统是一个物流奇迹。东京这座城市大约有 1400 万人口,是一个繁忙的国际商业中心,是许多年轻日本工人的目的地,也是 2020 年奥运会的举办地,因此它很少睡觉。有这么多的人试图在城市里移动,一个高效的公共交通系统是至关重要的。东京创造了,可以说是世界上最好的(也是最广泛的)。以铁路为主要交通工具,东京发展了一个庞大的系统,由独立拥有和运营的铁路线和汽车组成的互联网络组成,能够每天运送大约 2000 万乘客。
面对如此惊人的庞大数字,我认为分析东京最受欢迎的运营商之一东武铁路公司会很有意思。数据是通过东京公共交通 API 的开放数据挑战获得的,这篇文章的全面分析可以在我的 GitHub 页面找到。虽然分析的数据只是整体运营的一小部分,但它确实揭示了日本铁路行业的真实规模,以及我们外国人如何更有效地利用火车为我们的旅游服务。
列车容积
最初,数据被清理是为了显示一天中有多少列车在东武线上运行。重要的是要记住,不是所有的列车都是一样的,所以列车的类型是作为一个额外的维度添加到分析中的。下图显示了日本铁路每天运行的数百万辆列车,不仅强调了总数,还强调了类列车的数量。
这张图表告诉我们的是,在早上 7-9 点和下午 5-7 点之间,火车的总数达到了每天的最高水平。这是有道理的,因为这些窗口是在早晚高峰期间,数百万日本上班族和女性上下班的时候。此外,半特快列车只在早高峰运行,直到晚高峰前才再次启动。这表明它们的目的是在高峰时段帮助人们涌入东京附近的车站。
晚上 8 点到 10 点的高峰也很有趣,因为它凸显了一种日本文化现象。日本员工应该在老板离开后离开,一旦他们离开办公室,同事们应该聚在一起喝酒和吃饭。典型的日本工薪族通常要到晚上 10 点以后才回家(关于日本的工作文化是否与该国不断下降的出生率有任何关联,有很多猜测,但那是另一个时间的讨论)。
上图还显示了三种火车构成了日常运输的主体:慢车、快车和半快车。就百分比而言,这 3 个系列可以细分如下:
在这里,我们可以清楚地看到,慢车和快车处理大约 85%的全天交通,而半快车主要用于高峰时间。作为一个外国人,如果你想在这个城市四处走走,你最好的选择是乘坐慢车或快车。
另一个有趣的点是进站和出站列车之间的差异。虽然火车在东京只有两种移动方式(进或出),但一天中的时间和火车移动的方向之间可能有关系。按照一天中的时间绘制进站和出站列车的数量,以产生以下视觉效果:
同样,我们在图中看到峰值出现在上午 7–9 点和下午 5–7 点。但是,如果仔细观察,您会发现早高峰期间进站列车略多于出站列车。晚高峰的情况正好相反。这实际上是非常直观的,因为大多数人从其他地方来到东京工作,工作一结束就离开。晚上 8-10 点的峰值也说明了上述文化点。然而,这种涌入在一定程度上也是因为年轻人来到这个城市享受东京的夜生活。
就百分比而言,上图可以表示如下:
这个图表的有趣之处在于,它澄清了一个事实,由于上面提到的原因,一天中进站的列车比出站的列车多。
车站音量
既然对列车容量有了概念,下一个合乎逻辑的步骤就是看看这些列车到底要去哪里。东武铁路连接 158 个车站;然而,只有那些接收 20,000 或更多进站列车的列车才包括在分析中。这个数字被选为阈值,因为接收较少的站的数量变化如此之大,以至于我们无法洞察哪些站是最繁忙的(因为图表会变得过于混乱)。
我们可以清楚地看到,东武动物园站是网络中最繁忙的。然而,这并不意味着所有的乘客都对看到奇异的动物感到兴奋。事实上,造成这种差异的原因是东武动物园实际上是东武铁路线上的最后一站。这意味着东武站用于换乘其他铁路,本质上充当了经由东武线进出东京的枢纽。
线路音量
最后的分析是为了确定哪些铁路线是最繁忙的。进站和出站列车是根据特定的东武铁路线计算的。
这张图说明了大江线、堀里线和东京晴空塔线构成了东武铁路系统的绝大部分。所有其他线路都是东京内更具体地点的分支。作为一名游客,你去任何地方的最佳选择就是使用这三条线路中的一条。
结论
东京公共铁路运营庞大而复杂。在这篇文章中,我只概述了一条具体的铁路。实际上,还有 29 家运营商运营着总共 108 条独立线路,连接东京境内总共 882 个火车站。由于网络的设计,实际上不可能获得所有城市的列车数据用于分析目的;然而,我们仍然可以从整个画面的一小部分中收集一些重要的见解。作为游客,我们应该预料到早晚高峰时车站会非常拥挤。我们还应该预料到,如果我们决定在晚上进入东京而不是离开(早上正好相反),我们的旅行将不会那么拥挤。此外,帕累托法则适用于铁路线——你的大部分运输可以在运营商的一小部分线路上完成。最后,火车路线上的最后一站通常是最繁忙的,因为许多人正在换乘火车在全国各地旅行。
文字游戏:矢量化、标记和情感分析
用自然语言处理分析《权力的游戏》第一册中的单词
Image from simisi1 from Pixabay
完全披露:我没有看过《权力的游戏》,但我希望通过分析文本来了解更多。如果你想了解更多关于基本文本处理的背景知识,你可以阅读我的其他文章。所有 5 本书的文本都可以在 Kaggle 上找到。在本文中,我将使用清理后的文本来解释以下概念:
- 矢量化:单词袋、TF-IDF 和 Skip-Thought 矢量
- 矢量化后
- 词性标注
- 命名实体识别(NER)
- 组块和叮当声
- 情感分析
- 其他 NLP 包
矢量化
目前,我有一个词条列表,但是我如何以一种机器能够理解的方式来组织它们呢?我将探索几个向量化方法,它们将单词列表转化为可用于不同机器学习方法的数字数组。这样做时,确保从文本中删除停用词和其他不必要的词是很重要的,这样创建的数组/向量系统将只有重要的维度可以建模。
一种方法叫做单词包,它定义了文本中包含的唯一单词的字典,然后找到文本中每个单词的计数。例如,如果我要从 A Game of Thrones 中收集一个独特的单词列表,然后按章节将整个列表拆分成单词,我将得到一个每行有一章、每个单词的计数跨列的数组。这种方法的缺点是它不能保持单词的顺序([坠落,死亡]和[坠落,爱]有非常不同的意思),而且它不能捕捉单词的任何实际意思。此外,如果我们将文本按章节划分,包含更多单词的章节会无意中被赋予更大的权重,因为它们在整个行中有很高的计数。然而,这仍然是查看术语分布的好方法,并且如果您想要查看某个特定单词出现了多少次,这是很有用的。这是一个在《权力的游戏》文本上的实现,按章节划分:
from sklearn.feature_extraction.text import CountVectorizerbow = CountVectorizer()
BOW = bow.fit_transform(page_lemm)
bagOFwords = pd.DataFrame(BOW.toarray())
bagOFwords.columns = bow.get_feature_names()
在这个例子中,page_lemm
是长度为 572(页数)的列表,每个元素是该页上的一串单词。CountVectorizer()
函数自动对所有字符串中的单词进行标记和计数。在使用上面的代码之前,我做了一些后台的停用词和词条去除,你可以在我的 github 上看到。这段代码创建了一个 dataframe,其中每一行对应于书中的一章,每一列对应于文本中的一个单词。框架的主体包含每章每个单词的计数。
另一种解决单词袋问题的方法叫做 **TF-IDF,**或者术语频率-逆文档频率。TF-IDF 与前面的方法类似,只是每行每列中的值是根据文档中的术语数量和单词的相对稀有程度来调整的。词频等于一个词在文档中出现的次数除以文档中的总字数。逆文档频率计算语料库中所有文档中的稀有单词的权重,其中稀有单词具有高 IDF 分数,并且出现在语料库中所有文档中的单词具有接近零的 IDF。这使得那些可能有很多意思的词,即使很少,在最后的分析中仍然有影响力。这么想吧——是知道‘手’这个词在一本书的所有章节中都有使用会更好,还是知道‘死亡’只在其中 10 章出现会更有影响?TF 和 IDF 相乘得到最终的 TF-IDF 分数。在中可以找到这个过程的一步一步。Python 中的 scikit-learn 包(sklearn
)有一个函数TfidfVectorizer()
,它将为您计算 TF-IDF 值,如下所示:
from sklearn.feature_extraction.text import TfidfVectorizervectorizer = TfidfVectorizer()
got_tfidf = vectorizer.fit_transform(page_lemm)
tfidf = pd.DataFrame(got_tfidf.toarray())
tfidf.columns = vectorizer.get_feature_names()
如您所见,这两种方法的代码非常相似,采用相同的输入,但在 dataframe 中给出不同的内容。计算 TF-IDF 分数,而不是每个单词的计数。这里是根据平均单词数的前 10 个单词和根据平均 TF-IDF 分数的前 10 个单词的比较。有一些重叠,但是 TF-IDF 给出的字符名称的平均分比单词袋高。我强调了这两种方法之间不重叠的地方。
跳跃思维 矢量是另一种矢量化方法,它使用神经网络中的迁移学习来预测句子的环境。迁移学习的概念是,机器可以将它从一项任务中“学到的”知识应用到另一项任务中。这是几乎所有机器学习技术背后的思想,因为我们试图让机器以比人类学习更快、更可量化的速度学习。特别是对于文本处理,其想法是一种算法建立一个神经网络,从数千本不同的书籍中学习,并找出句子结构,主题和一般模式。然后,这种算法可以应用于尚未阅读的书籍,它可以预测或模拟文本中的情感或主题。你可以在这里阅读更多关于这种方法以及它与 TF-IDF 的比较。
另一种严重依赖神经网络的矢量化方法是 word2vec ,它计算两个单词之间的余弦相似度,并在空间中绘制单词,以便将相似的单词分组在一起。你可以在这里阅读这个方法的一个简洁的实现。
矢量化后
现在你有了一个数字数组,接下来呢?使用单词包,您可以执行逻辑回归或其他分类算法来显示数组中哪些文档(行)最相似。当试图查看两篇文章在主题上是否相关时,这是很有帮助的。skip think Vectors 和 Word2Vec 都是基于文本内的含义来聚类单词,这是一种叫做单词嵌入的方法。这项技术很重要,因为它保留了单词之间的关系。尤其是在处理评论文本数据(任何带有数字评级的文本评论)时,这些技术可以产生关于消费者的感受和想法的有价值的见解。由于权力的游戏没有默认的分类值,我没有办法验证模型,我将在下面解释分析文本的替代方法。
位置标记
词性标注 (POS)是使用上下文线索将词性分配给列表中的每个单词。这很有用,因为同一个词有不同的词性,可能有两种完全不同的意思。例如,如果你有两个句子[‘一架飞机可以飞’和’房间里有一只苍蝇’],正确定义’飞’和’飞’是很重要的,以便确定这两个句子是如何相关的(也就是根本没有)。按词性标注单词可以让你进行组块和分块,这将在后面解释。一个重要的注意事项是,词性标注应该在标记化之后和移除任何单词之前立即进行,以便保留句子结构,并且更明显地知道单词属于哪种词性。一种方法是使用nltk.pos_tag()
:
import nltkdocument = ' '.join(got1[8:10])
def preprocess(sent):
sent = nltk.word_tokenize(sent)
sent = **nltk.pos_tag**(sent)
return sentsent = preprocess(document)
print(document)
print(sent)
【他说“死了就是死了”。“我们和死人没关系。”,’“他们死了吗?”罗伊斯轻声问道。“我们有什么证据?”]
[,…(’ ','
‘),(‘我们’,’ PRP ‘),(‘有’,’ VBP ‘),(‘无’,’ DT ‘),(‘商’,’ NN ‘,(‘有’,’ IN ‘),(’ the ‘,’ DT ‘,(‘死’,’ JJ ‘,(’.‘, ‘.’)、(“’ ‘”、“’ '”、…]
这是上面创建的一个片段,你可以看到形容词被表示为“JJ”,名词被表示为“NN”,等等。该信息将在以后分块时使用。
命名实体识别
有时,进一步定义特殊单词的词性会很有帮助,尤其是在尝试处理关于时事的文章时。除了名词之外,“伦敦”、“巴黎”、“莫斯科”和“悉尼”都是有特定含义的地点。同样的道理也适用于人名、组织名、时间名、金钱名、百分比名和日期名等等。这个过程在文本分析中很重要,因为它是理解文本块的一种方式。通常,要对文本应用 NER,必须先执行标记化和词性标注。nltk 包有两个内置的 NER 方法,这两个方法都在本文中有很好的解释。
执行 NER 并能够可视化和排序结果的另一个有用方法是通过 spaCy 包。一个很好的演练可以在找到。我使用这种方法研究了 get 文本,得到了一些有趣的结果:
import spacy
from collections import Counter
import en_core_web_sm
nlp = en_core_web_sm.load()
from pprint import pprintdoc = nlp(document3)
pprint([(X.text, X.label_) for X in doc.ents])
(‘乔治·r·r·马丁’,‘人’),
(‘威玛·罗伊斯爵士’,‘人’),
(‘五十’,‘红衣主教’),
(‘威尔’,‘人’),
(‘罗伊斯’,‘人’),
(‘八日’,‘日期’),
(‘九日’,‘红衣主教’),
(‘威玛·罗伊斯’,‘人’, (‘加雷斯’,‘人’),
(‘加雷斯’,‘组织’),【组织】。
在上面的代码中,document3
是一个字符串中的一个权力的游戏的全文。这个包可以有效地发现和分类所有类型的实体。在 Gared 的一些例子上有点混乱(在某一点上它把他归类为 PERSON,另一个归类为 ORG,后来又归类为 WORK_OF_ART)。然而,总的来说,这比单纯的词性标注更能洞察文本的内容。下面是每种实体类型的匹配数和找到的顶级实体。不出所料,文中有很多名字。
labels = [x.label_ for x in doc.ents]
items = [x.text for x in doc.ents]print(Counter(labels))
print(Counter(items).most_common(5))
计数器({ ‘红衣主教’:340,‘日期’:169,’ FAC’: 34,’ GPE’: 195,‘法律’:2,’ LOC’: 24,‘金钱’:1,’ NORP’: 32,‘序数’:88,’ ORG’: 386, ‘人’:2307 ,‘产品’:35,‘数量’:23,‘时间’:86,’ WORK_OF_ART’: 77})
(乔恩’、259)、( 奈德*’、247)、(* 艾莉亚*’、145)、(* 罗伯特*’、132)、(*
分块和分块
组块和分块是从文本中提取有意义短语的两种方法。它们结合了词性标注和正则表达式来生成与所请求的短语结构相匹配的文本片段。组块的一个实现是找到提供不同名词描述的短语,称为名词短语组块。名词短语块的形式通常由决定/所有格、形容词、可能的动词和名词组成。如果你发现你的组块中有你不想要的部分,或者你宁愿在特定的位置拆分文本,一个简单的方法就是通过 chinking 。这定义了在分块时应该移除或分割的小块(称为缝隙)。我不打算在这篇文章中探索 chinking,但可以在这里找到一个教程。
使用 NLTK 进行特定类型分块的最简单方法是使用nltk.RegexpParser(r‘<><><>’)
。这允许您指定您的名词短语公式,并且非常容易解释。每个< >引用一个单词的词性进行匹配,正常的正则表达式语法适用于每个< >。这非常类似于nltk.Text().findall(r’<><><>’)
的概念,但是只是用了 POS 而不是实际的单词。在创建要解析的正则表达式字符串时需要注意的几件事是,词性缩写(NN =名词,JJ =形容词,PRP =介词,等等。)可能因软件包而异,有时最好从更具体的开始,然后扩大搜索范围。如果你现在非常迷茫,可以在这里找到这个概念的。此外,在此之前温习一下句子结构和词性可能是个好主意,这样你就能完全理解组块分析的结果。下面是一个应用于掘地工具的示例:
document2 = ' '.join(got1[100:300])
big_sent = preprocess(document2) # POS tagging wordspattern = 'NP: {<DT>?<JJ>*<NN.?>+}'
cp = nltk.RegexpParser(pattern)
cs = cp.parse(big_sent)
print(cs)
(……,( NP 暮光/NNP )深化/VBD。/.( NP The/DT 万里无云/NN sky/NN )转身/VBD ( NP a/DT 深/JJ 紫/NN *),/,(NP The/DT color/NN)of/IN(*NP an/DT 老/JJ 青
这是一个非常类似于 NER 的想法,因为你可以将 NN 或 NNP(名词或专有名词)组合在一起以找到物体的全名。此外,匹配的模式可以是词类的任意组合,这在查找特定种类的短语时非常有用。但是,如果词性标注不正确,您将无法找到您要查找的短语类型。我在这里只寻找名词短语,但是在我的 github 代码中包含了更多类型的组块。
情绪分析
情感分析是计算机如何将目前为止所涉及的一切结合起来,并想出一种方法来传达一篇文章的整体主旨。它将句子、段落或文本的另一个子集中的单词与字典中的单词列表进行比较,并根据句子中单个单词的分类来计算情感得分。这主要用于分析评论、文章或其他观点,但我今天将把它应用于 GOT。我主要感兴趣的是看这本书的整体基调是积极的还是消极的,以及这种基调在各章之间的变化。进行情感分析有两种方法:你可以在以前分类的文本上训练和测试一个模型,然后用它来预测同一类型的新文本是正面还是负面,或者你可以简单地使用内置于函数中的现有词典来分析和报告正面或负面得分。下面是后者的一个例子或者是*《权力的游戏*第一页的一些句子:
from nltk.sentiment.vader import SentimentIntensityAnalyzer
nltk.download('vader_lexicon')sid = **SentimentIntensityAnalyzer**()
for sentence in sentences:
print(sentence)
ss = sid.polarity_scores(sentence)
for k in sorted(ss):
print('{0}: {1}, '.format(k, ss[k]), end='')
print()
……“死人让你害怕吗?”
compound:-0.7717*,neg: 0.691,neu: 0.309,pos: 0.0,
威玛·罗伊斯爵士带着刚刚的一丝微笑问道。
复合:0.3612,neg: 0.0,neu: 0.783,pos: 0.217,
Gared 未上钩。
复合:0.0,neg: 0.0,neu: 1.0,pos: 0.0,…*
因为这是在分析一本书的文本,而不是评论的文本,所以很多句子将会有一个中性的复合得分(0)。然而,这完全符合我的目的,因为我只是在寻找这本书的语言随着时间推移的一般趋势。但是当提到死亡的时候,一个负分被应用,这仍然是一件好事。
TextBlob 是另一个有用的可以进行情感分析的包。一旦你把你的文本转换成一个 TextBlob 对象(textblob.textBlob()
),它就具有对纯文本进行标记化、词汇化、标签化的功能,并生成一个 WordNet,它可以量化单词之间的相似性。这个包有很多不同的文本对象,允许非常酷的转换,在这里解释。甚至有一个correct()
功能会尝试纠正拼写错误。在本文中,我不打算深入讨论其中的大部分,因为我正在尝试分析一本书,它通常应该具有正确的拼写和语法,然而,当处理特别混乱的文本数据时,这些工具中的许多将是有用的。下面是 TextBlob 在权力的游戏第一页的情绪分析版本:
from textblob import TextBlob
def detect_polarity(text):
return TextBlob(text).sentiment
for sentence in sentences:
print(sentence)
print(detect_polarity(sentence))
“死人让你害怕吗?”
情绪(polarity =-0.2*,主观性=0.4)
威玛·罗伊斯爵士带着刚才那一丝微笑问道。
情绪(极性=0.3,主观性=0.1)
加雷斯没有上钩。
情绪(极性=0.0,主观性=0.0)*
nltk 和 textblob 的情感评分之间有相似性,但是 nltk 版本有更多的可变性,因为它是一个复合评分。或者,textblob 情感具有主观性得分,这有助于判断句子可以被分类的准确程度。下面是每种方法的页面情感分布。总的来说,Textblob 给出了更高的情感评级,而 nltk 与分数的差异更大。
如果你试图从社交媒体文本或表情符号中收集情绪,VADER 情绪分析是一个专门为这项任务设计的工具。它内置了俚语(lol,omg,nah,meh 等。)甚至能看懂表情符号。如何使用它的一个很好的演练可以在这里找到。此外,如果 Python 不是您进行文本分析的首选语言,那么在不同的语言/软件中还有其他方法来进行情感分析,这里的将对此进行解释。
其他 NLP 包
在本文中,我只解释了nltk
、textblob
、vaderSentiment
、spacy
和sklearn
包的功能,但是根据您试图完成的任务,它们有许多优点和缺点。其他一些可能更适合你的任务是多语种和 Genism。 Polyglot 以具有分析大量语言的能力而闻名(根据任务支持 16–196 种)。 Genism 主要用于对文本的无监督学习任务,并且需要用不同的包进行任何预处理。你可以在这里找到所有这些信息的图表。
结论
我从撰写本文中学到的一个关键点是,完成一项任务至少有三种方法,而确定最佳选择取决于您使用的数据类型。有时候你会优先考虑计算时间,而其他时候你会需要一个可以很好地进行无监督学习的包。文本处理是一门迷人的科学,我迫不及待地想看看它在未来几年里将我们引向何方。在本文中,我介绍了矢量化以及它如何确定文本之间的相似性,标记允许将意义附加到单词上,以及情感分析,它可以大致判断文本的积极或消极程度。我从《权力的游戏》中收集了很多见解,比如有很多死亡,先生是一个常见的头衔,拼写为 Ser,龙的例子没有我被引导相信的那么多。不过,我现在可能被说服去看书了!我希望你喜欢这篇文章!
我的代码副本,有更多的例子和解释,可以在 github 上找到!请随意获取和使用代码。
我的另一篇文章《文本预处理来了》可以在这里找到!
假设检验的一般指南
对 P 值、I 型误差、II 型误差、统计功效的温和解释
假设检验作为一种重要的统计技术,广泛应用于各种商业案例的 A/B 检验中,同时也给许多人带来了困惑。本文旨在总结假设检验的几个关键要素的概念,以及它们如何影响检验结果。
故事从假设开始。当我们想知道一个总体的任何特征,如分布形式、感兴趣的参数(均值、方差等)时。),我们对它做一个假设,叫做人口假设。然后,我们从总体中抽取样本,并测试样本结果在给定的假设下是否有意义。
例如,您的经理不知何故知道公司网站上所有用户的平均点击率为 0.06(总体点击率的平均值),而您对此表示怀疑,并认为点击率应该更高。如何检验你或你的经理是否会赢?假设检验所做的是首先提出一个假设,即假设 CTR μ = 6%,然后随机抽取大量用户的样本数据,并从中计算 CTR 的均值(样本的 CTR 均值)。根据对样本的观察,决定是否拒绝你的假设。于是就出现了零假设和替代假设。
零假设:μ = 6%
替代假设:μ > 6%
假设样本量足够大是有效的,你发现 CTR 的均值是 7.5%。你能不能直接告诉你老板,真实的 CTR 是 7.5%?基于样本的 1.5%的差异是否足以让你做出这个决定?概率总能帮助解决不确定性。希望您能提供这样的答案:如果零假设为真,即 CTR 的均值等于 6%,那么从样本中看到 CTR 的均值等于或大于 7.5%的概率为 1%。百分之一的概率是很少发生的。因此,拒绝零假设是合理的。这正是 P 值所解释的。
" P 值是在零假设下获得与观察到的结果一样或更极端的结果的概率."
P 值= P(μ ≥ 7.5% |零假设成立)
让我们看看下面两种情况下 P 值的可视化。
黑线代表零假设的分布,蓝线代表样本均值的分布。粉色区域是如上所述的 P 值。第一种情况是这两种分布彼此远离,P 值很低,为 0.01。表示看到样本均值分布均值等于或大于 7.5%的概率为 1%,假设总体真实均值等于 6%的零假设为真。这是非常罕见的,但这是我们观察到的,所以我们声明假设是不正确的。类似地,第二种情况,P 值等于 30%,意味着有 30%的概率看到样本分布的均值等于或大于 7.5%,这是一个相当高的概率,所以我们不能拒绝零假设。这就是为什么当 P 值很小时,拒绝零假设。
另一方面,不管零假设是否为真,也不管你是否应该拒绝零假设,它们的组合都可以表示为概率。这就是假设检验的美妙之处。查看可视化的更多细节:
假设在这种情况下,P 值(近似粉色三角形包含的区域)足够小,等于 0.05。那么α,β和 1- β的意义是什么呢?
α是I 型误差,是假设假设为真,错误拒绝零假设的概率。也算是你能容忍的 P 值的最大值。设置好α的值后,你会有一个相应的显著性水平。例如,当α = 5%时,显著性水平为 95%。
α = P(拒绝零假设|零假设为真)
β是第二类错误,是假设无效,拒绝无效假设失败的概率。
β = P(拒绝零假设失败|零假设为假)
1-β是**统计功效,**是在假设无效的情况下,正确拒绝无效假设的概率。
1-β = P(拒绝零假设|零假设为假)
下表总结了四种情况:
这让我想起了我们通常用于分类问题的混淆矩阵。α其实是假阳性,β是假阴性,1-β是真阳性,1-α是真阴性。见下图。
显然,我们希望最小化 FP 和 FN,最大化 TP,但正如上面所示的分布图,随着α减小,β增大,统计功效减小。所以它们之间总是有一个权衡,我们不能只优化一个单一的指标。当你试图平衡召回率和精确度时,这与混淆矩阵的逻辑完全相同。影响α和β的因素有几个,如样本大小、分布范围、假设和观察之间的差异等。统计功效的标准选择是 80%,α是 5%。
本文从概念上提供了假设检验的顶级摘要以及一些重要的度量标准。在分布和要测量的度量标准的选择方面,还有许多其他的计算细节和要求。继续潜水!
分类的生成方法
简单的解释
Image by Prashant Sharma from Pixabay
每当有人谈到生成性分类模型和区分性分类模型时,我总是感到困惑。
我读了一遍又一遍,但不知何故,它避开了我。
所以我想到了写一篇关于它的帖子来提高我的理解。
这篇文章是关于理解生成模型以及它们与判别模型的区别。
最后,我们将自己创建一个简单的生成模型。
区别性与生成性分类器
问题陈述: 有了一些输入数据,X
我们要把数据分类到标签里y
。
生成模型学习联合概率分布p(x,y)
,而判别模型学习条件概率分布p(y|x)
那么真的,有什么区别?他们看起来几乎一样。
假设我们有一个小样本数据:
(x,y) : [(0,1), (1,0), (1,0), (1, 1)]
那么p(x,y)
就是
而p(y|x)
是
如你所见,他们模拟了不同的概率。
判别分布p(y|x)
可以直接用于将示例x
分类到类别y
。判别分类模型的一个例子是逻辑回归,其中我们尝试对 P(y|X)建模。
Logistic Regression
生成算法模型p(x,y)
。一个例子是朴素贝叶斯模型,在该模型中,我们尝试对 P(X,y)建模,然后使用贝叶斯方程进行预测。
生成分类背后的中心思想
- 用概率分布分别拟合每个类别。
- 对一个新的点进行分类,找出它最有可能来自哪个分布。
还不明白的也不要着急。你一定会在这篇文章结束时得到它。
一个小例子
让我们使用虹膜数据集。
对于我们的简单示例,我们将使用单个 x 变量 SepalLength 和我们的目标变量 Species。
让我们看看不同物种的萼片长度的分布。为此我使用了 plotly_express 。
import plotly_express as px
px.histogram(iris, x = 'SepalLengthCm',color = 'Species',nbins=20)
要创建创成式模型,我们需要找出两组值:
1.个别类别的概率:
获得单个类的概率是相当简单的——例如,我们的数据集中的实例数,即 seta 除以数据集中的案例总数。
0.3333333333333333 0.3333333333333333 0.3333333333333333
虹膜数据集非常平衡。
2.每类 x 的概率分布:
这里我们拟合了 X 上的概率分布。我们假设 X 数据是正态分布的。因此,我们可以找到这三个分布的样本均值和方差(因为我们有三个类别)
在上图中,我用三个物种的样本均值和方差为每个物种拟合了三个正态分布。
那么,我们如何用这个来预测呢?
假设我们得到一个新的例子,SepalLength = 7 cm。
因为我们看到最大概率出现在 virginica,我们预测 x=7 的 Virginica,也是基于该图;这看起来是非常正确的选择。
您也可以使用代码获取值。
Setosa 3.062104211904799e-08
Versicolor 0.029478757465669376
**Virginica 0.16881724812694823**
这一切都很好。但是我们什么时候处理过单一变量呢?
让我们把例子扩展到两个变量。这一次让我们也使用 PetalLength。
px.scatter(iris, 'SepalLengthCm', 'PetalLengthCm',color = 'Species')
那么在这种情况下我们该如何进行呢?
第一次我们在单个 x 上拟合正态分布,这次我们将拟合双变量正态分布。
这是它的样子:
现在,其余的计算保持不变。
在上面的方程中,只有法线被二元法线代替。如你所见,通过使用二元正态分布,我们得到了更好的分类分离。
作为对多变量(多于 2 个)这种情况的扩展,我们可以使用多元正态分布。
结论
生成型模型擅长生成数据。但与此同时,创建这样的模型来捕捉数据的底层分布是极其困难的。
生成建模涉及许多假设,因此,这些模型在分类设置中的表现不如判别模型。在上面的例子中,我们还假设分布是正态的,这可能是不正确的,因此可能会引起偏差。
但是理解它们是如何工作的仍然是有帮助的。一类这样的模型被称为生成对抗网络,它对于生成新图像非常有用,也非常有趣。
这里的是包含所有代码和可视化效果的内核。
如果你想了解更多关于生成模型和机器学习的知识,我推荐圣地亚哥大学的这门机器学习基础课程。上面的帖子大体上是从来自 SanDiego 的 MicroMasters 课程的内容中得到启发的,我目前正在努力构建我的数据科学学习。
谢谢你的阅读。将来我也会写更多初学者友好的帖子。在 媒体 关注我,或者订阅我的 博客 了解他们。一如既往,我欢迎反馈和建设性的批评,可以通过 Twitter @mlwhiz 联系到我。
Python 决策树的简明指南
决策树算法是一种监督学习模型,用于用一系列训练变量预测因变量。决策树算法可用于分类和回归目的。
在这个特别的项目中,我将用一个离散随机变量的分类来说明它。
决策树可以回答的一些问题。
- 贷款申请人是否应该被接受?这是基于他/她的历史和其他措施。
- 哪种药物最适合特定的病人。
- 癌细胞是良性的还是恶性的?
- 电子邮件是否是垃圾邮件?
以及现实生活中更多的场景。
理解决策树算法。
决策树是使用递归分区将数据分为两组或更多组来构建的。
现实生活的例子。
假设我们有随着时间的推移进行癌症筛查的患者的数据。基于来自筛选练习的测试,筛选的细胞被分类为良性和恶性。基于该数据,可以建立决策树模型,以比医生更好地为未来的患者以最高的准确度预测这些病例。
决策树从具有最高预测能力、较少杂质和较低熵的变量开始,逐个变量分割数据变量。
该方法的主要目的是最小化杂质和每个节点。节点的杂质通过节点中数据的的熵来计算。
熵
熵是信息量的无序或简单地说是数据的随机性或不确定性。
数据集的熵取决于节点的随机性。应当注意,熵越低,分布越不均匀,节点越纯。如果样本完全同质,则熵完全为零,如果样本被等分,则熵为 1。
参考上述数据,假设一个节点具有 7 个恶性和 1 个良性,而另一个节点具有 3 个恶性和 5 个良性,前者与后者相比具有低熵。
这是熵的数学计算方法:
最佳树的选择取决于分裂后具有最高信息增益的节点。
信息增益
这是拆分后可以增加确定性水平的信息。计算如下。
这个过程继续构建一个基本的决策树。下面是 python 中的一步一步的过程。
用 Python 实现。
导入库
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns
数据摄取
loan_data = pd.read_csv(“loan_data.csv”)
loan_data.info()
loan_data.describe()
loan_data.sample(5)
探索性数据分析。
这是一个简短的 EDA,因为该项目旨在决策树插图。
plt.figure(figsize=(10,6))
loan_data[loan_data[‘credit.policy’]==1][‘fico’].hist(alpha=0.5,color=’blue’,
bins=30,label=’Credit.Policy=1')
loan_data[loan_data[‘credit.policy’]==0][‘fico’].hist(alpha=0.5,color=’red’,
bins=30,label=’Credit.Policy=0')
plt.legend()
plt.xlabel(‘FICO’)
设置数据。
我这里的数据有一些我必须驯服的分类变量,因为如果格式不正确,模型算法可能无法很好地处理这些数据。
categorical_var = [‘purpose’]
loan_data2 = pd.get_dummies(data= loan_data,columns=categorical_var,drop_first=True)
loan_data2.columns
列车测试分离
from sklearn.model_selection import train_test_split
X = loan_data2.drop('not.fully.paid',axis = 1)
y = loan_data2['not.fully.paid']
X_trainset, X_testset, y_trainset, y_testset = train_test_split(X, y, test_size=0.30, random_state=2)
训练决策树模型
from sklearn.tree import DecisionTreeClassifier
loanTree = DecisionTreeClassifier(criterion="entropy", max_depth = 4)
loanTree
loanTree.fit(X_trainset,y_trainset)
模型评估
predLoan = loanTree.predict(X_testset)
from sklearn.metrics import confusion_matrix,classification_report,precision_score
print(classification_report(y_testset,predLoan))
这产生了 78%的准确度水平
让我们来看看混淆矩阵的可视化
sns.heatmap(confusion_matrix(y_testset,predLoan),cmap=”viridis”,lw = 2,annot=True,cbar=False)
决策树模型产生了 78%的准确性,这是令人印象深刻的,因为没有进行任何功能工程,甚至没有调整参数来改进模型。
通过应用随机森林分类器算法也可以提高精度,该算法比简单决策树更好。在另一个内核上有更多的内容。
决策树的可视化
from IPython.display import Image
from sklearn.externals.six import StringIO
from sklearn.tree import export_graphviz
import pydotfeatures = list(X.columns)
# features
dot_data = StringIO()
export_graphviz(loanTree, out_file=dot_data,feature_names=features,filled=True,rounded=True)graph = pydot.graph_from_dot_data(dot_data.getvalue())
Image(graph[0].create_png())
欢迎所有意见、建议和任何改进!
请在此处查看上面的代码。在我的 Github 库中。
干杯!
配对交易中强化学习的温和实现
tensor flow 中结构化编程的一个例子
Source: Pexels
这涵盖了基于 1 分钟股市数据的协整配对交易中从概念到 RL 实现的主题。对于这里的强化学习,我们使用 N 臂 bandit 方法。代码是可扩展的,因此如果你遵循这种风格,你可以将任何策略、数据 API 或机器学习算法插入到工具中。
Left-to-Right: Code Structure, Sample Price Relationship, Testing Profits
外卖 :
1。具有数据获取实用程序
2 的可扩展基础设施。python 代码构造的组合技术
3。跨越编码、计量经济学和强化学习主题的一般概念和理论
在 GitHub 上创建一个帐户,为 wai-I/Pair-Trading-Reinforcement-Learning 的发展做出贡献。
github.com](https://github.com/wai-i/Pair-Trading-Reinforcement-Learning)
第 1 部分:理论之前的数据
1.1 数据来源— Tiingo
ti Ingo是一个金融研究平台,提供包括新闻、基本面、价格等数据。我们可以通过其 REST IEX API 提取当天的股票市场数据,该 API 从 IEX 交易所检索 TOPS 数据(书籍顶部、最后销售数据和顶部买价和卖价报价)。
例如,只需在浏览器中粘贴以下链接:
*https://api.tiingo.com/iex/aapl/prices?startDate=2019-01-02&endDate=2019-01-02&resampleFreq=5min&token=ef79e455ba9b04c3df719407e34f05e1b051b4d6*
您将获得一份 JSON 格式的 AAPL 截至 2019 年 1 月 2 日的历史 5 分钟盘中价格列表:
*[{"date":"2019-01-02T14:30:00.000Z","open":154.74,"high":155.52,"low":154.58,"close":154.76},{"date":"2019-01-02T14:35:00.000Z","open":154.8,"high":155.0,"low":154.31,"close":154.645},{"date":"2019-01-02T14:40:00.000Z","open":154.67,"high":154.94,"low":154.25,"close":1...*
为了自动完成这项任务,我们需要能够在特定的历史窗口内获得一系列股票的标准化日内数据的函数。
限制
- 历史数据频率高达 1 分钟
- 对于每种股票的每个请求,检索的样本的最大数量是不一致的,也就是说,即使您指定了 365 天的窗口,您也只能获得几天的数据
- 每天的样品数量并不一致(例如,它也可能在市场开放时间后给你价格)
- 不够快(可能只是我的问题)
解决方案
- 一次获取 1 个日期的 1 只股票
- 通过固定数量的观察值截断样本,即假设每个日期有 391 个 1 分钟的价格
- 异步输入输出
Pandas 还提供相关工具不仅从 Tiingo 提取数据,也从其他数据提供商提取数据,但似乎他们只提取日常数据。
请注意,根据您的订阅和相应的请求限制,API 数据是免费的。但是,在使用代码时,您应该注意用法,避免挑战限制。
1.2 实施
首先,你需要一个 Tiingo API 令牌。只需注册一个账户,你就会在这里找到你的代币。
这些是位于代码内数据/API.py 中的主要函数:
- 我们需要一个函数,它可以为我们提供检索特定股票[股票代码、日期[目标日期、频率[频率]的当天数据的 url,假定您的令牌[令牌 T13 是有效的。您可以想象我们可以动态地调用这个函数来逐个日期地检索数据,这样我们就可以在一系列读取中轻松地标准化格式。
2.Pandas read_json 允许我们将获取的 json 数据读入一个系列或数据帧。我们可以在类中包装它:
3.上述函数在 Data/API.py 中的 Tiingo 类下定义。该代码将基于关键输入重复提取:开始日期、结束日期、目标属性(即“日期”和“收盘”)以及股票列表,并输出组合数据帧
异步输入/输出
上面的实现受到限制并且很慢。每次当我们开始获取数据时,程序将打开 API 连接,从服务器请求数据,并在关闭连接之前等待它的响应。在所有的进程重复之后,直到最后的 url 被获取。
我们来看看和 AsyncIO 的区别有多大。以下代码获取 GOOG 和 FB 在 2018 年 1 月 1 日至 2018 年 1 月 31 日之间的 1 分钟价格。每个日期 391 个样本,总共约 15,600 次观察。所有设置都在配置字典中配置,但我们暂时忽略它。
下面的结果表明,使用 AsyncIO 比普通的获取快 17 倍。
Speed comparison between normal and asynchronous fetching
根据 python 文档:
asyncio 是一个使用 async/await 语法编写并发代码的库。
asyncio 被用作多个 Python 异步框架的基础,这些框架提供了高性能的网络和 web 服务器、数据库连接库、分布式任务队列等。
与并行编程不同,AsyncIO 是单线程的。一个简单的想法是,它在一个组织任务分布的单事件处理程序中管道化分配的任务,以便多个任务可以在其他任务空闲时开始运行。
为了说明这个概念,让我们来看一个官方示例:
Source: https://python.readthedocs.io/fr/latest/library/asyncio-task.html
上面的关键字 async def 将相应的函数定义为一个协程,它可以暂停或恢复执行。每当一个任务在关键字下等待时,进程控制被传递回事件控制器(循环),事件控制器为另一个任务分配并启动进程。简单来说,就是不浪费等待时间。
在我们的代码中也有类似的东西。 _fetch_hist_async 将创建一个事件循环来控制 fetch_data_async 的进程,该进程是获取当天价格的底层任务。当遇到 await 时,控制返回到触发另一个获取请求的事件循环,即使前一个尚未完成。
1.3 数据存储
理想情况下,我们应该建立一个数据库来存储价格。为了简单起见,让我们把价格存进去。目录下的 csv 统计/价格:
第 3 部分:配对交易——概念和分析
让我们跳过第 2 部分,它涵盖了枯燥的代码和结构,并做一些分析。但是我仍然建议你去读完这篇文章,如果你感兴趣的话,先对骨骼有个概念。
3.1 配对交易
配对交易是一种市场中性策略。如 Gatev 等人(2006 年)所述:
“配对交易的概念非常简单。找出两只历史上价格变动一致的股票。当它们之间的价差扩大时,做空赢家,买入输家。如果历史重演,价格就会趋同,套利者就会获利。”
它将战略总结为两个阶段:
- 在制定阶段,我们衡量股票之间的价格关系,并在范围内确定股票对。
- 在随后的交易期间,将根据预定义的规则监控和交易该关系。
简单地说,它利用均值回复价差交易。问题是,我们如何估计或验证股票对之间的价格动态?
Krauss (2017)将成对交易策略中的常用方法归纳为五类:距离方法、协整方法、时间序列方法、随机控制方法以及其他方法,如机器学习、主成分分析和 copula。本文将展示经典的 Engle 和 Granger (1987)协整方法在配对交易的强化学习算法组合中的应用。
这里的想法与时间序列分析中一个叫做平稳性的概念有关。它通常被称为金融时间序列中的弱形式(或协方差)平稳性,其标准如下:
- 随机变量 x 的期望值,即E[x(t)],与时间 t 无关
- *方差**Var(x(t)*是一个与时间无关的正的有限常数
- 协方差 **Cov( x ( t ),x(s)**是有限的,与时差 t-s 有关,但既不是也不是 s**
**通常情况下,**x(t)被视为对数价格回报(或差异),而非价格水平。如果一个时间序列在一阶差分后变得平稳,则称之为一阶积分 I(1) 。
尽管股票价格也可能是均值回复的,但它们很少振荡,即由于持续的经济驱动因素和市场活动的混合影响,它们是趋势性的和非平稳的(随机游走)。所以可能有人会从定向投注中获利,但这不是我们关注的重点。
我们实际上想要的是找到一对价格差异或价差持续稳定(且协整)的股票。
3.2 分析(参见 EXAMPLE/Analysis.py)
我提取了 21 只美股从 2018–01–01 到 2018–07–30 的 1 分钟价格。所有价格都保存在 STATICS/PRICE in 中。csv 格式。让我们取时间序列的前 70%做一些分析。
皮尔森相关性
Pearson correlations across 21 stock prices
这里我们计算价格(不是回报)相关性。相关性最高的是 PEP 、 PG 、 JNJ 和 KO 。从经济角度来说,他们应该分为两对: JNJ-PG 和 KO-PEP 。请注意,高相关性并不一定意味着协整。请参见下面的详细信息。
边际分配
如果我们研究他们的边际分布,线性关系应该有所认识。我们还可以找到一些可能有用的集群,但暂时让我们避免进一步的数据挖掘,这不是我们的主要关注点。
****
Marginal distributions of JNJ-PG and KO-PEP intraday prices
价格图表
让我们创建一个函数来绘制样本期的价格和价差。在样本开始时,价格重新以 1 为基础。注意,第二个子图将描述由对称交易阈值(th)和止损(stop)指定的交易范围:
JNJ-PG price pairing
KO-PEP price pairing
协整检验
以下代码计算协整检验的 p 值,零假设为无协整。所以如果 p 值小**,观察到协整关系的概率应该相对高。**
请注意,下面的测试是针对整个时间序列的。在培训期间,我们应该根据选择的样本进行测试和交易。
下面的结果表明,即使他们的相关性是可比的,找到一个协整关系的概率是非常不同的。
有时我们可以找到一个相关但不协整的价格关系。例如,如果两只股票价格随着时间的推移一起上涨,它们就是正相关的。然而,如果这两只股票以不同的速度上涨,价差将保持增长,而不是在均衡点振荡,因此是非平稳的。
让我用一个实验来说明这一点。下面的代码通过 几何布朗运动 和 乔莱斯基分解 模拟了两个高度相关的股票价格,每个包含 1000 个样本。
如您所见,尽管相关性很高,但 p 值非常大。
让我们画出这些时间序列:
Prices and spread of correlated GBM prices
底部子图所示的价差是趋势性的,而不是均值回复性的。
3.3 协整
Engle 和 Granger (1987)定义了两步识别法:
- 协整时间序列的元素应该具有相同的积分顺序
- 它们的线性组合应该产生具有较低积分阶的变量
在我们的情况下,当一个以上的 I(1) 非平稳和外生变量(因此它们在理论上是相互独立的)完全相互抵消时,在随机趋势成分中存在协整,从而给出平稳的线性组合和长期均衡。更具体地说,两个 I(1) 对数股价x(1, t )** 和 x (2, t ) 如果存在一个协整系数 b 则进行协整,给出一个平稳的时间序列 y ( t )**
**其中 a 简单来说就是一个常数,**y(t)就是我们的目标交易价差。
显然,我们可以简单地使用普通的最小二乘法(OLS)方法,通过回归×套期保值比率 (1, t )** 对 x (2, t ) 来估计利差和系数 b 。最初的想法是基于格兰杰表示定理,并以误差修正模型(ECM)的形式表示。然而,基于股票(1987)中的一个叫做超一致性的想法,由于更快地收敛到真实的回归系数,OLS 估计量更容易实现,并且预计在估计协整关系时具有更好的性能。**
3.4 协整检验
检验协整的最常见方法是通过使用迪基富勒(DF)** 或增广迪基富勒(ADF) 单位根检验来检查上述回归的残差是否平稳。**
单位根和迪基-富勒(DF)检验
****单位根是随机过程的一个特征。考虑一个阶为 1 的自回归过程(一个 AR(1) 过程):
****e(t)是白噪声而 0 < c ≤ 1 。如果过程是非平稳的,也不是完全随机的,那么假设值 c 等于 1 (即方程的根是 1,因此过程是 I(1) )。例如,这可能意味着今天的价格等于昨天的价格加上一个随机值。
Dickey 和 Fuller (1979)表明,这种情况下的统计量不符合 T2 分布,因此检验是不一致的。为了解决这个问题,我们可以将上述模型改为:
并检验 ( c -1)=0 的零假设。这就是所谓的迪基-富勒测试**。我们也可以添加一个截距或趋势项,并根据假设检验其系数等于零的零假设。**
增强迪基-富勒(ADF)试验
如果我们把自回归过程展开成一个 p 的顺序(即 AR( p ) ):
然后我们可以用这个公式应用增强的迪基-富勒检验:
并检验以下的零假设:
3.5 实施
在第 2.3 节中,我们已经看到了策略类 EGCointegration ,其实现与上面的解释一致。注意,这里的测试是基于stats models . TSA . stat tools . coint的,在用于单元根测试的同一个库中还有另一个函数stats models . TSA . stat tools . ad fuller**。不同的是:**
- coint 是有效的恩格尔-格兰杰两步协整检验。它用 I(1)测试估计的协整对(2 个时间序列输入)的残差,而
- adfuller 测试单变量过程的单位根(1 个时间序列输入)
在大多数情况下,这两个测试应该产生相同的结论,但是 coint 对于我们的实现来说更加直观。
第四部分:强化学习的思想
我从这个系列中受益匪浅,在代码开发过程中汲取了一些想法。肯定推荐。
4.1 一般概念
强化学习的基础由两个主要部分组成:代理和环境**。环境由具有预定义状态空间的不同状态来表示,而代理学习确定在动作空间之外执行什么动作的策略。在完全强化学习问题中,智能体的学习周期可以归纳为以下几个阶段:**
- 对环境状态进行观察
- 根据现有策略执行相应的操作
- 根据所执行的行动获得相应的奖励
- 更新策略
Source: Reinforcement Learning: An Introduction, Sutton, R., Barto A.
举个例子,想象一只小狗(代理人)正在学习如何对主人的命令(环境)做出反应。它是只知道如何执行以下动作的懒狗:
- 答:坐下
- b 站
- c .无所事事
为了训练小狗,他的主人定期给他一套命令(状态),包括“坐”、“站”和“跳”。如果他反应正确,他的主人会给他一些狗粮(奖励)。
开始时,小狗并不真正理解他的主人想要什么,也就是说,他不知道(政策)如何将命令正确地“映射”到期望的动作。然而,偶尔他可以以正确的方式做出反应并获得奖励,并逐渐建立他们之间的联系(更新政策)。
经过多次尝试,他终于知道,每当他听到“坐”或“站”这个词时,他都应该“坐”。但是不管他的主人让他跳多少次,他都不知道该怎么办。在这种情况下,他几次试图坐着或站着,但都没有得到任何回报。
最终小狗选择 c .对于“jump”命令什么都不做是因为与其他动作相比,这个选项耗费的精力要少得多(所以负面奖励也少)。
强化学习 vs .监督学习
在监督学习中,算法从指令中学习。每个实例都有一个要比较的估计目标,以便计算差异的成本,并且通过迭代最小化成本来更新算法,因此该过程在某种程度上是由目标输出“指示”的,它告诉什么是正确的结果。
然而,在强化学习中,通过评估来学习策略。样本中没有这样的绝对目标可以比较。代理只能通过不断评估反馈来学习,也就是说,它不断选择一个动作并评估相应的回报,以便调整策略,保留最理想的结果。所以工艺流程要复杂得多。当我们在交易中应用强化学习时,我们需要问自己代理人到底在学习做什么,并且在定义元素时要小心,特别是状态和动作空间。
4.2 基本强化学习问题
N 武装匪徒
Illustration of n-armed bandit problem
问题:上图中,有一个双臂吃角子老虎机。为了使我们的回报最大化,拉哪只胳膊最好?
答:右臂自拉的预期回报大于左臂的。
但是机器如何学习解决这个难题呢?
从 RL 的角度来看,这是 RL 问题中最简单的设置,上面的任务可以总结为以下空间:
- 状态空间:无
- 动作空间:【左】或【右】
- 奖励 : [1]或[0]
在训练过程中,RL 算法将重复上述任务(拉手臂)并评估所获得的回报,并递归地更新其策略。最终,通过评估政策权重,它应该能够给出一个结论,即哪只手臂是最好的拉。在这篇文章中可以看到更好的解释。
上下文强盗
上下文土匪问题是 n 臂土匪的扩展。如上图所示,假设吃角子老虎机不是只有 1 台而是 3 台,我们需要考虑对于特定的机器(状态)来说,拉哪个手臂最好。现在设置变成了:
- 状态空间:【机器 A】、【机器 B】、【机器 C】
- 动作空间:【左】或【右】
- 奖励 : [1]或[0]
4.3 实施
我们到底希望机器学会执行什么?遵循对于每一对时间序列的思想,它通过选择历史窗口、交易窗口、交易阈值和止损[动作]的最佳组合,学习最大化预期交易利润[报酬]。
换句话说,我们将其公式化为一个 N 武装土匪问题(无状态):
- 状态空间:【无】(由虚拟状态确定—交易成本)
- 动作空间:【历史窗口】、【交易窗口】、【交易阈值】、【止损】、【信心水平】
- 奖励:【平均回报】
第 5 部分:将所有东西放在一起
现在我们可以走了。下面是的实现**😗*
- 加载相关配置和价格数据
- 将它们标准化并分成训练集和测试集
- 创建状态空间和动作空间
- 创建和构建网络
- 创建学习对象并进行培训
- 从学习对象中提取记录并执行测试分析
S 设置
对 : JNJ-PG
数据周期:2018–01–01 至 2018–07–30
频率 : 1 分钟
状态:无(设置为固定交易成本 0.1%)
动作 :
—一、历史窗口:60 至 600 分钟,60 分钟步长
—交易窗口:120 到 1200 分钟,120 分钟一步
—三。交易门槛:(+/-)1 到 5,价格步长为 1
— iv。止损:(+/-)1 到 2 在交易阈值之上,价格步长为 0.5
—五、信心水平:90%或 95%
获利回吐水平 : 0
报酬:平均回报(如果是协整,否则设置为交易成本)
交易数量 : 1 个买卖信号的价差
校准价格:标准化
【T33
试运行后,我发现玻尔兹曼探索的概率输出可以达到 1。为了减轻异常高回报的影响,平均回报上限为 10 英镑。
配置
config_train.yml
步骤 1 和 2:加载相关的配置和价格数据,将它们标准化并分成训练集和测试集
步骤 3:创建状态空间和动作空间
步骤 4:创建和构建网络
步骤 5:创建学习对象并进行培训
步骤 6:从学习对象中提取记录,并执行测试分析
从培训结果来看,尽管有上限,平均奖励仍为正:
Positive expected reward in training
Distribution of training reward
以下测试使用从训练结果中获得的最佳操作,在每分钟进行交易,不包括最大可能的历史窗口和交易窗口:
****
No. of trades (pair) [LHS] and PnL [RHS] across testing samples (1-minute, 2018–5–29 to 2018–7–30)
或者,我们也可以使用滑索和 Pyfolio 进行更复杂的回溯测试。
虽然结果看起来很有希望,但在现实世界中,情况会因许多因素而变得复杂,如买卖价差、执行延迟、保证金、利息、零股等。然而,我们在这里的目的是给出一个例子,说明如何将各种技术结合起来,开发一个具有结构化机器学习组件的系统化交易工具。我希望这是一个愉快的页面给你。
回到第 2 部分:代码设计
Illustration of the code structure
2.1 配置
执行由配置**(字典)管理。这个组件允许我们封装大量的执行并整理代码。它也可以用作附加参数的载体。**
例如,在上一节中, API 的实例化。Tiingo 将配置作为输入,将其设置为一个属性。当它调用底层函数时,将从配置中提取输入参数,如开始日期、结束日期、令牌、每天的样本数量和数据频率。
config_data.yml for data fetching
目前只实现了一个配置。理想情况下,我们应该为不同的组件实现多种配置。
使用 PyYAML 包,代码可以识别T5 的字段。yaml /** 。 yml 文件并自动转换格式*😗**
***-空字段:加载到 **None
- True/False** :加载到布尔字段 True 或 **False
- 1.0** :加载到 float **1.0
- 1** :加载到 integer **1
- string** :加载到**‘string’
-【1,2,3】【T32*****
*****Folder:
Folder A:** Math Notes
**Folder B:** [Memo, Magazines]***
该包可以识别缩进并将其加载到字典中:
***{'Folder A': 'Math Notes', 'Folder B': ['Memo', 'Magazines']}***
检查 UTIL/FileIO.py 的读写功能:
2.2 数据 API
对于这一点,我们已经讨论了主要细节,所以我将跳过这一点。如果你想添加另一个 API,我建议你简单地创建另一个类,使用与类 Tiingo 中的 fetch 相同的接口。
2.3 战略
在中。/STRATEGY* 每个模块包含一个策略类别,每个策略应该由一个类表示。该类继承自一个抽象基类,该抽象基类要求它实现以下内容:***
- process() :由机器学习脚本在训练或测试时调用
- 奖励:定义 RL 奖励(即交易利润)的属性
- 记录:训练期间要存储的任何其他属性
在这个包中,我们可以找到一个策略类 EGCointegration ,它在实例化期间接受价格数据 x 和 y 以及其他参数。当底层函数需要样本数据集时,它们将调用 get_sample 函数从其数据属性中执行采样。
EGCointegration class in ./Strategy/Cointegration.py
在训练阶段,在每次迭代中,我们需要校准 p 值和系数,以决定是否以及如何触发配对交易。这些执行嵌入在同一个类中。当调用进程时,对象将自动从其数据属性执行采样并运行校准。基于校准的结果,该功能将获得奖励和记录,并将其设置为相应的属性。**
参见第三部分中关于协整及其测试的更多内容。**
Key functions for calibration in EGCointegration
2.4 基本构建模块、处理器和 ML 算法
这些组件高度集成,不仅由配置管理,而且由控制整个高度自动化的 ML 流程的定制代理管理。许多 ML 算法是硬编码的。这意味着如果需要微调逻辑,就必须修改代码,这有点不方便。在这里,虽然设计有点复杂,如果你能理解的风格,你将能够以任何方式扩展它。
最近,谷歌发布了一个名为 TF-Agents 的强化学习(RL)开源库。请随意检查这个出来。一些概念是相似的,但是我们代码的主要焦点是自动化,所以如果你想构建一个新的,你可以使用它作为基础。
2.4.1 基本构建模块
- 代理人
Agent class in Basic.py
它是 ML 中运行和控制流程的主体。在 RL 中,它有另一层含义:一般来说,它是接收环境状态并相应地决定采取什么行动的组件。代理类应该由机器学习类继承。它应该由一个网络对象和一个配置字典启动。主要功能包括:**
***- 对接:附加网络输入输出层
- assign_network :给代理对象分配新网络并连接
- set_session :设置 tensor flow
-get _ counter:从 config 中提取参数并获取一个 StepCounter 对象的字典用于循环或增量如变概率
-save _ model/restore _ modelckpt 文件 - 流程:训练或测试要实现的抽象方法***
- 网络
构建张量流神经网络的一种典型方式是这样的,其中的层和每个层中的参数都是硬编码的:
或者,我们也可以构建一个重复上述过程的函数,牺牲设置层参数的灵活性。
如果你想建立一个 ML 系统或一些带有 GUI 的东西,在保留自动化的同时,灵活地定制每一层的细节(即层类型、层输入、层参数),这里有一个建议:
Network and TFLayer in Basics.py
左边的两个函数在类网络下。**
- build_layers :它将一个字典 layer_dict 作为输入,通过依次添加从 TFLayer 类中选择的层来构建网络,如右侧所示。只要正确定义了每层的参数,就可以递归调用该函数,在当前网络中现有的最终层之上添加层。每个层都被设置为网络对象的属性,因此它们的名称必须是唯一的。**
- add_layer_duplicates :与 build_layers 类似,它接受一个 layer_dict 作为输入,并需要一个 n_copy 的输入,该输入指定在现有网络之上应该添加多少个由 layer_dict 规定的层的副本。通过连接副本中的图层名称和图层编号,将为复制的图层创建新名称。
例如:
创建网络的步骤:
- 发起一个网络对象。这必须由第一个输入层实例化,在本例中是 tf.placeholder。
- 基于 layer_dict1 构建网络。它指定了两个层:一个是实际上具有 5 个输出的 tf.one_hot 的 ‘one_hot’ 层,另一个是具有 10 个输出的 TF . contrib . layers . fully _ connected 的 ‘coint1’ 层。TF . contrib . layers . fully _ connected 的输入参数由关键字‘layer _ para’定义。
- 通过添加 layer_dict2 规定的层的副本来扩展网络。具有 10 个输出的层‘coin T2’被添加到当前网络 3 次。
因此,网络对象 N 现在总共应该有 6 个属性。它们中的每一个都是具有预定义属性的层:
由于网络的构建是基于层字典的,如果这种字典的生成是流线型的,那么自动化就开始起作用,并且我们不再需要在每次构建新的东西时对网络进行硬编码。
- 空间
基本上它指的是一个样本空间物体。它将一个 list 字典作为输入,并通过对 list 元素进行完全组合来创建样本空间。例如,对于以下示例空间:
***space_dict = {'dice': [1, 2, 3, 4, 5, 6],
'coin': ['H', 'T']}
S = Space.states_dict***
S 包含骰子和硬币的所有组合*,共 12 个元素。它包含必要的函数,可将样本从字典转换为单个索引、索引列表或 one_hot 数组,反之亦然,以适应 TensorFlow 中不同类型的输入或输出载体。***
- 计步器
在训练期间,一些参数是递增的,例如 for 循环中的当前步长,或者学习速率被设置为可变的。我们甚至可能希望在实际步骤被触发之前添加一个缓冲区(即,学习速率在 100 次循环之后开始下降)。我们可以用一个计数器来执行上述操作,而不是在脚本中硬编码。该计数器还具有缓冲预训练步骤的能力。例如,实际计数值仅在 100 个缓冲步骤之后才开始改变。
2.4.2 处理器
一个处理器类应该将一个代理对象作为初始化的输入。当进程被调用时,它将从代理对象中提取相关参数,包括附加的配置字典,并将任何输出附加到作为代理属性的数据字典。我们实际上可以创建另一个对象来携带这些属性,但是为了简单起见,我们不要在这里重载这个结构。**
- 状态空间和动作空间
两者都继承父类空间*,用于生成状态样本或动作样本。基于配置中指定的 方法 ,它们可以以不同的形式(即索引/一个热点/字典)或不同的方式(有/无探索)输出样本,用于不同的目的,如网络训练,或作为策略对象中 过程 函数的输入。***
StateSpace and ActionSpace in PROCESSOR/MachineLearning.py
- 奖励引擎
它采用一个包含进程方法的引擎对象。在我们的例子中,它将是一个 EGCointegration 对象。
RewardEngine class
- 探索
这篇文章很好的介绍了强化学习中的探索方法。该对象的目的是探索可能的操作。所选择的方法将向代理对象中的数据载体返回动作索引。当调用 ActionSpace 中的进程函数时,就实现了探索。
- 经验缓冲
这利用了这篇文章中的体验回放实现。目的是在训练过程中存储样本和结果,并从缓冲区重新采样,以允许代理从历史中重新学习。
- 记录器
最后但同样重要的是,我创建了一个记录器类,它可以用来跟踪存储在代理对象内部的数据字典中的记录。我们可以通过在配置文件的recorder data field字段中指定键名来选择我们希望它存储的字段:
****RecorderDataField:** [NETWORK_ACTION, ENGINE_REWARD]**
Recorder class
2.4.3 毫升算法
有了上面描述的组件,我们可以定制任何采用这些构建块的类,并创建一个运行过程。这是唯一需要为不同目的定制的部分,但对于类似的情况,逻辑仍然是相当标准化的。
例如,在这个项目中,我创建了一个上下文强盗类,它实际上可以执行 N 臂强盗或者上下文强盗运行,取决于状态的数量。如果我们想对 N 臂土匪问题运行它,我们可以只指定一个具有单一固定状态(虚拟)的状态空间。
ContextualBandit class in MAIN/Reinforcement.py
- init :初始化对象并继承父方法和属性。TensorFlow 机器学习属性也在这里定义。之后上面描述的所有处理器都会被组合实例化,以对象本身作为输入实参(【agent】)。
- update_network :从数据字典中提取样本,更新张量流层和网络。
- 缓冲:如果配置中有指定,将样本存储在 ExperienceBuffer 对象中。
- create_sample_list :为经验缓冲创建样本。
- 流程:控制培训或测试流程的主要程序。需要一个 tf。Session()并根据代理启动的 StepCounter 对象中的值执行循环。
放弃
这篇文章和相关的代码和内容纯粹是信息性的,所提供的任何信息都不构成对任何特定人的任何安全、交易或投资策略的任何建议。本文中描述的实施可能会有风险,市场状况可能会不稳定,并与上述时期不同。所有交易策略和工具的实施均由用户自担风险。
文献学
[1] Dickey,D. A .,Fuller,W. A .,具有单位根的自回归时间序列估计量的分布(1979),美国统计协会杂志。74(366): 427–431.
[2] Engle,R.F .,Granger,C.W.J .,《协整和误差修正:表示、估计和检验》(1987),计量经济学55(2):251–276
*[3] Gatev,e .、Goetzmann,W.N .和 Rouwenhorst,K.G .,《配对交易:相对价值套利规则的表现》(2006 年),*《金融研究评论》19(3):797–827
[4] Granger,C.W .,时间序列数据的一些性质及其在计量经济模型规范中的应用(1981),经济学杂志16(1):121–130
*[5] Johansen,s .,协整向量的统计分析(1988),*《经济动态与控制杂志》12(2–3):231–254
[6] Krauss,c .,统计套利对交易策略:回顾与展望(2017),经济学调查杂志31(2):513–545
[7] Stock,J.H .,协整向量的最小二乘估计的渐近性质(1987),计量经济学55:277–302。
[8]萨顿,R.S .,巴尔托,A.G,《强化学习:导论》( 1998),麻省理工学院出版社,第二版
算法公平性的温和介绍
算法公平问题的温和介绍:一些美国历史,法律动机,和四个带有反证的定义。
历史
在美国,借贷中的公平问题由来已久。
例如,划红线:
1935 年,联邦住房贷款银行委员会要求住房所有者贷款公司调查 239 个城市,并绘制“住宅安全地图”,以显示每个被调查城市的房地产投资安全水平。在地图上,“D 型”社区用红色标出,被认为是抵押贷款支持风险最高的地区…
’20 世纪 60 年代,社会学家约翰·麦克奈特创造了“红线”一词,用来描述银行基于社区人口统计数据而避开投资领域的歧视性做法。在红线的全盛时期,最常受到歧视的地区是市中心的黑人社区……’
划红线显然是不公平的,因为投资的决定不是基于个人房主偿还贷款的能力,而是基于位置;这个基础系统地拒绝贷款给一个种族群体,黑人。事实上,1988 年在《亚特兰大宪法日报》上发表的普利策奖系列文章第一部分表明,位置比收入更重要:“T10 在收入相同的稳定社区中(在亚特兰大市区),每 1000 户家庭中,白人社区总是获得最多的银行贷款。融合的社区得到的总是较少。黑人社区——包括市长的社区——收到的总是最少。
1968 年《公平住房法案》和 1977 年《社区再投资法案》就是为了打击住房和贷款中的这类不公平行为而通过的。
最近,在 2018 年,WUNC 报告称北卡罗来纳州一些城市的黑人和拉丁美洲人被拒绝的抵押贷款利率高于白人:
贷方和他们的贸易组织不否认他们拒绝有色人种的比率远远高于白人。但他们坚持认为,这种差异可以用该行业竭力隐瞒的两个因素来解释:潜在借款人的信用记录和整体债务收入比。他们特别指出三位数的信用评分——银行用来确定借款人是否有可能偿还贷款——在贷款决策中尤为重要。"
WUNC 的例子提出了一个有趣的观点:通过一个指标(按人口统计的贷款利率)看起来不公平是可能的,但通过另一个指标(根据信用历史和债务收入比判断的支付能力)看起来不公平是不可能的。衡量公平是复杂的。在这种情况下,我们无法判断贷款行为是否公平,因为我们无法获得这些特定群体的信用历史和债务收入比数据,来评估贷款人对差异的解释。
2007 年,美国联邦储备委员会(FRB) 报告了关于信用评分及其对信贷可用性和可负担性的影响。他们得出结论信用历史评分模型中包含的信用特征并不能代表种族,尽管不同的人口统计群体平均而言有着显著不同的信用评分,并且*“对于给定的信用评分,不同的人口统计群体的信用结果——包括贷款绩效、可用性和可负担性的衡量指标——也是不同的。”*该 FRB 研究支持贷方的主张,即信用评分可能解释抵押拒绝率的差异(因为人口统计群体具有不同的信用评分),同时也指出不同群体的信用结果不同。
这公平不公平?
定义公平
随着机器学习(ML)的普及,人们对 ML 中的公平、问责和透明越来越感兴趣(例如 fat* 会议和 fatml 研讨会)。
一些研究人员说,公平不是一个统计学概念,没有任何统计数据能够完全捕捉到它。有许多统计定义,人们试图联系(如果不是定义)公平。
首先,这里有两个在许多关于公平的讨论中出现的法律概念:
- 不同的待遇 :“根据《美国民权法案》第七章,由于受保护的特征(如种族或性别)而对某人的不平等行为。”如果目的是拒绝黑人贷款,红线就是完全不同的待遇。
- 三教九流冲击 :“惯例…这对具有受保护特征的一组人的不利影响比对另一组人的不利影响更大,即使规则适用…在形式上是中立的。”(“完全不同的影响原则在具有里程碑意义的美国最高法院格里戈斯诉杜克电力公司案(1971)中正式确立。1955 年,杜克电力公司制定了一项政策,规定雇员必须有高中文凭才能被考虑晋升,这大大限制了黑人雇员的资格。法院发现这一要求与工作表现几乎没有关系,因此认为它具有不合理的——也是非法的——完全不同的影响。”【corb 2018】)
[Lipt2017]指出这些是差异的法律概念,并为应用于机器学习分类器的奇偶技术概念创建了相应的术语:
- 处理奇偶校验:分类器应该对给定的受保护特征不敏感。在[Corb2018]中也被称为反分类,或“通过无意识的公平”
- 影响均等:在不同的群体中,做出肯定决定的人的比例应该是均等的。这也叫人口统计奇偶性,统计奇偶性,或者被保护阶层与分数的独立性[Fair2018]。
有大量关于算法公平性的文献。从[Corb2018]开始,又多了两个定义:
- 分类奇偶校验:某个给定的分类错误度量在由受保护属性定义的组之间是相等的。[Hard2016]称此为均等机会如果测量值为真阳性率,以及均等几率如果有两个均等测量值,即真阳性率和假阳性率。
- 校准:以风险分值为条件,结果独立于受保护属性。也就是现实符合风险评分。例如,预计有 20%违约几率的所有贷款中,约有 20%实际上违约了。
研究界对公平的理想统计定义缺乏共识。事实上,同时实现多个公平概念是不可能的结果([Klei2016] [Chou2017])。正如我们之前提到的,一些研究人员认为公平不是一个统计概念。
没有完美的定义
上面描述的每个统计定义都有反例。
待遇平等不公平地忽略了真正的差异。[Corb2018]描述了 COMPAS 评分用于预测累犯(某人如果出狱是否会犯罪)的案例。在控制了 COMPAS 评分和其他因素后,女性复发的可能性降低了。因此,在这个预测中忽略性可能会不公平地惩罚女性。请注意,平等信贷机会法案从法律上规定了待遇平等:“债权人在某些情况下可能会向你索要【受保护的阶级信息,如种族】,但在决定是否给你信贷或设定信贷条款时,他们可能不会使用这些信息。“因此,[Corb2018]暗示这种不公平是法律规定的。
影响均等并不能确保公平(人们反对配额),而且会削弱模型的准确性,损害模型对社会的效用。[Hard2016]在其导言中讨论了这个问题(使用术语“人口均等”)。
Corbett 等人[Corb2018]详细论述了分类奇偶性自然被违反:“当暴力累犯的基本比率在不同群体之间不同时,真实的风险分布也必然不同——无论在预测中使用了哪些特征,这种差异都将持续存在。
他们还认为,校准不足以防止不公平。他们假设的例子是,一家银行只根据邮政编码内的违约率发放贷款,而忽略了收入等其他属性。假设(1)在邮政编码范围内,白人和黑人申请者有相似的违约率;(2)黑人申请者居住在违约率相对较高的邮政编码区。那么,该银行的计划将会不公平地惩罚信誉良好的黑人申请者,但仍然是经过校准的。
结论
总之,可能的公平没有单一的衡量标准。我们对四个统计定义进行了旋风式的考察,其中两个由历史驱动,两个最近由机器学习驱动,并总结了每个定义的反驳意见。
这也意味着自动决定一个算法是否公平具有挑战性。开源公平测量包通过提供许多不同的测量方法来反映这一点。
然而,这并不意味着我们应该忽略统计数据。他们可以给我们一个主意,我们是否应该更仔细地看。精神食粮。我们应该好好喂养我们的大脑,因为它最有可能做出最后的决定。
(注意:这个问题是有争议的。我们的目的是以富有成效、尊重的方式加入对话。我们欢迎任何形式的反馈。)
感谢 克里希纳拉姆扎克·利普顿卢克·梅里克阿米特·卡帕 ,以及 克里希纳·加德 的反馈**
参考
- 亚历山德拉·乔尔德乔娃。"具有不同影响的公平预测:对累犯预测工具偏差的研究."大数据 5,第 2 期(2017 年 6 月 1 日):153–63。https://doi.org/10.1089/big.2016.0047。
- 科比特-戴维斯、萨姆和沙拉德-戈埃尔。"公平的测量和错误测量:公平机器学习的评论."ArXiv:1808.00023 [Cs],2018 年 7 月 31 日。http://arxiv.org/abs/1808.00023。
- [Fair2018]“公平与机器学习。”2019 年 4 月 9 日接入。https://fairmlbook.org/。
- [哈德 2016]哈特、莫里茨、埃里克·普莱斯和内森·斯雷布罗。"监督学习中的机会均等."ArXiv:1610.02413 [Cs],2016 年 10 月 7 日。http://arxiv.org/abs/1610.02413。
- [Klei2016]克莱恩伯格、乔恩、森迪尔·穆莱纳坦和马尼什·拉格哈万。"公平确定风险分值的内在权衡."ArXiv:1609.05807 [Cs,Stat],2016 年 9 月 19 日。http://arxiv.org/abs/1609.05807。
- 利普顿、扎卡里·c、亚历山德拉·乔尔德乔娃和朱利安·麦考利。"减轻 ML 的影响差异需要治疗差异吗?"ArXiv:1711.07076 [Cs,Stat],2017 年 11 月 19 日。【http://arxiv.org/abs/1711.07076】T42。
原载于 2019 年 4 月 23 日https://blog . fiddler . ai。**
阿帕奇箭与阿帕奇火花和熊猫的温柔介绍
这一次我将尝试解释如何将 Apache Arrow 与 Apache Spark 和 Python 结合使用。首先,让我分享一些关于这个开源项目的基本概念。
Apache Arrow 是内存数据的跨语言开发平台。它为平面和层次数据指定了一种标准化的独立于语言的列内存格式,为现代硬件上的高效分析操作而组织。[ 阿帕奇箭头页
简而言之,它促进了许多组件之间的通信,例如,用 Python (pandas)读取 parquet 文件并转换为 Spark 数据帧、 Falcon 数据可视化或 Cassandra 而不用担心转换。
Overview Apache Arrow [Julien Le Dem, Spark Summit 2017]
一个好问题是问数据在内存中是什么样子的?Apache Arrow 利用列缓冲区来减少 IO 并提高分析处理性能。
Columnar In-memory [Apache Arrow page]
在我们的例子中,我们将使用 pyarrow 库来执行一些基本代码并检查一些特性。为了安装,我们有两个选项使用 conda 或 pip 命令*。
conda install -c conda-forge pyarrowpip install pyarrow
*建议在 Python 3 环境中使用 conda。
Apache Arrow with Pandas(本地文件系统)
将 Pandas 数据帧转换为 Apache 箭头表
import numpy as np
import pandas as pd
import pyarrow as pa
df = pd.DataFrame({'one': [20, np.nan, 2.5],'two': ['january', 'february', 'march'],'three': [True, False, True]},index=list('abc'))
table = pa.Table.from_pandas(df)
Pyarrow 表到熊猫数据框
df_new = table.to_pandas()
读取 CSV
from pyarrow import csv
fn = ‘data/demo.csv’
table = csv.read_csv(fn)
df = table.to_pandas()
从 Apache 编写拼花文件箭头
import pyarrow.parquet as pq
pq.write_table(table, 'example.parquet')
阅读拼花文件
table2 = pq.read_table(‘example.parquet’)
table2
从拼花文件中读取一些列
table2 = pq.read_table('example.parquet', columns=['one', 'three'])
从分区数据集读取
dataset = pq.ParquetDataset(‘dataset_name_directory/’)
table = dataset.read()
table
将拼花文件转换成熊猫数据帧
pdf = pq.read_pandas('example.parquet', columns=['two']).to_pandas()
pdf
避开熊猫指数
table = pa.Table.from_pandas(df, preserve_index=False)
pq.write_table(table, 'example_noindex.parquet')
t = pq.read_table('example_noindex.parquet')
t.to_pandas()
检查元数据
parquet_file = pq.ParquetFile(‘example.parquet’)
parquet_file.metadata
参见数据模式
parquet_file.schema
时间戳
记住熊猫使用纳秒,所以为了兼容你可以用毫秒截断。
pq.write_table(table, where, coerce_timestamps='ms')
pq.write_table(table, where, coerce_timestamps='ms', allow_truncated_timestamps=True)
压缩
默认情况下,Apache arrow 使用 snappy 压缩(不那么压缩,但更容易访问),尽管也允许使用其他编解码器。
pq.write_table(table, where, compression='snappy')
pq.write_table(table, where, compression='gzip')
pq.write_table(table, where, compression='brotli')
pq.write_table(table, where, compression='none')
此外,可以在一个表中使用多种压缩
pq.write_table(table, ‘example_diffcompr.parquet’, compression={b’one’: ‘snappy’, b’two’: ‘gzip’})
写一个分区拼花表
df = pd.DataFrame({‘one’: [1, 2.5, 3],
‘two’: [‘Peru’, ‘Brasil’, ‘Canada’],
‘three’: [True, False, True]},
index=list(‘abc’))
table = pa.Table.from_pandas(df)
pq.write_to_dataset(table, root_path=’dataset_name’,partition_cols=[‘one’, ‘two’])
- 兼容性说明:如果您使用 pq.write_to_dataset 创建一个将由 HIVE 使用的表,则分区列值必须与您正在运行的 HIVE 版本的允许字符集兼容。
带 HDFS(远程文件系统)的 Apache Arrow】
Apache Arrow 附带了与 Hadoop 文件系统的基于 c++(T3)的接口的绑定。这意味着我们可以从 HDFS 读取或下载所有文件,并直接用 Python 解释。
连接
主机是 namenode,端口通常是 RPC 或 WEBHDFS 更多的参数像用户,kerberos 票证是允许的。强烈建议阅读所需的环境变量。
import pyarrow as pa
host = '1970.x.x.x'
port = 8022
fs = pa.hdfs.connect(host, port)
- 可选,如果您的连接是在数据或边缘节点可能使用的前端
fs = pa.hdfs.connect()
将拼花文件写入 HDFS
pq.write_to_dataset(table, root_path=’dataset_name’, partition_cols=[‘one’, ‘two’], filesystem=fs)
从 HDFS 读取 CSV
import pandas as pd
from pyarrow import csv
import pyarrow as pa
fs = pa.hdfs.connect()
with fs.open(‘iris.csv’, ‘rb’) as f:
df = pd.read_csv(f, nrows = 10)
df.head()
Reading CSV from HDFS
从 HDFS 读取拼花文件
从 HDFS 读取拼花文件有两种形式
使用熊猫和 Pyarrow 引擎
import pandas as pd
pdIris = pd.read_parquet(‘hdfs:///iris/part-00000–27c8e2d3-fcc9–47ff-8fd1–6ef0b079f30e-c000.snappy.parquet’, engine=’pyarrow’)
pdTrain.head()
Pyarrow .拼花地板
import pyarrow.parquet as pq
path = ‘hdfs:///iris/part-00000–71c8h2d3-fcc9–47ff-8fd1–6ef0b079f30e-c000.snappy.parquet’
table = pq.read_table(path)
table.schema
df = table.to_pandas()
df.head()
其他文件扩展名
因为我们可以存储任何类型的文件(SAS、STATA、Excel、JSON 或 objects),所以大多数文件都很容易被 Python 解释。为了实现这一点,我们将使用 open 函数返回一个缓冲区对象,许多 pandas 函数如 read_sas 、 read_json 可以接收这个对象作为输入,而不是一个字符串 URL。
斯堪的纳维亚航空公司
import pandas as pd
import pyarrow as pa
fs = pa.hdfs.connect()
with fs.open(‘/datalake/airplane.sas7bdat’, ‘rb’) as f:
sas_df = pd.read_sas(f, format='sas7bdat')
sas_df.head()
擅长
import pandas as pd
import pyarrow as pa
fs = pa.hdfs.connect()
with fs.open(‘/datalake/airplane.xlsx’, ‘rb’) as f:
g.download('airplane.xlsx')
ex_df = pd.read_excel('airplane.xlsx')
JSON
import pandas as pd
import pyarrow as pa
fs = pa.hdfs.connect()
with fs.open(‘/datalake/airplane.json’, ‘rb’) as f:
g.download('airplane.json')
js_df = pd.read_json('airplane.json')
从 HDFS 下载文件
如果我们只是需要下载文件,Pyarrow 为我们提供了下载功能,将文件保存在本地。
import pandas as pd
import pyarrow as pa
fs = pa.hdfs.connect()
with fs.open(‘/datalake/airplane.cs’, ‘rb’) as f:
g.download('airplane.cs')
上传文件到 HDFS
如果我们只是需要下载文件,Pyarrow 为我们提供了下载功能,将文件保存在本地。
import pyarrow as pa
fs = pa.hdfs.connect()
with open(‘settings.xml’) as f:
pa.hdfs.HadoopFileSystem.upload(fs, ‘/datalake/settings.xml’, f)
阿帕奇箭带阿帕奇火花
Apache Arrow 从版本 2.3 开始与 Spark 集成,存在关于优化时间、避免序列化和反序列化过程以及与其他库集成的良好演示,如来自 Holden Karau 的关于在 Spark 上加速 Tensorflow Apache Arrow 的演示。
还有其他有用的文章,比如由 Brian Cutler 发表的文章,以及 Spark 的官方文档中非常好的例子
Apache Arrow 的一些有趣用法是:
- 加速从 Pandas 数据帧到 Spark 数据帧的上转换
- 加速从 Spark 数据帧到 Pandas 数据帧的上转换
- 使用熊猫 UDF(又名矢量化 UDF)
- 使用 Apache Spark 优化 R
第三项将是下一篇文章的一部分,因为这是一个非常有趣的话题,以便在不损失性能的情况下扩展 Pandas 和 Spark 之间的集成,对于第四项,我建议您阅读文章(发表于 2019 年!)去了解更多。
让我们先测试熊猫和 Spark 之间的转换,不做任何修改,然后允许 Arrow。
from pyspark.sql import SparkSession
warehouseLocation = “/antonio”
spark = SparkSession\
.builder.appName(“demoMedium”)\
.config(“spark.sql.warehouse.dir”, warehouseLocation)\
.enableHiveSupport()\
.getOrCreate()#Create test Spark DataFrame
from pyspark.sql.functions import rand
df = spark.range(1 << 22).toDF(“id”).withColumn(“x”, rand())
df.printSchema()#Benchmark time
%time pdf = df.toPandas()
spark.conf.set(“spark.sql.execution.arrow.enabled”, “true”)
%time pdf = df.toPandas()
pdf.describe()
在结果中使用箭头来减少时间转换显然更方便。
Optimizing transformation from Spark Data Frame to Pandas
如果我们需要测试相反的情况(熊猫引发 df ),我们也能及时看到优化。
%time df = spark.createDataFrame(pdf)
spark.conf.set("spark.sql.execution.arrow.enabled", "false")
%time df = spark.createDataFrame(pdf)
df.describe().show()
总之
这篇文章的目的是发现和理解 Apache Arrow,以及它如何与 Apache Spark 和 Pandas 一起工作,我还建议你查看它的官方页面,以了解更多关于其他可能的集成,如 CUDA 或 C++,如果你想更深入地了解 Apache Spark,我认为 Spark:权威指南是一本很好的书。
PS 如果你有任何问题,或者想要澄清一些事情,你可以在 Twitter 和 LinkedIn 找到我。我最近发表了 一篇关于 Apache Druid 的温和介绍,这是一个新的 Apache 项目,非常适合分析数十亿行。
[## Google 云平台中 Apache Druid 的温和介绍
使得分析数十亿行变得容易
towardsdatascience.com](/a-gentle-introduction-to-apache-druid-in-google-cloud-platform-c1e087c87bf1)
Google 云平台中 Apache Druid 的温和介绍
使得分析数十亿行变得容易
Druid UI
概念和目的
为了对阿帕奇德鲁伊有一个清晰的理解,我将参考官方文档中的内容:
Apache Druid(孵化)是一个实时分析数据库,旨在对大型数据集进行快速切片分析(【OLAP】【查询】)。Druid 最常被用作支持实时接收、快速查询性能和高正常运行时间非常重要的用例的数据库。
Druid Logo [Meetup Group Photo Album]
在我的 Druid 简历中,我们不仅可以批量分析数十亿行,还可以实时分析,因为它与不同的技术有许多集成,如 Kafka、云存储、S3、Hive、HDFS、DataSketches、Redis 等。
德鲁伊最有趣的特征是:
- 云原生,轻松实现水平扩展
- 支持 SQL 分析数据
- API REST 支持查询或上传数据
在 GCP 测试 Druid 的目的是需要一种工具,可以批量上传数百万行(面向事件),然后使用传统的数据可视化工具进行分析,如 Tableau 或 Qlik,将 Druid 作为主要的处理引擎,所以因为我不知道服务器的正确数量,所以必须使用灵活的环境,如 GCP,并从最简单的部署开始。
一般建筑
对于一个生产集群,Druid 主要由 6 个进程组成:协调器、霸王、代理、历史、中间管理器和路由器,其中建议组织成 3 种类型的服务器:主服务器、查询服务器和数据服务器。根据需要,这些服务器可能不止一台。
主人
由协调者和支配者组成。
管理数据接收和可用性:它负责启动新的接收作业,并协调“数据服务器”上数据的可用性
查询
由路由器和代理组成。
提供用户和客户端应用程序交互的端点,将查询路由到数据服务器或其他查询服务器[ Druid 文档
数据
由中层管理者和历史管理者组成。
执行摄取作业并存储可查询的数据[ Druid 文档
其他组件
除了三个服务器和六个服务之外,Druid 还需要一个元数据存储,深度存储对于理解 Druid 读取数据的来源以及负责与所有组件通信也很重要。
基本上用于存储关于系统的元数据(审计、数据源模式、任务等。).对于实验环境,建议使用 Derby ,尽管对于生产目的,MySQL 或 PostgreSQL 是最佳选择。
Derby for a non-production cluster [Apache Derby website]
Druid 为接收的数据使用单独的存储,这意味着该过程与存储分离,使其成为一种容错技术。一些深度存储技术有云存储、亚马逊 S3、HDFS、Redis 等。
Google Cloud Storage Logo [DataKitchen]
Druid 中的所有数据都被组织成 段、,这些数据文件通常每个都有几百万行。在 Druid 中加载数据被称为摄取或索引 ,包括从源系统读取数据并基于该数据创建数据段。[ 德鲁伊文献 ]
德鲁伊完全支持批处理和流式摄取,支持的一些技术有 Apache Kafka、Kinesis、云存储和本地存储。
Druid support Streaming sources like Apache Kafka [Kafka Website]
德鲁伊使用 Zookeeper 来整合所有的服务。对于实验,你可以使用德鲁伊自带的 Zookeeper,而对于生产来说,必须单独安装它,一个好的做法是为 Zk 准备一个自己的服务器。
ZooKeeper is the coordinator for all the Druid services [Wikipedia]
初始图
考虑到所有组件,我们可以创建一个代表生产集群 Google 云平台的架构图。
GCP Diagram
建立在 GCP 的基础上
对于本文,我将使用 Druid 独立服务器,Apache Derby 作为深度存储,云存储作为元数据服务器。虽然它包含了所有的特性,所以我们没有遗漏任何东西。
德鲁伊单服务器
我们将按照文档中的建议将 Druid 部署为 IAS,好的配置是 4CPU/16GB RAM。您可以使用 UI 或通过 Cloud Shell 提交来创建计算实例。
#Using Cloud Shellgcloud beta compute --project=[PROJECT-ID] instances create instance-druid --zone=us-central1-a --machine-type=n1-standard-4 --subnet=default --network-tier=PREMIUM --maintenance-policy=MIGRATE --service-account=[SERVICE-ACCOUNT] --scopes=https://www.googleapis.com/auth/devstorage.read_write,https://www.googleapis.com/auth/logging.write,https://www.googleapis.com/auth/monitoring.write,https://www.googleapis.com/auth/servicecontrol,https://www.googleapis.com/auth/service.management.readonly,https://www.googleapis.com/auth/trace.append --image=debian-9-stretch-v20191014 --image-project=debian-cloud --boot-disk-size=15GB --boot-disk-type=pd-standard --boot-disk-device-name=instance-druid --reservation-affinity=any
重要:默认情况下,您的权限/范围到devstorage (google cloud storage)
是read_only
您需要更改为read_write
。
安装德鲁伊
我们使用 SSH 连接到实例,并遵循以下步骤:
- 下载的最新版本,在当时,是 0.16
- 解压缩文件
wget [https://www-eu.apache.org/dist/incubator/druid/0.16.0-incubating/apache-druid-0.16.0-incubating-bin.tar.gz](https://www-eu.apache.org/dist/incubator/druid/0.16.0-incubating/apache-druid-0.16.0-incubating-bin.tar.gz)
tar -xzf apache-druid-0.16.0-incubating-bin.tar.gz
cd apache-druid-0.16.0-incubating
**注意:**有趣的是,我们下载了与安装生产集群的人相同的 Druid 版本和相同的文件,没有version lite.
这意味着我们可以在以后将其作为主节点、数据节点或查询节点重用。
- 下载动物园管理员
curl https://archive.apache.org/dist/zookeeper/zookeeper-3.4.14/zookeeper-3.4.14.tar.gz -o zookeeper-3.4.14.tar.gz
tar -xzf zookeeper-3.4.14.tar.gz
mv zookeeper-3.4.14 zk
- 浏览文件
如果你这样做了ls conf/druid/cluster
,你将会找到对应于数据服务器、主服务器和查询服务器的文件。在这种情况下,我们可以欣赏 Druid 的模块化,例如,如果您想将此安装用作数据服务器,您只需运行相应的.sh
文件,不需要更改任何内容,这同样适用于 Query 和 Master。
Production folders
在我们的例子中,我们将启动micro-quickstart
Differents size for Druid single-server
正如我们注意到的,这个微型快速入门提供了所有的服务
Druid services included in the single-server
- 安装 Java8 和 Perl
sudo apt-get install openjdk-8-jdk
sudo apt-get install libmodule-install-perl
#${PROJECT} is your GCP project ID
#${ZONE} is the zone of your VMs in this case us-central1-a
gcloud compute ssh ${HOSTNAME} \
--project=${PROJECT} --zone=${ZONE} -- \
-4 -N -L 8081:${HOSTNAME}:8888
- 快跑!(在另一个云壳里)
./bin/start-micro-quickstart
Druid running
- 德鲁伊界面工作!
Druid landing page
深度存储
正如我提到的,默认情况下,深度存储是本地的,因此在这种情况下,我们将修改为指向云存储。
- 停止德鲁伊服务器(Ctrl+c)
_ _common . runtime . properties 这个文件非常重要它包含:
- 德鲁伊扩展
druid.extensions.loadList=["druid-google-extensions", “druid-hdfs-storage”, “druid-kafka-indexing-service”, “druid-datasketches”]
- 动物园管理员 ip
druid.zk.service.host=localhost
druid.zk.paths.base=/druid
- 元数据的引用
druid.metadata.storage.type=derby
druid.metadata.storage.connector.connectURI=jdbc:derby://localhost:1527/var/druid/metadata.db;create=true
druid.metadata.storage.connector.host=localhost
druid.metadata.storage.connector.port=1527
- 深层存储的引用。
druid.storage.type=local
druid.storage.storageDirectory=var/druid/segments
此外,您可以在生产中为所有类型的服务器找到相同的文件。
我们来修改一下:
vi conf/druid/single-server/micro-quickstart/_common/common.runtime.properties
添加德鲁伊扩展druid-google-extensions
注释引用本地,并添加对谷歌云存储的引用
# For Google Cloud Storage
druid.storage.type=goole
druid.google.bucket=[BUCKET-NAME]
druid.google.prefix=[FOLDER-PATH]
退出并保存:wq
元数据
Druid 在使用 MySQL 或 Postgres 时有更好的性能。在这个机会中,我们将启动一个云 SQL 实例并连接到我们的服务器。
注意:在防火墙规则中允许 Druid IP 和 Cloud SQL IP 的入口/出口。
我们再修改一下:
vi conf/druid/single-server/micro-quickstart/_common/common.runtime.properties
- 注释所有对 Derby 的引用,并添加 MySQL 的凭证
druid.metadata.storage.type=mysql
druid.metadata.storage.connector.connectURI=jdbc:mysql://druiddb:3306/druid
druid.metadata.storage.connector.user=.[USER]
druid.metadata.storage.connector.password=[PASSWORD]
- 再次启动我们的服务器
./bin/start-micro-quickstart
基本摄入
Druid 允许 as 上传来自不同来源的数据,将这些数据组织成段(保存在深层存储和 MySQL 的元数据中),我们可以在那里查询并开始分析数据。可以通过四种方式摄取数据:
- 使用 Druid UI(数据加载器)
Data Loader
- JSON 任务
- 命令行
- API Rest
出于我们的目的,我们可以从图形选项开始,并使用可用的快速入门数据。
- 选择加载数据,然后选择本地数据
- 相应地完成基本目录和文件过滤
quickstart/tutorial/
wikiticker-2015-09-12-sampled.json.gz
Uploading data using Druid UI
- 按下 next(建议仔细阅读)并在配置模式步骤中停止,这里我们可以看到在 Pase 时间步骤中添加的**_ _ 时间列**。这个列很重要,因为 Druid 有基于时间的分区。此外,我们可以观察用于汇总的查询粒度字段,保持不变。
Query granularity
- 在最后一步,我们看到 JSON 已经完成了所有的配置,并准备好提交,这将生成一个任务。
JSON with all the configurations
- 有可能看到运行任务生成
Task running
- 成功!
Success
查询数据
用户可以通过 SQL 处理数据,也可以使用 REST API。
Querying data
推荐
- For exploration 建议保留本文中描述的体系结构,因为以后可以将数据迁移到更大的集群中。
- 尝试测试所有的 SQL 函数并注意其优势,例如,再次运行相同的查询,您可以看到时间的显著减少,这是因为 Druid 缓存。
- 如果您有自己的数据,可以上传(CSV 或 JSON)到
apache-druid-0.16.0-incubating/quickstart/tutorial/
- 在上图中,您可以看到一些列,如“sum_added”、“sum_deleted”和“sum_delta”。这些列是在上传期间创建的,您可以省略或添加其他的聚合列。
- 如果您想要更好的性能,建议您在创建虚拟机时选择 SSD 存储。
后续步骤
在德鲁伊中存在许多需要测试的特性,我希望在下一篇文章中介绍。
- 用户管理(角色、身份验证和授权)
- 使用 REST API 上传和查询数据
- 查找
- 连接到数据可视化工具
- 数据和查询服务器的水平扩展
- 上传大数据源的调优
- 查询和并发支持调优
- 其他扩展如 Apache Data Sketchs 或 Redis
- 将数据和元数据迁移到另一个 Druid 集群
- 升级集群
- 摄取流数据
如果您想更进一步部署生产集群,请查看文章表单 Jesús Méndez Galvez 此处。
PS 如果你有任何问题,或者想要澄清一些事情,你可以在 Twitter 和 LinkedIn 上找到我。如果你想了解阿帕奇 Arrow 和阿帕奇 Spark 我有一篇文章对阿帕奇 Arrow 与阿帕奇 Spark 和 Pandas 有一些例子。
这一次我将尝试解释如何将 Apache Arrow 与 Apache Spark 和 Python 结合使用。首先…
towardsdatascience.com](/a-gentle-introduction-to-apache-arrow-with-apache-spark-and-pandas-bb19ffe0ddae)
客户细分的简明介绍
使用 K-Means 聚类来理解营销响应
Photo by Nick Karvounis on Unsplash
概观
我这篇文章的目的是要表明,你并不总是需要超级复杂和精密的机器学习模型来从你的数据中获得有意义的见解。
对于这个迷你项目,我使用流行的 K-Means 聚类 算法,根据客户对一系列营销活动的反应对他们进行细分。这种技术相对容易实现,但它允许我从我的数据中收集大量信息,并挖掘我的客户群中有趣的行为模式。
什么是市场细分?
市场细分 是指根据共同的属性、兴趣和行为将现有和/或潜在客户的消费市场划分为多个群体(或细分市场)的过程。
基本概念是,拥有共同特征的消费者更有可能以相似的方式对营销传播做出回应,这样公司就可以以更相关、更有效的方式接触每个群体。
什么是 K-Means 聚类?
K-Means 聚类是 无监督学习 建模家族的一部分,这是一套用于在尚未标记、分类或归类的数据中寻找模式的技术。由于这种方法不要求有一个聚类目标,它可以在客户细分的探索阶段有很大的帮助。
基本思想是,分配到一个组的客户尽可能相似,而属于不同组的客户尽可能不相似。每个聚类由其centre
表示,对应于分配给该聚类的元素的平均值。
为了说明这个原理,假设你有一组像下图中那样的元素,并想把它们分成 3 个簇
K-Means 将为您做的是将它们分组在每个簇的中间或centre
,这里用**“X”的**表示,以最小化每个元素到其centre
的距离的方式
那么这如何帮助你更好地了解你的客户?嗯,在这种情况下,你可以利用他们的行为(具体来说,他们选择或不选择哪种优惠)作为一种方式,将他们与想法相似的客户分组。现在,您可以研究每一组,发掘趋势和模式,并用它们来塑造未来的产品。
稍微技术性一点的说明,重要的是要提到有许多 K-Means 算法可用( Hartigan-Wong 、 Lloyd 、 MacQueen 等等),但它们都有相同的基本概念:每个元素都被分配到一个聚类中,从而使到centre
的欧几里德距离的平方和最小化——这个过程也被称为使总数最小化
加载包
**library**(tidyverse)
**library**(lubridate)
**library**(knitr)
**library**(readxl)
**library**(broom)
**library**(umap)
**library**(ggrepel)
数据
数据集来自约翰·福尔曼的书数据智能。它包含虚构葡萄酒零售商的促销数据,包括 32 次促销的详细信息(包括葡萄酒品种、最低购买量、折扣百分比和原产国)以及 100 名客户及其响应的促销的列表。
offers_tbl <- **read_excel**('../00_data/WineKMC.xlsx',
sheet = 'OfferInformation')offers_tbl <- offers_tbl %>%
**set_names**(**c**('offer', 'campaign',
'varietal', 'min_qty_kg',
'disc_pct','origin','past_peak')
)**head**(offers_tbl)## # A tibble: 6 x 7
## offer campaign varietal min_qty_kg disc_pct origin past_peak
## <dbl> <chr> <chr> <dbl> <dbl> <chr> <chr>
## 1 1 January Malbec 72 56 France FALSE
## 2 2 January Pinot Noir 72 17 France FALSE
## 3 3 February Espumante 144 32 Oregon TRUE
## 4 4 February Champagne 72 48 France TRUE
## 5 5 February Cabernet~ 144 44 New Zeala~ TRUE
## 6 6 March Prosecco 144 86 Chile FALSEtransac_tbl <- **read_excel**('../00_data/WineKMC.xlsx',
sheet = 'Transactions')transac_tbl <- transac_tbl %>%
**set_names**(**c**('customer', 'offer')
)**head**(transac_tbl)## # A tibble: 6 x 2
## customer offer
## <chr> <dbl>
## 1 Smith 2
## 2 Smith 24
## 3 Johnson 17
## 4 Johnson 24
## 5 Johnson 26
## 6 Williams 18
数据需要被转换成一个User-Item format
(又名客户-产品矩阵),顶部是客户,下方是报价。单元格中填充了 0 的和 1 的,其中 1 的表示客户是否对特定报价做出了回应。
这种矩阵也被称为二进制评级矩阵和不需要标准化。
wine_tbl <- transac_tbl %>%
**left_join**(offers_tbl) %>%
**mutate**(value = 1) %>%
**spread**(customer,value, fill = 0) **head**(wine_tbl)## # A tibble: 6 x 107
## offer campaign varietal min_qty_kg disc_pct origin past_peak
## <dbl> <chr> <chr> <dbl> <dbl> <chr> <chr>
## 1 1 January Malbec 72 56 France FALSE
## 2 2 January Pinot Noir 72 17 France FALSE
## 3 3 February Espumante 144 32 Oregon TRUE
## 4 4 February Champagne 72 48 France TRUE
## 5 5 February Cabernern~ 144 44 New Zea~ TRUE
## 6 6 March Prosecco 144 86 Chile FALSE
## # ... with 100 more variables: Adams <dbl>, Allen <dbl>, Anderson <dbl>, Bailey <dbl>, Baker <dbl>, Barnes <dbl>, ...
对客户进行聚类
K-Means 算法附带了 stats 包,这是 R 中的核心系统库之一,使用起来相当简单。我只需要将几个参数传递给 kmeans() 函数。
user_item_tbl <- wine_tbl[,8:107]**set.seed**(196)
kmeans_obj <- user_item_tbl %>%
**kmeans**(centers = 5, # number of clusters
nstart = 100, # number of random sets to be chosen
iter.max = 50) # max number of iterations allowed
我可以用broom
包中的glance()
快速检查模型,它提供了模型级统计数据的摘要
**glance**(kmeans_obj) %>% **glimpse**()## Observations: 1
## Variables: 4
## $ totss <dbl> 283.1875
## $ tot.withinss <dbl> 189.7255
## $ betweenss <dbl> 93.46201
## $ iter <int> 3
真正需要关注的一个指标是总的类内平方和(或tot.withinss
),因为类的最佳数量是最小化tot.withinss
的数量。
所以我想为不同数量的集群拟合 k-means 模型,看看在哪里tot.withinss
达到最小值。
首先,我为一定数量的centers
(本例中为 4)构建一个函数,并检查它是否在glance()
上工作。
kmeans_map <- **function**(centers = 4) {
user_item_tbl %>%
**kmeans**(centers = centers,
nstart = 100,
iter.max = 50)
}4 %>% **kmeans_map**() %>% **glance**()## # A tibble: 1 x 4
## totss tot.withinss betweenss iter
## <dbl> <dbl> <dbl> <int>
## 1 283\. 203\. 80.0 2
然后,我创建了一个嵌套 tibble ,这是一种在数据框中“嵌套”列的方式。
嵌套数据框的伟大之处在于,您可以在其中放置任何您想要的东西:列表、模型、数据框、绘图等!
kmeans_map_tbl <- **tibble**(centers = 1:15) %>%
# create column with centres **mutate**(k_means = centers %>%
**map**(kmeans_map)) %>%
# iterate `kmeans_map` row-wise to
# gather kmeans models for each centre **mutate**(glance = k_means %>%
**map**(glance))
# apply `glance()` row-wise to gather
# each model’s summary metrics kmeans_map_tbl %>% **glimpse**()## Observations: 15
## Variables: 3
## $ centers <int> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, ...
## $ k_means <list> [<1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ...
## $ glance <list> [<tbl_df[1 x 4]>, <tbl_df[1 x 4]>, ...
最后,我可以构建一个scree plot
,并在图上寻找“肘部”,即额外集群的数量趋于稳定的地方。在这种情况下, 5 似乎是一个最佳数字,因为 6 的tot.withinss
下降不如前一个明显。
kmeans_map_tbl %>%
**unnest**(glance) %>% *# unnest the glance column*
**select**(centers,
tot.withinss) %>% *# select centers and tot.withinss*
**ggplot**(**aes**(x = centers, y = tot.withinss)) +
**geom_line**(colour = 'grey30', size = .8) +
**geom_point**(colour = 'green4', size = 3) +
**geom_label_repel**(**aes**(label = centers),
colour = 'grey30') +
**theme_light**() +
**labs**(title = 'Scree Plot')
将片段可视化
现在我已经确定了集群的最佳数量,我想把它们可视化。为此,我使用了(均匀流形逼近和投影),这是一种维度缩减技术,可以以类似于 主成分分析 和 t-SNE 的方式用于聚类可视化。
首先,我创建一个 umap 对象,取出layout
参数(包含可用于可视化数据集的坐标),将其格式更改为 tibble,并从wine_tbl
中附加offer
列。
umap_obj <- user_item_tbl %>% **umap**()
umap_tbl <- umap_obj$layout %>%
**as_tibble**() %>% *# change to a tibble*
**set_names**(**c**('x', 'y')) %>% *# remane columns*
**bind_cols**(wine_tbl %>% **select**(offer)) *# attach offer reference*
然后,我从嵌套表中pluck
出第 5 个 kmeans 模型,将 cluster 参数从kmeans
函数附加到输出,并将 offer 和 cluster 加入 umap_tbl。
umap_kmeans_5_tbl <- kmeans_map_tbl %>%
**pull**(k_means) %>%
**pluck**(5) %>% *# pluck element 5*
broom::**augment**(wine_tbl) %>% *# attach .cluster to the tibble*
**select**(offer, .cluster) %>%
**left_join**(umap_tbl,
by = 'offer') *# join umap_tbl by offer*
最后,我可以看到星团的 UMAP 投影。plotly
增加了一些很好的交互性,让图表变得栩栩如生!
umap_kmeans_5_tbl %>%
**mutate**(label_text = **str_glue**('Offer: {offer}
Cluster: {.cluster}')) %>%
**ggplot**(**aes**(x,y, colour = .cluster)) +
**geom_point**() +
**geom_label_repel**(**aes**(label = label_text), size = 3) +
**theme_light**() +
**labs**(title = 'UMAP 2D Projections of K-Means Clusters',
caption = "") +
**theme**(legend.position = 'none')
评估集群
现在我们终于可以更仔细地观察单个聚类,看看 K-Means 识别出了什么。
但是,让我们首先将所有信息放在一个数据框中。
cluster_trends_tbl <- wine_tbl %>%
**left_join**(umap_kmeans_5_tbl) %>%
**arrange**(.cluster) %>%
**select**(.cluster, offer:past_peak)
群组 1 和 2
集群 1 中的顾客购买大量的起泡酒(香槟和普罗塞克),而第二细分市场中的顾客喜欢购买不同品种的少量。
cluster_trends_tbl %>%
**filter**(.cluster ==1 | .cluster ==2) %>%
**count**(.cluster, varietal, origin, min_qty_kg, disc_pct) %>%
**select**(-n)## # A tibble: 9 x 5
## .cluster varietal origin min_qty_kg disc_pct
## <fct> <chr> <chr> <dbl> <dbl>
## 1 1 Champagne France 72 48
## 2 1 Champagne New Zealand 72 88
## 3 1 Prosecco Chile 144 86
## 4 2 Espumante Oregon 6 50
## 5 2 Espumante South Africa 6 45
## 6 2 Malbec France 6 54
## 7 2 Merlot Chile 6 43
## 8 2 Pinot Grigio France 6 87
## 9 2 Prosecco Australia 6 40
群组 3 和 4
当谈到葡萄酒时,这些群体中的顾客有非常特殊的口味:那些在第三部分的顾客偏爱黑皮诺,而第四组的顾客只购买大量的法国香槟。
cluster_trends_tbl %>%
**filter**(.cluster ==3 | .cluster ==4 ) %>%
**group_by**() %>%
**count**(.cluster, varietal, origin, min_qty_kg, disc_pct) %>%
**select**(-n)## # A tibble: 6 x 5
## .cluster varietal origin min_qty_kg disc_pct
## <fct> <chr> <chr> <dbl> <dbl>
## 1 3 Pinot Noir Australia 144 83
## 2 3 Pinot Noir France 72 17
## 3 3 Pinot Noir Germany 12 47
## 4 3 Pinot Noir Italy 6 34
## 5 4 Champagne France 72 63
## 6 4 Champagne France 72 89
第 5 组
****第五部分更难分类,因为它包含许多不同的属性。唯一明显的趋势是,这一细分市场的顾客选择了所有可用的赤霞珠葡萄酒。
cluster_trends_tbl %>%
**filter**(.cluster ==5 ) %>%
**count**(.cluster, varietal, origin, min_qty_kg, disc_pct) %>%
**select**(-n)## # A tibble: 17 x 5
## .cluster varietal origin min_qty_kg disc_pct
## <fct> <chr> <chr> <dbl> <dbl>
## 1 5 Cabernet Sauvignon France 12 56
## 2 5 Cabernet Sauvignon Germany 72 45
## 3 5 Cabernet Sauvignon Italy 72 82
## 4 5 Cabernet Sauvignon Italy 144 19
## 5 5 Cabernet Sauvignon New Zealand 144 44
## 6 5 Cabernet Sauvignon Oregon 72 59
## 7 5 Champagne California 12 50
## 8 5 Champagne France 72 85
## 9 5 Champagne Germany 12 66
## 10 5 Chardonnay Chile 144 57
## 11 5 Chardonnay South Africa 144 39
## 12 5 Espumante Oregon 144 32
## 13 5 Malbec France 72 56
## 14 5 Merlot California 72 88
## 15 5 Merlot Chile 72 64
## 16 5 Prosecco Australia 72 83
## 17 5 Prosecco California 72 52
最后的想法
虽然它不会给你所有的答案,聚类是一个强大的探索性练习,它可以帮助你揭示你的消费者群的模式,尤其是当你有一个全新的市场要探索,并且之前没有任何关于它的知识。
这很容易实现,甚至在像我在这里使用的小数据集上,你也可以在你的客户群中挖掘出有趣的行为模式。
我们不费吹灰之力就了解到,我们的一些顾客喜欢某些种类的葡萄酒,而另一些顾客则喜欢买高买低。这些信息可以用来针对那些更倾向于做出回应的客户,定制你的定价策略和营销活动**。此外,客户细分允许更有效地分配营销资源,并最大限度地增加交叉销售和追加销售机会。**
还可以通过叠加客户的人口统计**(年龄、种族、宗教、性别、家庭规模、种族、收入、教育水平)地理(他们生活和工作的地方)心理(社会阶层、生活方式和个性特征)等信息来丰富细分,但这些都超出了这个小型项目的范围。**
代码库
完整的 R 代码可以在我的 GitHub 档案中找到
参考
- 有关客户细分潜力的更广泛观点
- 对于某些 k-means 缺点的批判
- 贝恩&公司的客户细分
原载于 2019 年 5 月 25 日https://diegousei . io。**
Dash 开发的温和介绍
构建交互式可视化并将其部署到 Heroku 的有效工作流
你是一个饱受 D3 羡慕的 Python 程序员吗?
第一步:垂涎于 D3 画廊的性感&互动剧情
第二步:从 Javascript
的陡峭学习曲线上掉下来第三步:回到制作平庸的 seaborn 剧情
有更好的办法!
A Dash interactive visualization in <150 lines of Python code, deployed to Heroku from this GitHub repo
进入 Dash ,这是一个用纯 Python 构建交互式 web 可视化的简单而强大的框架。因为它是建立在 D3 之上的,所以你得到了你渴望的美感(这里有一些社区的例子)。Dash 有一篇优秀的介绍文章,和一篇示范性的教程,它将教你在 1-2 小时内制作一个交互式可视化。我想澄清一下,我不是在写 Dash 的教程(说真的,他们的很棒),也不是在争论 Dash 与 Altair 或 Bokeh 的优点(我会把这个留给专家和他们的机智决策树)。
那么我为什么要写 Dash 开发的教程呢?在用户指南的彻底性中丢失的是一个通用工作流的简明图片——构建和部署交互式可视化。如果你对 git 特性分支、Heroku 或虚拟环境有点模糊,很容易被本教程简单的可视化所迷惑。在您探测堆栈溢出的深度之前,这里有一个有效的工作流,它帮助我构建了多个仪表板(示例)并避免了常见的陷阱。我使用以下工作流程开发了 GitHub 教程及以上 Heroku app 。
Sample workflow
项目设置
同样,本教程是关于构建非平凡的 Dash 应用程序和部署到 Heroku ,所以我们必须使用版本控制和 GitHub。为了便于进入,我在教程 repo 中做了一个样板分支。
注:你可能熟悉cookiecutter,一个项目设置的自动化工具。Plotly 提供了一个 dash 组件样板 ,并且至少有一个 dash app 样板 。我的是一个轻量级版本,对于本教程来说已经足够了。
Github tutorial boilerplate branch
除了琐碎的破折号app
,我还包含了 Heroku 部署所需的文件。这些文件在 Heroku 的 Python 快速入门中有很好的记录。简而言之,Procfile
、runtime
和setup
告诉远程 Heroku 服务器如何运行你的应用,requirements
列出了用于构建你的应用的虚拟环境的 Python 包(及其具体版本)。
步骤 1: 创建自己的 Git 资源库
步骤 2 :克隆或下载教程的样板文件分支,手动将这些文件添加到您的 Git 资源库中(这比分叉整个 Git 资源库要干净)。
第三步:创建你的虚拟环境。我使用的是一个由pip
作为包管理器的conda
环境。打开一个终端,运行conda create -n dash_tutorial pip
。(将pip
添加到您的环境中使得稍后通过pip freeze
跟踪包变得更加容易)。注意我的runtime
用的是 Python 3.7.2。
**第四步:**安装你的软件包。首先,确保您在您的环境中:对于 Windows,运行activate dash_tutorial
。如果你只是按照教程来做,你可以通过运行
(env_name) C:/path/to/dash_tutorial> pip install -r requirements.txt
来复制我的环境(激活环境并在你的 git repo 中)【如果你正在为你自己的应用程序使用样板文件(而不是使用 cookiecutter 模板),你需要一个包含最新版本包的环境。你可以pip install
最新的 Dash 库,以及pip install jupyter pandas
和任何其他你打算用于你的应用的库。在这一点上,你可能会感到沮丧,因为我答应了一个“温和的”介绍,然后让你承担环境管理的重担。然而,环境勤奋对于使 Heroku 部署顺利进行至关重要(实际上,对于任何重要的 Python 开发都是如此)。如果你真的愿意,你可以在处理 Heroku 部署之前构建完整的 Dash app
,但是根据我的经验,Heroku 部署对于初学者来说很棘手,在你构建一个你无法部署的复杂应用之前,用一个玩具应用来调试部署更容易。
部署到 Heroku
第五步:本地运行 app。
(env_name) C:/path/to/dash_tutorial> python app.py
这将启动一个本地服务器,您可以通过浏览器导航到 http://127.0.0.1:8050/ 来访问该服务器。您应该会看到类似这样的内容
Boilerplate app running on local server
**第六步:**创建你的 Heroku app
如果你没有 Heroku 账号,参考他们的 Python 快速入门快速安装配置。否则,通过heroku create my-app-name
创建一个 Heroku 应用程序(注意,应用程序名称必须是唯一的,所以你不能使用例如dash-tutorial
) 步骤 7: 部署到 Heroku
运行git push heroku master
如果你没有得到错误,访问你的应用程序,其 url 由https://my-dash-name.herokuapp.com/
组成如果一切顺利,你应该看到与你的本地服务器完全相同的输出。
Boilerplate app running on Heroku
开发您的 Dash 应用程序(有趣的部分)
至此,你已经有了一个坚实的应用程序开发框架。参考前面的工作流程图,您现在将使用以下步骤在 git 分支上开发应用程序特性(我用字母标记了这些步骤,以表示独立的循环流程)。我将展示来自教程 repo 的特性分支的例子:第一个创建了散点图,第二个添加了 choropleth 图。
创建一个 git 分支。
在回购教程中,我运行git checkout -b scatterplot
创建一个分支,我将在其中添加第一个交互式散点图。
B)添加特性 常见的工作流程是在自己喜欢的文本编辑器或 IDE 中编写代码,然后启动本地服务器。下面是交互式散点图的精简代码,摘自第一个功能分支的[app.py](https://github.com/zwrankin/dash_tutorial/blob/scatterplot/app.py)
(显示在上面的应用程序中)。我通常从简单的例子开始复制粘贴代码,比如 Dash 教程的散点图(它解释了layout
和callbacks
)。
对于小的调整,您可以使用 Dash 的自动加载器迭代地编写代码并查看更新。下面,我启动本地服务器,然后通过向 choropleth 地图添加autocolorscale=False
参数来更改 colorscale,并立即看到更新后的地图。
Tweaking Plotly code and seeing autoreloader update local app
对于某些任务,比如数据管理,我发现在将代码添加到app.py
之前,在 Jupyter 笔记本上写代码是值得的。
Data munging is easier in Jupyter than Dash’s autoreloader
我发现在 Jupyter 笔记本上开发 Plotly 代码并没有特别大的帮助。这需要一些额外的设置步骤,Dash autoreloader 为调试提供了不错的堆栈跟踪。如果你想在笔记本上运行 Plotly,你需要导入特殊的工具,如下:
A few extra steps to use Plotly in Jupyter notebook
C)从功能分支 推送至 Heroku 一旦您对运行在 http://127.0.0.1:8050/ 上的应用版本感到满意,就该重新部署了。您可以将您的分支合并到 master,然后将 master 推送到 Heroku,但是我发现在将它合并到 master 之前从我的特性分支进行部署更令人欣慰。这是通过git push heroku branchname:master
完成的。
E)推送并合并您的特性分支 如果您的部署进展顺利,推送您的分支:
git push --set-upstream origin branchname
转到 GitHub,创建一个 pull 请求(PR),然后合并您的 PR。在合并 PR 之后,最好的做法可能是git push heroku master
,但是如果你有干净的特性分支工作流,你可以从特性分支部署。
冲洗并重复
继续开发功能分支,并重新部署到 Heroku。
解决纷争
我的应用可以在本地运行,但不能在 Heroku 上运行 1)仔细检查你的环境。确保您的requirements.txt
是最新的(运行pip freeze > requirements.txt
并确保提交任何更新)。
2) Heroku 有一些回购结构要求。例如,确保你的[Procfile](https://devcenter.heroku.com/articles/procfile)
在你的应用程序的根目录下。有时候,你的本地设备和 Heroku 的操作系统之间的差异会产生严重的错误。比如我写了一个在 Windows 本地运行的 app,用pd.read_csv('data.csv')
读取data.CSV
。它成功地部署到 Heroku,但是不能在 Heroku 的 Unix 系统上运行,与FileNotFoundError: 'data.csv' does not exist
中断。
后续步骤
Dash 允许你用纯 Python 制作强大的可视化应用。除了 Heroku 部署所需的文件,本教程的app.py
模块包括所有数据转换、绘图和应用布局代码。很可能这对你的目的来说已经足够了。随着你的应用程序的开发,你可能需要将你的代码重构为多个模块,例如:
-将数据转换、调色板等重构为独立的模块,这样你的app.py
模块就可以专注于应用程序本身。
-用你自己的 CSS 设计你的应用
-用React.js
编写你自己的组件
探索性数据分析的简明介绍
包括:漂亮的图画,一个漫游卡格尔例子和许多挑战
粉色汗衫,染红头发,镀灰胡子,不穿鞋,约翰列侬眼镜。真是个人物。想象一下他会有怎样的故事。他停好车,走进咖啡馆。
这家咖啡馆是当地人的最爱。但是椅子不太舒服。所以我会尽量简短(剧透:我说的简短是指与你实际花在 EDA 上的时间相比的简短)。
当我第一次在 Max Kelsen 公司做机器学习工程师的时候,我从来没有听说过 EDA。有一堆我从来没听说过的缩写。
我后来知道 EDA 代表探索性数据分析。
这是你第一次遇到数据集时要做的事情。但这不是一次性的过程。
过去几周,我一直在做一个机器学习项目。一切都很顺利。我用少量数据训练了一个模型。结果相当不错。
是时候加快步伐,添加更多数据了。我照做了。然后就断了。
我填满了我正在使用的云计算机的内存。我又试了一次。同样的问题。
某处出现了内存泄漏。我错过了一些东西。什么变了?
更多数据。
也许我获取的下一个数据样本与第一个有所不同。确实如此。有一个异常值。一个样本的购买量是平均值的 68 倍(100)。
回到我的代码。它对异常值并不稳健。它将异常值应用于其余的样本,并用零填充它们。
不是有 1000 万个长度为 100 的样本,而是都有 6800 个长度。大部分数据都是零。
我改了密码。重新运行模型,开始训练。内存泄漏已被修补。
暂停。
穿粉色汗衫的人走了过来。他告诉我他的名字叫约翰尼。
他继续说道。
女孩们因我没打招呼而责骂我。
“你赢不了,”我说。
“太对了,”他说。
我们笑了。这里的女孩真的很好。常客会被取笑。约翰尼是常客。他告诉我他在家里有自己的农场。他的脚趾甲被涂成粉色和黄色,交替着,粉色,黄色,粉色,黄色。
强尼走了。
回去吧。
发生了什么事?为什么 EDA 的故事中断了?
除了向你介绍约翰尼的传奇,我想举一个例子来说明你是如何认为前方的路是清晰的,但实际上,有一个弯路。
EDA 是一个很大的弯路。没有真正结构化的方法来做这件事。这是一个反复的过程。
EDA 为什么?
当我开始学习机器学习和数据科学时,大部分(全部)都是通过在线课程。我用它们创造了我自己的人工智能硕士学位。他们都提供了优秀的课程和优秀的数据集。
这些数据集非常出色,因为它们可以立即与机器学习算法一起使用。
你可以下载数据,选择你的算法,调用.fit()
函数,将数据传递给它,突然之间,损失值开始下降,你会得到一个精度指标。魔法。
这就是我大部分学习的过程。然后我找了一份机器学习工程师的工作。我想,终于,我可以把我学到的东西应用到现实世界的问题中了。
路障。
客户把数据发给了我们。我看了看。这是搞什么鬼?
单词、时间戳、更多单词、缺少数据的行、列、很多列。数字在哪里?
我该如何处理这些数据?我问 Athon。
他说,你必须做一些特征工程,对分类变量进行编码,我会给你一个链接。
我去找了我的数字导师。谷歌。什么是特征工程?
再次谷歌。什么是分类变量?
Athon 给发了链接。我打开了。
就在那里。我必须穿过的下一座桥。埃达。
在运行机器学习模型之前,您需要进行探索性数据分析来了解更多信息。
你为数据创建自己的心理模型,这样当你运行机器学习模型进行预测时,你将能够识别它们是否是谎言。
我没有回答你所有关于 EDA 的问题,而是设计了这篇文章来激发你的好奇心。让你思考你可以问数据集的问题。
你从哪里开始?
你如何探索山脉?
你直接走到顶端吗?
沿着基地试着找到最佳路径怎么样?
这取决于你想要达到的目标。如果你想到达顶峰,最好尽快开始攀登。但花些时间寻找最佳路线也可能是好的。
探索数据也是一样。你想解决什么问题?或者更好,你想证明什么假设是错的?
你可以花一整天来讨论这些。但是最好从简单的开始,证明它是错的,并根据需要增加复杂性。
示例时间。
首次提交 Kaggle
你一直在网上学习数据科学和机器学习。你听说过卡格尔。你已经读过一些文章,说在他们的问题上练习你的技能是多么有价值。
路障。
尽管你听说过卡格尔的种种优点。您尚未提交。
你决定是时候参加自己的比赛了。
你在 Kaggle 网站上。您可以转到“从这里开始”部分。有一个包含泰坦尼克号乘客信息的数据集。你下载它并加载一个 Jupyter 笔记本。
你是做什么的?
你想解决什么问题?
我能根据其他乘客的数据预测泰坦尼克号上乘客的存活率吗?
这似乎是一个很好的指路明灯。
EDA 清单
如果一份清单足够好,可供飞行员在每次飞行中使用,那么它也足够好,可供数据科学家在每个数据集上使用。
EDA 清单
1.你试图解决什么问题(或证明什么问题是错的)?
2。你有什么样的数据,如何对待不同的类型?
3。数据中缺失了什么,如何处理?
4。离群值在哪里,为什么要关心它们?
5。如何添加、更改或删除功能以充分利用您的数据?
我们将逐一讲解。
**Response opportunity:** What would you add to the list?
你想解决什么问题?
我在副标题中加了一个 s。忽略它。从一个开始。不要担心,随着你的发展,会有更多的事情发生。
对于我们的泰坦尼克号数据集示例,它是:
我们能根据其他乘客的数据预测泰坦尼克号上的幸存者吗?
太多的问题会让你的思维空间变得混乱。人类不擅长同时计算多个事物。我们会把它留给机器。
Sometimes a model isn’t required to make a prediction.
Before we go further, if you’re reading this on a computer, I encourage you to [**open this Juypter Notebook**](http://bit.ly/yourfirstkagglesubmission) and try to connect the dots with topics in this post. If you’re reading on a phone, don’t fear, the notebook isn’t going away. I’ve written this article in a way you shouldn’t *need* the notebook but if you’re like me, you learn best seeing things in practice.
你有什么样的数据,如何看待不同类型?
您已经导入了泰坦尼克号训练数据集。
我们去看看。
training.head()
.head() shows the top five rows of a dataframe. The rows you’re seeing are from the Kaggle Titanic Training Dataset.
一列接一列,有:数字,数字,数字,单词,单词,数字,数字,数字,字母和数字,数字,字母和数字和 NaNs,字母。类似于强尼的脚趾甲。
让我们将特征(列)分成三个框,数字、分类和不确定。
Columns of different information are often referred to as features. When you hear a data scientist talk about different features, they’re probably talking about different columns in a dataframe.
在数字桶中,我们有:PassengerId
、Survived
、Pclass
、Age
、SibSp
、Parch
和Fare
。
分类桶包含Sex
和Embarked
。
而在不确定中我们有Name
、Ticket
和Cabin
。
现在,我们已经将这些列分解到单独的桶中,让我们检查每一个。
数字桶
还记得我们的问题吗?
我们能根据其他乘客的数据预测泰坦尼克号上的幸存者吗?
由此,你能找出我们要预测的是哪一列吗?
We’re trying to predict the green column using data from the other columns.
Survived
栏。因为它是我们试图预测的列,我们将把它从数值桶中取出来,暂时不考虑它。
还剩下什么?
PassengerId
、Pclass
、Age
、SibSp
、Parch
和Fare
。
想一想。如果你试图预测泰坦尼克号上是否有人幸存,你认为他们独特的PassengerId
真的会对你的事业有帮助吗?
大概不会。所以我们暂时也将这个专栏放在一边。EDA 并不总是必须用代码来完成,你可以从你的世界模型开始,然后用代码来看看它是否正确。
Pclass
、SibSp
、Parch
怎么样?
这些都是数字,但它们有所不同。你能捡起来吗?
Pclass
、SibSp
和Parch
到底是什么意思?也许我们应该在这么快建立模型之前多读些文档。
谷歌。Kaggle 泰坦尼克号数据集。
找到了。
Pclass
是车票等级,1 =一等,2 =二等,3 =三等。SibSp
是乘客在飞机上的兄弟姐妹的数量。并且Parch
是某人在船上的父母的数量。
这些信息很容易找到。但是如果你有一个从未见过的数据集呢?如果一个房地产经纪人想要帮助预测他们城市的房价。你查看他们的数据,发现一堆你不理解的栏目。
你给客户发邮件。
Tnum
是什么意思?
他们回应。Tnum
是一个物业的卫生间数量。
很高兴知道。
当你处理一个新的数据集时,你并不总是像 Kaggle 提供的那样有关于它的可用信息。这是你想要寻求中小企业知识的地方。
另一个缩写。太好了。
SME 代表主题专家。如果您正在从事一个处理房地产数据的项目,EDA 的一部分可能涉及到与房地产经纪人交谈和向他们提问。这不仅可以节省您的时间,还会影响您将来对数据的提问。
既然泰坦尼克号上已经没有人活着了(安息吧,米莉维娜·迪恩,最后的幸存者),我们将不得不成为我们自己的中小企业。
Pclass
、SibSp
和Parch
还有一些独特之处。尽管它们都是数字,但它们也是类别。
为什么
这样想吧。如果你能在头脑中很容易地将数据组合在一起,那么它就有可能是一个类别的一部分。
Pclass
列可以标记为第一、第二和第三列,其含义与 1、2 和 3 相同。
还记得机器学习算法有多喜欢数字吗?由于Pclass
、SibSp
和Parch
都已经是数字形式,我们就让它们保持原样。Age
也是如此。
唷。这并不难。
分类桶
在我们的分类桶中,我们有Sex
和Embarked
。
这些是分类变量,因为你可以把女性乘客和男性乘客分开。或者说从 s 出发的人走上 C 的人。
为了训练机器学习模型,我们需要一种将这些转换成数字的方法。
你会怎么做?
还记得Pclass
吗?第一次= 1,第二次= 2,第三次= 3。
对于Sex
和Embarked
,你会怎么做?
也许你可以为Sex
做些类似的事情。女性= 1,男性= 2。
至于Embarked
,S = 1,C = 2。
我们可以使用sklearn
库中的[.LabelEncoder()](https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.LabelEncoder.html)
函数来改变这些。
training.embarked.apply(LabelEncoder().fit_transform)
Wait? Why does C = 0 and S = 2 now? Where’s 1? Hint: There’s an extra category, Q, this takes the number 1. See the data description page on Kaggle for more.
我们在将分类数据转换成所有数字方面取得了一些进展,但是其他列呢?
**Challenge:** Now you know Pclass could easily be a categorical variable, how would you turn Age into a categorical variable?
不确定桶
剩下Name
、Ticket
、Cabin
。
如果你在泰坦尼克号上,你认为你的名字会影响你的生还几率吗?
不太可能。但是你还能从一个人的名字中提取出什么信息呢?
如果你给每个人一个数字,根据他们的头衔是先生,夫人还是小姐。?
您可以创建另一个名为 Title 的列。这一栏,先生= 1,太太= 2,小姐的。= 3.
您所做的是在现有特征的基础上创建一个新特征。这被称为特征工程。
将标题转换成数字是一个相对容易创建的特性。根据你所拥有的数据,特征工程可以变得任意奢侈。
这个新特性会对模型产生怎样的影响?这是你必须调查的事情。
现在,我们不会担心Name
列做出预测。
那Ticket
呢?
前几个例子看起来根本不太一致。还有什么?
training.Ticket.head(15)
The first 15 entries of the Ticket column.
这些也不是很一致。但是再想想。你认为票号能提供更多关于某人是否幸存的信息吗?
也许如果票号与这个人乘坐的舱位等级有关,它会有影响,但是我们已经在Pclass
中有了这个信息。
为了节省时间,我们暂时忽略Ticket
列。
你第一次在数据集上进行 EDA 的目的不仅仅是提出更多关于数据的问题,而是用尽可能少的信息建立一个模型,这样你就有了一个工作的基线。
现在,我们拿小屋怎么办?
你知道,因为我已经看过数据了,我的蜘蛛感官告诉我这是下一部分的完美例子。
**Challenge:** I’ve only listed a couple examples of numerical and categorical data here. Are there any other types of data? How do they differ to these?
数据中缺失了什么,如何处理?
missingno.matrix(train, figsize = (30,10))
The missingno library is a great quick way to quickly and visually check for holes in your data, it detects where NaN values (or no values) appear and highlights them. White lines indicate missing values.
Cabin
栏看起来像约翰尼的鞋子。不在那里。Age
中也有相当多的缺失值。
在没有数据的情况下,你如何预测呢?
我也不知道。
那么,在处理缺失数据时,我们有什么选择呢?
最快最简单的方法是删除每一行缺少的值。或者完全移除Cabin
和Age
柱。
但是这里有一个问题。机器学习模型喜欢更多的数据。删除大量数据可能会降低我们的模型预测乘客是否幸存的能力。
下一步是什么?
输入值。换句话说,用从其他数据计算出的值来填充缺失的数据。
您将如何为Age
列执行此操作?
When we called .head() the Age column had no missing values. But when we look at the whole column, there are plenty of holes.
你能用平均年龄来填补缺失值吗?
这种价值填充是有缺点的。假设您总共有 1000 行,其中 500 行缺少值。您决定用平均年龄 36 来填充 500 个缺失的行。
会发生什么?
随着年龄的增长,你的数据变得堆积如山。这将如何影响对 36 岁人群的预测?或者其他年龄?
也许对于每个缺少年龄值的人,您可以在数据集中找到其他相似的人,并使用他们的年龄。但是这很耗时,也有缺点。
还有更高级的方法来填充本文范围之外的缺失数据。应该注意的是,没有完美的方法来填充缺失值。
如果Age
栏中的缺失值是泄漏的排水管,则Cabin
栏是破裂的堤坝。无可救药。对于你的第一个模型,Cabin
是你应该忽略的一个特性。
**Challenge:** The Embarked column has a couple of missing values. How would you deal with these? Is the amount low enough to remove them?
异常值在哪里,为什么您应该关注它们?
“你查过发行情况了吗,”Athon 问。
我用了第一组数据,但没有用第二组数据……”
它击中了我。
就在那里。其余的数据被加工以匹配异常值。
如果您查看数据集中唯一值的出现次数,您会发现最常见的模式之一是齐夫定律。看起来是这样的。
Zipf’s law: The highest occurring variable will have double the number of occurrences of the second highest occurring variable, triple the amount of the third and so on.
记住 Zipf 定律有助于思考异常值(不经常出现的接近尾部末端的值是潜在的异常值)。
对于每个数据集,异常值的定义是不同的。作为一个通用的经验法则,你可以认为任何偏离平均值超过 3 个标准差的数据都是异常值。
You could use a general rule to consider anything more than three standard deviations away from the mean as an outlier.
或者换个角度。
Outliers from the perspective of an (x, y) plot.
如何发现异常值?
分销。分销。分销。分销。四次就够了(我在这里努力提醒自己)。
在您第一次通过 EDA 时,您应该检查每个特性的分布情况。
分布图将有助于表示不同数据值的分布。更重要的是,有助于识别潜在的异常值。
train.Age.plot.hist()
Histogram plot of the Age column in the training dataset. Are there any outliers here? Would you remove any age values or keep them all?
为什么要关心离群值呢?
在数据集中保留异常值可能会导致模型过度拟合(过于精确)。移除所有的异常值可能会导致你的模型过于一般化(它不会在任何异常的情况下做得很好)。和往常一样,最好反复试验,找到处理异常值的最佳方法。
**Challenge:** Other than figuring out outliers with the general rule of thumb above, are there any other ways you could identify outliers? If you’re confused about a certain data point, is there someone you could talk to? Hint: the acronym contains the letters M E S.
利用特征工程从数据中获取更多信息
泰坦尼克号数据集只有 10 个特征。但是如果你的数据集有数百个呢?还是几千?或者更多?这并不罕见。
在您的探索性数据分析过程中,一旦您开始形成一种理解,您对分布有了一个概念,您发现了一些异常值并处理了它们,下一个最大的时间块将花费在特征工程上。
特征工程可以分为三类:添加、删除和更改。
泰坦尼克号的数据集开始时状态很好。到目前为止,我们只需要改变一些特性就可以实现数值化。
然而,野外的数据是不同的。
假设您正在解决一个问题,试图预测一家大型连锁超市全年香蕉库存需求的变化。
您的数据集包含库存水平和以前采购订单的历史记录。你可以很好地对这些进行建模,但你会发现一年中有几次股票水平发生不合理的变化。通过您的研究,您发现在一年一度的全国庆祝活动“香蕉周”期间,香蕉的库存水平直线下降。这是有道理的。为了跟上庆祝活动,人们买更多的香蕉。
为了补偿香蕉周并帮助模型了解它何时发生,您可以向数据集中添加一个包含香蕉周或不包含香蕉周的列。
# We know Week 2 is a banana week so we can set it using np.where()df["Banana Week"] = np.where(df["Week Number"] == 2, 1, 0)
A simple example of adding a binary feature to dictate whether a week was banana week or not.
添加这样的功能可能并不简单。您可能会发现添加要素没有任何作用,因为您添加的信息已经隐藏在数据中。与过去几年一样,香蕉周期间的采购订单已经高于其他周。
移除功能怎么办?
我们在泰坦尼克号数据集上也做到了这一点。我们删除了Cabin
列,因为在我们运行模型之前,它丢失了太多的值。
但是,如果您已经使用剩下的特性运行了一个模型,该怎么办呢?
这就是功能贡献的用武之地。特征贡献是一种计算每个特征对模型影响程度的方法。
An example of a feature contribution graph using Sex, Pclass, Parch, Fare, Embarked and SibSp features to predict who would survive on the Titanic. If you’ve seen the movie, why does this graph make sense? If you haven’t, think about it anyway. Hint: ‘Save the women and children!’
为什么这些信息会有帮助?
知道一个特征对模型的贡献有多大可以给你指明下一步特征工程的方向。
在我们的泰坦尼克号例子中,我们可以看到Sex
和Pclass
的贡献是最高的。你认为这是为什么?
如果你有 10 个以上的功能会怎样?100 怎么样?你也可以做同样的事情。制作一个图表,显示 100 个不同特征的特征贡献。
“哦,我以前见过这个,”
齐夫定律又回来了。顶部特征比底部特征的贡献大得多。
Zipf’s law at play with different features and their contribution to a model.
看到这一点,您可能会决定削减贡献较小的功能,改进贡献较大的功能。
你为什么要这么做?
移除要素会降低数据的维度。这意味着您的模型需要进行更少的连接来找出拟合数据的最佳方式。
您可能会发现移除要素意味着您的模型可以在更少的数据和更少的时间内获得相同(或更好)的结果。
就像 Johnny 是我所在咖啡馆的常客一样,特征工程是每个数据科学项目的常规部分。
**Challenge:** What are other methods of feature engineering? Can you combine two features? What are the benefits of this?
构建您的第一个模型
最后。我们已经完成了一系列步骤,为运行一些模型准备好数据。
如果你和我一样,当你开始学习数据科学时,这是你最先学习的部分。上面所有的东西都已经被别人做过了。你所要做的就是在上面放一个模型。
我们的泰坦尼克号数据集很小。因此,我们可以在它上面运行大量的模型,以找出哪一个是最好的。
注意我是怎么在副标题里放一个(s)的,你可以注意这一个。
Cross-validation accuracy scores from a number of different models I tried using to predict whether a passenger would survive or not.
在我们的小型 Titanic 数据集上运行多个模型是没问题的。但是对于较大的数据集可能不是最好的。
一旦你对不同的数据集进行了一些实践,你就会开始发现什么样的模型通常效果最好。例如,大多数最近的 Kaggle 比赛都是通过不同梯度提升树算法的集合(组合)赢得的。
一旦你建立了几个模型并找出了最好的,你就可以开始通过超参数调整来优化最好的一个。把超参数调谐想象成在烹饪你最喜欢的菜肴时调节烤箱上的转盘。开箱后,烤箱上的预设设置工作得很好,但根据经验,你会发现降低温度和提高风扇速度会带来更美味的结果。
机器学习算法也是一样。他们中的许多人开箱即用。但是只要稍微调整一下它们的参数,它们就能工作得更好。
但无论如何,如果没有足够的数据准备,即使是最好的机器学习算法也不会产生一个伟大的模型。
EDA 和建模是一个重复的循环。
The EDA circle of life.
最后的挑战(和一些课外活动)
我离开了咖啡馆。我的屁股很痛。
在这篇文章的开始,我说过我会保持简短。你知道结果如何。它将与您的 EDA 迭代相同。当你认为你完成了。还有更多。
我们以 Titanic Kaggle 数据集为例,介绍了一个不完整的 ed a 清单。
1.你想解决什么问题(或者证明什么问题是错的)?
从最简单的假设开始。根据需要增加复杂性。
2.你有什么样的数据?
你的数据是数字的,分类的还是其他的?你如何处理每一种?
3.数据中缺失了什么,你如何处理?
为什么数据丢失了?丢失数据本身就是一个信号。你永远无法用和原来一样好的东西来代替它,但是你可以试试。
4.离群值在哪里,为什么要关注它们?
分销。分销。分销。总结三遍就够了。数据中的异常值在哪里?你需要它们吗?还是它们正在破坏你的模型?
5.如何添加、更改或删除功能以充分利用您的数据?
默认的经验法则是数据越多越好。遵循这一点通常会很有效。但是有什么东西你可以去掉得到同样的结果吗?从简单开始。少而精。
GitHub 上的笔记本中有我们在这里讨论的所有内容的例子(以及更多),YouTube 上有我一步一步浏览笔记本的视频(编码从 5:05 开始)。
**FINAL BOSS CHALLENGE:** If you’ve never entered a Kaggle competition before, and want to practice EDA, now’s your chance. Take the [notebook I’ve created](http://bit.ly/yourfirstkagglesubmission), rewrite it from top to bottom and improve on my result.**Extra-curriculum bonus:** [Daniel Formoso's notebook](https://github.com/dformoso/sklearn-classification/blob/master/Data%20Science%20Workbook%20-%20Census%20Income%20Dataset.ipynb) is one of the best resources you’ll find for an extensive look at EDA on a Census Income Dataset. After you’ve completed the Titanic EDA, this is a great next step to check out.
如果你认为这篇文章遗漏了什么,请在下面留言或者给我发个短信。