TowardsDataScience 博客中文翻译 2019(四百五十四)

原文:TowardsDataScience Blog

协议:CC BY-NC-SA 4.0

用 Matplotlib 动画增加 Python 可视化的趣味

原文:https://towardsdatascience.com/spice-up-your-python-visualizations-with-matplotlib-animations-d437d7e98e67?source=collection_archive---------5-----------------------

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Photo by Egor Kamelev from Pexels

还有约翰·康威的《生命的游戏》的一些有趣的 gif

如果你对我的代码感兴趣,可以在我的 GitHub 这里找到 。第一次运行时,代码会抛出一个错误(这是我一辈子都无法摆脱的)。但是,如果您只是再次执行同一个单元,它将运行良好。

他的帖子显然不是数据科学帖子,但它确实有数据科学和商业智能应用。Python 的 Matplotlib 是绘制和可视化数据的首选库。我们都熟悉折线图、条形图和热图。但是你知道你也可以使用 Matplotlib 制作简单的动画吗?

下面是一个用 Matplotlib 创建的动画的例子。它展示了约翰·康威的生命的游戏——Metis 的编码挑战,这给了我创作第一个 Python 动画的借口。查看最终结果的 gif:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

The Game of Life

如果你对我如何编写生命游戏感兴趣,请参考 GitHub 上的我的代码(和评论】。这篇博客将主要关注如何使用 Matplotlib 在 python 中添加动画。

但是对于那些不熟悉模拟的人来说(这更像是你观看的模拟,而不是你玩的游戏),这里有一些规则:

  • 我们从一块 N 乘 N 大小的板开始(在我的动画中是 50 乘 50)。
  • 我们在板上随机填充了一些细胞(我开始模拟时,在板上 2500 个细胞中随机填充了 1500 个细胞)。
  • 具有一个或更少邻居的每个被占用的单元死亡。
  • 具有四个或更多邻居的每个被占用的单元死亡。
  • 每一个有两三个邻居的被占据的细胞存活下来。
  • 每个空的细胞与正好三个相邻的细胞发展出一个新的有机体。

设置董事会

我们从导入我们需要的库开始。

import time
from IPython import display
import matplotlib.pyplot as plt
import matplotlib.animation as animation

我们将利用 matplotlib 动画模块中的 FuncAnimation()函数。FuncAnimation()通过反复调用函数来制作图像动画,每次调用时更新图像。我们将一步一步地完成这个过程。

但是首先,我们需要初始化我们的板。下面几行代码收集了我们的输入:

  • 我们想要一块 50 乘 50 大小的木板。
  • pad 变量使得计算邻居变得更加容易。通过用总是空着的额外单元填充边缘,我们就不需要编写额外的逻辑来处理电路板的边缘。因此,我们的 50 乘 50 板被空白单元的边界包围,使得实际的 numpy 数组具有 52 乘 52 的大小。
  • initial_cells 变量是我们希望棋盘从多少个有机体开始。它们将被随机放在黑板上。
# Input variables for the board
boardsize = 50        # board will be X by X where X = boardsize
pad = 2               # padded border, do not change this!
initial_cells = 1500  # this number of initial cells will be placed 
                      # in randomly generated positions

接下来,我们随机生成一串坐标(我们在上面选择了 1500)**,我们最初的生物体将生活在那里。**这些坐标存储在变量 pos_list 中。

# Get a list of random coordinates so that we can initialize 
# board with randomly placed organisms
pos_list = []
for i in range(initial_cells):
    pos_list.append([random.randint(1, boardsize), 
                     random.randint(1, boardsize)])

然后是实例化电路板的时候了。我们将使用一个名为 my_board 的 numpy 数组来表示我们的棋盘 —我们从一个 52 乘 52 的零数组开始(由于填充,它比 50 乘 50 大),然后调用函数 init_board()根据 pos_list 中的坐标用有机体填充它。我不会在这里详述助手函数,但是它们在我的 GitHub 上有文档。

# Initialize the board
my_board = np.zeros((boardsize+pad, boardsize+pad))
my_board = init_board(pos_list, my_board)

为棋盘制作动画

我们期待已久的部分——动画!首先,我们需要解决一些手续问题。下面几行代码创建的 matplotlib 图形将显示我们的动画。

# Required line for plotting the animation
%matplotlib notebook
# Initialize the plot of the board that will be used for animation
fig = plt.gcf()

是时候制作我们的第一个相框了。matplotlib 的函数 imshow()接收一个 numpy 矩阵,并将其呈现为图像。相当酷!

# Show first image - which is the initial board
im = plt.imshow(my_board)
plt.show()

我们传递给 imshow()的变量是我们的初始板,它存储在 my_board 中。创建的图像如下所示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

The Initial State of the Game Board (Yellow = Organism)

现在我们需要做一个 FuncAnimation()可以调用的 helper 函数。函数 animate() 取帧,它只是一个计数器。帧计数器是 FuncAnimation()与 animate() 函数通信的方式——对于每一步时间(也称为帧),它将调用 animate() 一次**。**和 animate() 会依次使用辅助函数 update_board()迭代棋盘一圈。最后,函数 set_data()用迭代板更新了我们的图像,我们可以开始了。

# Helper function that updates the board and returns a new image of
# the updated board animate is the function that FuncAnimation calls
def animate(frame):
    im.set_data(update_board(my_board))
    return im,

万岁!我们准备调用 FuncAnimation() 。注意输入:

  • fig 是我们之前为容纳动画而创建的绘图变量。
  • animate 是我们的函数, FuncAnimation() 使用帧计数器与之通信(它是自动传递的,不需要显式指定)。
  • 帧是我们希望动画持续多少帧,在这种情况下,我们希望我们的动画是 200 帧长。
  • interval 是帧之间的延迟,以毫秒为单位。所以我们希望两帧之间有 50 毫秒。
# This line creates the animation
anim = animation.FuncAnimation(fig, animate, frames=200, 
                               interval=50)

就是这样!不算太糟吧?为了庆祝我们的成功动画,这里是另一个 gif:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

The Game of Life: Endgame 😃

结论

我希望你觉得这是有用的。在我离开之前,让我来为我们今天学到的动画功能的更多类似数据科学的应用集思广益:

  • 一个接一个画出蒙特卡洛模拟,这样你可以观察结果分布是如何逐渐形成的。
  • 向前遍历时间序列数据,以便描述模型或数据在新观测值到达时的反应。
  • 突出显示当您更改输入(如分类数)时,算法所识别的分类是如何变化的。
  • 一段时间内或跨数据的不同子样本的关联热图,以直观显示不同样本如何影响模型的估计参数。

干杯!

更从我:

关于数据科学的思考。

随机森林算法。

神经网络如何工作。

逻辑回归解释。

用分类为故事片增色不少——第二部分

原文:https://towardsdatascience.com/spicing-up-feature-film-credits-with-classification-part-ii-c715d8375975?source=collection_archive---------15-----------------------

为什么我记不住名字,但结果却是最好的。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Nobody:
The credits: Tom Skerritt, Sigourney Weaver, Veronica Cartwright, Harry Dean Stanton, John Hurt, Ian Holm, Yaphet Kotto, Bolaji Badejo, Helen Horton, Eddie Powell, Gordon Carroll, David Giler, Walter Hill, Ivor…

背景

对于试图将“接触这部电影”的人与最终观看这部电影的人联系起来的努力来说,演职员表是一个可悲的借口。这是一种如此沉闷的形式,以至于影院通常会为电影观众照亮房间,让他们舒适地离开影院*。我想遏制这种习俗,让人们对滚动而过的无数名字产生兴趣。在我的上一篇文章中,我解释了为什么我用国籍来标记名字——简单地说,当陌生人发现他们有共同的起源故事时,他们会立即联系起来。*

我想做一些像下面的样本信贷序列。让我们称这种格式为“标记标志”

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

The flag-tags are randomly assigned and do not accurately represent ethnonationality.

这篇文章是关于一个开发者的快速和肮脏的技术解决方案如何不能改变一种文化,而是作为一个强大的工具来揭露和纠正幼稚。谁的?首先是我的。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Situation ‘A’ — I am the go-to credits-processing guy.
Situation ‘B’ — Where I am now, developing a client-side post-production solution.

我将从解决我最初项目的绝望开始。理想情况下,我会将国籍信息整合到电影本身(国旗标记)中,以使片尾字幕比电影结尾的浴室更有吸引力。由于我没有运行一个已建立的编辑操作,也没有为快速演职员表处理提供素材,所以我更倾向于编写一个脚本,通过(1)以编程方式在流式视频中查找姓名,以及(2)插入动画图形以追溯分配国籍信息来自动标记标志。这超出了我的能力范围。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Over time, the credits grow longer and list more distinct roles. [1]

但我艰难地前行,双手握着分类工具和公共数据库,希望揭示故事片漫长历史中的民族叙事。故事片的历史已经超过 100 年了,包括大约 30 万个名字。这是一个增加姓名数据的巨大过程,我遇到的挑战使我将范围缩小到一小群有趣的人。

请负责任地命名

从马克·哈米尔这个名字,你不能确定地收集种族国籍,但是你可以分配一个概率,这个名字是由某个种族血统的父母起的。这种任务非常适合分类算法,该算法分析输入,并使用机器学习模型将它们分类到与其属性最接近的类别中。让我们看看 NamSor 如何预测这位著名美国演员的名字的遗传,我们知道是英国、爱尔兰、苏格兰、威尔士(父系)和瑞典(母系)血统。

Baby Name Wizard and Ancestry serve as the base-truth origin for the first and last names. For “Mark Hamill, U.S.A.,” NamSor’s most confident predictions are “British” and then “(German).”

看到它正确地猜中了英国吗?那是 1 对 1!只差 299,999 就行了!

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

NamSor’s Diaspora endpoint drives the ethnonationality classification.

然而,我们不能指望 NamSor 是 100%准确的,即使它是一个神奇的 API。这是因为纳姆瑟认为传统是一种规则——父母给孩子取名以反映他们的传统。NamSor 不知道它被要求分类的名字可能不是出生时起的名字。

叫我那个名字

历史上,好莱坞的犹太人、意大利人和波兰人将他们的名字改成了更容易发音的艺名,有时会取一个没有任何血统痕迹的名字,以此与容易受到歧视的种族群体划清界限。

I don’t care what anyone says, Herschlag has a nice ring to it!

我不能继续给人们贴上假定的民族标签,因为我知道我通过电影数据库获得的大部分名字并不是出生时就有的。幸运的是, IMBb 有我要找的每个人的出生和艺名,所以我只需要找出自 1900 年以来每年最受欢迎的 5-7 部电影中的每个人(n≈100,000),他们为了在行业中更好的销售而改变了他们的名字,以便纠正“改名者”。在不知道任何个人改名的动机的情况下,为了消除误报,我假设他们换名字的原因不是下面的:

  1. 他们因结婚而改名。(对于娱乐圈女性来说,婚后保留婚前姓氏实际上更加专业。这些女人违背了自己的意愿。)
  2. 他们用名字的首字母或昵称来称呼。(想想作家兼演员 B·J·诺瓦克。)
  3. 他们获得了荣誉。(想想帕特里克·斯图尔特爵士。)

过滤之后剩下的是大约 7000 个重要的名字变更,相当于样本的 7%,以及一个值得探索的有趣群体!在我深入研究之前,我对这些数据有一些疑问,包括:

我们能观察到英语化是改名的一种趋势吗?

假设:由于大部分人的名字都是英国名字,你可以观察到美国电影业名字变化的英国化模式。

解析:**用 NamSor 确定某人的实际民族志是哑巴。相反,我使用 NamSor 来完成它的构建目的;我获得了与某人以前和现在的名字相关的民族“印象”,以寻找民族文化对该行业的重要性。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Too much?

事实:事实证明,听起来像英国人的名字会流向听起来像其他地方的名字。我的假设是错误的。看看这个。

We’re only looking at the eastern hemisphere because ethnonationality, even in melting pots like the USA, is categorized primarily by geographical ties developed over the Modern Era, when populations were concentrated outside of the Americas.

关于上面的可视化:
一条红线表示名字起源于英国,终止于别处。
蓝线表示该名称源自其他地方,终止于英国。
白线表示与英国无关。
线条的粗细表示一年内从一个“民族”到另一个“民族”的更名次数。

这里有很多可看的,但我想让你注意红色和蓝色的线,特别是在 1900-1970 年之间。在这段时间里,似乎有大量英国口音的名字流向了葡萄牙口音的名字。与假设相反,这表明存在巨大的去英语化效应。也许是因为人们想用听起来有民族特色的名字来区别于娱乐圈的人?

总结要点

在我发现我的假设是错误的之后,我觉得我的项目走进了死胡同。我的目标是推出一款能改变人们观看演职员表方式的产品,但我觉得自己一无所获。即使我开发了后期制作软件来改变演职员表的视觉内容,也不能保证任何人都会注意到,更不用说关心他们的电影结束后还有什么可看的了。相反,我开发了一个独立的网站,作为一个平台来展示我在探索和理解数据集的过程中取得的进步。当然,这意味着我发表的任何东西都与我所涉足的行业无关。虽然这个让我的影响可以忽略不计**,但它迫使我在暴露一些人们会觉得有趣的东西之前,发现我手头的数据和工具的真正价值。当我发现我的假设是错误的时候,探索一个未被触及的数据集的乐趣很快就消失了,但我相信从这群改名者身上可能会发现比我最初想象的更多的东西。**

会有第三部吗?只有时间会证明…同时,感谢您的阅读!如果我观察的人群或者我的分析过程对你有任何兴趣,请联系!

[1] 电影数据库(每年约 5 部最受欢迎的故事片)

我和 Jyotsna Pant 一起承担了这个项目,她是南加州大学社会公益计算的研究生。

将数据转化为思想

原文:https://towardsdatascience.com/spinning-data-into-thought-e8f89b417374?source=collection_archive---------24-----------------------

计算机如何思考:导论

任何一个见过一团羊毛、一段亚麻纤维或一捆原棉的人,理论上都能想象出如何将这些纤维捻成纱线,如何织一件羊毛套衫,或者如何织一条棉毯或帆布帆。纺织的艺术和科学没有利用线的任何隐藏的特性;要理解一种纤维如何被扭曲和打结以生产出一种柔韧、结实的织物,不需要发现晦涩的化学或古怪的物理现象。任何人都可以清楚地看到,正是物体的几何形状允许了这一点。

那是理论上的。但理论一如既往地超越实践。因为历史上没有一个人仅仅看着一段纤维就能完全理解。没有一个人能仅仅通过观察就能搞清楚所有的事情,或者如果有的话,他们有别的想法。纺织技术在历史上发展缓慢得令人难以置信,它是成千上万个人的聪明才智、辛勤劳动和辉煌时刻的产物。

考古记录告诉我们,第一批织物已有近三万年的历史,人们只知道它们留在小块粘土上的印记。从那时起,技术在世界各地被发明、遗忘和重新发现,并且随着简单线的几何性质的进一步回旋被解开,技术继续发展。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

A complex pattern emerges from the repetition of simple steps

现在最复杂的针织图案就像计算机代码的线条,事实上,这种相似不仅仅是表面上的。针织图案是一系列简单步骤的指令,重复这些步骤会产生非常复杂的结果。针织图案和计算机程序都编码了逻辑操作,这些逻辑操作被(人或计算机)解释成一个过程,该过程将简单的输入转换成非常有用的输出。这就是“算法”这个词的定义。这个词让人想起一些留着胡子、穿着粗花呢的教授在黑板上潦草书写的希腊字母,或者一台不可言喻的机器神秘的内部运作。但现实要容易得多。“算法”简单地说就是“一系列可重复的逻辑步骤”。从字面上看,纺织品是数学运算的产物,人类大规模运算能力的进步对我们的生活产生了深远的影响。

工业革命

纺车是一种非常简单的装置。它允许工人将纤维喂入旋转的轮子,轮子巧妙地将纤维捻成线,并将线收集在锭子上。至少在过去的一千年里,人们已经知道了各种形式的纺车。1764 年,根据一个几乎肯定不真实的传说,来自英国兰开夏郡的纺织工兼木匠詹姆斯·哈格里夫斯看到了一个翻转的纺车,并注意到这个方向可以让多个锭子排成一行,允许单个轮子将纤维纺到每个锭子上。他随后的发明“珍妮纺纱机”允许一个纺纱机完成八个纺纱机的工作。大约在哈格里夫斯发明的时候,据估计英国只有不到 50,000 个纱锭生产纱线。到了 19 世纪 20 年代,人口超过了 700 万。这种观点的微小转变,这种简单几何学在大范围内的应用,是后来成为英国工业革命的第一步。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

The Spinning Jenny

工业革命对我们社会的影响大得难以理解。起初是数量上的变化——一个工人做八个,然后十六个,然后几百个工人的工作——变成了社会结构的质变。全新的产业从旧产业的灰烬中诞生。曾经是富人专属的产品现在大众都可以买到了。随着交通网络的改善,世界实际上变得更小了——欧洲大陆上相距遥远的市场突然之间只有几天的路程。权力从根本上转移了,从拥有土地的贵族手中转移到拥有工厂的中产阶级手中。

并非所有这些变化都是积极的,也并非所有人都从技术发展的快速步伐引发的彻底社会变革中受益。虽然整个国家都渴望新的廉价织物,以及它们带来的可能性,但至少有一个群体中这些创新基本上不太受欢迎,他们开始了一场社会运动,其名称今天仍然流行,尽管不完全是他们希望的方式。他们自称为“勒德分子”。

当一个新的过程意味着一个人可以做八个人的工作时,结果不是八个人做八分之一的工作,而是一个人做同样多的工作,而另外七个人挨饿。为长袜和针织品生产精细针织面料的熟练工匠对这一数学结果并不感到兴奋。袜架发明于 1589 年,但在 18 世纪末得到改进和广泛采用,威胁到他们的生活方式。一个织袜机可以让相对不熟练的操作者更快、更可靠地完成织袜机的精细工作。为了保护他们的生计和生活方式,牧民们起义了。灵感来自(可能是虚构的)一个纺织工 Ned Ludd 的故事,他砸碎了他的织布机,库存商的抗议通常采取摧毁正在摧毁他们生计的机器的形式。卢德派诞生了。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

From 1812, “Machine Breaking” was punishable by deportation or death

在流行的观念中,卢德分子已经成为对技术恐惧的一种隐喻,他们的名字与顽固抵制进步同义。但是他们的实际立场更接近于传统的劳工运动。他们关心的不是新技术本身的兴起,而是这些新机器对他们的生活、他们预期的工作环境以及他们将获得的报酬的影响。

历史的潮流对卢德分子并不友好。抗议者们还来不及砸碎袜子,袜子框架就被制造出来了,袜子商们所学的手艺已经过时了,取而代之的是在炎热危险的工厂里挥汗如雨的非熟练工人。或许这些放养者能够找到新工作,在工业革命后出现的许多新行业中的一个——比如司炉工、点灯工、灯笼裤等等——但对他们中的许多人来说,这肯定是一个改变生活、痛苦的转变。

人工智能

根据这篇文章的背景,你可能会猜到我在打什么比方。在二十一世纪的最初几十年,我们正处于一个类似的过渡阶段。大量任务的自动化,用人工智能取代熟练工人,用按键取代困难任务的新流程,都呼应了工业革命的变化。伴随这些技术发展的社会变革是否会像工业革命那样深刻还有待观察,但我认为这种比较是有启发性的。

就像针织和编织技术在工业化之前很多年就已经为人所知一样,支撑人工智能的数学在很大程度上并不特别新。与纺织工艺品一样,它们只是对原材料几何特性的简单处理。对于纺织品来说,这是线。对于人工智能来说,这就是数据。已经发生的决定性转变并不在于新技术的发展(尽管已经开发了许多新技术)。更确切地说,是执行这些操作的速度和可供他们使用的数据量从质的方面改变了他们能够实现的目标。

我所说的“人工智能”并不是指无所不知的电脑霸主或邪恶的机器人助手的流行文化形象。我指的是允许我们将数字信息转化为更有用产品的广泛技术。这些技术已经广泛应用于各类消费品中。我们可以将关于人们购物习惯的信息转化为对明年流行趋势的预测,或者转化为关于某人下一步可能想要购买什么的建议。根据事故和伤害数据,我们可以建立模型,将人们分为高或低保险风险。我们可以将数字化文档转化为搜索算法,识别相关立法或研究。我们甚至可以建立模仿人类语言并对其做出反应的程序。当我们将“智能”应用于人类思维时,这些工具中的许多并不完全符合“智能”的概念。但总的来说,它们产生了以前只有人类思维才能实现的结果。

本质上,我们正在经历的变化是量变。我们总是能够预测时尚趋势,搜索文档,说话和倾听。改变的是我们执行这些行动的速度和规模。从受到人类操作员的速度和注意力范围的限制,我们已经转移到(在某些方面)较少受限的计算机能力。就像工业革命一样,技术发展提高了单个工人的生产率,就像工业革命一样,这对哪些技能是需要的,哪些技能是过时的产生了越来越大的影响。

我们用计算机智能分享我们的世界。如果可以说这些智能是“思考”的话,那么它们的思考方式与你我截然不同。描述它们作为智能是如何工作的,将它们与思考、感知的生物联系起来,充其量是一种牵强的隐喻,一种通过将新事物与熟悉的事物联系起来帮助我们理解新事物的工具。但这是一个越来越贴切的比喻。

像人类智能一样,人工智能是其历史的产物。他们是他们父母的孩子,他们反映了他们的创造者的偏见、弱点以及优点和价值观。他们和建造他们的人一样无辜。

人类智能和人工智能之间最大的区别,也是我们认为它们“像我们一样”思考的最大障碍,是它们的过程在很大程度上可以完全暴露。人类思维的内部运作对我们来说可能永远是神秘的,只有不精确的心理学和人类学才能探索。人脑的软肉很少告诉我们它所包含的内在生命。但是一个机器智能,尽管它可能是复杂的,总是可以简化为它的组成部分。当人工智能是一个“黑匣子”,向我们隐藏了它是如何得出结论的,我们可以想象它是一些抽象人格的作品。但是暴露出机器上的齿轮,解开挂毯上的结,我们可以看到简单的规则,1 和 0,编织和反编织,这些构成了这个复杂的织物。

就像在巫师的窗帘后面瞥见一样,看到魔术是如何表演的就驱散了魔法。但这是一个应该被驱散的魔法。为了理解这些新的人工智能,以更接近他们自己的方式与他们联系,我们需要对他们能做什么和不能做什么形成更好的预期,我们需要说一点他们的语言。像储备者一样,我们面临一个选择:我们可以效仿路德,试图摧毁或阻碍这些新机器,并保留我们目前享受的生活方式。或者我们可以试着与他们协商一种新的关系,为他们在我们的生活中找到一个家。

在下面的文章中,我将深入浅出地解释人工智能算法将数据转化为思维拟态的过程。我将从这些算法中最简单的开始,并介绍机器学习中的一些关键概念。我还将探索其他类型的人工智能,例如用于驱动视频游戏中计算机控制角色动作的人工智能。我还将冒险进入“深度学习”的世界,探索试图模仿人类大脑结构的神经网络。在这个过程中,我们将了解一些与我正在使用的数据相关的有趣话题。我们将找到与每个人的生活越来越相关的几个问题的答案:什么是人工智能?它是如何工作的?它能做好什么,做不好什么?智力到底是什么?最后,我希望,我们能对这些新的计算机智能多一点同情,多一点理解,这些智能正与我们分享我们的生活。

转到第一部分:决策树和恐龙

拆分数据集

原文:https://towardsdatascience.com/splitting-data-sets-cac104a05386?source=collection_archive---------9-----------------------

顶尖科学家如何简化庞大的数据集

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

科学家提供了非常大的数据集。这是有充分理由的;更多的数据让他们对正在研究的现象有了更透彻的理解。但这也产生了一个问题。随着数据集变得越来越大,它们变得越来越难以理解和使用。

一个很好的解决方案是学习如何自动将这些数据集分割成单独的文件。这样,每个数据文件就变成了一个易于管理的解释现象的一个方面的信息。很容易相处。而且,通过自动完成它,你可以不费吹灰之力就让你的生活变得更加轻松。

本文将教你如何做到这一点。它将引导你完成这个过程,并提供具体的 Python 代码(Python 2.7)。

它是在科学和工程中经常遇到的真实生活问题的背景下进行的。要理解正在研究的概念,请参见自动化性能图创建教程。

你可以通过阅读这篇文章来学习这些概念。如果您想更进一步,并且对您的技能和有用的工具充满信心,您可以下载配套数据集。这个配套数据集将允许您测试您的代码和检查您的结果,确保您正确地学习这个过程。

这篇文章将涵盖哪些内容?

在本教程的这一部分,我们将介绍将包含多个实验室测试结果的数据集分割成单个文件的过程。这些文件将分别包含单个测试的结果,并且将具有描述性文件名,说明其中包含的数据。我们将使用在自动化科学分析第二部分中描述的技术。

事不宜迟,我们开始吧。第一步是导入将启用数据分析过程的 Python 包。

如何用 Python 导入包?

每个 Python 脚本都需要以导入所需包和功能的语句开始。在这个数据文件分割脚本中,我们需要:

  • Pandas :这个包是 Python 中数据分析的首选。它允许您将数据读入数据帧(本质上是表),并提供了大量用于操作数据的工具。关于熊猫的丰富信息可以在创作者的书中找到 Python 进行数据分析
  • os : os 是一个 Python 包,可以让你使用电脑操作系统的命令,在数据分析过程之外影响电脑。在这种情况下,我们将使用它来创建新的文件夹。
  • Bokeh 是 Python 中的一个交互式绘图工具。它使您能够编写在分析数据时自动生成图的代码,并为用户提供与它们交互的选项。进一步了解散景的一个很好的来源是用散景进行实际数据可视化

在这种情况下,我们将拉进熊猫和操作系统的整体,但只有从散景特定的功能。为此,请将以下代码添加到程序的开头。

import pandas as pd
import os
from bokeh.plotting import figure, save, gridplot, output_file,   ColumnDataSource
from bokeh.models import HoverTool

您可以看到这四行输入了指定的包。注意,导入 pandas 的行还指定 pandas 已经作为 pd 导入,这意味着我们可以在代码的其余部分将 pandas 称为“pd”。另请注意,“ColumnDataSource”位于以“from bokeh.plotting”开头的代码行上。

既然我们的包已经导入,下一步就是读取必要的数据,这样脚本就可以使用它了。

我如何读取数据文件?

熊猫有一个很棒的导入数据集的工具。是 read_csv 函数。为了读取文件,您调用 pandas.read_csv 函数,指定文件的位置,并将其设置为一个变量。如果需要,您可以使用其他几个修改器来定制导入,但是我们不会在这里使用它们。

这个命令需要用来导入两个文件。首先是数据集本身。如果你下载了配套的数据集,它的标题是’缔约方会议 _HPWH_f_Tamb&Tavg.csv '。如果我们假设您将文件保存在文件夹“C:\ Users \ your name \ Documents \ automated data analysis”中,那么我们可以使用以下代码导入数据:

Data = pd.read_csv(r’C:\Users\YourName\Documents\AutomatedDataAnalysis\COP_HPWH_f_Tamb&Tavg.csv’)

执行该代码将导致数据集保存到变量“data”中。然后,通过引用数据,可以在数据集上使用 Pandas 的所有数据分析功能。

第二个需要的文件是描述文件中包含的测试的表格。为了便于学习,如果您自己创建表格会很有帮助。数据集包含在不同环境温度(环境温度指被测设备周围的空气温度)下进行的三次测试的结果。要创建此数据集,请生成一个包含以下信息的表格,并将其另存为“Test_Plan.csv ”,保存在与数据集相同的文件夹中。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

请注意,您稍后将引用列的名称,因此确保您使用的文本与示例数据中的文本相同非常重要。

现在已经创建并保存了表,您可以通过编写以下内容将其读入 Python:

Test_Plan = pd.read_csv(r’C:\Users\YourName\Documents\AutomatedDataAnalysis\Test_Plan.csv’)

既然数据已经读入 Python 脚本,下一步就是识别每个测试结束和下一个团队开始的行。

我如何识别每个测试的终点?

为了确定每个测试的结束和下一个测试的开始,你需要一些关于测试本身的知识。您需要考虑测试是如何执行的,并确定一个指示测试结束的条件。

在这种情况下,我们正在分析来自研究热泵热水器 (HPWH)测试的数据,这种热水器使用电力来加热水。因为我们在看加热水消耗了多少电力,所以我们可以知道在每次测试中消耗了多少电力。这意味着每次测试都在设备停止用电时结束。

我们需要确定设备停止用电的行。我们可以通过从前一行的用电量中减去每一行的用电量来实现。如果结果是否定的,这意味着 HPWH 消耗的电力比以前少,测试已经结束。

我们可以通过使用。我们数据集上的 shift()函数。这个函数做的和它听起来一样;它将数据移动指定的行数。我们可以利用。shift()在包含耗电量 P_Elec (W)的数据框中创建新行,数据已移动一行。我们可以用下面一行代码来实现这一点:

Data[‘P_Elec_Shift (W)’] = Data[‘P_Elec (W)’].shift(periods = -1)

这导致数据框中出现两个不同的列来描述 HPWHs 电力消耗。P_Elec (W)表示每行的耗电量,单位为瓦特。P_Elec_Shift (W)表示下一行的耗电量,单位为瓦特。如果我们从 P_Elec_Shift (W)中减去 P_Elec (W),负值的行将表示 HPWH 已经停止加热。我们可以用下面的代码做到这一点:

Data[‘P_Elec_Shift (W)’] = Data[‘P_Elec_Shift (W)’] — Data[‘P_Elec (W)’]

此时,我们有一个数据帧,其中每一行都包含 0,除了每个测试结束的行。我们可以使用这些信息创建一个列表,告诉我们每次测试何时结束。我们称这个列表为“测试结束”,以清楚地表明其中包含的信息。然后,我们将使用. index.tolist()函数来填充该列表。这可以通过下面的代码来完成:

End_Of_Tests = []
End_Of_Tests = End_Of_Tests + Data[Data[‘P_Elec_Shift (W)’] < 0].index.tolist()

第一行创建空列表“End_Of_Tests”虽然它最初不保存数据,但它已经准备好接受来自其他命令的数据。第二行向 End_Of_Tests 添加数据。它说要查看数据以识别‘P _ Elec _ Shift(W)’为负的行,识别这些行的索引,并将它们添加到 End_Of_Tests 列表中。

既然我们已经确定了对应于每个测试结束的行,我们可以将数据集分成单独的数据集,每个测试一个。

我如何分割数据文件?

可以使用以下步骤将数据文件分割成更易于管理的文件:

  • 首先,您需要为每个测试重复这个过程一次。这意味着我们需要为 End_Of_Tests 中的每个条目迭代一次。
  • 其次,您需要创建一个新的数据框,该数据框是仅包含来自单个测试的数据的原始数据框的子集。
  • 第三,您需要使用测试的条件来识别这个数据所代表的测试计划中的测试。
  • 第四,您需要将数据保存到一个新文件中,该文件的文件名说明了文件中包含的数据。

迭代测试结束

第一步可以通过一个简单的 for 循环来完成,该循环遍历 End_Of_Tests 列表。这可以通过下面的代码来完成:

for i in range(len(End_Of_Tests)):

这将创建一个运行 x 次的循环,其中 x 是 End_Of_Tests 中的行数/文件中包含的测试数。请注意,I 将是一个递增的整数(0、1、2 等等),可以用于索引。还要注意,我们现在有一个活动的 for 循环,所以所有未来的代码都需要缩进,直到我们离开 for 循环。

使用数据子集创建新的数据框

第二步可以通过使用 End_Of_Tests 的值来标识对应于每个测试的数据行来完成。在第一个测试中,这意味着我们需要选择 End_Of_Tests 中第一行和第一个值之间的数据。在第二个测试中,这意味着我们需要选择 End_Of_Tests 中第一个值和 End_Of_Tests 中第二个值之间的数据。第三次也是如此,如果我们在这个数据集中进行了三次以上的测试,那就更多。

第一个测试(从硬编码的第 0 行开始)和未来的测试(从 End_Of_Tests 中的一个条目开始)在处理上的不同之处在于,我们需要一个 if 语句来改变代码,这取决于我们是否要取出第一个测试。

然后,代码需要使用 End_Of_Test 值来标识我们想要的数据部分,并将其保存到新的数据帧中。

这可以通过下面的代码来实现:

if i == 0:
    File_SingleTest = Data[0:End_Of_Tests[i]]
else:
    File_SingleTest = Data[End_Of_Tests[i-1]:End_Of_Tests[i]]

第一行检查这段代码是否是第一次执行。如果是,这意味着这是第一次通过循环,我们提取第一个测试。如果是,代码通过 End_Of_Tests 中的第一个条目(用 End_Of_Tests[i]表示,当前是 End_Of_Tests[0])提取第一行数据(索引 0),并将其存储在名为 File_SingleTest 的新数据帧中。如果不是第一次通过代码,这意味着我们需要从一个不是第一次的测试中提取数据。在这种情况下,我们提取从前一个测试结束时(End_Of_Tests[i-1])到当前测试结束时(End_Of_Tests[i])的数据,并将其保存到 File_SingleTest。

注意,数据总是保存到 File_SingleTest。这意味着包含来自单个测试的数据的数据帧将总是在下一次迭代中被覆盖。在这种情况发生之前保存数据非常重要!

确定每个测试的条件

现在我们有了一个包含特定测试数据的数据框架。但是到底是哪个测试呢?我们需要阅读数据来理解测试中发生的事情,并将其与测试计划中的规范进行比较。通过这种方式,我们可以识别数据框中的测试,并给数据框一个描述性的名称。

查看测试计划,我们会发现每次测试的环境温度都会发生变化。测试 1 的环境温度为 55 华氏度,测试 2 的环境温度为 70 华氏度,测试 3 的环境温度为 95 华氏度。这意味着环境温度是我们这里的描述符。

我们可以用以下代码识别测试过程中的环境温度:

Temperature_Ambient = File_SingleTest[‘T_Amb (deg F)’][-50:].mean()

此行读取 File_SingleTest 数据帧中代表环境温度(’ T_Amb(华氏度)')的列的最后 50 个条目([-50:]),并计算平均值(。均值())。然后,它将该值存储在 Temperature_Ambient 中。这样,我们将测试最后几分钟的环境温度存储在一个变量中。

第二步是将这个值与测试计划进行比较,并确定执行了哪个测试。这很重要,因为没有测试数据是完美的,平均环境温度也不会完全符合测试计划中的规格。例如,指定环境温度为 55 度的测试实际环境温度可能为 53.95 度。识别相应的测试使文件管理更容易。

相应的试验可通过 1)计算试验中的平均温度与每次试验中要求的环境温度之间的差值,以及 2)确定具有最小差值的试验来确定。这可以通过下面两行代码来完成:

