仪表板教程(二):HTML,CSS,PHP 和 Heroku
照片由freepik.com拍摄
网页的布局通常是通过 CSS 选择器来显示内容的元素。通过 CSS,您可以确定文本颜色、内容显示以及网页背景和图像。当在服务器上部署 web 页面时,Heroku 通常是常见的部署平台。在 Heroku 平台,可以免费托管网站,管理多个 app 文件夹,轻松与终端协同工作。在这篇博客中,我们将浏览 web 布局、文件结构和到 Heroku 的 web 部署的 CSS 选择器。
在本文中,您将学习在 Heroku 服务器上创建一个基本的仪表板和部署:
(CSS 布局介绍
(HTML、Javascript 和 CSS 文件的文件夹结构
⑶部署到 Heroku
CSS 布局介绍
CSS 是一种装饰网页的样式表语言。CSS 使网页能够显示定制的演示文稿,包括块布局、边距、填充、字体等。在本教程中,我们将深入图表的布局。用导航条绘制 JS。
CSS 网格布局
CSS Grid 是一个带有水平线和垂直线的二维元素——一组定义列,另一组定义行。
首先,我们创建一个网格容器
通过声明display: grid
或display: inline-grid.
该元素的所有直接子元素成为网格项目来创建网格容器。在下面的例子中,有 4 个单独的图表是用 canvas 标签和 h3 作为图的描述创建的。这 4 个图被包装在类包装器的< div >元素中,这些子元素成为网格项。
<div class="wrapper"><div><h3>Bar Chart of COVID active cases count in June</h3><canvas id="myChart" width="500" height="300"></canvas></div><div><h3>Line Chart of COVID cumulative cases in Canada</h3><canvas id="myChart1" width="500" height="300"></canvas></div><div><h3>Doughnut Chart of cumulative COVID cases count in Canada</h3><canvas id="myChart2" width="500" height="300"></canvas></div><div><h3>Bar Line Chart of COVID cases in Canada</h3><canvas id="myChart3" width="500" height="300"></canvas></div></div>
下面的例子展示了如何为网格容器创建一个包装器的选择器。接下来,我们定义网格轨迹,它是网格中显示的 2 条线的空间。在网格容器中,网格模板列和网格模板行用于定义行和列。属性的grid-template-columns
可以定义列轨道的大小。Grid-template-columns 设置为 800 像素宽的列轨道。另外,我在行和列上都创建了一个 50 像素的间隙。
.wrapper {display: grid;grid-template-columns: 800px 800px;column-gap: 50px;row-gap: 50px;}
链接 CSS 文件和外部资源
下面的例子展示了在 CSS 文件中嵌入的方法。外部和内部 CSS 源都应该包含在元素中,元素需要放在<head>
和</head>
标签之间。因此,HTML 网页将从< link >元素链接到样式表。
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous"><link rel="stylesheet" href="../static/styles/style.css">
HTML、Javascript 和 CSS 文件的文件夹结构
对于仪表板,有几个不同的文件,如数据集 CSV 文件、绘图 javascript 文件、样式表 CSS 文件和网页 HTML 文件。最好将不同的文件存放在项目主目录下的每个文件夹中。通常,子目录中有一个静态文件夹来存储各种文件。例如,我的所有数据集都存储在文件夹中,js 文件存储在 js 文件夹中,CSS 文件存储在 styles 文件夹中,HTML 文件存储在 template 文件夹中。
Javascript 和 HTML 文件的交互
下面显示了在 HTML 文件中创建的 ChartJS 图,其画布 id 为“myChart”。
<div><h3>Bar Chart of COVID active cases count in June </h3><canvas id="myChart" width="500" height="300"></canvas></div>
在 Javascript 文件中,我们将通过使用**document . getelementbyid()**函数调用调用“myChart”id 来呈现绘图。
var ctx = document.getElementById("myChart");
在 html 文件中,我们可以通过使用
<script src="../static/js/bar.js"></script><script src="../static/js/line_chart.js"></script><script src="../static/js/donut.js"></script><script src="../static/js/line_bar.js"></script>
CSS 文件选择器
现在,我们深入研究 CSS 选择的字体和文本。我们通过使用属性font-family.
来定义文本的字体。文本对齐被指定为以text-align: center.
为中心。也可以选择为网页指定字体大小为font-size: 10px;
。就边距而言,它显示了文本周围的空间。使用margin: 0 0 0.1em
,文本显示为上、下、左、右边距,后跟顺序。因为<body>
是内容页面的父元素,所以它里面的所有元素都继承了相同的text-align
和font-family
。
body {font-family: Helvetica Neue, Arial, sans-serif;
text-align: center;
font-size: 10px;
margin: 0 0 0.1em 0;
color: rgb(89, 112, 240);}
接下来,我们研究 CSS 选择器中的块概念。在 HTML 文件中,我们使用标签<a>
来定义一个超链接,用于从一个页面链接到另一个页面。在 CSS 文件中,block 元素可以应用边距和其他间距值。然而,<标签是一个没有边距值的内嵌元素。为了对超链接标签应用边距,我们需要添加显示参数display: block;
。然后,我们可以通过margin-top: 50px;
给出顶部的保证金值
a {margin-top: 50px;display: block;color: #3e95cd;}
Heroku 部署
要在 web 服务器而不是本地主机上部署仪表板,Heroku 通常是一个流行的部署选项。从 Heroku 开始,支持 Ruby,Node.js,Clojure,Python,Java,Gradle,Grails,Scala,Play,PHP,Go。然而,编程语言并不是为典型的 HTML、CSS、Javascript 语言提供的。好的解决方案是为网页构建一个 PHP 应用。我将向您介绍 Heroku 设置、git 设置、PHP 文件创建和项目部署到 Heroku 平台的步骤。和 tada,项目显示有一个 HTML 链接,外部访问可以共享该链接。
(1)注册一个帐户并安装 Heroku。( link1 , link2 )
(2)在终端中,输入 Heroku 登录所需的凭证。
(3)初始化一个本地 Git 存储库并提交您的应用程序代码。
在终端中,键入下面列出的命令行
cd your_project
git config --global user.email “you@example.com”
git config --global user.name "Your Name"
(4)准备 PHP 文件来呈现 HTML 页面。前往包含 index.html 的项目根目录
(5)在这个目录下,运行touch composer.json
创建一个文件:composer.json,在 json 文件中,添加{}。
(6)在同一个目录下,运行touch index.php
创建一个文件:index.php。在 PHP 文件中,添加<?php include_once("index.html"); ?>
(7)在终端,我们初始化 git 库,将项目文件夹推送到 Heroku 服务器进行部署。在终端中,键入下面列出的命令行
cd your_project
# Create a local git repository
git init
# Add all your local files to repository
git add .
# Commit your files
git commit –m “First commit”
# Create an empty Heroku app, make sure the app name is unique with your own creation
heroku create php-dashboard-tutorial
# push our application to Heroku
git push heroku master
# check one instance of the app is running
heroku ps:scale web=1
神奇的事情发生了。现在,您的仪表板已经成功部署到 Heroku 服务器上,并且可以通过外部 web 链接进行访问。
总之:
- CSS 选择器可以用来设置用类定义的 HTML 标签元素的样式。网格容器可以显示网页项目的列和行的布局。元素用于在 HTML web 文件中嵌入内部和外部 CSS 样式表。
- 最好将不同的文件存储在静态目录下的特定文件夹中。在 HTML 文件中,我们可以通过使用
- Heroku 作为一个 web 服务器平台,支持 Ruby、Node.js、Clojure、Python、Java、Gradle、Grails、Scala、Play、PHP、Go。作为一种选择,带有 HTML、CSS、Javascript 的仪表板可以通过一个额外的 PHP 文件为 Heroku 部署工作。此外,Heroku 需要一个 git 存储库来将项目文件夹推送到 web 服务器。
我的简单 COVID 仪表盘链接: 【https://dd-dashboard-3.herokuapp.com/】
希望您能从教程中获得一个仪表板!
参考:
- CSS 基础知识:https://developer . Mozilla . org/en-US/docs/Learn/Getting _ started _ with _ web/CSS _ basics
3.将您的 Python web 应用程序部署到 Heroku cloud:https://Python how . com/deploying-your-web-application-to-the-cloud/
4.如何在 Heroku 上运行简单的 HTML/CSS/Javascript 应用:https://medium . com/@ Winnie Liang/how-to-Run-a-Simple-HTML-CSS-Javascript-Application-on-Heroku-4 e 664 c 541 b0b
5.Heroku 用 Git 部署:https://devcenter.heroku.com/articles/git
仪表板死了
在过去的几十年中,仪表板一直是分发数据的首选武器,但它们并不是故事的结尾。为了让数据访问越来越民主化,我们需要重新思考,答案可能比你想象的更接近…!
你好仪表板,我的老朋友
当我开始职业生涯时,我在一家大型科技制造公司工作。该公司刚刚购买了第一个仪表板工具,我们的团队负责从疲惫的电子表格和 SSRS 报告到闪亮的新仪表板的激动人心的过渡。
对我们来说,从电子表格到仪表板的转变是分析成熟度的重大飞跃。仪表板的周到设计和交互性大大降低了数据的“准入成本”。突然,你会在办公室里走来走去,看到来自任何角色和背景的员工摆弄仪表板。这是数据爱好者的天堂,对吧?
不完全是。我们很快发现仪表板带来了一系列新问题:
- 你有仪表板,你有仪表板,你有仪表板!突然,仪表盘随处可见。工程师想要一些数据进行特别分析?这是一个仪表板。副总裁下周有一个演示,想要一些图表?她得到了一个仪表板。不,他们再也没有看他们一眼。一刀切的方法耗尽了我们团队的时间、资源和动力。这是一种独特的令人沮丧的感觉,看着你的仪表板又一次被抛弃,比你 2008 年的 Myspace 账户还快。
- 死于 1000 个过滤器:在一个仪表板上线后,我们立即被新视图、过滤器、字段、页面等各种请求所淹没(提醒我告诉你我曾见过一个 67 页的仪表板……#难以忘怀)。很明显,仪表板没有回答每个人的问题,这要么是仪表板设计步骤的失败,要么是其他工具在提供人们需要的答案方面的失败。更糟糕的是,我们发现人们使用所有这些过滤器将数据导出到 Excel 中,然后用它们做自己的事情🤦♀️.
- 不是我的仪表盘。仪表板炒作来得越快,它就开始消退。人们开始贬低仪表盘是“错误的”,并公然忽视它们。许多人认为这是对他们工作的威胁,如果他们看到他们没有预料到的数字,就把它归结为“坏数据”。我们有一个严重的信任问题,仪表板几乎没有提供机会来减轻他们的担忧。毕竟,我们不能把我们的 SQL 查询发送给他们;他们将无法阅读它们,更不用说理解它所反映的极其复杂的模式了。在每个团队创建他们自己的度量定义的情况下,我们不能给他们发送原始数据。我可能轻描淡写了…我们有一个巨大的、溃烂的、渗出的信任问题。
真实例子:吓人的红点里有什么?
为了进一步证明这一点,让我们考虑一个在当前冠状病毒危机中广泛流行的数据仪表板:约翰霍普金斯冠状病毒仪表板。
2020 年 4 月 7 日拍摄的 JHU 仪表盘截图
JHU 仪表板在视觉上很吸引人;红色和黑色唤起了这一刻应得的严肃感和紧迫感。当我们的目光扫过页面时,我们会遇到数字、各种大小的点,以及几乎总是越来越向右上方移动的图表。我们感觉情况很糟糕,而且似乎越来越糟。这个仪表板旨在以一种可访问且引人入胜的方式获取数据。它甚至可能被设计用来回答一些关键问题,如“今天我国有多少新病例?我的县?”需要明确的是,这是所以比他们仅仅发布一个表格或下载链接要好得多。
但是除了那些肤浅的发现,我们不能用这些数据采取行动。如果我们想将这些数据用于某个特定的目的,我们将缺乏必要的上下文来使这些数据变得有用——并相信它们是我们自己的(例如,社交距离测量在我的国家/县是什么时候开始的?在我的国家,检测的可用性如何?)即使我们设法获得了信任这些数字的必要背景,仪表板本身也缺乏进行我们需要的定制分析的能力和灵活性。
就像我在某个不知名的公司的经历一样,这个仪表板成功地让人们用数据做一些事情,但不一定是用数据做一些有意义的事情。在一家不知名的公司,我们试图通过添加越来越多的仪表板,然后添加越来越多的过滤器来解决这个问题,然后当这些仪表板毫无用处时,就将其删除。这种负反馈循环导致了对数据的严重不信任和团队间的分裂,如果消极进取的 LinkedIn 更新是可信的,我认为其中许多仍然存在。
仪表板为数据赋权做了大量工作(还有我的职业生涯!)但它们肯定不是数据协作和报告的最佳接口。谢天谢地,有一个竞争者你可能已经在用了…
数据进入肖像模式
从仪表板到笔记本的演变
像 Jupyter 这样的数据笔记本在过去几年里在数据科学领域变得非常流行。在进行数据分析和数据科学时,面向过程的本质已经被证明优于传统的脚本。这不仅有利于从事这项工作的分析师,也有助于不得不使用它的老板/同事/不情愿的朋友。
从根本上说,笔记本电脑提供了机会:
- 为了让大家信任这个过程(因为他们可以从字面上看到代码和作者的评论),
- 拥有回答任何问题的能力和灵活性(只要用户知道工具所用的语言),以及
- 一种让与合作、展示以及与更广泛的受众分享这些决定的方式。
我当然不是第一个想将笔记本电脑的强大功能和灵活性应用到数据分析/商业智能领域的人。我们已经和许多使用笔记本电脑而不是仪表板的公司谈过了。有些人只使用 Jupyter 笔记本进行报告,其他人会将图表剪切并粘贴到文本编辑器中以达到类似的效果。这些都是不完美的解决方案,但标志着公司愿意超越精心制作的仪表板,实现笔记本电脑的优势。
我们只是需要一种方法来将这些原则扩展到数据科学之外,并使笔记本变得像仪表板一样易于访问。
面向大众的笔记本
在 Count ,我们对笔记本电脑的基本优势深信不疑,因此我们围绕它们建立了一个数据分析平台。这里没有仪表板!
为了在数据科学之外使用它们,我们不得不制作自己的版本,但基本原则仍然适用,但有一些额外的好处…
专为各种经验水平打造
- 无需向团队中的每个人教授 Python 或 SQL,因为可以通过拖放、编写“笔记本 SQL”(见下文)或编写完整的 SQL 来构建查询。
Count 的笔记本 SQL 示例
- 只需一次点击即可获得快速视觉效果,因此无需复杂的可视化软件包或软件。
- 表和查询输出的自动连接,因此无需编写复杂的连接或尝试解释模式
已启用协同
- 与队友、整个团队、整个公司或任何有链接的人共享笔记本
- 添加注释和标注,使其真正成为共享文档
计数笔记本夹
通过以笔记本电脑为核心,Count 提供了团队所需的能力、透明度和协作,不仅可以为人们提供数字,还可以让他们获得所需的见解并与公司其他人分享。
由于我们一直在构建 Count,我们一直在与许多组织合作,以了解笔记本如何改变数据在团队中的使用方式。以下是我们的发现:
- 分析师使用笔记本而不是疯狂的 SQL 脚本来创建一些其他团队使用的基础表。任何人都可以查看(和解读)这些笔记本,所以没有人会因为不知道这些数字来自哪里而对其不屑一顾。
- 数据团队创建了一些基本报告。这些报告充满了评论,以帮助指导读者如何解释这些数字和任何需要考虑的问题。
- 然后,用户可以使用这些笔记本或创建自己的笔记本来回答他们的特别问题。他们与数据团队分享这些笔记本电脑,这样他们就可以帮助指导他们,然后他们将这些笔记本电脑展示出来,并与企业的其他部门分享。
由于每个人都可以消费所有东西,而且在一个地方,信任问题开始改善(或者,在现实中,只是变成了其他事情)。他们没有为不使用仪表板的人创建仪表板,也没有为满足各种需求而创建数以千计的过滤器,因为人们有更多的权力来创建他们真正需要的报告。他们描述的场景证明,从仪表板到笔记本的小小转变会对您的团队利用数据的方式产生巨大影响。
如果您想了解更多关于笔记本如何帮助您的团队更加数据驱动的信息,请在 hello@count.co 给我们写信,或者您可以在这里阅读更多!
这是我们“呼唤更好的分析工具”系列的文章之一。该系列的其他文章包括:
报名参加我们的 快讯 获取最新帖子。
资源
[1] C 新冠肺炎冠状病毒全球病例由约翰·霍普金斯大学(JHU)系统科学与工程中心(CSSE)(2020)约翰·霍普金斯大学
使用 Dash 和 SQL 快速浏览圣诞歌曲
使用 SQL 数据库创建 Dash 仪表板的简单项目
塞巴斯蒂安·斯宾德勒在 Unsplash 上拍摄的照片
女士们先生们,宾·克罗斯比先生!TL;dr 在 GitHub 上找到网络应用的代码。
宾·克罗斯比和安德鲁斯姐妹演唱《铃儿响叮当》
本文概述了我使用破折号在 SQL 数据库上创建 web 应用程序的奋斗和成功。本文分为 4 个部分:创建 SQL 数据库、安装 Dash 环境、构建 web 应用程序、运行应用程序。我不熟悉 Dash 环境,这种灵活性给了我灵感。还有其他 web 应用程序创建引擎,如 Viola 也值得探索。Dash 令人耳目一新,因为它包含了可以显示不同对象的标签的独特功能。
用 python 将 CSV 文件转换成 SQL 数据库
我用维基百科上的美国圣诞歌曲表创建了一个 CSV 文件。经过一些手动格式化后,我有了一个包含 4 列的 CSV 文件[歌曲名、艺术家名、发行年份、其他信息]。在下面的脚本中将 CSV 转换为 SQL 数据库之前,Pandas 是唯一需要安装的库。模块 sqlite3 已经是我的 python 发行版的一部分。
import pandas as pd
import sqlite3conn = sqlite3.connect('Songs.db')
c = conn.cursor()# Create table with headers
c.execute('''CREATE TABLE Songs
([generated_id] INTEGER PRIMARY KEY,[Song_Name] text,
[Artist_Name] text, [Year] date, [Other] text)''')conn.commit()# Convert CSV to SQL
read_songs = pd.read_csv('Chrismas_Songs.csv')
read_songs.to_sql('Songs', conn, if_exists='append', index = False)
简而言之,该代码使用 execute 函数创建了一个 SQL 表,其中包含 SQL 命令。当我们将 CSV 文件导出到先前为空的 SQL 表中时,使用 Pandas 填充该表。现在我们的 SQL 数据库已经准备好了,接下来是安装 Dash 的时候了。
创建 Dash 环境
仪表板安装提示可在仪表板主页上找到。这个项目需要安装的包包括
## Install Plotly
pip install plotly## Install Pandas
pip install pandas## Install Dash and Components
pip install dash
pip install dash-bootstrap-components
pip install dash-html-components
pip install dash-table
Web 应用的构建结构
index.py
app.py
database/
|_ __init__.py
|_ Chrismas_Songs.csv
|_ Songs.db
|_ transforms.py
tabs/
|_ __init__.py
|_ sidepanel.py
|_ tab1.py
|_ tab2.py
我从数据库/和选项卡/文件夹中导入数据函数,所以我必须包含一个名为 init 的空白文件。py 让 python 知道这是一个模块。web 应用程序将从 index.py 运行,该文件调用包括 app.py 在内的所有其他 python 文件。
构建 Dash Web 应用程序
构建应用程序从 app.py 文件开始。下面,我们调用应用程序并将其连接到服务器(在索引文件中)
app.py
**import** dash
**import** dash_bootstrap_components **as** dbc
app = dash.Dash(__name__, external_stylesheets =[dbc.themes.BOOTSTRAP])
server = app.server
app.config.suppress_callback_exceptions = True
索引文件是奇迹发生的地方。这是您的 web 应用程序的四分卫。Dash 使用回调从客户端(web)获取数据,并在服务器端(后端)操作。下面是一个来自索引文件的例子,完整的索引文件见 GitHub 。
index.py
[不是完整的文件,从 Github 下载或复制完整的文件]
app.layout = sidepanel.layout
@app.callback(Output(**'tabs-content'**, **'children'**),
[Input(**'tabs'**, **'value'**)])**def** render_content(tab):
**if** tab == **'tab-1'**:
**return** tab1.layout
**elif** tab == **'tab-2'**:
**return** tab2.layout@app.callback(Output(**'table-sorting-filtering'**, **'data'**)
, [Input(**'table-sorting-filtering'**, **"page_current"**)
, Input(**'table-sorting-filtering'**, **"page_size"**)
, Input(**'table-sorting-filtering'**, **'sort_by'**)
, Input(**'table-sorting-filtering'**, **'filter_query'**)
, Input(**'Last_Decade'**, **'value'**)
, Input(**'year-slider'**, **'value'**)
, Input(**'Artist-drop'**, **'value'**)]) **# function to filter data table
def** update_table(page_current, page_size, sort_by, filter, yearcheck, prices, artists):
# insert function content here**if** __name__ == **'__main__'**:
app.run_server(debug = True)
让我们从头开始分析一下。app.layout 使用用户输入创建侧栏,更多内容将在 sidepanel.py 分解中解释。@app.callback 既呈现选项卡(数据表和直方图),也调用用户选择的过滤。@app.callback 中的输入对应于下面函数中的参数。例如,在 first @app.callback 中,tabs 与 render_content 函数中的 tab 参数相同,Last_Decade 与 update_table 函数中的 yearcheck 相同。最后一部分是在 if name == 'main '函数中对应用服务器的调用。
Database/transforms.py
这个文件将 SQL 数据库连接到我们的索引文件和 web 应用程序。
**import** dash
**import** dash_bootstrap_components **as** dbc
**import** dash_core_components **as** dcc
**import** dash_html_components **as** html
**import** pandas **as** pd
**import** sqlite3
**import** dash
**from** dash.dependencies **import** Input, Output
**import** dash_table
**import** pandas **as** pd
conn = sqlite3.connect(**r"/Users/stronglab2/Documents/Datadolittle/Dashing_Through/database/Songs.db"**)c = conn.cursor()
df = pd.read_sql(**"select * from Songs"**, conn)
Tabs/tab1.py
[不是完整的文件,从 Github 下载或复制完整的文件]
Tab1.py 在第一个选项卡中创建数据表。该文件还包括单元格的格式选项。
## Load SQL Database into table
df = transforms.df
PAGE_SIZE = 50layout = html.Div(dash_table.DataTable(id='table-sorting-filtering',columns=[{**'name'**: i, **'id'**: i, **'deletable'**: True} **for** i **in** df[[**'Song_Name'**,**'Year', 'Artist_Name'**,**'Other'**]]], ..................
SQL 数据被加载到 pandas 数据框中,并显示为 dash 数据表。然后,我们为数据表创建列。定义列后的点表示整个文件中有更多的细节。
Tabs/tab2.py
[不是完整的文件,从 Github 下载或复制完整的文件]
Tab2.py 在第二个选项卡中创建直方图。该文件还包含@app.callbacks,用于根据用户选择进行更新。
df = transforms.df
layout = html.Div(
id=**'table-paging-with-graph-container'**,
className=**"five columns"** )
@app.callback(Output(**'table-paging-with-graph-container'**, **"children"**),
[Input(**'Last_Decade'**, **'value'**)
, Input(**'year-slider'**, **'value'**)
, Input(**'Artist-drop'**, **'value'**)
])
**def** update_graph(yearcheck, prices, artists):
....................................................................
graph_plotly = go.Histogram(x=dff[**'Year'**],
name=**"Histotest"** , opacity=0.7) **return** html.Div([dcc.Graph(id=**'histo-graphic',figure={'data':[graph_plotly], ..........**
直方图是通过 go 生成的。直方图功能,并通过 html 显示在标签页中。Dic([dcc。图表…
Tabs/sidepanel.py
[不是完整的文件,从 Github 下载或复制完整的文件]
侧面板允许用户与 web 应用程序交互。有三种相互作用。“最近十年”复选框(2009 年之后发行的歌曲)、“艺术家”下拉列表(可以选择多个艺术家)或发行日期范围(1910-2020)。表格和直方图根据选择进行更新。
layout = html.Div([
html.H2(**'Filters'**)
, dcc.Checklist(id=**'Last_Decade'** , options=[
{**'label'**: **' Released this decade '**, **'value'**: **'Y'**}
])
,html.Div([html.P() ,html.H5(**'Artist'**), dcc.Dropdown(id = **'Artist-drop'** ,options=[
{**'label'**: i, **'value'**: i} **for** i **in** df[**'Artist_Name'**].unique()],
value=[],
multi=True
)])
,html.Div([html.H5(**'Year Slider'**)
, dcc.RangeSlider(id=**'year-slider'...............**
这个文件创建了所有三个交互选项。每个选项由 html 分隔。Div 然后用交互类型调用 dash_core_components (dcc)。在 index.py 文件和 tab2.py 中调用 id 来更新表和直方图。
运行 Web 应用程序
对于那些希望看到工作示例的人,安装所需的包并从 GitHub 下载资源库。下面的屏幕截图显示了 web 应用程序。
数据表选项卡,过滤宾·克罗斯比和玛丽亚·凯莉的歌曲
宾·克罗斯比和玛丽亚·凯莉的歌曲直方图
要运行应用程序,只需在应用程序目录中键入python index.py
。您可能会看到下面的提示,显示该应用程序正在您的本地计算机上运行。
Running on http://127.0.0.1:8050/Debugger PIN: 829-054-282* Serving Flask app "app" (lazy loading)* Environment: productionWARNING: Do not use the development server in a production environment.Use a production WSGI server instead.* Debug mode: onRunning on http://127.0.0.1:8050/Debugger PIN: 182-493-929
只需将http://127.0.0.1:8050/
复制并粘贴到网络浏览器中,应用程序就启动了!
这是一个使用 Dash 和 SQL 构建的简单 web 应用程序。我的名字是科迪·格利克曼,可以在 LinkedIn 上找到我。请务必查看我在数据科学或 web 开发领域的一些其他文章。
查找模板和为静态网页创建表单的位置
towardsdatascience.com](/building-a-beautiful-static-webpage-using-github-f0f92c6e1f02) [## 探索大学足球薪酬
按州和会议列出的每个学生的价格
medium.com](https://medium.com/swlh/exploring-college-football-salaries-dc472448684d) [## 机器学习中的什么或为什么
使用 Python 解释模型的综合指南
towardsdatascience.com](/what-or-why-in-machine-learning-e2a73da528c8)
DASK:使用并行化处理大型数据集的指南
理解大数据、数据科学、机器学习
Numpy、Pandas 和 Scikit-Learn 框架中大数据并行计算的简单数据分析解决方案
托马斯·詹森在 Unsplash 上拍摄的照片
介绍
如果您正在处理大量数据,并且您担心 Pandas 的数据框架无法加载它或者 NumPy 数组卡在中间,您甚至需要一个更好的并行化解决方案来处理您的数据和训练机器学习模型,那么 dask 为这个问题提供了一个解决方案。在深入研究之前,让我们看看 dask 到底是什么?
在潜入深海之前,你听说过懒惰装载吗?了解 Vaex 如何主导加载大型数据集的市场。
[## 现在,使用惰性计算在一秒钟内加载巨大的数据集😴用 Python?
厌倦了用熊猫加载数据集…了解 Vaex 如何帮助在几秒钟内加载大量数据…
towardsdatascience.com](/now-load-huge-datasets-within-a-second-using-lazy-computation-in-python-2698bdb02250)
dask 是什么?
Dask 是一个极其高效的开源项目,它使用现有的 Python Apis 和知识结构,使得在 Numpy、Pandas、Scikit-learn 之间进行修改成为它们的 Dask 支持的对等物变得简单。此外,Dask 的调度程序可以扩展到千节点集群,其算法在全球内最重要的台号超级计算机上进行测试。
来源:使用 Dask 并行化扩展到集群
装置
quality 是预装在您的 Anaconda 中的,但是对于 pip,您可以使用以下命令获得完整的版本:
Dask 的 Conda 安装:
**!conda install dask**
Dask 的 pip 安装:
**!pip install “dask[complete]”**
达斯克是做什么的?
Dask 有助于并行化阵列、数据帧和机器学习,以处理大量数据,例如:
数组:并行化的 Numpy
**# Arrays implement the Numpy APIimport dask.array as da
x = da.random.random(size=(10000, 10000), chunks=(1000, 1000))
x + x.T - x.mean(axis=0)**
数据帧:平行熊猫
**# Dataframes implement the Pandas APIimport dask.dataframe as dd
df = dd.read_csv('financial_dataset.csv')
df.groupby(df.amount).balance.sum()**
机器 学习:并行化 Scikit-Learn
**# Dask-ML implements the Scikit-Learn APIfrom dask_ml.linear_model import LogisticRegression
lr = LogisticRegression()
lr.fit(train, test)**
Dask 中的数据帧
大多数 Dask API 与 Pandas API 非常相似,因此您可以使用非常相似的命令直接使用 Dusk 中 Pandas 的数据帧。要生成一个离散的数据帧,您可以像以前调用 Pandas 一样简单地调用**read_csv()**
方法,或者轻松地将 Pandas 数据帧转换成 Dask 数据帧。
**import dask.dataframe as ddf
dd = ddf.from_pandas(df, npartitions=N)**
基准数据框架:熊猫 vs 达斯克
对于下面的基准测试,所用的机器具有标准的 4 核处理器,该处理器在测试两种框架时保持标准。
我做了一个非常简单而有趣的基准测试,来展示 Dask DataFrame 与传统的 Pandas DataFrame 相比,从一个包含 500 万条记录的. csv 文件中读取数据集的速度有多快。
为读取 CSV 数据帧对 Pandas 和 Dask 进行基准测试
结果 :读取一个大小超过 600MB 的 5M 数据文件,Pandas DataFrame 大约需要 6.2 秒,而 Dask DataFrame 由于其令人印象深刻的并行化能力,执行同样的任务所需的时间远少于秒。
注 :这个测试是在一个小数据集上进行的,但是随着数据量的增加,读取数据的时间差会成倍增加。
您可以使用下面的代码来改变更大数据集的基准测试。
基准阵列:Numpy 与 Dask
在这个基准测试中,我使用 Numpy 数组和 Dask 数组生成了一个1 万亿大小的随机数数组。
创建阵列的基准测试 Pandas vs Dask
结果 :正如所料,结果非常明显,因为 Numpy 数组的计算时间不到 8 秒,而 Dask 数组的计算时间可以忽略不计!
您可以使用下面的代码尝试相同的基准测试
关键要点
Dask 有助于更快地进行数据分析,因为它并行化了现有的框架,如 Pandas、Numpy、Scikit-Learn,并使用机器 CPU 的全部潜力并行处理数据。你可以在这里尝试 Dask 的惊人功能。
延迟加载与并行处理的结合确实是一个致命的组合,可以帮助您在任何需要的时候利用系统的全部潜力,要了解更多信息,您可以阅读这篇关于 Vaex 的文章。
数据科学家的进一步阅读:
**** [## 如何评价 Python 中机器学习模型性能?
一个实用的方法来计算模型的性能和在 Python 中的实现,涵盖了所有数学…
medium.com](https://medium.com/towards-artificial-intelligence/how-to-evaluate-machine-learning-model-performance-in-python-135b4ae27f7e) [## 如何在短短 1 年内把数据科学从初学者学到大师(我的亲身经历)
随着时间的推移,我的从初学者到大师学习数据科学的清单的完整汇编只需要一年时间…
towardsdatascience.com](/how-to-learn-data-science-from-beginners-to-masters-in-just-1-year-my-personal-experience-6152bedd8157)
谢谢!****
机器学习的 Dask 第一印象
一种在服务器集群中扩展类似 numpy 的阵列、类似 pandas 的数据帧和 scikit-learn 模型的方法
出于研究大数据的不同机器学习工具的好奇心,我上周花了一些时间来熟悉 Dask 和 Dask-ML ,这是另一种在计算机集群上并行运行机器学习算法的替代工具。以下是对它的一些第一反应的简要总结。
图片来源:来自 pexels.com 的的免费库存照片
达斯克将军
Dask 是一个相当年轻的框架,根据官方记录,它的开发似乎在 2015 年左右就开始了。但它是由一个非常受信任的 NumPy、Pandas、Jupyter 和 Scikit-Learn 开发人员社区维护的,背后有强大的机构支持,并且它在机器学习从业者社区中获得了相当大的关注。
虽然我学习 Dask 的个人动机与机器学习有关,但 Dask 本身更普遍地是一个平台,它将帮助您在一个计算机节点集群上并行处理数据。对于一个有 python 经验的数据科学家或数据工程师来说,设计逻辑和结构看起来非常好,很容易掌握。如果你有任何使用 Hadoop MapReduce、Apache Spark 或 TensorFlow 的经验,那么你会立即认识到有向非循环任务图的惰性分布式执行的熟悉概念。如果您对 python 的 concurrent.futures 模块有任何经验,那么您会认识到正在使用的另一种模式。但对于 numpy/pandas/sklearn 用户来说,这一切都变得更容易理解、更容易和更自然,其数组和数据帧有效地将 numpy 的数组和 pandas 的数据帧纳入一个计算机集群。虽然在看起来非常熟悉的数组和数据帧的层次上学习可能感觉很容易,但是不仅直接进入 Dask-ML,而且熟悉所有一般层次的 Dask 概念和 API 也是值得的。
Dask-ML
Dask-ML 用两种不同的策略来处理机器学习任务的扩展:
- **Dask-ML + scikit-learn 组合。**您可以使用 Dask 来跨集群扩展并行化的 scikit-learn 估计器和其他算法(具有 n_jobs 参数的算法)。Scikit-learn 本身能够在依赖 Joblib 的多核单台机器上并行化其大量算法,通过 Joblib,Dask 提供了一个替代后端,能够跨计算机集群执行并行作业。当处理的数据集和模型适合于单个节点内存时,这是一个强大而合适的设置,但是运行训练和预测对 CPU 来说太重,实际上无法在单台计算机上运行。
- **估计器和其他算法的 Dask-ML 实现。**这适用于不仅单节点 CPU 太慢,而且单节点内存太少无法加载整个数据集的用例。Dask-ML 自己的估算器实现将数据带到 Dask 的数组和数据帧中,这些数组和数据帧看起来非常像 numpy 数组和 pandas 数据帧,但它们将数据分布在计算机集群中,因此能够处理否则无法放入单机内存中的数据。
不幸的是,Dask-ML 中实现的估计器并不多。根据当前文档,Dask-ML 提供的算法集仅限于以下内容:
- 预处理: MinMaxScaler、QuantileTransformer、StandardScaler、LabelEncoder、OneHotEncoder、RobustScaler、分类器、DummyEncoder、OrdinalEncoder、SimpleImputer、PCA、TruncatedSVD
- 模型选择: train_test_split,ShuffleSplit,KFold,GridSearchCV,RandomizedSearchCV
- **估计量:**线性回归、逻辑回归、泊松回归
- 元估计量: ParallelPostFit,增量
- 度量: MAE,MSE,r2_score,accuracy_score,log_loss
- **聚类:**k 均值,PartialMiniBatchKMeans 均值,光谱聚类
差不多就是这样——Dask-ML API 参考点的完整列表。
然而,使 Dask-ML 仍然是一个强大的工具的是 scikit-learn 的并行化估计器列表(实现了 n_jobs 参数的列表),即 Dask 能够跨节点集群并行化的 scikit-learn 估计器列表,非常丰富。仅在 sklearn 中就有略多于 15 个这样的分类器,还不算回归器和其他算法。
摘要
Dask-ML 是一个很好的工具,可以将 scikit-learn 的并行估算器从在单台计算机上运行扩展到在计算机集群上运行,Dask 通常是处理大数据的一个很好的替代工具。
Dask 与 Vaex:大型数据处理中数据点的经验
需要处理十亿行数据?我来和大家分享一下我使用 Dask 和 Vaex 的经验。
嗨,我是数据 N!
你好。很高兴见到你!😄我是 Data N(你可以叫我 N),今天,我想分享我作为 data point 与我的新经理 Dask 和 Vaex 一起工作的经验,以及一些与他们建立良好工作关系的技巧(wink)。
背景故事
背景故事是这样的…最近,我们公司有一个小的重组和我们的前经理,熊猫🐼,由两名新员工接管。官方给出的原因是熊猫转移到了新的机会,但我们所有内部人士都知道发生了什么。
嗯,事实是高层管理人员对熊猫最近的表现不满意。我们公司发展迅速,业务呈指数增长。熊猫最初做得很好,但逐渐发现自己无法应对不断增加的数据。当满载美国数据的卡车到达时,我们被证明对熊猫来说太多了。通常,我们会坐在一个名为硬盘的大型仓库中,但当我们需要处理时,有一个名为随机存取存储器(又名 RAM)的临时存储室,我们将被运送到那里进行进一步处理。问题就在这里:RAM 中没有足够的空间容纳我们所有人。
RAM 里没有足够的空间容纳我们所有的人
所以我们的猜测是,高层管理人员决定用 Dask 和 Vaex 替换 Pandas,以便进一步发展公司。这很可悲,但这只是生意。
介绍 Dask 和 Vaex…
自从两个新经理来了,他们每个人都部署了一个独特的伎俩来对付我们。
使用 Dask,我们被分成组(或者用他的话说,“分区”),并且一次处理一个到几个组。
有了 Vaex,我们每个人都提供了我们在仓库中的地址,当 Vaex 需要我们时,她会根据我们的地址调用那些需要来 RAM 的人(她将这种技术称为“内存映射”)。
不管怎样,好的一面是工作完成了,尽管我们太多了,无法一次全部放入 RAM,高层管理人员现在很高兴。
我如何适应 Dask 和 Vaex?
在一起工作了这么长时间后,我们已经很习惯熊猫的命令了,所以 Dask 和 Vaex 都试图用类似的命令来命令我们,这很有帮助。但是它们毕竟不是熊猫,所以还是有一些不同。
# Dask
Dask 喜欢一个关键词叫“计算”。在他说“计算”之前,我们不能完全处理。达斯克总是告诉我们做事要有目的。因此,他的程序通常会是一系列类似熊猫的命令,然后他会在最后一个主要命令中喊出“计算”,然后我们开始全面的实际工作。
以下面的简单代码为例:
import dask.dataframe as dddf_dask = dd.read_csv(’data.csv’)
df_dask.describe()
您得到的不是您期望得到的一般统计数据,而是这个(基本上没有显示任何数据):
发生的情况是,当执行dd.read_csv
时,Dask 只从 CSV 文件的开头读入一个美国数据点样本。当调用describe()
时,Dask 仅仅记录下要采取的步骤。
为了得到你从熊猫那里得到的通常的输出,你需要一个神奇的词“计算”:
# Vaex
对于 Vaex,她也有自己的风格。一个显著的区别是,她总是希望我们在任何处理完成之前执行列车测试拆分。
那是因为 Vaex 有一个巧妙的技巧。
假设我们对训练数据进行了一些转换:
通过下面两行代码,这些相同的转换可以很容易地应用于测试数据:
state = df_train.state_get()
df_test.state_set(state)
我问 Vaex 这个技巧背后的秘密,她告诉我实际上她的每个数据帧都包含一个状态,它基本上包含了应用于数据帧的所有转换的信息。
因此,我们在两行代码中所做的是获取包含在df_train
的状态中的转换信息,并将这个状态应用到df_test
,所有的更改都将被传递。
如果你想知道,标准化转换在df_test
上使用了df_train
的平均值和标准偏差。
我猜她真的知道如何聪明地工作!
Dask 和 Vaex 对比如何?
这是一个敏感的话题…
老实说在我看来,就性能而言,Vaex 似乎比 Dask 更有能力。我们中的一些人喜欢 Dask,所以不要告诉别人我说了什么,否则他们可能会解雇我。你也不用信我的话,看看别人(这里和这里)在说什么就知道了。
尽管是一个高水平的表演者,Vaex 还是有点害羞,所以她不适合和其他人相处,比如 Scikit-Learn,通常需要一些中介。然而,Dask 和许多其他人合作得很好,因为他经常和 Numpy,Pandas 和 Scikit-Learn 交往。
您可能还会注意到,Dask 擅长在集群环境中工作,而 Vaex 在单台机器上表现出色。
这就是我对我的新经理 Dask 和 Vaex 的所有分享!希望我的建议是有用的,你也将有一个与 Dask 和/或 Vaex 合作的好时光。😄
感谢听我的分享,如果你有任何问题或意见,请随时告诉我。
我的内部消息来源
[## 简介- Dask 教程文档
您可以在实时会话中运行该笔记本,也可以在 Github 上查看它。欢迎来到 Dask 教程。Dask 是平行的…
tutorial.dask.org](https://tutorial.dask.org/00_overview.html) [## Vaex 是什么?- vaex 3.0.0 文档
Vaex 是一个 python 库,用于懒惰的核外数据框架(类似于 Pandas),以可视化和探索大的表格…
vaex.readthedocs.io](https://vaex.readthedocs.io/en/latest/index.html)
数据获取、网络搜集和 KDD 过程:巴西新冠肺炎数据的实践研究
你对数据科学感兴趣吗?你知道巴西的新冠肺炎的情况吗?在这篇文章中,我将探索数据获取的概念,并演示如何使用简单的网络搜集技术来获取疾病进展的官方数据**。值得一提的是,通过对代码进行一些修改,这里使用的策略可以很容易地扩展到来自其他国家的数据,甚至扩展到其他应用程序。所以戴上你的面具,洗干净你的手,现在就开始读吧!**
我必须以向所有新冠肺炎受害者表达最诚挚的情感开始这篇文章。正是在他们的记忆中,我们必须寻找科学发展的动力,同时不要忘记,如果科学存在,那是因为有一种人性在支撑着它。关于数据科学,正是通过它,我们可以最大限度地减少假新闻,并以某种方式为一个更明智、更明智和更有思想的社会做出贡献。
几周前,当我意识到在官方基础上几乎没有关于巴西新冠肺炎病例数量的分层信息时,我开始了这个数据分析项目,例如世界卫生组织(世卫组织-世界卫生组织)或约翰·霍普斯金大学维持的小组的信息。作为分层,其实我们理解的是可以在不同层观察到的数据集。也就是说,对于像巴西这样的大陆规模的国家,了解该国的病例总数只能提供信息。为了使这些信息发挥作用,必须按照地区、州或城市进行分层。
为了提供问题的概述和指导分析的发展,我为这项研究列出了一些目标,希望读者也能采纳:
- 获得 KDD 过程的概述,并理解数据采集在其中的位置。
- 探索数据采集技术,以便类似的问题可以很容易地使用它们。
- 从官方数据库获取巴西新冠肺炎病例和死亡人数,按州分类。
在接下来的几节中,我们将探索其中的每一项,以构建必要的概念并在 Python 开发环境中实现它们。准备好了吗?
KDD——数据库中的知识发现
数据库中的知识发现是人们从数据库中寻找全面的、有效的和相关的模式的过程。这是一个交互和迭代的过程,其中包括许多今天被称为数据科学的技术和方法。
然而,在探索 KDD 过程的主要阶段之前,有必要区分三个关键术语:数据、信息和知识。KDD 的每一步都与处理每一项有关,如图 1 中我们正在解决的问题所示。
图一。从数据到知识:KDD 的信息处理。
简而言之,数据都是与某种现象或事件相关的符号链,无论是否结构化。数据本身没有任何意义。看:408 万这个数字只是个基数。只有当我们赋予它这是新冠肺炎确诊病例数的感觉后,它才变得可怕。换句话说,只有当我们赋予数据意义时,我们才有信息。
正是通过对信息的处理,我们获得了知识,这与我们所说的“认知过程”密切相关——感知、语境化和学习——因此也与智力密切相关。
一旦理解了这三个概念,我们现在就可以进入 KDD 进程的三个基本阶段。这些无非是分别专用于获取和探索数据、信息和知识的任务集,如下图 2 所示:
图二。KDD 过程及其阶段(I)数据获取,(ii)信息处理,和(iii)知识提取。
KDD 流程由多个步骤组成,而这些步骤又是多个任务的组合。例如,数据挖掘任务是信息处理步骤的一部分。相比之下,那些与数据可视化、分析和决策制定相关的任务是最后一步的一部分,旨在进行知识提取。
在本文中,我们将只关注数据采集任务,这是任何 KDD 项目的基础。通常,没有预定义的方法来获取数据。中长期项目通常需要数据模型,以及用于收集和存储数据的健壮架构。反过来,短期项目大多需要立即可用的资源。
牢记这些基本概念,我们准备朝着我们的目标前进:获得巴西新冠肺炎病例数量的官方数据。
定义数据采集策略
正如我们所见,数据采集策略只有从完整的 KDD 过程的角度构建才有意义。也就是说,在我们开始收集数据之前,我们需要有一个明确的目的定义。我们试图解决或阐明的问题是什么?
CRISP-DM 参考模型如图 3 所示,在经历 KDD 过程时可用作指南针。你还记得我们之前定义 KDD 为一个互动和迭代的过程吗?
交互意味着两个或更多代理之间的相互或共享的动作。它既可以指持有数据和信息的人,也可以指阶段。在图 3 的第一步之间的两个方向上的短箭头表示该过程中必要的交互。我敢说,这是 KDD 或数据科学领域的新手面临的最大挑战:最初有一种错觉,认为这些步骤是连续不断的,这导致了一种做错事的感觉。恰恰相反,通过理解业务,我们使得理解数据成为可能,数据为我们收集关于业务的新见解,等等。
反过来,迭代与相同过程的重复相关联。图 3 中较大的箭头形成一个圆圈,象征着这种重复。它从第一步开始,经过开发之后,又回到第一步。KDD 项目永远不会停滞不前。
图 3。根据 CRISP-DM 参考模型的 KDD 流程阶段。(改编自维基共享资源。)
图 3 中突出显示的方框代表与数据采集过程相关的步骤。根据 CRISP-DM,第一步——理解业务——是整个项目成功的基础,包括(I)业务目标的定义;(二)对现状的评估,包括清单和风险分析,以及哪些数据和信息已经可用;㈢确定应用数据挖掘的目标,以及成功标准的定义;(四)项目计划的制定。
第二步——理解数据——包括(I)第一个数据样本和(ii)其详细描述;㈢数据的初步探索;以及(iv)对该数据质量的评估。请记住,这两个初始步骤是交互进行的,并且在理想情况下,涉及领域和业务专家。
第三步从发起人对项目计划的确认开始,其特点是准备数据。一般来说,这一阶段所需的工作变得更加昂贵,因此,他们的变化应该是温和的。这一步的主要任务包括数据的选择和清理、格式的构建和调整以及数据的合并。所有这些任务通常被称为数据预处理。
一旦你在理解业务(或问题)和跟踪可用数据之间有了第一次互动,你的数据获取策略就会开始被定义。为此,我主要考虑两种可能的情况:
- 有对数据源的控制:你可以以某种方式管理和控制生成数据的源(传感器、仪表、人员、数据库等)。).以护士长的活动为例:他可以监督、审计、建立协议,并记录他所在病房的所有患者数据。重要的是要注意,控制是针对数据的来源或注册,而不是针对与数据相关的事件。
这种场景通常发生在高度复杂或持续时间长的项目中,在这些项目中,数据模型已经建立,并且有方法确保其一致性和完整性。 - 没有对数据源的控制:这是在短期项目或偶尔感兴趣的项目中,以及在使用或要求来自其他来源的数据的项目、分析或常年研究中最常见的情况。无论采取何种举措,数据采集策略都必须更加可靠,因为任何超出其控制范围的变化都会增加分析成本,甚至导致项目不可行。
我们自己的研究说明了这种情况:如果我们无法控制这些数据的产生和传播,如何获得巴西新冠肺炎病例的分层数量?
期望在企业和机构中,或者在领导纵向研究或制造过程时,找到第一个场景。否则,您可能需要处理第二个问题。在这种情况下,您的策略可能包括订阅、数据分配协议,甚至是在获得适当许可后从互联网上搜索和编译此类数据的技术。Web 抓取是这些技术中的一种,我们接下来会看到。
通过网络搜集获取数据:通过巴西卫生部的官方小组获取数据
随着目标的确立——巴西新冠肺炎病例的分层数量——我的第一个行动是寻找可能的来源。2020 年 4 月中旬我咨询了巴西卫生部门户网站。当时,我还没有找到统一的、容易获取的信息,所以我增加了可能来源的粒度:州和市卫生部门。在这种情况下,挑战在于他们呈现数据的方式的异质性。在咨询一名公共卫生工作者时,她告诉我,这些数据已经整合到国家公报中,具体的需求应该向机构提出。因此,我的第一个策略是设计一种自动收集此类公告(PDF 格式)并开始提取所需数据的方法——部分结果可在这个 GitHub 资源库中获得。
然而,在 2020 年 5 月初,我有一个令人不快的惊喜:在新闻公告中,我用来提取数据的表格(对于那些好奇的人来说,pdf plumb库是一个极好的工具)被数字所取代,这使得我的提取方法不可行。
我特意详细阅读了上述段落,原因很简单:再次展示 KDD 数据采集步骤固有的交互过程。此外,当我们承担一个我们无法控制数据源的项目时,我想强调风险和不确定性。在这种情况下,最好有其他选择,建立合作关系或协议,并寻求建立自己的数据集。
当我在寻找新的策略时,巴西卫生部开始将数据整合到一个 CSV 文件中,几天后将格式改为 XLSX,并每天更改文件名。在下面的段落中,我将详细说明我是如何针对这种新情况调整我的过程和代码的。
顺便说一句,从互联网上可用的页面和内容中自动检索数据和信息被称为 web 抓取。这项技术可以通过 Python 库轻松实现,如图 4 所示。
图 4。使用 Google Chrome 检查工具和 Python 语言简化网页抓取过程的步骤。
我们终于到了这篇文章的实际部分——也许是最令人期待的部分。按照图 4 中定义的步骤,我们首先(1)访问冠状病毒小组,以及(2)通过点击页面右上角的“Arquivo CSV”按钮,确定我们感兴趣的数据。当我们激活 Google Chrome 页面检查工具(3)时,我们会找到与 HTML 页面上的项目相对应的代码,如图 5 所示。记住,检查工具是由检查菜单激活的,当我们右击页面的任何元素时,或者通过快捷键 ctrl+shift+I 。
图 5。通过谷歌 Chrome 检查工具看到的巴西卫生部冠状病毒门户网站。
下一步(4)是从我们的 Python 代码中访问这些数据,这可以通过 Jupyter 笔记本、脚本或 IDE 来完成。在任何情况下,这个想法都是通过使用 Selenium 库来模拟对所需门户的访问以及与 web 页面的交互。
在下面几行中,我开始创建一个虚拟环境来访问 Google Chrome 模拟器中的门户网站 URL:
# Initial statements
from pyvirtualdisplay import Display
from selenium import webdriver # Parameters definition
url = 'https://covid.saude.gov.br' # Starts the virtual environment:
display = Display(visible=0, size=(800,600)) display.start() # Opens the Chrome emulator in the desired URL:
driver = webdriver.Chrome() # Reads and gets the page content encoded in UTF-8:
driver.get(url) page = driver.page_source.encode('utf-8')
一旦我们读取了页面,我们就进入图 4 的步骤(5 ),在这里我们重复这个过程,直到我们保证数据是我们想要的形式。为此,我们可以首先检查加载页面的大小(例如,如果它为空,则意味着出现了问题)及其前几行:
# What is the length of the loaded page?
print(len(page)) # What is the data type?
print(type(page)) # What is the content of the first positions of the byte stream? print(page[:2000])
大多数情况下,下一步将是使用被称为解析器的工具来探索 HTML 内容——beautiful soup库是一个优秀的 Python 解析器。在我们的例子中,考虑到我们感兴趣的数据不在 web 页面上,而是来自这个页面上的一个操作,我们将继续只使用 Selenium 方法来模拟按钮上的点击,自动下载缺省系统文件夹中所需的文件:
## Path obtained from inspection in Chrome:
xpathElement = '/html/body/app-root/ion-app/ion-router-outlet/app-home/ion-content/div[1]/div[2]/ion-button' ## Element corresponding to the "Arquivo CSV" button:
dataDownloader = driver.find_element_by_xpath(xpathElement)
## Download the file to the default system folder: dataDownloader.click()
下一步是验证文件下载是否正确。因为它的名字不是标准化的,所以我们通过 glob 库列出了最近的文件:
import os
import glob ## Getting the last XLSX file name:
list_of_files = glob.glob('/home/tbnsilveira/Downloads/*.xlsx')
latest_file = max(list_of_files, key=os.path.getctime)
print(latest_file)
至此,网页抓取任务完成,同时我们进入数据准备阶段(如有必要,查看图 3)。
假设我们感兴趣的是每个州的病例数、人口数、死亡率(死亡人数与病例数之比)和死亡率(死亡人数与人口数之比)。下面的代码行执行提供所需信息所需的预处理任务。
## Reading the data
covidData = pd.read_excel(latest_file)
covidData.head(3) ## Getting the last registered date:
lastDay = covidData.data.max() ## Selecting the dataframe with only the last day
## and whose data is consolidated by state
covidLastDay = covidData[(covidData.data == lastDay) &
(covidData.estado.isna() == False) & (covidData.municipio.isna() == True) & (covidData.populacaoTCU2019.isna() == False)] ## Selecting only the columns of interest:
covidLastDay = covidLastDay[['regiao', 'estado', 'data', 'populacaoTCU2019', 'casosAcumulado', 'obitosAcumulado']]
预处理步骤通过从预先存在的数据生成一些附加特征来完成:
## Copying the dataframe before handling it:
normalCovid = covidLastDay.copy() ## Contamination rate (% of cases by the population of each state) normalCovid['contamRate'] = (normalCovid['casosAcumulado'] / normalCovid['populacaoTCU2019']) * 100 ## Fatality rate (% of deaths by the number of cases) normalCovid['lethality_pct'] = (normalCovid['obitosAcumulado'] / normalCovid['casosAcumulado']) * 100 ## Mortality rate (% of deaths by the population in each state) normalCovid['deathRate'] = (normalCovid['obitosAcumulado'] / normalCovid['populacaoTCU2019']) * 100
从这一点出发,我们可以在预处理的数据库中进行搜索:
normalCovid.query("obitosAcumulado > 1000")
数据采集阶段到此结束。如果我们遵循 CRISP-DM 方法,下一步可能是构建模型、分析或可视化。为了说明这一 KDD 过程的可能结论,图 6 展示了一个四象限图,该图将巴西不同州的病例百分比与致死率相关联(生成该图的代码可在 GitHub 上获得)。
图 6。根据卫生部 2020 年 5 月 22 日发布的数据,巴西各州新冠肺炎病例数与致死率之间的关系。
上图是我们到目前为止所介绍的整个过程的结果:数据是通过网络搜集获得的,信息是通过处理数据生成的,知识是通过结构化信息获得的。亚马孙州是每个居民感染人数最多的州,也是死亡率最高的州。圣保罗州是累计死亡人数最多的州,但就其总人口而言,感染人数相对较低。
最终考虑
在本文中,我试图概述 KDD 过程以及如何使用 web 抓取技术实现数据采集,尤其是在小型项目或分析的情况下。
您可能想知道访问门户网站并简单下载那里的可用数据是否会更容易。是的,会的!然而,数据科学项目期望数据获取尽可能自动化,为预测分析提供空间,预测分析的模型需要每天或不断更新。此外,对于相对更简单的问题,理解复杂的主题并获得对我们技术的信心变得更加有效——或许还很有趣。
最后,我必须提醒你,在获取和处理数据时,一定要遵守道德规范。自动收集公共领域的数据,不需要行政费用,是一种完全合法的活动。然而,如果你对何时允许或不允许使用这种算法有疑问,请不要犹豫,向专家甚至数据所有者咨询。
原载于 2020 年 5 月 23 日http://tbnsilveira . info。
基于 R 的 IMDb 刮削数据的数据分析与可视化
极客(也不那么极客)喜欢的电视剧
电视剧数据可视化仪表盘,文末链接
当然,你和我一样,在一天或一周中的任何时候,利用这个休息时间来摆脱日常事务、工作和问题,只是放松一下,在流媒体服务上、电视上看你最喜欢的电视连续剧,或者掸掉旧 DVD 上的灰尘。
我们享受那些娱乐的时刻,无论是单独还是与人一起,我们可以沉浸在一个吸引我们、感动我们、让我们反思或娱乐我们的故事中。这些电视连续剧中有许多以离奇的告别告终,而在其他情况下,它们的结局很糟糕,惹恼了它们的粉丝。无论如何,很多事情的发生并不是每个人都能看到的。
在这种动机下,我们将对一些最受欢迎的电视剧(尤其是在极客文化中)进行数据可视化和分析:《黑镜》、《西部世界》、《权力的游戏》、《瑞克与莫蒂》、《奇人异事》。
IMDb 数据的准备和提取
IMDb(互联网电影数据库)目前有一个更新的和广泛的在线数据库,包含关于电视剧、电影等的信息。它还根据世界各地用户的意见和投票,为它存储的每个主题保存评级。
我们将使用一个有趣而简单的脚本来准备我们将要使用的数据集。这个脚本使用 rvest 包从 IMDb 网站执行 Web 抓取(一种用于以自动化方式从网站提取信息的技术),不需要深入研究这个主题,稍后再详细描述。你所要做的就是更新文件" series_urls.csv" ,在这里你可以添加连续剧、电影或者任何你想包含在 IMDb 中的东西的标题以及它的标识符(例如对于《权力的游戏》来说,它的 URL 是https://www . IMDB . com/title/tt0944947/,其中“TT 0944947”是我们所关心的)。
在这篇文章中,考虑到所选的电视剧,CSV 文件看起来像这样:
分析电视剧《IMDb》——要分析的电视剧
一旦定义了这个 CSV 文件,就可以在根文件夹中运行 R 脚本,对每个系列的所有数据进行 Web 抓取(更新到最新)。
解析电视剧《IMDb——运行网络抓取脚本》
我们将获得什么数据?那么,它将在**【data】**文件夹中生成一个新的 CSV 文件,该文件由每部电视剧的以下变量组成:
连续剧 _ 名称 < chr >电视剧名称
连续剧 _ep < int >剧集编号
季号
季 _ep < int >季号
网址 < chr > IMDb 网址为剧集
剧集 <
用户投票 < dbl >评分票数
r1 < dbl >评分为 1 分的用户比例
…
r10评分为 10 分的用户比例
本文在 Web 抓取后获得的 CSV 文件如下所示:
分析电视剧《IMDb—IMDb 1》网络抓取后获得的 CSV 文件
分析电视剧《IMDb—IMDb 2》网络抓取后获得的 CSV
我知道,我知道……现在你看着我问:对于这一切,你说了那么多的剧本在哪里,索尔?我只是想先弄清楚它是如何工作的,在分享之前,你可以在作者的资源库中找到:https://github.com/nazareno/imdb-series。现在你只需要下载、定制并在本地运行。
让我们开始处理数据
好了,用 R 开始一个新的脚本,现在我们有了数据集,是时候做一点分析了。我们将首先加载我们将使用的库,以及先前通过 Web 抓取获得的数据集的 CSV。
library(tidyverse)
library(plotly)
library(kableExtra)
library(knitr)theme_set(theme_bw())# CSV READING GENERATED BY IMDB WEB SCRAPING
series_imdb <- read_csv("series_imdb.csv")# EN CASO DE HABER GENERADO UN DATASET MÁS GRANDE, FILTRAR SÓLO ALGUNAS SERIES DEL CSV COMPLETO
mis_series <- series_imdb %>% filter(series_name %in% c("Black Mirror", "Game of Thrones", "Rick & Morty", "Stranger Things", "Westworld")) %>%
mutate(series_name = factor(series_name)) %>%
mutate(season = factor(season))# TV SERIES AND COLUMNS PREVIEW
levels(mis_series$series_name)
colnames(mis_series)
现在,我已经从 5 部电视剧中获得了 189 个观察值和 18 个可用变量。
浅析电视剧《IMDb》——电视剧与控制台中的变量
我们准备好回答问题了。丹妮莉丝的龙有性别吗?狄摩高根会继续伤害人类吗?。不,不幸的是不是这些类型的问题,但我们将能够获得相关的数据和回答问题,如哪个系列更成功?最不成功的几季或几集是什么?
用户评级变量概述
在本分析中,我们将用来回答问题的一个变量是用户评级,在下文中理解为指定评级。让我们来看看 5 部电视剧集的收视率分布情况。
# GLOBAL USER RATING HISTOGRAM
mis_series %>%
ggplot(aes(UserRating)) +
geom_histogram(binwidth = .1, fill = "#0196d7") +
scale_x_continuous(breaks = seq(0, 10, 1)) +
labs(y = "Número de ocurrencias", x = "Calificación de episodio") +
ggtitle("UserRating general de las 5 series", "Black Mirror, Westworld, Game of Thrones, Rick and Morty y Stranger Things.")
用户评分是用 0 到 10 分来衡量的。我们将获得下面的情节,其中我们可以看到,在这五个选定的电视连续剧的情况下,8 到 9 之间的分数占主导地位。一开始就必须承认,选择的五个系列都是制作相当精良的电视剧。
分析电视连续剧《IMDb》——普通用户评分
收视率最高的电视剧就是票数最多的电视剧吗?
IMDb 提供了 UserVotes 变量,通过它我们可以发现投票数和每部电视剧获得的收视率之间是否存在关系。
# RELATION BETWEEN RATING AND VOTES PER TV SERIES
series_votos <- mis_series %>% group_by(series_name) %>% summarise(rating = median(UserRating), totalvotos = sum(UserVotes))
series_votos %>%
ggplot(aes(x = reorder(series_name, totalvotos), y = totalvotos, fill = rating)) +
geom_histogram(stat = "identity") +
scale_fill_distiller(name="Calificación", palette = "Blues") +
scale_y_continuous(labels = scales::comma) +
coord_flip() +
xlab("Serie") + ylab("Número total de votos") +
ggtitle("Calificación de cada serie", "Relación entre calificación y número de votos")
通过获得的直方图,我们发现虽然收视率最高的电视剧是票数最高的电视剧,但与此相反,收视率最低的电视剧并不完全是票数最少的电视剧。所以我们可以说总票数和收视率没有太大关系。
电视连续剧《IMDb》的收视率和投票分析
每部电视剧从头到尾的接受度如何?
为了回答这个问题,我们将使用折线图。中位数用于衡量评估,因为它不受平均值等极值的影响。
# ACCEPTANCE OF EACH TV SERIES BASED ON ITS SEASON RATING
series_acep <- mis_series %>% group_by(series_name, season) %>% summarise(rating = median(UserRating))
series_acep %>%
ggplot(aes(x = as.numeric(season), y = rating, color = series_name)) +
geom_line() +
geom_point() +
scale_x_continuous(breaks = 1:10) +
scale_color_brewer(name = "Serie", palette = "Set1") +
xlab("Temporada") + ylab("Calificación") +
ggtitle("Evolución de la aceptación de cada serie", "Calificación por temporada")
ggplotly()
随着剧情的展开,我们可以看到每一部电视剧的悲惨衰落,尤其是《黑镜》和《权力的游戏》(也许有充分的理由,他们给了我们一个多么精彩的大结局,对吗?).
电视剧《IMDb》各季收视率和接受度分析
每章是如何演变影响每部电视剧的季收视率的?
这是一个也许你们很多人也会有的问题。嗯,我们会用每一季的方框图来验证,用它也可以看到每集的收视率汇总。
# SEASON RATING BY EPISODES
series_temp <- mis_series %>%
ggplot(aes(season, UserRating, color = season)) +
geom_boxplot() +
geom_jitter(width = .2) +
facet_wrap(~series_name) +
labs(x = "Temporada", y = "Calificación de episodio", color = "Temporada") +
ggtitle("Evolución de cada temporada por capítulo", "Calificación por episodio")
获得的剧情会让我们更深刻、更悲哀地看到,比如《权力的游戏》的没落,以及一些因收视率低得多而脱颖而出的剧集。
分析电视剧《IMDb》——每部电视剧的季集收视率
所选电视剧最好和最差的几集是什么?
你肯定会有另一个大问题。我们可以根据按升序和降序排列的用户评级所获得的最低和最高评级来查看哪些是最好的和最差的剧集。
# TOP OF BEST AND WORST EPISODES BASED ON THEIR RATING
mejores_ep <- knitr::kable(x = head(arrange(mis_series, desc(UserRating)) %>%
select(series_name, Episode, ,season, UserRating), 7),
col.names = c('Serie', 'Episodio', 'Temporada', 'Calificación'))
kable_styling(mejores_ep, "striped", position = "left", font_size = 12)peores_ep <- knitr::kable(x = head(arrange(mis_series, (UserRating)) %>%
select(series_name, Episode, ,season, UserRating), 7),
col.names = c('Serie', 'Episodio', 'Temporada', 'Calificación'))
kable_styling(peores_ep, "striped", position = "left", font_size = 12)
需要注意的是,相比其他电视剧,《权力的游戏》的集数和季数都更多。很明显,它很可能会出现在收视率最高和收视率最低的剧集中。
分析电视连续剧《IMDb》—收视率最高剧集的表格视图
分析电视连续剧《IMDb》——收视率最差剧集排行榜
你怎么想呢?你同意获得的前 7 名吗?如果你很想看看前 20 名,除了看到用 plotly 产生的稍微有点花哨的情节,我还分享了我为这篇文章整理的flex dashboard:https://rpubs.com/cosmoduende/imdb-tv-series-analysis-r
在这里你还可以找到完整的代码,如果你想用你最喜欢的电视剧进行分析:https://github . com/cosmoduende/r-analisis-exploratorio-TV-series-IMDB
我希望你有一个非常快乐的分析,你可以体验把它付诸实践,你玩,并惊讶于非常有趣的结果。
应不讲西班牙语的人的要求,这篇文章被翻译成了英文,这些人在小组和论坛上问我是否愿意用英文发表这篇文章。
谢谢你,下次再见。
使用 Jupyter 笔记本进行数据分析和可视化
用 JS 库可视化 Jupyter 笔记本中的数据
丰富的交互计算体验是我最喜欢 Jupyter 笔记本的地方。此外,这是一个完美的基于网络的环境,用于执行探索性分析。
在本教程中,我将展示如何使用两个作为 JavaScript 库提供的交互式数据可视化工具来支持项目的探索阶段。本指南将花费最少的时间和步骤来完成。
我认为数据报告应该易于理解,比分析本身花费的时间更少。这就是为什么,在没有添加任何扩展的情况下,我们将在最短的时间内为 Jupyter 笔记本内的报道建立一个工作环境。
希望由此产生的笔记本模板将帮助每个处理数据的人回答重要的特定领域的问题,并以可理解的形式呈现数据分析见解。
开始吧!
数据
作为一个数据样本,我们将使用 Kaggle 的世界幸福报告来探索数据的空间和时间趋势。
工具
- JupyterLab —我们的环境。你也可以使用 Jupyter 笔记本
- WebDataRocks 数据透视表
这个数据透视表将处理所有与数据相关的计算:聚合、过滤和排序数据。它的主要特点是交互性和易用性。此外,它将作为我们的仪表板的引擎,处理数据并以汇总的形式将其传递给图表。
- 谷歌图表
由于我们的大脑对图表的反应更快,我们可以将表格数据表示与交互式图表结合起来。为此,Google Charts 是一个不错的选择。这个 web 服务提供了所有的基本图表,这些图表可以根据我们的需要进行定制。
关键想法
让我们将本教程分解为构建数据报告解决方案的几个关键步骤:
- 导入所需的库
- 数据准备
- 在笔记本中嵌入数据透视表和图表
- 将数据发送到表
- 配置报告
- 将数据从表格发送到图表
- 保存报告
- 保存并共享笔记本
步骤 1:导入 Python 库
让我们弄清楚每个库代表什么功能:
1.IPython . display—IPython 中显示工具的 API
- json —用于序列化和反序列化 Python 对象的模块。
3.熊猫——数据处理和分析的主要图书馆
第二步:获取数据
首先,这一步取决于您如何存储和访问您的数据。
我使用 Google Sheets 将几个 CSV 文件中对应于各个年份的数据记录合并在一起。使用本教程中描述的方法,我将数据从 Google Sheets 导入到 pandas dataframe。
或者,您可以简单地使用 read_csv() 方法从文件系统导入数据。要合并几年的信息,可以使用“ concat 操作。
让我们看看我们的数据是什么样子的:
现在已经使用最适合您的方法将数据加载到 dataframe 中,使用指定的“records”方向将其转换为 JSON。生成的类似列表的结构是数据透视表理解的数据格式:
步骤 3:创建一个数据透视表,并向其输入数据
接下来,使用指定的报表配置定义数据透视表对象:
以下是我们到目前为止的配置:
- 一个数据片——我们希望在网格上呈现的字段的子集。
- 一个数据源及其类型。使用 json.loads()方法,我们将包含 json 文档的字符串反序列化为 Python 对象。
- 可选视图相关设置:条件格式,数字格式。
接下来,让我们将 Python 字典表示的数据透视表转换为 JSON 格式的字符串:
步骤 4:以 HTML 呈现数据透视表和图表
最重要的一步来了。
让我们定义一个函数,它将在笔记本单元格中呈现数据透视表和图表。
这里,我们已经指定了 HTML 布局,它包含 Google Charts 和 WebDataRocks 的脚本、CSS 样式以及将呈现数据透视表和图表实例的容器。整个布局用三重引号括起来。
此外,我们传递一个参数,它代表执行定制的 JavaScript 代码。在这段代码中,我们实现了应该如何以及何时绘制图表的逻辑。只有当数据透视表完全呈现、加载了数据和本地化文件时,才会出现这种情况。我们可以通过“ reportcomplete ”事件来跟踪这一时刻。
下面的代码完成了从表中获取数据、创建图表以及将汇总数据传递给图表的所有繁重工作:
最后,让我们调用渲染函数并查看生成的仪表板:
结果
如您所见,数据透视表填充了数据,字段根据我们设置的报表规则显示和格式化。
请注意,仪表板本身是交互式的:您可以使用报告工具的不同元素,在网格上分割数据记录,对它们进行过滤和排序。此外,图表会对报告中的每个变化做出反应。
下一步是什么?
现在,您有了一个报告工具,可以从多个角度查看数据。花些时间探索数据:更改跟踪的指标,过滤数据。尝试添加其他可视化图表。
你可以尝试其他数据集,寻求真知灼见。我希望您会喜欢这个过程和结果,并通过这种方法提高您的笔记本电脑工作效率。完成数据分析后,保存笔记本并与朋友或同事分享。讲述你的数据故事。他们会喜欢的!
参考
网络安全 101 的数据分析:检测横向移动
这是一系列博客文章的第二部分。第一个可以在数据渗透上看。
这篇博文的结构如下:
- 介绍横向运动 (4 分钟):一个玩具例子来说明什么是横向运动
- 网络异常检测 (7 分钟) : 统计和机器学习技术检测横向移动
- CTF 挑战 (3 分钟) : 解决3 CTF 挑战关于寻找横向运动
- 违规报告 (4 分钟) : 真实的例子以及我们可以从中学到什么
- 可见度和传感器优势 (3 分钟) : 检查您的数据质量和可见度
- 黑暗空间和蜜罐 (2 分钟) : 要做的事情让检测横向移动变得更容易
前提
好看!您已经成功地检测并阻止了 数据泄露 ,但战斗还远未结束。我们仍然怀疑有坏人潜伏在你的网络中。作为一名负责任的网络安全管理员,你开始搜寻。 利用网络流量数据搜寻更多异常安全事件。
妥协的假设
作为一名网络安全管理员,一种健康的心态是假设您已经受到威胁,这样,您的目标就是找到对手的利用后活动的证据。
请记住,利用只是攻击者的第一步,他们还需要采取额外的步骤来实现他们的主要目标。
回想一下之前的博文,在最初的妥协之后,为了窃取你的数据,攻击者需要经历以下步骤:
(1)攻击者需要使用一些命令和控制(C2)通道与他们的立足点进行协调。
(2)使用 C2,攻击者需要通过网络导航和横向移动来到达数据。
(3)一旦攻击者获得了数据,攻击者需要将这些数据从网络中泄漏出去。
在这篇博文中,我们将介绍一些检测横向移动的简单方法,以及一些关于如何设计可防御和可监控系统的注意事项,以便我们能够持续防御我们的网络。**
横向运动
玩具案例研究
当攻击者成功入侵主机时,很可能主机没有必要的凭证、权限和网络访问权来获取公司的机密数据。在这种情况下,攻击者必须利用他们现有的立足点来获得更高级别的访问权限。
在我们更深入地检测横向运动之前,让我们使用一个玩具案例研究,以便我们可以“描绘”到底发生了什么。
我们的网络中有两个主要部门:
- 人力资源部:打开大量来自求职者的随机电子邮件。他们只能访问内部电子邮件服务器。
- IT 团队:管理生产数据库和员工桌面
人力资源员工是坏人更容易攻击的目标,但是,即使人力资源员工被诱骗打开恶意 PDF,让攻击者访问人力资源机器,这种访问也不会立即让他访问生产数据。那现在怎么办?
下面,我们举例说明我们的玩具案例研究,以及攻击者如何能够在内部进行转换,以便最终能够访问生产数据。
玩具示例,从电子邮件到生产数据库
- 攻击者向发送了一封伪造的求职电子邮件**,HR 打开了附件中的简历。这原来是一个恶意的 PDF ,它帮助攻击者使用反向 shell 在 HR 子网中建立立足点。**
- 通过强力攻击,攻击者获得了 HR 机器的本地管理员凭证,并能够提升他的权限。
- 不幸的是,所有机器都有相同的本地管理员证书。使用这些凭证,他能够转到 IT 组子网中的主机。
- ****通过 IT 组主机的隧道,他能够访问所有生产服务器。
我们如何检测这种横向运动?有些事情上图没有显示出来。想想攻击者是如何发现他所使用的不同帐户、主机和服务的。
主机发现 [1]
“我可以使用这台人力资源机器访问什么?”
在上述第 2 步和第 3 步之间,坏人必须以某种方式知道他可以转到 IT 子网中的哪个 IP 地址。他是怎么找到这个 IP 的?
一种方法是通过扫描可访问的 IP 进行网络扫描。如果扫描结束时枚举了子网中的所有主机,这可能会产生噪声,很容易被检测到。
****这种情况的一个迹象是一个单一的源主机试图建立到许多目的主机的连接。这是在 TCP、UDP 和 ICMP 中。
另请注意,在扫描可访问的主机时,防火墙可能不允许某些连接。如果您的防火墙记录这些放弃的尝试,那么这将为我们提供另一种检测网络扫描的方法。另一个危险信号是当防火墙丢弃来自单个源主机的大量不同连接时。
****在某些情况下,单一掉线也是一个危险信号,尤其是在网络设置良好的情况下。例如,如果一个生产 web 服务器试图连接到一个内部主机,但被防火墙规则丢弃,可能会发生一些奇怪的事情。
服务发现【2】
“这是数据库吗?还是 web 服务器?”
现在,攻击者知道他可以到达哪些主机,下一个问题可能是,他们有什么服务?在第 3 步和第 4 步之间,攻击者可能首先发现在端口 5432 上运行着一个服务,这就是为什么他认为它可能是一个 PostgreSQL 服务器。
发现公开的正在运行的服务包括通过端口扫描尝试找到主机的开放端口。根据攻击者的目标,这也可能非常嘈杂。
默认情况下,nmap 扫描每个协议的 1000 个“感兴趣的”端口 [3】。然而,攻击者可能会选择扫描所有的 65535 端口,这将更加嘈杂。或者,攻击者可能更隐蔽,选择只扫描他关心的几个端口,例如用于 NetBIOS 服务扫描的端口 139 和 445。
对于更隐蔽的端口扫描,这更接近于寻找主机扫描的迹象。
对于有噪声的端口扫描,我们正在寻找试图连接到许多目的端口的源。相关日志是网络流日志或防火墙日志。
如果您为此设置了警报,您首先应该找到的是您的漏洞扫描器。如果你没有发现这些,那么你就是做错了。
端点到端点连接
您应该在网络中看到的最常见的连接是从一个端点到一个服务器或从一个服务器到另一个服务器。端点之间相互对话并不常见,从服务器到端点的连接就更不常见了。(尤其是当您不希望员工创建自己的文件共享时。)
端点到端点的良性连接的一个例子是从它到网络上不同主机的连接。这可能是为了维护或更新。
IT 机器与 HR 机器对话可能是正常的
然而,HR 机器直接与 IT 机器建立连接可能是不正常的。
HR 机器连接到 IT 机器可能不正常
你对你的网络了解得越“深入”,你就越能缩小可能异常的连接类型,以及哪些是真正重要的。
在 toy 示例中,我们可以为从 HR 机器到除电子邮件服务器之外的任何其他主机的任何连接尝试设置警报。我们甚至可以阻止 HR 与除邮件服务器之外的任何内部主机的任何连接。
网络异常检测
到目前为止,我们讨论的方法都是基于签名的。基于我们确定的横向移动可能如何发生的机制,我们定义业务规则并将它们编码为警报。有了这些,我们能够将尽可能多的领域专业知识和背景融入到我们的警报中。如果处理得当,这些可以为我们提供具有良好信噪比的警报。
尽管这些类型的规则对于保护我们的网络是必要的,但是它们可能不足以适当地保护我们的网络,因为:
- 网络复杂性的增长速度远远超过安全团队增加的人力
- 企业网络不是静态的,需要持续的监控和重新建立基准
- 业务规则在检测新型攻击方面不够稳健
如果您的网络相对较小,那么您可以使用基于规则的警报,因为您可以描述所有您期望看到的正常连接,并为那些您认为不正常的连接创建警报。这些主机应该只与 DNS 和代理服务器通信。此服务器应该只连接到此数据库。只有这些特定的主机应该连接到生产数据库……
然而,随着您的网络变得越来越大、越来越复杂,跟踪所有这些变得几乎不可能。这就是为什么我们想探索一些网络异常检测技术。
****假设是恶意主机会偏离典型行为。如果我们能够以某种方式自动捕捉“正常”的特征,那么希望我们能够通过筛选“异常”来捕捉恶意活动。
定义和模拟“正常”的好方法是一个正在进行的研究领域。在这篇博文中,我们将主要关注两种方法:
- 发现宿主行为特征中的异常值。
- 通过新的边/链路预测发现意外连接
主机行为特征中的异常值
这里的想法是将主机在给定时间间隔内的行为总结到一个向量中。有了所有这些主机矢量,我们就可以识别出与网络其余部分格格不入的主机。
特色工程
最重要的第一步是为网络中的每台主机构建功能。
我们应该得到什么特征?这取决于您能得到什么,以及您所在组织的安全领域专家有什么建议。
首先,我们来看看论文[13]使用了什么特征。他们总共使用了 23 项功能:
- ****RDP 特色:SuccessfulLogonRDPPortCount,UnsuccessfulLogonRDPPortCount,RDPOutboundSuccessfulCount,RDPOutboundFailedCount,RDPInboundCount
- ****SQL 特性:UnsuccessfulLogonSQLPortCount、SQLOutboundSuccessfulCount、SQLOutboundFailedCount、SQLInboundCount
- ****成功登录功能:SuccessfulLogonTypeInteractiveCount、SuccessfulLogonTypeNetworkCount、SuccessfulLogonTypeUnlockCount、successfullogontyperoteractivecount、SuccessfulLogonTypeOtherCount
- ****未成功登录功能:UnsuccessfulLogonTypeInteractiveCount、UnsuccessfulLogonTypeNetworkCount、UnsuccessfulLogonTypeUnlockCount、unsuccessfullogontyperemoteeinteractivecount、unsuccessfullogontypoethercount
- 其他: NtlmCount,DistinctSourceIPCount,DistinctDestinationIPCount
[14]中的其他特征示例有:
- 过去 24 小时内是否至少有一个地址验证失败
- 给予用户访问网站的 IP 地址的最大异常值
- 从登录到结账的最短时间
- 用户在过去 24 小时内访问网站的不同位置的数量。
有了这些特征向量,我们可以构建一个矩阵,并开始使用离群点检测技术。
主成分分析
*我们从使用*主成分分析【15】寻找异常值的经典方法开始,因为在我看来这是最直观的。
简单来说,你可以认为 PCA 是一种压缩和解压缩数据的方法,其中压缩过程中的数据丢失被最小化。
由于大多数数据应该是正态的,因此 PCA 的低秩近似将集中于正态数据。PCA 在异常值的情况下表现如何?
由于离群值不符合其余数据的相关结构,因此这些数据将具有较高的重建误差。一种形象化的方法是将“正常数据”视为网络的后台活动。
下面我们从 fast.ai [12]的数值线性代数课程中看到一个更直观的例子。他们从视频的不同帧中构建了一个矩阵。左边的图像是视频的一帧。中间的图像是使用鲁棒 PCA 的低秩近似。右边的图像是差异,重建误差。
在上面的例子中,视频的“主题”通常出现在重建误差中。同样,我们希望异常的宿主能从背景中突出出来,并具有较高的重建误差。
注意:经典的主成分分析可能对异常值敏感(不幸的是),因此使用[12]中讨论的健壮主成分分析可能更好
自动编码器
我不会详细介绍自动编码器,但是您可以将自动编码器视为 PCA 的非线性版本。
神经网络的构建使得中间存在信息瓶颈。通过迫使网络通过中间的少量节点,它迫使网络优先考虑最有意义的潜在变量,这就像 PCA 中的主成分。
类似于 PCA,如果自动编码器是在正常数据上训练的,那么它可能很难重构异常数据。重建误差可以用作“异常分数”。
PCA 和 autoencoder 是[14]用来以无人监管的方式检测恶意主机的一些组件。
隔离林等方法
发现异常主机的另一种流行方法是使用隔离森林,这种方法比其他方法表现更好[13]。就像许多基于树的算法一样,这可以处理数值和分类数据,并且对数据的分布和形状没有什么假设。
最后,我们希望使用我们的历史数据来学习一个可以帮助我们区分正常和不正常的函数。
使用 Py OD [16]从不同方法中学习到的示例决策边界
如果你想探索不同的离群点检测方法,我推荐你浏览一下PyOD【16】和他们引用的实现算法和论文。
如果你有兴趣了解更多这方面的知识,我推荐你观看异常检测:算法、解释、应用【17】。
新边缘/链路预测
这是对[11]的过度简化
不像以前的方法,我们试图找到恶意主机。在这里,我们试图找到异常边,其中边是客户端和服务器之间的连接,或者是源和目的地之间的连接。源/客户端也可以是用户名,边代表认证。
现在假设这是我们第一次看到这个边缘。我们可能会问,观察到这个新边缘的概率有多大?
对于上面的函数,我们需要客户端和服务器都有某种表示。我们可以有分类协变量,例如:
- 主机的子网
- 主机的类型(端点或服务器)
- 用户的职务
- 用户的位置
有了分类协变量,用二进制指示变量表示,我们可以使用源和目的地的相互作用来了解新边的可能性有多大。
获取源和目的地的嵌入
表示主机的另一种方式是生成某种嵌入。
让我们从历史数据中说,我们以前已经观察到以下优势。主机在某些连接中可以是源,在另一些连接中可以是目的。
由此,我们可以构建一个邻接矩阵,其中每个单元代表一条可能的边,而被观察到的边的值为 1。
邻接矩阵来自
使用这个矩阵,我们可以执行一些非负矩阵分解。这是我们在其他应用中看到的,比如推荐系统和协同过滤。通过因式分解,我们能够得到源和目的地的嵌入。
链接预测
文件[18]显示了如何能够因子分解,同时纳入分类协变量。这是通过泊松矩阵分解(PMF)完成的。
在估计嵌入和一些必要的系数之后,我们可以估计观察到新边缘的可能性。
我希望您对我们正在尝试做的事情有一个高层次的了解,但是当然,以一种计算高效的方式估计α、β和ɸ的值是问题的关键。详细可以看【18】还有。另一篇也是关于边缘预测的论文是[11]。
如果你对统计学在网络安全中的更多应用感兴趣,我建议你去看尼克·赫德的演讲网络安全中的数据科学和相关的统计挑战【19】。
CTF 挑战
以下是针对横向运动的 2019 趋势科技 CTF 中通配符 400 挑战的(很晚)部分演练。在这里尝试挑战,解决方案可以在这个内核中找到。
这里的数据是合成的,不模拟典型的网络协议和行为。因此,这些挑战不需要深入了解网络协议。
横向蛮力
问题 9:一旦一台机器被弹出,它通常被用来探索还能到达什么地方。一台主机被用来大声探测整个企业,试图找到企业中所有其它主机的方法。它的 IP 是什么?
我们看到主机正在扫描整个网络。这可能意味着我们在寻找主机扫描的迹象。
要做到这一点,我们需要获得具有许多唯一目的 IP 的源 IP 地址。
这里很明显13.42.70.40正在扫描并试图横向移动。为了完整起见,我们查看了它的网络活动,发现在非工作时间进行扫描会导致网络活动激增。
未节流的扫描噪音很大,会产生大量流量。因此,如果攻击者不试图隐藏他们的网络扫描,这是我们希望看到的。
横向间谍
问题 10:一台主机试图找到一条更安静地连接其他主机的途径。它的 IP 是什么?
这是一个更棘手的问题。排除 13.42.70.40 之后,很难找到任何其他主机在唯一目标 IP 或目标端口数量方面表现突出。
于是,下面的情节就没用了。坏主机正在与正常主机的后台活动融合在一起。
****
我们必须找到一种更有创造性的方法来发现扫描活动。如果我们有更多的网络背景,那么我们可以专注于:
- 连接到网络中未使用的端口
- 到端点的连接
这是我们从网络基线中了解到的情况,但我们的网络中没有这种环境。为了推断哪些端口是“正常的”,我们假设如果多个源主机连接到一个特定的 IP 地址和端口,那么它可能是正常的。
给定目标 IP 和端口的源 IP 列表
例如,我们看到多个主机连接到12.37.117.51:56
,那么这可能是一个正常的连接。
对于12.32.36.56:68
,我们看到只有12.49.123.62
试图连接到它。这大概是不正常的。
过滤掉正常的目的 IP 和端口后,剩下的唯一源主机就是12.49.123.62
。这是我们的烂 IP。
奖金(可选):内部 P2P
问题 5:有时我们的低度感染在其他方面也是可见的。一种特殊的病毒已经在许多机器中传播,这些机器现在被用来相互传递命令。恶意软件创建了一个内部 P2P 网络。在所有相互通信的主机中,最大的内部集团使用哪个唯一端口?
这与检测横向移动有关,但解决方案涉及使用图论中的分析技术。
这个问题很简单,因为这个问题直接问最大的集团。集团是一组彼此都有联系的主机。
每个圈都是网络中的一个主机。如果两个主机彼此有连接,我们在它们之间放置一个端点。
使用 NetworkX ,通过枚举所有派系并找到最大的一个,就可以得到确切的答案。然而,这并不能很好地扩展。
我们选择使用快速近似方法 large_clique_size(G) 。
**G = networkx.Graph()
G.add_nodes_from(internal_nodes)
for l, r **in** zip(internal_edges.src, internal_edges.dst):
G.add_edge(l, r)
_size = large_clique_size(G)**
使用上面的代码,我们可以获得特定端口的大集团,但这对于所有端口来说运行起来太慢了。为了加速搜索,我们过滤掉最大团保证很小的端口。
很容易证明,如果图G
中存在一个规模为K
的团,那么G
中至少应该存在K
个度大于或等于K-1
的节点。鉴于这一事实,我们可以计算每个端口的团大小的上限。完成这项工作的代码在解决方案内核中。
这些是具有最高上限的端口
处理这些端口我们发现端口 83 有一个 264 的最大团。
违规报告
在这一节中,我们将回顾以前数据泄露造成的历史“灾难”。
2018 SingHealth 数据泄露 [5]
2018 年新加坡健康数据泄露事件中,新加坡卫生服务部门的 150 万名患者的数据被盗。这个事件最接近我们的玩具案例研究。
SingHealth 数据泄露事件报告中关键事件的图解摘要[5]
简而言之,关键事件包括:
- 尽管报告中并不清楚这是如何发生的,但我们知道攻击者能够通过网络钓鱼攻击在的*工作站上安装恶意软件。*****
- 通过访问工作站 A,攻击者能够危害多个端点和服务器。根据报告,攻击者可能已经侵入了 Windows 身份验证系统,并从域控制器获取了管理员和用户凭据。
- 最终,攻击者能够控制工作站 B,这是一个可以访问 SCM 应用程序的工作站。有了它,攻击者能够找到适当的凭证来破坏 SCM 系统****
- 在 SCM 数据库上运行了大量查询
- 通过工作站 A** 对数据进行了过滤**
以下是报告中确定的一些促成因素。
SGH 思杰服务器和配置管理数据库之间的网络连接是允许的。
允许 Citrix 服务器场与 SCM 数据库服务器之间的网络连接。对网络体系结构的基本安全审查可能已经表明,这种开放的网络连接产生了安全漏洞。
如果这两个系统是隔离的,攻击者就不能轻易地访问 SCM 数据库。
根据该报告,在两个系统之间保持这种开放网络连接的原因之一是为了提高操作效率。管理员希望能够使用 Citrix 服务器和安装在那里的工具来管理不同系统的多个数据库。
SGH 思杰服务器未能充分防范未经授权的访问
这里的许多因素都与身份验证和凭证管理有关,这超出了本文的范围。现在与我们相关的一个问题是,缺乏防火墙来防止使用 RDP 对 SGH Citrix 服务器进行未经授权的远程访问。
2018 年美国宇航局 JPL 违规事件【6】
2018 年美国宇航局 JPL 漏洞因标题为“美国宇航局因未经授权的树莓派被黑”而闻名。最终报告没有详细说明最初的进入点是什么,攻击者是如何获得树莓 pi 的访问权限的,以及树莓 pi 位于何处。
我觉得有趣的是发现“与外部合作伙伴共享的网络环境分割不充分”。
应该限制第三方对内部网络的访问
JPL 建立了一个网络网关,允许外部用户及其合作伙伴,包括外国航天局、承包商和教育机构,远程访问特定任务和数据的共享环境。然而, JPL 没有适当地隔离各个合作伙伴环境以将用户限制在他们已经批准访问的那些系统和应用……2018 年 4 月事件中的网络攻击者利用 JPL 网络缺乏分段性在连接到网关的各种系统之间移动,包括多个 JPL 任务操作和 DSN。
横向流动可能导致跨组织的访问。对外部各方实施严格的信任边界,并确保将他们对您网络的访问仅限于必要的连接。外部各方可能不像您的组织那样具有安全态势,并且可能是薄弱环节。确保微调您的 IDS 或 IPS,将来自这些子网的流量视为外部流量。
2017 年 Equifax 数据泄露 [7]
2017 年的 Equifax 泄露是一次数据泄露,导致大约 1.43 亿美国消费者的数据泄露。
切入点是 ACIS 应用程序,该应用程序没有为现在著名的关键 Apache Struts 漏洞打补丁。利用该漏洞,攻击者能够获得服务器上的 web 外壳并运行任意命令。
鉴于货物信息预报系统服务器已经受损,最好是将攻击的影响仅限于货物信息预报系统。网络或主机防火墙会阻止对外部资源的访问。
网络分割限制了爆炸半径
不幸的是,根据报告,Equifax 网络是平坦的!这是最坏的情况,因为任何主机的危害都可能导致网络中任何主机的危害。
扁平网络使得攻击非常高效
安全问题 1。在 Sun 应用服务器和 Equifax 网络的其余部分之间没有分割。从互联网获得应用服务器控制权的攻击者可以在全球范围内转向[Equifax]网络中的任何其他设备、数据库或服务器 …如果攻击者利用扁平、未分段的网络突破组织的网络边界,他们可以在整个网络中横向移动,并获得对关键系统或重要数据的访问权限。
通过远程访问 ACIS web 服务器,攻击者能够:
- 装载包含未加密应用程序凭据的文件共享
- 在 51 个不同的数据库上运行 9,000 个查询。
- 泄露所有数据
具体来说,货物信息预报系统只使用了 3 个数据库,但可以通过网络访问 48 个不相关的数据库。如果货物信息预报系统被隔离在相关的数据库中,那么这个漏洞就不会那么严重。
网络分段
我们在所有不同的漏洞中看到的一个共同主题是,网络分段在防止横向移动方面起着重要作用。
如果网络被适当地分段,爆炸半径被限制在特定的系统内,攻击者将很难在网络中穿行。
然而,对于传统网络,非常精细的分段可能成本过高,并且您可能会受到网络物理拓扑的限制。但是对于云部署,因为一切都是虚拟化的并且可以自动化,我们可以将微分段应用到我们的系统中【4】。您可以基于每个工作负载隔离机器,而不是基于每个网络。
能见度和传感器优势
在设置警报和规则之前,您需要做的一项重要工作是检查您实际收集的日志。你和你的团队必须清楚你的可见性极限是什么。
- 我从哪些数据源收集日志?
- 我在每个数据源中启用了哪些类型的日志?
- 我的数据源/传感器实际“看到”了什么?
- 我测试过吗?
我从哪些数据源收集日志?
检查您从哪些数据源收集数据。
对于传统的内部部署,您可以列出来自您的设备和供应商的所有常用内容,包括 DNS、DHCP、Active Directory、防火墙、代理、SQL 等。也可以查一下安全洋葱。它是免费的,你可以利用围绕它发展起来的社区的知识和工具。
对于云部署,确保您正在收集您在云提供商中使用的关键服务的审计和访问日志。另外,收集网络流量日志。
我在每个数据源中启用了哪些类型的日志?
默认情况下,有些数据源不会保存所有必要的日志,您需要打开它。
您可能会发现,由于配置错误,一些防火墙规则没有被记录。“我关闭了 X 的所有日志记录,因为它正在填满磁盘。”
例如,在谷歌云平台中,防火墙日志、VPC 流日志和 GCS 数据访问日志在默认情况下是不打开的。
我的数据源/传感器实际“看到”了什么?
传感器的优势描述了传感器能够观察到的数据包。优势是由传感器的位置和网络的路由基础设施之间的相互作用决定的[8]。
并非所有连接对我们的传感器都可见
对于我们的玩具用例,可能是同一子网内的连接不需要通过防火墙。如果我们分析防火墙日志,我们看到的唯一连接来自两个不同子网中的主机。
一些企业可能有单独的外围防火墙和内部防火墙设备。假设您只从外围防火墙收集日志,那么很可能任何内部到内部的连接都不会出现在您的 SIEM 上。
小心这个。你必须知道你的盲点在哪里。使用不能让您充分了解网络的日志会给您一种虚假的安全感。你可能认为没有人在扫描网络,而事实上,有,只是你没有看到它!
对于云基础设施,这取决于您的云服务提供商能为您提供什么。因为一切都是虚拟化的,所以您不再受网络物理拓扑的限制,并且有可能在整个 IaaS 中实现统一的可见性。但是如果你使用 PaaS,特别是对于 SaaS 来说,你对得到什么样的日志有更少的控制。
我测试过吗?
确保您的数据源正在工作并且您正在获取日志的唯一方法是测试它!
根据您的威胁建模制定几个简单的场景并进行模拟。尝试在不同的子网上实际运行网络扫描,或者尝试使用某些 honey 凭据。看看这些是否会生成您想要的日志和警报。看看你的新机器学习模型是否会检测到这一点。
运行模拟时,您可以从日志收集、数据转换、SIEM 接收、规则和警报,甚至调查和事件响应等方面对您的检测系统进行端到端测试。
它还将让您有机会捕捉数据源失败或配置错误的实例。也许服务停止了,它的存储空间被填满了,或者它的许可证过期了。可能是防火墙配置的更改无意中禁用了一组防火墙规则的日志记录。
不要因为没有收到 NIDS 或 DLP 的警报就认为一切正常;如果 NIDS 主机在 3 个月前被意外关闭了呢?
Equifax 没有看到数据泄漏,因为用于监控 ACIS 网络流量的设备由于安全证书过期而已经停用了 19 个月。[7]
测试测试测试!
黑暗空间和蜜罐
这里有些东西可能对那些拥有更成熟的网络和安全状况的人有用。
暗区
未使用的地址或端口号称为暗空间,合法用户很少尝试访问暗空间。大多数用户不手动输入 IP 地址,通常依赖 DNS 或应用程序为他们连接。另一方面,当攻击者试图横向移动时,很可能最终会访问它们[8]。
我们可以对试图进入这些黑暗空间的内部主机发出警报。这可能很吵,但在某些情况下很容易调查。该警报的一个常见原因是地址错误或配置错误。
根据[8],这里有一些事情你可以做,使攻击者更有可能进入黑暗的空间,使它更容易被检测到:
- ****重新排列地址:大多数扫描是线性/顺序的,重新排列地址使它们均匀地分散在网络中,或者在网络中留下大片空白是一种创造黑暗空间的简单方法。
- ****移动目标:如果分配给一个服务的端口是非标准的,攻击者只有在枚举更多的端口后才能找到它,使它们更加可见。当然,这是有代价的,因为过多地改变端口可能会让每个人感到困惑。
蜂蜜制品
蜂蜜的事情类似于黑暗空间,我们不期望这些事情出现在我们的日志中。
与黑暗空间不同,这些东西确实存在于我们的网络中,但它们没有任何合法用途。正式员工不知道这些,也不应该需要使用或接触这些。另一方面,攻击者可能会在网络中移动时遇到这些东西,如果这些宝贝看起来有价值或有用,他们可能会尝试用它们做些什么。
这些例子有:
- 蜂蜜证书:
- 蜂蜜代币
- 蜂蜜文件共享
- 蜂蜜服务器
我们希望它们看起来有价值,一旦这些出现在我们的日志中,我们就会进行调查。蒂姆·梅丁在他最近的网络直播“ 肮脏的防御,做得极其便宜:让我的生活更艰难让你的生活更轻松【9】中对此进行了更详细的讨论。****
其他人
这是相关的,但我只会简单地提一下(因为这篇博文已经够长了,抱歉)。
虽然 NIDS 和 NIPS 有时部署在外围。如果位置和配置正确,它们可以帮助检测横向运动。它将能够检测网络上的客户端漏洞、可能的文件传输和不常见的未授权网络协议的使用。您还可以使用 NIDS 来检测网络上明文传输的蜜糖令牌。
检查它是否配置为忽略内部到内部的连接。
接下来:指挥与控制,信标
我们仅仅触及了表面。您可以使用诸如 Active Directory 之类的日志进行更深入的研究,以便更精确地搜索横向移动,但是我们现在必须继续。
在下一篇博文中,我们将讨论一些关于寻找命令和控制证据的问题。****
攻击者需要能够控制他们的立足点,以便在您的网络中导航。就像他们说的,杀一条蛇砍下它的头。
参考
[2] Nmap 参考指南,第 15 章,服务和版本检测**。**
丹尼尔·米斯勒,秘书。
[4] CSA 安全指南(v4),7.3.3 微分段和软件定义的边界。
[5] 调查委员会对新加坡医疗服务数据库网络攻击的公开报告。
喷气推进实验室的网络安全管理和监督。
[8] M. Collins,通过数据分析实现网络安全(2014 年)
肮脏的防御,廉价的交易:让我的生活变得更艰难,让你的生活变得更轻松。
安全洋葱。
[11] 梅泰利,s .,赫德,N. (2019)。计算机网络中的贝叶斯新边缘预测和异常检测。
[12] Rachel Thomas,Fast.ai 数值线性代数,第 3 课:鲁棒 PCA 背景去除。
[13] Siddiqui,Md Amran 等人,“使用异常检测和解释以及专家反馈来检测网络攻击。”
[14] Veeramachaneni,Kalyan 等人,《AI^ 2:训练大数据机器进行防御》
PyOD:一个用于可伸缩异常检测的 Python 工具箱。
帕西诺、弗朗切斯科·桑娜、梅丽莎·JM·特科特和尼古拉斯·a·赫德。“使用泊松矩阵分解的计算机网络中的图链接预测.”
[19]尼克听说了。网络安全和相关统计挑战中的数据科学
照片由来自佩克斯的阿尔特姆·萨拉宁、来自佩克斯的丹尼斯·尤丁、来自佩克斯的伊格纳西奥·帕莱斯、来自佩克斯的汤姆·斯温嫩、来自伊戈尔·戈里亚切夫的乌斯普拉什、埃莉诺拉·帕特里科拉
MySQL 中的数据分析——关系数据库中的运算符、连接等
【Craig Dickson 的 SQL 教程
学习使用 SQL 和免费开源软件创建、更新和查询您自己的全功能关系数据库——第 3 部分
我们的国际语言学校数据库的实体关系图(ERD)
这是一个由 3 部分组成的系列文章的第 3 部分,从零开始,带您了解使用 MySQL 设计、编码、实现和查询关系数据库的过程。参见第一部分( 设计关系数据库并创建实体关系图 ) 此处 )和第二部分( 使用 MySQL)此处 )。
本教程的所有代码和信息都可以在相关的 GitHub 资源库 中找到。我使用了lucid chart来制作文章中显示的图表。
在本系列的第 1 和 2 部分中,我们已经经历了设计数据库、在 MySQL 中实现它以及用我们的数据填充它的步骤。在进入本文之前阅读这些是一个好主意,但是如果你想跳到这个分析部分,让你跟上进度的 SQL 代码都包含在相关的 GitHub 库中。
查询关系数据库
我们从头开始设计并实现了一个全功能的关系数据库。很棒的东西!但是现在呢?
如果我们从事数据分析师类型的工作,我们会希望开始分析这些数据。但是首先,我们需要知道如何到达它。本文将描述如何用 SQL(特别是 MySQL )编写查询,这将允许我们访问锁定在我们的 RDBMS 中的数据和关系。
正在设置
我们在本系列的第 2 部分的中介绍了安装 MySQL 社区服务器,所以我们应该有一个运行在我们系统上的实例。我们还将再次使用 PopSQL 的免费计划来运行我们的查询。
查询数据库
SQL 查询语句的主要形式如下:
SELECT *columns* FROM *table* WHERE *condition;*
例如,要从我们的教师表中选择所有的列,我们使用语句:
SELECT *
FROM teacher;
在这种情况下,星号(*)表示“所有列”。我们正在选择教师表,并且没有附加条件,这意味着我们将从该表中选择所有列和所有记录(即所有数据)。
不错!
如果我们只需要表中的特定属性,我们可以命名这些属性,而不是使用星号。
SELECT last_name, dob
FROM teacher;
使用条件
在大多数情况下,我们不想选择一个表中的所有记录,而是寻找满足某些条件的特定记录。在这些情况下,我们将需要使用一个 WHERE 子句。
请注意,在本文中,我们将在 SELECT 语句中使用 WHERE 子句,但是完全相同的语法和操作符也适用于 UPDATE、INSERT 或 DELETE 语句。WHERE 子句是所有需要掌握的 SQL风格的重要方面。
WHERE 子句允许我们设置一个条件,例如:
SELECT *
FROM course
WHERE language = 'ENG';
这将从课程表中选择语言为英语的课程的所有详细信息。
果不其然,确实如此
也许我们想对结果进行不同的排序,那么我们可以使用 ORDER BY 来做到这一点。
SELECT *
FROM course
WHERE language = 'ENG'
ORDER BY start_date;
默认情况下,MySQL 按升序排序。如果我们想让结果按降序排列,我们可以像这样添加 DESC:
SELECT *
FROM course
WHERE language = 'ENG'
ORDER BY start_date DESC;
我们也可以按顺序使用 ORDER BY,因此我们可以:
SELECT *
FROM course
WHERE language = 'ENG'
ORDER BY start_date DESC, client, teacher;
这将按照属性的层次结构,按照它们被指定的顺序,给出我们的结果。
AND、OR 和 NOT 运算符
我们也可以在 WHERE 子句中使用和运算符。如果你熟悉任何其他编程语言,你很可能以前就遇到过这些逻辑操作符。在 SQL 中,它们完全按照您的预期工作。
SELECT *
FROM course
WHERE language = 'ENG' AND level = 'C1';
使用和给我们所有的课程,语言是英语和水平是 C1
使用 AND 运算符排除了所有不符合这两个条件的结果,因此只显示 C1 英语水平的课程——在国际语言学校数据库中,这给了我们一个结果。如果我们在同一个语句中使用 OR,我们会得到一些不同的结果。
SELECT *
FROM course
WHERE language = 'ENG' OR level = 'C1';
使用 OR 为我们提供所有英语课程,以及所有其他 C1 水平的课程
使用 OR 运算符包括满足任一条件的所有记录,因此现在我们有所有英语课程(因为这些课程满足 language = 'Eng '条件)加上一门 C1 级别的课程,高级俄语。
我们还可以在条件前使用 NOT 运算符,以排除符合该条件的所有记录,并包括所有其他记录。
SELECT *
FROM course
WHERE NOT language = 'ENG' OR level = 'C1';
使用 NOT 会给出表中不满足第一个条件的所有记录。此处包含课程 15,因为它满足第二个条件(级别= 'C1 ')。
当我们想要排除某些记录时,这非常有用。
比较运算符
还可以在 WHERE 子句中使用其他的比较操作符。这是我们可以在 MySQL 中使用的比较运算符的完整列表:
- 等于:
=
- 不等于:
<>
或!=
- 小于:
<
- 小于或等于:
<=
- 大于:
>
- 大于或等于:
>=
这给我们带来了许多可能性。一个非常简单的例子是识别 1990 年以前出生的所有教师的姓名和联系信息:
SELECT first_name, last_name, phone_no
FROM teacher
WHERE dob < '1990-01-01';
这些是我们的,我们可以说,更有经验的老师
BETWEEN 运算符
运算符之间的允许我们选择值在某个范围内的记录。我们可以使用以下代码来识别 2020 年 1 月开始的所有课程:
SELECT *
FROM course
WHERE start_date BETWEEN '2020-01-01' AND '2020-01-31';
只有 2020 年 1 月的一门新课程
在我们的示例中,我们比较的是日期,但是 BETWEEN 也可以用于查找特定范围内的任何值(销售额、订单数量等)。非常有用!
我们喜欢这样
LIKE 操作符允许我们搜索与指定模式匹配的值。我们可以使用两个通配符来构建我们的模式:
- 此通配符代表零个、一个或多个字符
_
该通配符仅代表一个字符
如果你熟悉正则表达式,那么这似乎对你来说很熟悉,因为 SQL 基本上实现了它们的简化版本。这允许我们构建模式,并让 RDBMS 找到符合这些模式的记录。
让我们看一个例子:
SELECT course_name
FROM course
WHERE course_name LIKE '%interm%';
我们找到了三个名字符合我们模式的课程
通过在模式的开头和结尾使用%
通配符,我们指示 MySQL 识别出现在 course_name 属性中的字符序列‘interm’。这是一个非常有用的特性。
进入最佳状态
我们的下一个操作符是中的——这是许多 or 条件的有效简写,允许我们给 MySQL 一个我们想要使用的值的列表。
SELECT first_name, last_name
FROM participant
WHERE last_name IN ('Garcia', 'Nowak', 'Mustermann');
这里的输出与我们从查询中得到的输出相同:
SELECT first_name, last_name
FROM participant
WHERE last_name = 'Garcia' OR last_name = 'Nowak' OR last_name = 'Mustermann';
好处是我们不用重复写出列名。当我们的查询中包含很多值时,这是非常有益的。
为空
我们将在本节中讨论的最后一个运算符是为空,及其负表亲不为空。这允许我们识别特定属性(或一组属性)为空的记录。这对于用其他值更新它们、从数据集中排除它们或者出于各种其他原因可能是有用的。
在我们的数据库中,我们可以用这个来识别“只”用一种语言教学的教师:
SELECT *
FROM teacher
WHERE language_2 IS NULL;
詹姆斯和斯蒂芬妮,这仍然令人印象深刻。继续努力吧!
或者,我们可以使用 IS NOT NULL 来查找用两种语言教学的教师:
SELECT *
FROM teacher
WHERE language_2 IS NOT NULL;
然而,这更令人印象深刻
错认假频伪信号
有时,我们数据库中的数据以属性名称存储,这可能会使我们报告的受众感到困惑,或者只是以一种不吸引人的方式格式化。我们可以通过使用别名在查询阶段解决这个问题。
这里我们使用 AS 关键字来指示 MySQL 显示带有临时名称的特定列。例如:
SELECT course_name AS 'Course Name', course_length_weeks AS 'Length of Course (Weeks)'
FROM course
WHERE language = 'DEU';
哦,太棒了!
当我们为其他利益相关者准备报告并想让事情看起来更漂亮时,或者当一个列的名称在我们的特定查询的上下文中可能会引起混淆时,这可能是有用的。
汇总数据
在执行数据分析时,能够汇总我们的数据也很重要。MySQL 提供了许多方法来实现这一点,我们不会在这里一一介绍。这里有一个全面的列表,供那些想深入了解的人使用。
我们想要执行的最常见的聚合之一是求平均值。在 MySQL 中,我们用 AVG() 聚合器来做这件事。如果我们想知道一门课程的平均长度,我们可以使用以下查询:
SELECT AVG(course_length_weeks)
FROM course;
平均疗程为 20.5556 周。有意思!
在使用聚合器时,通常会将它们与由语句组成的组合使用。这可以帮助我们超越基础,在数据库中找到真正有用的信息。
SELECT client, AVG(course_length_weeks)
FROM course
GROUP BY client;
这就更有趣了!
您可以看到我们如何使用 GROUP BY 和一些聚合器在我们的数据库中找到真正的见解。在这里,我们可能希望了解为什么客户 101 预订的课程平均比其他客户长得多,并看看我们是否可以在那里学到一些东西,让其他客户效仿。
另一个需要了解的重要聚合器是计数。这让我们可以计算表中特定值的实例数。
SELECT COUNT(*)
FROM course;
好的,很高兴知道
让我们说得更具体些。
SELECT COUNT(*)
FROM course
WHERE language = 'Eng';
哦,不错
但是我们可以使用 GROUP BY 使它更加有用。
SELECT language, COUNT(language)
FROM course
GROUP BY language;
非常好!
在 MySQL 中,我们还可以做更多的聚合数据的工作,但这已经足够让我们开始了。
嵌套查询
当我们开始在其他查询中嵌套查询时,事情会变得更加令人兴奋。这就是 SQL 真正展示其威力的地方。我们将通过一个简单的例子来演示这些原理,但是通过在查询中嵌套查询,编写真正复杂和强大的查询的可能性几乎是无限的。
让我们看看我们的教师表。
如果我们想要识别比我们老师的平均年龄更年轻的老师(一系列日期的平均年龄到底是多少是一个复杂的问题,但我们现在会忽略它)。我们可以用如下的嵌套查询来回答这个问题:
SELECT *
FROM teacher
WHERE dob >
(SELECT AVG(dob)
FROM teacher);
这里发生的事情是,在我们的 WHERE 子句中,我们放置了另一个完整的 SQL 查询(在括号中)。这就是嵌套查询,一个完整的查询在另一个查询中使用。
这是一个基本的例子,但是希望这种查询的能力和潜力是显而易见的。这不会是我们的最后一个嵌套查询,所以我们以后会有更多的机会看到它是如何工作的。
请加入我们
关系数据库的全部好处是实体/表相互关联的方式,允许我们探索和分析各种实体之间的关系。到目前为止,我们所有的查询都是针对单个表的。这很好,但是我们错过了关系数据库的一些主要优势。让我们通过观察加入的奇妙世界来补救这一点。
如果您熟悉其他流行的数据分析工具,如针对 Python 的熊猫或针对 R、的 dplyr ,那么您可能已经熟悉了连接。MySQL 中的概念完全相同,但是语法当然有一点不同(首先是更多的大写字母)。
要将任何表连接到另一个表,我们需要在两个表中都有一个包含相同数据的列。在关系数据库中,这是外键的功能(或者对于 N 到 M 关系,是存储这些关系的表),这些连接使得关系数据库成为存储大量数据的强大而有效的方式。
SQL 中有三种类型的连接——一个内部连接(在 MySQL 中,当只使用连接时,这是默认的)和两种类型的外部连接——左连接和右连接和。我们将从最常见、也最容易理解的内部连接开始,在 SQL 中也称为普通连接。
内部联接
内部联接采用两个表,并基于它们共有的列将它们联接起来。这允许我们利用表之间存在的关系。让我们先看一个例子:
SELECT course.course_id, course.course_name, course.language, client.client_name, client.address
FROM course
JOIN client
ON course.client = client.client_id
WHERE course.in_school = FALSE;
我们第一次加入的产出!
这里我们从两个不同的表中选择了列。在 MySQL 中,当我们处理多个表时,有必要一起标识列和表。一般来说,这样做也是一种很好的做法,因为它可以让任何阅读您代码的人清楚地知道引用了哪些列。
这是使用上面看到的 table.column 语法完成的。在我们的查询中,我们从 course 表中选择 course_id、course_name 和 language 列,从 client 表中选择 client_name 和 address 列。
然后,像往常一样,我们使用 FROM 语句来标识我们的第一个表(在本例中,当然)。到目前为止,一切顺利。
接下来的几行变得有趣了。首先,我们通过使用 JOIN 关键字让 MySQL 知道我们想要做一个连接,然后是第二个表(Client)。我们必须指示 MySQL 哪个表包含共有的数据——不可能将两个没有共享数据的表连接在一起。
为此,我们使用 ON 关键字,后跟table 1 . column = table 2 . column,其中标识的列是包含公共数据的列(通常是一个表的主键和另一个表的外键)。
在我们的示例中,我们还使用了 WHERE 关键字来添加条件。我们只是在寻找不在国际语言学校进行的课程的详细信息和公司地址。这对我们的老师来说是有用的信息,这样他们就知道他们需要去哪里旅行!
也许我们想为我们的一位老师——在我们的例子中,让我们选择斯蒂芬妮·马丁——提供她必须在校外授课的班级的地址。使用连接,这很简单:
SELECT course.course_id, course.course_name, course.language, client.client_name, client.address
FROM course
JOIN client
ON course.client = client.client_id
WHERE course.in_school = FALSE AND course.teacher = 2;
斯蒂芬妮只有一门课是在客户的办公室教的
复杂的事情——嵌套查询的连接
通过将连接与嵌套查询结合起来,我们可以进一步提高强度,从而从数据库中获得更具体的见解。如果我们想知道所有参加由 Niamh Murphy 教授的课程的人的名字会怎么样?
使用两个连接和一个嵌套查询,我们可以立即从数据库中获得这些信息:
SELECT participant.first_name, participant.last_name
FROM participant
JOIN takes_course ON takes_course.participant_id = participant.participant_id
JOIN course ON takes_course.course_id = course.course_id
WHERE takes_course.course_id =
(SELECT takes_course.course_id
WHERE course.teacher = 6);
瞧啊。
现在事情严重了!为了解构这一点,并了解更复杂的 SQL 查询中一般会发生什么,向后工作会非常有帮助(在构造自己的更复杂的查询时,这也是一个有用的技巧)。如果我们在这里这样做,我们可以看到我们有一个标识 course_id 的子查询,其中 teacher_id = 6 — Niamh 的 id 号。
(SELECT takes_course.course_idWHERE course.teacher = 6)
其结果将是 Niamh 教授的所有课程的 course_id 值,用于我们查询的 WHERE 条件中。因此,该条件可以用简单的英语表达为“该课程由 Niamh Murphy 教授”。
因为我们正在寻找的输出是参与者的名字,所以我们的最终目标是将课程表连接到参与者表。然而,由于这些表之间的关系是 N 对 M 的关系(一个参与者可以选修多门课程,一门课程可以由多个参与者选修),我们不能简单地使用典型的 1 对 N 关系的外键关系。
相反,我们需要连接到我们创建的额外的 Takes_Course 表,以存储 Course 和 Participant 表之间的 N 对 M 关系。唷!
SELECT participant.first_name, participant.last_name
FROM participantJOIN takes_course
ON takes_course.participant_id = participant.participant_idJOIN course
ON takes_course.course_id = course.course_id
我们使用键 participant_id 将 Participant 表连接到 Takes_Course 表,然后立即使用 course_id 将其连接到 Course 表。这就是我们如何从存储在数据库中的这些更复杂的关系中提取信息。
起初,这似乎很复杂,有点令人生畏,但是如果我们花点时间思考一下,再看一下我们的 ERD,就会明白这就是我们如何解锁存储在这个结构中的数据的方法。整个过程完全符合逻辑,完全由我们放入查询中的代码驱动。
理解这一点的一个好方法是尝试编写我们自己的查询。看着这些数据,思考“我们怎样才能把这种联系从数据库中提取出来”,并尝试不同的组合。这总是习惯一项新技术的最好方法,尝试一下,看看会发生什么。另外,这是一个有趣的时间!
其他类型的连接
现在我们已经掌握了内部连接,让我们看看 MySQL 中不同种类的外部连接。当我们有一些信息没有包含在所有表中时,连接类型之间的差异就变得更加明显了。ILS 数据库被很好地组合在一起,但大部分包含完整的数据。遗憾的是,在现实生活中情况并非如此,我们需要知道如何应对。
这意味着是时候向我们的数据库添加一个新表了!激动人心的时刻!
国际语言学校的管理层正在进行一些市场调查。他们想知道新课程以哪些行业为目标,并咨询了商业出版社,整理了一份汇总表,展示了一些不同行业的预测前景。
显然,现在不是物流的好时机
让我们将它与我们的客户端表进行比较。
当我们比较每个表中的行业列时,我们注意到有些行业同时出现在两个表中(零售、物流),而有些则没有。
在我们继续之前,让我们将行业前景表添加到我们的数据库中。
CREATE TABLE industry_prospects (
industry VARCHAR(20) PRIMARY KEY,
outlook VARCHAR(20)
);INSERT INTO industry_prospects VALUES
('Retail', 'Good'),
('Hospitality', 'Poor'),
('Logistics', 'Terrible'),
('Tourism', 'Great'),
('Events', 'Good');
成功!
内部联接
到目前为止,我们一直在处理内部连接。这种类型的连接遵循下面的文氏图。
内部联接将只返回两个表都匹配的值,而不会返回只在一个表中正在执行联接的列中有值的任何记录。
因此,使用下面的代码对我们的客户和行业前景表执行内部连接,将得到两个表都有匹配条目的条目。
SELECT client.client_name, client.industry, industry_prospects.outlook
FROM client
JOIN industry_prospects
ON client.industry = industry_prospects.industry;
因为零售和物流都包含在这两个表中,所以我们只得到匹配的条目,没有空值。
左连接
一个左连接将给出左表中的所有值,以及右表中的匹配值。如果右边的表中没有匹配的值,我们将在输出中得到空值。
左连接的代码与我们的内连接代码相同,只是我们用左连接替换了连接,如下所示:
SELECT client.client_name, client.industry, industry_prospects.outlook
FROM client
LEFT JOIN industry_prospects
ON client.industry = industry_prospects.industry;
我们的输出现在包括来自客户端表的每个条目(它出现在我们的代码中的第一个位置,所以它是“左边”的表,但这纯粹是因为我们选择了对表进行排序的方式)。如果行业前景表中没有相应的条目,则返回空值。
当我们想从另一个表中引入数据,但又不想丢失主表中的任何数据时,左连接是很好的。这可能发生在大量的现实场景中,左连接是我们数据分析工具箱中的一个主要部分。
右连接
右连接是左连接的镜像,不常用。因为哪个表是左表,哪个是右表完全取决于编写查询的人,所以数据分析人员和查询编写人员倾向于将我们想要保存所有数据的表放在左边。但是,仍然有一些情况需要正确的连接,特别是在连接多个表的更复杂的查询中。
为了完整起见,让我们在表上执行一个右连接。
SELECT client.client_name, client.industry, industry_prospects.outlook
FROM client
RIGHT JOIN industry_prospects
ON client.industry = industry_prospects.industry;
我们的输出现在包括了来自行业前景表的所有条目,正如所预期的那样,空值现在出现在来自客户表的数据中。
这只是左连接的镜像,我们可以简单地通过交换代码中client
和industry_prospects
的顺序来获得相同的输出。
还有另一种类型的连接,在其他 SQL 风格中也支持,即完全外部连接。这将合并左连接和右连接,并返回两个表中的所有条目,如果不匹配,则返回空值。
MySQL 中不支持这个,这里就不看了。它可以在 MySQL 中相对容易地被模拟,这就是(人们推测)为什么开发者没有选择包含它的原因。如果我们最终使用另一种风格的 SQL,了解这一点对我们很有好处。所以现在我们知道了!
哇!在本文中,我们已经讨论了很多基础知识,从学习 SQL 查询的基础知识到构建具有多个连接的复杂嵌套查询。希望 SQL 在数据库中创建、检查、更新和删除数据的能力比开始时更加清晰。
关系数据库和 SQL 是处理大型数据集的一些最广泛使用的技术,尽管它们可能不像 R 或 Python 中的最新库那样流行,但对于工作数据分析师来说,它们是一个必不可少的组件,理解起来非常有用。
我们在这里看到的查询已经从相当基本的到仍然相当基本的,但是希望我们可以看到如何组合不同的条件和操作符可以让我们产生真正令人敬畏的结果。勇往直前,负责任地使用这种新的力量!
这是我的三篇系列文章中的最后一篇,介绍如何使用 SQL 和免费开源软件创建、更新和查询自己的全功能关系数据库。我们一起讨论了很多问题,我希望这对您有所帮助。
我希望听到您的反馈,如果您有任何意见或问题,请通过我的网站联系我。如果你想了解我的项目,你可以在 Medium 上关注我,在 GitHub 上关注我,或者访问我的网站。
谢谢你花时间和我在一起,下次再见!
更像这样?访问craigdoedata . de
python 中的数据分析:熊猫入门
Pandas 是一个广泛用于数据分析和操作的 python 工具。最近,我一直在使用具有大数据帧(> 50 米行)的 pandas,并通过 PyDataUK May Talks 和 exploring StackOverflow 线程发现了几个对优化我的分析非常有用的技巧。
本教程是一个系列的第一部分,旨在介绍熊猫和它在探索帕尔默企鹅数据集时提供的一些有用的功能。
在本文中,我们将介绍:
- 如何安装和导入熊猫
- 熊猫中的数据结构
- 如何输入和输出数据
- 检查数据
- 数据清理入门
介绍帕尔默企鹅
虹膜数据集是数据科学中常用于可视化和模式识别的数据集。自从最近发现作者与优生学的联系后,一直在努力寻找其他数据集供使用。
这就是企鹅的用武之地,帕尔默企鹅数据集已经由克里斯汀·戈尔曼博士和艾利森·霍斯特根据 CC-0 许可收集并发布,作为可供探索的备选数据集!
它已经发布了供 R 使用的示例,但是我们将使用 python 包 pandas 来探索可以在 GitHub 上找到的原始数据集(注意原始 CSV 文件不再包含在主分支中,因为它是我第一次开始查看数据集时包含的,所以我在这里引用存储库中的一个旧提交。如果该文件被重新添加到主分支中,我将更新它)。
这些数据包含了对南极洲帕尔默群岛岛屿上三种不同企鹅物种的观察,我们可以使用这些数据来比较不同物种或岛屿之间的体重、鳍状肢长度或长度。
我们开始吧!
1.安装和导入熊猫
Pandas 可以使用 python 包管理器pip
: pip install pandas
从 PyPI 安装
或者将conda
与conda install pandas
配合使用
安装完成后,我们可以导入熊猫,并为其分配一个别名pd
,别名为:
import pandas as pd
2.数据结构
pandas 中有两种数据结构,Series 和 DataFrames。我最常用的结构是 DataFrame,这是一种带有标签的 2D 表格结构,类似于电子表格或 SQL 表。
另一种结构是一个序列,它是一个 1D 标记的数组,可以保存任何数据类型。每一行都标有索引。
数据帧可以用许多不同的方式构建,包括作为系列的字典,其中每个系列是数据帧中的一列。
本文将关注数据帧,我们将使用 CSV 作为数据帧的输入。
要进一步探索数据结构,请查看熊猫文档。
熊猫系列和数据框示例
3.输入输出数据
投入
数据框可以通过多种方式创建:
a)创建一个空的数据帧:df = pd.DataFrame()
b)输入数据:df = pd.DataFrame(data = data)
,其中输入数据data
可以是多种不同的格式,这使得构建数据框架变得灵活和方便,因为您处理的数据可以是任意数量的结构,包括序列字典,如上图所示,并且可以用以下代码构建:
d = {'student1': pd.Series([85., 72.], index=['maths', 'science']),
'student2': pd.Series([62., 70., 55.], index=['maths', 'science', 'english']),
'student3': pd.Series([45., 48., 70.], index=['maths', 'science', 'english'])}
df = pd.DataFrame(d)
print(df.head())
c)来自文件或数据源的输入,我们将在此重点关注 Palmer Penguin 数据的原始 CSV。
与使用输入数据构建数据框架非常相似,当从文件或数据源创建数据框架时,可以接受许多输入格式。
这包括:
- excel 电子表格
- 结构化查询语言
- 战斗支援车
- JSON
查看熊猫文档获取完整列表。
这在数据存储方面提供了很大的灵活性,你可以读入存储在本地或远程的文件,甚至可以接受压缩文件。
我们感兴趣的 Palmer Penguin 文件存放在 GitHub 上,您可以下载该文件并阅读指定您机器上位置的 CSV 文件,或者我们可以提供原始文件的链接并使用一行代码打开它:
df = pd.read_csv('https://raw.githubusercontent.com/allisonhorst/palmerpenguins/1a19e36ba583887a4630b1f821e3a53d5a4ffb76/data-raw/penguins_raw.csv')
另一个常见的数据源是 SQL 数据库。有许多不同的 python 包可以连接到数据库,其中一个例子是pymysql
,我们可以首先连接到 MySQL 数据库,然后使用read_sql
将数据从查询加载到df
。这提供了快速和容易地连接到远程数据库的灵活性!
注意:这只是一个如何读入 SQL 数据的例子,在教程中没有用到
# Example code: how to use read_sql
import pymysqlcon = pymysql.connect(host='localhost', user='test', password='', db='palmerpenguins')df = read_sql(f'''SELECT * FROM penguins''', con)
💡 Python 3 的 f 字符串在输入查询时使用起来很方便。
我们稍后可能会输入一个条件,该条件使用了我们之前定义的变量,我们可以直接在 f 字符串中包含该变量,而不是对该条件进行硬编码。
三重引号还允许在 f 字符串中使用单引号,而不使用转义字符,如下例所示。例如:
# Example code: f-string's and variables
region = tuple('Anvers')df = read_sql(f'''SELECT * FROM penguins WHERE Region IN {region} AND Date Egg > '2007-11-11' ''', con)
大型数据帧
在读取大型数据集时,通过指定选项chunksize
至read_csv
、read_sql
或其他输入函数,可以迭代文件并每次迭代读取有限的行数。然而,值得注意的是,这个函数现在返回一个TextFileReader
而不是一个数据帧,并且需要进一步的步骤将块连接成一个数据帧。
df = read_csv('https://raw.githubusercontent.com/allisonhorst/palmerpenguins/1a19e36ba583887a4630b1f821e3a53d5a4ffb76/data-raw/penguins_raw.csv', chunksize = 10000)df_list = []
for df in df:
df_list.append(df)df = pd.concat(df_list,sort=False)
在 Palmer Penguin 数据集上不一定要使用这一步,因为它只有几百行,但这里展示的是如何在一个大文件上使用这一步,一次读取 10k 行中的数据。
输出
正如我们可以以多种格式输入文件一样,将数据帧输出到文件也同样灵活和容易!
经过一些操作后,我们可以将数据帧写入 CSV,如果需要,可以选择压缩文件:
df.to_csv('output.csv', compression='gzip)
如果您的数据存储在 AWS 中,那么有一个用于 Python 的 AWS SDK,[boto3](https://boto3.amazonaws.com/v1/documentation/api/latest/index.html)
可以用来连接到您的 AWS 服务。
4.检查数据的快速检查
在我们开始研究我们的数据之前,首先要检查它是否已经正确加载并包含我们所期望的内容。
数据帧的尺寸
我们可以首先通过检查行数是否大于 0 来检查数据帧是否为空,并使用以下函数进一步检查维度:
- 获取行数:
len(df)
- 获取列数:
len(df.columns)
- 获取行数和列数:
df.shape
- 获取元素的数量(行数 X 列数):
df.size
if len(df) > 0:
print(f'Length of df {len(df)}, number of columns {len(df.columns)}, dimensions {df.shape}, number of elements {df.size}')else:
print(f'Problem loading df, df is empty.')
这将返回:
Length of df 344, number of columns 17, dimensions (344, 17), number of elements 5848
我们的数据集已经正确加载,有 344 行和 17 列。这个数据帧包含什么?
数据类型和存储器
我们已经加载了数据帧,但是我们仍然不知道它包含什么类型的数据。我们可以用df.info()
得到一个摘要——让我们看看它返回了哪些细节:
df.info()<class 'pandas.core.frame.DataFrame'>
RangeIndex: 344 entries, 0 to 343
Data columns (total 17 columns):
studyName 344 non-null object
Sample Number 344 non-null int64
Species 344 non-null object
Region 344 non-null object
Island 344 non-null object
Stage 344 non-null object
Individual ID 344 non-null object
Clutch Completion 344 non-null object
Date Egg 344 non-null object
Culmen Length (mm) 342 non-null float64
Culmen Depth (mm) 342 non-null float64
Flipper Length (mm) 342 non-null float64
Body Mass (g) 342 non-null float64
Sex 334 non-null object
Delta 15 N (o/oo) 330 non-null float64
Delta 13 C (o/oo) 331 non-null float64
Comments 54 non-null object
dtypes: float64(6), int64(1), object(10) memory usage: 45.8+ K
df.info()
回报:
- 索引数据类型(dtype)和范围,在本例中,pandas 数据帧有 344 个条目,用索引值 0-343 表示,
- 每列的名称和数据类型,以及非空值的数量,
- 内存使用情况
penguin 数据包含混合或字符串数据类型的列objects
,整数int64
和浮点数float64
。下表总结了熊猫的数据类型。
熊猫类型
使用[df.info()](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.info.html)
默认情况下,内存使用量只是根据列数据类型和行数进行估计。
我们可以指定使用深度内省来计算实际内存使用量,这在处理大型数据帧时特别有用:
df.info(memory_usage='deep') <class 'pandas.core.frame.DataFrame'>
RangeIndex: 344 entries, 0 to 343
Data columns (total 17 columns):
studyName 344 non-null object
Sample Number 344 non-null int64
Species 344 non-null object
Region 344 non-null object
Island 344 non-null object
Stage 344 non-null object
Individual ID 344 non-null object
Clutch Completion 344 non-null object
Date Egg 344 non-null object
Culmen Length (mm) 342 non-null float64
Culmen Depth (mm) 342 non-null float64
Flipper Length (mm) 342 non-null float64
Body Mass (g) 342 non-null float64
Sex 334 non-null object
Delta 15 N (o/oo) 330 non-null float64
Delta 13 C (o/oo) 331 non-null float64
Comments 54 non-null object
dtypes: float64(6), int64(1), object(10) memory usage: 236.9 KB
我们还可以使用以下命令检查每列的内存使用情况:
print(df.memory_usage(deep=True))
Index 80
studyName 22016
Sample Number 2752
Species 31808
Region 21672
Island 21704
Stage 25800
Individual ID 21294
Clutch Completion 20604
Date Egg 23048
Culmen Length (mm) 2752
Culmen Depth (mm) 2752
Flipper Length (mm) 2752
Body Mass (g) 2752
Sex 21021
Delta 15 N (o/oo) 2752
Delta 13 C (o/oo) 2752
Comments 14311
dtype: int64
或者总内存使用量如下:
print(df.memory_usage(deep=True).sum())242622
我们可以看到,数字列明显小于包含对象的列。不仅如此,我们对分析中的所有列都不感兴趣。
这个原始文件包含已经收集的所有数据。如果我们要比较不同种类和不同岛屿上的企鹅的体重、鳍状肢长度和长度,那么我们可以清理数据,只保留相关的数据。
5.数据清理
要清理数据,我们可以采取几个步骤:
删除我们不感兴趣的列
让我们先看一下前几行数据:
print(df.head())
df.head()的输出
这个输出看起来不太对。我们知道有 17 列,但我们只能看到其中的 4 列以及这里的索引。根据您正在使用的控制台的大小,您可能会在这里看到更多的列,但可能不是全部 17 列。
为了查看所有的列,我们可以将display.max_columns
的值设置为None
。
pd.set_option('display.max_columns', None)
print(df.head())
将最大列数设置为 None 后 df.head()的输出
查看数据样本,我们可以确定我们想要使用的列,并通过只指定我们想要保留的行和列来为df
重新分配一个新值。我们可以用df.loc(rows, cols)
来做到这一点。在 rows 参数中,冒号表示所有值,而 columns 可以指定我们感兴趣的列,它们是:物种、地区、岛屿、竿长(mm)、竿深(mm)、鳍长(mm)、体重(g)、性别
keep_cols = ['Species', 'Region', 'Island', 'Culmen Length (mm)', 'Culmen Depth (mm)', 'Flipper Length (mm)', 'Body Mass (g)', 'Sex']df = df.loc[:, keep_cols] print(df.columns) >>> Index(['Species', 'Region', 'Island', 'Culmen Length (mm)', 'Culmen Depth (mm)', 'Flipper Length (mm)', 'Body Mass (g)', 'Sex'], dtype='object'
现在,我们的数据帧中只存储了感兴趣的列。
替换或删除空值
我们可以再次检查df
的内存使用情况,我们可以看到,通过删除不相关的列,它减少了一半。
df.info(memory_usage='deep')<class 'pandas.core.frame.DataFrame'>
RangeIndex: 344 entries, 0 to 343
Data columns (total 8 columns):
Species 344 non-null object
Region 344 non-null object
Island 344 non-null object
Culmen Length (mm) 342 non-null float64
Culmen Depth (mm) 342 non-null float64
Flipper Length (mm) 342 non-null float64
Body Mass (g) 342 non-null float64
Sex 334 non-null object
dtypes: float64(4), object(4) memory usage: 104.8 KB
看看非空值的数量,大小数据有 2 个空值,性别有 10 个。我们也可以看到这一点:
print(df.isna().sum())
Species 0
Region 0
Island 0
Culmen Length (mm) 2
Culmen Depth (mm) 2
Flipper Length (mm) 2
Body Mass (g) 2
Sex 10
dtype: int64
我们可以删除这些值,或者用另一个值替换它们,在函数中指定inplace=True
来重新赋值。
在这种情况下,我们可以用Unknown
替换Sex
列的na
值,并删除其他列的na
值。
df['Sex'].fillna('Unknown', inplace=True) print(df.isna().sum()) Species 0
Region 0
Island 0
Culmen Length (mm) 2
Culmen Depth (mm) 2
Flipper Length (mm) 2
Body Mass (g) 2
Sex 0
dtype: int64 df.dropna(inplace=True) print(df.isna().sum()) Species 0
Region 0
Island 0
Culmen Length (mm) 0
Culmen Depth (mm) 0
Flipper Length (mm) 0
Body Mass (g) 0
Sex 0
dtype: int64
由于我们只移除了少数几个值,这不太可能影响内存使用,但有助于清理数据,并且在处理包含大量空值的大型数据帧时,可能会减少内存。
将对象转换为分类数据类型
清理数据的最后一步是检查每一列中唯一值的数量,以确定分类数据类型是否比使用对象更合适。
我们已经知道在我们的数据中有三种企鹅,但是这个列的 dtype 是一个object
,所以让我们先检查这个列。
print(df['Species'].head()) 0 Adelie Penguin (Pygoscelis adeliae)
1 Adelie Penguin (Pygoscelis adeliae)
2 Adelie Penguin (Pygoscelis adeliae)
4 Adelie Penguin (Pygoscelis adeliae)
5 Adelie Penguin (Pygoscelis adeliae)
Name: Species, dtype: object print(df['Species'].nunique()) 3 print(df['Species'].unique()) ['Adelie Penguin (Pygoscelis adeliae)' 'Gentoo penguin (Pygoscelis papua)' 'Chinstrap penguin (Pygoscelis antarctica)']
正如所料,这一列中只有 3 个唯一值,但每一行都包含一个企鹅种类的字符串,占用了大量内存。相反,我们可以将该列转换为类别数据类型,并检查这如何改变内存。
print(df.memory_usage(deep=True))Index 2736
Species 31626
Region 21546
Island 21575
Culmen Length (mm) 2736
Culmen Depth (mm) 2736
Flipper Length (mm) 2736
Body Mass (g) 2736
Sex 21213
dtype: int64df['Species'] = df['Species'].astype('category')print(df.memory_usage(deep=True))Index 2736
Species 702
Region 21546
Island 21575
Culmen Length (mm) 2736
Culmen Depth (mm) 2736
Flipper Length (mm) 2736
Body Mass (g) 2736
Sex 21213
dtype: int64
这已经从 31626 字节变成了 702 字节,大大减小了大小!对于其他对象列、区域、岛和性别,可以采用相同的方法。
for col in ['Region','Island','Sex']:
print(f'Column: {col}, number of unique values, {df[col].nunique()}, unique values: {df[col].unique()}')Column: Region, number of unique values, 1, unique values: ['Anvers']
Column: Island, number of unique values, 3, unique values: ['Torgersen' 'Biscoe' 'Dream']
Column: Sex, number of unique values, 4, unique values: ['MALE' 'FEMALE' 'Unknown' '.']
Region 只有一个值,此列可以删除,因为它不提供任何值。
df.drop(columns=['Region'], inplace=True)
print(df.columns)Index(['Species', 'Island', 'Culmen Length (mm)', 'Culmen Depth (mm)',
'Flipper Length (mm)', 'Body Mass (g)', 'Sex'],
dtype='object')
岛和性别可以像种一样转换成分类数据类型。在此之前,在句号的性别栏中有一个奇怪的值,让我们看看有多少个这样的值。
print((df['Sex']=='.').value_counts())False 341
True 1
Name: Sex, dtype: int64
因为只有 1 个值,所以让我们用替换空值的方法用Unknown
替换它。
df['Sex'].replace('.','Unknown', inplace=True)
print((df['Sex']=='.').value_counts())False 342
Name: Sex, dtype: int64
现在我们对这些列感到满意,让我们将 Sex 和 Island 转换为类别,并检查最终的内存使用情况:
df['Sex'] = df['Sex'].astype('category')
df['Island'] = df['Island'].astype('category')print(df.memory_usage(deep=True))Index 2736
Species 702
Island 613
Culmen Length (mm) 2736
Culmen Depth (mm) 2736
Flipper Length (mm) 2736
Body Mass (g) 2736
Sex 610
dtype: int64print(df.memory_usage(deep=True).sum())15605
总的内存使用量从 242622 增加到了 15605。
df.info(memory_usage='deep')<class 'pandas.core.frame.DataFrame'>
Int64Index: 342 entries, 0 to 343
Data columns (total 7 columns):
Species 342 non-null category
Island 342 non-null category
Culmen Length (mm) 342 non-null float64
Culmen Depth (mm) 342 non-null float64
Flipper Length (mm) 342 non-null float64
Body Mass (g) 342 non-null float64
Sex 342 non-null category
dtypes: category(3), float64(4)
memory usage: 15.2 KB
将内存从 236.9 KB 减少到 15.2 KB。
任务完成!
到目前为止,我们已经介绍了如何开始将文件读入 pandas 并清理文件以优化性能。penguin 的数据已经有了很好的结构,与您可能从其他来源遇到的数据类型相比,只需要最少的清理。它也是一个相当小的数据集,从 200 KB 开始,当 pandas 可以处理 GBs 量级的文件时,这些快速提示可以显著提高性能。
这是系列教程的第 1 部分。在接下来的部分中,我们将定量和图形化地探索我们的数据,使用 pandas 和一些额外的 python 包(matplotlib 和 seaborn)创建数据可视化,并且我们将深入了解 pandas 的一些其他有用的特性!
本教程的完整代码可以在 GitHub 上找到!
你想知道更多关于熊猫的数据检查和清理功能吗?或者我错过了什么?让我知道!
如果你喜欢这篇文章,你可以订阅我的每月简讯直接在你的收件箱里获得我的最新文章、我使用的资源和其他技巧!
想了解 web 抓取或学习资源,包括 python 中的数据科学课程,请查看我的一些其他文章:
作为一名数据科学家,我在工作中接受的第一批任务之一就是网络搜集。这完全是…
towardsdatascience.com](/data-science-skills-web-scraping-using-python-d1a85ef607ed) [## 学习编码。学习 Python。
你想学习编码但是不知道从哪里开始吗?开始您的编码之旅,并决定 python 是否…
towardsdatascience.com](/learn-to-code-learn-python-efb037b248e8)