TowardsDataScience 博客中文翻译 2016~2018(八十四)
使用 Python 中的散景进行数据可视化,第一部分:入门
提升你的视觉游戏
如果没有交流结果的有效手段,最复杂的统计分析可能是没有意义的。我最近在我的研究项目中的一次经历让我明白了这一点,在这个项目中,我们使用数据科学来提高建筑能效。在过去的几个月里,我的一个团队成员一直在研究一种叫做小波变换的技术,这种技术用于分析时间序列的频率成分。这种方法取得了积极的效果,但她很难在不迷失于技术细节的情况下解释它。
她有些恼怒,问我是否可以制作一个展示这种转变的图像。在几分钟内,我用一个名为gganimate的 R 包制作了一个简单的动画,展示了该方法如何转换一个时间序列。现在,我的团队成员不用费力解释小波,而是可以展示这个剪辑来直观地展示这项技术是如何工作的。我的结论是,我们可以做最严格的分析,但最终,所有人想看到的是一个 gif!虽然这种说法是幽默的,但它也有一定的道理:如果不能清楚地交流,结果将没有什么影响,而且通常呈现分析结果的最佳方式是可视化。
可用于数据科学的资源正在快速发展,这在可视化领域尤为明显,似乎每周都有另一种选择可以尝试。所有这些进步都有一个共同的趋势:互动性增强。人们喜欢在静态图中看到数据,但他们更喜欢摆弄数据,看看变化的参数如何影响结果。就我的研究而言,一份告诉业主通过改变空调时间表可以节省多少电的报告是不错的,但更有效的是给他们一个交互式图表,让他们可以选择不同的时间表,并查看他们的选择如何影响电力消耗。最近,受交互式情节趋势和不断学习新工具的愿望的启发,我一直在与 Python 库 Bokeh 合作。我为自己的研究项目制作的这个仪表板展示了散景互动功能的一个例子:
虽然我不能分享这个项目背后的代码,但我可以通过一个例子来使用公开可用的数据构建一个完全交互式的散景应用程序。这一系列文章将涵盖使用散景创建应用程序的整个过程。在第一篇文章中,我们将介绍散景的基本要素,我们将在后续文章中对此进行阐述。在整个系列中,我们将使用nyflights 13 数据集,它记录了 2013 年以来超过 30 万次航班。我们将首先专注于可视化单个变量,在这种情况下,以分钟为单位的航班到达延迟,我们将从构建基本直方图开始,这是显示一个连续变量的分布和位置的经典方法。《T4》的完整代码可以在 GitHub 上找到,第一个 Jupyter 笔记本可以在这里找到。这篇文章关注的是视觉效果,所以我鼓励任何人查看代码,如果他们想看数据清理和格式化的乏味但必要的步骤!
散景基础
散景的主要概念是一次一层地构建图形。我们首先创建一个图形,然后向图形添加元素,称为字形。(对于那些使用过 ggplot 的人来说,字形的概念本质上与 geomss 的概念是一样的,geom 一次添加一个“层”。)字形可以呈现许多形状,这取决于所需的用途:圆形、线条、补丁、条形、弧形等等。让我们用正方形和圆形制作一个基本图表来说明字形的概念。首先,我们使用figure方法绘制一个图,然后通过调用适当的方法并传入数据,将我们的字形附加到图中。最后,我们展示我们的图表(我正在使用 Jupyter 笔记本,如果您使用output_notebook调用,它可以让您看到代码正下方的图表)。
这就产生了下面这个略显乏味的情节:
虽然我们可以在任何绘图库中轻松地制作这个图表,但我们可以免费获得一些工具,用于右侧的任何散景绘图,包括平移,缩放,选择和绘图保存功能。这些工具是可配置的,当我们想要调查我们的数据时会派上用场。
现在让我们开始显示航班延误数据。在我们直接进入图表之前,我们应该加载数据并对其进行简要检查( bold 是代码输出):
# Read the data from a csv into a dataframe
flights = pd.read_csv('../data/flights.csv', index_col=0)# Summary stats for the column of interest
flights['arr_delay'].describe()**count 327346.000000
mean 6.895377
std 44.633292
min -86.000000
25% -17.000000
50% -5.000000
75% 14.000000
max 1272.000000**
汇总统计数据为我们的绘图决策提供了信息:我们有 327,346 次航班,最小延迟为-86 分钟(意味着航班提前了 86 分钟),最大延迟为 1272 分钟,令人震惊的 21 小时!75%的分位数仅在 14 分钟,因此我们可以假设超过 1000 分钟的数字可能是异常值(这并不意味着它们是不合理的,只是极端)。在柱状图中,我将重点关注-60 分钟到+120 分钟之间的延迟。
为了创建条形的数据,我们将使用 numpy [histogram](https://docs.scipy.org/doc/numpy-1.14.0/reference/generated/numpy.histogram.html) 函数来计算每个指定条块中的数据点数。我们将使用 5 分钟长度的箱,这意味着该功能将计算每个 5 分钟延迟间隔内的航班数量。生成数据后,我们将它放在一个 pandas 数据帧中,以便将所有数据保存在一个对象中。这里的代码对于理解散景并不重要,但它仍然很有用,因为 numpy 和 pandas 在数据科学中很流行!
"""Bins will be five minutes in width, so the number of bins
is (length of interval / 5). Limit delays to [-60, +120] minutes using the range."""arr_hist, edges = np.histogram(flights['arr_delay'],
bins = int(180/5),
range = [-60, 120])# Put the information in a dataframe
delays = pd.DataFrame({'arr_delay': arr_hist,
'left': edges[:-1],
'right': edges[1:]})
我们的数据看起来像这样:
flights栏是从left到right的每个延误间隔内的航班数。从这里,我们可以制作一个新的散景图形,并添加一个指定适当参数的四边形 glpyh:
制作这个图表的大部分工作来自数据格式化,这在数据科学中并不罕见!从我们的图中,我们看到到达延迟几乎是正态分布,在右侧有一个轻微的正偏斜或严重的尾部。
在 Python 中有更简单的方法来创建基本的直方图,使用几行[matplotlib](https://en.wikipedia.org/wiki/Matplotlib)就可以得到相同的结果。然而,开发散景图所需的回报来自于工具和与数据交互的方式,我们现在可以轻松地将这些数据添加到图表中。
添加交互性
我们将在本系列中讨论的第一种交互类型是被动交互。查看器可以采取这些操作,但不会改变显示的数据。这些被称为检查员,因为他们允许观众更详细地“调查”数据。一个有用的检查器是当用户将鼠标放在数据点上时出现的工具提示,它在散景中被称为悬停工具。
A basic Hover tooltip
为了添加工具提示,我们需要将数据源从 dataframe 改为 ColumnDataSource,这是散景中的一个关键概念。这是一个专门用于绘图的对象,包括数据以及一些方法和属性。ColumnDataSource 允许我们向图形添加注释和交互性,并且可以从 pandas 数据帧中构造。实际数据本身保存在一个字典中,可通过 ColumnDataSource 的 data 属性进行访问。这里,我们从我们的数据帧创建源,并查看对应于我们的数据帧的列的数据字典的键。
# Import the ColumnDataSource class
from bokeh.models import ColumnDataSource# Convert dataframe to column data source
src = ColumnDataSource(delays)
src.data.keys()**dict_keys(['flights', 'left', 'right', 'index'])**
当我们使用 ColumnDataSource 添加字形时,我们将 ColumnDataSource 作为source参数传入,并使用字符串引用列名:
# Add a quad glyph with source this time
p.quad(source = src, bottom=0, top='flights',
left='left', right='right',
fill_color='red', line_color='black')
请注意代码是如何引用特定的数据列的,比如“flights”、“left”和“right ”,使用的是单个字符串,而不是以前的df['column']格式。
散景中的悬停工具
一开始,HoverTool 的语法看起来有点复杂,但是通过练习,它们很容易创建。我们向我们的HoverTool实例传递一个tooltips列表作为 Python 元组,其中第一个元素是数据的标签,第二个元素引用我们想要突出显示的特定数据。我们可以引用图形的属性,比如使用' $ '引用 x 或 y 位置,或者使用' @ '引用源中的特定字段。这听起来可能有点令人困惑,所以这里有一个我们两者都做的悬停工具的例子:
# Hover tool referring to our own data field using @ and
# a position on the graph using $
h = HoverTool(tooltips = [('Delay Interval Left ', '[@](http://twitter.com/f_interval)left'),
('(x,y)', '($x, $y)')])
这里,我们使用“@”引用 ColumnDataSource(对应于原始数据帧的“左”列)中的left 数据字段,并使用“$”引用光标的(x,y)位置。结果如下:
Hover tooltip showing different data references
(x,y)位置是鼠标在图上的位置,对我们的直方图没有太大的帮助,因为我们需要找到给定条形中对应于条形顶部的航班数量。为了解决这个问题,我们将修改工具提示实例来引用正确的列。格式化工具提示中显示的数据可能会令人沮丧,所以我通常在 dataframe 中用正确的格式创建另一列。例如,如果我希望工具提示显示给定条形的整个间隔,我会在 dataframe 中创建一个格式化列:
# Add a column showing the extent of each interval
delays['f_interval'] = ['%d to %d minutes' % (left, right) for left, right in zip(delays['left'], delays['right'])]
然后,我将这个数据帧转换成 ColumnDataSource,并在 HoverTool 调用中访问这个列。以下代码使用引用两个格式化列的悬停工具创建绘图,并将该工具添加到绘图中:
在散景风格中,我们通过将元素添加到原始图形中来将它们包含在图表中。注意在p.quad字形调用中,有几个额外的参数,hover_fill_alpha和hover_fill_color,当我们将鼠标放在工具条上时,它们会改变字形的外观。我还使用一个style 函数添加了样式(代码见笔记本)。美学打字很繁琐,所以我一般会写一个可以适用于任何剧情的函数。当我使用样式时,我保持事情简单,并关注标签的可读性。图表的主要目的是显示数据,只添加不必要的元素会降低图表的有用性!最终的情节如下:
当我们将鼠标悬停在不同的条柱上时,我们会获得该条柱的精确统计数据,显示时间间隔以及该时间间隔内的航班数量。如果我们对我们的绘图感到自豪,我们可以将它保存到 html 文件中进行共享:
# Import savings function
from bokeh.io import output_file# Specify the output file and save
output_file('hist.html')
show(p)
进一步的步骤和结论
我花了不止一个情节来获得散景的基本工作流程,所以如果看起来有很多东西要学,也不用担心。在这个系列的课程中,我们将得到更多的练习!虽然散景看起来工作量很大,但当我们想将视觉效果扩展到简单的静态数字之外时,好处就来了。一旦我们有了一个基本的图表,我们可以通过添加更多的元素来增加视觉效果。例如,如果我们想按航空公司查看到达延迟,我们可以制作一个交互式图表,允许用户选择和比较航空公司。我们将把改变显示数据的主动交互留到下一篇文章中,但我们可以做些什么:
主动交互需要更多的脚本,但是这给了我们一个在 Python 上工作的机会!(如果有人想在下一篇文章之前看看这个情节的代码,这里是。)
在整个系列中,我想强调的是,散景或任何一个库工具都不会是满足你所有绘图需求的一站式工具。散景在允许用户探索图形方面很棒,但对于其他用途,如简单的探索性数据分析,像matplotlib这样的轻量级库可能会更有效。本系列旨在展示散景的功能,为您提供另一个必要的绘图工具。您了解的库越多,您就越有能力使用正确的可视化工具来完成任务。
一如既往,我欢迎建设性的批评和反馈。可以在推特上找到我 @koehrsen_will 。
用 D3.js 和 Dimple 实现数据可视化
周五开源培训:第 9 卷
这个星期,我决定我需要可以在浏览器上看到的高质量的图表。我还想用最少的代码来完成这一切,我想强迫自己开始挖掘用 D3 呈现浏览器端数据的能力。
这些图表需要满足很多要求。但我最想要的是能够快速部署。学习的时间结束了,现在是交付的时候了。
由于我是一名 D3.js 新手,我需要一种方法在短短几天内制作出一些一流的图形。之前用过 C3.js ,很喜欢。但是我想要看起来和感觉上有点不同的东西。我从不满足于让事情变得简单。
嘿酒窝!😏
我发现这个整洁的图书馆叫做酒窝。在使用它之前,我就爱上了它。医生很棒,而且马上就觉得很有趣。它还兼容最新版本的 D3,这很好,因为我想使用不会被废弃的库*。*
关于 Dimple 的理念,我最喜欢的部分是,它是为了利用 D3 而构建的,而不是在最终用户和底层框架之间创建某种“保护层”。
**例如,**在写这篇文章之前,我不知道 D3 有自己的 HTTP 请求工具。如果你碰巧运行着一个代理服务器(阅读这篇文章了解你应该运行的原因),这可以完美地工作,D3 可以为你获取各种各样的好东西,而不需要任何其他库的帮助。
潜入水中🏊
我在 Reddit 上找到了一个很酷的美国总统数据集。我将会用它来制作一些非常有用的图表。我从来没有真正考虑过“总统”的位子。这个名字最初是为了让担任这个职位的人看起来没有首相或国王这样的头衔重要。这个想法似乎最终没有按计划实现,因为它让这项工作看起来不那么宏伟。这些小项目的副作用是收集有时有用,有时不那么有用的琐事知识。
代码挑战!
使用上面列出的美国总统数据集,创建显示以下项目的图表:
- 哪一个政党拥有最多的独特总统?
- 哪个政党担任总统的任期最长?
- 哪个政党的连续选举次数最多
- 哪个政党的连续选举频率最高?
查看我的解套码,查看这个回购。
先做什么?1️⃣
我想看看哪个政党有最多的候选人赢得总统选举的柱状图。不是谁任期最多,而是哪个政党有最独特的总统。
Y axis plotted by ‘Title’ which is last name, first name.
有意思!与民主党总统相比,我们多了四位共和党总统。*注意:*有四位“民主-共和”总统,但我忽略了那个字段,因为我不知道它意味着什么!因为它包含了两个名字,让我们考虑 a 类清洗。
Organized By Term (presidents chronological number)
尽管共和党拥有最独特的头衔持有人,但民主党拥有相同数量的任期!我不会猜到的。但是这里又出现了奇怪的民主-共和分类。在 2017 年,这个概念似乎很陌生。
我快速查了一下维基百科。原来民主共和党(由杰斐逊创建)出现在民主党、共和党甚至辉格党之前!奇怪!
让我们检查一下连续术语
Party victory organized by start of term
民主党和共和党以 7 个连续任期保持了最高的连续胜利数!这是一个单一政党的 28 年。现在我想知道,在如此长的时间里,他们的平均立法是有效还是无效。但是我从这个图表中可以得出的另一个结论是,共和党人连续当选的频率最高。
结论
酒窝很牛逼。尽管它非常简单,但我需要多加练习。最近我喜欢上了 Seaborn 和 Bokeh 。但是对于我现在正在做的一个项目来说,我需要一个不需要 Django 框架就可以在浏览器中做任何事情的东西。
看来我不能离开你。😍
与熊猫的数据争论
“panda eating bamboo” by Jay Wennington on Unsplash
互联网的巨大资源使我进入数据科学的旅程成为可能。《T2 数据科学杂志》将它定义为几乎所有与数据有关的事物。在工作中,这意味着使用数据通过增加价值对组织产生影响。最常见的是使用和应用数据来解决复杂的业务问题。数据科学工作中最常见的步骤之一是数据争论。以下是如何使用熊猫库在 python 中探索、操作和重塑数据的简明指南。
我们将探索一个乳腺癌数据集(鸣谢: UCI ),并使用熊猫来清理、重塑、按摩并给我们一个干净的数据集,所有这些都将有助于大幅提高我们数据的质量。注:数据质量是机器学习算法实现最佳性能的关键。
将涵盖以下熊猫功能:
- 数据浏览 —列、列中的唯一值、描述、重复
- 处理缺失值 —量化每列缺失值,填充&丢弃缺失值
- 重塑数据 —一次热编码、数据透视表、连接、分组和聚合
- 过滤数据
- 其他——制作描述性列,基于元素的条件运算
数据探索
让我们首先将数据集(csv 文件)读入 pandas,并显示列名及其数据类型。还需要花点时间来查看整个数据集。
import pandas as pdfilename = 'data/breast_cancer_data.csv'
df = pd.read_csv(filename)
df.dtypespatient_id int64
clump_thickness float64
cell_size_uniformity float64
cell_shape_uniformity int64
marginal_adhesion int64
single_ep_cell_size int64
bare_nuclei object
bland_chromatin float64
normal_nucleoli float64
mitoses int64
class object
doctor_name object
dtype: object
在数据中,我们有以下由源描述的列—患者 ID: id 号,团块厚度:1–10,细胞大小均匀度:1–10,细胞形状均匀度:1–10,边缘粘附:1–10,单个上皮细胞大小:1–10,裸细胞核:
基于此,我们可以假设patient_id是一个唯一的标识符,class将告诉我们肿瘤是恶性的(癌变的)还是良性的(非癌变的)。除了作为分类特征的doctor_name之外,其余列是肿瘤的数字医学描述。
要记住的事情——如果我们的目标是根据剩余的特征来预测肿瘤是否是癌性的,我们将不得不对分类数据进行编码,并清理数字数据。
从我们的第一个输出中,我们看到bare_nuclei被读取为object数据类型,尽管描述是数字的。因此,我们需要改变这一点。
为了验证我们的数据与源匹配,我们可以使用 pandas 中的描述选项:
*df.describe()*
这简洁地总结了所有数字列的一些统计数据。似乎所有的。对于分类数据,我们可以通过将的值组合在一起:
*df.groupby(by =[‘class’, ‘doctor_name’]).size()*
处理缺失值
对于每个数据集,评估缺失值至关重要。有多少?是错误吗?缺少的值太多了吗?缺失的值相对于其上下文有意义吗?
我们可以使用以下公式来合计总缺失值:
*df.isna().sum()*
既然我们已经确定了我们缺少的价值,我们有几个选择。我们可以用某个值(零、平均值/最大值/按列中值、字符串)填充它们,或者按行删除它们。由于几乎没有丢失的值,我们可以删除这些行,以避免在进一步的分析中扭曲数据。
*df = df.dropna(axis = 0, how = 'any')*
这允许我们删除行和行中的任何缺失值。
检查副本
要查看重复行,我们可以从查看每列中唯一值的数量开始。
*df.nunique()*
我们在这里看到,虽然有 699 行,但只有 645 个唯一的 patient_id。这可能意味着一些患者在数据集中出现不止一次。为了隔离这些患者并查看他们的数据,我们使用后面的:
*df[df.duplicated(subset = 'patient_id', keep =False)].sort_values('patient_id')*
该行按顺序显示所有重复的患者编号。还可以查看患者在数据集中出现的次数。
*repeat_patients = df.groupby(by = 'patient_id').size().sort_values(ascending =False)*
这表明一个患者在数据中出现了 6 次!
过滤数据
如果我们想删除在数据集中出现 2 次以上的患者。
*filtered_patients = repeat_patients[repeat_patients > 2].to_frame().reset_index()filtered_df = df[~df.patient_id.isin(filtered_patients.patient_id)]*
如果我们没有波浪号(“~”),我们将得到重复两次以上的所有个体。通过添加波浪符,熊猫布尔序列被反转,因此得到的数据帧是那些不重复超过两次的数据帧。
重塑数据
数据集在“doctor_name”列中有分类数据的元素。为了将这些数据输入到机器学习管道中,我们需要将其转换为一个 hot 编码列。这可以通过一个科学工具包学习包来完成,但是我们将在熊猫身上做,以演示旋转和合并功能。首先用分类数据创建一个新的数据框架。
*categorical_df = df[['patient_id','doctor_name']]
categorical_df['doctor_count'] = 1*
我们增加了一列,一个额外的列,用来识别病人与哪个医生联系。旋转该表,使单元格中只有数值,列成为医生的姓名。然后用 0 填充空单元格中的。
*doctors_one_hot_encoded = pd.pivot_table( categorical_df,
index = categorical_df.index,
columns = ['doctor_name'],
values = ['doctor_count'] )doctors_one_hot_encoded = doctors_one_hot_encoded.fillna(0)*
然后删除多索引列:
*doctors_one_hot_encoded.columns = doctors_one_hot_encoded.columns.droplevel()*
“doctors_one_hot_encoded”数据帧如下所示。
我们现在可以将它重新加入我们的主牌桌。熊猫的左连接通常是这样的:
*leftJoin_df = pd.merge(df1, df2, on ='col_name', how='left')*
但是我们是在索引上连接,所以我们传递“left_index”和“right_index”选项来指定连接键是两个表的索引
*combined_df = pd.merge(df, one_hot_encoded, left_index = True,right_index =True, how =’left’)*
生成的左连接如下所示。
我们可以通过下面的方法删除不再需要的列
*combined_df = combined_df.drop(columns=['doctor_name']*
逐行操作
数据争论的另一个关键部分是能够进行行或列操作。这方面的例子有:根据列的值重命名列中的元素,并创建一个新列,该列根据行中的多个属性生成特定的值。
在本例中,我们创建一个新列,根据属性将患者单元分类为正常或异常。我们首先定义我们的功能以及它将执行的操作。
*def celltypelabel(x): if ((x['cell_size_uniformity'] > 5) & (x['cell_shape_uniformity'] > 5)):
return('normal') else:
return('abnormal')*
然后,我们使用 pandas apply 函数在数据帧上运行 celltypelabel(x)函数。
*combined_df['cell_type_label'] = combined_df.apply(lambda x: celltypelabel(x), axis=1)*
结论
虽然其中一些数据操作步骤可以在 SAS 和 excel 中完成。用 python 来做不仅可以让你将数据连接到计算机视觉、机器和深度学习中的大量开源资源,还可以用于 ETL 自动化目的等等。
感谢您的阅读。
穆罕默德·拉扎
Data2Vis:使用序列间递归神经网络自动生成数据可视化
A collection of random examples generated by the model given datasets from the RDataset collection..
我们将数据可视化公式化为一个序列到序列的翻译问题。
TLDR;我们训练一个模型,该模型可以接受数据集作为输入,并生成一组可信的可视化作为输出。
我们有一份早期的论文草稿,描述了关于 arxiv 、短视频、源代码和演示的工作。欢迎反馈、讨论( @vykthur 、 @serravis )和评论!
Demo web app. We provide a web interface where a user can paste data (or load a random dataset) and get set of generated visualizations (using beam search).
想尝试训练一个类似的模型吗?我们已经公布了实验中使用的源代码。
Data2Vis:使用序列对序列递归神经网络自动生成数据可视化…
github.com](github.com/victordibia…)
这项工作是与一位同事(Cagatay Demiralp) 共同完成的,从我们在一次论文讨论会后的一次谈话开始。我们读过一些论文,其中使用了各种形式的生成和序列模型来创建各种各样的东西——从生成图像(gan)、音乐、源代码、图像标题到生成关于图像的问题和答案(VQA)等。尽管这些模型有时会有一些怪癖(独眼猫、最终缺乏自然感觉的音乐等),但当它们被大规模训练和部署时,它们都展示了价值的承诺。
因此,我们很好奇将这些深度学习模型中展示的进步应用于创建可视化任务的可能性。这是有价值的,因为可视化创作可能是一个耗时的过程(选择使用什么工具、可视化哪些字段、应用哪些转换以及支持哪些交互)。如果我们能找到一个模型来做这些事情,我们觉得可以改进可视化创作过程。第一个挑战与寻找制定问题的方法有关,这样它就可以服从深度学习*。我们如何教会模型理解,然后生成可视化?我们如何管理我们的训练数据集?我们是否收集了可视化+数据对的好图像,并在这些图像上进行训练?*
幸运的是,研究人员过去已经考虑过与改进可视化创作相关的类似问题。一项有趣的工作是围绕着创建声明性语言的努力,这些语言简洁地描述了可视化,并在创作可视化时提供了表达性和速度之间的"右"权衡。有了这些语言或语法,你可以编写一些简洁的文本规范,一些引擎会努力将文本转化为实际的可视化。这种声明性语言的一个很好的例子是由华盛顿大学交互数据实验室的令人敬畏的研究人员创造的 Vega-Lite 。对于我们的实验,我们使用 Vega-Lite 语法规范(JSON)来表示可视化。
A Vega-Lite visualization (left), specified using the Vega-Lite grammar (right).
我们做了什么
如果我们把可视化想象成文本规范,问题就简化成生成一堆这些规范,给一些输入数据。因此,我们的下一个任务是探索学习可视化规范格式的空间的模型,也许可以“幻觉”一些新的可视化?我们根据卡帕西的博客中的笔记开始了最初的实验——我们生成了可视化规范(数据+规范),将它们连接起来,并试图训练一个 RNN 来生成看似合理的东西。
从第一次实验中,我们了解了字符 rnn(lstm)在学习嵌套结构(如 Vega-Lite 规范)中包含的数据结构方面的表现。我们发现,虽然基于字符的 rnn 对于该问题工作得很好(与基于单词的 rnn 相比),但是它们在模拟长序列方面的局限性和对长序列的存储要求使得它们难以训练(在几个字符之后的难以理解的记号,即使在数千个步骤之后也有限的学习进度,未经预处理的数百万个参数)。此外,字符 RNNs 没有为训练提供输入和输出对之间的具体映射。这些挑战促使我们思考其他方式来表述问题——学习我们输出空间的结构,并在给定一些输入的情况下生成新数据的方法。本质上是从源数据规范到目标可视化规范的转换。
序列到序列模型
序列对序列模型[1,2,3]能够做到这一点—它们接受一些输入(例如一种语言的文本)并生成一个输出(例如不同语言的相同文本)。它们已经被广泛成功地应用于诸如语言翻译、文本摘要、图像字幕等问题。利用编码器解码器架构的序列到序列模型的变体也已经被探索用于正式编程语言、学习领域特定程序和通用程序合成之间的翻译。
We train a sequence to sequence model based on the work of Britz et al 2017. Training data consists of a source token (a single row from a data set) and a target token (a valid Vegalite visualization specification) pair.
数据和培训
首先,我们基于有效的 Vega-Lite 可视化示例组装了一个训练数据集(源和目标对)。基于 11 个不同的数据集和 4300 个 Vega-Lite 示例,总共使用了 215k 对样本。请参见论文了解更多关于数据采样和预处理的详细信息。
我们使用架构和 Britz 等人 2017 提供的样本代码训练一个序列到序列模型。该模型是一个具有注意机制的编码器-解码器架构(请参见 Britz 论文了解关于该架构的更多细节)。我们还在数据集上执行一些简单的标准化来简化训练。我们在源序列(数据集)中使用一个短符号“str”和“num”来替换字符串和数字字段名。接下来,在目标序列中复制类似的反向转换(后处理),以保持字段名的一致性。这些转换有助于通过减少词汇量来构建学习过程,并防止 LSTM 学习字段名(在训练集中学习字段名对我们的生成问题没有用)。反过来,我们能够减少总的源和目标序列长度,减少训练时间,并减少模型需要收敛的隐藏层的数量。
波束搜索解码
为了探索各种生成的可视化,我们使用波束搜索解码算法。与输出输入序列的最有可能(最高概率)的翻译相反,波束搜索在生成期间扩展所有可能的后续步骤,并保持最有可能的 k ,其中 k 是用户指定的参数,称为 波束宽度 。与传统的语言翻译系统不同,在传统的语言翻译系统中,波束搜索主要用于通过最大化生成序列的条件概率来提高翻译质量,我们还探索波束搜索作为一种通过输出所有并行波束结果来生成一组多样的候选可视化的方式。通过波束搜索,我们观察到模型生成了不同的图,探索了图表类型的组合和多个变量的使用。
早期结果
Examples where the model has learned to generate univariate plots that summarize fields selected from the dataset.
**
Examples of generated visualizations where the model has learned common selection patterns and leverages concepts such as responses (yes, no) and sex (male, female)
为了评估这个模型,我们使用了 Rdataset 存储库(被清理并转换成有效的 JSON 格式),它不包括在我们的培训中。生成的有效单变量和多变量可视化的范围表明该模型捕获了可视化生成过程的各个方面。随着训练的进行,模型逐渐学习有效 Vega-Lite 规范的词汇和语法,学习使用引号、括号、符号和关键字。该模型似乎还学会了在 Vega-Lite 语法中使用正确类型的变量规范(例如,它正确地为文本字段分配了字符串类型,为数值字段分配了数量类型)。
定性结果还建议在适当的字段上使用适当的转换(箱、集合)(例如,在数值字段上执行均值)。该模型还了解可视化中出现的常见数据选择模式,以及它们与其他变量的组合,以创建二元图。例如,当专家创建可视化时,通常按地理(国家、州、性别)、个人特征(公民身份、婚姻状况、性别)等对数据进行分组。早期的结果表明,我们的模型开始学习这些模式,并将其应用于可视化的生成。例如,它学习使用常见的顺序字段(如回答(是/否)、性别(男性/女性)等)对数据进行子集划分,并根据其他字段绘制这些值。最后,在所有情况下,模型都会生成一个 完全有效的 JSON 文件和有效的 Vega-Lite 规范,其中包含一些小的失败案例。
A collection of random examples generated (beam width k = 15) by the model given some test dataset. These examples demonstrate initial results on the model’s capabilities in generating plausible visualizations and its current limitations (cases where the model generates non-existent fields or uses transforms that result in invalid charts).
正如许多深度学习模型所观察到的,有有希望的结果,也有怪癖或失败的案例(例如,5 条腿的狗,3 只眼的猫等)。在我们的例子中,模型有时使用不存在的(幻象变量)生成规范,或者应用在运行时无效的转换,导致空图。见上图(用橙色标记的图)。我们的直觉是,这些挑战可以通过扩大我们相对较小的数据集来解决。
为什么要用深度学习实现可视化自动化?
我们认为可视化的自动生成有趣的原因有几个:
使可视化创作更加容易。
为很少或没有编程经验的用户提供快速创建表达性数据可视化的能力,增强了他们的能力,并将数据可视化带入他们的个人工作流。对于专家来说,像 Data2Vis 这样的模型也有可能“播种”数据可视化过程,减少指定可视化语言语法和支持可视化可能性迭代所花费的时间。这种动机与探索源代码生成和翻译的深度学习方法的其他研究工作产生了共鸣( UCBerkeley ,微软研究院等)。
规模的承诺
从理论上讲,随着我们收集更多多样和复杂的训练例子(也许还有更好的架构),DataVis 应该学习更复杂的可视化策略,增加它的效用。这有望创造一个模型,有朝一日可能在数据可视化方面达到人类水平(或超人水平),就像我们在图像识别、游戏、医学成像等其他领域观察到的那样。
超越“启发式”
现有的可视化生成方法倾向于基于规则和试探法,这些规则和试探法生成单变量汇总图或基于规则组合的图。在这种情况下, Jeff Dean 发现了使用机器学习(ML)改进这种系统的机会,并鼓励“学习”和“元学习”一切方法。他还强调了启发式的挑战——它们不适应使用模式,并且可能无法考虑有价值的上下文。我们也同意,ML 和 AI 方法通过从现有的训练例子中学习,提供了超越基于规则的方法的机会。
下一步是什么
这个项目仍然是非常进展中的工作-有限制和未来的工作,我们希望解决。除了收集更多的数据和运行实验来改进 Data2Vis,一些未来的工作包括:
扩展 Data2Vis 以生成多种可信的可视化效果
Data2Vis 目前被实现为序列到序列的转换模型,并为给定的数据集输出单个可视化规范。现在,训练一个模型来为给定的数据集生成多个有效的可视化效果不是很棒吗?我们认为会的。
瞄准附加语法
也许训练模型可以将输入数据映射到多种不同的可视化规范语言(例如 Vega-Lite、ggplot2、D3 等。).或者将可视化从一种语言翻译成另一种语言。通过跨语言、平台和系统重用可视化规范,这将有助于使可视化更易访问。
自然语言和可视化规范
Text2Vis 怎么样?-除输入数据外,还使用自然语言文本生成可视化效果的模型。想象一个人在大屏幕前探索数据,并要求基于特定的字段*、转换或交互的可视化?*
结论
本文讨论了使用序列对序列模型自动生成可视化的一些想法和早期结果。希望这项工作可以作为未来使用深度学习方法自动生成可视化的基线。请随意阅读论文了解更多详情,也分享你的想法( @vykthur , @serravis )!我们希望很快能分享代码和训练好的模型。
确认
这项工作是由许多个人的贡献促成的。感谢 Vega-Lite 、 Voyager 库的作者,感谢他们分享了我们实验中使用的示例数据。非常感谢tensor flowseq 2 seq模型实现和 TensorFlow 库团队的作者——他们的工作使我们能够快速了解序列模型和原型,我们的实验将很少以前的经验。**
参考
[1] Dzmitry Bahdanau、Kyunghyun Cho 和 Yoshua Bengio。2014.通过联合学习对齐和翻译的神经机器翻译。(2014 年 9 月)。
[2] Ilya Sutskever、Oriol Vinyals 和 Quoc V. Le。2014.用神经网络进行序列对序列学习
[3] Denny Britz、Anna Goldie、Thang Luong 和 Quoc Le。2017.神经机器翻译架构的大规模探索
[4]豪尔赫·波科和杰弗里·赫尔。2017.可视化逆向工程:从图表图像中恢复可视编码。计算机图形学论坛。EuroVis) (2017)。
数据库术语:分区
您的老板告诉您学习分区主题来扩展您的数据库或了解您公司的数据库设计。你搜索互联网,然后你来到这里。欢迎!下面是对数据库表分区关键点的简要介绍:
数据库表分区:
vertical vs horizontal partitioning
首先,我们需要了解数据库(db)通常是如何设计的。数据库由表组成,表有列和行——每个表可以有多个或几个。分区一个数据库(为了方便起见,假设我们的数据库只包含一个大表)是将非常大的表分成多个小表的地方。这样,查询只访问数据的一部分,因此可以运行得更快,因为需要搜索的数据更少。分区的任务是帮助维护大型表,并减少为 SQL 函数读取和加载数据的总体响应时间。
垂直分区与水平分区
垂直分区将表&/或列存储在单独的数据库或表中。
水平分区( 分片 ) 将一个表的行存储在多个数据库簇中。
分片使我们的数据更容易一般化,并允许集群计算(分布式计算)。如果数据集太大,无法存储在一个数据库中,就需要进行分片。最重要的是,分片允许数据库根据数据增长来扩展 T21。它还减小了表的大小(更确切地说是索引的大小),从而提高了搜索性能。
分片(水平分区)的通用规则/指南:
- 每个数据库服务器在结构上必须相同。
- 数据记录在分片数据库中被合理地划分。
- 每个数据记录只存在于一个碎片中(审计表/备份除外)
分区优势:可伸缩性,提高大型数据库的速度
缺点:使编程和操作更加复杂
对数据库进行分区的 SQL 示例
Add Filegroups to partition by month SQL example
Create a Partition function for each month’s records
Create a Partition Scheme to map the partitions of a partitioned table to filegroups
Create the table using the PartitionBymonth partition scheme, and fill it with data
接下来,我们将谈论我们的下一个数据库关键术语:索引。
数据库— SQL 和 NoSQL
对 NoSQL 数据库的介绍和对 MongoDB 的一瞥
SQL 在 1970 年与 E. F. Codd 博士的研究论文“大型共享数据库的关系数据模型”一起使用。是啊!!这是 Boyce-Codd 规范化中的 Codd。
SQL/NoSQL: url
NoSQL 在 20 世纪 90 年代后期开始出现。然而,在此之前就已经有这样的数据库了。NoSQL 是带着打破传统事务型数据库瓶颈的动机引入的。一个常见的误解是 NoSQL 数据库不是关系型的,这是不太正确的。关系确实存在于数据中,否则这些数据将毫无用处。让我们看看发生了什么变化以及如何变化。
布鲁尔帽定理和酸性
CAP 定理—与 NoSQL 有关
CAP 定理试图证明 NoSQL 数据库所期望的性质。大多数数据库被设计成以另一个属性为代价来实现这些属性中的两个。
C —一致性 这证明了一旦更新被更新者确认,更新的执行和可用性的保证。简而言之,如果数据库是一致的,更新一旦完成就可用,这在分布式环境中是无法保证的。
A —可用性 这展示了一个数据库的属性,它能够服务于一个请求。如果加载/执行时间较长,大多数 SQL 数据库都会删除查询。通过消除 SQL 数据库中存在的事务属性,NoSQL 数据库的可用性预计会非常高,响应时间预计会非常短。
P —分区容差 数据库能够在节点间因网络问题出现故障时正常运行的属性。例如,一个数据库可能包含几个协同工作的节点(MongoDB 节点)(通过 Mapreduce 之类的机制)。如果在分布式环境中,即使一个或多个节点不可访问,数据库作为一个整体也可以运行,则保留该属性。
ACID — SQL 数据库
ACID 属性是传统关系数据库(如 MySQL、MS SQL 或 Oracle 数据库)中的预期属性。
A —原子性 保证原子操作的性质,要么一组查询可以作为一个整体完成,要么一个都不能。这是交易的关键特征。
C —一致性 数据一旦完全插入或更新,即可使用。
I —隔离 暗示,事务是独立的。因此,数据不会受到发生在同一组数据上的两个事务的负面影响。
D —耐久性 事务或任何其他操作后提交的数据永不丢失。要么插入它们,要么通知失败(失败的事务)。
NoSQL 数据库
NoSQL 数据库保证遵守两个 CAP 属性。这种数据库有几种类型。
- 键值存储 — 哈希表形式的存储{例如- Riak、亚马逊 S3(迪纳摩)、Redis}
- 基于文档的 存储— 存储对象,主要是 JSON,它是 web 友好的或支持 ODM(对象文档映射)。{示例- CouchDB、MongoDB}
- 基于列的存储— 每个存储块只包含来自一列的数据{示例- HBase,Cassandra}
- 基于图形— 关系的图形表示,主要用于社交网络。{示例- Neo4J}
MongoDB
MongoDB 是广泛使用的 文档数据库 之一。它服从 CAP thoerem 中的 C 和 A 。由于其 JSON 友好的 API,MongoDB 大量用于 NodeJS。
MongoDB: url
Mongo 官网有安装说明:www.mongodb.com。让我们考虑几个使用 MongoDB CLI 的命令。
进入命令行界面:mongo并按回车键。如果安装成功,您应该会看到以下输出。通常 mongo DB 运行在端口 27017 上。
*# mongoMongoDB shell version v3.4.5connecting to: mongodb://127.0.0.1:27017MongoDB server version: 3.4.5Server has startup warnings:2017-09-15T11:42:04.673+0000 I STORAGE [initandlisten]2017-09-15T11:42:04.673+0000 I STORAGE [initandlisten] ** WARNING: Using the XFS filesystem is strongly recommended with the WiredTiger storage engine2017-09-15T11:42:04.673+0000 I STORAGE [initandlisten] ** See http://dochub.mongodb.org/core/prodnotes-filesystem2017-09-15T11:42:05.313+0000 I CONTROL [initandlisten]2017-09-15T11:42:05.313+0000 I CONTROL [initandlisten] ** WARNING: Access control is not enabled for the database.2017-09-15T11:42:05.313+0000 I CONTROL [initandlisten] ** Read and write access to data and configuration is unrestricted.2017-09-15T11:42:05.313+0000 I CONTROL [initandlisten]
>*
显示数据库:show dbs
启动、选择数据库和执行插入。
插入 回车use mydb会创建一个新的数据库名 mydb 。与 MySQL 中的表不同,Mongo 有集合。文档(JSON)被插入到这样的集合中。让我们创建 狗 集合,并添加一个 狗 名称莫莉 与 重量 50kg。 既然我们选择了 mydb 我们就可以称之为 db 并执行运算。
*db.dogs.insert({name: ‘Molly’, weight:50})*
查看数据 使用 pretty 方法将输出格式化为可读性更强的格式。正如您所看到的,mongo 添加了一个 id 名称_id作为每个文档的索引。
*> db.dogs.find()
{ "_id" : ObjectId("59bbce8288b6c364cefd9de6"), "name" : "Molly", "weight" : 50 }
> db.dogs.find().pretty()
{
"_id" : ObjectId("59bbce8288b6c364cefd9de6"),
"name" : "Molly",
"weight" : 50
}
>*
更新数据 下面的命令更新我们创建的条目,并将名称设置为 Tommy。
*> db.dogs.update({_id: ObjectId('59bbce8288b6c364cefd9de6')},
{$set: {name:'Tommy'}})*
删除数据 让我们删除我们插入的内容
*> db.dogs.remove({_id:ObjectId('59bbce8288b6c364cefd9de6')})
WriteResult({ "nRemoved" : 1 })
>*
这些是任何数据库的基本操作,或者简称为 CRUD 操作。
C —创建 R —读取 U —更新 D —删除
数据清理战斗机
当我最终开始着手处理xfiew数据集时,我发现测试集和评估页面被锁定了……这对我的计划来说是一个小小的打击。它可以用作任何卫星图像任务的训练数据,但如果你想看看你的模型在完成的 Kaggle 比赛中排名如何:那现在还不太可能。
所以我回到了 fast.ai 的一个暂停的家庭作业项目:战斗机探测器。这个想法很简单:训练一个飞机分类器,然后把它变成一个探测器。为什么是战斗机?即使我不得不看着它们几个小时,我也不会发疯。
注 :你可以跟着我用的工作笔记本一起来,这里。
1.获取数据
fighter-jets-datacleaning.ipynb
我在十月份开始这个项目,但是想给它一个新的开始。我已经下载并清理了数据,这是我不想重复的经历。主要的障碍是云机器上缺少一个 GUI。我使用 Jupyter 通过用 matplotlib 显示 16 个一批的图像来解决这个问题。有问题。如果不花时间学习小部件,我就找不到输入的方法,所以我不得不将一个 2 层 for 循环分解成一系列必须按正确顺序运行的单元格。
This is not how you want to do things.
我给文件编号,并输入不属于它的图片的编号。这既耗时又复杂。最糟糕的是,这个过程很难重复或改变。你做了一次就再也不回来了。工作不应该是这样做的。在进行的过程中,我记下了事情可以做得更好的方法,但是已经来不及实施了。
先不说:回顾你在学习期间所做的工作,有趣的是,你似乎一直在寻找完成一项任务最难的方法。我经常听到研究人员在发现一个更简单的算法优于之前更复杂的算法时表示惊讶,比如 OpenAI 的 PPO。但是我觉得复杂性通常是对理想解决方案的不完美近似的结果。
这一次我更有条理地处理事情。我在旧剧本中使用的搜索术语是“龙卷风”。猜猜当你用谷歌图片搜索时会发生什么?这一次我使用了一个 javascript 命令,它可以捕捉到你可以看到的图片的链接,而不是基于 chromedriver 的命令行脚本。这让我可以优化搜索。
获取数据非常容易。当您看到想要的图像时,可以通过在浏览器控制台中输入 javascript 命令(cmd-alt-i 或-j)来构建 url 文件。然后对它们运行 for 循环,调用 fastai 的download_images函数:
*# download dataset*
*for url_path in urls.ls():*
*aircraft_type = url_path.name.split('.')[0] # get class name*
*print(f'downloading: {aircraft_type}')*
*dest = path/aircraft_type; dest.mkdir(parents=True, exist_ok=True) # set & create class folder*
*download_images(url_path, dest)*
其中url_path是指向您的 url 文件(.csv 或。txt),假设它们是按类命名的。我在打印一个 python f 串,非常有用。
1.1 跟踪数据
现在当你这样做的时候有一个问题。我在我的 Mac 上这样做的目的是,我可以利用我的操作系统的 GUI 来快速删除我不想要的图像。为了让这个项目对我来说更专业,我不能把所有清理过的图像都复制到我的云机器上。我需要建立一个好的下载链接列表。问题是 fastai 的download_images将文件重命名为连续整数:0000001.jpg、00000002.jpg等。有两个解决方案:保存 url 文件名,或者存储文件名和 URL 之间的映射。
我先尝试了前者。我让它工作了,但是它并不漂亮,并且引入了两个新问题:
- 编码为 utf8 字节的非拉丁字母
- 使用相同文件名的链接
字符编码问题一开始非常混乱,特别是因为 URL 在地址栏中显示为解码后的,并且只有当我复制了整个地址而不是其中的一部分时才显示出来。这是一个简单的解决方案:
第二期不会有任何进展。所以我决定弄清楚如何映射文件名和 URL。我在这里遇到了一系列令人困惑的问题。为了保存 url 文件名,我不得不改变 fastai 保存下载图像的方式。为了做到这一点,我必须编辑由download_images调用的_download_image_inner,全部来自 fastai.vision.data。解码 utf8 字节对此是必要的(转念一想,我不确定它是否是),但从那里开始就很简单了。为了将文件名映射到 URL,我必须更深入一层,编辑由_download_image_inner调用的download_image。
我首先尝试在全局范围(任何函数之外)定义一个字典,然后让download_image更新它。但是那没有用。我将字典定义为以下格式:
{class : {filepath : url}}
其中在download_images中指定飞机的当前等级,其值为空dict,在download_image中用filepath:url对填充dict。
顶层的 class-key 会被赋值,但是底层的dict都是空的。download_image中没有存储任何内容。我不想花时间去理解 python 的global关键字,我想这是因为我的url_fname_dict的一个副本正在低级函数中被写入。我打印了一份,事实上键:值对被分配到了download_image中,但是原来的字典最后仍然是空的。
所以我创建了一个类对象来保存字典,而不必担心范围。经过一些调试后,我让它工作了,并为我的班级感到骄傲。
旁白:这实际上是我第一次真正开始使用面向对象编程的方式。我从来没有真正理解过课堂上发生的事情。在 fastai 和我自己的项目的深度学习中,我遇到了不得不在一堆函数中上下传递数据的难题。你要么必须有大量的参数——这很容易导致混乱并限制了你的工作方式——要么你使用一个附加了你只能访问的数据的类对象。
ImageDownloader class at the end of a debugging session. Downloading is commented out, and I’m testing if anything gets stored in the dictionary
而新的downloader.url_fname_dict …仍然是空的。我在这个问题上花了几个小时,找到了十几种方法来解决这个问题,直到我有了一个想法:
“我过去对使用多个流程的
download_images有疑问,对吗?等等,用max_workers=8有 8 个进程试图同时在字典中存储数据 Python 能处理吗?我觉得它只是…不是。”
我对此进行了测试,将max_workers设置为-1、0 和 1(我不知道 0 和-1 是指使用每个核心和很多进程,还是只使用一个,所以我测试了它们)。它非常有效。
在堆栈溢出上有一个线程。我不知道本身是否与我的问题有关,但它感觉足够正确,我不想陷入多重处理/线程。所以结果是我根本不需要上课。
399 file-url maps stored when max_workers ≤ 1; 0 stored when > 1
所以这奏效了。又拿了。永远。只有一个过程,记得吗?这意味着如果一个链接花了 10 秒钟下载…你在等待。数据集中大约有 10,500 张图片。然后我意识到一件很棒的事情。
我可以把这该死的东西打印出来。全速前进。
旁白:我在努力磨练做事的方式,为我经营公司做准备。所以问题变成了:在我自己的公司里,我能接受这样做吗?有趣的是:对于强力快速“搞定”类型的交易,答案并不总是否定的。有时你可以通过牺牲一点“优雅”或自动化来获得更多的灵活性。在这种情况下,简单的解决方案要强大得多,因为它可以在命令行上运行并写入磁盘。我被卡在了 Jupyter 笔记本范式里,放手了,收获了很多。
然后复制粘贴到一个文本文件,并运行几个 regex 过滤器来提取文件路径及其对应的 url,然后将它们放入您的字典。
我做了 4 个正则表达式过滤器:
fail_pat = re.compile(r'Error \S+') *# split*
clas_pat = re.compile(r'downloading: \S+') *# split*
save_pat = re.compile(r'data/\S+')
link_pat = re.compile(r'\s-\s\S+') *# split*
那些带有‘split’注释的需要删除部分过滤文本,因为我还不知道如何在 regex 中做到这一点。这些字典仅仅是:
removal_urls = defaultdict(**lambda**:[])
file_mapping = defaultdict(**lambda**:{})
然后构建文件-url 映射,并删除 url 的第一位:
urls that don’t download are the first additions to remove_urls
一切终于准备好打扫了。
2.数据清理
清理该数据集有 3 个部分:
- 验证下载(删除损坏/不完整的图像)
- 外观检验
- 更新 URL 文件
实际上,我做了 2 的一部分。1 之前。,但结果是一样的。FastAI 有一个有用的功能来验证图像是可见的,并且有 3 个颜色通道,还可以让你调整它们的大小。原始数据集约为 2.27GB,调整到最大 500px 后,大小降至 141MB。
旁白:我喜欢你可以在笔记本电脑上完成许多深度学习任务。GPU 仅用于训练密集模型&数据。调整大小和扫描 6-10k 张+2GB 大小的图片只需要一两分钟。
2.1 目视检查
这很有趣,触及了人工智能中一个沉闷而有争议的部分。
我有意见。
很多人使用亚马逊的机械土耳其人(顺便说一下,可能是一个可疑的名字)来批量标记数据。我不喜欢为不动脑筋的工作支付报酬。想象一下约束条件:
你一个人,没有钱。找到一个聪明的方法来做到这一点。
现在,我不能应用资金和资源总是存在的假设来直接解决问题,因为当我看我的银行账户时,这个假设不成立。因此,与其买断这个问题,你必须考虑更多的移动部件,以及它们是如何装配在一起的。如果你是这个系统的的一部分,除了是这个系统的建筑师,你还适合在哪里?
就视觉而言,我们非常善于挑选出特别的一个。其他 fast.ai 学生制作了一个 Jupyter 小工具,让你可以查看、删除或重新分类图像。它会创建一个. csv 文件来反映您的更改,因此数据集不会被更改。我肯定会这么做。一个问题是:该界面是为较小的数据集设计的,如果不使用 PyTorch 数据加载器,就无法关闭训练集的混合:
A step in the right direction; but this just isn’t going to cut if for 10,000+ images.
如果我能有一整屏的图片来复习,如果我能按班级来复习,那将会是最完美的。这可能需要对 ImageCleaner 小部件进行大量的编辑,并学习如何构建 Jupyter 小部件。我不想在那上面花时间。这也可能是不切实际的,因为这很可能是图像清理器的发展方向。
但是通过放弃一点功能,我可以获得很多可用性。
事实证明,在我的操作系统上,我几乎可以得到我需要的东西,以及我想要的方式:
A bit small though. Can we get a balance between ‘batch size’ and visibility?
Perfect.
这就是将文件名映射到 URL 的工作得到回报的地方。我可以删除任何我想要的图像,并记录对数据集的更改。我开始滚动并挑出损坏的图像(没有图片的文件图标)和明显不属于它的图像。对于 27 个班级的 10,241 张图片,这需要 20 分钟。FastAI 的图像验证器只需 1/20 的时间就能处理这个问题。
我想要更好的控制,所以我使用了 macOS 的图库视图来扫描每张图片:
Do I want to recognize silhouettes? I don’t know if this would hurt or help the model so I’ll leave it in. Oh, and I think the artist is Ivan Krpan.
That is definitely an Su-30, not an Su-34.
浏览每张照片花了大约 3 . 15 小时。重要的是,我可以选择我想要的细粒度控制。如果我保持图标视图,我可以在一个小时内浏览整个数据集。
2.2 更新数据集
我使用了一个包含文本文件的文件夹,其中包含了构成我的数据集的图像的 URL。由于之前所做的工作,这很容易更新。
a more efficient way to lookup filenames is to store in a [dict](https://docs.python.org/3/tutorial/datastructures.html#dictionaries) then lookup instead of searching an array. This is O(1) instead of O(n/c)
这主要是重用和修改以前的代码。首先,为每个类文件夹构建一个文件路径列表。然后,对于字典中的每个 filepath 键——对应于当前的类——检查它是否在您的列表中。如果不是:将其 URL 添加到removal_urls字典中。
然后,对于每个类,您打开并读取 URL 文件到一个列表中— 而不是添加存在于您的removal_urls字典中的 URL——然后覆盖该文件。
现在,您有了一组 URL 文件,其中包含指向属于您的数据集中的图像的链接。如果你愿意,你可以选择保存字典。 PyTorch 对这个很有用(如果你定义了一个带有lambda函数的defaultdict,你必须把它转换成一个 [dict](https://stackoverflow.com/a/38456371)):
torch.save(dict(file_mapping), path/'file_mapping.pkl')
torch.save(dict(removal_urls), path/'removal_urls.pkl')
仅此而已。你可以使用 github 或其他任何东西将 URL 文件复制到云 GPU 机器,然后在那里下载图像。我运行了最后一次检查,以查看清理后的数据集有多大:
tot = 0
for clas in aircraft_types: tot += len((path/clas).ls())
tot
是 6373。从最初的 10,241。
3.又来了
对于一个 10k 图像数据集的零开始,我估计粗略的视觉检查需要 1 个小时,详细的检查需要 4 个多小时。我在这里展示了很多代码,但那是发现过程。在修改下载代码以打印文件路径和 URL(并获得您的 URL)之后,再做一次,您真正需要的是:一个下载块,
*for url_path in urls.ls():*
*aircraft_type = url_path.name.split('.')[0] # get class name*
*print(f'downloading: {aircraft_type}')*
*dest = path/aircraft_type; dest.mkdir(parents=True, exist_ok=True) # set & create class folder*
*download_images(url_path, dest)*
(将打印输出保存到文本文件中)。一行用于验证,
**for** c **in** aircraft_types:
verify_images(path/c, delete=**True**, max_size=500)
(做个目测);然后正则表达式过滤器:
fail_pat = re.compile(r'Error \S+') *# split*
clas_pat = re.compile(r'downloading: \S+') *# split*
save_pat = re.compile(r'data/\S+')
link_pat = re.compile(r'\s-\s\S+') *# split*
这项工作由三个模块完成。一个记录断开的链接(从打印输出):
**with** open(download_printout_path) **as** f:
**for** line **in** f:
*# update class*
aircraft_type = clas_pat.findall(line)
clas = aircraft_type[0].split()[-1] **if** aircraft_type **else** clas
*# search download path & url*
save,link = save_pat.findall(line), link_pat.findall(line)
**if** save **and** link:
link = link[0].split(' - ')[-1]
file_mapping[clas][save[0]] = link
*# search failed download url*
fail_link = fail_pat.findall(line)
**if** fail_link: removal_urls[clas].append(fail_link[0])
另一个记录清除的文件:
*# lookup urls of missing files in file_mapping & add to removal_urls*
**for** clas **in** aircraft_types:
flist = (path/clas).ls() *# pull all filepaths in class folder*
**for** fpath **in** file_mapping[clas].keys():
**if** Path(fpath) **not** **in** flist:
removal_urls[clas].append(file_mapping[clas][fpath])
以及更新 url 文件的最后一个块:
**for** aircraft_type **in** removal_urls.keys():
fpath = path/'fighterjet-urls'/(aircraft_type + '.txt')
*# open file; read lines* **with** open(fpath) **as** f: text_file = [line **for** line **in** f]
**for** i,line **in** enumerate(text_file):
line = line.rstrip() *# remove trailing /n for searching
# remove line from text file*
**if** line **in** removal_urls[aircraft_type]: text_file.pop(i)
# overwrite url files
**with** open(fpath, mode='wt') **as** f:
**for** line **in** text_file: f.write(line)
整个流程是:下载&打印→验证&检查→记录断链&移除文件→更新 url 文件。
现在你知道了。是时候识别一些战斗机了。
数据不可否认的流通性:增加数据隐私会阻碍社会进步吗?
这是我之前的文章 AI,透明度及其与隐私的拉锯战 的后续,在文章发表的时候,我收到了关于矛盾领域的反馈。这篇文章的目的是展示不同的观点:从业务的角度,业务总是可以访问数据,并使用这些信息来获得越来越多的客户洞察力;从消费者的角度来看,世卫组织在很大程度上对收集的数据及其使用情况一无所知。在这篇文章发表期间,剑桥分析公司和脸书之间的数据泄露事件以及用户数据的滥用成为了焦点(今天仍然如此)。消费者比以往任何时候都更加充分地意识到他们在多大程度上被监视、被影响或被操纵。这篇文章讨论了日益增长的按需市场以及数据是如何实现这一点的。它还对企业与其客户之间的关系提出了挑战,并引入了一个更加平衡的数据控制时代,以及不可避免的隐私和安全法规,这些法规将推动符合道德标准的数据使用向前发展。
还记得#Prism2013 吗?爱德华·斯诺登和美国国家安全局的丑闻出现在电视上,就像今天脸书/剑桥分析公司的丑闻一样。正是这种监视行为将讨论从侵犯隐私转移到了人身侵犯,在这一点上,没有留下任何蛛丝马迹。我和我的朋友朱莉当时在 FB 上有过这样的讨论。这件事让我质疑这个行业的诚信以及我在其中扮演的角色。
数据一直是推动洞察力和收入的引擎
大数据的过去十年是一个新时代的开端:由技术、增强的连通性、计算能力和高级数据功能驱动的数据爆炸。这极大地增加了我们对事物和人的理解。现在,环境就是一切,而我们,作为人类,现在是焦点的中心。
对于企业来说,信息是驱动利润的动力。这是所有公司的首要任务。毫无疑问,股东价值取代了客户价值。确定消费者消费的动机、地点、时间和原因是营销人员几十年来试图回答的问题。显而易见,我们对驱动行为的触发因素了解得越多,我们就能卖出越多的产品。无论是 A . C Neilson 还是 Gartner,公司都在消费者研究上投入了数百万美元,不仅是为了了解市场趋势,也是为了了解这种趋势如何转化为个人的欲望和动机。
大约 4 年前,我写了这篇文章:交换:品牌和消费者之间的终极舞蹈。大数据的出现也催生了一场新的社会参与运动。突然之间,这些丰富的数据为企业提供了他们所需的信息,以通过社会背景来增加交易信息,从而定义内容、对话、评论等方面的影响,从而进一步完善对消费者的理解。
在过去 5 年中,情绪和个性(如五大)指标引起了人们的关注,以真正深入研究这些指标如何、何时以及在多大程度上影响购买行为。广告网络已经增加了他们的足迹,将更多的网站纳入他们的覆盖范围,以创建一些关于客户在哪里以及以什么顺序浏览他/她的在线路径来推断消费者倾向的理解的表象。大约在 2006 年,当时我在雅虎!我向亨特·麦德森(Hunter Madsen)汇报,这位大师在行为定向成为“事物”之前就引领了它。了解用户在我们的网络中消费了什么内容,他们搜索了什么,他们如何在我们的平台上互动,并利用位置和人口统计等邻近因素,有助于开发精确的目标配置文件。自然语言处理(NLP)在检测社交帖子中的购买意愿方面也取得了很大进展。技术已经变得如此复杂,以至于今天能够使用过去的行为/提及作为消费者结果的预测器。
凯茜·奥尼尔在她的书《数学毁灭的武器》中写道,
数学家和统计学家正在研究我们的欲望、行为和消费能力。他们预测我们的可信度,计算我们作为学生、工人、恋人和罪犯的潜力……这就是大数据经济,它承诺了惊人的收益。一个计算机程序可以在一两秒钟内快速浏览成千上万份简历或贷款申请,并把它们整理成整齐的列表,最有希望的候选人排在最上面。这不仅节省了时间,而且被宣传为客观公正。
所有这一切的目标是向消费者提供最相关的沟通,这将符合他/她购买该项目的倾向:正确的信息,正确的时间。很简单,对吧?不是这样的,但我们已经越来越接近实现这一点。
如今,来自社交网络和第三方网站的个人数据的可用性增加,这使得个人层面的情境化增加,接近实时。
到 2010 年,Cathy O'Neill 声称数学正以前所未有的方式进入“人类事务,公众很大程度上欢迎它”… 直到现在。
数据在很大程度上是社会的救星
在贫困、灾难恢复、医疗保健、教育等领域做出关键决策时,效率和进步的例子数不胜数。
还记得那场让新西兰克赖斯特彻奇成为一片废墟的地震吗?在接下来的几年里,这座城市使用实时信息来帮助重建工作:从摄像头,交通系统,公用事业,水质。随着城市的重建,这些数据点将被分析以衡量经济发展,并指导城市如何重建以最大限度地减少未来的地震影响。
公用事业公司也在使用数据向客户提供更多关于能源消耗的信息,通常会降低成本,并将做出更好的能源使用决策的责任推给客户。
在医疗保健行业,由于行业的分散性以及将大量基于纸张的信息集转换为数据,数据一直是绊脚石。医疗保健社区正在缓慢转型,数据聚合正被用于预测流行病、治疗和预防疾病,以及总体上提高生活质量。重点是尽可能多地了解患者病史,以确定潜在的警告信号,并确定与疾病的相关性。在类似的患者使用案例中大规模地这样做可以使预防变得更加合理。
对于消费者来说,技术让我们的生活变得更简单、更高效。我们的垃圾邮件会被自动标记或从电子邮件中删除。我们的工作日更有条理。在记录我们的健康或健身习惯,或寻找最符合我们要求的工作方面,我们现在有更多的信息可以利用。我们已经习惯于依靠技术为我们想要购买的产品找到最好的交易,我们对按需履行的期望提高了优步、美食或、网飞和钥匙咖啡馆等便利服务。
脸书在实现更具情境性的用户洞察(比任何其他出版或广告平台都多)以推动更个性化的信息方面发挥了重要作用。这创造了一个非常有利可图的广告平台,是脸书成功的支柱。虽然他们一开始是一个社交网络,与志同道合的人一起闲逛和分享想法,但它很快变成了一个赚钱的机会,用户是主要演员,吸引垂涎的广告商。
www.theguardian.com/technology/…
还记得扎克伯格几年前说过这句话吗?“隐私已死”
早在 2010 年,扎克就认为
“如果人们更多地分享,世界将变得更加开放和互联。一个更加开放、联系更加紧密的世界会更加美好。”
八年后,由于最近剑桥分析公司的事件,这个受人喜爱的社交网络将有助于改变我们分享、分析、再利用或出售数据的方式。消费者默许免费社交访问以换取公司访问和分析个人信息将不再是标准。
尽管数据用于改善社会的例子数不胜数,但也有同样多的例子质疑政府和企业对数据的使用。这个就不在本帖讨论了。我鼓励你阅读凯茜·奥尼尔的《数学毁灭的武器》,因为它包含了对当今存在的数学模型的全面看法,即使有证据表明存在错误,这些模型也是“不受监管和无可争议的”。
媒体对这种数据操纵的曝光将在政策和数据治理的监管方面创造明显的界限……尤其是在人工智能出现之后。
随着人工智能的出现,隐私的增加会阻碍进步吗?
如果消费者愿意放弃便利和随时可获得的信息,去强烈地捍卫本属于他们的东西,这已经不再是一个问题。帮助预测疾病发生的医疗保健进步是否会因为消费者控制的数据而停滞不再是一个问题。
人工智能的真正危险在于,如果反馈回路开始以前所未有的速度从来自其他数据源的数据中发现模式,那么继续进行今天无法无天的狂野西部实践而不考虑潜在的社会影响。没有规则,没有透明度,没有适当的披露,危险在于人工智能失控,放大当前有偏见的模型,并最终影响可能产生有害社会影响的决策。
人工智能的目标应该是提高当前系统的效率,并为人类的整体利益改善决策。如果这是灵丹妙药,那么人类应该有权控制哪些数据可以被使用、分析和转售。他们有权同意使用数据的“环境”,数据是否得到安全处理,数据是否得到完整处理。他们有权质疑是否需要特定的个人身份信息(PII)来实现预期的结果。
根据欧洲通用数据和保护条例 (GDPR),基本信息受到保护。
- 姓名、地址和身份证号码
- 位置、IP 地址、cookie 数据和 RFID 标签等网络数据
- 健康和遗传数据
- 生物特征数据
- 种族或民族数据
- 政治观点
- 性取向
加拿大前隐私部长 Ann Covoukian 提出了“设计隐私”的概念。这个概念就是隐私,它被构建并编程到任何产品的每一层结构中,旨在创建一个透明的功能系统,该系统被授权使用法律政策定义的公平和道德标准来处理个人身份数据。它将减少当前黑盒人工智能的发生,并且组织将被法律授权在未来使用它。这已经嵌入于 2018 年 5 月 25 日推出的 GDPR。正如我们已经看到的,这种影响将是全球性的。
回答这个问题:隐私不应该阻碍这个人工智能新世界的进步。道德模型不应因个人信息的使用而有所偏差。如果将各种因素和事件综合起来决定预测,真的有必要使用 PII 吗?
我 4 年前在我的交换帖子中写的话今天仍然适用:
这里的平衡在于确保消费者不会陷入困境。另一方面,企业必须从总体和消费者层面了解影响购买周期各个部分的信息...然后双方(企业和消费者)必须达成一个互谅互让的协议。
作为一家企业,我要怎么做才能留住你这个顾客?
作为顾客,我愿意给你什么,让我满意并再次光顾的生意?
从历史的角度来看,我们必须从过去吸取教训,想象我们想要的未来。我们在业务中实现的功能需要从一万英尺的高度来看待。我从一个名为 Writingya 的博客中得到了这个智慧。:
“[脸书首席执行官]]马克·扎克伯格说隐私不再是一种社会规范。当他这么说的时候,我发了微博——我从来不发微博,这就是我的愤怒——这可能与社交网络无关,但它与亲密和民主有关。技术让人变傻。它会让你看不到你的潜在价值是什么,需要什么。我们真的愿意放弃我们努力争取的宪法和公民自由吗?人们为此而流血,而不是生活在一个监视的社会里。我们看着史塔西说,“那不是我们。”而现在我们让苹果做,让谷歌做?为了什么?”~ Writingya
数据集创建和清理:使用 Python 进行 Web 抓取—第 1 部分
“world map poster near book and easel” by Nicola Nuttall on Unsplash
在我的上一篇文章中,我讨论了使用应用编程接口(API)和 Python 库生成数据集。API 允许我们以一种简单的方式从网站中获取非常有用的信息。然而,并非所有网站都有 API,这使得收集相关数据变得困难。在这种情况下,我们可以使用网络抓取来访问网站的内容并创建我们的数据集。
网络抓取是一种用于从网站提取大量数据的技术,通过这种技术,数据被提取并以表格(电子表格)格式保存到您计算机中的本地文件或数据库中。— 韦伯维
一般来说,网络搜集包括访问大量网站并从这些网站收集数据。然而,我们可以限制自己从单一来源收集大量信息,并将其用作数据集。在这个特殊的例子中,我们将探索维基百科。我还会解释我们需要的 HTML 基础知识。完整的项目可以在 Github 知识库中使用 Python 进行网络抓取。
这个例子只是为了演示。但是,我们必须始终遵循网站指南,然后才能抓取该网站并出于任何商业目的访问其数据。
这是一篇由两部分组成的文章。在第一部分中,我们将探索如何使用 BeautifulSoup 从网站获取数据,在第二部分中,我们将清理收集的数据集。
确定内容
“man drawing on dry-erase board” by Kaleidico on Unsplash
我们将访问按人口统计的国家和属地列表维基百科网页。该网页包括一个表格,其中列有国名、人口、数据收集日期、占世界人口的百分比和来源。如果我们去任何一个国家的页面,所有关于它的信息都写在页面上,右边有一个标准框。该框包括许多信息,如总面积、含水量、GDP 等。
这里,我们将把这两个网页的数据合并成一个数据集。
- **国家列表:**在访问第一页时,我们将提取国家列表、其人口和占世界人口的百分比。
- **国家:**然后我们将访问每个国家的页面,并获得包括总面积、水资源百分比和 GDP(名义)在内的信息。
因此,我们的最终数据集将包括每个国家的信息。
HTML 基础
您在浏览器中浏览的每个网页实际上都是用超文本标记语言(HTML) 构建的。它有两个部分, head 包含标题和任何样式和 JavaScript 的导入, body 包含显示为网页的内容。我们对网页的主体感兴趣。
HTML 由标签组成。标签由开始的<和结束的> 尖括号描述,尖括号内的标签名称作为开始,如果在开始的尖括号后有正斜杠/,则标记结束。比如<div></div>、<p>Some text</p>等。
Homepage.html as an example
有两种方法可以直接访问网页上的任何元素(标签)。我们可以使用唯一的 id ,或者我们可以使用可以与多个元素关联的类。在这里,我们可以看到<div>的属性 id 为base,作为对该元素的引用,而所有用td标记的表格单元格都有相同的类,称为data。
通常有用的标签包括:
- 每当你包含某些内容时,你就把它们一起包含在这个单一的实体中。它可以作为许多不同元素的父元素。因此,如果这里应用了一些样式更改,它们也会反映在它的子元素中。
<a>:该标签中描述了链接,其属性href中提到了点击该链接将加载的网页。<p>:每当一些信息要以文本块的形式显示在网页上时,就使用这个标签。每个这样的标签都作为它自己的段落出现。<span>:当信息要内联显示时,我们使用这个标签。当两个这样的标签并排放置时,它们会出现在同一行,不像段落标签。<table>:表格借助该标签在 HTML 中显示,其中数据显示在由行和列交叉形成的单元格中。
导入库
我们首先从导入必要的库开始,即 numpy、pandas、urllib 和 BeautifulSoup。
- numpy: 一个非常流行的库,它使得数组操作变得非常简单和快速。
- 它帮助我们转换表格结构中的数据,因此我们可以使用已经被高效开发的众多函数来操作数据。
- 我们使用这个库来打开我们想要从中提取数据的 url。
- BeautifulSoup: 这个库帮助我们获得想要处理的页面的 HTML 结构。然后,我们可以使用它的功能来访问特定的元素并提取相关信息。
Import all libraries
了解数据
最初,我们定义我们只是读取 url,然后从中提取 HTML 的基本功能。我们会在需要的地方引入新的功能。
Function to get HTML of a webpage
在getHTMLContent()函数中,我们传入 URL。这里,我们首先使用urlopen方法打开 url。这使我们能够应用 BeautifulSoup 库来使用解析器获取 HTML。虽然有许多可用的解析器,但在这个例子中我们使用了html.parser,它使我们能够解析 HTML 文件。然后,我们简单地返回输出,然后我们可以用它来提取我们的数据。
我们使用这个函数来获取国家列表的维基百科页面的 HTML 内容。我们看到这些国家出现在一张表格中。因此,我们使用find_all()方法来查找页面上的所有表格。我们在这个函数中提供的参数决定了它返回的元素。当我们需要表格时,我们将参数作为table传递,然后遍历所有的表格来确定我们需要的表格。
我们用prettify()函数打印每个表格。该函数使输出更具可读性。现在,我们需要分析输出,看看哪个表有我们要搜索的数据。经过大量的检查,我们可以看到带有类 wikitable sortable 的表中有我们需要的数据。因此,我们的下一步是访问这个表及其数据。为此,我们将使用函数find(),它不仅允许我们指定我们正在寻找的元素,还允许我们指定它的属性,比如类名。
Print all country links
HTML 中的表格由标签<tr></tr>表示的行组成。每一行都有单元格,这些单元格可以是使用<th></th>定义的标题,也可以是使用<td></td>定义的数据。因此,要访问每个国家的网页,我们可以从表格的国家列(第二列)的单元格中获取链接。因此,我们遍历表中的所有行,并读取变量country_link中第二列的数据。对于每一行,我们提取单元格,并在第二列中获得元素a(Python 中的编号从 0 开始,因此第二列意味着cell[1])。最后,我们打印所有的链接。
这些链接不包括基地址,所以每当我们访问这些链接时,我们将附加[https://en.wikipedia.org](https://en.wikipedia.org)作为前缀。
虽然我开发的从每个国家的网页中提取数据的功能可能看起来很小,但在我完成这个功能之前,已经对它进行了多次迭代。让我们一步一步来探索。
每个国家的页面右侧都有一个信息框,包括座右铭、名称、GDP、面积和其他重要特征。所以,首先我们用和以前一样的步骤来识别这个盒子的名称,它是一个类为infobox geography vcard的表。接下来,我们定义变量additional_details来收集我们将从这个页面获得的所有信息,放在一个数组中,然后我们可以将这个数组追加到国家数据集的列表中。
当我们在国家页面进入 Chrome 浏览器的 inspect 模式(右击任意位置并选择Inspect选项)时,我们可以查看表格中每个标题的类别。我们对四个领域感兴趣,面积—总面积、水(%),以及 GDP(名义)—总量、人均。
Area — Total Area and Water (%)
GDP (nominal) — Total and Per capita
我们可以很容易地推断出标题Area和GDP (nominal)有类mergedtoprow,而我们想要提取的数据有类mergedrow或mergedrowbottom。但是,我们不能直接访问任何数据元素,因为它们发生的顺序会根据每个国家而变化。对于一些国家,特定字段可能缺失,且total area的mergedrow可能是第 6 位,而对于一些其他国家,它可能是第 7 位。因此,我们需要首先看到mergedtoprow的文本,如果它与面积或 GDP(名义)匹配,我们应该读取并收集这些数据。
看起来很简单,但是当我尝试的时候,我立刻发现了一个问题,因为有些国家的water (%)超过了 100。这是不可能的,因此,我们一定遗漏了什么。这是我意识到问题所在的时候。你看,如果我们在区域标题后读取两个值,并且缺少水值,我们将错误地读取人口的第一个值,从而给我们错误的数据。因此,我们需要确保当Population标题被识别时,我们停止读取值。
现在,我们可以简单地将我们所有的知识添加到函数getAdditionalDetails()中。我们定义变量read_content来标记是否读取下一个值。除了前面描述的函数之外,我们在这里还使用了三种类型的函数:
get():这使我们不仅能找到,还能得到对特定元素的引用。get_text():获取元素开始和结束标签中的值。strip():这将删除文本中可能出现的任何额外的前导和尾随空格。我们还可以指定我们希望删除的任何特定值,例如,在我们的情况下,新的行字符\n。
迭代表中的所有行,迭代器检查当前行是否是匹配区域或 *GDP(名义)*的标题,并开始读取。在读取模式下,它会检查新元素是总面积还是总面积,如果是,它会读取并继续读取,以便在下一次运行时分别读取水(%)或人均 GDP。
我们使用try和except来确保即使我们错过了某些值,我们的整个过程也不会结束。这是我在遍历完整的国家列表时学到的另一个教训。有些国家没有我们需要的所有信息,或者我们可能找不到所需名称的表格。在这种情况下,我们的流程可能会抛出一个错误,我们必须捕获该错误以返回一个空数组,并允许该流程在其他国家继续进行。
创建数据集
最后,我们现在知道我们需要收集哪些信息以及如何收集。
我们首先从国家列表中读取表格的每一行,并收集每个国家的名称、人口和占世界人口的百分比。然后,我们使用该链接获得所有其他详细信息,包括总面积、水(%)、总 GDP 和人均 GDP。但是,如果附加信息少于 4,则该国家的信息缺失,我们不使用该数据。否则,我们将所有的信息添加到data_content数组中,该数组被编译到dataset数据帧中。
接下来,我们阅读表格的标题,并为 4 个额外的列添加标题。这些作为我们的数据集的标题,我们将其导出到一个. CSV 文件中,可以在这里找到。
尽管我们的数据集现在已经准备好了,但它不是最有用的格式。我们需要使用适当的格式、统一的指标并删除不必要的符号和值来清理这个数据集。我们将在本文的第二部分讨论这个问题。
希望你喜欢我的文章。请随时伸出手,分享你的想法。
数据集创建和清理:使用 Python 进行 Web 抓取—第 2 部分
“open book lot” by Patrick Tomasso on Unsplash
在这个由两部分组成的系列文章的第一部分中,我们介绍了一种在 BeautifulSoup 和 Python 的帮助下使用网络抓取从维基百科网页中提取数据的方法。仔细观察数据集,我们可以清楚地看到,在收集的数据中存在必须消除的噪声。每当使用网络抓取收集数据时,它通常充满了我们在这里可以看到的噪音。因此,在执行任何分析之前,需要清理收集的数据以创建统一且可用的数据集。
在这里,我们将处理我们在上一部分创建的维基百科数据集,并逐一处理每一列,直到我们有一个相当有用的数据集。
导入库并读取 CSV 文件
我们导入三个库 re 、 numpy 和 pandas 。我们已经知道 numpy 和熊猫。第三个库re实际上是用来根据一个正则表达式(通常称为 regex )格式化字符串,它基本上定义了一个搜索模式。我们将在本文中探索它的用法,如何使用它摘自它的文档。
首先,我们使用 pandas 函数read_csv()读取数据集文件,并将其存储在dataset变量中。
替换标题
仔细检查后,可以发现标题可以做得更具描述性,因此我们将在必要的地方进行替换。我们将把Country(or dependent territory)改为Country、% of worldpopulation改为Percentage of World Population、Total Area改为Total Area (km2)。
Finalised column headings
现在,我们将使用新确定的列名来访问数据并细化其中的内容。
分析和清理数据集
Dataset.csv
让我们首先来看看数据集的当前状态。我们可以看到,几乎所有的列都有方括号和圆括号内的信息。方括号内的文字通常是指维基百科页面上的进一步链接。在我们的例子中,我们不会访问任何链接,这些链接不会提供有用的信息,所以我们可以删除它们。括号内的数据通常包括关于真实数据的附加信息。例如,在列Total Area中,面积在括号内以其他公制单位定义。我们也不需要这些数据,所以我们也可以删除所有的括号。
此外,我们从列Percentage of World Population和Percentage Water的所有值中删除“%”符号,因为这从列标题中可以直接看出。
Resultant dataframe
数据现在看起来好多了,但我们还有很多事情要做。
一次处理一列
Country帮助我们识别上下文,现在不需要修改。在我们前面的步骤之后,Percentage of World Population已经是正确的格式了,所以我们也不改变它。
人口和总面积列
Population列中的数字用逗号分隔。这里,我们将使用replace()函数删除逗号。这个函数将第一个参数作为我们想要替换的字符,第二个参数定义我们想要替换的字符。这里,我们什么都不用替换,,因此有了函数replace(',','')。
为了访问每个单元格,我们在for循环的帮助下迭代该列,然后使用 Pandas 函数iloc[]读取和更新该列中的特定单元格。
接下来,我们移动到Total Area (km2)列。我们也从删除该列中的逗号开始。接下来,一些列的数据在sq mi中,而其他列的面积在km2中。我们必须将所有的sq mi值乘以 2.58999,转换成km2。
为了实现这一点,我们检查单元格在字符串中是否有sq mi,如果有,我们删除除了.之外的所有非数字字符,并将值乘以 2.58999。该值最终被转换回整数。我使用了一个函数repr来获得该列中值的可打印版本。在查看结果时,我看到在可打印的格式中,sq mi实际上被表示为sq\xa0mi,匹配必须针对它进行。这也反映在代码中。如果单位已经在km2中,我们只需删除除.之外的所有非数字字符,并将值转换为整数。为了移除所有这样的字符,我们使用正则表达式[^0-9.]+,这基本上意味着我们移除任何计数中的所有字符(用+标记),除了数字 0 到 9 和.
然而,在做同样的尝试时,我发现一些值在范围内,我们需要首先解决它们。因此,对于所有这些国家,我们读取范围,使用split('-')函数将其分割,并将第一个值作为我们将为该单元格考虑的值。
Modified ‘Population’ and ‘Total Area’ columns
百分比水柱
该列有一些异常值。首先,对于一些含水量很低的国家,如阿尔及利亚和阿富汗,此栏数值标有Negligible或negligible。此外,对于智利来说,最后还有一个额外的b存在。
因此,我们需要用0.0替换Negligible和negligible,并删除这些单元格中除数字和.之外的任何额外字符。我们使用前面定义的不带和带正则表达式的replace()函数来实现这一点。
对于某些缺失值的情况,记录的值不正确,必须删除。幸运的是,这很容易解决。我们可以简单地删除所有大于 100 的行。这是可行的,因为百分比值不能超过 100。我们首先使用astype(float)将列中的每个字符串转换为 float,然后只保留那些值小于或等于 100 的列。
Modified ‘Percentage Water’ column
名义国内生产总值和人均国内生产总值栏
对于Total Nominal GDP,我们首先去掉美元符号。接下来,我们观察到这一列中的金额被表示为万亿、十亿和百万的字符串。因此,我们读取每个单元格,识别面额,删除除数字和.之外的所有字符,并将金额乘以面额数值。
对于Per Capita GDP,我们保留数字 0-9 和.,同时删除所有其他字符,包括逗号和美元符号。
Final Dataset
导出最终数据集
最后,我们的数据集现在是干净的,可以导出到外部 CSV 文件中。我们使用熊猫函数to_csv将数据帧导出到文件Final_dataset.csv。
结论
在这篇文章中,我们发现虽然网络搜集的第一步即数据收集很重要,但是数据的清理也同样重要。这一步本身也有一些挑战,我们必须先解决这些挑战,然后才能开发合适的数据集进行有用的分析。最终的数据集也存在于 GitHub 存储库中。
希望你喜欢我的作品。请随时伸出手,评论,分享,发表你的看法。
DataViz 战斗入口:分析美国运输安全管理局的航空公司数据
这是我关于数据分析的第二篇中型文章。鉴于 Reddit 竞赛的性质,下面的内容更侧重于数据的可视化,而不是探索。
首先,挑战的数据集是这个 TSA 索赔数据,它由一堆 PDF 文档组成。运输安全管理局是美国国土安全部的一个机构,负责美国公众旅行的安全。该数据包含关于在运输过程中丢失和索赔的物品的信息。
我用 Tabula 在线解析 pdf 文件,结果运行良好。我只看了 2016 年的数据。然而,这些行有点分散,这需要我使用 OpenRefine (大喊到 @warrenski 获取提示)。我不能强调 OpenRefine 对我的帮助有多大!然后我用 Python 进行重组和可视化。
这是我第一次使用 Plotly ,它真的创造了奇迹。我错过了散景船,所以作为第一次使用 Plotly 的用户,我需要花很多时间才能跳槽。
下面所有的图形都是交互式的,但是,唉,Medium 不支持这种嵌入。无论如何,以下是我对 DataViz 之战的记录:
奇怪的是,如上所示,批准的项目在接近年底时减少了——这就引出了一个问题,如果这个部门最终耗尽了预算。你会看到曲线在四月、五月和六月上升,之后直线下降,在十二月下降到零。
我决定添加一个考虑 2016 年所有状态的堆积图,如下所示:
正如您所看到的,随着审查数量的增加,批准的项目的总价值下降了。与此同时,那些被认为不充分的申请清楚地表明,预算最终会在年底耗尽。
机场和部门并不总是因为丢失物品或批准何时支付而受到责备,所以我也分析了航班,看看在索赔的物品类型中是否有一些有趣的变化。
This is my new favourite graphic :)
有趣的是,在查看航班及其对索赔总额的最大贡献时, UAL 偷看狩猎项目,美国航空公司负责索赔的大多数医疗项目。UAL 的总部设在芝加哥,芝加哥因枪支管制而臭名昭著,所以也许有关联。
总的来说,进一步研究物品类别是很有趣的,从它们的总体价值和数量来看。
This graph is best viewed as an interactive element, so please excuse the terrible hover texts
可能不是这种表示的最佳图表,但我想在上面添加一些变化。我也很喜欢泡泡散点图。
上图显示,就申请商品的量而言,服装、其他、行李和电子产品超受欢迎。然而,到目前为止,就物品的价值而言,电子产品胜出。****
气泡的大小由相对于体积的平均物品价值决定——我认为这是相当必要的。正如你将在下面看到的,它有助于揭示即使狩猎物品数量相对较少,它们的价值也比医疗物品或相机要高得多。
A zoomed in version of the above
放大上图中难看的集群,在角落里你会看到狩猎物品气泡相对较大,表明它的低容量但每件物品的高价值。
这篇文章到此为止,感谢你的阅读,希望你觉得有趣。如果你喜欢它的视觉效果,请随意鼓掌或分享。干杯!
DataViz 案例研究:规划大学招生的七种微妙不同的方法
即使是最简单的数据集也能以一百万种方式呈现。即使我们坚持最基本的方法,用线和点和条,我们也有很多选择。
这些选择微妙地塑造了我们的数据如何被阅读和解释,以及观众会得出什么结论。没有单一的“正确方式”来显示数据,因为每个项目都有编辑意图:要提出的论点、要讲述的故事、要让人难忘的关键见解。数据可视化人员只能通过密切关注他们技术的微妙之处来做到这一点——我想通过一个案例研究来说明这一点。
案例研究:大学招生
最近,我需要帮助一位同事将 2003 年至 2015 年美国学院和大学的学生注册总数数据可视化,这些数据按公立或私立学校分类。她还想强调大衰退可能带来的任何影响。
让我们看看这些数据的七种呈现方式。在每一种情况下,我们都希望关注哪些趋势或事实被揭示,或变得更加明显,以及哪些趋势或事实被隐藏。
#1:基本折线图
我的同事在她的报告草稿中开始了这个明显的选择,我在这里重新做了,而不是让你接受 MS Word 图形:
强调什么
- 公立和私立学校的总体差异:公立学校招收的学生数量是私立学校的三倍多。
- 经济衰退期间对公众的提价。其次,随后公众支出放缓,而私人支出小幅但稳步增长。
评估
很难讨厌这种直接的方法,但我觉得效果很弱。公私比较太重要了:这种关系是众所周知的,所以没什么意思。衰退时期的增长需要更多的关注,但它的规模和速度很难判断,因为相比之下图表的规模太大了。在这个数据集和许多数据集中,规模是一个基本的权衡:如果我们显示整体趋势,我们就会掩盖细节;如果我们放大细节,我们就失去了大局。我们必须选择哪个更重要。
接下来的选择探索了如何显示使该数据集的增加更重要。
#2 阶梯折线图
一个小的变化是:不同年份之间的平滑,显示为一条平坦的线(样本每年只记录一次)。
重点的变化
- 我们可以更容易地看到增长的幅度。在公共场合,我们可能会察觉到经济衰退前和衰退期间的加速增长。
- 2011 年后,公众经历了一次下降,这一点更加明显。
评估
与基本折线图具有相似的效果。还不够远。
#3 年度变化(增量)
以下是各组的同比变化。虽然这些可以显示为折线图,但我发现这种做法有些欺骗性,因为一个值不会直接影响下一个值。我喜欢箭头,因为它表明我们在应对变化。(但你可以想象不同的造型,以下几点保持不变。)
强调什么
- 这次公招 人数激增中间。其次,它在衰退前就开始了,在衰退期间加速,然后停止。
- 经济衰退后,公共部门和私人部门都经历了衰退。
什么被巧妙地编码了
- 公立学校的变化幅度几乎不可避免地会比私立学校大,因为它们招收了更多的学生。所以总入学人数被巧妙地包括在内,尽管我们并没有真正用图表显示出来。
失去了什么
大的公共线路(几乎是字面上的)盖过了私人线路。而且观众可能会因为大小不同而得出奇怪的结论,这在这张图上并不明显。例如,人们可以推断,对公共机构而言,衰退是一个更加动荡的时期。但是每种类型的相对变化并没有显示出来:即使是很小的入学减速对士兵来说也可能是毁灭性的,但是在这里很难评估。
评估
这让我们聚焦于这些变化,并引起人们对经济衰退期间事件发生方式的关注。这个故事变得更加与公众有关。
#4.相对年度变化
接下来,我将年度变化显示为当前值的百分比。
强调什么
- 在经济衰退之前和期间,T21 的私人和公众都经历了增长。公众在此期间有了更大的增长。
- 近年来,所有机构都在从沉寂中复苏;但是士兵们反弹得更快。
隐藏着什么
- 我们再也看不到,即使是间接的,公共机构总体上招收的学生要多得多。
- 我们看不到有多少学生受到影响。
评估
这对于真正比较公立和私立学校的兴衰更有用:它们受到相同事件的影响吗?他们可能会做出类似的决定吗?他们往同一个方向走吗?对于所有这些问题,我们现在可以形成部分答案:泡沫和衰退都受到了打击,但它们的反应不同。这也许可以从直线图中看出,但被严重模糊了。
#5 公私比例
一种选择是计算某种比较并直接用图表表示出来,而不是强迫观察者两个组进行比较。这是私立学校占公立学校的比例:
强调什么
- 我们可以看到学生的总体分布实际上是如何随着时间推移而发生变化的,这可能与经济衰退有关。
什么被遮住了
- 有多少学生参与其中?
- 是公众、私人或两者的变化导致了这些变化吗?
评估
有趣的是,第一张图中的观察结果——公立学校招收了 3 倍多的学生——显示出随着时间的推移而变化。私立学校招生人数的早期增长也令人惊讶。这些直接比较在图 1 和图 2 中很难看出,在图 3 和图 4 中看不到。
这些转变可能会引发一些有趣的问题,但不幸的是,这些问题不能仅靠这张图表来回答。例如,是什么导致了私人资本的激增?我们可以回顾过去,看到这两种制度都有所增加;但是士兵相对增加更多。但也存在其他可能性(公共注册人数骤降),我们在这里不会知道。
因此,如果我们可以用多张图来构建一个故事,这一张图可能值得包括在内。如果我们局限于一个图表(就像我一样),它就没有包含足够的信息。
#6 净变化
我们展示了自 2003 年以来的变化,而不是逐年变化。也就是我们把 2003 年定为零点。
强调
- 入学人数全面上升,但公立学校占据了大部分份额。
- 这种增长在衰退期间加速,在衰退之后减速——尤其是对公众而言。
隐藏了什么
- 2003 年的实际值是多少;总共有多少学生?
评价
这张图表在我们所看到的一些东西之间找到了一种有趣的平衡。我们仍然可以间接地看到公共机构的重要性,它们负责招收大多数新生。衰退期间的变化相当明显。人们很容易对私立学校和公立学校进行比较——但不是在相对的基础上,也不是在细节上,尤其是对于私立学校。
我们做出的主要牺牲是将事情固定在某一年。在这种情况下,这一年很大程度上是任意的(基于数据可用性),而不是与一些关键事件相一致。这也会给人一些错误的印象:
- 入学人数开始时很低
- 公立学校和私立学校的总注册人数差不多
如果你的观众不熟悉数据的背景,他们更有可能做出这些错误的结论(没有办法自我纠正)。这意味着这种展示依赖于特定类型的观看者——以及一如既往的仔细标注。
#7 相对净变化
同上,但我们采用自 2003 年以来的百分比变化:
强调什么
- 一种公与私的竞赛;不同时间谁在“赢”;以及 2008 年和 2014 年发生的逆转。
- 衰退期间公众的快速增长(T2 ),之后趋于平稳。
隐藏着什么
- 再问一次,我们所说的学生的绝对数量是多少
- 这两种类型在总注册人数上有何不同,例如,它们从哪里开始
评估
如果百分比增长真的是讨论中的一个关键变量(而不仅仅是一个好的代理),或者两组之间存在某种竞争,那么这个图表提供了一些很好的见解。这些遐想确实将人们的注意力吸引到了衰退和随后的复苏上。
但是这里也有很多潜在的误解。没有背景知识的观众可以很容易地得出结论,这两种类型在入学人数上非常相似,并且确实在激烈争夺学生。但是我们知道公立学校在美国学生总数中所占的比例要大得多,不管我们在这张图表中看到的变化有多大。同样,有人可能会认为私人即将 超过公共,这当然也是不正确的。
与第 6 条相比,这张图表更需要一个见多识广的观众,或者提供大量的背景和教育——当图表经常被浏览时,这是一个很高的要求。
由于我没有特别想强调一个“种族”,我判断这个情节太混乱了。
最后决定
最后,我向我的同事展示了图表 6,净变化。这似乎达到了……的恰当平衡
- 围绕经济衰退,以合理的细节强调变革
- 允许公/私之间的比较
- 仍然表明公立学校的总入学率更高
使用这个图表是可能的,因为我知道我的观众将熟悉高等教育的世界,包括公立和私立机构的典型入学情况。但是我的读者也不精通技术,也不一定擅长仔细阅读图表:我不想把他们和#5 或#7 混淆。
结论
我展示的七张图中的任何一张都可能是正确的选择:这取决于我们的编辑目的,以及我们试图接触的受众。
数据可视化是另一种交流模式,它与写作、漫画和演讲有着共同的问题。做出正确的选择取决于您对不同图形结构影响人类数据感知的方式的理解。这取决于你仔细检查你的选择,并考虑它们的影响。这取决于你了解你的观众。相反地…
不要
- 不要相信任何虚假的建议,告诉你有一种正确的方法来绘制特定类型的数据(“折线图是用于时间序列的!”).就像生活中的许多事情一样,“视情况而定。”
- 不要假装你能以某种宇宙的形式中立地呈现数据,这样所有包含在其中的美好智慧就会自然而然地流入读者体内。你需要做出编辑上的选择来帮助你的读者——你不可避免地会这样做,不管你是不是有意的。
- 不要以为你能拥有一切。为了强调某些事情,你必须淡化其他事情。最花哨的数据可视化也救不了你:随着复杂性的增加,解释会面临新的挑战。你必须做出取舍。
作为一名数据可视化从业者,你的工作就是要擅长于此,并发展出一种什么会起作用的感觉。你不能尝试所有可能的选择;但是可以试几个合理的。希望我在这里列举的七个例子能对你的过程有所帮助。
Tableau 中的日期
这篇文章对那些正在处理财务数据的人或者任何正在追踪与去年数字相比企业表现的人来说都是有用的。
用例
用户希望从宏观和微观层面跟踪上一年的销售情况(YTD,Day YOY)。
- 在日期字段之间切换 —当处理财务数据时,用户希望看到订单进来时的销售情况,但也希望跟踪订单何时在会计系统中登记。为用户提供在这两个日期字段之间轻松切换的能力,同时只保留一组报告和图表。
- 上下钻取 —当使用日期字段构建时间序列图时,Tableau 包括上下钻取日期部分的能力。但是并不是所有的用户都知道将鼠标悬停在日期轴上来查看-/+,或者您希望限制用户向下或向上钻取的能力。
- 日期比较 —相对日期范围过滤器可用于选择 YTD、MTD 和 Today,但不允许同比(YOY)或滚动日期范围比较。构建最常见的日期范围比较,以考虑同比和滚动周期。此外,能够改变结束日期,以跟踪有多远,从去年的销售。
下面的工作簿显示了每个步骤的最终结果,最后一个选项卡“时间序列”将所有内容汇总在一起。我决定展示如何使用连续和离散来构建每个组件,因为根据需求,两者都有用例。
[## 选择日期字段;在字段日期视图之间切换;向上钻取和向下钻取日期比较;年初至今…
选择日期字段;在字段日期视图之间切换;向上钻取和向下钻取日期比较;年初至今,年复一年以及…
public.tableau.com](public.tableau.com/views/DateS…)
警告涉及参数(如本工作簿中的参数)的行级计算将增加加载时间。查看这篇互联文章。
基于 Carl Slifer 的 Interworks 帖子,日期比较变得容易。
在日期字段之间切换
使用参数可以很容易地在两个或多个日期字段之间转换。
**第一步。**创建一个参数字段,[选择日期字段]。将数据类型设置为整数将有助于提高性能,因为整数和布尔数据类型比日期和字符串数据类型运行得更快。每个值都可以有一个容易识别的名称。
**第二步。**创建计算[选择日期字段]
CASE [Parameters].[Select Date field]WHEN 1 THEN [Order Date]WHEN 2 THEN [Ship Date]END
**第三步。**在视图中放置计算并显示参数。
下面我使用[选择日期字段]计算构建了两个图表。请注意,连续图表的线条中没有断点,每个月/年都没有标记,而离散图表的线条中有断点,每个年、季度和月都有标记。
上下钻取
构建一个参数来切换到不同的日期级别。
**第一步。**创建【日期视图】参数
**第二步。**创建日期视图计算。请注意,我将使用在上一步中创建的[选择日期字段]。如果您不需要在日期之间切换,则可以使用所需的日期字段。
日期视图 _ 连续
CASE [Parameters].[Date View]WHEN 5 THEN DATE(DATETRUNC('year', [Select Date field]))WHEN 1 THEN DATE(DATETRUNC('quarter', [Select Date field]))WHEN 2 THEN DATE(DATETRUNC('month', [Select Date field]))WHEN 3 THEN DATE(DATETRUNC('week', [Select Date field]))WHEN 4 THEN DATE(DATETRUNC('day', [Select Date field]))END
日期视图 _ 离散
CASE [Parameters].[Date View]WHEN 5 THEN DATENAME('year',[Select Date field])WHEN 1 THEN DATENAME('year',[Select Date field]) + ' Q' + DATENAME('quarter',[Select Date field])WHEN 2 THEN DATENAME('year',[Select Date field]) + '-' + STR(DATEPART('month',[Select Date field]))WHEN 3 THEN DATENAME('year',[Select Date field]) + ' W' + STR(DATEPART('week',[Select Date field]))WHEN 4 THEN DATENAME('year',[Select Date field]) + '-' + STR(DATEPART('month',[Select Date field])) + '-' + STR(DATEPART('day',[Select Date field]))END
**第三步。**放置在视图上
连续的
在 Mac 上,按住“option”键,选择[日期视图 _ 连续]并放置在列上。将弹出一个新的下拉字段窗口,选择第一个选项“日期视图 _ 连续(连续)”,该选项不会分配静态日期部分,并允许参数正常工作。
分离的
将[Date View_Discrete]移动到列中,默认情况下将不应用日期部分。
让我们比较两个时间序列。现在我们看到离散时间序列不包含间断。我也更喜欢离散标签,因为它根据选择的日期视图来标记日期。在连续时间序列将根据所选的日期视图进行调整的情况下,连续几个月显示的日期可能会误导用户。
年初至今,滚动期间,同比
相对日期允许用户选择 YTD、MTD 和 Day,但不允许进行年度比较或滚动期比较。
为了便于进行年度对比和滚动期对比,我们将创建 2 个参数;第一个[结束日期选择器]将决定结束日期,第二个[日期比较]将告诉您要查看的日期范围类型。下面是我经常听到用户要求的 7 个日期对比。这些比较可以分为三类:年初至今,滚动期,同比。每个结束日期将根据[结束日期选择器]确定。在日期比较仪表盘上,进行选择以查看日期范围如何变化。
年初至今:从日历年的第一天开始
1.年初至今(例如,2018 年 9 月 15 日)
2.一年中的每一天(例如,星期一、星期二)
滚动周期:连续天数
3.年初至今累计
同比:上年同期的当前结果
4.季度至今
5.本月至今
6.日期(例如,2018 年 9 月 15 日)
7.一周中的某一天(例如,星期一、星期二)
构建[结束日期选择器]首先创建参数
然后构建[结束日期选择器]计算。这可以根据数据更新的频率进行调整。大多数时间默认为昨天,因为在许多情况下,提取每天刷新一次。当用户需要跟踪 YTD 和 MTD 发生的事情时,月末选项(例如,当前、上一个月、2 个月、当前年末)很方便。
DATE(Case [Parameters].[End Date Chooser]WHEN 1 THEN TODAY()WHEN 2 THEN DATEADD('day', -1, TODAY())WHEN 3 THEN { MAX([Select Date field]) }WHEN 4 THEN [Custom End Date]WHEN 5 THEN DATEADD('day',-1,DATEADD('month',1,DATETRUNC('month',TODAY())))WHEN 6 THEN DATEADD('day',-1,DATETRUNC('month',TODAY()))WHEN 7 THEN DATEADD('day',-1,DATEADD('month', -1, DATETRUNC('month',TODAY())))WHEN 8 THEN DATEADD('day',-1,DATEADD('month', -2, DATETRUNC('month',TODAY())))WHEN 9 THEN DATEADD('day',-1,DATEADD('month', -3, DATETRUNC('month',TODAY())))WHEN 10 THEN DATEADD('day',-1,DATEADD('month', -4, DATETRUNC('month',TODAY())))WHEN 11 THEN DATEADD('day',-1,DATEADD('month', -5, DATETRUNC('month',TODAY())))WHEN 12 THEN DATEADD('day',-1,DATEADD('month', -6, DATETRUNC('month',TODAY())))WHEN 13 THEN DATEADD('day',-1,DATEADD('month', -7, DATETRUNC('month',TODAY())))WHEN 14 THEN DATEADD('day',-1,DATEADD('month', -8, DATETRUNC('month',TODAY())))WHEN 15 THEN DATEADD('day',-1,DATEADD('month', -9, DATETRUNC('month',TODAY())))WHEN 16 THEN DATEADD('day',-1,DATEADD('month', -10, DATETRUNC('month',TODAY())))WHEN 17 THEN DATEADD('day',-1,DATEADD('year', 1, DATETRUNC('year',TODAY())))END)
接下来创建[日期比较对象]参数。
创建[比较日期]计算
CASE [Parameters].[Date Compare to]//Year To DateWHEN 1 THEN//CurrentIIF( DATEPART('dayofyear',[Select Date field]) <= DATEPART('dayofyear',[End Date Chooser])AND YEAR([Select Date field]) - YEAR([End Date Chooser]) = 0, 0//Previous, IIF( [Select Date field] <=DATEPARSE('MM-dd-yyyy',(STR(MONTH([End Date Chooser]))+'-'+STR(DAY([End Date Chooser]))+'-'+STR(YEAR(DATEADD('year',-1, [End Date Chooser])))))AND YEAR([Select Date field]) - YEAR([End Date Chooser]) = -1, -1, NULL))//Year to WeekdayWHEN 2 THEN//CurrentIIF( DATEPART('dayofyear',[Select Date field]) <= DATEPART('dayofyear',[End Date Chooser])AND YEAR([Select Date field]) - YEAR([End Date Chooser]) = 0, 0//Previous, IIF( YEAR([Select Date field]) - YEAR([End Date Chooser]) = -1AND DATEPART('week', [Select Date field])*10 + DATEPART('weekday', [Select Date field])<= DATEPART('week', [End Date Chooser])*10 + DATEPART('weekday', [End Date Chooser]), -1, NULL))//Rolling Year to DateWHEN 3 THEN//The first statement sets the conditions for a date to be considered the “current period.”//It will check every date to the date we've chosen as our starting point.//It must be based on whatever date we’ve chosen and go back a user chosen number of months, weeks, days, etc.//If the difference between these dates is >=0 and < our Period Length, we consider it to be the “current period.”IIF( DATEDIFF('day',[Select Date field],[End Date Chooser]) >=0AND DATEDIFF('day',[Select Date field],[End Date Chooser])< 365, 0//The second statement sets the conditions for a date to be considered the “previous period.”//It will compare every date to the date we've chosen as our starting point.//It will be based on whatever date we've chosen and it will immediately precede the “current period” and be the same length.//If the difference between the dates is > the Period Length but also < two times the length, it will be the “previous period.”, IIF( DATEDIFF('day',[Select Date field],[End Date Chooser]) >= 365AND DATEDIFF('day',[Select Date field],[End Date Chooser]) < 2*365, -1, NULL))//Quarter to DateWHEN 4 THEN//CurrentIIF( [Select Date field] <= [End Date Chooser]AND DATEDIFF('quarter',[Select Date field],[End Date Chooser])= 0, 0//Previous, IIF( [Select Date field] <=DATEPARSE('MM-dd-yyyy',(STR(MONTH([End Date Chooser]))+'-'+STR(DAY([End Date Chooser]))+'-'+STR(YEAR(DATEADD('year',-1, [End Date Chooser])))))AND DATEDIFF('quarter',[Select Date field],[End Date Chooser]) = 4, -1, NULL))//Month to DateWHEN 5 THEN//CurrentIIF( [Select Date field] <= [End Date Chooser]AND DATEDIFF('month',[Select Date field],[End Date Chooser])= 0, 0//Previous, IIF( DAY([Select Date field]) <= DAY([End Date Chooser])AND DATEDIFF('month',[Select Date field],[End Date Chooser])= 12, -1, NULL))//DateWHEN 6 THEN//CurrentIIF( DATEDIFF('day',[Select Date field],[End Date Chooser])= 0, 0//Previous, IIF( [Select Date field] =DATEPARSE('MM-dd-yyyy',(STR(MONTH([End Date Chooser]))+'-'+STR(DAY([End Date Chooser]))+'-'+STR(YEAR(DATEADD('year',-1, [End Date Chooser]))))), -1, NULL))//WeekdayWHEN 7 THEN//CurrentIIF( DATEDIFF('day',[Select Date field],[End Date Chooser])= 0, 0//Previous, IIF( YEAR([Select Date field]) - YEAR([End Date Chooser]) = -1AND DATEPART('week', [Select Date field])*10 + DATEPART('weekday', [Select Date field])= DATEPART('week', [End Date Chooser])*10 + DATEPART('weekday', [End Date Chooser]), -1, NULL))END
把所有的放在一起
最后一步,我们将创建一个仅使用当前年份的日期字段,这样我们就可以在月/日匹配的时间序列上显示上一个和当前时间段。
创建计算[日期月/日/年]。这将创建一个匹配月和日的新日期字段,并使用[结束日期选择器']获取当前期间年份。添加了额外的逻辑来考虑闰年,闰年在二月有额外的一天。如果不加上这个,那么 2 月 29 日将在 3 月结束。
DATE(STR(MONTH([Select Date field]))+"/"+IIF(DATEPART('day',[Select Date field]) = 29 AND DATEPART('month',[Select Date field]) = 2,STR(DAY(DATEADD('day', -1, [Select Date field]))),STR(DAY([Select Date field])))+"/"+STR(YEAR([End Date Chooser])))
然后根据上一节中的逻辑创建[日期视图 _ 连续]和[日期视图 _ 离散],将日期切换到[日期月/日/年]。
现在我们有了满足本文开头描述的原始用例的最终视图。最终的工作簿可以在这里查看。
Datmo:用于跟踪和可重复机器学习实验的开源工具
由于数据科学家在研究生院和工作中经常训练模型,我们在模型构建过程中面临许多挑战。特别是,这些是我们认为最大的问题:
- 管理库:我们大多数人都面临着安装运行代码所需的神奇的软件包排列的问题。有时候把 CUDA 升级到 9.2 就是 TensorFlow 1.9 破了。对于其他人来说,这是解决 PyTorch,CuDNN 和 GPU 驱动程序的魔方。用于构建机器学习模型的不断发展的框架和工具越来越多,它们都是独立开发的——简而言之,管理它们的交互是一个巨大的痛苦。
- 管理实验:三次运行前测试精度较高,但我忘记了我使用了什么超参数配置,会发生什么情况?或者试图从最近一批运行中记住哪个版本的预处理产生了最好的模型?需要记录实验以及环境、代码和数据,以及其他实验元数据的例子数不胜数,但是它们在现状中是分离的。
这个问题最近在社区中引起了广泛关注,这是谷歌的皮特·沃顿的博客。
我们的解决方案:
这些问题让我们头疼了无数次,在和朋友聊过之后,我们知道我们并不孤单。我们想要的东西不仅能在实验过程中跟踪配置和结果,还能让数据科学家通过重新运行来重现任何实验!
我们最初将它构建为一个内部解决方案,用于跟踪我们的实验,使它们具有可重复性,并且易于设置环境。当我们开始扩展它时,我们努力开发一种工具,它有一个开放简单的接口,可以与我们已经在做的机器学习的方式无缝集成;相对于框架来说是通用的,但是功能强大,提供了完全的可复制性。基本上,我们可以给我们的朋友一些东西,这样他们可以在命令行上用几个命令运行他们的实验,并且仍然可靠地重复它们。
在我们自己构建和使用它之后,我们决定将其作为一个名为 datmo 的开源工具提供。
下面是datmo的工作原理!
在初始项目设置之后,只需要一个命令来运行实验,最后再用另一个命令来分析结果!
Running an experiment with datmo
这看起来不错,但是当我们有多个实验时会发生什么呢? datmo 在这里得到了更多的使用,因为我们可以使用它来比较和分析结果,并在稍后的时间点重新运行先前的实验。下面是如何使用 datmo 来实现这一点!
Rerunning a previous experiment with datmo
现在,让我们用这个例子来弄脏我们的手。有了 datmo ,我们消除了复杂性,同时提供了一种快速启动一切的方法。
快速入门:
对于这个例子,我们将展示来自经典 Fisher Iris 数据集的一个简单分类器的训练
0。预先请求:
让我们首先确保我们有 datmo 的先决条件。Docker 是主要的先决条件,所以让我们确保 docker 已经安装(并且正在运行!)才开始。你可以在这里找到基于你的 OS 的说明: MacOS , Windows , Ubuntu 。
然后,我们可以使用以下命令从您的终端安装 datmo :
$ pip install datmo
1.克隆这个 GitHub 项目,
$ git clone [https://github.com/shabazpatel/quick-start.git](https://github.com/shabazpatel/quick-start.git)
2.在您的项目中,使用 CLI 初始化 datmo 客户端
$ cd quick-start
$ datmo init
然后,响应以下提示:
Enter name: (up to you!)Enter description: (up to you!)
接下来,将询问您是否想要设置您的环境。
选择y并在出现提示时依次选择以下选项:
Please select one of the above environment type: **cpu**Please select one of the above environments: **data-analytics**Please select one of the above environment language: **py27**
3.现在,使用下面的命令运行您的第一个实验,
$ datmo run ‘python script.py’
让我们看看所有运行的列表,
$ datmo ls
4.现在让我们修改新一轮的脚本,
我们将更改script.py文件。让我们取消脚本中下面一行的注释,并删除另一个配置字典,
# config = { "solver": "liblinear", "penalty": "l1" }
5.现在我们已经更新了脚本中的环境和配置,让我们运行另一个实验,
$ datmo run ‘python script.py’
6.一旦完成,我们现在将有两个实验跟踪,能够在任何机器上重新运行
Check for the previous runs with:
$ datmo lsSelect the earlier run-id to rerun the first experiment
$ datmo rerun <run-id>
恭喜你,你已经成功地复制了之前的实验!
以前,这一过程会浪费时间和精力,带来各种故障排除和令人头痛的问题!使用 datmo ,我们运行实验,跟踪它们,并用 4 个命令重新运行它们。现在,您可以使用这个通用标准分享您的实验,而不必担心复制,无论是队友复制您的工作还是尝试将模型部署到生产中。这显然只是一个小样本,但是您可以出去亲自尝试其他流,例如在 2 分钟内旋转 TensorFlow jupyter 笔记本!
在 GitHub 上查看我们,并在 @datmoAI ✌️给我们反馈
数据形状的 DBSCAN 聚类 k-means 不能很好地处理(在 Python 中)
在这篇文章中,我想从 Andreas C. Müller & Sarah Guido 的使用 Python 进行机器学习简介中选取一些内容,并简要阐述其中一个示例,以展示当 k-means 集群似乎不能很好地处理数据形状时 DBSCAN 集群的一些优势。我将直奔主题,所以我鼓励你阅读第三章的全部内容,从 168 页开始,如果你想扩展这个话题的话。当描述算法的工作时,我将引用这本书。
使聚集
- 它的任务是将数据集划分成组,称为集群
- 目标是以这样的方式分割数据,使得单个聚类中的点非常相似,而不同聚类中的点不同
k 均值聚类
- 试图找到代表数据的特定区域的聚类中心
- 在两个步骤之间交替:将每个数据点分配给最近的聚类中心,然后将每个聚类中心设置为分配给它的数据点的平均值
- 当实例到集群的分配不再改变时,算法结束
这就是 k-means 在可视化表示中的工作方式:
import mglearnmglearn.plots.plot_kmeans_algorithm()
k-means 聚类的一个问题是,它假设所有方向对于每个聚类都是同等重要的。这通常不是大问题,除非我们遇到一些形状奇怪的数据。
在本例中,我们将人工生成该类型的数据。使用本书作者提供的以下代码(对聚类数做了一些小的修改),我们可以生成一些 k-means 无法正确处理的数据:
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_blobs
from sklearn.cluster import KMeans# generate some random cluster data
X, y = make_blobs(random_state=170, n_samples=600, centers = 5)
rng = np.random.RandomState(74)# transform the data to be stretched
transformation = rng.normal(size=(2, 2))
X = np.dot(X, transformation)# plot
plt.scatter(X[:, 0], X[:, 1])
plt.xlabel("Feature 0")
plt.ylabel("Feature 1")
plt.show()
正如你所看到的,我们可以说有 5 个定义好的具有拉伸对角线形状的集群。
让我们应用 k 均值聚类:
# cluster the data into five clusters
kmeans = KMeans(n_clusters=5)
kmeans.fit(X)
y_pred = kmeans.predict(X)# plot the cluster assignments and cluster centers
plt.scatter(X[:, 0], X[:, 1], c=y_pred, cmap="plasma")
plt.scatter(kmeans.cluster_centers_[:, 0],
kmeans.cluster_centers_[:, 1],
marker='^',
c=[0, 1, 2, 3, 4],
s=100,
linewidth=2,
cmap="plasma")plt.xlabel("Feature 0")
plt.ylabel("Feature 1")
我们在这里可以看到, k-means 已经能够正确地检测到中间和底部的集群,而对于顶部的集群则存在问题,这些集群彼此非常接近。作者说:“这些群体向对角线拉伸。由于 k-means 只考虑到最近的聚类中心的距离,它不能处理这类数据”
让我们看看 DBSCAN 聚类如何帮助处理这种形状:
基于密度的噪声应用空间聚类
关于 DBSCAN 集群的一些亮点摘自该书:
- 代表*“基于密度的有噪声应用的空间聚类”*
- 不需要用户预先设置聚类数和
- 可以捕捉复杂形状的集群
- 可以识别不属于任何聚类的点(作为异常值检测器非常有用)
- 比凝聚聚类和 k-means 稍慢,但仍可扩展到相对较大的数据集。
- 工作原理是识别特征空间的拥挤区域中的点,其中许多数据点靠得很近(特征空间中的密集区域)
- 密集区域内的点称为岩心样本(或岩心点)
- DBSCAN 中有两个参数:
min_samples和eps - 如果在到给定数据点的距离
eps内至少有min_samples个数据点,则该数据点被分类为岩心样本 - 通过 DBSCAN 将彼此距离比距离
eps更近的核心样本放入同一簇中。
这是聚类如何根据两个参数的选择而变化的示例:
mglearn.plots.plot_dbscan()
In this plot, points that belong to clusters are solid, while the noise points are shown in white. Core samples are shown as large markers, while boundary points are displayed as smaller markers. Increasing eps (going from left to right in the figure) means that more points will be included in a cluster. This makes clusters grow, but might also lead to multiple clusters joining into one. Increasing min_samples (going from top to bottom in the figure) means that fewer points will be core points, and more points will be labeled as noise.
参数eps在某种程度上更重要,因为它决定了点靠近*意味着什么。*将eps设置得很小将意味着没有点是核心样本,并且可能导致所有点被标记为噪声。将eps设置得非常大将导致所有点形成一个单独的簇。
让我们回到我们的例子,看看 DBSCAN 如何处理它:
from sklearn.cluster import DBSCAN
from sklearn.preprocessing import StandardScalerscaler = StandardScaler()
X_scaled = scaler.fit_transform(X)# cluster the data into five clusters
dbscan = DBSCAN(eps=0.123, min_samples = 2)
clusters = dbscan.fit_predict(X_scaled)# plot the cluster assignments
plt.scatter(X[:, 0], X[:, 1], c=clusters, cmap="plasma")
plt.xlabel("Feature 0")
plt.ylabel("Feature 1")
在扭曲eps和min_samples一段时间后,我得到了一些相当一致的集群,仍然包括一些噪声点。
- 虽然 DBSCAN 不需要显式地设置簇的数量,但是设置
eps隐式地控制将找到多少个簇。 - 缩放数据后,为
eps找到一个好的设置有时会更容易,因为使用这些缩放技术将确保所有特征具有相似的范围。
最后,考虑到我们创建了明确定义 5 个聚类的数据点,我们可以使用 adjusted_rand_score 来衡量性能。这种情况并不常见,因为在实际情况中,我们一开始就没有聚类标签(因此我们需要应用聚类技术)。因为在这种情况下,我们有标签,我们可以测量性能:
from sklearn.metrics.cluster import adjusted_rand_score#k-means performance:
print("ARI =", adjusted_rand_score(y, y_pred).round(2))
ARI = 0.76#DBSCAN performance:
print("ARI =", adjusted_rand_score(y, clusters).round(2))
ARI = 0.99
你有它!DBSCAN 得分为 0.99,而 k-means 仅得 0.76
DC:他们都去哪里了?
与 R 一起寻找 310 万次骑行的罗盘方位
几周前,我写了一篇关于人们使用 DC 自行车共享服务的文章,我仍然对这些数据感到好奇。热图的结果是显而易见的:在早上和下午的高峰时段,乘车人数激增。写完帖子后,我开始思考每一次旅行的方向,以及这些旅行在一天中的每个小时分手的方向。警告:了解 DC 的地理位置将有助于理解这篇文章。
关于自行车共享数据有无数的事情要做(并且已经被做了),但是我从来没有见过它和我想象的一样。在我的脑海中,我看到了上午前往杜邦公司办公室、法拉古广场和 K 街地区的自行车流,然后在下午前往周围的街区。
在这篇文章中,我想回答两个问题:
- 对于一天中的每个小时,自行车共享用户的平均去向是什么?
- 对于几个选定的站点,自行车共享用户通常每小时去哪个方向?
数据准备
数据集需要一些调整才能成形。首先,我删除了在同一个车站开始和结束的乘车。有 113,167 个观察被删除,因为我不认为它们是真正的乘坐。接下来,我需要添加每次骑行的起点和终点的 GPS 坐标。最后,我需要计算从起点到终点的方位。
每个自行车共享点的纬度和经度都要感谢区交通局。然后,我总结了这个大型数据集,向我展示了每个小时的平均起点和终点。和往常一样, dplyr 库在这里是个救星。为了最终得到我想要的可视化效果,我需要两个数据集(一个接一个的骑行和一个接一个的小时),就像这篇文章后面解释的那样。
有了两个数据集中的开始和结束 GPS 坐标,通过我从 fossil 包中提取的一个函数,计算每个观测的方位就很简单了。我不是地理空间分析方面的专家,所以这省去了我一些大麻烦。
然而,要找到所有这些乘坐的平均方位,我不能简单地取每个观察方位的平均值。罗盘方位在 0 到 359.9 度之间。想象一个指南针,其北 0 度,东 90 度,南 180 度,西 270 度。例如,假设大多数游乐设施通常是向北行驶的。轴承将是 350 至 360 度和 0 至 10 度的组合。这些乘坐的平均值将是大约 180 度(南方),并给我们准确的错误答案(数学版本:350 度乘坐和 10 度乘坐的平均值将是(350 + 10) / 2 = 180)。因此,我必须找到每小时的平均起点坐标和平均终点坐标,然后计算这条路线的方位。
在这里的一张表格中,无聊地呈现了每小时 DC 自行车共享出行的平均方位。为了美好的事物…
可视化自行车方向
是时候策划了!为了实现我想象的可视化,我需要使用两个数据集,完整的 310 万个观察数据集和逐小时数据集。第一个数据集将显示来自所有单独乘坐的方向喷雾,第二个数据集将显示该小时块中平均乘坐的方位。
在我的脑海中,我看到一个指南针,背景中所有的游乐设施都是透明的,一个箭头根据小时和平均方向从左向右摆动。 ggplot 包有一个 coord_polar 函数将直方图转换成圆形图。在修改了主题之后,我能够让它看起来像一个指南针!
我为每张图表创建了 24 张图片,每张图片代表一天中的一个小时,然后把它们放入 GIF 制作机中,瞧!
gif 上的一些注释:
- 灰色条代表那个小时在那个方向乘坐的次数。最长长度代表该小时在该方向上的最大乘坐量。因此,您无法比较一个小时与下一个小时的条形高度。我插入了那个小时乘坐的次数来提供一些背景。
- 这张 GIF 不以任何方式代表每次乘坐的距离。只是方向。
车站 gif
我决定分解上面的 GIF,看看骑行一站一站地走向哪里。我选择它们是基于它们在 DC 的位置(还有一个在芒特普莱森特,因为我曾经住在那里)。
正如你所看到的,根据车站在 DC 的位置,大多数乘坐的方向可能会有很大的不同。同样,这在直觉上是有意义的。如果你从 DC 的东边开始,你可能会向西走,因为那个方向有更多的活动,有更多的车站可以停靠你的自行车。
我还注意到有很多游乐设施直接向西、向东或向南行驶。由于 DC 是一个网格城市,似乎许多自行车骑行开始和结束在同一条街上。这是有道理的。这也是有道理的,某些车站,如联合车站,主要是向西(K 街,杜邦等)的方向。).
我有几个理由来解释为什么正北不是一个常见的方位。首先,我删除了所有在同一个车站开始和结束的乘车。这些飞行会产生 0 度的方位,或者直接指向北方。然而,应该仍然有直接向北行驶的游乐设施,比如说沿第 14 街西北方向。我的理论是,你越往北走,就越容易上坡。作为重型自行车,上坡骑并不流行。
结论和后续步骤
看一下自行车共享站的地图(以及整个 DC 的地图),大多数的站和人都在西北方向。从西北方向去上班,你通常会往南走。要回家,你通常会向北走。
再往深里看,另一个帖子的成因,除了方位,我还想看每一次乘坐距离每一站的距离。这将把我带到 ggmap 包,我刚刚开始修补它。
一如既往,我很想听听你的想法。如果你认为我错过了一个很棒的电视台,给我一声,我很乐意去看看。或者如果你想看看你当地的车站,我也可以帮你!
代码可在我的 GitHub repo 这里(寻找 dc bikeshare 方向。R 和站承载 gif 功能。R )。
深度卷积生成对抗网络
生成对抗网络最有趣的部分之一是生成网络的设计。生成器网络能够获取随机噪声并将其映射到图像中,使得鉴别器无法辨别哪些图像来自数据集,哪些图像来自生成器。
这是神经网络的一个非常有趣的应用。通常,神经网络将输入映射为二进制输出(1 或 0),可能是回归输出(某个实数值),甚至是多个分类输出(如 MNIST 或 CIFAR-10/100)。
在本文中,我们将看到神经网络如何从随机噪声映射到图像矩阵,以及在生成器网络中使用卷积层如何产生更好的结果。
我们将回顾在 ICLR 展示 DCGANs 的论文,这是一个生成卧室的生成器网络架构,我们将回顾来自 GANs-in-Action 知识库的一些 Python/Keras 代码。
这是 LSUN 场景建模论文中介绍的 DCGAN 生成器。该网络接收一个 100×1 的噪声矢量,表示为 Z,并将其映射到 64×64×3 的 G(Z)输出。
这种架构特别有趣的是第一层扩展随机噪声的方式。网络从 100x1 到 1024x4x4!这一层被称为“项目和整形”。
我们看到,在这一层之后,应用了传统的卷积层,该卷积层使用传统卷积层教导的(N+P — F)/S + 1 等式来重塑网络。在上图中,我们可以看到 N 参数(高度/宽度)从 4 到 8 到 16 到 32,似乎没有任何填充,内核过滤器参数 F 是 5x5,步幅是 2。您可能会发现,这个等式对于设计定制输出大小的卷积层非常有用。
我们看到网络从
100 x 1→1024 x 4 x 4→512 x 8 x 8→256 x 16 x 16→128 x 32 x 32→64 x 64 x 3
以上是论文中给出的网络输出,引用了 5 代训练后的结果。相当令人印象深刻的东西。
现在,让我们看一些 python 代码:
这段代码摘自 Jakub Langr 和 Vladimir Bok 创建的 gans-in-action 知识库,据我所知,这是在 Keras 中实现 gans 的最佳入门代码。我认为这本书还没有发行,但我想象它会很好。
行动中的 GANs 的伙伴知识库:具有生成性对抗网络的深度学习…
github.com](github.com/GANs-in-Act…)
下面的代码是我如何运行我的第一个 GAN 网络的,(没有实现 DCGANs):
def generator(img_shape, z_dim):
model = Sequential() # Hidden layer
model.add(Dense(128, input_dim = z_dim)) # Leaky ReLU
model.add(LeakyReLU(alpha=0.01)) # Output layer with tanh activation
model.add(Dense(28*28*1, activation='tanh'))
model.add(Reshape(img_shape) z = Input(shape=(z_dim,))
img = model(z) return Model(z, img)
下面的架构并不复杂,实际上在 MNIST 数据集的例子上产生了相当不错的结果。该模型接受噪声矢量并将其映射到密集连接的层,该层映射到输出层,该输出层是被整形为 28×28 MNIST 数字矩阵的平面 784×1 矢量。
现在让我们将其与 gans-in-action 存储库中提供的 DCGAN 代码进行对比:
def generator(img_shape, z_dim):
model = Sequential()
# Reshape input into 7x7x256 tensor via a fully connected layer
model.add(Dense(256*7*7, input_dim = z_dim))
model.add(Reshape((7,7,256)) # Transposed convolution layer, from 7x7x256 into 14x14x128 tensor
model.add(Conv2DTranspose(
128, kernel_size = 3, strides = 2, padding='same')) #Batch normalization
model.add(BatchNormalization()) #Leaky ReLU
model.add(LeakyReLU(alpha=0.01)) # Transposed convolution layer, from 14x14x128 to 14x14x64 tensor
model.add(Conv2DTranspose(
64, kernel_size=3, strides=1, padding='same')) # Batch normalization
model.add(BatchNormalization()) # Leaky ReLU
model.add(LeakyReLU(alpha=0.01)) # Transposed convolution layer, from 14x14x64 to 28x28x1 tensor
model.add(Conv2DTranspose(
1, kernel_size = 3, strides = 2, padding='same')) # Tanh activation
model.add(Activation('tanh')) z = Input(shape=(z_dim,))
img = model(z) return Model(z, img)
我们看到上面的架构非常类似于 ICLR,LSUN 场景生成器论文中提出的 DCGAN。输入从 100×1 噪声投射到 7x7x256 张量,然后卷积,直到达到 28×28×1 MNIST 数字输出。
同样,我们看到同样的项目和整形,接着是卷积层进入起始代码中图表的输出。
结论
我希望这篇文章能帮助您开始构建自己的 DCGANs。我认为它至少很好地解释了高层架构应该如何工作。剩下的挑战在于为卷积层以及投影和整形层找到正确的参数。
我真的发现这个开源代码库和 ICLR 论文的结合有助于我理解这个概念。我对构建 GANs 并看到他们能做什么感到非常兴奋,请留下您认为真正有帮助的任何其他资源的评论。如果您想了解使用这些 DCGANs 进行数据扩充的进一步研究,请点击此处。
CShorten
Connor Shorten 是佛罗里达大西洋大学计算机科学专业的学生。对计算机视觉、深度学习和软件工程感兴趣。
对随机森林进行解码
用 Python 从头开始构建你的随机森林,并解释“黑盒”背后的数学原理
动机:随机森林集成广泛用于现实世界的机器学习问题、分类以及回归。它们的受欢迎程度可以归因于这样一个事实,即实践者通常使用随机森林算法获得最佳结果,只需最少的数据清理,并且没有特征缩放。为了更好地掌握基本原理,我决定从头开始编写一个随机森林,并对数据中的不同特征及其在决定最终结果中的作用进行一些可视化。人们普遍认为这是一个黑盒,快速浏览一下这个算法将会证明它实际上是很容易解释的,除了是一种利用“多数票的力量”的强大技术。
简介
随机森林集成是一种分而治之的方法,用于提高单个弱决策树模型的性能。这背后的主要原理是一群“弱学习者”可以走到一起形成一个“强学习者”。每个分类器,单独地,是一个“弱学习者”,而所有的分类器放在一起是一个“强学习者”。
随机森林集合
随机森林概念的核心是对大量决策树的结果进行平均。决策树通常是向不熟悉机器学习的人解释预测背后的直觉的便捷工具。但是解释一个随机森林如何得出一个预测,以及使用哪些特征或独立变量,可能是一项相当艰巨的任务。随机森林经常被误解为“黑盒”或难以理解。
Random Forest Ensemble
用 Python 编码随机森林
下一节将从头开始演示随机森林的编码和优化,并深入了解随机森林的工作方式。我们还着眼于理解在预测结果时,某些特征如何以及为什么比其他特征被赋予更大的权重。
随机森林和决策树类
使用 Python 中面向对象的编程方法,定义了由“决策树”对象组成的类“RandomForest”。随机森林基本上是其决策树执行的每个核心功能的收集器和平均值计算器。
The Random Forest class
The Decision Tree class
- 构造函数(init)采用样本大小或训练样本的数量来为每棵树考虑,以及为每棵树分割的最大特征,以便在每棵树中灌输随机性并消除预测中的偏差。它还需要随机森林算法中使用的一些其他常见超参数。
- fit 方法执行使用训练数据构造随机森林的成员树的关键功能。
- predict 方法执行跨每个成员树平均测试数据预测的功能,以决定测试数据的最终预测结果。
训练模型的逻辑流程
随机森林类中的 fit()方法被调用用于训练模型,该方法调用决策树类中的函数 var_split(),该函数循环调用 find_better_split()。这将构造林中的每个成员决策树,并适合每棵树的定型数据。
var_split() function implementation in Decision Tree
find_better_split() function implementation
在找到在每次分裂时最大化同质性的特征之后,决策树递归地分裂成左和右子树。这是通过双回路逻辑实现的:
- 第一个循环是遍历树的所有特征,以确定哪个特征对于特定级别的分割是最好的。var_split()函数为每个特性调用 find_better_split(),以确定产生最纯粹叶节点的特性。
- 在内部循环中,对每个特征调用 find_better_split()函数,以计算该特征的最佳值,在该最佳值处应该对树进行分割。
- 这是通过对该特性的值进行排序、遍历该特性的所有不同值并跟踪因变量的相应值来实现的。
- 计算分数,该分数是通过在每个不同的值处为每个特征分割树而获得的信息增益。该分数表示分裂后形成的子树的方差,需要将其最小化。
- 导致子树中最低方差或最大同质性的特征和特征值的组合被选择用于分割。
- 在找到最佳特征和最佳分割值后,树被分割成左右子树,并且该类的 leftTree 和 rightTree 成员被填充。
- 在 leftTree 和 rightTree 成员上递归调用函数 var_split(),以找到要分割的最佳特征以及该特征的最佳值。这一直持续到满足叶节点的条件之一,即达到树的最大深度或达到数据样本的最小量。
- 分配给该叶的值是包含在其中的训练样本的平均值,或者是共同代表该叶中数据的相应值。
对测试/验证数据集进行预测
predict() function for Random Forest
predict() implementation for a Decision Tree
使用该模型预测结果的代码非常简单明了。
- 随机森林集合的预测是通过平均每个单独决策树的预测来完成的。
注意:对于随机森林回归器来说,平均预测是一种很好的技术,但是对于随机森林分类器来说,考虑树木预测的频率并选择具有最大频率的预测值才是正确的做法。
- 在决策树中,对每一行的预测是通过遍历树,将特征值与要在每一层上分割的特征值进行比较,如果特征值较小,则导航到左侧子树,如果特征值较大,则导航到右侧子树来完成的。这一直持续到到达叶节点。
- 然后,对该行的预测是叶节点中包含的值,该值已在训练阶段预先分配。
解释随机森林模型
我们现在有了自己的随机森林集合库的基本结构。让我们添加功能交互和树解释的超级有用的增强,这是帮助解释随机森林的重要概念。
特征重要性背后的直觉
Calculate feature importances for this model
要了解某个特定特征在决定模型预测时有多大发言权,请遵循以下方法:
- 记录数据集预测的评估指标(准确性或误差)。
- 接下来,使用 Numpy 库中的 shuffle()方法对重要性有待确定的特性的值进行随机洗牌。
- 通过运行模型来预测具有该特定特征列的值的数据集的值,再次计算评估度量。
- 计算得到的度量和没有打乱特征值的先前度量之间的差异。
- 如果差异很大,得到的度量要差得多,那么这个特定的特性在决定模型预测时肯定有很大的发言权。
如果随机改变某个特性的值会导致模型性能急剧下降,我们可以说这个特性肯定会在树中相对较高的位置使用,并且很重要。在我的实现中,这种技术已经通过遍历特性而被用于每个特性。
可以为特征及其相应的重要性绘制漂亮的可视化图形,如条形图,以解释我们的随机森林模型赋予不同特征的权重。
Feature Importance Chart
随机森林的部分相关图
部分相关图对于理解结果或目标变量如何随特定特征变化,以及目标和该特征之间的实际关系特别有用。是单调递增/递减还是无规律?
单变量图与部分相关图
如果我们看一个典型的单变量图来寻找一个特征对目标变量的影响,我们很少看到连续的上升/下降曲线或直线。单变量图通常是不规则的。
例如,假设您正在处理一个住房数据集,并希望在给定一组要素(包括房屋建造年份、地点和其他要素)的情况下预测房价。如果你绘制一个价格与建造年份的单变量图,你会看到一条连续上升的曲线,因为房地产价格通常会随着时间的推移而上涨。但是在几年间,这一情节变得越来越糟。这会错误地让你相信这几年是房价下跌的年份。
Univariate Plot for the log of House Prices varying with Year Built
然而,这可能是因为那些年买的大多数房子都在相对便宜的地方,因此曲线下降。
一个部分相关图将解决这个问题,并显示价格和年份之间的真实关系,你会发现它们毕竟是持续增长的。这是因为在部分相关图中,我们只考虑变化的特征,确保所有其他特征保持不变。因此,我们从其他因变量中剔除噪声,并更好地理解每个特征与目标变量之间相互作用的真实性质。
Partial Dependence Plot would show the correct trend of prices against Year Built
部分依赖图的实现
我的部分依赖图的实现方式如下:
Partial Dependence Plot for a particular feature
- 保持所有其他列(特征值)不变,继续遍历要考虑的要素的所有唯一值,并运行模型来预测该要素的每个唯一值的结果。
- 绘制结果值,以了解目标变量随特定特征变化的趋势。
保持其他列不变并改变计算依赖的特征,是解决来自外部源的噪声渗入特征和目标之间关系的描述的问题的关键。
随机森林的树解释器
解释随机森林预测流程的一个非常有用的技术是对每个决策树使用树解释器。
Tree Interpreter Class with the predict function
树根处的值(简单地说就是训练数据中所有目标的平均值)被称为“偏差”。
树解释器类中的 predict()函数调用决策树中的 predict_row_for_ti()函数,该函数预测“行”或单个数据样本的结果。
在每次分割时,使用分割成子树后样本平均值的变化量来计算分割中使用的特征的贡献。
特征贡献被存储在一个数组中,直到到达一个叶子,然后结果被返回给树解释器。
在叶节点,返回该数据样本的预测值,以及偏差和“贡献”,即每个特征对最终结果的贡献。
Function inside the Decision Tree, used by the Tree Interpreter
基于树形解释器的瀑布图
使用 python 中最新的“瀑布”包,每个特性对到达叶节点(最终结果)的贡献可以通过瀑布图可视化。
瀑布图是可视化每个特性贡献的最有效方法之一,从树根开始,直到叶节点的最终结果。
Waterfall Chart with the contributing features in X axis and resulting target values in the Y axis
未来的增强功能
需要进一步丰富随机森林库的几个方面如下:
在树解释的基础上,需要对随机森林中的特征的相关性进行工作,通过找到在连续分割中经常使用的特征组,因此可能具有更高的相关性。
这将是探索随机森林中特征相互作用的第二种方法,第一种方法是可以扩展到多个特征的部分相关图。
注意:Python 中的标准机器学习库在速度和效率方面进行了高度优化,因此你从头编写的任何代码都可能需要在cy thon(Python 使用的底层 C 实现)中执行,以获得更快的结果。为此,只需将“%%cython”添加到代码的开头。
最终想法和关键要点
这是从头开始编写机器学习算法的良好起点,也是理解 Python 中几个超级有用的库的良好起点,包括用于快速数学计算的 Numpy 和用于数据可视化的 Matplotlib。
完整的实现可以在我的 Jupyter 笔记本中找到,网址是 https://github . com/SonaliDasgupta/mlandiaalgorithmsfromsscratch,在那里我尝试了使用数据集的想法和可视化。代码必须进一步完善和优化,我正在努力。非常欢迎任何建议或贡献。
这对我来说是一个非常愉快的练习,我希望我已经帮助你在数学上和视觉上更深入地研究了随机森林实现,并且理解它并不像看起来那么复杂或“黑箱”。
参考文献:
- http://blog . citizennet . com/blog/2012/11/10/random-forests-ensembles-and-performance-metrics
- course.fast.ai/ml.html
用于分类的类不平衡数据集的处理。
倾斜的数据集并不少见。它们很难处理。当遇到这样的问题时,通常的分类模型和技术经常失败。虽然你的模型甚至可以让你在这种情况下达到 99%的准确率,但是,如果你用一个合理的指标来衡量自己,如 ROC Auc 得分,那么你将面临登上排行榜的麻烦。这是因为如果数据集是倾斜的,例如,阳性与阴性的比例为 10:1,那么通过预测每个样本的阳性而无需任何学习,您就可以获得 90%的准确率!那么,我们如何解决这个问题呢?这篇文章将会强调一些你可以用来做好这些工作的有效技巧。这些技术包括对数据进行不同的采样,巧妙地设置一些超参数,以及使用包含不同版本的常用算法的库,这些算法可以在内部处理不平衡。
- 采样
你可以用两种不同的方法做到这一点。
a.欠采样。
比方说,您的数据集中有 40,000 个阳性样本和 2,000 个阴性样本。从今以后,我们将把它作为我们的运行范例。你可以做的只是从 40,000 个样本中随机选取 2,000 个阳性样本,所有 2,000 个阴性样本,然后只在这 4,000 个样本上训练和验证你的模型。这将允许你以通常的方式使用所有的分类算法。这种方法很容易实现,运行速度也很快。然而,一个不利之处是,您可能会丢弃您拥有的 38,000 个阳性样本,并且这些数据将付诸东流。
为了克服这个问题,您可以创建一个模型集合,其中每个模型使用一组不同的 2,000 个阳性样本和所有 2,000 个阴性样本,并分别进行训练和验证。然后在你的测试集上,你对所有这些模型进行多数投票。这使您可以考虑到所有的数据,而不会导致不平衡。此外,你甚至可以对不同的集合使用不同的算法,这样你的集合会更加健壮。然而,这在计算上有点昂贵。
b.过采样
在这个方法中,您生成了少数类的更多样本。为此,您可以先创建创成式模型,然后创建新样本,也可以只选取现有样本进行替换。存在多种过采样技术,如 SMOTE、ADASYN 等。您将不得不看看哪一个最适合您的用例。此外,过采样本身是一个计算量很大的过程。主要的优点是,这允许你的一个模型立刻考虑你的所有数据,并且还帮助你生成新的数据。
2。使用秤重位置重量参数。
如果你使用的是 XGBoost 这样的算法,有一个简单的方法。您可以设置算法的 scale_pos_weight 超参数,以指示您的数据集具有一定比例的正负类,XGBoost 将处理其余部分。因此,在我们运行的 40,000 个阳性样本和 2,000 个阴性样本的例子中,如果我们想在这个数据集上训练我们的 XGBoost 分类器,我们应该将 scale_pos_weight 的值设置为 40,000/2,000 = 20。 这在实践中非常奏效。然而,一个缺点是这限制了您使用 XGBoost 和其他类似的算法,因为不是所有的算法都有这个可调的超参数。
3。使用不平衡学习图书馆。
是的,你猜对了。已经有一个成熟的 python 库专门用于处理这类问题。这个库是 sklearn-contrib 的一部分。但是很容易迷失在图书馆的细节里。对我来说效果最好的是BalancedRandomForestClassifier。通常的随机森林算法在不平衡数据集上表现极差。然而,这个平衡随机森林分类器是 imblearn 包的一部分,工作得非常好。它在内部处理采样问题。您可以获得随机森林所有功能以及熟悉的 sklearn API。该库的其他特性包括内置过采样器、欠采样器以及两者的组合。)和其他专门用于处理倾斜数据集的算法。这里有很多值得探索的地方。
一个月前,当我开始处理一个 20:1 不平衡的数据集时,我找不到关于如何解决这个问题的很好的资源。因此,我决定展示我在试图找到有效处理倾斜数据集的方法时学到的东西。我希望这对你有好处。你可以在我的 GitHub repo 找到代码库中使用的所有这些技术。请随时在评论中提出建议,你可能知道的任何其他方法可以帮助人们在这场不平等的战斗中找到伟大的平等!谢谢你。
处理数据灾难:简单的修复
您多久收到一次已经准备好用于分析或数据可视化的数据集?不是来自 viz 示例或教程的?我不知道你是怎么想的,但是几乎所有到我手里的数据都需要至少一点点的清理。
大多数修复都相当简单,有些可以很容易地手动完成。然而,有时我会得到一大堆文件,手动修复每个文件会相当耗时。所以我一直在用 python 开发一个小的命令行工具,来加速一些数据清理的苦差事。该工具创建了一个新文件,因此原始数据文件仍然是完整的。
通过命令行使用工具
撇
python cleaner.py -skim
这将删除 csv 文件开头的任何空行或多余行。这个似乎很容易用手修复。只需打开文件并删除有问题的行。很简单。
但是如果它是一组 2000+的文件,并且在每个文件的前两行都有一个徽标,也许还有一个电子邮件地址呢?有些文件下面有一个空行,有些有三个空行。有些还有邮寄地址。
为了处理标题行上面的这些额外的行,标记“-skim”告诉 cleaner.py 脚本将所有数据保存到一个新的 csv 文件中,不包含额外的行。空行很容易。该脚本只是检查这些行中是否有任何内容,然后跳到第一个有内容的行。
empty rows above header row
如果有额外的行,带有徽标或联系信息,事情会变得稍微复杂一些。大多数情况下,文件有几列数据,额外的行只是一列或两列。在这种情况下,脚本会检查第一行的长度是否小于数据行的一半(在此之前会检查数据行的长度)。
extra rows above header row
但是,这种方法在只有几列数据的情况下会失败。还有其他边缘情况,但这是最常见的。
extra row, but too few columns of data → fail to
在这种情况下,最好使用'-rows '标志来指示要保存哪些行。
选择特定行
如果您知道您想要哪些行,您可以使用'-rows '标志来选择它们。
python cleaner.py -rows 1-20
如果你只想跳过前 10 行,你可以只使用你想开始的行号。该脚本假设您需要这之后的所有内容。
python cleaner.py -rows 10
这也可以与“-skim”和“-cols”结合使用
python cleaner.py -skim -rows 10 -cols 2-5
这将删除额外的标题,并保存原始文件中的特定行和列。当我创建测试或样本集时,我经常使用它。
选择特定列
python cleaner.py -cols 2, 5-9, 12
- 可以按索引在范围内单独选择特定的列
- 索引从 0 开始。
功能与选择行时略有不同。
- 如果只给出一个索引->只选择该列。
- 要选择一个列和后面的所有列,如 8 之后的所有列,请使用“+”
-cols 8+
为什么?
列选择不同于行选择,因为我发现我通常对原始数据集中的特定列感兴趣。有时候,一个数据集有很多问题,我只想一次处理一列(或几列)。对于行,我通常只需要一个小的子集,哪一行并不重要。我可能只想要最后 20 行,但是我可能不需要指定第 9 行和第 21 行。
默认情况下会发生什么?
许多事情会自动完成。
“未命名”标题-移除
将 excel 表转换为 csv 可能会导致第一行的空单元格被填充“未命名:#”,因此这些单元格将从标题行中删除。
移除空列和空行
一些 csv 文件有空的列和行。这些会自动删除。如果一个文件有很多空的列和行,我可能会在做任何事情之前运行清除程序,不带任何标志。
删除多余的表格和总和行:
有些文件在所有实际数据下方有汇总表,或者有一行是列总和。这些都被移除了。
展平标题:
如果有多层标题,它们会被展平成一行。
所以像这样的桌子
会被转换成这样的东西
注意:如果设置了“另存为 json”选项,这个展平功能将被忽略…我们将在另一篇文章中详细讨论 json。
在本系列的下一篇文章中,我们将讨论一些稍微复杂一点的修复…比如从一列的内容中提取一个丢失的标题名。
这个项目的源代码可以在 github 上找到。
处理开发数据管理
西方世界大多数组织和公司的企业数据让我想起了牧牛。一场人与野兽之间的精心舞蹈,由经验丰富的训练员团队在值得信赖的边境牧羊犬的帮助下,将大量高价值商品从一个地方运送到另一个地方而不损失任何数量,保护牛群免受秃鹫或偷猎者的伤害……就像处理消费者数据一样!
在国际发展领域处理数据更像是一种乡村的放牧心态--山坡上孤独的牧羊人的心态。他的兽群通常只有一根杖,规模要小得多,也没有那么强大。但它的价值丝毫不减——它是他幸福的命脉——而且通常就像狂奔的牛群一样难以驾驭!
谢泼德这种小而装备不足的心态阻碍了开发部门真正利用我们周围正在发生的数据革命(即使是在我们工作的同一个新兴市场的商业部门!).ICT4D 的倡导者多年来一直在推动数字数据收集和基于证据的决策,但技术勒德分子和非早期采用者的使用率并不令人鼓舞。
那么问题是什么呢?为什么追不上大数据用户?
对我建议的 20 个项目进行的非正式调查揭示了阻碍开发专业人员利用快速扩展的现代数据管理产品/应用程序/工具市场的根本问题。
我们不知道外面有什么工具。
我们不知道如何使用它们。
我们买不起。
好消息是:所有这些障碍都是可以克服的。坏消息:另一边有很容易陷入的危险陷阱。
我们不知道有什么工具存在。
这在 5 年前是一个很难克服的重大挑战,除非你是一个网络良好的技术迷,喜欢在空闲时间尝试应用程序。今天,市场上有太多成熟的工具(有时是激进的)向开发专业人员推销。捐助者已经购买了数据管理平台,如 T2 的发展成果,或者与大型软件供应商建立了合作关系,如美国国际开发署的微软。
陷阱: 买错了技术。 随着技术服务提供商扩展到开发市场,组织或项目很容易陷入在技术领域臭名昭著的“闪亮的小玩意”宣传中,而没有尽职调查以了解特定的产品或应用程序是否真的能满足他们的需求。在购买新产品之前,一定要货比三家。社交媒体是一个很好的平台,可以获得对特定工具的公正反馈——使用# YourPotentialProductSolution 的标签搜索 Twitter,看看其他用户在说什么。
我们不知道如何使用这些工具。
Youtube 提供的不仅仅是可爱的动物视频和真人秀的精彩片段。几乎市场上的每一个 ICT 解决方案或数据管理产品都有在线“操作”视频,可以给你一个速成班,让你立刻成为专家。昆西·拉森在自由代码营向他的学生宣讲:阅读-搜索-提问。通读工具开发者提供的任何博客或说明。如果你的阅读材料没有提供解决方案,谷歌是你寻找答案的最好朋友。在大多数情况下,你不是第一个对软件解决方案或应用程序有疑问的人——在互联网上的某个地方,有人问过你的问题,并且(希望)分享了一个解决方案。还是找不到答案?使用 Twitter 上的#ICT4D 标签来寻找专家,他们可能会给你指出正确的资源。
陷阱: 知识刚够就闯祸。为你可能遇到的一个问题找到答案可能相对容易。但是许多实施者发现他们遇到了更大的挑战,他们有限的知识无法解决。这可能会给项目活动带来重大问题——尤其是当您正在进行一项重要的数据收集工作或者明天要交报告的时候!如果您的组织认真对待使用技术进行数据管理,投资培训和能力建设是至关重要的。像所有专业化一样,数据管理是一个很深的领域,需要时间来掌握。但是不要沮丧——像 TechChange 这样的组织提供了大量关于在开发领域使用技术的课程。提前计划,培养你需要的技能,这个陷阱很容易避免。
我们买不起工具。
经济学是我们的朋友,尤其是随着商业数据管理解决方案在发展中市场的出现。只要稍加规划,即使是最小的项目,良好的数据收集、存储、分析和可视化工具也不再遥不可及。免费的开源选项分布很广,但即使是收费的工具也变得负担得起——通过良好的规划,一个完整的数据管理生态系统每年只需 1000 美元即可部署。数据存储领域的知名企业,如 SalesForce 甚至为人道主义组织提供折扣价。全面扩展定价模式使我们这些拥有较小(但仍有价值!)数据集,利用与大公司相同的“大数据”技术产品。
陷阱: 在不需要的技术上花费过多。人们很容易对一种新工具的功能惊叹不已,却没有意识到你所支付的东西在埃塞俄比亚的农村低地或亚马逊的深处是行不通的。更糟糕的是,当我们需要的只是一辆菲亚特时,我们却为一辆凯迪拉克付出了太多。在购买一项技术之前,和你的项目经理坐在一起,想出一个必备特性&功能的清单。当你找到你梦想的工具时,确保你没有把钱浪费在你不会(或没有能力)使用的东西上。
寻找更多关于为开发项目选择数据管理工具的建议?
查看这些资源: Digital @ DAI ICTworks ICTs 促进发展
处理学习系统中的脏数据
每个伟大的学习系统背后都有数据。你对系统做得越多,产生的数据就越多。你越是合并系统、导入历史记录和篡改数据来解决问题,你生成的数据就越多,数据就越不“干净”。什么是干净数据?什么是脏数据?如何清理脏数据?问题问题。
让我们从如何定义干净数据和脏数据开始。在我的定义中,干净数据是通过直接使用系统生成的数据。在学习系统中,当你创建一个新用户时,当他们完成学习时,当他们变得有能力或在作业中被标记时,就会产生数据。它是通过使用系统工具,由学习者或辅导员的行动直接分配的。很完整也很准确。脏数据不干净!它包含错误或缺少关键字段。脏数据是由许多其他因素引入的。这可能是管理员“调整”学习记录,导入其他地方记录的记录,合并不同系统的记录,引入不完整的数据以及任何涉及直接调整后端数据库的事情。
所以导入和调整数据是有风险的事情。这并不是说你做了一次就自动破坏或污染了你所有的数据,但这肯定意味着你要冒这个风险。如果你的数据杂乱无章,你就有被感染的风险,而且很难清除。如果你想知道我们到底为什么要这么做,答案很简单,现实生活并不像总是让一切都通过你的系统那样简单或清晰。我们知道,有时您会在其他地方生成或保存数据,并且确实需要将它们带过来。所以为了安全起见,你必须采取保护措施。就数据而言,这意味着在让数据进入您和您的系统之前,要对数据进行相当严格的筛选。这也是为什么项目的早期范围界定阶段如此重要的另一个原因。一旦您发现数据是脏的,下一步就是清理它。数据清理可能是一个及时的(因此也是昂贵的)过程,所以你提供的数据越好,清理的痛苦就越少。
我在导入的数据中看到的最大的失败是每一行数据都缺少唯一的标识符。如果没有,清理工作将从为每个用户、每个“对象”、系统中任何地方引用的所有内容分配唯一标识符开始。您可以将唯一标识符的不一致使用算作没有唯一标识符,事实上这更糟糕,因为它使数据在第一次检查时看起来相当干净。数据差距也是常见的问题,在某些情况下,这是可以接受的,在其他情况下,它们是不可接受的;想象一个课程代码为空,但有一个完成日期。你认为一个学习系统如何处理一门课程,而不识别它,只知道它是什么时候完成的(这不是很好)。相似但不相同的数据是另一个痛苦。您和我都知道 St .和 Street 可能是相同的,但是在数据方面它们是不同的(这就是为什么那些唯一的 id 如此重要)。
如何判断您的数据是否是脏的?有一些非常关键的事情将很快决定这一点。差距是最明显的一个,尤其是在必需的 ID 字段中。使用 Excel 等电子表格程序中的工具进行一些分析,可以找到重复的内容。缺少 ID 字段也是一个非常明显的起点!数据的关键是系统化。脏数据通常是由人为错误引起的,在日常使用中不接触数据会增加数据保持干净的机会。当然,具有讽刺意味的是,脏数据通常需要大量的人工干预才能恢复到干净的状态。
最大的危险是,你的系统中有很多数据因为系统而变得非常干净,然后你向其中引入了一点脏数据。突然,您的系统的整个数据集遭到破坏,您的少量脏数据突然产生了大量脏数据。清理由此造成的混乱远比在导入数据之前确保数据是干净的要重要得多。
我的建议是双重的。首先让你的系统来管理你的数据,如果可以的话,不要插手。其次,如果您必须从外部来源引入数据,那么在导入之前,需要花费时间和精力来确保数据是干净的。当然,你可以导入它,然后清理它,但这样你就清理了整个系统,这是非常痛苦的。
如果你打算改变系统或者把你的学习系统和你的人力资源系统联系起来,这是清理你的数据的好时机。请记住,干净数据加上脏数据等于大量脏数据,最好在链接或引入新系统之前完成这项工作。
如果那些肮脏的数据让你觉得肮脏,我道歉!请随意附和您的肮脏数据故事,但不要期望我做所有的清理工作!