Test_Plan[‘Error_Temperature_Ambient’] = abs(Test_Plan[‘Ambient Temperature (deg F)’] — Temperature_Ambient)
Temperature_Ambient = Test_Plan.loc[Test_Plan[‘Error_Temperature_Ambient’].idxmin(), ‘Ambient Temperature (deg F)’]

第一行向 Test_Plan 数据框添加了一个新列,该列说明了该测试中要求的环境温度与活动测试中的平均环境温度之间的差值的绝对值。第二行使用。loc()和。idxmin()函数识别测试中误差最小的环境温度,并将该环境温度设置为我们的 Temperature_Ambient 变量。

现在我们准备将数据保存到一个新文件中。

将数据保存到新文件

有了包含单个测试结果的数据框和该测试条件的知识,我们现在可以将结果保存到一个新文件中。本节将向您展示在中讨论的技术之一,自动存储来自自动化数据集的结果。

第一步是确保我们要保存数据的文件夹存在。我们可以手动完成,但这是一篇关于自动化的文章!让我们编写脚本来为我们做这件事。

假设我们希望数据存储在文件“C:\ Users \ your name \ Documents \ automating data analysis \ Files _ individual tests”中。为了确保该文件夹存在,我们可以使用以下代码:

Folder = ‘C:\Users\YourName\Documents\AutomatingDataAnalysis\Files_IndividualTests’
if not os.path.exists(Folder):
    os.makedirs(Folder)

