TowardsDataScience-博客中文翻译-2016-2018-一百零七-

18 阅读1小时+

TowardsDataScience 博客中文翻译 2016~2018(一百零七)

原文:TowardsDataScience Blog

协议:CC BY-NC-SA 4.0

对伦敦不同类型犯罪之间关系的探索性数据分析

原文:towardsdatascience.com/exploratory…

最近,我制作了一张地图,标出了伦敦警察局记录的 14 种不同类型的犯罪地点。这让我思考是否有某些类型的犯罪更有可能一起发生(例如,犯罪之间是否存在关系)。

我最初的假设是,因为我们正在处理犯罪问题,如果一个地区的犯罪率较高,那么该地区的所有类型的犯罪率通常都会较高,反之亦然。然而,我想更深入地探讨各种类型的犯罪之间的关系以及犯罪发生的地点。

大量的数据使得简单地使用我创建的互动图来直观地查看一些犯罪是否倾向于一起发生变得有点困难。

数据概述

本次分析中使用的数据与我在之前的帖子中使用的数据相同,只是这次我用纬度和经度变量替换了 LSOA(低层超级输出区域)名称定义的犯罪发生区域。这是因为我对每个犯罪事件的确切位置不感兴趣,而是对各种犯罪发生的区域感兴趣。不涉及太多细节,LSOA 是对较小地区的人口普查测量,基于从 1000 到 3000 的人口规模和从 400 到 1200 的家庭。更多信息可以在国家档案馆网站伦敦数据商店网站上找到。

根据数据集定义,总共有 5149 个 LSOA 地区在 2016 年经历了 14 种不同类型的犯罪中的至少一种。

方法

首先,我需要使用基本的描述性统计和可视化进行一些探索性分析,以提供对数据的总结和理解,并识别任何模式和特征。为了观察哪些犯罪可能一起发生,我设想使用 k-means 聚类来将这些特定的犯罪分组在一起。在进行聚类之前,我需要将数据的维度从 14 个变量(犯罪类型)减少到一个更容易解释和可视化的数量,同时仍然考虑所有类型的犯罪。为此,我选择使用主成分分析。

用于此分析的代码可以在 Github 上找到。

第 1 部分:探索性数据分析

从对数据的基本总结开始,很明显,每种类型的犯罪都经历了非常广泛的发生率。14 种犯罪中有 5 种的第一个四分位数为 0,所有犯罪类型的最大值都显著高于各自的第三个四分位数。这可以通过创建 14 种不同类型犯罪的方框图来看出(图 1)。同样清楚的是,每项犯罪都有大量的异常值。因此,这个箱线图对于可视化每个犯罪的分布不是很有用,所以我为此创建了另一个没有异常值的箱线图(图 2)。

Figure 1: Box plot of the 14 different types of crime including all the data

Figure 2: Box plot of the 14 types of crime without the outliers

通过查看图 1,很难理解每种犯罪的大量异常值和零值,因此我计算了每种犯罪的内部上限值,然后确定有多少区域记录的发生率高于每种犯罪的内部上限值(即外部上限值)。这一点可以在表 1 中看到,其中有一栏列出了每种类型的犯罪都没有发生的地区的数量。

Table 1: List of the 14 types of crime and their respective upper inner fence value, the number of areas that recorded outliers and the number of areas that recorded zero incidences for any particular type of crime.

这现在开始提供数据的一些特征以及 14 种犯罪类型之间分布的差异。我们也越来越了解每起犯罪可以解释多少差异。我计算了每项犯罪的方差,其中“反社会行为”的方差最大,为 3437.2,而“持有武器”的方差最小,仅为 4.3。为这两个变量创建直方图可以更详细地解释它们的分布。

Figure 3: Histogram for anti social behaviour

Figure 4: Histogram for possession of weapons

可以看出,在 5149 个地区中,有 4000 多个地区的“拥有武器”发生率为零或为零。上表也证实了这一点,我们可以看出,这些值中的大多数实际上为零,因为“拥有武器”有 3022 个地区的发生率为零。

“反社会行为”在每个地区记录的事件数量中有更广泛的分布。从上表可以看出,“反社会行为”有 279 个零发生率区域,这少于上异常值的数量(345)。

第二部分:犯罪类型的关系

在对数据和数据中的某些特征有了更详细的理解之后,下一步是探索不同类型犯罪之间的关系。

人们可以从制作 14 种犯罪的相关矩阵开始。即使相关矩阵确定了某些类型的犯罪比其他类型的犯罪具有更高的相关性,也很难理解所有 14 种犯罪之间的这些关系。制作所有 14 种犯罪的散点图也很难解释。

在不涉及太多细节的情况下,主成分分析(PCA)因此可以用于将数据的维度减少到更少的成分,这将尽可能多地保留原始数据所表达的可变性。更少的组成部分将有助于描述原始变量之间的关系,方法是将它们投影到二维网格上,以便更容易地进行可视化,从中可以将相似类型的犯罪分组在一起。

PCA 结果提供了 14 个分量(等于变量的数量),分量的重要性可以通过每个分量所占方差的比例来确定。在该分析中,前两个成分分别占方差的 60%和 7%。第一个分量明显高于其余分量,总方差的 80%归因于前 4 个分量。这可以通过每个成分的累积方差比例的折线图来可视化(图 5)。

Figure 5: Proportion of variance explained by each component displayed showing the cumulative proportion of variance.

该图有助于确定保留用于分析的成分数量。很明显,第一个分量是最重要的分量,其后的分量所解释的差异量变化很小,因此在分析中只需要保留前两个分量。

最后,为了确定不同类型的犯罪之间的关系,可以使用前两个主要部分,使用双标图(图 6)进行直观解释。双标图显示代表每个变量的向量,这些向量远离原点,指向由前两个主成分分配给每个变量的值。它还包括分配给数据集中每个区域(5149)的两个组件的值,但是,此分析的重点更多地是变量(犯罪类型)的向量之间的关系。

Figure 6: Biplot representing the values assigned to the variables (crime types) and data points (areas) by the first two principal components.

解释该图时需要考虑的两个因素是矢量的长度和它们之间的角度。较长的向量意味着变量被图很好地表示,反之亦然。向量之间的角度大小决定了变量的相关性,这是实现此分析目标的理想指标。小角度表示强正相关,90 度表示不相关,180 度表示负相关。

由于拥挤,很难解释这一情节,但是,很明显,在比较“其他犯罪”与其他类型犯罪的关系时,“其他犯罪”似乎有点离群。可以生成分配给 14 种犯罪类型的前两个分量值的散点图,以便从双标图中更好地显示其他犯罪类型之间的关系。没有显示两个非常相似的图,一个单独显示散点图,另一个只添加了 K-means 聚类输出,我只是包含了后者(图 7)。

Figure 7: Scatter plot displaying the 14 different types of crime along with their respective clusters.

通过这个情节,各种类型的犯罪之间的关系开始变得更加明显。除了已经被确定为异常值并在此重申的“其他犯罪”之外,还有其他两个变量分组(1 和 3)。在群组 1 和群组 3 中,可以确定某些类型的犯罪比其他类型的犯罪具有更强的关联性。例如,在第一组中,“刑事破坏和纵火”、“反社会行为”、“毒品”、“公共秩序”和“暴力和性犯罪”被认为是密切相关的。而在群组 3 中,“自行车盗窃”、“入室盗窃”、“抢劫”和“偷窃他人”密切相关。

我没有深入分析这些犯罪发生的具体区域,因为我将违反这一分析的范围,所以我将把这一点留给另一篇文章,我制作了一个表格来查看每个犯罪发生率最高的区域。从表 2 中可以看出这一点,表中列出了各地区每种犯罪的发生率,突出显示的单元格代表该特定类型犯罪记录的最高数字。

Table 2: List of areas that had recorded the highest figure for each type of crime.

有趣的观察结果是,有两个地区(威斯敏斯特 018A 和威斯敏斯特 013E)记录了 4 种不同类型犯罪的最高发生率:

威斯敏斯特 018A -“反社会行为”、“刑事破坏和纵火”、“公共秩序”和“暴力和性犯罪”。

威斯敏斯特 013E -“入室盗窃”、“其他盗窃”、“入店行窃”和“偷窃他人财物”。

这些犯罪非常类似于前一情节中产生的集群中被确定为彼此相关的两组明显不同的犯罪类型。

结论

这篇文章开头列出的目标是深入挖掘 2016 年伦敦记录的不同类型的犯罪事件,并确定某些类型的犯罪之间是否存在任何关系。

这是通过一些探索性的数据分析方法进行分析的,以确定数据的特征,然后使用主成分分析来降低数据的维度,从而可以使用 K-均值聚类来可视化某些类型的犯罪之间的关系。

进行这一分析是因为我在以前的帖子中获得了一些好奇心,在那里我绘制了不同犯罪的地理位置。我已经开始考虑的下一步是探索不同地区的犯罪概况,并确定某些地区是否可能经历类似的犯罪,以及这些犯罪彼此之间的关系。

探索性数据分析(JHU 课程,课程 4)

原文:towardsdatascience.com/exploratory…

数据科学专业的第四门课程“探索性数据分析”还不错。没有开始任何建模有点令人沮丧,但课程的很大一部分是绘图,这是一项基本技能。一如既往,测验和作业的代码位于我的 github 上。

**第一周回顾:**分析图形和基本绘图系统的基础有点令人失望。测验看起来不太实用,但我希望第二周更实用。任务本身并不太糟糕。

Exploratory Data Analysis Quiz 1

Exploratory Data Analysis John Hopkins Project 1

**第二周回顾:**第二周远比第一周实用,因为它涉及到了 ggplot,这是 R 中主要的绘图库,在工业中广泛使用。小测验其实是让人理解 ggplot 的。

Exploratory Data Analysis Quiz 2 JHU Coursera

**第三周回顾:**终于!令人高兴的是,这种专门化正在进入广泛使用的层次聚类和 K-Means 聚类。不幸的是,没有测验来测试知识,也没有在课程的其他地方涉及。本周没有作业到期,所以这是一个很好的喘息机会。

**第 4 周回顾:**空气污染案例研究是一个非常好的项目,因为它让人们下载解压文件,对数据进行清理和聚合,绘制并导出 png 文件。

Exploratory Data Analysis Project 2 John Hopkins Coursera

请让我知道你是否有任何问题,或者如果你有任何关于我如何改进我的编码技巧!这个约翰·霍普斯金专业甚至帮助了我的工作。下一门课程“可重复研究”的复习已上线。

电子商务数据的探索性数据分析

原文:towardsdatascience.com/exploratory…

发现不同客户和国家的有趣交易模式

(Source)

一般的解释,数据科学无非是利用先进的统计和机器学习技术,利用数据解决各种问题。然而,更容易的是直接应用一些奇特的机器学习算法——瞧!你得到了预测——没有首先理解数据。

这正是探索性数据分析(EDA) (由贾迪普·哈雷定义)的重要性所在,不幸的是,这是作为数据科学过程的一部分通常被低估的步骤。

EDA 如此重要(至少)有以下三个原因:

  1. 确保业务利益相关者提出正确的问题——通常通过探索和可视化数据——并通过彻底的调查验证他们的业务假设
  2. 发现数据中的任何潜在异常,以避免向机器学习模型输入错误的数据
  3. 解释模型输出并测试其假设

这就是了。现在我们已经了解了 EDA 的**“什么和为什么”方面,让我们一起检查一个数据集,并浏览“如何”**,这将最终引导我们发现一些有趣的模式,我们将在下一节中看到。

我们将关注 EDA 的整体工作流程、可视化及其结果。关于技术参考,如果您想对代码有更详细的了解,请随时查阅 Kaggle 上的 我的笔记本。

为了给出一个简要的概述,这篇文章分为以下 5 个部分:

  1. 数据的上下文
  2. 数据清理(又称数据预处理)
  3. 探索性数据分析
  4. 结果
  5. 结论

让我们开始玩吧!

数据的上下文

(Source)

在本帖中,我们将调查从 Kaggle 获得的电子商务数据集。在处理数据集之前,让我们试着理解它是什么,以便更好地理解它的上下文。

简而言之,数据集由来自不同国家的客户的交易数据组成,这些客户从一家总部位于英国的在线零售公司购买独特的全场合礼品。信息概述如下:

  • 公司——总部位于英国并注册的无店铺在线零售
  • 销售产品—主要是各种场合的礼品
  • 客户—大多数是批发商(本地或国际)
  • 交易期—2010 年 12 月 1 日至 2011 年 12 月 9 日(一年)

数据清理

(Source)

我们都知道现实世界中的数据是混乱的(包括 Kaggle!)因此,让我们花一些时间将数据整理成我们需要的格式。下面是将数据集加载到 dataframe 后原始数据的快照。

虽然变量(列名)听起来很直观,但让我们更进一步,理解每个变量的含义:

invoice no(invoice _ num):分配给每笔交易的编号 stock code(stock _ code):产品代码 描述 (description) :产品名称 数量(数量)** :每笔交易购买的产品数量 invoice date(invoice _ date**

注释→在我们的整个分析中,假设单位产品价格遵循相同的货币

Check missing values

到目前为止,一切顺利。我们发现客户 ID 和描述中缺少一些值。因此,具有任何这些缺失值的行将被删除。

Descriptive statistic of data

通过以更具描述性的方式理解数据,我们注意到两件事:

  1. 数量有负值
  2. 单价有零值(自由项?)

有意思…

在这一阶段,我们将只删除具有负值的数量— 本笔记本解释了负值的含义—具有零值的单价将在后面的部分解释。

要计算每次购买的总花费,我们只需将数量乘以单价:

花费金额=数量*单价

最后,我们为每个事务添加几列,包括年、月、日和小时,供以后分析。最终的数据帧将如下所示:

Final dataframe

探索性数据分析

(Source)

订单数量和购买金额最高

Top 5 customers with most number of orders

Top 5 customers with highest money spent

在电子商务世界中,我们经常想知道哪些客户——他们来自哪里——下的订单最多,花的钱最多,因为他们推动了公司的销售。

从结果中我们观察到,大多数订单都是在英国完成的,而荷兰顾客的购物金额最高。

(每月)有多少订单?

Number of orders for different months

总体而言,我们认为该公司在 2011 年 11 月收到的订单数量最多,因为我们没有 2011 年 12 月整月的数据。

(每天)有多少订单?

Number of orders for different days

令人惊讶的是,在整个期间(2010 年 12 月 1 日至 2011 年 12 月 9 日),星期六没有任何交易。由于数据集及其上下文有限,后面的原因留待讨论。

我们还发现了一个趋势,即公司收到的订单数量从周一到周四趋于增加,之后减少。

多少订单(每小时)?

Number of orders for different hours

就时间而言,晚上 8:00 之后直到第二天早上 6:00 都没有交易。

此外,我们注意到该公司在中午 12:00 收到的订单数量最多。原因之一可能是因为大多数顾客在中午 12:00-2:00 的午餐时间购物。

发现单价的交易模式

Descriptive statistics of Unit Price

Boxplot for Unit Price

在我们将注意力转移到单价的零值(自由项)之前,我们制作一个箱线图来检查所有产品的单价分布。

我们观察到 75%的数据单价低于 3.75 美元,这表明大多数产品相对便宜。只有少数单位价格高(同样,我们假设每个单位价格遵循相同的货币)。

嗯……购买免费物品?是的,也许…

Frequency of giving out FREE items for different months

从剧情来看,公司倾向于每个月不定期的免费发放购物品(2011 年 6 月除外)

然而,不清楚是什么因素促成了向特定顾客发放免费物品。可以做更深入的分析来进一步解释。如果你发现了背后的原因,请告诉我!😏

发现每个国家的交易模式

订单数量最多的前 5 个国家

Number of orders in each country (with UK)

Number of orders in each country (without UK)

不出所料,该公司在英国获得的订单数量最多(因为它是一家总部位于英国的公司)。

为了更好地辨别趋势,为了更清楚地比较其他国家,英国被删除。因此,订单数量最多的前 5 个国家(包括英国)如下:

  • 联合王国
  • 德国
  • 法国
  • 爱尔兰
  • 西班牙

花钱最多的前 5 个国家

Total money spent by each country (with UK)

Total money spent by each country (without UK)

由于该公司从英国客户那里收到的订单数量最多,因此很自然地会看到英国客户的购物支出最多。

和以前一样,为了更清楚地比较其他国家,英国被删除。花钱最多的前 5 个国家(包括英国)如下:

  • 联合王国
  • 荷兰
  • 爱尔兰
  • 德国
  • 法国

来自 EDA 的结果

  1. 订单数量最多的客户来自英国
  2. 购买金额最高的客户来自荷兰
  3. 该公司从英国客户那里收到的订单数量最多(因为它是一家总部位于英国的公司)。因此,下单数量最多的前 5 名国家(包括英国)如下→ 英国、德国、法国、爱尔兰(EIRE)、西班牙
  4. 由于该公司从英国客户处收到的订单数量最多(因为它是一家总部位于英国的公司),英国客户的采购支出最多。因此,在购买上花费最多的前 5 个国家(包括英国)如下→ 英国、荷兰、爱尔兰(EIRE)、德国、法国
  5. **2011 年 11 月销量最高。**销售额最低的月份尚未确定,因为数据集包含截至 2011 年 12 月 9 日的交易
  6. 从 2010 年 12 月 1 日到 2011 年 12 月 9 日,星期六没有交易
  7. 公司收到的订单数量从周一到周四呈上升趋势,之后呈下降趋势
  8. 公司在中午 12:00 收到的订单数量最多。可能大多数顾客都是在中午 12:00 到下午 2:00 的午餐时间购买的
  9. 该公司倾向于每月不定期地发放免费购物品(2011 年 6 月除外)。然而,不清楚是什么因素促成了向特定顾客发放免费物品

结论

(Source)

厉害!

通过对数据集执行 EDA,我们发现了一些有趣的结果。当然,结果不止于此。它们总是可以用来验证业务假设(如果有的话),并解释机器学习模型的输出等等!

记住。做 EDA 的时候创意是你的极限。这真的取决于你对商业的理解,提出有趣问题来挑战和验证假设的好奇心,以及你的直觉。

感谢您的阅读。希望通过展示 EDA 的整体工作流程、可视化及其结果,EDA 将变得不那么让你害怕,并且你将更有兴趣在下一次弄脏你的手。

一如既往,如果您有任何问题或意见,请随时在下面留下您的反馈,或者您可以随时通过 LinkedIn 联系我。在那之前,下一篇文章再见!😄

关于作者

Admond Lee 目前是东南亚排名第一的商业银行 API 平台 Staq 的联合创始人/首席技术官。

想要获得免费的每周数据科学和创业见解吗?

你可以在 LinkedInMediumTwitter脸书上和他联系。

[## 阿德蒙德·李

让每个人都能接触到数据科学。Admond 正在通过先进的社交分析和机器学习,利用可操作的见解帮助公司和数字营销机构实现营销投资回报。

www.admondlee.com](www.admondlee.com/)

使用 Python 对日本旅舍进行探索性数据分析

原文:towardsdatascience.com/exploratory…

Photo by Andre Benz on Unsplash

作为一个热爱旅行却又未能再次赢得破纪录的彩票的人(我正瞪着你这个南卡罗来纳州的中奖者),在旅行中找到省钱的方法是至关重要的。对于预算有限或只想体验传统酒店之外的不同体验的旅行者来说,招待所是一个令人惊叹的、负担得起的住宿选择。在与几个人交谈后,令我惊讶的是,有这么多资金有限的人没有住旅馆,甚至没有考虑过。

受到这些对话和我自己最近旅行的启发,我为我的第一篇媒体博客文章进行了一项关于日本旅馆的简单研究。我使用的数据集是 Koki Ando 从 Hostelworld 网站上搜集的,可以在 Kaggle 上找到。

数据清理和基本可视化

今年早些时候,我参加了一个编码训练营课程,以将我的职业生涯过渡到数据分析,我的导师说,几乎总结了我所有未来数据项目的一件事是,数据科学是 80%的数据清理和 20%的分析。这个项目就是如此。让我们从导入我们的依赖项并读取 CSV 文件作为 pandas 数据帧开始。

# Dependencies
import pandas as pd
import matplotlib.pyplot as plt
from scipy import stats
import seaborn as sns
from wordcloud import WordCloud# Read CSV file
hostels_df = pd.read_csv("../raw_data/Hostel.csv", index_col=0)
hostels_df.head()

在处理新数据集时,我喜欢做的第一件事就是检查缺失值并查看列名。

# Check for missing values and column names
hostels_df.count()

Number of existing values for each column

为了可读性,我们将继续重命名这些列,这对我们的第一对可视化来说应该足够了。

# Rename column names
hostels_df.columns = ["Hostel Name", "City", "Min. Price for One Night (yen)", "Distance from City Center", "Summary Score", "Rating", "Atmosphere", "Cleaniness", "Facilities", "Location", "Security", "Staff", "Value for Money", "Longitude", "Latitude"]

让我们用柱状图和饼状图来看看每个城市有多少旅舍。

# Get hostel count for each city
city_group = hostels_df.groupby("City").count()# Reset index
city_group = city_group.reset_index()# Create new dataframe for city name and hostel count
city_group = city_group[["City", "Hostel Name"]]# Rename columns
city_group.rename(columns={"Hostel Name": "Hostel Count"}, inplace=True)# Create bar chart for hostel count by city
city_bar = city_group.plot.bar(x="City", y="Hostel Count", rot=90, legend=None, color="teal", zorder=3)
plt.grid(which="major", axis="y", zorder=0)
plt.xticks(rotation=45)
plt.title("Hostel Count by City in Japan")
plt.ylabel("# of Hostels")

# Create pie chart for hostel count by city
hostel_count = city_group["Hostel Count"]
colors = ["aqua", "lightblue", "gold", "olive", "turquoise"]
city_labels = city_group["City"]
plt.figure(figsize=(8,6))
plt.pie(hostel_count, labels=city_labels, colors=colors, startangle=115, autopct="%1.1f%%")plt.title("Hostel Count Percentage by City in Japan")

大多数旅馆位于东京、大阪和京都,这并不奇怪,因为这些城市是日本的顶级旅游目的地。超过三分之一的旅舍位于东京,大约三分之二的旅舍位于东京和大阪。

人们普遍认为,越靠近市中心的酒店和 Airbnb 房源越贵,尤其是在热门旅游目的地。让我们通过创建一个散点图来看看这是否也适用于这些旅馆。需要进一步清理数据,因为我们只需要距离列中的数值,然后数据集中的所有数值都必须转换为浮点数。

# Use split to obtain numeric value from distance column
hostels_df["Distance from City Center (km)"] = hostels_df["Distance from City Center"].str.split("km").str[0]# Change all numeric values to integer/float data types
hostels_df.apply(pd.to_numeric, errors="ignore")hostels_df[["Distance from City Center (km)"]] = hostels_df[["Distance from City Center (km)"]].apply(pd.to_numeric)

如果我们现在使用数据集绘制散点图,图表的尺寸将非常大...关了。让我们调查一下这里发生了什么。

# Check value count for each unique value for minimum price column
hostels_df.groupby(["Min. Price for One Night (yen)"]).count()["City"]

两家旅馆的最低价格是 1003200 日元(8965.40 美元)一个晚上…让我想一想。这些值是偏离旅馆价格分布的异常值。由于这两个异常值具有相同的值,而且值高得令人怀疑,因此有理由假设某个数字是无意中刮出来的价格值。去除这些异常值是合适的。

# Remove outlier
hostels_reduced = hostels_df[hostels_df["Min. Price for One Night (yen)"] < 8000]

我们现在可以继续创建散点图。

# Create scatter plot
hostels_reduced.plot.scatter("Distance from City Center (km)", "Min. Price for One Night (yen)", alpha=0.6)
plt.tight_layout()
plt.title("Minimum Price for One Night vs. Distance from City Center")
plt.grid()

令人惊讶的是,在靠近市中心的地方,有很多价格较低的旅馆。似乎离市中心的距离并不是影响旅馆价格的重要因素。现在,让我们快速看一下每个城市的平均成本和价格分布。

# Obtain the mean and standard deviation (STD)
price_mean = hostels_reduced.groupby(["City"]).mean()["Min. Price for One Night (yen)"]
price_std = hostels_reduced.groupby(["City"]).std()["Min. Price for One Night (yen)"]# Create new dataframe for mean values
mean_df = pd.DataFrame({"City":price_mean.index, "Price Mean (yen)":price_mean.values})
mean_df = mean_df.set_index("City")# Create bar chart with y error bar 
mean_df.plot(kind="bar", yerr=price_std.values, color="teal", legend=None)
plt.xticks(rotation=45)
plt.grid()
plt.title("Average Minimum Hostel Cost for One Night")
plt.ylabel("Minimum Hostel Cost (yen)")

正如我们在这里看到的,在所有列出的日本城市中,旅馆每晚的平均最低费用约为 2500 日元(22.43 美元),其中东京的价格分布差距最大。

缺失数据和宿舍评级分析

很明显,这些旅馆并不贵,但我们现在必须问问自己,即使价格合理,它们是否真的值得入住。幸运的是,这个数据集包括入住过的顾客对每家旅馆不同类别的平均评分。

为了进行这种额外的分析,我们必须克服每个数据分析师、数据科学家、数据工程师等的障碍。面,那就是缺失的数据。有几种方法来处理丢失的数据,有许多博客文章和文章涉及这个主题(我最近读了一篇我喜欢的文章,是这个的一篇)。当我们之前检查缺失值时,我们可以看到所有具有评级分数的列都缺失相同数量的值。根据这一观察,我们可以假设当一家旅馆缺少一个评级分数时,那么它就缺少所有的分数。但是,我们仍然应该仔细检查,看看丢失的值在哪里。

# Create new data frame for hostel rating analysis and view # of missing data
score_df = hostels_df.loc[:, "Summary Score":"Value for Money"]
score_df = score_df.drop(["Rating"], axis=1)# Create new data frame that only returns rows with missing values
null_df = score_df[score_df.isnull().any(axis=1)]

正如这里看到的,我们最初的假设是正确的。我们知道丢失的值在哪里,现在我们需要决定如何处理它们。为了帮助确定如何处理丢失的数据,我们可以使用 pandas 的 describe 函数为我们提供数据框的基本统计数据的快照。

# Generate descriptive statistics for data frame to help determine what to do with missing data
score_df.describe()

由于分布变化不大,只有一小部分数据集丢失数据,因此删除丢失数据的行不会对后续分析产生太大影响。

# Drop missing values
score_df = score_df.dropna().reset_index(drop=True)

现在我们可以继续我们的可视化和分析。要查看不同评级类别之间的关系,我们可以使用从 Seaborn pairplot 模块生成的 Pearsons R 分数。

# Use seaborn package to find pairwise relationships in dataset
def corrfunc(x, y, **kws):
    r, _ = stats.pearsonr(x, y)
    ax = plt.gca()
    ax.annotate("R = {:.2f} ".format(r), 
                xy=(0.1, 0.9), xycoords=ax.transAxes)

g = sns.pairplot(score_df)
g.map_lower(corrfunc)
g.map_upper(corrfunc)

氛围、清洁度、设施和性价比是与汇总分数正相关度最高的类别,对汇总分数的正面影响最大。接下来,让我们创建箱线图,按城市分析旅舍评级。

# Create Seaborn boxplots
fig = plt.figure(figsize=(12,18))
fig.subplots_adjust(hspace=1.4, wspace=0.3)
for idx, col in enumerate(city_analysis.columns[3:11]):
    fig.add_subplot(3, 3, idx+1)
    sns.boxplot(x=city_analysis["City"], y=city_analysis[col], data=city_analysis)
    plt.xticks(rotation=90)

广岛旅馆的评分紧密分布在平均值附近,这意味着这些旅馆具有最一致的分数。这一数据表明,广岛的旅舍为旅行者提供了最一致的服务和体验。

结论

很明显,日本的旅馆是一个不容忽视的旅游住宿选择。他们离市中心很近,成本低,物有所值,顾客满意,这些都是任何旅行者值得考虑的理由,无论他们是否在预算之内。为了完成这一数据探索,我使用旅行者给出的评价旅馆的形容词生成了一个词云。

# Create list from column values
rating_list = hostels_df["Rating"].tolist()# Convert list to one big string
rating_list = " ".join(rating_list)# Create word cloud
wordcloud = WordCloud().generate(rating_list)
wordcloud = WordCloud(background_color="white", max_words=len(rating_list), max_font_size=100, relative_scaling=0.5).generate(rating_list)
plt.figure()
plt.imshow(wordcloud, interpolation="bilinear")
plt.axis("off")

请随时通过我的联系人留下您的意见和反馈。旅行愉快。

参考资料:

  1. 日本旅社 Kaggle 数据集
  2. 该 EDA 的个人 Github 库

探索性数据分析:2015 年美国警察杀人可视化。

原文:towardsdatascience.com/exploratory…

The Counted 是《卫报》的一个项目,旨在统计 2015 年和 2016 年美国被警察和其他执法机构杀害的人数,监测他们的人口统计数据,并讲述他们是如何死亡的故事。这些数据可以在 kaggle.com 下载。

简要描述:

该数据共包含 1146 个观察值——2015 年美国警察杀人事件的受害者——以及每个受害者的共 14 个特征——属性。

每个受害者的特征记录如下…

  1. id :给每个受害者的唯一号码
  2. 姓名:受害者姓名
  3. 年龄:受害者的年龄
  4. 性别:受害者的性别
  5. 种族/民族:受害者的种族/民族
  6. 武装:受害者有武装吗?如果是,什么类型的手臂?
  7. :事件发生的月份
  8. :事件发生的当天。
  9. :事件发生的年份(2015 年)
  10. 街道地址:发生杀人事件的街道
  11. 城市:杀戮之城
  12. 状态:事件发生的状态
  13. 经度:杀戮的经度(坐标)
  14. 纬度:杀戮的纬度(坐标)
  15. 分类:受害者的死因,即枪击/车辆
  16. 执法机构:负责攻击的办公室

可视化:

性别:毫不夸张地说,在美国,大多数被警察杀害的受害者都是男性,因为这占了所有记录数据的 95%以上。

**年龄分布:**年龄呈长尾正态分布,25-35 岁年龄组出现次数最多。

0-10 之间的小酒吧是杰里米·马迪斯,一个 6 岁的男孩,他没有武装,在 2015 年 11 月被枪杀。他是“白人”,负责谋杀的执法机构是洛杉矶第二区执法官。

种族/民族:被杀的‘白人’比所有其他民族/种族加起来还多。2015 年,超过 50%被美国警察部队杀害的人是白人,阿拉伯裔美国人记录的受害者人数最少,不到记录的总数据的 1%。

**嫌犯持有武器?:**超过 50%的受害者持有枪支或刀具。超过 30%的记录数据没有使用火器,或者使用了非致命火器,或者不知道他们是否携带武器。

死亡性质:几乎 90%的受害者都是被枪杀的,死因和死亡性质都很清楚。其他形式有:羁押期间死亡,被车辆、泰瑟枪和其他物体击中(不到 1%)。

**每月杀戮:**杀戮在 7 月份达到顶峰,有超过 125 名受害者,在 6 月份达到最低点,有 80 名受害者。

**各州的杀人事件:**在记录在案的 51 个州中,有 10 个州的杀人事件超过 50%,其中加利福尼亚州以 211 起记录在案的事件高居榜首,占总数据的 18.4%。

基于武器的种族/民族:这显示了基于武器杀死每个民族的比率。

几乎 50%被杀的“白人”携带枪支,而阿拉伯裔美国人携带枪支的受害者比例最低。此外,不到 30%的阿拉伯裔美国人被枪杀。

基于种族/民族的死亡性质:正如我之前所说,几乎 90%的受害者是被枪杀的。所有记录在案的阿拉伯裔美国人都是被枪击致死的,这是唯一一个 100%死亡的群体。美洲原住民在羁押中死亡的比例最高,亚太裔被泰瑟枪致死的比例最高。请注意,这些比率是相对于每组记录的总数而言的。

2015 年美国警察杀人的探索性数据分析到此结束。

数据和代码可以从github.com下载。

留下你的评论、问题和你想让我添加到帖子中的内容,别忘了推荐。

干杯

Ede

使用 Jupyter 笔记本对熊猫进行探索性数据分析

原文:towardsdatascience.com/exploratory…

A beautiful red panda doing his thing. If you look close enough at that bamboo, you can see the commas. Source: pixabay

作为一名数据科学家,我花了大约三分之一的时间查看数据,并试图获得有意义的见解。这些是我最常用的工具。

你是数据科学闪亮世界的新人吗?探索性分析之类的花哨词汇会吓到你吗?别担心,我是来帮你在这个疯狂的世界里导航的。

今天我们将看看两个很棒的工具,紧跟我在 github 项目上上传的代码。一个是 Jupyter 笔记本,一个是 Python 框架叫 Pandas。

如果你是 Python 的新手,我建议你在阅读这篇文章之前先阅读一篇语言教程。我不会用太晦涩的东西,但也不会停下来解释列表理解。我推荐 O'Reilly 的书“用 Python 从零开始的数据科学”,但是任何其他教程都可以。

首先来说说 Jupyter 笔记本。如果您曾经使用过 Python 控制台,您可能已经意识到它是多么有用。通过检查输出是否符合您的预期来轻松调试功能(我不是说这是测试的良好替代品,但是谁没有这样做过呢?),运行一次昂贵的进程,这样就可以将它们加载到内存中,然后就可以开始在它们的输出上测试其他东西,拥有一个交互式环境有很多好处。

Just picture this but, like, less round, and a bit more squarish. Rectanglish? Is that a word? Source: Pixabay

Jupyter 笔记本也是同样的环境,使用类固醇。这就像运行 Python 控制台工具进行读取-评估-打印循环,但是界面很可爱,并且能够记录和保存您测试的内容。这非常方便,例如,如果你正在写一篇关于 Python 工具的中型文章!

要安装 Jupyter,您只需运行以下 pip 命令:

python -m pip install jupyter

如果您在 Linux 上,您应该使用这个命令:

pip3 install --user jupyter

(注意:本文之前的版本推荐使用 sudo 进行安装。一位 善良的读者 告诉我这是 不安全的,实际上是非常糟糕的做法 ,因为它给了 setup.py 程序 sudo 特权,这通常是不好的,会允许恶意代码运行。你知道的越多!)

然后要运行程序,打开你的终端,在你想要存储笔记本的文件夹中,然后运行

jupyter notebook

是的,就这么简单。该命令将初始化您计算机中的服务器,并将您最喜欢的浏览器重定向到该服务器。从那里,只需使用 GUI 创建一个新的笔记本,并打开它。使用 + 按钮创建一个新的代码块,使用**“剪切”按钮删除一个代码块。通过将光标放入代码块并点击“Shift+Enter”,每个代码块都可以独立运行(尽管不是并发的)。**

既然你已经知道了如何运行 Jupyter 笔记本,那么克隆并拉动我刚刚链接的项目将是明智的。你所要做的就是点击克隆绿色按钮,获得链接,然后开始

git clone *link*

现在有趣的部分,让我们来谈谈熊猫。Pandas 是一个开源 Python 框架,由 PyData 社区维护。它主要用于数据分析和处理,主要用于操作 CSV 文件。

They keep making them cuter and cuter. It’s like each hair is a commit. Source: pixabay

如果您不知道,CSV 只是一种将数据编码为序列(列)的文件格式,其中每个对象(行)都有一个值。文件中的每一行都是一行,每一个值都用逗号与前一个值隔开,这样就形成了逗号分隔值文件。第一行是为标题保留的,包含每一列的名称。

在本教程中,我们将首先生成一个“员工数据”的模拟数据集,该数据集来自一家非常冷的公司,它只存储每个员工的姓名和工资。数据集将被生成为 CSV 文件,如 generate_people.py 程序所示。

我们首先要做的是使用一些常规的 Python 来生成字典,每个字典代表一个人。如果你更熟悉 JavaScript,每个字典只是一个 JSON,有“名字”、“姓氏”和“薪水”字段。

然后,我们生成一个 Pandas Dataframe:它们在 CSV 文件上的抽象,其中每一列都是 Pandas 系列(可通过一些矢量化运算进行迭代)。

就是这样。如您所见,Dataframe 是作为字典列表和列名的(可选)参数生成的。将它导出到 CSV 文件就像调用 to_csv 方法一样简单,只需将文件名作为唯一的参数。

现在,对于读取和处理部分,让我们运行“分析数据集”笔记本,它也在存储库中。我建议你打开它,把它放在边上阅读这篇文章,但我会添加相关的片段,以防你在手机上或只是觉得有点懒。

我们首先将 CSV 文件作为数据帧导入,其中包含以下行:

**import** **pandas** **as** **pd** 

df = pd.read_csv("random_people.csv")

对一些 k 使用 df.head( k )会让我们看到数据帧的第一个 k 行,由于 Jupyter 的魔力,这些行看起来会非常漂亮。这是了解数据的一种简单方法(也是开始处理数据时的主要调试工具)。

为了只看到系列中的一个,我们所要做的就是将其作为字典字段进行索引:

df[“*series name*”]

您可以调用在任何其他数据分析工具(如 SQL)中使用的任何常用聚合作为方法。如果我想直观地了解一个系列,我最喜欢的方法是 value_counts 方法,它显示每个值,以及它在系列中出现的次数,按照幽灵数量的降序排列。其他选项包括平均值总和计数中位数。

使用这些方法而不是手动打开 CSV 并自己运行这些函数的实现的一个优点是,其中大多数都是矢量化的(它们使用 SIMD 指令和其他黑魔法),这意味着它们将快大约 10 倍。这也适用于加法、减法、除法和乘积,它们可以很容易地通过一个系列进行广播。

然而,有时你会想把一个功能应用到一个不那么琐碎的系列中,也许制作熊猫的人没有真正考虑过你的用例。在这种情况下,您可以定义自己的函数,并对您想要修改的系列使用 apply 方法。这将是一个有点慢,但仍然运行简单的功能顺利。它相当于 Python 的原生地图,将为你的处理增加大量的灵活性。

如果您想过滤数据帧,只保留保持某个属性的行,您可以这样做:

df_high = df[df["salary"]>=6000]

其中*" df[" salary "]>= 6000 "*可以用任何返回一系列布尔值的东西来切换,或者" *df["any series name"]。应用(f)"*使得 f 为布尔函数。但是,请确定该系列与您要过滤的系列具有相同数量的元素。

最后,请记住,这些过滤后的数据帧在默认情况下是只读的,因为它们是通过引用生成的。如果你想改变它们,只需添加*。loc"* 在数据帧名称之后,第一个括号之前,如下所示:

df_low= df.loc[df["salary"]<6000]

您也可以将列名作为第二个参数添加到 loc 中,以保留单个过滤序列,而不是整个数据帧。这是一个直接来自笔记本的例子。

df_low= df.loc[df["salary"]<6000,"salary"]

请注意,后者是通过引用制作的切片,因此您对它们所做的任何更改也将在原始数据帧上进行。为了避免这种情况,只需在行尾调用 copy 方法。

就是这样,这是一些关于熊猫和笔记本的初步速成课程。我希望你会觉得它很有用,如果你觉得还有什么我应该介绍的,或者有什么你想问我的,你知道在哪里可以找到我。我也欢迎任何批评,不管是好是坏,因为这是我第一篇关于媒体的文章,我还在适应。

如果你想在这方面展开,这里有一篇我写的关于如何在并行上做数据分析的文章。

有一本 O'Reilly 的书我很喜欢,当我开始我的数据科学之旅时,我发现它非常有用。用 Python 从零开始叫 数据科学,大概也是我得到这份工作的一半原因。如果你读到这里,你可能会喜欢它!

附:关注我以获得更多的 Python 教程、技巧和诀窍,如果您发现本文有用,请 支持我的写作

使用熊猫的真实数据集的探索性统计数据分析

原文:towardsdatascience.com/exploratory…

Your Data Science journey has just begun. source: Pixabay

有时候,当面临一个数据问题时,我们必须首先深入数据集并了解它。它的属性,它的分布——我们需要沉浸在这个领域中。

今天我们将利用 Python 的 Pandas 框架进行数据分析,利用 Seaborn 进行数据可视化。

作为一个审美感很差的极客程序员,我发现每当我需要理解某个观点时,Seaborn 都是一个非常棒的可视化工具。

它在幕后使用 Matplotlib,但用默认的样式值设置图形,使它们看起来比我能做的漂亮得多。我们将查看一个数据集,我将尝试让您直观地了解如何查看不同的要素。谁知道呢,也许我们真的会从中得到一些启示!

没有鸡蛋就做不成煎蛋卷:数据集。

在这个分析中,我使用了 120 年奥运数据集,你可以点击链接下载或阅读更多信息。

我从 Kaggle 免费下载了它,如果你需要一个数据集来尝试一些新的机器学习算法,温习一些框架的 API,或者只是找点乐子,这是一个很棒的网站。

我将只使用“运动员事件”CSV 文件,该文件包含自 1900 年以来每届奥运会上的每名参赛者,以及他们的出生国、是否获得奖牌等。

一个有趣的事实是,奖牌栏有 85%是空的,所以平均来说只有大约 15%的奥运会运动员真正拿到了奖牌。除此之外,一些运动员还获得了不止一枚奖牌,这表明,在已经很少的奥运级别运动员中,有更少的人真正获得了奖牌。更值得称赞的是他们!

开始分析:数据集是什么样的?

首先,在我试图收集任何见解之前,我想对数据集有一个更好的直觉。有多少数据丢失了?有多少不同的列?我喜欢从这些问题开始。

我使用 Jupyter 笔记本进行分析,但是我将为我运行的每一行相关代码添加代码片段,以便您能够理解。

尽管如此,我还是在这个库中提供了这个笔记本,以防你自己想看一眼,想有一个起点。

我首先要做的是加载熊猫的数据,并检查它的大小。

在本例中,数据集有 15 个不同的列,总共 271116 行!超过 27 万运动员!不过,我想知道实际上有多少不同的运动员。还有,他们中有多少人真的获得了奖牌?

为了检查这一点,我将首先列出对数据集调用“list”函数的数据集的行。我看到我们有许多有趣的特点。

我能想到的一些事情是,我们可以看看奥运会运动员的平均身高和体重,或者按照运动项目来划分。我们也可以看到这两个变量的分布取决于性别。我们甚至可以看到每个国家获得了多少奖牌,以及整个二十世纪文明的兴衰。

可能性是无限的!但首先让我们解决一些难题:我们的数据集有多完整?

对一个序列使用 Pandas 的计数方法,我可以得到非空行的数量。但是查看 shape 属性,我实际上可以看到行的总数,不管它们是否为空。

之后就是减法和除法的问题了。我们看到只有四栏是不完整的:身高、体重、年龄和奖牌。

任何时候运动员没有真正赢得奖牌,奖牌都是不完整的,所以它不会太满。然而,在体重、身高和年龄方面,我们面临着相当大的挑战。

我试图按不同年份过滤这些数据,但随着时间的推移,不完整性似乎是一致的,这使我认为可能有几个国家不提供这些关于他们运动员的数据。

开始我们的实际分析:奖牌是怎么回事?

关于这个领域,我们问的第一个问题是,自 1900 年以来,有多少人真正获得了奖牌?以下片段回答了这个问题:

正如你所看到的,在过去的 120 年里,几乎有 135,000 名不同的人参加了奥运会的比赛,但是只有 28,000 多一点的人至少获得了一枚奖牌。

这大约是五分之一,还不算太糟。然而,一旦你考虑到许多人实际上在不止一个类别中竞争,这就不那么乐观了。

现在我们来看看,在这 120 年中,我们实际上获得了多少枚奖牌?

不出所料,奖牌分布几乎是一致的:获得的金牌、银牌和铜牌数量几乎相同。

然而,总共颁发了近 39,000 枚奖牌,这意味着如果你属于获得任何奖牌的前 20%的运动员,你平均应该获得不止一枚奖牌。

按国家分配呢?为此,我们将运行以下代码片段:

使用该函数,我们可以获得某个国家获得的每种类型的奖牌数量,而通过获取熊猫头像数据帧,我们可以看到获得奖牌最多的国家。

有趣的是,第二个获得更多奖牌的国家仍然是苏联,尽管它已经不存在近 20 年了。

在所有类别中,第一名是美国,第三名是德国。我还调查了我的两个国家阿根廷和克罗地亚,惊讶地发现克罗地亚已经获得了 58 枚金牌,尽管它从 1991 年才出现(奥运会年份是 1992 年)。

找到一个脚本来获取一个国家参加奥运会的不同年份将作为读者的一个练习——我打赌你可以做到这一点!

女性参与

我想到的另一件有趣的事情是,在整个世纪的奥运会中,女性代表是什么样的?这个片段回答了这个问题:

我很惊讶地看到女性早在 1900 年就开始参加奥运会了。然而,从历史上看,奥运会上每有一名女性,就有三名男性。对 1900 年女性参加奥运会感到惊讶,我决定随时核查她们的人数。我终于用了 Seaborn!

我们可以看到,在过去的几十年里,女性的参与率一直在快速上升,从几乎为零开始,到数千人。然而,她们的参与率真的比男性增长得快吗?或者这仅仅是世界人口的问题?为了解决这个问题,我制作了第二张图表:

Female participation (Orange) vs male (Blue) over time.

这一次我们可以清楚地看到一种模式是如何出现的:女性实际上正在以绝对的数量接近男性!另一件有趣的事情出现了:看到下面右边的那些小点了吗?我想那是冬季奥运会!无论如何,女性代表的情况看起来相当乐观,尽管还没有哪一年女性参与者多于男性。

杂集分析:身高和体重

我花了很长时间查看身高和体重图表,但没有收集到任何有趣的结论。

  • 对于大多数运动来说,两者都是明显的正态分布
  • 在我检查过的所有运动项目中,男性总是比女性更重更高
  • 唯一有趣的变化似乎是一种性别与另一种性别的差距有多大,这取决于运动。

如果你有任何有趣的想法可以用体重和身高来分析,请告诉我!我还没有足够深入地按每项运动进行分组,因此可能会有一些见解。今天就到这里,我希望你对这个分析感兴趣,或者至少你了解了一些关于熊猫或数据分析的知识。

我在 GitHub 上提供了这个笔记本,这样你就可以分叉这个项目,做你自己的分析,然后做一个拉取请求。

你当然可以把所有的功劳都拿走!希望你在图表和视觉效果方面比我强。

第二部关于体育的真知灼见 已经有了。

有一本 O'Reilly 的书我很喜欢,当我开始我的数据科学之旅时,我发现它非常有用。用 Python 从零开始叫 数据科学,大概也是我得到这份工作的一半原因。如果你读到这里,你可能会喜欢它!

请考虑 用一点点捐款支持我的写作习惯

如果您还没有,请关注我的 Medium 和Twitter以获取更多针对软件开发人员和数据科学家的教程、提示和技巧。

如果你真的喜欢这篇文章,就和朋友分享吧!

也可上www . data stuff . tech

探索原始数据并从中获取价值:Splunk 简介

原文:towardsdatascience.com/explore-and…

您刚刚接触了一些原始数据文件(json、csv 等)。现在会发生什么?你如何理解它?

你打开一个控制台,开始使用 less、grep、jq 等工具。开始的时候很棒,但是…除了基本的,很难做更多的事情。

这听起来熟悉吗?太好了!请继续阅读,了解 Splunk 如何帮助您。

安装 Splunk 企业版

让我们从在您的计算机中安装 Splunk Enterprise 开始。安装 Splunk 非常简单,安装包几乎适用于所有平台:OSX/Linux/Windows。在此下载软件包,并按照安装说明进行操作。

Splunk 企业?但是...它有免费许可证吗?是啊!

指标 500 MB/天。(…)60 天后,您可以转换为永久免费许可证或购买 Splunk 企业许可证,以继续使用专为企业级部署设计的扩展功能。”

https://www . splunk . com/en _ us/download/splunk-enterprise . html

虽然在介绍和个人使用的范围内,在您的机器上进行本地安装是很好的,但我强烈建议您在开始更广泛地使用 Splunk 时,尽快转移到正确的 Splunk 部署(内部部署或云中部署)。

Splunk Enterprise 7.1.3 Web Interface

如果您的本地安装进展顺利,您将会看到一个类似于上面截图的 web 界面。耶!

导入原始数据

本文适用于任何类型的原始数据- Splunk 与其生成一些无意义的虚拟测试数据集,我决定搜索一个有趣的真实世界数据集,作为开放数据

赫尔辛基公共交通(HSL)—2016 年 10 月期间每个车站的客运量

我从赫尔辛基地区交通局(HSL)找到了一个有趣的数据集,其中包含赫尔辛基地区每个车站的乘客量。数据集(此处可用)包含 2016 年 11 月期间每天的平均乘客人数,从乘客旅行卡系统中收集。虽然我对这个特定的数据集只有可用的旧数据(2016 年 11 月)感到有点失望,但我非常惊讶地发现 HSL (以及芬兰公共当局总体而言)有相当大的公开可用的数据目录(【www.opendata.fi/en】)。不错!

通过下载这个特定的 HSL 数据集——我选择了 GeoJSON APIJSON 数据格式——您将获得一个名为HSL % 3 an _ nousijam rt . geo JSON的原始数据文件

Raw data from HSL

正如你所看到的,在顶层我们有一个单一的 FeatureCollection ,其中包含了所有的 Feature 事件。

由于我们只关心事件(不需要高级 FeatureCollection 数组部分),我们可以通过删除 JSON 数组并把所有的特性事件放到一个新文件(HSLvolumes.json)中来清理数据。

将数据添加到 Splunk

从本地硬盘中的文件向 Splunk 添加新数据非常简单。让我们前往 Splunk 并使用 UI 选项来实现这一目的。

Splunk > Add data

单击添加数据选项,然后选择上传(从我电脑中的文件)

Splunk > Add data: Select Source

将出现一个逐步指南。让我们从选择原始数据文件开始。在我的例子中,我将使用包含特性事件的 HSLvolumes.json 文件。

Splunk > Add data: Set Source Type

在获得您的数据后,Splunk 将尝试自动"理解""您的数据,并允许您调整和提供有关数据格式的更多细节。

在这个特殊的例子中,您可以看到它自动将我的数据识别为 JSON (Source type: _json),总体来说事件看起来不错。但是,有一些警告说它无法解析每个事件的时间戳。

为什么?Splunk 的核心是事件处理,时间至关重要。基于您正在索引的事件,Splunk 将自动尝试查找时间戳。由于我们的数据没有时间戳字段,Splunk 将使用每个事件被编入索引时的当前时间作为事件时间戳。

有关 Splunk 时间戳分配如何工作的深入解释,请查看此 Splunk 文档页面。

Splunk > Add data: Save Source Type

因此,在时间戳部分,我们将通过选择当前来实施这一点,因为我们修改了 _json 源类型,所以让我们点击“另存为”并根据我们的数据源对其进行命名(例如 hslvolumesjson)。

Splunk > Add data: Input Settings

在此部分中,我们需要选择要在哪个 Splunk 索引中存储此数据。为不同类型的数据创建单独的索引是一个好习惯,所以让我们创建一个新的索引。

Splunk > Add data: New Index

选择您的索引名称,然后单击保存。我们可以保留其他字段的默认值。

仔细检查是否选择了新索引。点击审核提交 & 开始搜索即可。

有关在 Splunk 中获取数据的更深入的解释,请查看 Splunk 文档:dev.splunk.com/view/dev-gu…

第一眼看你的数据:探索

单击开始搜索按钮后,您将被引导至 Splunk 搜索面板。

这个观点里有很多有趣的东西。如果你以前从未使用过 Splunk,你可能会感到有点不知所措。请允许我重点介绍一些领域,并为您拆分视图。

在左上角,你会找到你当前所在的 Splunk 应用(默认:搜索&报告)和面板(默认:搜索)。

在它的正下方,您会发现 Splunk 搜索栏,其中有一个查询(乍一看)可能有点复杂。给定我们的简单用例,完全相同的搜索结果将出现在查询中: *index="hslnov2016 "。*我们将在下面探索查询语言。

在右上角,你会发现时间选择器(默认:所有时间)。这允许您选择搜索的时间范围。因为我们的时间戳被设置为索引当前时间,这在这里没有用。

在左下角,你会发现有趣的字段。这些是 Splunk 能够从您的数据中自动提取的字段。

最后,剩下的下半部分将显示您的搜索查询结果事件。在这种情况下,所有的索引结果都会出现。

酷,现在怎么办?

在 Splunk 中浏览数据时,我最喜欢首先使用的选项之一是“有趣的字段”面板。通过点击任何一个领域,你都可以很快获得有价值的见解。

在这种情况下,通过选择字段 properties.nimi_s ,我们能够快速了解什么是字段顶值,即在大多数事件中出现的 HSL 站名称。[对任何赫尔辛基地区的居民来说都不奇怪的是,劳塔廷托里(中央火车站)和坎皮在最上面:]

获得答案:统计和数据整理

Splunk 搜索和查询语言既强大又庞大,但是通过一些简单的命令和少量经验,您可以快速获得一些有价值的答案。

如果您从: index=yourindex | command, Splunk 将为您提供关于每个命令的自动完成、指导和解释。

由于每个事件都包含单个车站的日平均乘客量,假设我们想知道每个车站的总乘客量是多少。我们如何做到这一点?

轻松点。我们可以快速使用 stats 命令,将所有的日平均值(properties.nousijat)相加,并按电台名称(properties.nimi_s)汇总这些结果。

额外收获:通过获得 5071 个结果,我们还知道了数据集中的电台总数。不错!

想知道前几名或者后几名的 X 站怎么办?

通过追加到我们之前的查询: | sort -volume | head 20 ,我们立即得到了这个问题的答案。

我们使用排序来获得较高音量的结果,即降序(对于较低,即升序,将是排序*+音量*)和来仅过滤出前 X 个结果

通过不同的 Splunk 查询探索您的数据并获得有价值的答案。

仪表板和可视化

一旦您开始掌握 Splunk 搜索的诀窍并保存了几个最有趣的查询,您就可以创建您的第一个仪表板并以不同的方式可视化您的数据。

转到仪表板部分,点击*创建新仪表板。*为您的仪表板命名,并添加您的第一个面板。

使用与前面相同的查询,我添加了一个简单的柱形图面板。

很快,我们的前 20 个车站在客流量方面变得非常非常不同。Kamppi 和 Rautatientori 的客流量是排名前五的其他三个车站的两倍。当我们看剩下的 15 个站(前 20!)我们得到 3 倍的体积。

在这一点上,我决定添加两个额外的新面板…

左边是每站旅客量前 50 名(与*|头 50* 查询相同)和一个简单的可视化表格。

右边是每个车站的**乘客量(最下面一排,不到 30 名乘客)。**带饼图和查询:index = " HSL nov 2016 " | stats sum(properties . nousijat)as volume by " properties . nimi _ s " | sort+volume | search volume<30 | stats count by volume

我决定只包括客流量少于 30 人的车站。而且我很惊讶的看到有这么多车站(1827)0 乘客。

最后一个面板…

由于我的数据集包括每个站点的地理坐标(纬度和经度),我决定再添加一个面板(类型地图)。为此,我扩展了我的 Splunk 并安装了一个名为 Maps+ for Splunk 的第三方可视化工具。

您可以通过浏览现有的可视化类型并转到“查找更多可视化”来完成同样的操作

Splunk 具有内置的地图可视化功能。为什么不用它呢?

我一开始确实使用了内置地图,但我发现了一些局限性:你不能在城市级别缩放,而且我的 Splunk 查询更复杂。Splunk 的 Maps+对我来说是一个明显的赢家。

面板 Splunk 搜索查询为:index = " hslnov 2016 " | spath path = " geometry . coordinates { 0 } " output = longitude | spath path = " geometry . coordinates { 1 } " output = latitude | stats first(latitude)为纬度,first(longitude)为经度,first(properties.nimi_s)为描述,sum(properties.nousijat)为标题 by " properties . nimi _ s " | sort-title |搜索标题> 0

需要使用 spath 进行初始转换,因为纬度和经度在同一个字段中(多值 json 类型),因此我必须将它们“分割”到不同的字段中。

这种可视化(Maps+ for Splunk)只要求您在一个表中有一些带有特定标签名称的字段。更多详细信息,请查看位于github.com/sghaskell/m…的项目文件。

base_search | table latitude, longitude [ description| title | (...)

我发现这张地图真的很好,很有帮助。我可以通过悬停在某个车站上来快速查看该车站的乘客量。

我希望这篇文章对你有用!请分享您的反馈和想法。

伸出手,在 TwitterInstagram 上关注

布鲁诺

探索机器学习模型,无需编写代码或安装软件包

原文:towardsdatascience.com/explore-mac…

利用 scikit-learn 的模型并通过 API 公开它们的行为

“forced perspective photography of bus on road” by Muneeb Syed on Unsplash

机器学习|人工智能|深度学习——它们是下一件大事,我知道,我们都知道。那为什么要等?当机会就在我们面前时,是什么阻止我们抓住它?也许是因为从日常琐事中抽身出来,做一些新的事情需要一点努力。

早在我开始探索机器学习及其应用的时候,我的很大一部分时间都浪费在猜猜是什么上了。—搞清楚和研究 1。要使用的语言,请按 2。框架选择,3。安装(以正确的方式安装)和设置开发环境。在所有的戏剧之后,我开始实际的发展。

然后出现了构建基于云的应用程序的想法,任何人都可以开始利用机器学习的力量,它会处理其余的事情。当然有很多可用的服务——其中一个是 BigML ,但是为了我自己的实践和满足,我开始发展这个想法。

一夜之间成了我梦寐以求的项目;一个独立的 web 应用程序,可以通过 API 学习和测试机器学习模型。没有更多的编码专业知识,安装问题,兼容性问题,需要一个强大的钻机。甚至邮递员也能跑。

示例用法

获取当前用户的 JWT

POST /api/login/
Content-Type: application/json
{
  "username": "username",
  "password": "password"
}

Response
{
  "token": "abcd12345"
}

考虑下面的问题陈述

你是一名物理系学生,参加了期末考试,迫不及待地想知道你的最终分数。但是给你打分的老师非常严格。他有一个计算总分的公式但是没人知道(就是 0.5 * Paper_1 + 2 * Paper_2 + Paper_3)。你有一份你朋友的考试成绩和期末成绩的清单,并想计算你的分数。

用给定的输入启动线性回归器

POST /api/model/
Content-Type: application/json
Accept: application/json
Authorization: JWT abcd12345

{
  "model_path": "sklearn.linear_model.LinearRegression",
  "action": "new_model",
  "name": "Compute Final Score",
  "input_x": [[95, 87, 69], [99, 48, 54], [85, 57, 98], [90, 95, 91]],
  "input_y": [291, 200, 254, 326]
}

Response
{
  "status": "Trained",
  "model_id": "randommodelid"
}

使用这个训练好的模型来预测你的分数

POST /api/model/
Content-Type: application/json
Accept: application/json
Authorization: JWT abcd12345

{
  "action": "predict",
  "model_id": "randommodelid",
  "input_x": [[90, 95, 91]]
}

Response
{
  "status": "OK",
  "prediction": [
      326
  ]
}

这是一个正在开发的活动项目。目前服务于线性回归,SVM 和 KNN。更复杂的型号即将推出…

更新(2018 . 9 . 30):重构代码,动态获取用户在 API 中提到的模型类。理论上,scikit learn 中的所有模型现在都可以测试。

model_path参数决定调用哪个模型。您可以尝试各种其他方法,包括但不限于:

  • sklearn.svm.SVR
  • sklearn.svm.SVC
  • sklearn.linear_model.BayesianRidge

还有很多很多…

我期待着志同道合的人,就像我一样,谁愿意帮助或有助于该项目。该项目托管在 Github 上,我很乐意接受一些拉请求。😃

使用 R 探究两个变量

原文:towardsdatascience.com/explore-two…

本文是 R-One Variable 中的探索性数据分析的继续,其中我们讨论了伪 facebook 数据集的 EDA。

在本文中,我们将基于 Udacity 策划的伪 facebook 数据集,了解数据聚合、条件均值和散点图。

现在我们将同时考察两个连续变量。散点图是检验两个变量之间关系的最佳图之一。让我们在所有用户的年龄和朋友数量之间画一个散点图。

qplot(age,friend_count,data=pf)

运筹学

ggplot(aes(x=age,y=friend_count),data=pf)+
  geom_point()

当我们使用 geom_point()时,散点图是默认图。

Plot 1 Scatter Plot — Friend Count Vs Age

从上面的图中,以下两个观察结果是相当明显的:

  1. 年龄不到 30 岁的人有成千上万的朋友
  2. 69 岁接近 100 岁有一条垂直线,似乎不正确。

我们知道持有脸书帐户的最低年龄要求是 13 岁,90 岁似乎是一个相当大的年龄,所以让我们将 x 轴限制在 13 到 90 岁之间。

ggplot(aes(x=age,y=friend_count),data=pf)+
  geom_point()+
  xlim(13,90)

Plot 2 Scatter Plot — Age Vs Friend Count (x axis restricted)

上面的地块比地块 1 好,但是下部人满为患,很难统计那个区域的点数。

geom_point 的 Alpha 美学可用于设置地块过度拥挤区域中点的透明度级别。

对于我们的场景,让我们设置 alpha 值为 1/20,这意味着需要 20 个点来完成一个黑点。

ggplot(aes(x=age,y=friend_count),data=pf)+
  geom_point(alpha=1/20)+
  xlim(13,90)

Plot 3 Scatter Plot — Age Vs Friend Count — Alpha Aesthetics

此外,将抖动添加到我们的绘图中,以分散过度绘制的点。

抖动是指向数据中添加少量随机噪声。

ggplot(aes(x=age,y=friend_count),data=pf)+
  geom_jitter(alpha=1/20)+
  xlim(13,90)

Plot 4 Scatter Plot — Age Vs Friend Count — Jittering

从上面的情节可以推断,25 岁以下的用户绝大多数朋友都在 1000 以下。这个推论与我们从情节 1 的第一个观察相矛盾,象征着 EDA 的重要性。

更进一步,让我们使用 coord_trans 进行可视化。

ggplot(aes(x=age,y=friend_count),data=pf)+
  geom_point(alpha=1/20)+
  xlim(13,90)+
  coord_trans(y = "sqrt")

运筹学

ggplot(aes(x=age,y=friend_count),data=pf)+
  geom_point(alpha=1/20,position=position_jitter(h=0))+
  xlim(13,90)+
  coord_trans(y = "sqrt")

Plot 5 Scatter Plot — Age Vs Friend Count — Use coord_trans

有了这个剧情,看好友数,条件,年龄就容易多了。 从数据可视化到现在,我们可以观察数据集中的每一个点。尽管如此,从这样的显示中还是无法确定重要的量,如平均值和中间值。有时我们想了解均值或中值如何随其他变量而变化。让我们创建一个表格,为我们提供所有年龄的平均值和中间值。为此我们将使用D Plyr。【Dplyr 的基础教程可以在这里这里找到。同样可以使用 lapply、tapply 和 split 等函数创建。

age_groups<-group_by(pf,age)
pf.fc_by_age<-summarise(age_groups,
          friend_count_mean=mean(friend_count),
          friend_count_median=median(friend_count),
          n=n())
pf.fc_by_age<-arrange(pf.fc_by_age,age)
head(pf.fc_by_age)

上面的代码产生一个tible,输出如下所示:

A tibble: 6 x 4
    age friend_count_mean friend_count_median     n
  <int>             <dbl>               <dbl> <int>
1    13              165\.                 74    484
2    14              251\.                132   1925
3    15              348\.                161   2618
4    16              352\.                172\.  3086
5    17              350\.                156   3283
6    18              331\.                162   5196

Tibbles 是数据帧的现代版本。他们保留了经受住时间考验的功能,放弃了过去方便但现在令人沮丧的功能。更多信息可以在找到

使用上面的表格,我们将绘制年龄与朋友数量平均值的散点图。

ggplot(aes(age,friend_count_mean),data=pf.fc_by_age) +
  geom_point()

Plot 6 Scatter Plot — Age Vs Friend Count Mean

用线将分散的点连接起来。请遵守几何线。

ggplot(aes(age,friend_count_mean),data=pf.fc_by_age) +
  geom_line()

Plot 7 Line Plot — Age Vs Friend Count Mean

酷!让我们在图 5 中绘制之前的散点图。

ggplot(aes(x=age,y=friend_count),data=pf)+
  geom_point(alpha=1/20,
             position=position_jitter(h=0),
             color='red')+
  xlim(13,90)+
  coord_trans(y = "sqrt")+
  geom_line(stat='summary',fun.y=mean)+
  geom_line(stat='summary',fun.y=median,linetype=2,color='blue')+
  geom_line(stat='summary',fun.y=quantile,fun.args=list(probs=0.9),color='blue')

Plot 8 — Overlaying summaries with raw data

从上面我们可以推断,超过 1000 个朋友是很少发生的。要进一步缩放,我们可以使用 coord_cartesian。

ggplot(aes(x=age,y=friend_count),data=pf)+
  coord_cartesian(xlim=c(13,70),ylim=c(0,1000))+
  geom_point(alpha=1/20,
             position=position_jitter(h=0),
             color='orange')+
  geom_line(stat='summary',fun.y=mean)+
  geom_line(stat='summary',fun.y=median,linetype=2,color='blue')+
  geom_line(stat='summary',fun.y=quantile,fun.args=list(probs=0.9),color='brown')

Plot 9— Overlaying summaries with raw data — Zoomed

接下来,作为探索性数据分析系列的一部分,我们将通过 R 探索多个变量。

探索神经网络的激活函数

原文:towardsdatascience.com/exploring-a…

在这篇文章中,我想更多地关注我们在神经网络中使用的激活函数。为此,我将使用具有不同激活函数的简单全连接神经网络来解决 MNIST 问题。

MNIST 数据是一组大约 70000 张手写数字的照片,每张照片的尺寸是 28x28,并且是黑白的。这意味着我们的输入数据形状是(70000,784),我们的输出是(70000,10)。

我将使用一个基本的全连接神经网络,只有一个隐藏层。它看起来像这样:

输入层有 784 个神经元,照片中的每个像素一个,隐藏层有 512 个神经元,输出层有 10 个神经元,每个数字一个。

keras中,我们可以为每一层使用不同的激活功能。这意味着,在我们的情况下,我们必须决定在隐藏层和输出层使用什么激活函数,在这篇文章中,我将只在隐藏层进行实验,但它也应该与最终层相关。

有许多激活函数,我将只讨论基本的:Sigmoid,Tanh 和 Relu。

首先,让我们尽量不要使用任何激活功能。你认为会发生什么?下面是代码(我跳过了数据加载部分,你可以在这个笔记本里找到完整的代码):

model = Sequential()
model.add(Dense(512, input_shape=(784,)))
model.add(Dense(10, activation='softmax'))

我说过,784 个输入,512 个在隐藏层,10 个神经元在输出层。在培训之前,我们可以使用model.summary and model.layers查看网络架构和参数:

Layers (input ==> output)
--------------------------
dense_1 (None, 784) ==> (None, 512)
dense_2 (None, 512) ==> (None, 10)Summary
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense_1 (Dense)              (None, 512)               401920    
_________________________________________________________________
output (Dense)              (None, 10)                5130      
=================================================================
Total params: 407,050
Trainable params: 407,050
Non-trainable params: 0
_________________________________________________________________
None

好了,现在我们确定了网络的架构,让我们针对 5 个时期进行培训:

Train on 60000 samples, validate on 10000 samples
Epoch 1/5
60000/60000 [==============================] - 3s - loss: 0.3813 - acc: 0.8901 - val_loss: 0.2985 - val_acc: 0.9178
Epoch 2/5
60000/60000 [==============================] - 3s - loss: 0.3100 - acc: 0.9132 - val_loss: 0.2977 - val_acc: 0.9196
Epoch 3/5
60000/60000 [==============================] - 3s - loss: 0.2965 - acc: 0.9172 - val_loss: 0.2955 - val_acc: 0.9186
Epoch 4/5
60000/60000 [==============================] - 3s - loss: 0.2873 - acc: 0.9209 - val_loss: 0.2857 - val_acc: 0.9245
Epoch 5/5
60000/60000 [==============================] - 3s - loss: 0.2829 - acc: 0.9214 - val_loss: 0.2982 - val_acc: 0.9185Test loss:, 0.299
**Test accuracy: 0.918**

我们得到的结果不是很好,在 MNIST 数据集上 91.8%的准确率相当糟糕。当然,你可以说我们需要比 5 个时期多得多的时期,但是让我们画出损失:

你可以看到验证损失没有改善,我可以向你保证,即使经过 100 个时代也不会改善。我们可以尝试不同的技术来防止过度适应,或者让我们的网络更大更智能,以便更好地学习和提高,但让我们只是尝试使用sigmoid激活功能。

Sigmoid 函数看起来像这样:

它将输出压缩为(0,1)区间,并且是非线性的。让我们在网络中使用它:

model = Sequential()
model.add(Dense(512, activation='sigmoid', input_shape=(784,)))
model.add(Dense(10, activation='softmax'))

你可以看到架构是完全一样的,我们只改变了Dense层的激活功能。让我们再训练 5 个纪元:

Train on 60000 samples, validate on 10000 samples
Epoch 1/5
60000/60000 [==============================] - 3s - loss: 0.4224 - acc: 0.8864 - val_loss: 0.2617 - val_acc: 0.9237
Epoch 2/5
60000/60000 [==============================] - 3s - loss: 0.2359 - acc: 0.9310 - val_loss: 0.1989 - val_acc: 0.9409
Epoch 3/5
60000/60000 [==============================] - 3s - loss: 0.1785 - acc: 0.9477 - val_loss: 0.1501 - val_acc: 0.9550
Epoch 4/5
60000/60000 [==============================] - 3s - loss: 0.1379 - acc: 0.9598 - val_loss: 0.1272 - val_acc: 0.9629
Epoch 5/5
60000/60000 [==============================] - 3s - loss: 0.1116 - acc: 0.9673 - val_loss: 0.1131 - val_acc: 0.9668

Test loss: 0.113
**Test accuracy: 0.967**

那好多了。为了理解为什么,让我们回忆一下我们的神经元是什么样子的:

其中x是输入,w是权重,b是偏差。可以看到,这只是输入与权重和偏差的线性组合。即使在叠加了很多之后,我们仍然可以把它表示成一个线性方程。这意味着,它类似于一个完全没有隐藏层的网络,对于任何数量的隐藏层都是如此。!).我们将添加一些层到我们的第一个网络,看看会发生什么。看起来是这样的:

model = Sequential()
model.add(Dense(512, input_shape=(784,)))for i in range(5):
    model.add(Dense(512))model.add(Dense(10, activation='softmax'))

这是网络的样子:

dense_1 (None, 784) ==> (None, 512)
dense_2 (None, 512) ==> (None, 512)
dense_3 (None, 512) ==> (None, 512)
dense_4 (None, 512) ==> (None, 512)
dense_5 (None, 512) ==> (None, 512)
dense_6 (None, 512) ==> (None, 10)

_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense_1 (Dense)             (None, 512)               401920    
_________________________________________________________________
dense_2 (Dense)             (None, 512)               262656    
_________________________________________________________________
dense_3 (Dense)             (None, 512)               262656    
_________________________________________________________________
dense_4 (Dense)             (None, 512)               262656    
_________________________________________________________________
dense_5 (Dense)             (None, 512)               262656    
_________________________________________________________________
dense_16 (Dense)             (None, 10)                5130      
=================================================================
Total params: 1,720,330
Trainable params: 1,720,330
Non-trainable params: 0
_________________________________________________________________
None

这些是训练 5 个时期后的结果:

Train on 60000 samples, validate on 10000 samples
Epoch 1/5
60000/60000 [==============================] - 17s - loss: 1.3217 - acc: 0.7310 - val_loss: 0.7553 - val_acc: 0.7928
Epoch 2/5
60000/60000 [==============================] - 16s - loss: 0.5304 - acc: 0.8425 - val_loss: 0.4121 - val_acc: 0.8787
Epoch 3/5
60000/60000 [==============================] - 15s - loss: 0.4325 - acc: 0.8724 - val_loss: 0.3683 - val_acc: 0.9005
Epoch 4/5
60000/60000 [==============================] - 16s - loss: 0.3936 - acc: 0.8852 - val_loss: 0.3638 - val_acc: 0.8953
Epoch 5/5
60000/60000 [==============================] - 16s - loss: 0.3712 - acc: 0.8945 - val_loss: 0.4163 - val_acc: 0.8767

Test loss: 0.416
**Test accuracy: 0.877**

这很糟糕。我们可以看到网络无法学习我们想要的东西。这是因为没有非线性,我们的网络只是一个线性分类器,不能获得非线性关系。

另一方面,sigmoid是一个非线性函数,我们不能将其表示为我们输入的线性组合。这给我们的网络带来了非线性,使它能够学习非线性关系。让我们再次尝试训练 5 个隐藏层网络,这次使用sigmoid激活:

Train on 60000 samples, validate on 10000 samples
Epoch 1/5
60000/60000 [==============================] - 16s - loss: 0.8012 - acc: 0.7228 - val_loss: 0.3798 - val_acc: 0.8949
Epoch 2/5
60000/60000 [==============================] - 15s - loss: 0.3078 - acc: 0.9131 - val_loss: 0.2642 - val_acc: 0.9264
Epoch 3/5
60000/60000 [==============================] - 15s - loss: 0.2031 - acc: 0.9419 - val_loss: 0.2095 - val_acc: 0.9408
Epoch 4/5
60000/60000 [==============================] - 15s - loss: 0.1545 - acc: 0.9544 - val_loss: 0.2434 - val_acc: 0.9282
Epoch 5/5
60000/60000 [==============================] - 15s - loss: 0.1236 - acc: 0.9633 - val_loss: 0.1504 - val_acc: 0.9548

Test loss: 0.15
**Test accuracy: 0.955**

还是那句话,好多了。我们可能过拟合,但我们得到了一个显着的性能提升只是通过使用激活函数。

Sigmoid 很棒,它有许多积极的属性,如非线性、可微性和(0,1)范围为我们提供了返回值的概率,这很好,但它也有缺点。当我们使用反向传播时,我们必须将输出的导数反向传播回我们的第一个权重,换句话说,我们希望将最终输出值中的分类/回归误差传递回整个网络。这意味着我们应该派生我们的层并更新权重。sigmoid 的问题是,它的导数是这样的:

你可以看到导数的最大值非常小(0.25),这意味着我们只会将一小部分误差传递给前面的层。这可能会导致我们的网络学习缓慢(我所说的缓慢是指我们需要更多的数据或时期,而不是计算时间)。

为了解决这个问题,我们可以使用Tanh函数,如下所示:

tanh函数也是非线性和可微的。它的输出在(-1,1)范围内,没有(0,1)范围好,但是对于隐藏层来说还是可以的。最后,它的最大导数是一个很好的值,因为现在我们可以更好地传递误差。

要使用tanh激活功能,我们只需改变Dense层的activation属性:

model = Sequential()
model.add(Dense(512, activation=’tanh’, input_shape=(784,)))
model.add(Dense(10, activation=’softmax’))

还是那句话,网络架构是一样的,只是激活不同。让我们训练 5 个纪元:

Train on 60000 samples, validate on 10000 samples
Epoch 1/5
60000/60000 [==============================] - 5s - loss: 0.3333 - acc: 0.9006 - val_loss: 0.2106 - val_acc: 0.9383
Epoch 2/5
60000/60000 [==============================] - 3s - loss: 0.1754 - acc: 0.9489 - val_loss: 0.1485 - val_acc: 0.9567
Epoch 3/5
60000/60000 [==============================] - 3s - loss: 0.1165 - acc: 0.9657 - val_loss: 0.1082 - val_acc: 0.9670
Epoch 4/5
60000/60000 [==============================] - 3s - loss: 0.0843 - acc: 0.9750 - val_loss: 0.0920 - val_acc: 0.9717
Epoch 5/5
60000/60000 [==============================] - 3s - loss: 0.0653 - acc: 0.9806 - val_loss: 0.0730 - val_acc: 0.9782

Test loss: 0.073
**Test accuracy: 0.978**

太好了!仅通过使用不同的激活函数,我们就将测试精度提高了 1%以上。

我们能做得更好吗?似乎在大多数情况下,我们可以使用 relu 激活功能。Relu 长这样:

这个激活函数的范围是(0,inf),在零点不可微(这个有解)。关于relu最好的事情是它的梯度是总是等于1,这样我们可以在反向传播过程中通过网络传递最大数量的误差。

让我们训练一下,看看结果:

Train on 60000 samples, validate on 10000 samples
Epoch 1/5
60000/60000 [==============================] - 5s - loss: 0.2553 - acc: 0.9263 - val_loss: 0.1505 - val_acc: 0.9516
Epoch 2/5
60000/60000 [==============================] - 3s - loss: 0.1041 - acc: 0.9693 - val_loss: 0.0920 - val_acc: 0.9719
Epoch 3/5
60000/60000 [==============================] - 3s - loss: 0.0690 - acc: 0.9790 - val_loss: 0.0833 - val_acc: 0.9744
Epoch 4/5
60000/60000 [==============================] - 4s - loss: 0.0493 - acc: 0.9844 - val_loss: 0.0715 - val_acc: 0.9781
Epoch 5/5
60000/60000 [==============================] - 3s - loss: 0.0376 - acc: 0.9885 - val_loss: 0.0645 - val_acc: 0.9823

Test loss: 0.064
**Test accuracy: 0.982**

到目前为止,我们取得了最好的成绩。98.2%这是一个不错的结果,我们只使用了一个隐藏层。

需要注意的是,这里没有最好的激活功能。在许多情况下,一个可能比另一个更好,但在另一些情况下会更差。

另一个重要的注意事项是,使用不同的激活函数不影响我们的网络可以学习什么,只影响多快(它需要多少数据/时期)。这是我们尝试过的所有激活函数的曲线图,但这次是在一个更长的训练周期内。你可以看到所有的激活函数最终都达到了 98%的准确率。

希望你喜欢这篇文章。你可以在这里找到代码

使用 pandas 和 Bokeh 探索和可视化芝加哥公交数据—第一部分(pandas 简介)

原文:towardsdatascience.com/exploring-a…

Source: Me

本系列的第 1 部分 是对 pandas 的介绍,其中我将描述如何用 python3 将大型开放存取数据集读入 Jupyter 笔记本,并演示 pandas 作为高性能数据操作/分析软件包的一些功能。

第 2 部分 将更深入地探讨散景软件包,在这里我将演示交互式可视化功能和地理空间映射特性,以便以引人入胜的方式呈现第 1 部分中的数据。

芝加哥市提供了大量的开放数据供市民探索。芝加哥数据门户(data.cityofchicago.org/)收藏了成千上万可供下载的数据集。它包括从电影拍摄地到你最喜欢的餐馆的卫生数据。人们已经创建了以非常独特和有用的方式集成这些数据的应用程序!例如,我发现了一个应用程序,当你的街道将被清扫时,它会通过短信提醒你(以避免城市在清洁期间因未能移动你的汽车而发放的可爱罚单):参见 sweeparound.us

我认为探索芝加哥数据门户上的数据集会很有趣,围绕的主题是大多数芝加哥人都熟悉的:芝加哥运输管理局(CTA)公共列车系统(也称为“L”列车)。对于非芝加哥地区的人,CTA 列车系统分为 8 条不同的“L”型列车线路:

每条铁路线都经过一组车站。有些车站有多条线路穿过。以下是 CTA 列车系统的地图:

CTA Transit Map. Source: www.transitchicago.com/assets/1/6/…

数据

芝加哥数据门户有许多与公共交通系统相关的数据集。我们将在本教程中使用的两个数据集位于下面:

  1. CTA —乘客量—‘L’站条目—月日型平均值&总计
  2. CTA-系统-信息-停靠站列表-地图

第一个数据集包含所有 CTA 火车站的每月乘客总数,并进一步按日类型细分。(工作日、周六或周日/假日)令人惊叹的是,这些数据可以追溯到 2001 年。相当令人印象深刻!

第二个数据集是一个地图,它包含关于每个 CTA 火车站的地理空间数据(包括车站名称和地图上的坐标)。您可以将信息导出为多种格式(JSON、TSV、XML)。我们将在本教程的第 2 部分更多地使用它。

制定问题/探索数据

作为一个芝加哥人,我坐过大部分的火车线(除了紫色线)。通过经常乘坐火车,你可以对某些交通模式有所了解。例如,你会知道在早上 7:30 带你的全家去红线州/湖站是一个非常糟糕的主意。使用这些交通数据,可以发现更多具体的和基于数据的模式。

在全力以赴分析数据和绘制曲线之前,你必须提出问题——或者开始探索数据。总的来说,你希望发现什么?你在查阅资料前后有哪些具体的疑问?你不知道你想知道什么?这个过程可以是迭代的:发现新信息后,可以开始提出新的问题。

我对 CTA 培训系统的一些一般性问题,这些数据将有助于回答:

问题

  1. 我公寓旁边的两个火车站哪个更受欢迎?
  2. 总体上最受欢迎(和最不受欢迎)的电台有哪些?
  3. 城市的哪个区域乘客最多?
  4. 随着时间的推移,有什么有趣的乘客趋势吗?

与大多数数据科学问题一样,首先要做的是读入和处理数据(并检查数据质量问题),开始回答一些基本问题。这将是本文的主要焦点(问题 1-2 的答案)。我计划在第 2 部分深入一些更高级的主题,包括使用散景和彩虹贴图的更高级的可视化方法(问题 3-4)。

Jupyter 笔记本入门

我将使用一个 Jupyter 笔记本和 python3,以及其他一些有用的软件包(熊猫散景)来进行分析。如果你不知道什么是Jupyter**笔记本,那你就错过了!**这是一种令人难以置信的编程/进行探索性数据分析的可视化方式,它帮助我成为一名更好的 python 程序员,并使我摆脱了 Excel 的束缚。这里有一些在你的电脑上安装 Jupyter 的说明:jupyter.org/install

你还需要安装 python3 和 pandas / bokeh 包。我在命令行中使用了以下命令:

pip3 install pandas
pip3 install bokeh

要从命令行(在所需的目录中)启动 Jupyter 笔记本,请键入:

jupyter notebook

这将在浏览器中启动 Jupyter 笔记本仪表板(默认情况下,[http://localhost:8888](http://localhost:8888))。

这个 dasboard 视图显示了启动笔记本服务器的目录中的笔记本、文件和子目录的列表。

要创建一个新的笔记本,你点击顶部的新建按钮,并选择一个 Python 3 环境。

这将打开笔记本视图,您可以在这里开始用 python3 编码!

要想快速了解如何使用 jupyter 笔记本,请看 t 他的文章

工具

我将在此分析中使用的两个主要工具是熊猫和散景:

  • pandas:Python包提供了快速、灵活且富于表现力的数据结构,旨在使处理“关系”或“带标签”的数据变得既简单又直观。它的目标是成为用 Python 进行实际的、真实世界的数据分析的基础高级构建块”
  • 散景 :“散景是一个交互式可视化库,面向现代 web 浏览器进行演示。它的目标是提供优雅、简洁的通用图形结构,并通过超大型或流式数据集的高性能交互来扩展这种能力。散景可以帮助任何想要快速轻松地创建交互式绘图、仪表盘和数据应用的人。”

底线是 pandas 将主要用于存储和操作数据(在数据帧中),而 Bokeh 将用于数据的呈现和可视化

读入数据

为了读入数据,我导入了 pandas 包并读入了 L-stop 平均客流量的 CSV。我可以用 。read_csv() 函数执行此操作,传递文件名和一个分隔符作为参数:

import pandas as pdL_Rides = pd.read_csv('CTA-L-Monthly-Ridership.csv',sep=',')
L_Rides.head()

L_Rides 是包含乘客数据的数据帧。使用 。head() 功能允许我只查看数据帧的前几行。这是查看数据外观的好方法。

这里我们看到有几列数据:

  • station_id,stationame,month_beginning,avg_weekday_rides,avg_saturday_rides,avg_sunday-holiday_rides,month_total

看起来每个车站和每个月都有工作日/周末的乘客统计,以及每个车站的每月总数。

熊猫还有一个有用的功能叫做 。describe() 用于汇总数据帧,提供关于数据集的有用统计信息(平均值、最小值、最大值等)。运行以下命令,将提供这些统计信息:

L_Rides.describe()

查看该数据框,我们可以看到该数据集中总共有 29,460 个数据点(行)。一些更有趣的花絮:

  • 工作日最多乘车一站已给过24041**。
  • 一个普通的 CTA 站一个月大概给 100,573 次乘车。
  • 一般来说,工作日的乘客最多,而周日的乘客最少。

查看这些单独的数字可以提供线索,了解哪些统计数据值得可视化,尽管创建表格和可视化可以提供更完整的图片并讲述更有说服力的故事。

回答问题

问题 1: 我公寓旁边的两个火车站哪个更受欢迎?

在阅读完数据后,我现在可以开始回答我最初的一些问题了。我住在城市的北边 Edgewater ,离我最近的两个站是 Bryn MawrBerwyn 红线站。想知道 2017 年这两个哪个最受欢迎?我可以使用熊猫非常有用的索引功能来做到这一点。

为了选择 Berwyn 站的数据,我最终回答了以下问题:

找到伯温站是**【车站名称】**的所有数据

您可以将此查询转换成熊猫索引选择,如下所示:

L_Rides[L_Rides['stationame'] == 'Berwyn']

您可以看到,这将数据帧过滤为只有那些以“Berwyn”作为“stationame”的电台。

好吧,那很酷。但是我怎么只得到 2017 年的伯温站数据呢?

简单。您可以添加更多布尔运算符来过滤日期。

查找 2017 年 1 月 1 日到 2017 年 12 月 31 日之间的所有数据

这里棘手的部分是导入的数据被分配了特定的变量类型。Pandas 尽力为每一列中的数据分配变量类型。例如,stationame 列作为一个字符串被引入(技术上是一个 python 对象:在这里阅读更多内容)。您可以使用以下命令对此进行检查:

L_Rides.dtypes

您可以在上面看到所有列及其关联的数据类型。有一些好消息和坏消息。

坏消息:‘month _ beginning’列不是日期-时间类型,我们不能很容易地进行日期比较

好消息:熊猫让改变这种类型变得容易

要在 pandas 中更改整列的数据类型,可以使用 到 _datetime() 函数。我通常的做法是为这些数据创建一个新列。我使用以下命令来完成此操作:

L_Rides['date_time'] = pd.to_datetime(L_Rides['month_beginning'])

您可以看到添加了另一个名为 date_time 的列,它现在是“datetime64[ns]”数据类型。

现在我们可以执行日期时间比较操作。我可以使用以下命令选择 2017 年 1 月 1 日至 2017 年 12 月 31 日之间的 Berwyn 数据:

Berwyn_Rides = L_Rides[(L_Rides['stationame'] == 'Berwyn') &
                       (L_Rides['date_time'] >= '01/01/2017') &
                       (L_Rides['date_time'] < '12/31/2017')]

现在,我们拥有 2017 年 Berwyn 站各月数据的 12 个数据点。

最后一步是获得全年的总乘客量。为此,我必须汇总我选择的不同数据行。同样,pandas 使这变得非常容易,因为数据框具有数学函数,可以应用于每一列数据。

不出所料,求和函数被称为 。求和() 。您可以直接使用这个函数来获得数据框中每一列的总和,或者您可以传递特定的列名。我使用以下命令获得了 Berwyn 2017 年的乘客总数:

Berwyn_Rides['monthtotal'].sum()

可以对布林莫尔站执行相同的命令:

最终,我们看到2017 年,布林莫尔站的受欢迎程度比伯温站高出近 50%(1,094,141 次伯温骑行到 1,509,613 次布林莫尔骑行)。

问题 2:最受欢迎(和最不受欢迎)的电台有哪些?

下一个问题是关于 CTA 运输系统中所有车站的数据,因此我们将使用整个 L 车站数据集。要获得最受欢迎的中转站的列表,我们必须首先按每个中转站对数据进行分组。

像我在前面的例子中所做的那样为每个站创建单独的总数将会是一堆不必要的工作。相反我们要用这个奇妙的 。groupby() 熊猫才有的功能!

groupby 函数在这里有很好的描述,但基本概念是执行以下一个或多个函数:

分割—将数据分组

应用—对组执行功能

合并-将结果合并到数据结构中

对于这个公交示例,我们希望数据分成公交站组,对总乘客量应用求和函数,然后结果组合成单个数据帧。

首先,我们可以使用以下命令按站点执行分割:

Station_Group = L_Rides.groupby(['stationame'])

上面我们看到这个组只是一个熊猫数据帧组对象。对于这个对象,你可以执行各种应用功能。若要查看数据已被拆分的唯一组,可以使用。群组功能:

Station_Group.groups

结果表明,该对象只是一个字典,其中包含作为键的组,以及作为值的该组的值。

创建分组对象后,我们可以对组执行聚合(应用)操作。我想对数据帧的“monthtotal”列应用求和运算。我用下面的命令做到这一点:

Station_Group['monthtotal'].sum()#orStation_Group['monthtotal'].agg('sum')

这里我们可以看到每个站点的月总数,一直到 2001 年初。

对此我们能做的最后一件事是结果组合回一个数据帧中(并使用对结果进行排序)。sort_values( ) 函数):

Station_Totals = pd.DataFrame(Station_Group['monthtotal'].sum())
Station_Totals.sort_values(ascending=False,by='monthtotal',inplace=True)
Station_Totals

现在我们有了每个站点的乘客总数的数据框,我们可以回答提出的问题了。我将使用一些方便的 pandas 命令返回前 5 个站点和后 5 个站点:

Station_Totals.head(5)         # Top 5 stationsStation_Totals.tail(5)         # Bottom 5 stations

现在我们有最受欢迎和最不受欢迎的 CTA 站(从 2001 年初开始)。

好了,现在关于霍曼上的那 27 次乘坐…

迷你历史课

来自维基百科:

H 阿曼是芝加哥运输管理局绿线上的。车站位于芝加哥东加菲尔德公园附近的湖街霍曼大道。霍曼位于普拉斯基以东和凯兹以西。霍曼于 1894 年 3 月开放,1994 年 1 月 9 日关闭,当时整条绿线因一项翻新和修复工程而关闭。1996 年 5 月 12 日,该车站没有与绿线的其他部分一起重新开放。【1】

2001 年 3 月开始进行前站房的搬迁工作,2001 年 6 月 30 日新站在音乐学院落成。

所以很有可能在车站关闭之前/之后有 27 个人乘坐了一些车…

使用熊猫的基本情节

虽然 pandas 主要用于数据辩论,但它也有一些有用的绘图功能。熊猫可以从数据帧中绘制图表(使用引擎盖下的[matplotlib](https://matplotlib.org)库)。功能**。plot()** 可以追加到一个 dataframe 中来绘制所有的列。

Berywn_Rides.plot()

上图不是特别有用,尽管额外的参数(比如用于 x 和 y 轴的列)可以传递给。plot()函数来制作一个更有用的图形。下面,我将列名作为 x 和 y 参数传递,以绘制一段时间内工作日的平均乘车次数。

Berwyn_Rides.plot(x ='date_time', y='avg_weekday_rides')

The above graph displays the Berwyn stop average weekday rides over the course of 2017.

默认情况下,pandas 使用线条绘制数据,尽管也可以使用许多其他可视化类型:直方图、盒状图、饼状图等。

为了演示这一点,我将创建一个排名前 25 位的 CTA 站点的条形图。我可以使用我们上面制作的 Station_Totals 数据帧,并传递‘bar’作为‘kind’参数和 60 作为‘rot’参数,将标签旋转 60 度。

Station_Totals.head(25).plot(kind='bar',rot=60)

如您所见,pandas 中的绘图功能使用起来相当简单。因为 pandas 使用 matplotlib 库作为包装器,所以可以使用 matplotlib 的特性进一步定制绘图。

关于 pandas 的伟大之处在于数据帧可以在其他主要的 python 绘图包中使用。(即 plot.lyBokeh )。所以你不只是停留在一个选项来绘制你的数据。

在我的下一篇文章(第 2 部分)中,我将介绍散景可视化包,开始可视化一些 CTA 交通数据。我还将尝试将 CTA 地图集成到可视化中,以提供关于乘客数据的地理上下文信息!敬请期待!希望你喜欢第 1 部分。

使用 pandas 和散景探索和可视化芝加哥公交数据—第二部分(散景介绍)

原文:towardsdatascience.com/exploring-a…

Live Version of the Plot: cpreid2.github.io/Chicago-CTA…

本系列的第 1 部分是对 pandas 的介绍,其中我描述了如何使用 python3 将大型开放存取数据集读入 Jupyter 笔记本,并演示了 pandas 作为高性能数据操作/分析软件包的一些功能。

在这个大数据时代,从空间和地理角度进行思考从未如此重要,创建引人注目的地理空间数据可视化已经成为数据科学家的一项基本技能。

我上一篇文章的目的是帮助回答一些关于芝加哥交通数据的问题。我关于数据集的第三个问题如下:

3。这个城市的哪个地区有最多的火车乘客?

要回答这个问题,在地图上查看乘客数据很有帮助。随着 CTA 乘客数据加载到熊猫数据框架中,制作这样的地图是完全可能的。

在本文中,我将基于第 1 部分中使用的数据,介绍一个非常棒的交互式可视化库,名为散景,它可以使用熊猫数据帧作为输入来创建交互式图形。

Source: bokeh.pydata.org/en/latest/

什么是散景?

我喜欢子弹,所以这里有一些:

  • Bokeh 是 python 的一个数据可视化库****
  • 散景是为网络而建的
  • 创建由数据驱动的动态和交互式图/图形****

为什么要用散景?

除了我说你应该使用散景之外,还有很多好的理由使用散景。您应该在以下情况下使用它:

  • 你想构建交互式网络图形,但是比起 javascript,你更喜欢用 python 编码
  • 您希望使用大型数据集****

关于散景的另一个优点是,它有一个大型类库和函数库,所以你可以非常容易地构建非常高级的可视化效果。

总的来说,我发现它是一个制作漂亮的 web 2.0 可视化效果的非常简单的库。

散景积木

您可以将散景图视为一系列对象。在最底层是一个图形,,在它上面你可以添加**字形。******

一个是所有元素(即情节)的分组,而字形是散景可以显示的基本视觉标记。最基本的字形是一条线。还有很多其他字形,比如等。最棒的是,你可以在一个图形上堆叠多个字形。****

在接下来的几节中,我将参考本系列第 1 部分中的数据帧。假设您已经在机器上安装了 Jupyter、pandas 和 Bokeh。

构建您的第一个散景图

用散景构建一个简单的情节只需要几行代码。我们将遵循这一循序渐进的过程:

  1. 导入必要的模块
  2. 创建一个图
  3. 添加字形
  4. 显示/导出图形

我将为一些样本值构建一个简单的 x/y 图,对该图应用一些格式,然后显示该图。我可以用下面的代码做到这一点:

*****# 1\. Import Modules
**from bokeh.io import show, output_notebook
from bokeh.plotting import figure**# 2\. Create Figure
p = figure(plot_width = 600, plot_height = 400, title = 'My First Plot')# 3\. Add Glyphs
x = [1, 2, 3, 4, 5]
y = [10, 20, 40, 80, 160]p.circle(x, y, color = 'red', alpha = 0.5, size = 15)# 4\. Show the plot
output_notebook()
show(p)*****

我将在下面的部分中分解生成该图的代码:

  1. ****导入模块:show()和 output_notebook()函数用于显示 Jupyter 笔记本中的图形。如果您想生成一个静态 HTML 文件(用于创建独立的 web 应用程序),您可以导入并使用 output_file()函数。还导入了 figure()类,它包含大多数用于显示对象的默认选项。
  2. 创建 Figure :要创建一个新的绘图,您需要实例化一个新的 Figure()类。用外行人的话来说,你创建一个新的人物对象。您可以向该函数传递参数来修改默认布局。例如,我设置了情节的高度和宽度,并给情节加了一个标题。
  3. ****添加字形:可视化的要点是显示数据。我创建了两个数据列表(标记为 x 和 y)。虽然我已经创建了 python 列表,但是您可以使用 NumPy 数组或 Pandas 系列作为输入数据。有了数据,您可以将它传递给一个 render / Glyph 函数来呈现数据。这里我使用了圆()字形。如果我愿意,我可以在这个列表中使用任何其他字形
  4. ****显示/导出:我现在可以调用一些函数来显示剧情了。我调用 output_notebook()来渲染 Jupyter 笔记本中的情节。然后我调用 show()来实际渲染情节。

当剧情渲染时,你会注意到它是交互式的!您可以轻松地放大和缩小绘图。右边的工具控制这个功能。默认情况下,绘图包括缩放、保存和重置绘图的工具。你会看到这些都是可定制的

这就是全部了!现在让我们开始处理芝加哥的交通数据。

创建 CTA 乘客的地理空间地图

散景映射功能

散景非常适合处理地理空间数据。将地图数据添加到地块的方法与将其他类型的图示符添加到地物的方法相同。您将使用 add_tiles() 命令和一个 tile provider 作为参数来传入 tiles,而不是使用 circle()之类的东西。

要创建一个非常简单的地图,您可以使用以下代码:

*******from** **bokeh.plotting** **import** figure, show, output_notebook
**from** **bokeh.tile_providers** **import** CARTODBPOSITRONp = figure(x_range=(-9780000, -9745000), y_range=(5130000, 5160000),
           x_axis_type="mercator", y_axis_type="mercator")p.add_tile(CARTODBPOSITRON)output_notebook()
show(p)*****

这段代码生成了一张芝加哥周围的地图。就像其他散景可视化一样,该地图是交互式的,允许用户放大和缩小地图。

墨卡托投影:地图的拼贴使用了墨卡托投影。这里有一篇方便的维基百科文章描述了它是什么。本质上,地球的球面坐标(经纬度)可以投影到一个平面上(X 和 Y 坐标)。您在浏览器上以 2d 方式查看地图,因此这基本上就是用于显示数据的内容。

在上面的代码中,您可以看到图形是使用 X 和 Y 坐标绘制的。

  • 在 figure()函数中,我可以传入 X 和 Y 范围,以便地图放大感兴趣的区域(芝加哥)。
  • x/y_axis_type 参数指定我希望以熟悉的纬度和经度坐标显示坐标。

有了绘制地图的能力,我们现在可以开始准备将乘客数据投影到地图上。

读入 CTA 中转站位置数据

如第 1 部分所述,芝加哥市提供了每个中转站位于的信息。我可以将它读入数据框,并使用以下 pandas 命令查看数据:

*****L_Map = pd.read_csv('CTA-L-Stops-Map.csv',sep=',')
L_Map.head()*****

从这些数据中,我们注意到了一些情况:

  • 每行代表一个不同的站点,由 STOP_ID 标识
  • 每个站可以有个停靠点(即一个站在一个方向上停靠,另一个站在另一个方向上停靠)****
  • 每个站点由站点名称和唯一的地图 ID 标识
  • 该数据还包括关于每个站在哪条线上的信息(即绿色、红色、蓝色等)
  • 最后一列包括纬度和经度坐标

正如我们在上一节中讨论的,为了绘图的目的,地图需要 X 和 Y 坐标(不是纬度和经度)。

为了改变这些,我们需要使用一些数学和一点熊猫魔法。

我创建了一个函数,它接受一串纬度和经度坐标(类似于 dataframe 中的坐标),并将它们转换为一个 X 和 Y 坐标的元组。

*****import math
from ast import literal_eval**def merc**(Coords):
    Coordinates = literal_eval(Coords) lat = Coordinates[0]
    lon = Coordinates[1]

    r_major = 6378137.000
    x = r_major * math.radians(lon)
    scale = x/lon
    y = 180.0/math.pi * math.log(math.tan(math.pi/4.0 + 
        lat * (math.pi/180.0)/2.0)) * scale return (x, y)*****

现在我们有了一个可以将坐标转换成 Bokeh 喜欢的东西的函数,我们可以将它应用到整个数据集,并将其保存为数据框中的一个新列。我在 的帮助下做到了这一点。熊猫中的 apply() 函数和 python 中的一个 lambda 函数。

*****L_Map['coords_x'] = L_Map['Location'].**apply(lambda x: merc(x)[0])**
L_Map['coords_y'] = L_Map['Location'].**apply(lambda x: merc(x)[1])*******

创建这些列后,我们现在有了可以在地图上绘制的数据。如前所述,L_Map 数据帧中的行对于每个站点都有重复的行(列车行驶的每个方向有 2 个站点)。因此,我们将想要删除这些重复,否则我们将有两个点每站。

为此,我使用了熊猫 **。**drop _ duplicates()命令:

*****L_Map.drop_duplicates(subset='MAP_ID', keep="last", inplace=True)*****

现在,我们可以在之前创建的地图上叠加车站。我只需添加一个圆形符号,将列作为 x 和 y 参数传递。

*****from bokeh.plotting import figure, show, output_notebook
from bokeh.tile_providers import CARTODBPOSITRONp = figure(x_axis_type="mercator", y_axis_type="mercator")
p.add_tile(CARTODBPOSITRON)**p.circle(x = L_Map['coords_x'],
         y = L_Map['coords_y'])**output_notebook()
show(p)*****

太好了!现在我们知道了每个 CTA 站在地图上的位置。

结合地理空间和乘客数据

该图表的目标是可视化最受欢迎和最不受欢迎的电台。这可以通过改变每个圆形符号的直径(或比例)来表示每个车站的相对乘客量来实现。较大的圆圈代表繁忙的车站,较小的圆圈代表乘客较少的车站。

第一步是组合包含乘客数据的数据帧和包含站 GPS 坐标的数据帧。我们将使用熊猫 **。**merge()功能。****

此函数允许您指定 1)要合并的数据框,2)哪些列将用作键或合并两者的列,以及 3)它们的连接方式。

在下面的代码中,我将 L_Map 数据帧(包含坐标)与根据“station_id”列分组的 Station_Totals 数据帧(包含乘客总数)合并。

*****# 1\. Group ridership totals
Station_Group = L_Rides.groupby(['station_id'])
Station_Totals = pd.DataFrame(Station_Group['monthtotal'].sum())# 2\. Merge dataframes
Merged = pd.merge(L_Map, Station_Totals, left_on='MAP_ID', right_index=True)*****

我们现在已经合并了乘客数据和地图数据!

接下来要做的是将乘客数据缩放到适当大小的圆。我选择了 2000000 作为缩放因子,因为它不会使圆圈太大或太小。一些尝试和错误帮助我确定了这个数字。

*****Merged['circle_sizes'] = Merged['monthtotal'] / 2000000*****

我们现在准备绘制我们的最终地图!我将把“circle_sizes”列传递给圆形标志符号,并添加一些其他参数来更改圆形的颜色和填充。

*****from bokeh.plotting import figure, show, output_notebook
from bokeh.tile_providers import CARTODBPOSITRON# range bounds supplied in web mercator coordinates
p = figure(x_axis_type="mercator", y_axis_type="mercator")
p.add_tile(CARTODBPOSITRON)p.circle(x=Merged['coords_x'],
         y=Merged['coords_y'], 
         **size=Merged['circle_sizes']**,
        ** line_color="#FF0000", 
         fill_color="#FF0000",
         fill_alpha=0.05**)

output_notebook()show(p)*****

看起来棒极了!但是我们怎么知道哪个站是哪个站呢?

解决方案是定制默认工具。散景有交互式工具,可用于报告信息和更改绘图参数,如缩放级别或范围(如右侧面板所示)。

帮助我们的工具叫做 悬停工具 。当用户悬停在一个圆圈上时,我们可以用它来叠加每个站点的信息。

设置悬停工具需要两个部分:

  1. 创建一个 ColumnDataSource 对象,该对象将成为绘图的最终数据源。这个对象有其他功能,但是我们可以在另一篇文章中讨论
  2. 创建一个悬停工具对象并将其指向 ColumnDataSource 对象
*****from bokeh.plotting import figure, show, output_notebook
from bokeh.tile_providers import CARTODBPOSITRON
from bokeh.models import ColumnDataSource, HoverTool**source = ColumnDataSource(data=dict(
                        x=list(Merged['coords_x']), 
                        y=list(Merged['coords_y']),
                        ridership=list(Merged['monthtotal']),
                        sizes=list(Merged['circle_sizes']),
                        stationname=list(Merged['STATION_NAME'])))****hover = HoverTool(tooltips=[
    ("station", "**[**@stationname**](http://twitter.com/stationname)**"),
    ("ridership","**[**@ridership**](http://twitter.com/ridership)**")

])**# range bounds supplied in web mercator coordinates
p = figure(x_axis_type="mercator", 
           y_axis_type="mercator",
           **tools=[hover, 'wheel_zoom','save']**)p.add_tile(CARTODBPOSITRON)p.circle(x='x',
         y='y', 
         **source=source,**
         size='sizes',
         line_color="#FF0000", 
         fill_color="#FF0000",
         fill_alpha=0.05)

output_notebook()show(p)*****

现在我们有能力看到哪个站是哪个了!悬停框包括原始乘客号和站名。

我们现在可以开始研究数据并做一些笔记:

  • 市区环路是 CTA 系统中最繁忙的部分(拥有最大的环路)。
  • 95 号红线站作为终点站,客流量惊人。有一个项目来扩展 CTA 的这一部分是有意义的。
  • 城西的蓝线和绿线并没有我想象的那么繁忙。
  • 在芝加哥环线上,更南边和西边的火车站是最不繁忙的。

这里主持的剧情现场版:cpreid2.github.io/Chicago-CTA…

虽然我计划只制作这一系列的两个部分,但是我认为尝试在地图上添加一些动画特征会有所帮助,这样可以随着时间的推移查看数据。我将在以后的文章中介绍这一点,敬请关注!

用 Python 和 Tableau 探索抗生素处方

原文:towardsdatascience.com/exploring-a…

我最近参观了在伦敦卫生和热带医学院举行的“超级细菌——对抗细菌的军备竞赛”(哈佛大学出版社)的新书发布会。发起的作者和小组成员利用这个平台表达了他们对公共卫生的担忧,以及对开发新疗法的有限激励。据估计(2014 年)每年有 700,000 人死于耐药性感染,而只有很少的药物在研发中,这一警报需要保持在头条位置。

与此同时,我希望听到更多关于近年来取得的进展。2013 年,英国政府启动了一项五年战略来对抗抗生素耐药性。一年后,这本书的作者对英国的形势进行了一次广泛的回顾。甚至在审查之前就开始实施的战略效果如何?我决定使用由英国公共卫生部(PHE)维护和提供的 AMR 本地指标来探索这个问题,作为英国战略监测组成部分的一部分。我想看看我是否能够深入了解当地和地区的抗生素使用模式,以及我是否能够提供额外的可视化和分析模式。

我开始使用在临床委托组(ccg)级别获得的数据进行研究。英格兰保健委员会是 2012 年成立的 208 个区域组织,旨在组织英格兰的保健服务。他们的数据给出了不同地区处方模式的总体情况,随后可以在更精细的水平上进行探索。所有指标/ccg 的数据作为一个数据集可用,但使用由 PHE 数据科学小组开发的指尖库可以更方便地进行研究。使用 Python (pandas、seaborn、plotly)进行数据准备、清理和分析,使用 Tableau 的故事功能构建仪表盘。脚本可以作为 jupyter 笔记本和仪表盘在这里在这里获得。

仪表板基于每个 CCG 地区每个标准化处方单位(STAR-PU)12 个月的抗生素处方项目滚动总数。这种标准化不仅考虑到每个地区的患者人数,还考虑到他们的年龄和性别构成以及周期性波动,因此只能呈现总体趋势。为了计算减少的变化,我提取了指标的数据,然后使用 pandas 的描述性模块收集该期间的最大值和最小值,并计算处方中的差异和相对差异。

结果表明,所有 ccg 在此期间都降低了他们的处方水平,但一些 ccg 的处方水平显著下降,降幅在 20-40%之间,大多数 ccg 的处方水平下降了 10-15%。因此,对所有 ccg的单个时间序列的比较显示,在活动开始时的起点和减排力度方面,情况非常不一致:

国民医疗服务体系(NHS)制定的处方目标中的绿线和红线:竞选开始时的目标(1.161,绿色)和 2018/19 年更雄心勃勃的目标(0.965,红色)。为了方便直观地比较所有 ccg 与这些目标的关系,我对数据集中最近一年(截至 2017 年 12 月的 12 个月)的值进行了切片,将数据连接到英国国家统计局提供的 ccg 边界,并使用 Tableau 的映射功能绘制了连接的数据框架。其结果是一张与国民保健制度设定的早期和晚期目标相关的处方减少图:

虽然大多数 ccg 尚未实现更雄心勃勃的目标,但迄今为止看到的减少应被视为减少处方的努力的标志。虽然该战略是在全国范围内进行评估的,但数据表明,地方努力也应得到考虑。伦敦的大多数 ccg 已经实现了这两个目标;诺丁汉一个 ccg 的评论员看了数据后指出,他们在早期阶段就实现了这两个目标。这些成就,从下面的时间序列中可以清楚地看到,主要是由于当地的努力。

观察到的总体减少表明,该战略确实对抗生素处方有显著影响,但也需要考虑地方努力的故事。成功的故事不仅能鼓舞人心,还能提示干预最有效的领域。另一方面,应该听取仍在努力实现目标的 ccg 代表的意见,以了解他们对需要做出更多努力的领域的看法。虽然地理空间和时间序列分析经常被用来可视化总体趋势,但我发现它们在公共卫生问题的局部空隙中是有用的。

附加说明

项目文件及成果:github.com/ronyarmon/A…

总结并向英国议会健康和社会保健委员会(2018 年)设立的抗生素耐药性调查提交结果

一项关于抗生素处方与不同社会人口统计剥夺指数相关的随访研究

英国 5 年抗菌药物耐药策略

英国抗菌药物耐药性综述

仪表盘 : AMR 指标(PHE)药品优化(NHS)抗菌数据月报(PrescQIPP)

探索、聚类和映射多伦多的犯罪

原文:towardsdatascience.com/exploring-c…

Photo credit: Pixabay

我通过开放数据门户网站探索美国城市的犯罪数据,从中获得了很多乐趣,因为多伦多的犯罪数据根本不可用。

然而,在今年夏天,多伦多警方推出了一个公共安全数据门户网站,以增加公众和官员之间的透明度。因此,我有机会通过多伦多警察局公共安全数据门户探索多伦多的犯罪。我对2016 年主要犯罪指标(MCI)特别感兴趣,它包含了 2016 年 32,612 份报告的列表(这是唯一有数据可用的一年)。

我们用 R 来看看数据,看看有没有什么有趣的。

数据

library(ggplot2)
library(ggthemes)
library(dplyr)
library(viridis)
library(tidyr)
library(cluster)
library(ggmap)
library(maps)

经过一点探索,我发现有很多重复(event_unique_id),所以让我们从数据中删除它。

toronto <- read.csv('toronto_crime.csv')
toronto <- subset(toronto, !duplicated(toronto$event_unique_id))
unique(toronto$occurrenceyear)
unique(toronto$reportedyear)

发现什么有趣的东西了吗?发生年份从 2000 年到 2016 年不等,但所有犯罪的报告年份都是 2016 年。这意味着人们在 2016 年向警方报告了前几年发生的事件。让我们看看我们的数据中有多少延迟报告的事件。

year_group <- group_by(toronto, occurrenceyear)
crime_by_year <- summarise(year_group,
                          n = n())
crime_by_year

2 起发生在 2000 年,2 起发生在 2001 年,以此类推。但是,绝大多数事件发生在 2016 年,因此我们将只使用 2016 年的事件。我还删除了我们不需要的所有列,以及缺少值的四行。

drops <- c("X", "Y", "Index_", "ucr_code", "ucr_ext", "reporteddate", "reportedmonth", "reportedday", "reporteddayofyear", "reporteddayofweek", "reportedhour", "occurrencedayofyear", "reportedyear", "Division", "Hood_ID", "FID")toronto <- toronto[, !(names(toronto) %in% drops)]
toronto <- toronto[toronto$occurrenceyear == 2016, ]
toronto <- toronto[complete.cases(toronto), ]

探索

2016 年有哪些重大犯罪?

indicator_group <- group_by(toronto, MCI)
crime_by_indicator <- summarise(indicator_group, n=n())
crime_by_indicator <- crime_by_indicator[order(crime_by_indicator$n, decreasing = TRUE),]ggplot(aes(x = reorder(MCI, n), y = n), data = crime_by_indicator) +
  geom_bar(stat = 'identity', width = 0.5) +
  geom_text(aes(label = n), stat = 'identity', data = crime_by_indicator, hjust = -0.1, size = 3.5) +
  coord_flip() +
  xlab('Major Crime Indicators') +
  ylab('Number of Occurrences') +
  ggtitle('Major Crime Indicators Toronto 2016') +
  theme_bw() +
  theme(plot.title = element_text(size = 16),
        axis.title = element_text(size = 12, face = "bold"))

Figure 1

袭击是多伦多最普遍的暴力犯罪形式。什么是攻击?在刑法和民法中,攻击是指试图与他人进行有害的或冒犯性的接触,或威胁这样做。

突击有哪些不同的类型?哪种类型最差?

assault <- toronto[toronto$MCI == 'Assault', ]
assault_group <- group_by(assault, offence)
assault_by_offence <- summarise(assault_group, n=n())
assault_by_offence <- assault_by_offence[order(assault_by_offence$n, decreasing = TRUE), ]ggplot(aes(x = reorder(offence, n), y = n), data = assault_by_offence) +
  geom_bar(stat = 'identity', width = 0.6) +
  geom_text(aes(label = n), stat = 'identity', data = assault_by_offence, hjust = -0.1, size = 3) +
  coord_flip() +
  xlab('Types of Assault') +
  ylab('Number of Occurrences') +
  ggtitle('Assault Crime Toronto 2016') +
  theme_bw() +
  theme(plot.title = element_text(size = 16),
        axis.title = element_text(size = 12, face = "bold"))

Figure 2

这里没有太多的信息,因为最常见的攻击类别是…攻击。我最终通过 Attorneys.com 学会了不同类型的攻击。

然后让我们看看最严重的犯罪

offence_group <- group_by(toronto, offence)
crime_by_offence <- summarise(offence_group, n=n())
crime_by_offence <- crime_by_offence[order(crime_by_offence$n, decreasing = TRUE), ]ggplot(aes(x = reorder(offence, n), y = n), data = crime_by_offence) +
  geom_bar(stat = 'identity', width = 0.7) +
  geom_text(aes(label = n), stat = 'identity', data = crime_by_offence, hjust = -0.1, size = 2) +
  coord_flip() +
  xlab('Types of Offence') +
  ylab('Number of Occurrences') +
  ggtitle('Offence Types Toronto 2016') +
  theme_bw() +
  theme(plot.title = element_text(size = 16),
        axis.title = element_text(size = 12, face = "bold"))

Figure 3

袭击是最常见的犯罪,其次是破门而入。根据 Wikibooks 的说法,最典型的破门而入形式是闯入商业地产或私人住宅以窃取财产。这表明破门而入最有可能发生在家里或办公室没有人的时候。

一天中的犯罪时间怎么样?

hour_group <- group_by(toronto, occurrencehour)
crime_hour <- summarise(hour_group, n=n())ggplot(aes(x=occurrencehour, y=n), data = crime_hour) + geom_line(size = 2.5, alpha = 0.7, color = "mediumseagreen", group=1) + 
  geom_point(size = 0.5) + 
  ggtitle('Total Crimes by Hour of Day in Toronto 2016') +
  ylab('Number of Occurrences') +
  xlab('Hour(24-hour clock)') +
  theme_bw() +
  theme(plot.title = element_text(size = 16),
        axis.title = element_text(size = 12, face = "bold"))

Figure 4

最糟糕的时间是在午夜左右,另一个高峰时间是在中午左右,然后是晚上 8 点左右。

好吧,但是一天中的每个小时什么类型的犯罪最频繁?

hour_crime_group <- group_by(toronto, occurrencehour, MCI)
hour_crime <- summarise(hour_crime_group, n=n())ggplot(aes(x=occurrencehour, y=n, color=MCI), data = hour_crime) + 
  geom_line(size=1.5) + 
  ggtitle('Crime Types by Hour of Day in Toronto 2016') +
  ylab('Number of Occurrences') +
  xlab('Hour(24-hour clock)') +
  theme_bw() +
  theme(plot.title = element_text(size = 16),
        axis.title = element_text(size = 12, face = "bold"))

Figure 5

虽然袭击是一天中最常见的犯罪,但它们发生在傍晚和夜间的频率高于清晨。另一方面,破门而入的犯罪更经常发生在早上和午夜左右(那时没有人在家或在办公室)。抢劫和汽车盗窃更有可能在深夜发生。所有这些模式都有意义。

这些犯罪最有可能发生在多伦多的什么地方?

location_group <- group_by(toronto, Neighbourhood)
crime_by_location <- summarise(location_group, n=n())
crime_by_location <- crime_by_location[order(crime_by_location$n, decreasing = TRUE), ]
crime_by_location_top20 <- head(crime_by_location, 20)ggplot(aes(x = reorder(Neighbourhood, n), y = n), data = crime_by_location_top20) +
  geom_bar(stat = 'identity', width = 0.6) +
  geom_text(aes(label = n), stat = 'identity', data = crime_by_location_top20, hjust = -0.1, size = 3) +
  coord_flip() +
  xlab('Neighbourhoods') +
  ylab('Number of Occurrences') +
  ggtitle('Neighbourhoods with Most Crimes - Top 20') +
  theme_bw() +
  theme(plot.title = element_text(size = 16),
        axis.title = element_text(size = 12, face = "bold"))

Figure 6

最危险的街区是…滨水区。这个不断扩张的市中心包罗万象,不仅包括密集的公寓社区,还包括极度活跃的娱乐区。结果是:这里发生了数量惊人的暴力犯罪和纵火案。

Photo credit: Pixabay

第二危险的街区是教堂-央街走廊。它很受学生的欢迎,因为它靠近瑞尔森大学,而且它是多伦多同性恋村的一方的家。然而,这个地区也有它的犯罪问题,考虑到它离市中心有多近,这有点令人惊讶。

哪里是最安全的社区?

tail(crime_by_location, 5)

考虑搬到多伦多吗?我们已经为你选好了新家!森林山南是多伦多一个安全、华丽和富裕的社区,拥有许多漂亮的房子,比如这栋豪宅。

Photo credit: Torontorentals.com

让我们比较一下犯罪率最高的街区

offence_location_group <- group_by(toronto, Neighbourhood, offence)
offence_type_by_location <- summarise(offence_location_group, n=n())
offence_type_by_location <- offence_type_by_location[order(offence_type_by_location$n, decreasing = TRUE), ]
offence_type_by_location_top20 <- head(offence_type_by_location, 20)ggplot(aes(x = Neighbourhood, y=n, fill = offence), data=offence_type_by_location_top20) +
  geom_bar(stat = 'identity', position = position_dodge(), width = 0.8) +
  xlab('Neighbourhood') +
  ylab('Number of Occurrence') +
  ggtitle('Offence Type vs. Neighbourhood Toronto 2016') + theme_bw() +
  theme(plot.title = element_text(size = 16),
        axis.title = element_text(size = 12, face = "bold"),
        axis.text.x = element_text(angle = 90, hjust = 1, vjust = .4))

Figure 7

我没想到会这样。它不漂亮。然而,它确实告诉我们,除了袭击事件,教堂-央街走廊和海滨是破门而入犯罪最多的地方(不要去那里!),西亨伯-克莱尔维尔的车辆失窃率最高(不要把车停在那里!).

让我们试试不同的东西

crime_count <- toronto %>% group_by(occurrencemonth, MCI) %>% summarise(Total = n())
crime_count$occurrencemonth <- ordered(crime_count$occurrencemonth, levels = c('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'))ggplot(crime_count, aes(occurrencemonth, MCI, fill = Total)) +
  geom_tile(size = 1, color = "white") +
  scale_fill_viridis()  +
  geom_text(aes(label=Total), color='white') +
  ggtitle("Major Crime Indicators by Month 2016") +
  xlab('Month') +
  theme(plot.title = element_text(size = 16), 
        axis.title = element_text(size = 12, face = "bold"))

Figure 8

好多了!

人身攻击是一年中每个月最常见的犯罪类型,无一例外。5 月份的袭击事件似乎比去年其他月份都多。

day_count <- toronto %>% group_by(occurrencedayofweek, MCI) %>% summarise(Total = n())ggplot(day_count, aes(occurrencedayofweek, MCI, fill = Total)) +
  geom_tile(size = 1, color = "white") +
  scale_fill_viridis()  +
  geom_text(aes(label=Total), color='white') +
  ggtitle("Major Crime Indicators by Day of Week 2016") +
  xlab('Day of Week') +
  theme(plot.title = element_text(size = 16), 
        axis.title = element_text(size = 12, face = "bold"))

Figure 9

周六和周日比一周中的其他任何一天有更多的袭击,比其他任何一天有更少的盗窃。偷车贼几乎每天都很忙。

我希望发现一些季节性犯罪模式,如温度变化和日照时间可能与全年的犯罪有关,或者学年的开始和结束与全年的犯罪变化有关。但这一年的数据不足以解决我的上述担忧。我希望在不久的将来,多伦多警察局将通过其开放数据门户发布更多的数据。

凶杀案

homicide <- read.csv('homicide.csv', stringsAsFactors = F)
homicide$Occurrence_Date <- as.Date(homicide$Occurrence_Date)year_group <- group_by(homicide, Occurrence_year, Homicide_Type)
homicide_by_year <- summarise(year_group, n=n())ggplot(aes(x = Occurrence_year, y=n, fill = Homicide_Type), data=homicide_by_year) +
  geom_bar(stat = 'identity', position = position_dodge(), width = 0.8) +
  xlab('Year') +
  ylab('Number of Homicides') +
  ggtitle('Homicide 2004-2016') + theme_bw() +
  theme(plot.title = element_text(size = 16),
        axis.title = element_text(size = 12, face = "bold"))

Figure 10

2005 年被称为多伦多的“T2 枪年”。11 年后的 2016 年,多伦多的涉枪杀人案再次激增。

homicide$month <- format(as.Date(homicide$Occurrence_Date) , "%B")homicide_count <- homicide %>% group_by(Occurrence_year, month) %>% summarise(Total = n())
homicide_count$month <- ordered(homicide_count$month, levels = c('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'))
ggplot(homicide_count, aes(Occurrence_year, month, fill = Total)) +
  geom_tile(size = 1, color = "white") +
  scale_fill_viridis()  +
  geom_text(aes(label=Total), color='white') +
  ggtitle("Homicides in Toronto (2004-2016)") +
  xlab('Year') +
  theme(plot.title = element_text(size = 16), 
        axis.title = element_text(size = 12, face = "bold"))

Figure 11

令人担忧的是,与 2015 年相比,2016 年多伦多的凶杀案总数大幅增加。希望我们有一个更好的 2017 年。然而,当我读到多伦多被经济学家评为北美最安全的城市时,我感觉安全多了。

K 均值聚类

K-Means 是最流行的“聚类”算法之一。它是将数据点组划分为少量簇的过程。使用我们的犯罪数据,当我们测量攻击数量和其他指标时,攻击数量高的街区将被分组在一起。K 均值聚类的目标是为每个数据点(邻域)分配一个聚类。我们首先将数据点(邻域)划分为 k 个聚类,其中每个邻域属于具有最近平均值的聚类(用作聚类的原型)。

作为一种无监督学习算法,我们使用 K-Mean 来建立模型,帮助我们更好地理解我们的数据。它使我们能够对未标记的数据点进行分组。

为了进行聚类分析,我们的数据必须如下所示:

by_groups <- group_by(toronto, MCI, Neighbourhood)
groups <- summarise(by_groups, n=n())
groups <- groups[c("Neighbourhood", "MCI", "n")]
groups_wide <- spread(groups, key = MCI, value = n)

第一列——定性数据应从分析中移除

z <- groups_wide[, -c(1,1)]

数据不能有任何缺失值

z <- z[complete.cases(z), ]

必须对数据进行缩放以进行比较

m <- apply(z, 2, mean)
s <- apply(z, 2, sd)
z <- scale(z, m, s)

确定集群的数量

wss <- (nrow(z)-1) * sum(apply(z, 2, var))
for (i in 2:20) wss[i] <- sum(kmeans(z, centers=i)$withiness)
plot(1:20, wss, type='b', xlab='Number of Clusters', ylab='Within groups sum of squares')

Figure 12

由于该图显示了一个非常强的肘形,基于该图,我们可以有把握地说,我们不需要两个以上的群集(质心)。

试衣模特

kc <- kmeans(z, 2)
kc

释义:

  • 第一类有 121 个街区,第二类有 10 个街区。
  • 聚类意味着:如果这些数字的范围看起来很奇怪,那是因为我们在进行聚类分析之前对数据进行了标准化。负值表示“低于大多数”,正值表示“高于大多数”。因此,集群 1 具有低攻击、低汽车盗窃、低破门而入、低抢劫和低盗窃的街区。第 2 类社区具有高攻击性、高汽车盗窃、高非法闯入、高抢劫和高盗窃率。这两组在每一个变量上都有显著的差异,这是好事,因为这表明每一个变量在聚类分类中都起着重要的作用。
  • 聚类向量:第一、第二和第三邻域应该都属于聚类 1,第四邻域应该属于聚类 2,等等。
  • 更相关的度量是“在内”和“在之间”。 withinss 告诉我们每个数据点到聚类中心的距离的平方和。越低越好。betwess 告诉我们聚类中心之间距离的平方和。理想情况下,我们希望聚类中心彼此远离。
  • 可用组件不言自明。

绘制 k 均值结果

z1 <- data.frame(z, kc$cluster)
clusplot(z1, kc$cluster, color=TRUE, shade=F, labels=0, lines=0, main='k-Means Cluster Analysis')

Figure 13

看起来我们对集群数量的选择是好的,并且我们几乎没有噪声。

分层聚类

对于分层聚类方法,树状图是深入了解聚类解决方案的主要图形工具。

z2 <- data.frame(z)
distance <- dist(z2)
hc <- hclust(distance)

现在我们已经有了一个集群解决方案。让我们检查结果。

plot(hc, labels = groups_wide$Neighbourhood, main='Cluster Dendrogram', cex=0.65)

Figure 14

如果我们沿着树形图的 y 轴选择任意高度,并在树形图中移动,计算我们穿过的线的数量,每条线代表一个聚类。例如,如果我们观察高度为 10 的物体,并在该高度移动穿过 x 轴,我们将穿过两条直线。它定义了一个双集群解决方案;沿着这条线向下穿过它的所有分支,我们可以看到包含在这两个集群中的街区的名称。查看多伦多犯罪数据的树状图,我们可以看到我们的数据点非常不平衡。从树的顶端,有两个不同的组;一组由有分支和更多分支的分支组成,而另一组只由几个街区组成(我们可以看到这些街区是多伦多最危险的街区)。然而,我想立刻尝试许多不同的分组,开始调查。

counts = sapply(2:6,function(ncl)table(cutree(hc,ncl)))
names(counts) = 2:6
counts

解释:

  • 对于双集群解决方案,集群 1 中有 128 个小区,集群 2 中有 3 个小区。
  • 对于 3 个集群的解决方案,我们在集群 1 中有 128 个邻居,在集群 2 中有 2 个邻居,在集群 3 中有 1 个邻居。以此类推,直到我们得到一个 6 集群解决方案。

在实践中,我们希望有一个解决方案,其中没有太多只有少量观察值的聚类,因为这可能会使我们难以解释我们的结果。在这个练习中,我将坚持使用 3 集群解决方案,看看我将获得什么结果。

member <- cutree(hc, 3)
aggregate(z, list(member), mean)

在第一组中,所有犯罪指标都是负面的。与聚类 2 和聚类 3 相比,聚类 1 在每个变量上也有显著差异。除了汽车盗窃之外,第二组在大多数犯罪指标方面都高于第三组。

plot(silhouette(cutree(hc, 3), distance))

聚类 3 的轮廓宽度值是零,并且轮廓图表明我们确实不需要第三个聚类,绝大多数邻域属于第一个聚类,并且 2-聚类将是我们的解决方案。

制作多伦多犯罪地图

r 中有许多用于绘制和操作空间数据的包。我将使用 ggmap 来制作一个简单易用的多伦多犯罪地图。

lat <- toronto$Lat
lon <- toronto$Long
crimes <- toronto$MCI
to_map <- data.frame(crimes, lat, lon)
colnames(to_map) <- c('crimes', 'lat', 'lon')
sbbox <- make_bbox(lon = toronto$Long, lat = toronto$Lat, f = 0.01)
my_map <- get_map(location = sbbox, maptype = "roadmap", scale = 2, color="bw", zoom = 10)
ggmap(my_map) +
  geom_point(data=to_map, aes(x = lon, y = lat, color = "#27AE60"), 
             size = 0.5, alpha = 0.03) +
  xlab('Longitude') +
  ylab('Latitude') +
  ggtitle('Location of Major Crime Indicators Toronto 2016') +
  guides(color=FALSE)

Figure 15

从地图上可以清楚地看到这个城市的主要犯罪发生地。大部分集中在滨水区,北约克以南比其他任何地区都更和平。然而,在比较高密度区域时,点叠加没有帮助,所以让我们优化这种可视化。

ggmap(my_map) +
  geom_point(data=to_map, aes(x = lon, y = lat, color = "#27AE60"), 
             size = 0.5, alpha = 0.05) +
  xlab('Longitude') +
  ylab('Latitude') +
  ggtitle('Location of Major Crime Indicators Toronto 2016') +
  guides(color=FALSE) +
  facet_wrap(~ crimes, nrow = 2)

Figure 16

这当然更有趣,信息量也更大。一些犯罪,如袭击、破门而入,在城市各处都有发生,集中在滨水区。其他犯罪,比如偷车,西边比东边多点。抢劫和盗窃主要发生在海滨地区。

摘要

通过查看多伦多重大犯罪指标的数据,可以回答的问题并不多。不过没关系。这些数据当然还有其他有趣的用途(比如在 MicroStrategy 上创建一个仪表板)。

Figure 17

和往常一样,所有代码都可以在 Github 上找到。我将很高兴收到关于上述任何反馈或问题。