第一行将所需文件夹的路径设置为变量 folder。第二行使用 os 函数. path.exists()检查并查看该文件夹是否存在。如果它不存在,它将执行第三行代码来创建文件夹。这样我们才能确保它的存在。

一旦文件夹存在,我们可以使用相同的方法将数据文件保存到该文件夹中。我们指定想要使用的文件名,使用变量来包含关于环境温度的数据,并使用熊猫。to_csv()函数将文件保存到我们想要的位置。这可以通过下面的代码来完成:

Filename_SingleTest = “\PerformanceMap_HPWH_” + str(int(Temperature_Ambient)) + “.csv” 

File_SingleTest.to_csv(Folder + Filename_SingleTest, index = False)

第一行创建了我们想要使用的文件名。它描述性地指出,这是一个包含测试数据的文件,用于创建 HPWH 的性能图。第二部分更重要。它将我们在测试计划中确定的环境温度作为一个变量,将其转换为一个整数,转换为一个字符串,并将其添加到文件名中。现在文件名包含了测试的条件,在你打开它之前告诉你文件到底包含了什么。

第二行做了繁重的工作。它将先前指定的文件夹与当前数据框的文件名合并,并保存它。请注意,它还会删除索引,因为保存索引并不重要,有助于保持文件的整洁。

最后一步是什么?

现在,您已经进入了编写这个脚本的有趣部分。你可以经营它。你可以看到程序产生你需要的结果。

注意,这个过程在本教程中有点矫枉过正。伴随数据集有来自三个虚构测试的结果。在一个有三个测试的项目中这样做并不困难、耗时或者乏味。但是如果你有 1000 个测试呢?那么这个过程就变得极其有价值。

我如何检查我的结果?

在这个过程中有两个步骤来检查你的结果。

首先是确保您得到了正确的文件作为输出。为此,您将您的新文件夹中的文件与测试计划中所要求的测试进行比较。每个测试应该有一个文件,文件名中的条件与测试计划中要求的条件相匹配。

在此过程中,您应该会在文件夹中看到以下文件:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

注意在那个文件夹中有三个文件,并且在测试计划中指定了三个测试。还要注意测试计划是如何要求在 55 度、70 度和 95 度进行测试的,这三个温度是在三个文件名中指定的。到目前为止,看起来一切正常。

第二步是检查每个文件中包含的数据。最简单的方法是绘制数据并直观地检查它(不过,我们将在后面讨论自动完成这个任务的方法)。

快速检查是从每个数据文件创建一个显示环境温度的图。这可以通过散景来实现。

首先,我们需要在数据框中创建一个新列,以用户友好的方式给出测试时间。在测试开始后的几分钟内。我们可以通过在程序中添加下面一行来做到这一点(如果我们假设测量之间的时间是 5 秒):

File_SingleTest[‘Time_SinceStart (min)’] = File_SingleTest.index * 10./60.

这给了我们一个用户友好的时间来作为我们图中的 x 轴。然后我们可以使用散景来创建和保存情节。我们用下面的代码实现了这一点:

p1 = figure(width=800, height=400, x_axis_label=’Time (min)’, y_axis_label = ‘Temperature (deg F)’)
p1.circle(File_SingleTest[‘Time_SinceStart (min), File_SingleTest[‘T_Amb (deg F)’], legend=’Ambient Temperature’, color = ‘red’, source = source_temps)

第一行创建一个名为 p1 的图形,并指定地块的大小和轴标签。第二行将红色圆圈添加到绘图中,x 值由“自开始时间(分钟)”指定,y 值由“T_Amb(华氏度)”指定。它还规定图例读数应为“环境温度”。

可以使用 gridplot 和 outputfile 功能保存绘图。

p=gridplot([[p1]])output_file(Folder + '\PerformanceMap_HPWH_T_Amb=’ + str(int(Temperature_Ambient)) + ‘.html’, title = ‘Temperatures’)save(p)

散景有一个方便的功能叫做 gridplot,可以在一个文件中存储多个图。这使得查看相邻的相关图,比较其中的数据变得非常方便。此功能对于本教程不是必需的,因此我们只在网格中输入了当前图(p1)。但你应该了解一下,以防将来需要。

第二行指定文件的保存位置。它放在我们保存。数据的 csv 文件,并使用与以前相同的文件名约定。不同之处在于数据保存在。csv 文件,保存在. html 文件中。

第三行最后保存数据。

如果您重新运行代码,您会看到同样的结果。结果文件夹中的 csv 文件。此外,您现在会发现新的。html 文件。这些。html 文件包含数据集的绘图。

如果你打开这些图,你会看到什么?

首先,您希望测试期间记录的环境温度与测试计划和文件名中指定的值相似。在 55 度下的测试应该具有大约 55 度的环境温度,等等。

其次,这是真实世界的数据,你不应该期望它是完美的。有些值会是 54.5,有些会是 55.2,依此类推。别为那件事大惊小怪。

第三,你应该期望在测试开始时看到温度的调整。原始值将是之前测试的温度,然后实验室需要逐渐将温度更改为新的设置。

如果我们打开 55 度测试的曲线图,我们应该会看到这一点。您的结果应该是这样的:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

注意温度是如何从 75 度开始,然后迅速下降到 55 度的。不出所料。注意整个测试过程中的平均温度明显是 55 度。不出所料。还要注意数据是如何在 55 度左右反弹的,而不是像预期的那样准确。

这个文件意味着测试被正确地执行,并且数据文件被正确地分割。你已经准备好进入下一步了!

下一步是什么?

本文是指导您如何自动化科学数据分析的教程的第 1 步。既然庞大的数据文件被分成了三个独立的文件,每个测试一个,我们可以开始利用这些数据文件了。下一步是检查过程数据文件,执行我们的分析。当分析完成后,我们可以检查结果,以确保测试和计算正确进行。

教程目录

这是一系列文章的一部分,教你自动分析实验室数据和绘制热泵热水器性能图所需的所有技巧。本系列的其他文章可以通过以下链接找到:

简介

自动分析实验室测试数据

检查分析实验室数据的错误

如何编写检查数据质量的脚本

如何在 Python 中自动生成回归

拆分数据以适应任何机器学习模型

原文:https://towardsdatascience.com/splitting-your-data-to-fit-any-machine-learning-model-5774473cbed2?source=collection_archive---------14-----------------------

使用 scikit-learn,只需几行代码就可以将数据集分成训练和测试,并将特征从目标中分离出来。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

简介

在您执行了数据清理、数据可视化并了解了数据的详细信息后,是时候将第一个机器学习模型放入其中了。今天,我想与大家分享几行非常简单的代码,这些代码将把任何数据集划分为变量,您可以将这些变量传递给任何机器学习模型,并开始训练它。

这非常简单,但是对于任何数据科学家来说,理解训练和测试数据集的 split 函数是如何工作的是至关重要的。让我们深入研究一下。

准备数据

我们将为可视化目的创建一个简单的医疗数据集。想象一下,我们正试图根据患者的体重、身高以及他是否饮酒的信息来预测他是否健康。因此,我们将有三列包含此信息,第四列包含患者是否健康的记录。这是我们的目标变量,我们想要预测的东西。为了简单起见,这里我们将只有 10 个病人。让我们创建这个数据框:

import pandas as pd
import numpy as np
client_dictionary = {'weight': [112, 123, 176, 145, 198, 211, 145, 181, 90, 101], 
                     'height': [181, 165, 167, 154, 181, 202, 201, 153, 142, 169],
                     'drinks alcohol': [0, 1, 1, 1, 0, 1, 1, 1, 0, 1],
                     'healthy': [0, 1, 1, 1, 0, 0, 1, 1, 1, 1],}
df = pd.DataFrame(client_dictionary)
df.head(10)

我们的数据框如下所示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

从目标变量中分离特征

我们应该从将模型的特征从目标变量中分离出来开始。注意,在我们的例子中,除了*‘healthy’*之外的所有列都是我们想要用于模型的特性。我们的目标变量是“健康”。我们可以使用下面的代码来进行目标分离。

x_data = df[['weight', 'height', 'drinks alcohol']]
y_data = df['healthy']

现在我们的 x_data 看起来像这样:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

y_data 是这样的:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

使用 train_test_split

现在让我们使用 scikit-learn 函数中的 train_test_split 将特征数据(x_data)和目标数据(y_data)进一步划分为 train 和 test。

from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(x_data, y_data ,test_size = 0.2, shuffle=False)

因此,我们将初始数据框划分为四个不同的数据集,如下图所示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

当调用 train_test_split 函数时,我使用了参数 shuffle=False 来实现你在上图中看到的结果。前 80 %的数据用于训练,剩下的 20%用于测试。shuffle 的默认参数是 True ,这是您在现实生活中通常使用的参数。这意味着在数据被分成 test 和 train 之前的行被混洗,因此它们的顺序被改变。

控制测试列车分割比例

您可以通过使用测试大小参数来控制训练测试分割分数。请注意,在我们的示例中,我们将其设置为 0.2。它可以是 0.0 到 1.0 之间的任何数字。您不必指定训练集的分数,因为默认情况下,它将使用测试集未获取的所有剩余数据。

训练你的机器学习模型

您准备的数据现在可以输入到机器学习模型中了。我们就用一个非常简单的线性回归模型来说明吧。

from sklearn import linear_model
linear_regression_model = linear_model.LinearRegression()
linear_regression_model.fit(x_train, y_train)

上面的模型是使用 x_train 和 y_train 样本训练的。现在,您可以使用以下代码对测试集进行预测。

y_pred = linear_regression_model.predict(x_test)

现在,如果您想评估您的模型有多好,您需要将您在测试集(y_pred)上的预测与测试集(y_test)的实际目标值进行比较。但这是一个不同的故事,我们不会在这里讨论。

结论

您已经学习了如何将数据集分成特征和目标变量,然后进一步将其分成测试和训练部分。而这一切只需要几行代码,优雅而简单。

此外,我希望你能理解拆分是如何工作的,并且从现在开始你会明智地使用它,而不是仅仅复制粘贴你找到的代码。

如果你不时地复制粘贴代码,那也没问题,但是当你真正理解它的时候,你会感到多么满足。

最初发表于 aboutdatablog.com: 拆分你的数据以适合任何机器学习模型2019 年 10 月 23 日。

PS:我正在 Medium 和aboutdatablog.com上撰写深入浅出地解释基本数据科学概念的文章。你可以订阅我的 邮件列表 以便在我每次写新文章时得到通知。如果你还不是中等会员,你可以在这里加入

下面还有一些你可能喜欢的帖子

* [## python 中的 lambda 函数是什么,为什么你现在就应该开始使用它们

初学者在 python 和 pandas 中开始使用 lambda 函数的快速指南。

towardsdatascience.com](/what-are-lambda-functions-in-python-and-why-you-should-start-using-them-right-now-75ab85655dc6) [## Jupyter 笔记本自动完成

数据科学家的最佳生产力工具,如果您还没有使用它,您应该使用它…

towardsdatascience.com](/jupyter-notebook-autocompletion-f291008c66c) [## 当你开始与图书馆合作时,7 个实用的熊猫提示

解释一些乍一看不那么明显的东西…

towardsdatascience.com](/7-practical-pandas-tips-when-you-start-working-with-the-library-e4a9205eb443)*

体育分析:国际足球比赛的探索性分析-第一部分

原文:https://towardsdatascience.com/sports-analytics-an-exploratory-analysis-of-international-football-matches-part-1-e133798295f7?source=collection_archive---------26-----------------------

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

数据科学和分析有大量的应用领域,基本上每次这些信息都是以数据的形式提供的。

体育产业也不例外。到处都有很好的业务,通过强大的分析工具研究体育市场的可能性是一个很大的附加值。

在这篇文章中,我将提供一些分析足球比赛的工具。这个想法是用 Python Streamlit 开发一个 web 应用程序(如果你想阅读关于这个工具的介绍,你可以在这里阅读我以前的文章),它以一种直观和交互的方式,允许用户从庞大的数据集中总结相关信息。为此,我将使用从 1872 年到 2019 年的国际足球比赛结果,可在 Kaggle 上获得。

首先,让我们导入数据集并查看一下:

import pandas as pd 
df = pd.read_csv('results.csv') 
df.head()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

其中:

date - date of the match
home_team - the name of the home team
away_team - the name of the away team
home_score - full-time home team score including extra time, not including penalty-shootouts
away_score - full-time away team score including extra time, not including penalty-shootouts
tournament - the name of the tournament
city - the name of the city/town/administrative unit where the match was played
country - the name of the country where the match was played
neutral - TRUE/FALSE column indicating whether the match was played at a neutral venue

现在,这个想法是创建一系列与这个数据集的交互,突出我们感兴趣的球队或比赛的特征。我们可以获得大量的信息,本文并不打算列出所有的信息。尽管如此,Streamlit 的优势之一是它能够不断修改,因此您可以随着时间的推移使您的基本框架变得更加复杂,而无需放弃您到目前为止所做的工作。

和我之前的文章一样,我在这里附上了完整的代码:

import streamlit as st
import pandas as pd
import numpy as np
import plotly.express as px
import seaborn as sns
import matplotlib.pyplot as plt
import plotly.graph_objects as go
from plotly.subplots import make_subplotsst.title('Internationa Football matches')
df = pd.read_csv("results.csv")if st.checkbox('Show dataframe'):
    st.write(df)st.subheader('Filtering dataset per team')teams = st.multiselect('Pick your teams', df['home_team'].unique())new_df = df[(df['home_team'].isin(teams)) | (df['away_team'].isin(teams)) ]if st.checkbox('Show only home matches'):
    st.write(df[(df['home_team'].isin(teams))])if st.checkbox('Show only away matches'):
    st.write(df[(df['away_team'].isin(teams))])if st.checkbox('Show entire dataset'):    
    st.write(new_df)

st.subheader('Filtering dataset per event')
events = st.multiselect('Pick your events', df['tournament'].unique())
new_df_event = new_df[(new_df['tournament'].isin(events))]
st.write(new_df_event) 

st.subheader('Showing wins, losses and draws per team')team_wins = st.selectbox('Pick your teams', df['home_team'].unique()) 
new_df_wins = df[(df['home_team']==team_wins)|(df['away_team']==team_wins)]
new_df_wins=new_df_wins.reset_index(drop=True)

wins = 0
losses = 0
draw = 0
x = []    

for i in range(len(new_df_wins)):
    if new_df_wins['home_team'][i]==team_wins:
        if new_df_wins['home_score'][i]>new_df_wins['away_score'][i]:
            wins+=1
            x.append(1)
        elif new_df_wins['home_score'][i]<new_df_wins['away_score'][i]:
            losses+=1
            x.append(-1)
        else:
            draw +=1
            x.append(0)
    else:
        if new_df_wins['home_score'][i]<new_df_wins['away_score'][i]:
            wins+=1
            x.append(1)
        elif new_df_wins['home_score'][i]>new_df_wins['away_score'][i]:
            losses+=1
            x.append(-1)
        else:
            draw +=1
            x.append(0)

labels = ['Wins','Losses','Draws']
values = [wins, losses, draw]fig = go.Figure(data=[go.Pie(labels=labels, values=values)])
st.plotly_chart(fig)fig2 = go.Figure()fig2.add_trace(go.Scatter(x=list(new_df_wins['date']), y=x))# Add range slider

fig2.update_layout(
    xaxis=go.layout.XAxis(
        rangeselector=dict(
            buttons=list([
                dict(count=1,
                     label="1m",
                     step="month",
                     stepmode="backward"),
                dict(count=6,
                     label="6m",
                     step="month",
                     stepmode="backward"),
                dict(count=1,
                     label="YTD",
                     step="year",
                     stepmode="todate"),
                dict(count=1,
                     label="1y",
                     step="year",
                     stepmode="backward"),
                dict(step="all")
            ])
        ),
        rangeslider=dict(
            visible=True
        ),
        type="date"
    )
)st.plotly_chart(fig2)wins_h = 0
losses_h = 0
draw_h = 0
wins_a = 0
losses_a = 0
draw_a = 0
for i in range(len(new_df_wins)):
    if new_df_wins['home_team'][i]==team_wins:
        if new_df_wins['home_score'][i]>new_df_wins['away_score'][i]:
            wins_h+=1
        elif new_df_wins['home_score'][i]<new_df_wins['away_score'][i]:
            losses_h+=1
        else:
            draw_h+=1
for i in range(len(new_df_wins)):
    if not new_df_wins['home_team'][i]==team_wins:
        if new_df_wins['home_score'][i]<new_df_wins['away_score'][i]:
            wins_a+=1
        elif new_df_wins['home_score'][i]>new_df_wins['away_score'][i]:
            losses_a+=1
        else:
            draw_a +=1values_home = [wins_h, losses_h, draw_h]
values_away = [wins_a, losses_a, draw_a]
fig3 = make_subplots(rows=1, cols=2, specs=[[{'type':'domain'}, {'type':'domain'}]])
fig3.add_trace(go.Pie(labels=labels, values=values_home, name="Home"),
              1, 1)
fig3.add_trace(go.Pie(labels=labels, values=values_away, name="Away"),
              1, 2)fig3.update_layout(
    title_text="Wins, losses and draws home vs away",
    annotations=[dict(text='Home', x=0.18, y=0.5, font_size=20, showarrow=False),
                 dict(text='Away', x=0.82, y=0.5, font_size=20, showarrow=False)])fig3.update_traces(hole=.4, hoverinfo="label+percent+name")
st.plotly_chart(fig3)#4 subplots to see whether playing in a neutral field is causalwins_h_neutral = 0
losses_h_neutral = 0
draw_h_neutral = 0
wins_h_notneutral = 0
losses_h_notneutral = 0
draw_h_notneutral = 0wins_a_neutral = 0
losses_a_neutral = 0
draw_a_neutral = 0
wins_a_notneutral = 0
losses_a_notneutral = 0
draw_a_notneutral = 0for i in range(len(new_df_wins)):
    if new_df_wins['home_team'][i]==team_wins and new_df_wins['neutral'][i]:
        if new_df_wins['home_score'][i]>new_df_wins['away_score'][i]:
            wins_h_neutral+=1
        elif new_df_wins['home_score'][i]<new_df_wins['away_score'][i]:
            losses_h_neutral+=1
        else:
            draw_h_neutral+=1

for i in range(len(new_df_wins)):
    if new_df_wins['home_team'][i]==team_wins and not new_df_wins['neutral'][i]:
        if new_df_wins['home_score'][i]>new_df_wins['away_score'][i]:
            wins_h_notneutral+=1
        elif new_df_wins['home_score'][i]<new_df_wins['away_score'][i]:
            losses_h_notneutral+=1
        else:
            draw_h_notneutral+=1            

for i in range(len(new_df_wins)):
    if new_df_wins['home_team'][i]!=team_wins and new_df_wins['neutral'][i]:
        if new_df_wins['home_score'][i]<new_df_wins['away_score'][i]:
            wins_a_neutral+=1
        elif new_df_wins['home_score'][i]>new_df_wins['away_score'][i]:
            losses_a_neutral+=1
        else:
            draw_a_neutral +=1

for i in range(len(new_df_wins)):
    if new_df_wins['home_team'][i]!=team_wins and not new_df_wins['neutral'][i]:
        if new_df_wins['home_score'][i]<new_df_wins['away_score'][i]:
            wins_a_notneutral+=1
        elif new_df_wins['home_score'][i]>new_df_wins['away_score'][i]:
            losses_a_notneutral+=1
        else:
            draw_a_notneutral +=1            

values_home_neutral = [wins_h_neutral, losses_h_neutral, draw_h_neutral]
values_away_neutral = [wins_a_neutral, losses_a_neutral, draw_a_neutral]
values_home_notneutral = [wins_h_notneutral, losses_h_notneutral, draw_h_notneutral]
values_away_notneutral = [wins_a_notneutral, losses_a_notneutral, draw_a_notneutral]fig4 = make_subplots(rows=2, cols=2, specs=[[{'type':'domain'}, {'type':'domain'}], [{'type':'domain'}, {'type':'domain'}]],
                    subplot_titles=['Home neutral', 'Away neutral', 'Home not neutral', 'Away not neutral'])
fig4.add_trace(go.Pie(labels=labels, values=values_home_neutral, name="Home neutral"),
              1, 1)
fig4.add_trace(go.Pie(labels=labels, values=values_away_neutral, name="Away neutral"),
              1, 2)
fig4.add_trace(go.Pie(labels=labels, values=values_home_notneutral, name="Home not neutral"),
              2, 1)
fig4.add_trace(go.Pie(labels=labels, values=values_away_notneutral, name="Away not neutral"),
              2, 2)fig4.update_layout(title_text='Wins, losses and draws home vs away, neutral vs not neutral')fig4.update_traces(hole=.4, hoverinfo="label+percent+name")
st.plotly_chart(fig4)#best performance
st.subheader('Best Performance')t = []
for i in range(len(new_df_wins)):
    if new_df_wins['home_team'][i]==team_wins:
        t.append(new_df_wins['home_score'][i])
    else:
        t.append(new_df_wins['away_score'][i])

m = np.argmax(np.array(t), axis=0)
out = new_df_wins.iloc[m]
st.write(out)

我将我的脚本内容保存在一个名为 soccer.py 的文件中,然后在我的终端上用 streamlit soccer.py 运行它。

现在让我们一块一块地检查一下。首先,一旦给了用户可视化整个数据集的可能性,我添加了一些过滤器,以便您可以选择想要在您的数据集中可视化的团队。

st.title('International Football matches')
df = pd.read_csv("results.csv")if st.checkbox('Show dataframe'):
    st.write(df)st.subheader('Filtering dataset per team')teams = st.multiselect('Pick your teams', df['home_team'].unique())new_df = df[(df['home_team'].isin(teams)) | (df['away_team'].isin(teams)) ]

此外,您可以决定是否可视化您选择的球队在主场或客场进行的比赛:

if st.checkbox('Show only home matches'):
    st.write(df[(df['home_team'].isin(teams))])if st.checkbox('Show only away matches'):
    st.write(df[(df['away_team'].isin(teams))])if st.checkbox('Show entire dataset'):    
    st.write(new_df)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我还为锦标赛类型添加了一个过滤器:

st.subheader('Filtering dataset per event')
events = st.multiselect('Pick your events', df['tournament'].unique())
new_df_event = new_df[(new_df['tournament'].isin(events))]
st.write(new_df_event)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

很好,现在让我们来研究一些关于赢和输的特性。这个想法是,一旦选择了你感兴趣的球队,你将会看到一系列的信息(主要是图表形式),这些信息是关于不同时间和比赛的输赢趋势。

我们可以为一个团队做的一个非常基本的计算是计算赢、输和平的总数,然后用饼图显示我们的结果:

team_wins = st.selectbox('Pick your teams', df['home_team'].unique()) 
new_df_wins = df[(df['home_team']==team_wins)|(df['away_team']==team_wins)]
new_df_wins=new_df_wins.reset_index(drop=True)

wins = 0
losses = 0
draw = 0
x = []    

for i in range(len(new_df_wins)):
    if new_df_wins['home_team'][i]==team_wins:
        if new_df_wins['home_score'][i]>new_df_wins['away_score'][i]:
            wins+=1
            x.append(1)
        elif new_df_wins['home_score'][i]<new_df_wins['away_score'][i]:
            losses+=1
            x.append(-1)
        else:
            draw +=1
            x.append(0)
    else:
        if new_df_wins['home_score'][i]<new_df_wins['away_score'][i]:
            wins+=1
            x.append(1)
        elif new_df_wins['home_score'][i]>new_df_wins['away_score'][i]:
            losses+=1
            x.append(-1)
        else:
            draw +=1
            x.append(0)labels = ['Wins','Losses','Draws']
values = [wins, losses, draw]fig = go.Figure(data=[go.Pie(labels=labels, values=values)])
st.plotly_chart(fig)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们也可以看看这个数据的历史轨迹。可视化一些有意义的信息的一种方法是显示一个时间序列,其中如果在比赛中获胜,输出取值 1,如果失败,取值 1,如果平局,取值 0。通过这样做,如果有一段时间趋势持平于 1(这意味着我们正在检查的球队重复赢得了许多比赛),我们可能会有兴趣进一步调查这段时间(即,教练的名字,球队是否参加了特定的比赛,是否在主场比赛……)。

让我们来计算我们的时间序列(在我之前的代码中,我已经将我的值存储在数组 x 中):

fig2 = go.Figure()fig2.add_trace(go.Scatter(x=list(new_df_wins['date']), y=x))
# Add range slider

fig2.update_layout(
    xaxis=go.layout.XAxis(
        rangeselector=dict(
            buttons=list([
                dict(count=1,
                     label="1m",
                     step="month",
                     stepmode="backward"),
                dict(count=6,
                     label="6m",
                     step="month",
                     stepmode="backward"),
                dict(count=1,
                     label="YTD",
                     step="year",
                     stepmode="todate"),
                dict(count=1,
                     label="1y",
                     step="year",
                     stepmode="backward"),
                dict(step="all")
            ])
        ),
        rangeslider=dict(
            visible=True
        ),
        type="date"
    )
)st.plotly_chart(fig2)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

如您所见,我添加了交互式小部件(滑块和按钮),以便您可以关注相关的时间段。

现在,在调查输赢趋势时,可能相关的事情是分析比赛的地点(就主场/客场而言)是否会影响其结果。为此,让我们首先将我们的赢/输/平分为主场赢/输和客场赢/输/平:

wins_h = 0
losses_h = 0
draw_h = 0
wins_a = 0
losses_a = 0
draw_a = 0
for i in range(len(new_df_wins)):
    if new_df_wins['home_team'][i]==team_wins:
        if new_df_wins['home_score'][i]>new_df_wins['away_score'][i]:
            wins_h+=1
        elif new_df_wins['home_score'][i]<new_df_wins['away_score'][i]:
            losses_h+=1
        else:
            draw_h+=1
for i in range(len(new_df_wins)):
    if not new_df_wins['home_team'][i]==team_wins:
        if new_df_wins['home_score'][i]<new_df_wins['away_score'][i]:
            wins_a+=1
        elif new_df_wins['home_score'][i]>new_df_wins['away_score'][i]:
            losses_a+=1
        else:
            draw_a +=1
values_home = [wins_h, losses_h, draw_h]
values_away = [wins_a, losses_a, draw_a]
fig3 = make_subplots(rows=1, cols=2, specs=[[{'type':'domain'}, {'type':'domain'}]])
fig3.add_trace(go.Pie(labels=labels, values=values_home, name="Home"),
              1, 1)
fig3.add_trace(go.Pie(labels=labels, values=values_away, name="Away"),
              1, 2)fig3.update_layout(
    title_text="Wins, losses and draws home vs away",
    annotations=[dict(text='Home', x=0.18, y=0.5, font_size=20, showarrow=False),
                 dict(text='Away', x=0.82, y=0.5, font_size=20, showarrow=False)])fig3.update_traces(hole=.4, hoverinfo="label+percent+name")
st.plotly_chart(fig3)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

正如你所看到的,对于我们感兴趣的球队(苏格兰),有明确的证据表明,大多数胜利都发生在主场比赛的时候。我们还可以通过考虑位置的中立性来进一步研究这种关系:

wins_h_neutral = 0
losses_h_neutral = 0
draw_h_neutral = 0
wins_h_notneutral = 0
losses_h_notneutral = 0
draw_h_notneutral = 0wins_a_neutral = 0
losses_a_neutral = 0
draw_a_neutral = 0
wins_a_notneutral = 0
losses_a_notneutral = 0
draw_a_notneutral = 0for i in range(len(new_df_wins)):
    if new_df_wins['home_team'][i]==team_wins and new_df_wins['neutral'][i]:
        if new_df_wins['home_score'][i]>new_df_wins['away_score'][i]:
            wins_h_neutral+=1
        elif new_df_wins['home_score'][i]<new_df_wins['away_score'][i]:
            losses_h_neutral+=1
        else:
            draw_h_neutral+=1
for i in range(len(new_df_wins)):
    if new_df_wins['home_team'][i]==team_wins and not new_df_wins['neutral'][i]:
        if new_df_wins['home_score'][i]>new_df_wins['away_score'][i]:
            wins_h_notneutral+=1
        elif new_df_wins['home_score'][i]<new_df_wins['away_score'][i]:
            losses_h_notneutral+=1
        else:
            draw_h_notneutral+=1            

for i in range(len(new_df_wins)):
    if new_df_wins['home_team'][i]!=team_wins and new_df_wins['neutral'][i]:
        if new_df_wins['home_score'][i]<new_df_wins['away_score'][i]:
            wins_a_neutral+=1
        elif new_df_wins['home_score'][i]>new_df_wins['away_score'][i]:
            losses_a_neutral+=1
        else:
            draw_a_neutral +=1

for i in range(len(new_df_wins)):
    if new_df_wins['home_team'][i]!=team_wins and not new_df_wins['neutral'][i]:
        if new_df_wins['home_score'][i]<new_df_wins['away_score'][i]:
            wins_a_notneutral+=1
        elif new_df_wins['home_score'][i]>new_df_wins['away_score'][i]:
            losses_a_notneutral+=1
        else:
            draw_a_notneutral +=1
values_home_neutral = [wins_h_neutral, losses_h_neutral, draw_h_neutral]
values_away_neutral = [wins_a_neutral, losses_a_neutral, draw_a_neutral]
values_home_notneutral = [wins_h_notneutral, losses_h_notneutral, draw_h_notneutral]
values_away_notneutral = [wins_a_notneutral, losses_a_notneutral, draw_a_notneutral] fig4 = make_subplots(rows=2, cols=2, specs=[[{'type':'domain'}, {'type':'domain'}], [{'type':'domain'}, {'type':'domain'}]],
                    subplot_titles=['Home neutral', 'Away neutral', 'Home not neutral', 'Away not neutral'])
fig4.add_trace(go.Pie(labels=labels, values=values_home_neutral, name="Home neutral"),
              1, 1)
fig4.add_trace(go.Pie(labels=labels, values=values_away_neutral, name="Away neutral"),
              1, 2)
fig4.add_trace(go.Pie(labels=labels, values=values_home_notneutral, name="Home not neutral"),
              2, 1)
fig4.add_trace(go.Pie(labels=labels, values=values_away_notneutral, name="Away not neutral"),
              2, 2)fig4.update_layout(title_text='Wins, losses and draws home vs away, neutral vs not neutral')fig4.update_traces(hole=.4, hoverinfo="label+percent+name")
st.plotly_chart(fig4)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

最后,我们可以检索我们团队的最佳表现(这里,我认为最佳表现对应于我的团队进球最多的比赛)。

st.subheader('Best Performance')t = []
for i in range(len(new_df_wins)):
    if new_df_wins['home_team'][i]==team_wins:
        t.append(new_df_wins['home_score'][i])
    else:
        t.append(new_df_wins['away_score'][i])

m = np.argmax(np.array(t), axis=0)
out = new_df_wins.iloc[m]
st.write(out)st.subheader('Comparing 2 teams')
team1 = st.selectbox('Pick one team', df['home_team'].unique())
team2 = st.selectbox('Pick one team', df['home_team'].unique())

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

因此,我们使用直观的小部件收集了我们感兴趣的团队的相关信息,主要依靠数据的图形表示。在这里,我着重分析一个团队。在我的下一篇文章中,我将提出这个代码的进一步实现,从比较和检查两个队的比赛开始,然后进行一些预测。

所以敬请期待第二部

参考文献:

原载于 2019 年 10 月 25 日http://datasciencechalktalk.com

体育分析:国际足球比赛的探索性分析-第二部分

原文:https://towardsdatascience.com/sports-analytics-an-exploratory-analysis-of-international-football-matches-part-2-a20674cca78c?source=collection_archive---------18-----------------------

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在我之前的文章(本系列的第 1 部分)中,我已经为一个有意义的探索性分析实现了一些有趣的可视化工具。然后,用 Python 包 Streamlit ,我让他们以网络应用的形式互动。

在本文中,我将像以前一样继续处理相同的数据集,这次重点关注两个团队之间的交互。我将继续使用 Plotly 作为可视化工具,因为它提供了与图形交互和收集相关信息的可能性。由于我不会附上我以前的文章的代码,如果你是 Streamlit 的新手,我强烈建议你在开始之前阅读它。

现在,正如预期的那样,我想详细谈谈两支感兴趣的球队之间的比赛。因此,让我们首先用用户的多选来过滤我们的初始数据集(这里有可用):

import streamlit as st
import pandas as pd
import numpy as np
import plotly.express as px
import seaborn as sns
import matplotlib.pyplot as plt
import plotly.graph_objects as go
from plotly.subplots import make_subplotsst.title('Internationa Football matches')
df = pd.read_csv("results.csv")st.subheader('Comparing 2 teams')
teams_to_compare = st.multiselect('Pick your teams', df['home_team'].unique())comparison = df[(df['home_team'].isin(teams)) & (df['away_team'].isin(teams)) ]  
comparison = comparison.reset_index(drop=True)
st.write(comparison)
st.write('Number of matches: ', len(comparison))

对象’ teams_to_compare '将是两个队的列表,我对分析两个队之间的比赛感兴趣(不管哪一个队在主场比赛)。然后,我让我的应用程序显示新过滤的数据集以及匹配的数量:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在这里,我对英格兰对苏格兰的所有比赛都感兴趣,这是我最终的数据集。

现在让我们对这两个团队进行一些分析。

首先,我想知道哪场比赛的比赛强度最高,我决定将它量化为进球总数。因此,我创建了一个新的熊猫系列,作为两个“分数”列的总和,然后计算该系列的最大值的指数。

st.subheader('Highest intensity of play')out_c = comparison.iloc[np.argmax(np.array(comparison['home_score']+comparison['away_score']))]
st.write(out_c)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

所以,打得最多的比赛是 1961 年 4 月 15 日的英国锦标赛。同样的道理,你可以调查任何一种表现。也就是说,您可以要求显示两个队之间得分差距最大的比赛。

现在,我想可视化我的团队之间的胜败比例。为此,我将使用一个 Plotly 饼图:

team1_w = 0
team2_w = 0
teams_draw=0
team1_cum=[]
team2_cum=[]for i in range(len(comparison)):
    if comparison['home_team'][i]==teams_to_compare[0]:
        if comparison['home_score'][i]>comparison['away_score'][i]:
            team1_w+=1
            team1_cum.append(1)
            team2_cum.append(0)
        elif comparison['home_score'][i]<comparison['away_score'][i]:
            team2_w+=1
            team1_cum.append(0)
            team2_cum.append(1)
        else:
            teams_draw+=1
            team1_cum.append(0)
            team2_cum.append(0)
    else:
        if comparison['home_score'][i]<comparison['away_score'][i]:
            team1_w+=1
            team1_cum.append(1)
            team2_cum.append(0)
        elif comparison['home_score'][i]>comparison['away_score'][i]:
            team2_w+=1
            team1_cum.append(0)
            team2_cum.append(1)
        else:
            teams_draw+=1
            team1_cum.append(0)
            team2_cum.append(0)

comparison_labels = ['Team 1 wins','Team 2 wins','Draws']
comparison_values = [team1_w, team2_w, teams_draw]fig5 = go.Figure(data=[go.Pie(labels=comparison_labels, values=comparison_values)])
st.plotly_chart(fig5)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在上面的代码中,我还定义了两个列表,team1_cum 和 team2_cum,这样我就可以检查我的两个团队的获胜路径。因此,让我们构建一个带有按钮和滑块的折线图:

st.subheader('Cumulative wins of two teams')fig6 = go.Figure()fig6.add_trace(go.Scatter(x=list(new_df_wins['date']), y=np.cumsum(np.array(team1_cum)), name='team 1'))
fig6.add_trace(go.Scatter(x=list(new_df_wins['date']), y=np.cumsum(np.array(team2_cum)), name='team 2'))fig6.update_layout(
    xaxis=go.layout.XAxis(
        rangeselector=dict(
            buttons=list([
                dict(count=1,
                     label="1m",
                     step="month",
                     stepmode="backward"),
                dict(count=6,
                     label="6m",
                     step="month",
                     stepmode="backward"),
                dict(count=1,
                     label="YTD",
                     step="year",
                     stepmode="todate"),
                dict(count=1,
                     label="1y",
                     step="year",
                     stepmode="backward"),
                dict(step="all")
            ])
        ),
        rangeslider=dict(
            visible=True
        ),
        type="date"
    )
)st.plotly_chart(fig6)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

注意:在饼图中,看起来第二队(英格兰)赢了第一队(苏格兰)的大部分比赛。那么,为什么从上面的线形图来看,似乎在大部分时间里,苏格兰统治着英格兰呢?好吧,原因在于数据集:英格兰和苏格兰在 1910 年后打了大部分比赛,因此这与之前收集的信息一致。

此外,这个图表是有意义的。事实上,我们看到,直到 1910 年(或多或少),苏格兰一直统治着英格兰。这种趋势反转的原因是什么?人们可能会对这一特定事件感兴趣:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我还想检索另外两个元素。首先,我想知道这些比赛在每个城市进行了多少次。为此,我将构建一个条形图,显示每个城市在我的过滤数据集中出现的次数:

st.subheader('Frequency of city of matches')cities = comparison.groupby('city').count()['country'].index.values
occurrences = comparison.groupby('city').count()['country'].values
occurrences.sort()fig7 = go.Figure(go.Bar(
            x=occurrences,
            y=cities,
            orientation='h'))st.plotly_chart(fig7)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

其次,我想收集一些关于锦标赛类型的信息。这个想法是绘制一个气泡图,其 x 和 y 坐标是主客场得分,大小代表比赛的强度(进球总数),颜色代表锦标赛的类型。另外,为了知道我的哪支球队在主场比赛,我将把主场球队设置为 hover_name,它将显示在每个气泡的顶部。

st.subheader('Tournament information')comparison['challenge']=np.array(comparison['home_score']+comparison['away_score'])
fig8 = px.scatter(comparison, x="home_score", y="away_score",
          size="challenge", color="tournament",
                 hover_name="home_team")st.plotly_chart(fig8)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这张图表的第一瞥显示了进球数最高的比赛似乎是英国锦标赛。最后,让我们将这些信息与每对球队的锦标赛类型的频率结合起来:

tour = st.selectbox('Select a tournament', comparison['tournament'].unique())comparison_t = comparison[comparison['tournament']==tour] 
per = len(comparison_t)/len(comparison)st.write(f"{round(per*100,2)}% of matches between the 2 teams have been played as {tour} matches")

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

因此,不仅英国锦标赛举办了最高强度的比赛,而且英格兰和苏格兰之间的比赛也是最多的。

同样,正如我在上一篇文章中所预期的,这些只是你可以在数据集上构建的一小部分分析。这真的取决于你需要的信息,然而第一个解释性的洞察力总是一个好的起点,因为它可能会提供新的直觉和分析的角度。

我希望你喜欢阅读!

参考文献:

原载于 2019 年 10 月 28 日http://datasciencechalktalk.com

电子表格减缓了我们的进度

原文:https://towardsdatascience.com/spreadsheets-are-slowing-our-progress-caa1f7709302?source=collection_archive---------10-----------------------

当学术界关于可复制性危机的争论愈演愈烈的时候,还有一个更普遍的危机就在我们的眼皮底下

曾经有一段时间,我知道如何做分析的唯一方法是使用电子表格。我花了超过 15 年的时间在这些屏幕上建立高度复杂的模型和分析,这些屏幕上有很多人依赖的矩形盒子。

然后,2016 年初,我的一切都变了。对 R 和 Python 的大惊小怪开始了,我问了几个“了解内情”的人,他们认为哪种软件最适合进行最广泛的分析。我得到的一致回答都指向 R(大概是因为和我说过话的人)。所以我决定我要学习这个东西,看看所有的宣传是否值得。

六个月后,在经历了许多个深夜和周末后,通过寻找一切机会用 R 解决与工作相关的问题,我处于一种无法忍受再次在电子表格中工作的状态。我是认真的。今天打开它们是一件苦差事,我这样做的唯一原因是因为我和一些和我三年前处境相同的人交流。

我查看类似于VLOOKUP的函数,并将它们与dplyr::left_join()进行比较。这就好像我站在一条满是垃圾的街道上,一边是一个不幸的人,他有一个小垃圾收集器,正在一个接一个地收集垃圾,另一边是一个人拿着你见过的最大、最强大的垃圾吸尘器。

所以我认为电子表格对我们没有好处。他们把我们困在这一套狭窄的视图和选项中,在他们漂亮的外观和感觉上耗尽了我们所有的计算机内存,当有太多数据时,决定生闷气并冲进另一个房间,这一切都是因为(就像我们的孩子用 Snapchat 一样)我们已经沉迷于即时的视觉分析满足感。

多年来,电子表格造成危害还有另一个原因——它们造成了企业的可复制性危机。

什么是再现性危机?

科学方法的一个重要部分是要求重现结果以验证它们。大多数学者认为,至少在社会和生命科学领域,我们正处于可复制性危机之中。心理学是一个近年来被涂上厚厚一层油彩的领域。近年来,“研究”发表的速度导致了许多有问题的研究实践。在一项针对 2000 名心理学家的调查中,大多数人承认使用了至少一种有问题的研究方法,假阳性和确认偏差在受访者中普遍存在。当然,这种极端的结果是彻头彻尾的欺诈性研究,这是心理学和其他领域都无法避免的。

“流行心理学”出版物的兴起以及大众媒体对心理学理论和研究的大量引用导致了这样一种情况,即未经验证的想法正在作为“真理”获得地位,而没有人试图批评或验证产生这些理论的结果。在试图研究心理学中再现性危机的程度时,研究发现不到一半的验证先前研究的研究确实验证了它。

对于像心理学这样的领域,这可能是非常有害的。近年来,我们看到了一个积极的趋势,即越来越信任心理学家和心理计量学家在商业和企业中的价值,这是人们越来越认识到人才的战略重要性的一部分。但是,持续的无纪律出版无疑会在该领域造成越来越多的混乱和缺乏清晰度——很可能会有一天,你可以找到一些东西来支持你想表达的任何观点。从长远来看,这种稀释会损害油田。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

电子表格和这有什么关系?

暂时把学术放在一边,我认为在企业或公司环境中,分析需要可复制才能成功。通常不一定要验证以前的结果(尽管我认为这总是一件好事),但出于操作原因,通常需要重复类似的分析。我们看到,在企业环境中进行的分析的性质和类型越来越复杂,进行一次这样的分析而没有简单的方法在未来复制它是非常危险的。当环境发生变化时,可能需要再次运行分析,或者企业中的另一个团队可能需要为了不同的目的而尝试重复分析,

电子表格使得重现分析变得非常非常困难,特别是如果它本质上是复杂的。以下是几个原因:

  1. 电子表格的结构是混乱和非线性的。要追踪一个特定的值,您必须找到位于原始分析师选择的位置的从属值。你最终会在 EE:1254 单元格中某个名为“不重要”的隐藏表中找到它们。什么?
  2. 电子表格使得评论变得困难。当它跨越多个工作表和单元格时,作者很难解释他们在做什么。
  3. 电子表格将用户限制在一个共同的、狭窄的操作环境中。这使得非电子表格用户很难轻松直观地挖掘和理解已经完成的工作。相信我,非电子表格用户正在增加。

开源数据科学语言克服了所有这些挑战。每个人都可以访问他们需要的软件和代码库。代码是以线性的、逻辑的方式编写的,注释也很容易,因此其他用户可以理解你的逻辑,并准确理解你在每一步做什么。通过 Markdown 和 Jupyter 笔记本将代码集成到垂直文档中的能力使我们能够编写包含方法、代码和结果的完整研究描述。

对此我们应该做些什么?

如果你是一家企业的分析主管,而这家企业的大部分工作仍然是使用电子表格完成的,那么你就真的陷入了企业可复制性危机。是时候咬紧牙关向编码语言迈进了。这并不容易,开始可能会很艰难,但有了正确的承诺和资源,这是非常可行的。

如果你已经踏上了这条旅程,这里有一些我坚持认为有助于工作重现性的最佳实践:

  • 将鼓励和激励放在适当的位置,以确保所有代码都被自由地评论
  • 确保所有新的研究都写在一个包含嵌入式代码的垂直文档中(比如 R Markdown 或 Jupyter Notebook)。
  • 如果需要从以前的电子表格方法中复制一项工作,不要费心试图将电子表格拆开,只需从头开始,再次定义需求,并在新的环境中从头构建它。拆开一份电子表格就像让那个拿着垃圾收集器的人把他所有的东西放回原位,然后我们再用那个强大的真空吸尘器把它们全部吸走。这是毫无意义的,徒劳的努力。

我们可以轻松复制分析的未来是一个令人兴奋的未来,实现这个未来也越来越重要。但电子表格不会帮助我们实现这一目标。

最初我是一名纯粹的数学家,后来我成为了一名心理计量学家和数据科学家。我热衷于将所有这些学科的严谨性应用到复杂的人的问题上。我也是一个编码极客和日本 RPG 的超级粉丝。在 LinkedIn Twitter上找我。

大规模毁灭的电子表格

原文:https://towardsdatascience.com/spreadsheets-of-mass-destruction-754bf72724f9?source=collection_archive---------30-----------------------

人类最初的作品不是故事,而是电子表格。我们可以找到有关会计的最古老的文字记录。很快,电子表格成为了表现 it 的主要方式

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

The tablet Plimpton 322, from -1800 (source wikipedia)

人类同时创造了它最灾难性的发明:电子表格错误!上面的石碑已经包含了我们这个时代遗留下来的数学错误

工具从这些古老的时代开始进化。今天微软 Excel 或者谷歌工作表防止我们犯错……或者不犯错。

欧洲电子表格风险利益集团的任务是分析电子表格错误风险。他们的网站引用了恐怖故事,讲述一个简单的复制和粘贴就能让你损失数百万。

2016 年的一项研究分析了 35175 份包含基因研究数据的 excel 文件。他们发现几乎 20%的电子表格包含基因名称错误。2008 年的文学评论估计超过 90%的电子表格都有错误。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

使用电子表格进行战略决策

如今,金融服务业的高管们使用电子表格来制定战略决策。大多数情况下,电子表格导出是我们的客户要求与我们合作的一项强制性功能。这种格式现在是 B2B 数据通信标准,不是最好的。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

https://xkcd.com/1906/

这种流行是因为电子表格易于操作数据。今天,这种观点不再成立,许多编程语言更容易使用。例如,蟒蛇和熊猫是很好的替代品。

例如,假设您有一个电子表格,其中包含购买的元素数量及其价格。如果你想知道你的总支出,你可以写:

SUMPRODUCT(F1:F18,H1:H18)

在熊猫中,你可以写:

total_spending = ( data['quantity'] * data['price'] ).sum()

多亏了 Google Colab ,所有这些工具都可以在浏览器中获得,无需任何安装。

数据可靠性

改变你用来做战略决策的工具并不能解决所有问题。实现 math 时的错误并不新鲜。由于几十年的编程错误,好的实践出现了。电子表格用户大部分时间都不尊重它们:

  • 一个 excel 文件就能统治一切!让我们拥有这个包含所有数据和计算的巨大电子表格。
  • **复制粘贴。**让我们复制并粘贴公式及其包含的错误。
  • **手动操作。**为了对数据采取行动,让我们始终应用相同的手动步骤,确信不会出现人为错误。
  • **无测试。**为什么测试,这是平均值,平均值怎么会错?

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

今天,现代编码模式和方法应用最佳实践来限制这些问题:

  • 单一责任原则 。一个函数应该只负责分析的一部分。
  • 不要重复自己 。不要复制粘贴,使用函数,这样如果你发现一个错误,你可以在任何地方纠正它。
  • 绵集成 。自动化交付流程,使其更快、更可靠。
  • 自动测试。每次修改后都要测试。

我们仍然允许客户将数据导出为电子表格。但是我们把这种用法解释为我们产品中缺少的功能。这意味着他们需要更多的分析来采取行动。作为数据专家,我们的职责是提供这种分析,同时确保不出现灾难性错误。

这篇文章最初发表在 Cornis 的博客上。

2019 年春季:来自 Max Kelsen Genomics 的更新

原文:https://towardsdatascience.com/spring-2019-update-from-max-kelsen-genomics-6db00ebd3900?source=collection_archive---------40-----------------------

概述我们癌症和基因组学研究部门的最新进展和发展。

作者:ma ciej Trzaskowski 博士, Max Kelsen 的基因组学和机器学习研究负责人

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

自从我们的上一次更新以来,我们看到了我们研究团队的大量活动和成果。下面我们将概述我们癌症和基因组学研究部门的一些最新进展和发展,包括合作伙伴关系、项目和演示。

合作关系

MK Genomics 自豪地宣布,我们正在与谷歌云平台 (GCP)合作。通过这种合作关系,我们可以访问他们的 GPU 和 TPU 集群,以帮助处理全球多个地区的大量数据。

我们还与谷歌健康的基因组学团队签署了合作和拨款协议,利用这一基础设施开发新型深度学习应用。通过利用 Kubernetes-native 机器学习(ML)工作流编排器,如 ArgoKubeflow ,以及 GCP 基础设施(见下图 1),我们能够以最小的成本和周转时间交付成果。

对于谷歌健康团队,我们真的说不出足够多的好话,他们为研究社区做出了很大贡献,我们喜欢与他们合作。

项目

最初构想为免疫治疗结果预测(IOP) 的研究项目已被分解为一系列子项目,以使我们能够精确而集中地解决这些研究问题。这导致了一个名为 MK Genomics 的内部团体的形成。

MK Genomics 目前正在进行五个研究项目:

  • 免疫治疗结果预测(IOP)
  • 未知原发癌
  • 显著性映射
  • 心脏蛋白质组学
  • 慢性髓细胞白血病

IOPCUP 是两个最先进的项目,将在下面详细介绍。

免疫治疗结果预测(IOP)

处理生物学数据并不简单。它不仅在数据采集方面带来了很多挑战,这通常是高度侵入性的,而且在数据采用数字形式之前,在实验室中面临的生物和技术障碍也带来了很多挑战。在分析阶段,这些挑战往往被低估,因为分析的重点主要是统计的严谨性。

虽然在复杂数据集中寻找显著信号时,设计良好的 ML 模型至关重要,但仔细的专家质量控制通常是真正的生物信号和技术人工制品之间的屏障。在Max Kelsen,我们与一些世界上最有经验的癌症遗传学家合作( genomiQaQIMR ),他们拥有跨越数十年的专业知识,确保获得最高质量的数据。

基因研究的另一个重要挑战是有效处理海量数据的能力。在 x30 深度采样的单个全基因组序列可以消耗 100 千兆字节的存储空间,成对的肿瘤/正常文件集可以占用每个个体多达 300 千兆字节(因为肿瘤的测序深度通常至少是正常组织的两倍),成功的 ML 训练所需的样本可以达到数千千兆字节!

目前,MK Genomics 的人工智能基因组管道分为四个不同的阶段(见下图 1):

  • 第 1 阶段:数据接收 —来自我们自己的云基础架构、我们合作伙伴的基础架构或静态存储
  • 阶段 2:预处理——质量控制(例如,批次效应、分布特性等。),然后转换成 ML 友好的格式(例如,NumPy 数组、Pandas DataFrame、TensorFlow TFRecords 等)。)
  • 阶段 3:训练或推理 —运行模型推理,以分类、预测或重新训练超出模型分歧阈值的情况
  • 阶段 4: 分析后输出和存储 —用于报告或可视化目的

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 1: MK Genomics Generic Cloud Architecture

除了新的数据来源,我们还利用了公开可用的癌症数据集,探索癌症数据的统计特性,如分布概况、混杂控制、尺度转换和稀疏性。我们已经部署了各种各样的 ML 模型,不仅相互比较它们的性能,而且在可能影响我们发现的通用性的各种技术人工制品的场景中比较它们的性能。

传统疗法,如化疗和放疗是非特异性干预,旨在通过杀死所有细胞(包括健康细胞)来消除癌症。因此,这些疗法是高度侵入性的,并且许多患者经历了严重的副作用。不幸的是,传统疗法不能保证癌症的完全缓解。

另一方面,免疫疗法(见下图 2)的侵入性要小得多,因为它使用我们自身免疫系统的力量来靶向并摧毁癌细胞。这使得健康细胞保持完整,副作用更少——然而,并不是每个人都对这些疗法有反应。更好地理解缺乏应答的生物学原因不仅对总体上改进免疫疗法是至关重要的,而且对确定最有可能对其应答的个体也是至关重要的。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 2: Comparison of immunotherapy versus chemotherapy and radiotherapy

能够准确预测免疫疗法的结果可以防止新患者接受可能无效的昂贵疗法或错过那些有效的疗法。我们投入了大量精力来理解和准确估计我们模型的不确定性以及未知数据的不确定性,以最大限度地降低此类误诊的风险。

不明原发癌(CUP)

癌症研究中的一个重大挑战是癌细胞来源的不确定性。每当收集肿瘤活检时,所收集的细胞都有可能是转移性的。换句话说,细胞有可能从其他地方迁移到收集点。在未知原发性癌症(CUP)的情况下,起源部位是未知的。

几项研究已经解决了这个问题,表明 ML 模型非常善于对看不见的样本的组织类型进行分类。然而,还有一些新的方面尚未探索,如控制组织类型差异表达后的泛癌遗传信号,或特定特征贡献——这是我们旨在做出贡献的地方。

我们正在产生的结果非常令人鼓舞,我们将很快揭示进一步的见解和结果。

报告

我们的首席执行官 Nicholas 最近被邀请到新加坡参加 Google Cloud On Air 活动,讨论我们的癌症和基因组学研究。下面是他的完整演讲。

如果您想了解更多关于我们的工作,或者探索合作机会,请在下面留言或发电子邮件至hello@maxkelsen.com联系我们。

在 Julia 中美化您的 GR 可视化

原文:https://towardsdatascience.com/spruce-up-your-gr-visualizations-in-julia-88a964450a7?source=collection_archive---------19-----------------------

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Simple Scatter Plot with GR

没错,又是 Julia,我保证 R,Scala,Python 还在这个爱情圈里

剧情。当谈到朱莉娅的形象化时,JL 是一个如此多样化的组合。虽然大多数后端使用 Python,但是使用 Plots.jl 可以在一个包中使用 Plotly、Matplotlib(PyPlot)甚至更多的工具。关于后端的完整列表,包括基于 Python 的后端,以及 Julia、C 甚至 Plots.jl 的 LaTex 后端,可以在这里找到。

所有这些后端对于它们所应用的用例都是有用的。GR 后端通常关注快速、轻便和简单的可视化,只需设置最少的参数来改变您的绘图。尽管如此,它仍然是一个非常有用的工具。事实上,你可能会经常看到我在和 Julia 一起工作时使用 GR 进行可视化。一如既往,笔记本在这里。

确保您在 GR 后端

虽然感觉相对初级,但这将返回您选择的后端:

using Plots
# Using function we can return what backend we are in: 
println("Current Plots.jl Backend:")
Plots.backend()

如果它不是 GR,您可以通过执行以下操作将其设置为 GR:

Plots.gr()

基础知识

像往常一样,有一些(几乎)通用的参数将适用于每种类型的绘图,如背景颜色、网格线、xticks、yticks、标签等。这是一个相当基本的散点图,包含一些可用的参数:

scatter(x,y,
    # title:
    title = "GR Scatter Plot",
    # X label:
    xlabel = "Random data",
    # Y label
    ylabel = "Random data",
    # Grid:
    grid = false,
    # Legend position
    legend=:bottomright,
    # Color of the marker
    color = :lightblue,
    # Marker stroke width and opacity
    markerstrokewidth = 4,
    markerstrokealpha = .75,
    # Marker stroke color
    markerstrokecolor = :lightblue,
    # Adjust out y ticks
    yticks = [20,40,50],
    # Our font options
    fontfamily = :Courier,xtickfontsize=7,ytickfontsize=9,
    ytickfont=:Courier,xtickfont = :Courier,titlefontsize=13,
    # We can also add annotations, super easily:
    )

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

标题

当然,标题很简单,只需设置标题等于一个字符串,相对简单。我们也可以使用

titlefont = :FontName
titlefontsize = fontsize

我们也可以全局设置字体,当然我更喜欢将它们保存在一个实例中,但它可能对某些人有用:

gr(tickfont=font("serif"), titlefont=font("serif"))

网格和图例

要删除默认启用的网格,我们只需添加

grid = false

我们的参数。图例的位置有点复杂,因为我们只能将它放入预设的位置。例如,这里我用:bottom-right。我认为没有必要详细说明,因为其他位置是相对于语法。

此外,更改图例中的文本相对简单,

label="Sample"

x-标签和刻度,Y-标签和刻度

为了将 label =设置为一个字符串,很简单:

xlabel = "MyLabel"
ylabel = "MyLabel"

当然,刻度列表必须在 X 和 Y 的范围内,并作为一个数组被带入参数中。

yticks = [20,40,50]

标记形状、颜色、大小和 Alpha

**注意:**在混合了线条和其他元素的图上,颜色只会改变坐标点的颜色,而不会改变线条的颜色。标记笔划将决定我们轮廓的大小,以便应用与此相同的属性:

# Marker stroke width and opacity
    markerstrokewidth = 4,
    markerstrokealpha = .75,
    # Marker stroke color
    markerstrokecolor = :lightblue,

我们做同样的事情,但只是删除“标记笔划”

color = :lightblue,
width = 4
alpha = .75

不同参数输入下的线图

x = [1,2,3,4,5,6,7,8]
y = [3,4,5,6,7,8,9,10]
gr(bg=:whitesmoke)
plot(x,y,arrow = true,
    linewidth = 6,
    color = :pink,
    yticks=([0,250000,500000,750000,100000]),
    grid = false,legend=:bottomright,label="Sample",
    title = "Price per square footage in NYC",
    xlabel = "Square Footage", ylabel = "Sale Price",
    fontfamily = :Courier,xtickfontsize=4,ytickfontsize=4,
    ytickfont=:Courier,xtickfont = :Courier,titlefontsize=10
    )

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

新零件:

Arrow 是一个布尔值,它只是在你的线的末端放一个小三角形。

  • 行距

确定线条宽度的整数。

  • 背景颜色

我们只是使用 gr()函数并将以下内容放入参数中:

gr(bg=:whitesmoke)

现在我们将开始分散,并一起绘制一条线。

gr(leg=false, bg=:lightblue)
scatter(x,y,smooth = true,
title = "NYC Housing Pricing",arrow = true,
markershape = :hexagon,
    markersize = 3,
    markeralpha = 0.6,
    markercolor = :blue,
    markerstrokewidth = 1,
    markerstrokealpha = 0.0,
    markerstrokecolor = :black,
    linealpha = 0.5,
    linewidth = 5,
    linecolor = :orange,
    textcolor = :white,
xlabel = "Area",ylabel = "Pricing",
fontfamily = :Courier,grid = false)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Talk about ugly

新零件

  • 文本颜色

更改 x 坐标和 y 坐标的颜色。

  • 线性α
  • 行距
  • 线条颜色

正如我所解释的,当与其他图一起使用时,线条有单独的参数。这细分为“线条、宽度和颜色”这将改变我们的线的属性,而不是我们的标记,所以我们可以独立地改变我们的点和我们的线。

  • 马克舍普

当然,这改变了我们绘制的点的形状,这里我们使用六边形。

基本布局

布局也经常是任何图形库不可或缺的一部分。

gr(leg=false, bg=:black)
l = [@layout](http://twitter.com/layout) [  a{0.3w} [grid(3,3)
                         b{0.2h} ]]
plot(
    rand(200,11),
    layout = l, legend = false, seriestype = [:bar :scatter :path], textcolor = :white,
markercolor = :green, linecolor = :orange, markerstrokewidth = 0,yticks = [.5,1],
    xticks = [0,100,200]
)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

第一步是创建我们的布局,它实际上是一个被调用的构造函数。我们通过以下方式实现这一点:

layout = @layout [[parameters]]

现在,让我们来分解图例部分的每一部分的含义,我们将从以下内容开始:

l = [@layout](http://twitter.com/layout) **[ a{0.3w}** [grid(1,1)
                        b{0.5h}]]

前面的 a 部分并不重要,只是构造函数的局部访问器;你可以在这里放任何你想要的东西。{0.3w}所做的只是确定可视化的宽度。

l = [@layout](http://twitter.com/layout) [ a{0.3w} **[grid(1,1)
                        b{0.5h}]]**

至于这最后一部分,它创建了一个网格,在上面的演示中是 3 x 3,如果列表太小,可视化将重复到这些网格中。

新零件

  • 系列类型

这将设置我们的有序的模型类型数组,以应用于我们的布局。

  • 布局

这将布局设置为我们的可视化。

  • 神话;传奇

Legend 是一个布尔值,用于删除粘贴在我们的可视化顶部的图例。

基础动画

没错,GR 也支持动画。动画就像创建一个 for 循环并添加:

@gif

到它的开始。

p = plot([sin, cos], zeros(0), leg=false,linecolor=:white)
anim = Animation()
[@gif](http://twitter.com/gif) for x = range(0, stop=10π, length=100)
    push!(p, x, Float64[sin(x), cos(x)])
    frame(anim)
end

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

GR 是一个很好的工具

增加你的工作流程。GR 后端使许多图形非常平滑,非常容易。我计划更多地谈论 GR,以及与 Julia’s Plots.jl 相关的一些其他后端,但现在,GR 很快,非常适合,尤其是它的速度和易用性。

Python 中的 SQLAlchemy 简介

原文:https://towardsdatascience.com/sql-and-etl-an-introduction-to-sqlalchemy-in-python-fc66e8be1cd4?source=collection_archive---------11-----------------------

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

SQL 比 Pandas 更简单,可读性更好,这就是为什么许多人使用它,除了它也是一个遗留系统。虽然它很快,但它是与数据库对话和从数据仓库提取数据的语言。这是大规模数据科学的东西!

在本文中,我将浏览一下我的笔记本,它大致模拟了这种扩展数据科学的核心工作流:ETL。ETL 代表提取、转换和加载。虽然这个例子是我本地计算机上的一个笔记本,但是如果数据库文件来自一个源系统,提取就需要将它移动到一个数据仓库中。从那里,它将被转换使用 SQL 查询。最后一步是将数据加载到 Python 和 Pandas 之类的东西中,以进行机器学习和其他很酷的事情。

我用的是 SQLAlchemy,引擎盖下用的是 SQLite。SQLAlchemy 有自己的一套运行 SQL 查询的类和方法,但是我写出了原始的 SQL,以供更有兴趣或更熟悉它的读者使用。我这样做也是为了显示我的查询的逻辑,因为所有这些都会被 SQLAlchemy 抽象掉。

安装

我在这里下载了 chinook.db 数据库文件开始使用。这是一个 zip 文件夹,所以我用下面的 shell 脚本将它解压缩并复制到我笔记本的工作目录中:

!cd ..\..\..\Downloads && powershell.exe -NoP -NonI -Command 'Expand-Archive'.\chinook.zip''.\chinook\ && cd C:\Users\riley\Downloads\chinook\ && copy chinook.db C:\Users\riley\Documents\Coding\ETL

上面的命令对您来说会有所不同,因为您有不同的文件夹,但这是可以修改的。这也是一个 powershell 脚本,所以它只适用于 Windows。但是这是一种快速而巧妙的解压文件并将内容拷贝移动到目标目录的方法。“!”向 Jupyter 表明该行是一个 shell 脚本。相当整洁!

“摘录”

将数据库文件放在工作目录中后,我继续编写导入语句,并使用 Sqlite 通过 SQLAlchemy 库创建连接到数据库的引擎。

Import statements and connection to the database via sqlite

在我做任何事情之前,我查阅了下面的表模式。这让我了解了哪些变量是可用的,以及表之间的关系。在 SQL 中,关系和模式是预定义的,必须在创建表和随后用数据填充表之前声明。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Chinook.db table schemas

接下来,我需要定义元数据,它实例化组成表的对象。我还看了一下“雇员”表中的列,因为这是我很好奇的开始部分。

Metadata instantiation and column inspection

上面的代码返回“雇员”表的列。

言归正传,我调用了对‘employees’表的第一个查询,并查看了结果。

Connects to the engine, which is connected to the database, then returns the query written in raw SQL on line 4.

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

The output of the code above for the employees in that table

SELECT * FROM table_name基本上是 SQL 的 hello world。你可以把它想成df。不过,我不建议在没有LIMIT n(相当于df.head(n))语句的情况下这样做,其中 n 是要返回的行数。这将保护你和你的电脑,以防你的桌子太大。

为了查看数据库中的其他表,我调用了inspector.get_table_names(),它返回一个列表。这是一种便捷的方式来查看有什么可探索的。

基于这些数据,我对一些事情产生了兴趣:

  1. 有多少员工?
  2. 每个销售代表帮助了多少客户?
  3. 销售代表在公司的资历和他们帮助了多少客户之间有联系吗?
  4. 我如何利用不同表格中的信息来回答另一个问题:购买了多少分钟的音乐,按国家汇总?

改变

第一个问题很容易回答。我没有限制SELECT * FROM employees的输出,所以它把它们都给我看了。显然有 8 个条目。但是知道这一点很容易,因为桌子很大。如果是 300 万员工呢?不管表的大小如何,计算雇员数量的代码如下。

Returns the number of employees in the employees table

为了回答我的第二个问题,我查看了“customers”表中的“SalesRepId”变量,对其进行了计数,并将其分组,以查看每个“SalesRepId”有多少个实例。

Returns the number of customers employees 3, 4, and 5 helped respectively

员工 3 帮助了 21 个客户,4 帮助了 20 个客户,5 帮助了 18 个客户。为了回答我的第三个问题,我接下来检查了员工的雇佣日期。

Returns the hire date and employee ID for employees 3–5 ordered in ascending order (note: the BETWEEN clause in SQL is inclusive of the lower and upper bound of the range given)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

The output of the code above shows the difference in hiring date for employees 3–5

为了找到最资深的雇员,我编写了下面的查询。在这么小的桌子上,这是不必要的,但我还是把它写了出来,因为这就是如何缩放!

Returns the hire date and employee ID of the most senior employee (the “smallest” hire date — which translates to furthest date back)

似乎存在某种线性关系,至少在这个非常有限的数据集上。随着任期的延长,得到帮助的客户数量也在增加。显然,这些信息不足以推断技能——某人在某处工作的时间越长,他们完成的[在此插入任务]就越多。

把这一切联系起来

我想回答一个更复杂/有趣的问题,并为此编写同样更复杂的 SQL 查询。我还想使用不同表中的数据,因为有太多的数据可供选择。为此,我需要连接这些表。

在关系数据库中,表之间有关系。“客户”表有一个主键“客户 Id”。这是每个客户的唯一标识符。这一点很重要,因为虽然可能有多个 Joe,但不会有多个“CustomerId”编号 2355。

有了这个原则,我开始着手解决一个挑战,即调查每个国家销售了多少分钟的音乐。

“曲目”表中的歌曲长度以毫秒为单位。“客户”表告诉我们国家。

我将“tracks”表连接到“invoice_items”表,该表包含一个公共键:“TrackId”。“TrackId”是“tracks”表的主键,但却是“invoice_items”表的外键。因为这些表共享该列,所以它们可以根据该列合并在一起。

如果你喜欢,这里有一篇很好的中型文章详细介绍了 joins。

考虑到这一点,我使用所需的外键将“tracks”与“invoice_items”、“invoice_items”与“invoice”以及“invoice”与“customers”连接起来。

Returns a table of various attributes of various tables, the last column of which being the total length of music sold in minutes to each country

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Minutes of music sold to customers in the US, Canada, and so on

3277 分钟的音乐卖给了 494 个美国人!酷!🎵🎶

负荷

转换方面完成后,我继续加载它作为最后一步。一旦完成,你就可以深入到更复杂的分析中去,熊猫和 Python 宇宙就是以这些分析而闻名的!

代码是相同的查询,作为 Pandas 的pd.read_sql_query()方法的第一个参数输入。

Creates a Pandas dataframe from the SQL query (note: the engine.connect() argument must be included)

最后这里是调用df.head()的输出:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Back to the world of Pandas dataframes

结论

我简要概述了 ETL(提取、转换和加载)及其在大数据世界中的作用。这是对我的代码的一次演练,并附带了对关键 SQL 概念的解释。我在 SQLAlchemy for Python 中工作,它有一系列抽象的类和方法,所以如果我使用它们,SQL 查询看起来不会完全一样。您可以在文档页面上看到该语法。

这个项目的完整代码可以在我的 GitHub 上找到。

我希望你喜欢这本书!

如果你学到了新的东西,并想把它交给下一个学习者,考虑捐赠你觉得合适的任何数量,谢谢!

编码快乐!

奢侈的生活

SQL 和熊猫

原文:https://towardsdatascience.com/sql-and-pandas-268f634a4f5d?source=collection_archive---------3-----------------------

这些工具应该在哪里使用以及如何使用?

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

正如我在上一篇文章中提到的,我的技术经验几乎都在 SQL 方面。虽然 SQL 很棒,可以做一些非常酷的事情,但它有其局限性——这些局限性在很大程度上是我决定在 Lambda School 获得数据科学超能力的原因。在我以前的数据角色中,我需要分析来自外部来源的数据文件,我可以使用的工具要么限制了它可以处理的数据量,要么花费了过多的时间,使任务变得如此平凡,几乎不可能彻底完成。

在我担任的所有职位中,都有一条数据管道,大致是这样的:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们从外部来源接收数据,需要对这些数据进行质量分析和对 ETL 需求的理解。我们使用 excel 来做这件事,但是任何尝试过使用 excel 处理大文件的人都知道这是一场噩梦。所以我们会编写 excel 宏,但是由于每个文件都不同,它们并不总是很有用。我们本可以花钱购买为数据分析而构建的工具,但这些工具需要花钱,而且当那些买单的人没有直接感受到或理解数据生活的痛苦时,这些工具很难销售。

进入:Python 的熊猫库。我的脑子被炸了

我怎么会不知道这个非常有用的免费工具呢?这会让我的生活变得轻松很多!你会问,怎么会?好吧,让我告诉你。

熊猫

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

与 SQL 不同,Pandas 有内置函数,当您甚至不知道数据是什么样子时,这些函数会有所帮助。当数据已经是文件格式(.csv,。txt,。tsv 等)。Pandas 还允许您在不影响数据库资源的情况下处理数据集。

我将解释并展示一些我非常喜欢的功能示例:

pandas.read_csv()

首先,您需要将数据放入数据帧。一旦将它设置为一个变量名(下面的’ df '),就可以使用其他函数来分析和操作数据。将数据加载到 dataframe 时,我使用了“index_col”参数。该参数将第一列(index = 0)设置为数据帧的行标签。你可以在这里找到其他有用的参数。有时,在格式正确之前,您需要对参数进行调整。如果该函数被设置为变量,它不会返回输出,但是一旦设置好,您可以使用下一个函数来查看数据。

# Command to import the pandas library to your notebook
import pandas as pd# Read data from Titanic dataset.
df = pd.read_csv('...titanic.csv', index_col=0) 
# Location of file, can be url or local folder structure

熊猫. head()

head 函数在预览加载后的数据帧时非常有用。默认显示前 5 行,但是您可以通过键入.head(10)来调整。

df.head()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

First 5 rows of dataframe

这是一个很好的起点。我们可以看到字符串、整型、浮点型的组合,并且一些列有 NaN 值。

pandas.info()

info 函数将给出 dataframe 列的细目以及每个列有多少个非空条目。它还告诉您每一列的数据类型,以及数据帧中总共有多少条目。

df.info()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

熊猫.描述()

describe 函数对于查看数据的分布非常有用,尤其是像 int 和 floats 这样的数字字段。正如您在下面看到的,它返回一个数据帧,其中包含每一列的平均值、最小值、最大值、标准偏差等。

df.describe()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

为了查看所有列,而不仅仅是数字列,您必须使用如下所示的 include 参数。请注意,添加了“unique”、“top”和“freq”。这些仅对非数字数据类型显示,对数字数据类型显示 NaN。上面的其他细目不适用于这些新栏目。

df.describe(include='all')

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

pandas.isna()

isna 函数本身并不特别有用,因为如果字段已填充,它将返回整个数据帧,如果是 NaN 或 NULL 值,则返回 False 或 True。如果你包括。sum()和 isna(),然后您将得到如下所示的输出,其中每一列都有 NaN 或 NULL 字段。

df.isna().sum()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

pandas.plot()

熊猫绘图功能是非常有用的,只是得到一个快速可视化的数据。这个函数使用 matplotlib 进行可视化,所以如果你熟悉这个库,这将很容易理解。您可以在此找到可用于此功能的所有不同参数。

df.plot(x='age', y='fare', kind='scatter')

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这些只是我用于初始数据分析的一些有用的 Pandas 函数。还有许多其他的数据分析和操作功能,你可以在这里找到。

何时使用 SQL 与熊猫

使用哪种工具取决于你的数据在哪里,你想用它做什么,你自己的优势是什么。如果您的数据已经是一种文件格式,就没有必要使用 SQL 做任何事情。如果您的数据来自数据库,那么您应该通过以下问题来了解您应该在多大程度上使用 SQL。

要回答的问题:

你对数据库有多少权限?如果您只能编写一个查询,而其他人为您运行它,您将无法真正查看您的数据。此时,您应该将您认为可能需要的所有数据提取出来,并导出到 csv 中,以便使用 pandas。

*另一个考虑事项:*如果您需要为您的数据运行的查询将占用大量数据库资源,并且您知道您的数据库管理员不会允许或喜欢这样做,那么只需提取数据并使用 pandas 在数据库之外完成工作。避免在查询中使用 **SELECT *** ,尤其是当您不确定一个表中有多少数据时。

您希望如何转换/合并您的数据?
如果您已经知道您想要对数据做的一些事情,如过滤掉某些值、连接到另一个表、在计算或连接中合并某些字段等,那么运行 SQL 来提取您想要的数据,然后导出到 csv 中以用于任何数据分析或数据科学工作将会更加容易。

你的优势是什么?
最大的问题是你的优势在哪里。如果您觉得使用这两种语言中的一种更舒服,那么就坚持使用这种语言来进行数据操作。

总结一下:

这两个工具都非常有用。我建议两个都学。这种结合将使您能够有效地进行广泛的数据分析和操作。很快,您将不再需要处理 Excel 崩溃的问题。

*注意:如果你正在处理非常大的数据集,你可以使用 Dask ,它是专门为大数据构建的。将来我可能会写一篇关于 Dask 基础知识的文章。

SQL 中连接表的 8 种不同方式及其时间复杂度

原文:https://towardsdatascience.com/sql-complexity-and-8-different-ways-to-join-tables-22ed7ae0060c?source=collection_archive---------4-----------------------

最近在 Salesforce 营销云平台上工作时,我和一位同事遇到了一个由外部供应商编写的 SQL 查询,其形式我们相当不熟悉。事情是这样的:

Inefficient SQL query

这个查询在 Salesforce Marketing Cloud 上表现很差,运行时间很长,超时频繁,让我们很沮丧。与我们熟悉的 Google Bigquery 不同,它是一个完全托管的 Pb 级数据平台,Salesforce Marketing Cloud 的计算能力低得多,无法处理复杂的查询负载。

注意,在这个低效的查询中,它的两个子查询引用了 where 表中的主表。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Sub-queries referencing main query in the where function

这激发了团队内部的求知欲;我们可以用多少种方式编写一个 SQL 查询,每个查询的时间复杂度是多少?.

我们决定基于 Fabian Pascal 1988 年的一篇文章重放一个实验,以比较在 SQL 查询中编写连接的不同方式的性能。这将有助于在排除故障、编辑某人的查询以及理解 SQL 的时间复杂性时建立我们的直觉,并允许我们编写更好的 SQL。

展示了从 Google Bigquery 获得的增加了时间复杂度的 SQL 中连接表的 8 种方法,希望你喜欢这篇文章!

TLDR:最有效的连接也是最简单的连接,“关系代数”。如果你想找到更多关于所有连接方法的信息,请继续阅读。

方法 1:关系代数

关系代数是编写查询最常见的方式,也是最自然的方式。代码很干净,很容易排除故障,不出所料,这也是连接两个表的最有效的方法。

Relational Algebra

方法 2:不相关的子查询

不相关子查询方法通过首先创建 account_number 的子查询列表,然后使用 IN 函数来筛选子查询中的帐号,从而执行筛选函数。虽然效率不如关系代数,但由于易于编写,它是更常用的连接方法之一。

Uncorrelated Subquery

方法 3:相关子查询

在相关子查询中,EXISTS 函数用于在未筛选的子查询’ SELECT * '中进行搜索。子查询中的筛选操作需要一个“where MP . account _ number = t . account _ number”。这是inefficient query中使用的 join 函数之一。这种连接的性能令人苦恼。

Correlated Subquery

方法 WHERE 子句中的标量子查询

通过使用子查询作为 WHERE 函数的筛选器,该查询能够筛选 f_online = 1。这是一种很酷的思维方式,但不幸的是,它的表现并不好。

Scalar Subquery in the WHERE clause

方法 SELECT 子句中的标量子查询

编写查询的另一种非常有趣的方式是,该方法在 SELECT 函数中使用子查询从另一个表中提取 account_number,但是由于这两个表有多对多的关系,我们必须添加一个过滤器来删除空值。

Scalar Subquery in the SELECT clause

方法 6:聚合函数检查存在性

与标量子查询类似,此方法在 WHERE 函数中使用子查询。不同之处在于,该方法使用了一个子查询 COUNT(*),其过滤器大于 1。​

Aggregate function to check existence

方法 7:相关子查询(双重否定)

类似于相关子查询,但使用双重否定。这也是inefficient query中使用的连接之一。但有趣的是,它的表现并没有我预期的那么差。这可能仅仅是由于数据结构中异常多于包含。

Correlated subquery (double negative)

方法 8:不相关子查询(双重否定)

类似于不相关子查询,但使用双重否定。

Uncorrelated subquery (double negative)

总之,我们编写 SQL 查询的方式对查询的效率有很大的影响。效率最高的查询运行时间为 13.8 秒,而效率最低的查询运行时间为 52.6 秒。我们在 Salesforce Marketing Cloud 上重新编写了外部供应商的低效查询,我们能够将运行时间从 45 分钟(外加频繁超时)减少到 4 分钟。之后,我们去喝了一杯冰啤酒,庆祝我们的胜利。

参考 *1988 年 Fabian Pascal 的文章《SQL 冗余和 DBMS 性能》发表在《数据库编程》&Design—http://www . db deedged . com/2013/02/language-Redundancy-and-DBMS . html

感谢 Daniel 提供这篇文章背后的灵感,感谢 Shawn 提供关于 SQL sequence 的额外材料。我很幸运有像你们这样酷的同事。

感谢乔治分享费边·帕斯卡 1988 年的实验;)

干杯,

孟勇

SQL 连接陷阱。数据工程师/数据科学家必备知识

原文:https://towardsdatascience.com/sql-connection-traps-must-know-for-data-engineer-data-scientist-8ad95faa07ae?source=collection_archive---------17-----------------------

如果你的职称中有“数据”,那么你必须清楚这篇文章描述的是什么。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Photo by michael podger on Unsplash

本文中描述的陷阱意识可以防止您在查询数据库时得到错误的结果。

在本文结束时,您将:

  • 知道 SQL 中有哪些连接陷阱吗
  • 能够通过重构一个弱构建的 SQL 模型来解决问题
  • 请注意连接陷阱的存在,这样可以避免出现“误报”查询结果

我们将讨论在一些数据库中出现的两种连接陷阱,尤其是当数据库被构建为服务于具有一些明确定义的功能的特定应用程序时。尽管这样的数据库模型将很好地满足您现在的需求,但有一天,同一个数据库可能会以不同的方式被使用(查询)。也许,这一天你会意识到由于不正确的建模丢失了部分连接信息。

综合考虑,我尽最大努力让这篇文章 100%实用。

商业案例

你被要求为一个汽车市场链建立一个关系模型。

这家连锁店有几个商店,每个商店都出售许多不同的汽车。为了本文的问题,我们留下 3 个主要实体:汽车商店雇员汽车

假设链所有者给出的描述如下:

每个车店有 N 个员工,N 辆车。每辆车将由一个特定的员工销售,因为所有的车都分配给员工(这样员工的表现很容易跟踪)。

此外,有一类员工只负责豪华车,由于每个商店都没有足够的豪华车,这些员工不得不监管少数商店的汽车。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

一个关系表示将是:

  • 员工和汽车之间的 1:N 关系
  • 商店和雇员之间的 N:N 关系,因为一个商店可能有多个雇员,而一些雇员将在几个商店工作

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Entity relation diagram

裂缝陷阱

鸿沟陷阱——从一个实体到另一个实体的路径似乎存在,但在某些情况下可能会被打破。

鸿沟陷阱问题

你得数一数每个商店有多少辆车。为此,您必须编写一个查询,计算分配给每个商店每个员工的汽车数量。一切似乎都是正确的,但是,结果与您应该得到的结果不同。怎么发生的?

深沟陷阱

有些新车刚到,比如还没有分配。根据我们的模型,这些汽车与任何商店都没有关联,但事实并非如此。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

‘Car 7’ belongs to ‘Shop 1’ but we cannot deduct this information until car is unassigned to an employee

在上面的例子中,我们看到“商店 1”有 8 辆汽车和 5 名员工。由于“汽车 7”仍未分配给任何员工,我们在库存中没有看到它。基本上,它存在于我们的汽车表中,在外人不知情的情况下(例如,商店经理),它不能被分配给商店。因此,数据库本身不再可靠,因为它不能将汽车分配给商店。

如何避免陷入鸿沟陷阱

首先,你应该在收集需求时考虑到这种情况。

二、一对多是什么意思?1 比 0 还是一对多吗?有些人会说是的,他们是对的。例如, 1:0 是 1:N 的子集。

没有指定的下限是个问题。

我的建议是总是在这两个选项中选择一个:

  • 一对(零或多)
  • 一对(一个或多个)

在这种情况下,没有任何含糊不清的余地。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Try to avoid using top 2 types of connection explainers

当您检查您的实体关系图时,它似乎有从一个实体到另一个实体通过一些其他实体的路径,请确保没有 **xyz 到(零个或多个)或 xyz 到(零个或一个)**路径。

扇形陷阱

粉丝陷阱——从一个实体到另一个实体的路径是模糊的。

范陷阱问题

扇形陷阱比深沟陷阱更棘手因为模糊的东西有时会被遗漏或误解,而深沟陷阱总是会发出警报。

按照我们的例子,当豪华车销售员工成为等式的一部分时,粉丝陷阱就会出现。

迷因陷阱

这种类型的雇员(一个特定的雇员)可能与汽车商店有 1:N 的关系,与汽车有 1:N 的关系。因此,当您试图将汽车商店与汽车联系起来时,您无法正确识别哪辆汽车属于哪家商店。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Shops and cars are blades of a fan with employee being a center of a fan

Employee 实体就像一卷杂乱的单色字符串,你永远无法将它们分开。

现在,即使所有的连接都是XYZ对(一个或多个)你也无法正确识别特定汽车店里的汽车,因为路径是模糊的。

风扇陷阱的解决方案

店铺→汽车→员工

这样的建模会解决粉丝陷阱,但是,没有车的员工还是会迷失在鸿沟陷阱里。尤其是对于豪华车销售商来说,这将是一个案例。

两种陷阱的解决方案

(商店→员工→汽车)+(商店→汽车)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Add one additional connection between shop and a car

当我们添加一个额外的连接来跳过 employee 实体时,我们就消除了 fan 和 chasm 这两个陷阱。

这看起来像是一个冗余的连接,但它不是冗余的——这是构建模型的正确方式。

最坏情况的例子

想象你仍然同时有两个陷阱。您执行一个简单的检查,通过一个查询来计算一个商店中库存的汽车数量,您得到一个正确的结果。然而,一辆车尚未分配(-1),一名管理豪华车的员工从其他商店购买了豪华车(+1)。这两种效应正好相互抵消。您验证您的检查,并将模型投入生产。对企业来说,后果可能是灾难性的。

除非你知道连接陷阱,否则你永远不会发出警报并问自己正确的问题。

总结和结论

当两个或多个实体之间的路径不明确或虚假存在时,就会出现连接陷阱。

关系数据库看起来很简单,然而,正确编写的查询并不总是能得到正确的结果。

尽量总是指定连接的下限。

请注意在数据库建模或查询第三方数据库时可能出现的 SQL 连接陷阱,否则,您可能会犯一个小错误,从而在将来导致大问题。

希望有一天这些信息能帮到你。

面向初级数据科学家的 SQL

原文:https://towardsdatascience.com/sql-for-beginner-data-scientist-6bfab20ff368?source=collection_archive---------17-----------------------

为什么 SQL 是最值得学习的技能之一?

为什么学习 SQL 很重要?

在我的一生中,我掌握了许多技能,但没有一项技术技能比 SQL 更有用。SQL 对我来说是最有价值的技能,原因如下:

  • SQL 对不同的角色和学科都有好处。
  • 学习 SQL 其实并不难。
  • 这是招聘者最容易搜索到的技能之一。

什么是 SQL?
SQL(结构化查询语言)是负责管理关系数据库管理系统(RDBMS)中数据的主要语言。
SQL 用于查询、插入、更新和修改数据。大多数关系数据库都支持 SQL,这对数据库管理员(DBA)来说是一个额外的好处,因为他们经常需要支持跨多个不同平台的数据库。

SQL 于 20 世纪 70 年代初由 Raymond Boyce 和 Donald Chamberlin 在 IBM 首次开发,并于 1979 年由 Relational Software Inc .(现在称为 Oracle Corporation)发布。当前的标准 SQL 版本是自愿的,与供应商兼容,由美国国家标准协会(ANSI)监控。大多数主要供应商也有基于 ANSI SQL 的专有版本,例如 SQL*Plus (Oracle)和 Transact-SQL (T-SQL) (Microsoft)。

简单地说,SQL 是我们用来与数据库交互的语言。

SQL 是桌面开发人员、web 开发人员、系统管理员、开发人员和数据科学家最流行的语言之一:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我整理了一个名为 SQL for 初学者数据科学家的系列文章,共有七篇文章。通过阅读,你可以用一种非常务实和愉快的方式学习 SQL。

安装 PostgreSQL 和 pgAdmin

PostgreSQL 是什么?

PostgreSQL 或 Postgres 是一个关系数据库管理系统,它提供了 SQL 查询语言的实现。对于许多小型和大型项目来说,这是一个受欢迎的选择,它的优点是符合标准,并具有许多高级功能,如可靠的事务和无读锁的并发性。

这里有一个在数字海洋机器上安装 PostgreSQL 的逐步指南,在您的终端上编写一组命令行。这是应在 Ubuntu 18.04 LTS 上进行的安装,但在此之前,让我们检查一下您是否以前安装过 PostgreSQL,如果您没有任何安装,并且您的机器是干净的,您可以跳到下一步。

第一步:检查旧安装

在这一步你必须做三件事来完成你任务的第一步

第一项任务:列出所有与 Postgres 相关的包

$ dpkg -l | grep postgrespostgresql                            
postgresql-8.3                        
postgresql-client                     
postgresql-client-8.3                 
postgresql-client-common           
postgresql-common                  
postgresql-contrib                                
postgresql-contrib-8.3

第二项任务:移除上面列出的所有包

$ sudo apt-get --purge remove postgresql postgresql-10 postgresql-9.5 postgresql-client postgresql-client-10 postgresql-client-9.5 postgresql-client-common postgresql-common postgresql-contrib postgresql-contrib-9.5

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

第三项任务:删除以下文件夹

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

如果您检查最后一个图像,您会在此过程中发现一些警告消息,因此下一个任务是从我们的系统中删除这些文件

$ sudo rm -rf /var/lib/postgresql/
$ sudo rm -rf /var/log/postgresql/
$ sudo rm -rf /etc/postgresql/

让我们进入学习 SQL 旅程的下一步

第二步:安装 PostgreSQL

Ubuntu 的默认库包含 Postgres 包,因此您可以使用apt打包系统轻松安装这些包。如果这是您第一次在apt 这个会话中使用,您需要刷新我们的本地包索引。然后你可以安装 Postgres 包和一个-contrib包,这个包增加了一些额外的工具和功能:

$ sudo apt-get update

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

引入更新存储库的命令后,第一个屏幕要求您输入密码。

$ sudo apt-get install postgresql postgresql-contrib

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

第二个屏幕询问您是否想继续,请键入 yes!
干得好!您完成了安装 PostgreSQL 的第一个任务。

安装过程创建了一个名为postgres的用户帐户,它与默认的 Postgres 角色相关联。为了使用 Postgres,您可以登录该帐户。好的一面是,您可以使用以下命令登录这个超级用户的帐户:

$ sudo -i -u postgres

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

看看这个,第一件事就是问你密码。你注意到上面屏幕的变化了吗?如果没有,再看截图,拜托。
当前用户zaid变为新用户postgres.``postgres用户将能够使用以下命令访问 SQL:

$ psql

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

您将登录并能够立即与数据库管理系统进行交互。

从 PostgreSQL 控制台注销后,数据库中只配置了超级用户。因此,您需要使用 create user 命令从命令行创建新角色。让我们生成一个新用户,这样您就可以在将来使用服务器的相同用户来访问您的数据库(这是推荐的做法)。因此,您需要使用 create user 命令从命令行创建新角色。让我们生成一个新用户,这样您就可以在将来使用服务器的相同用户来访问您的数据库(这是推荐的做法)。

在我们的案例中:

create user zaid with encrypted password 'password_to_login_in_my_ server';

创建新用户后,首先从 PostgreSQL 控制台键入\q exit,然后键入exit退出超级用户并返回到您的主页。

postgres=# \q
postgres@wordpress:~$ exit

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

一旦有了合适的用户,就可以使用这个命令登录到 Postgres 数据库

$ psql -U zaid -d postgres

如果你看到下图,这意味着你做得很好。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

使用这个命令,您可以列出 Postgres 数据库的所有用户

\du

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

太棒了。让我们转到 SQL,通过键入这些 SQL 表达式来感受一下它的乐趣:

CREATE TABLE test_table(name TEXT, phone VARCHAR(11), address TEXT);
INSERT INTO test_table VALUES ('Zaid', '07911123456','Oxford Street');
INSERT INTO test_table VALUES ('David', '07922123456','Manchester Street');

第一行生成一个名为“test_table”的新表,第二行和第三行在表中插入一些值。

SELECT * FROM test_table;

上面一行将 test_table 中的所有值打印到屏幕上

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在这个 SQL 小游戏之后,您再次退出 PostgreSQL 控制台:

postgres=# \q

第三步:安装 pgAdmin

是时候安装和设置 pgAdmin 了。pgAdmin 是 PostgreSQL 及其相关数据库管理系统的开源管理和开发平台。它用 Python 和 jQuery 编写,支持 PostgreSQL 中的所有特性。您可以使用 pgAdmin 完成从连接到的所有工作。在这一步中,您必须完成三项任务才能从您的个人笔记本电脑完成 missionPostgres 数据库的第一步:编写基本的 SQL 查询、编写复杂的查询、监控您的数据库以及配置高级数据库架构。

在本教程中,我们将介绍安装和配置最新版本的 pgAdmin、通过 web 浏览器访问 pgAdmin 并将其连接到服务器上的 PostgreSQL 数据库的过程。

在这一步,你要做三件事来完成你的第三步任务。

第一项任务:配置连接。

作为第一项任务,通过键入这些代码,让您的远程服务器准备好连接。第一行是允许来自任何(或特定)IP 地址的传入连接。

$ sudo su - root
# echo "listen_addresses = '*'" >> /etc/postgresql/10/main/postgresql.conf

注意:上面的一行只有一行,小心点。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

第二行用于使适用的用户能够从任何(或特定)IP 地址访问所有数据库。

# echo 'host all all 0.0.0.0/0 md5' >> etc/postgresql/10/main/pg_hba.conf

注意:这是一行代码!只有你的浏览器把它分成 2 行!

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

添加这两行代码后,重启并退出 root bash。

# /etc/init.d/postgresql restart
# exit

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

第二项任务:下载并安装程序。

https://www.postgresql.org/media/keys/ACCC4CF8.asc导入存储库密钥:

$ sudo apt-get install curl ca-certificates curl [https://www.postgresql.org/media/keys/ACCC4CF8.asc](https://www.postgresql.org/media/keys/ACCC4CF8.asc) | sudo apt-key add -

(您可以通过运行 lsb_release -c 来确定您的发行版的代码名称)。对于上述内容的简化版本,假设您使用的是受支持的版本:

$ sudo sh -c 'echo "deb [http://apt.postgresql.org/pub/repos/apt/](http://apt.postgresql.org/pub/repos/apt/) $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list'

最后,更新软件包列表,并开始安装软件包:

$ sudo apt-get update sudo apt-get install pgadmin4

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

第三个任务:连接服务器。

完成后,您将看到以下屏幕:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

单击添加新服务器图标,并填写弹出窗口:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

常规:

“Name:” anything you want (eg. “our test server“)

“连接:”

“Host name/address:” your remote server’s IP Address (in my case: 46.101.128.118)
“Port:” 5432
“Maintenance database:” postgres
“User name:” [your_user_name]
“Password:” your recently generated SQL password for your user

点击并保存祝贺!!您已连接到您的数据库!

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

乍一看,这并不简单,但是您可以在左侧发现不同的文件夹。如果您右键单击服务器的名称(在我的截图上:“SafaCreations 测试服务器”),您可以从您的服务器断开连接。或者以同样的方式连接,当你想回去的时候。
请注意,在我们的屏幕截图上,您可以看到我们在 PostgreSQL 控制台中得到的完全相同的结果!

结论

你完成了任务,我亲爱的朋友,干得好!
您已经创建并安装了 SQL,这是您成为数据科学家的奇妙的第一步!
正如我在这篇文章中多次提到的,我已经创建了相当多的教程来向您展示如何使用 SQL。所有这些都是从最基本的开始。我们将在接下来的几周开始放更多的文章。因此,请与我们保持联系,找到学习 SQL 的简单方法。

查看我们在 udemy 上的免费课程

感谢阅读。如果你喜欢这篇文章,请点击下面的按钮,这样我们就可以保持联系。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值