TowardsDataScience-博客中文翻译-2021-七十-
TowardsDataScience 博客中文翻译 2021(七十)
在内存中绘制决策树
避免<10 lines
简介中不必要的慢速磁盘操作
决策树本质上是可解释的分类器,也就是说,可以绘制它们来理解它们如何以及为什么做出某些决策。对于非技术专家使用 ML 的用例来说,这是一个非常重要的属性。例如,使用 ML 执行疾病检测的医生可以从图中推导出分类器做出的精确 if-else 决策。
绘制决策树
绘制决策树最广泛使用的库是 Graphviz。它提供了命令行工具和 Python 接口,与 Scikit-learn 无缝集成。有了它,我们可以定制情节,他们只是看起来很好。问题是,Graphviz 大多支持写入文件,大多数教程只是将图像保存到文件,然后加载它。
此外,Graphviz 执行两阶段绘图。首先以点格式描述树结构(https://en . Wikipedia . org/wiki/DOT _(graph _ description _ language)),然后根据点文件绘制树。这意味着更多的磁盘操作。
为什么要避免磁盘操作?
实际上,ML 模型并不在客户端计算机上运行。相反,服务器或基于云的计算引擎使用 API 为模型服务。通常会发回 JSON 和结果,例如 Base64 编码的图像。
一次处理许多客户端请求会给磁盘 I/O 带来沉重的负载,而且磁盘本身可能不是快速 SSD,而是较旧的硬盘驱动器—无论是为了降低云成本还是为了获得更大的可用空间。
通常,我们绘制树,将其发送给客户端并删除图像。这意味着将映像写入磁盘纯粹是浪费。我们必须对每个图像执行 6 个操作:保存点文件,加载点文件,保存图像,加载图像,删除点文件,删除图像。我们如何改善这种情况?
在内存中绘图
虽然 Graphviz 总是写入类似文件的字节,但我们不需要将它们写入磁盘。相反,我们可以在 RAM 中执行整个操作。这意味着更快的内存访问和没有缓慢的磁盘操作。占用的空间不是问题,因为图像不是很大,而且几乎会被立即删除。我们也不需要记住删除文件,因为垃圾收集器会为我们做这件事。
很容易避免在 Graphviz 中使用点文件——通过设置适当的参数,它将点文件作为变量返回,而不是写入文件。但是,从内存中创建图像有点困难,因为绘图结果是作为字节缓冲区返回的。幸运的是,Numpy 可以从字节创建数组,OpenCV 可以将数组解码为图像。这样我们就完全避免了使用磁盘操作。
代码和时间比较
这两个函数(内存和磁盘操作)的代码如下所示。如您所见,额外的好处是内存版本更短,不到 10 行(不包括空格和格式)。
内存版本:
磁盘操作版本:

两个版本绘制的决策树(图片由作者提供)
为了进行速度比较,我使用了 Scikit-learn 中包含的乳腺癌数据集,对两个代码版本重复了 100 次。比较平均时间,内存中的版本要快 15% !这是在一个快速的固态硬盘上,几乎没有 I/O 操作。当在 I/O 负载较重且可能有硬盘驱动器的服务器上运行时,差异会大得多。
总结
在本文中,您了解了如何使用 Graphviz 绘制决策树,无需磁盘操作,仅使用基本的库并实现了不到 10 行的较短代码。在为需要可伸缩性的实际情况部署 ML 时,像这样的考虑可能很重要。
使用 matplotlib 和 python 并行绘图
技巧和诀窍、理解大数据
如何使用多重处理加速网格状绘图的创建
这是另一篇关于如何加快速度的文章。我并不是在反对matplotlib。事实上,它是我项目中的日常驱动力。但有时你需要额外的表演。
这里有一个例子。为了我的一项研究,我必须创造大量的小提琴情节。对于那些不熟悉它们的人,这里有一个非常简单的解释:基本上,这些图在某些方面类似于直方图,但你实际上可以在一个图中比较多个小提琴,这对于直方图来说很难。violin plots 不是计算离散频率,而是计算每个轴的核密度估计值(KDE )(连续函数)。然后他们画出最终的多边形。绘图在计算上相当便宜,但计算 KDE 却不便宜。我正在处理数以亿计的数据点和多个轴。因此,性能是关键。每个轴的 KDE 可以独立计算,甚至可以独立绘图(在共享轴上)。但是matplotlib是按顺序做的——唉!
我开始尝试让matplotlib使用多处理,但我不想只并行 KDE 计算,而是可以分成多个图的任何类型的图。Matplotlib 不是真正的线程安全,所以我的大多数方法都失败了。我确信有一些漂亮的方法来实现并行,也许我没有挖掘得足够深,但是我想要一个简单问题的简单解决方案,并想出了一个便宜的技巧。
我没有将matplotlib的内部并行化,而是将我的数据分配给不同的进程,然后让每个进程创建自己的绘图。然后每个情节被栅格化并反馈给主进程,主进程将所有的子情节组合成一个网格状的情节。
输出是一个完整的matplotlib图——您甚至可以将其输入 QT 后端,享受 GUI 的所有功能。但是每个子情节都是光栅化的图像。这意味着你不能放大,改变轴等。然而,如果你只是需要一种快速的方法来创建静态图,那么这种方法应该适合你。

工作原理概述。图片作者。
我在 GitHub 上发布了这个附带项目,但是还没有在 PyPi 上发布。如果只是想用,可以在这里不看了,直接装库。请阅读 GitHub 自述文件了解使用信息。
pip install git+https://github.com/paulgavrikov/parallel-matplotlib-grid/
但是如果你好奇的话,我也会用一个稍微精简的功能来解释这个原理。让我们深入研究一下。
首先,我们必须将数据分发给每个工人,然后汇集输出。我们可以通过使用池让多处理来处理这个问题。组装意味着我们只需创建支线剧情,并让每个情节通过plt.imshow显示光栅化的输出。
现在,是时候定义工人了。工人必须创建一个绘图,调用一个用户定义的函数,以任何方式将数据绘制到图形中,然后进行栅格化并返回输出。
matplotlib不太擅长在保存前计算每个图的边界。要么接受可能遗漏了一点情节的支线剧情,要么使用填充(默认完成)。
现在你可以用你的数据和你的自定义绘图程序调用parallel_plot,享受准并行绘图。
让我们创建一个 5x5 的小提琴网格:

5x5 小提琴绘图网格。图片作者。
或者一行 5 个散点图:

1x5 散点图网格。图片作者。
你可以创造任何你喜欢的情节,只要你的情节基于提供的fig和axes参数。只要记住multiprocessing不能很好地处理超过 4gb 的数据。
一点小提示。我很清楚,当谈到真正的并行性时,多处理并不是精英中的精英。但它在大多数情况下都足够好,我的主要用途是jupyter笔记本,我发现多处理是少数几个可靠工作的库之一。如果您想在jupyter之外使用它,并且想与工人共享更大的数据,您可能想考虑使用ray。
ray不使用pickle来序列化数据。因此,序列化速度更快,可以序列化大于 4gb 的数据。访问数据也更快,因为没有昂贵的反序列化。但是我也遇到了许多占用 RAM 的死进程的问题,并且从来没有让它在jupyter笔记本中工作过。
用 Matplotlib 绘制实时数据
如何用 Python 的 Matplotlib 可视化实时数据

CPU 和内存可视化—图片由作者提供
无论您是使用传感器、不断从 API 获取数据,还是拥有经常更新的文件,您都可能希望实时分析您的数据。

圆环图时钟—图片由作者提供
本文将探索一种简单的方法,通过 Matplotlib 的 FuncAnimation 使用函数为我们的绘图制作动画。
第一个例子的数据来自操作系统,为了检索这个信息,我们将使用 psutil 。
pip install psutil
我们将使用 deques 处理数据,但是您可以修改这个示例,使其适用于大多数集合,比如字典、数据框、列表等等。
在文章的最后,我将快速展示另一个关于熊猫和地质熊猫的例子。
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.animation import FuncAnimation
import psutil
import collections
让我们从收集和存储数据开始。我们将定义两个填充零的 deques。
cpu = collections.deque(np.zeros(10))
ram = collections.deque(np.zeros(10))print("CPU: {}".format(cpu))
print("Memory: {}".format(ram))

带零的德克-图片由作者提供
然后我们将创建一个用新数据更新 deques 的函数。它将删除每个队列的最后一个值,并追加一个新值。
def my_function():
cpu.popleft()
cpu.append(psutil.cpu_percent(interval=1)) ram.popleft()
ram.append(psutil.virtual_memory().percent)cpu = collections.deque(np.zeros(10))
ram = collections.deque(np.zeros(10))# test
my_function()
my_function()
my_function()print("CPU: {}".format(cpu))
print("Memory: {}".format(ram))

Deques 提供一些数据—图片由作者提供
现在我们可以定义人物和支线剧情了。除了更新 deques,我们的函数还需要将这些数据添加到图表中。
# function to update the data
def my_function():
cpu.popleft()
cpu.append(psutil.cpu_percent(interval=1))
**ax.plot(cpu)** ram.popleft()
ram.append(psutil.virtual_memory().percent)
**ax1.plot(ram)**# start collections with zeros
cpu = collections.deque(np.zeros(10))
ram = collections.deque(np.zeros(10))**# define and adjust figure
fig = plt.figure(figsize=(12,6), facecolor='#DEDEDE')****ax = plt.subplot(121)
ax1 = plt.subplot(122)****ax.set_facecolor('#DEDEDE')
ax1.set_facecolor('#DEDEDE')****# test
my_function()**
**plt.show()**

采用单一指标的 CPU 和内存折线图—图片由作者提供
当我们添加 FuncAnimation 时,它会反复调用我们的函数来刷新图表。
但是当我们在同一轴上多次使用.plot()时,它不会更新直线,而是绘制新的直线。

CPU 和内存折线图,每个指标对应一条线—图片由作者提供
很难追加到已经绘制的线条上,其他类型的可视化,如地图、条形图和饼图,更新起来更具挑战性。
一个更直接的解决方案是在绘图前清除轴,并在每次迭代时绘制一个新的图。
将axis.cla()添加到我们的函数中会得到那个结果。
这样,我们也不需要担心文本、注释或图表的其他元素。每次我们绘图,轴都会重置,函数会重新绘制所有内容。
# function to update the data
def my_function():
# get data
cpu.popleft()
cpu.append(psutil.cpu_percent(interval=1))
ram.popleft()
ram.append(psutil.virtual_memory().percent) # clear axis
ax.cla()
ax1.cla() # plot cpu
ax.plot(cpu)
ax.scatter(len(cpu)-1, cpu[-1])
ax.text(len(cpu)-1, cpu[-1]+2, "{}%".format(cpu[-1]))
ax.set_ylim(0,100) # plot memory
ax1.plot(ram)
ax1.scatter(len(ram)-1, ram[-1])
ax1.text(len(ram)-1, ram[-1]+2, "{}%".format(ram[-1]))
ax1.set_ylim(0,100)# start collections with zeros
cpu = collections.deque(np.zeros(10))
ram = collections.deque(np.zeros(10))# define and adjust figure
fig = plt.figure(figsize=(12,6), facecolor='#DEDEDE')
ax = plt.subplot(121)
ax1 = plt.subplot(122)
ax.set_facecolor('#DEDEDE')
ax1.set_facecolor('#DEDEDE')# test
my_function()
my_function()
my_function()
plt.show()

采用多种衡量标准的 CPU 和内存折线图—图片由作者提供
我们拥有让我们的图表栩栩如生所需的一切。
现在我们可以添加 FuncAnimation,并将我们的图形和函数作为参数传递。
ani = FuncAnimation(fig, my_function, interval=1000)
当我们定义这个数字时,我们将它赋给一个名为fig的变量。
另一种方法是跳过这一步,依赖 Matplotlib 创建的默认图形。替换第一个参数为plt.gcf(),它会自动为我们得到当前的数字。
请注意,我们还在 FuncAnimation 中设置了 1,000 毫秒的间隔。我们不需要把它放在。默认情况下,它会每隔 200 毫秒调用一次函数。
既然我们已经有了一个间隔,我们也可以从.cpu_percent()中删除这个间隔。
不涉及太多关于 psutil 的细节,当我们不设置间隔时,它将从最后一次调用函数开始计算——这意味着它返回的第一个值将是零。在那之后,它会像以前一样工作。
最后,FuncAnimation 将调用我们的函数,并使用帧作为第一个参数。因为我们不会为这个参数定义任何自定义值,所以它将作为一个计数器工作,传递迭代次数。
我们唯一要做的就是确保我们的函数可以接收这个参数,即使我们没有使用它。
# function to update the data
def my_function(**i**):
# get data
cpu.popleft()
cpu.append(psutil.cpu_percent())
ram.popleft()
ram.append(psutil.virtual_memory().percent) # clear axis
ax.cla()
ax1.cla() # plot cpu
ax.plot(cpu)
ax.scatter(len(cpu)-1, cpu[-1])
ax.text(len(cpu)-1, cpu[-1]+2, "{}%".format(cpu[-1]))
ax.set_ylim(0,100) # plot memory
ax1.plot(ram)
ax1.scatter(len(ram)-1, ram[-1])
ax1.text(len(ram)-1, ram[-1]+2, "{}%".format(ram[-1]))
ax1.set_ylim(0,100)# start collections with zeros
cpu = collections.deque(np.zeros(10))
ram = collections.deque(np.zeros(10))# define and adjust figure
fig = plt.figure(figsize=(12,6), facecolor='#DEDEDE')
ax = plt.subplot(121)
ax1 = plt.subplot(122)
ax.set_facecolor('#DEDEDE')
ax1.set_facecolor('#DEDEDE')**# animate
ani = FuncAnimation(fig, my_function, interval=1000)**plt.show()

CPU 和内存可视化—图片由作者提供
在下一个示例中,我们将使用 Pandas 和 Openpyxl 读取 Excel 文件,使用 Geopandas 绘制地图。当我们更改 Excel 文件中的某些内容并保存它时,绘图应该会反映出差异。
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.animation import FuncAnimation
import pandas as pd
import geopandas as gpd# function to update the data
def my_function(i):
# get data
try:
df = pd.read_excel('w.xlsx', engine='openpyxl')
except:
print('error reading file') # merge with world map
world_values = world.merge(df, how='right',
on='name', copy=True) # clear axis
ax.cla()
ax1.cla() # plot map
world_values.plot(column='Val', ax=ax1,
cmap='coolwarm_r', edgecolors='black',
linewidths=0.5, alpha=0.8) # remove spines and ticks
ax1.spines['left'].set_visible(False)
ax1.spines['right'].set_visible(False)
ax1.spines['top'].set_visible(False)
ax1.spines['bottom'].set_visible(False) ax1.set_yticks([])
ax1.set_xticks([]) # get records with a value higher than 0.97
alert = world_values[world_values['Val'] > 0.97]
alert = alert.sort_values('Val', ascending=False) # plot bars
ax.bar(alert.name, alert.Val, color='#E9493C', alpha=0.8) # limits, labels, and spines
ax.set_ylim(0.9, 1)
ax.set_xticklabels(alert.name, fontsize=11)
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)# world map
world = gpd.read_file(gpd.datasets.get_path('naturalearth_lowres'))# define and adjust figure
fig, (ax, ax1) = plt.subplots(2, 1, figsize=(16,12), facecolor='#707576', gridspec_kw={'height_ratios': [1, 3]})ax.set_facecolor('#707576')
ax1.set_facecolor('#707576')# animate
ani = FuncAnimation(fig, my_function, interval=500)fig.tight_layout()
plt.show()

地图可视化和 Excel 文件—图片由作者提供
用 Matplotlib 制作的动画可能看起来很吓人,但是它们相对来说很简单。
不同的数据源和可视化可能会带来独特的挑战,但总体而言,流程是相同的。您定义了一个函数来系统地更新您的图表,并使用 FuncAnimation 重复执行该操作。
你可以在这里找到这篇文章的代码。
我还在这个库中添加了另一个用 Matplotlib 绘制时钟的例子。

圆环图时钟—图片由作者提供
感谢阅读我的文章。我希望你喜欢它。
用 GeoPandas 绘制地图
地理空间数据绘图初学者指南

托姆·米尔科维奇在 Unsplash 上拍摄的照片
最近,在我的数据科学项目中,我们接到一个项目,需要创建一个模型来预测房价。我们收到的训练数据集包括与平方英尺、浴室数量、建筑等级等相关的财产数据。我最感兴趣的开始分析的数据是位置数据。我们得到了包含纬度和经度坐标的列,我立即想做的第一件事是创建一个热图,显示房地产价格的分布。
让我们来看看我们的数据:
**import** pandas **as** pd
**import** numpy **as** np
**import** geopandas **as** gpd
**import** matplotlib.pyplot **as** plt
**from** shapely.geometry **import** Point, Polygondf = pd.read_csv('kc_house_data_train.csv')
df = df[['id', 'price', 'sqft_living', 'waterfront', 'zipcode', 'long', 'lat']]
df.head()

KC 房地产数据,图片由作者提供
形状文件
在我们绘制这些坐标之前,我们需要一个“shapefile”来绘制它们。如果你和我一样,是绘制地理空间数据的新手,“shapefiles”是一个非常陌生的话题。幸运的是,我找到了这个精彩的描述:
基本上,像 Python 这样利用 Geopandas 的编码语言可以读取 shapefiles 并将它们转换成您可以在其上绘图的功能地图。令我惊讶的是,shapefiles 很容易从很多开源数据库在线获得。形状文件被指定为。shp’—形状格式/几何文件;但是它们也依赖于相关的。shx' —形状索引格式,以及'.‘DBF’—属性格式文件正常工作。所以,为了利用。shp '文件,您还必须将其他两个强制文件扩展名保存在同一目录中。让我们看看 Python 中的 shapefile。
kings_county_map = gpd.read_file('kc_tract_10.shp')
kings_county_map.plot()

作者图片
现在我们有了数据集位置的轮廓(华盛顿州金县。)您可能会注意到,我们的轴不是指纬度和经度坐标。这可以使用坐标参考系统或 CRS 进行调整。最常用的 CRS 是 WGS84 横向/纵向投影。
kings_county_map.to_crs(epsg=4326).plot()

作者图片
地理数据库数据框架
现在我们已经将 shapefile 映射到了正确的坐标,我们可以开始格式化我们的房地产数据了。为了查看数据帧中的属性所在的位置,我们首先需要将数据重新格式化为“GeoPandas 数据帧”
crs = {'init':'EPSG:4326'}
geometry = [Point(xy) for xy in zip(df['long'], df['lat'])]
geo_df = gpd.GeoDataFrame(df,
crs = crs,
geometry = geometry)
为了匹配“shapefile ”,我们需要指定相同的 CRS。接下来,我们需要利用 Shapely 将我们的纬度和经度数据转换成几何点。最后,将带有“crs”和“geometry”变量的原始数据框传递给 GeoDataFrame 函数将创建“geo_df ”,它是原始数据框的副本,但带有新创建的“geometry”列。
geo_df.head()

GeoPandas 数据框架,图片由作者提供
形象化
现在,我们的 shapefile 和 GeoPandas 数据帧都已正确格式化,我们可以开始可视化房地产数据的有趣部分了!让我们在 shapefile 地图上绘制我们的属性数据的位置。通过利用 Matplotlib 的子图,我们可以在同一轴上绘制两个数据集,并查看属性的位置。
fig, ax = plt.subplots(figsize = (10,10))
kings_county_map.to_crs(epsg=4326).plot(ax=ax, color='lightgrey')
geo_df.plot(ax=ax)
ax.set_title('Kings County Real Estate')

作者图片
我们可以看到,金县的大部分地产位于该县的西部。尽管如此,由于点的密度很高,很难判断是在北边还是南边有更多的房子。
fig, ax = plt.subplots(figsize = (10,10))
kings_county_map.to_crs(epsg=4326).plot(ax=ax, color='lightgrey')
geo_df.plot(ax=ax, alpha = .1 )
ax.set_title('Kings County Real Estate')
plt.savefig('Property Map')

作者图片
通过减少 GeoPandas 地块中的 alpha,我们现在可以看到位于该县西北部的房屋更多,因为蓝色地块与南部相比密度更大。
这很好,我们可以看到房产的位置,但在决定合适的房子时,最重要的因素是什么?价格。我们可以使用相同的绘图,但是我们可以在数据框架中指定价格列来创建热图!(注意, w e 对数转换 我们的价格数据,以减少异常值如何扭曲我们的数据。)
geo_df['price_log'] = np.log(geo_df['price'])fig, ax = plt.subplots(figsize = (10,10))
kings_county_map.to_crs(epsg=4326).plot(ax=ax, color='lightgrey')
geo_df.plot(column = 'price_log', ax=ax, cmap = 'rainbow',
legend = True, legend_kwds={'shrink': 0.3},
markersize = 10)
ax.set_title('Kings County Price Heatmap')
plt.savefig('Heat Map')

作者图片
最后,我们现在可以很容易地看到价格较高的房屋位于国王县!这是有道理的,因为西北部的红色圆圈包围了西雅图和麦地那,这是比尔盖茨和杰夫·贝索斯的家。)如果我们想看同样的图,但对于平方英尺的物业,我们可以:
geo_df['sqft_log'] = np.log(geo_df['sqft_living'])fig, ax = plt.subplots(figsize = (10,10))
kings_county_map.to_crs(epsg=4326).plot(ax=ax, color='lightgrey')
geo_df.plot(column = 'sqft_log', ax=ax, cmap = 'winter',
legend = True, legend_kwds={'shrink': 0.3},
alpha = .5)
ax.set_title('Sqft Heatmap')

作者图片
我们现在可以看到,随着我们远离西雅图,经度-122.3 以东的房子往往有更多的平方英尺。
结论
我希望这篇快速教程有助于理解如何使用 GeoPandas,以及为什么它是如此强大的工具。在任何探索性数据分析过程中,使用 GeoPandas 都是可视化地理空间数据的好方法。它提供了一种对数据集进行推断的方法,最重要的是,它让每个人都可以看到数据。
来源
- https://geopandas.org/index.html
- https://en.wikipedia.org/wiki/Shapefile
- 【https://www.kingcounty.gov/services/gis/GISData.aspx
- https://onlinestatbook.com/2/transformations/log.html
- https://matplotlib . org/stable/API/_ as _ gen/matplotlib . py plot . plot . html
- https://www . CNBC . com/2019/06/25/Medina-wash-home-to-Jeff-be zos-and-bill-gates-running-out-of-money . html
用一行代码绘制学习曲线
查看您的模型从添加更多训练数据中获益多少

科林·卡特在 Unsplash 上的照片
学习曲线 是任何数据科学家工具箱中的另一个伟大工具。这是一种可视化技术,可以看到我们的模型从添加更多的训练数据中受益多少。它显示了具有不同数量的训练样本的机器学习模型的训练分数和测试分数之间的关系。通常,交叉验证程序在绘制学习曲线时生效。
一个好的 ML 模型非常适合训练数据,也可以推广到新的输入数据。有时,ML 模型可能需要更多的训练实例,以便推广到新的输入数据。添加更多的训练数据有时会有利于模型进行概括,但并不总是如此!我们可以通过查看它的学习曲线来决定是否添加更多的训练数据,以建立一个更具普遍性的模型。
绘制学习曲线通常需要编写许多行代码,并且消耗更多的时间。但是,多亏了 Python Yellowbrick 库,现在事情就简单多了!通过正确使用它,我们可以只用一行代码就绘制出学习曲线!在本文中,我们将讨论如何用 Yellowbrick 绘制学习曲线,并学习如何解读它。
先决条件
为了充分利用今天的内容,建议阅读我的 k-fold 交叉验证浅显易懂的 文章的“使用 k-fold 交叉验证评估模型的性能”部分。
除此之外,最好了解支持向量机和随机森林算法。这是因为,今天,我们根据这些算法绘制学习曲线。如果你不熟悉它们,就看看我写的以下内容。
安装黄砖
Yellowbrick 没有默认的 Anaconda 安装程序。您需要手动安装它。要安装它,打开 Anaconda 提示符并运行下面的命令。
pip install yellowbrick
如果那对你不起作用,用 用户 标签试试下面的。
pip install yellowbrick --user
或者你也可以用康达锻造频道试试。
conda install -c conda-forge yellowbrick
或者用 DistrictDataLabs 频道试试。
conda install -c districtdatalabs yellowbrick
以上任何一种方法都会安装最新版本的 Yellowbrick。
绘制学习曲线
现在,考虑以下示例代码,其中我们使用 Scikit-learn 内置的乳腺癌数据集绘制了一个 SVM 和一个随机森林分类器的学习曲线。该数据集有 30 个特征和 569 个训练样本。让我们看看添加更多数据将有利于 SVM 和随机森林模型推广到新的输入数据。
学习曲线— SVM
等到加载 Python 代码!

(图片由作者提供)
学习曲线—随机森林分类器
等到加载 Python 代码!

(图片由作者提供)
解释

(图片由作者提供)
在上图中,训练集的准确率标记为“训练分”,测试集的准确率标记为“交叉验证分”。直到大约 175 个训练实例,SVC(支持向量分类器)模型的训练分数(左图)远大于测试分数。因此,如果当前数据集的训练实例远少于 175 个(例如,大约 100 个),则添加更多的训练实例将提高泛化能力。但是,在 175 水平之后,该模型可能不会从添加更多的训练数据中受益。对于随机森林分类器(右图),我们可以看到训练和测试分数尚未收敛,因此该模型可能会受益于添加更多的训练数据(例如,大约 700–1000 个训练实例)。
它是如何工作的!
当我们执行 learning_curve() 函数时,很多工作都是在幕后进行的。我们只需要运行一行代码来绘制学习曲线。这就是黄砖的力量! learning_curve() 函数的第一个参数应该是 Scikit-learn 估计器(这里是 SVM 或随机森林分类器)。第二个和第三个应该是 X (特征矩阵)和 y (目标向量)。 "cv" 定义交叉验证的折叠数。标准值是 3、5 和 10(这里是 10)。评分参数包含模型的评分方法。在分类上,首选【准确性】****【roc _ AUC】。回归中常用【R2】****【负均方误差】。除此之外,还有许多评估指标。你可以通过访问这个链接找到他们。
当我们执行 learning_curve() 函数时,交叉验证过程在后台发生。正因为如此,我们才输入 X 和 y 。我们不需要把数据集拆分为 X_train 、 y_train 、 X_test 、 y_test 。在交叉验证中,根据 cv 中指定的折叠数在内部进行分割。在这里使用交叉验证可以保证模型的准确性分数不会受到随机数据分割过程的太大影响。如果你只是使用 train_test_split() 函数而没有 t 交叉验证,那么根据你在 train_test_split() 函数内提供的 random_state ,准确率会有很大差异。在交叉验证中,使用 10 次(cv=10)这样迭代的平均值来计算精确度!
关键要点
学习曲线是一个很好的工具,你应该把它放在你的机器学习工具包里。它可用于查看您的模型从添加更多训练数据中获益多少。有时,添加更多数据将有利于模型推广到新的输入数据。通常,交叉验证程序在绘制学习曲线时生效,以避免随机数据分割过程的影响。
学习曲线不应与验证曲线混淆,后者用于绘制单个超参数的影响。两条曲线的功能完全不同。如果你有兴趣了解更多关于验证曲线的知识,你可以阅读我的验证曲线讲解——绘制单个超参数的文章。****
感谢阅读!
本教程由Rukshan Pramoditha,数据科学 365 博客作者设计创作。**
在 https://rukshanpramoditha.medium.com 阅读我的其他文章
2021–04–26
你还在使用 Virtualenv 管理 Python 项目中的依赖关系吗?
有一种更好的方法来管理依赖项、打包和发布 Python 项目。

惊讶地看到你仍然使用虚拟环境而不是诗歌——来自 Pexels 的 Andrea Piacquadio 摄影
当我开始我的数据科学生涯时,我被项目的复杂性吓住了。我们在所有的 python 项目中都使用了 Virutalenv。
我对节点包管理器(npm)印象深刻,并且一直想知道为什么我们在 Python 中没有一个这样的管理器。
我渴望有一个工具来维护隔离的环境、管理开发和生产依赖关系、打包和发布。
谢天谢地,我们现在有了诗。
简单来说,诗歌就是 Python 中依赖管理和打包的工具。但是这个官方定义是不完整的,因为我发现诗歌不仅仅是管理依赖性和打包。
在本指南中,您可以找到,
诗歌不是虚拟环境的替代品。它为他们提供了管理环境的智能方法等。
以下是我对诗歌一见钟情的原因。
与 Virtualenv 不同,我可以用诗歌重命名和重新定位我的项目。
虚拟环境与特定路径相关联。如果我移动或重命名项目文件夹,原始路径不会随之改变。
如果我想做,我会有很大的麻烦。
每次更改路径时,我都会创建一个新的虚拟环境,并再次安装软件包。
很辛苦。
Virtualenv 有一个— -relocatable标志来帮助它。但是这个选项我也不满意。每次我安装一个新的包,我必须标记环境— -relocatable
这是恼人的重复!我相信数据科学家和开发人员有比记住每次都运行这个更大的问题。
诗歌项目是可重定位的。
诗歌将虚拟人与项目隔离开来。它会自动在 HOME 目录下的.cache文件夹中创建一个 env。当我重新定位项目时,我可以告诉 poems 在一个命令中使用相同的 env。
poetry env use <your env location>
如果您喜欢将 env 放在自定义位置,可以用同样的方式指定路径。这样你就可以把它和外部环境联系起来。
我发现它对测试非常有用。如果我的代码需要兼容不同的 Python 版本,我可以随时更换解释器。
poetry env use python3.8
poetry env use python3.6
在诗歌中,我可以单独管理开发依赖。
这是虚拟环境的一个明显的缺点。
Virtualenv 在隔离的环境中管理依赖关系。但是他们并没有专门为开发而维护一套。
然而,诸如 black、flake8 和 isort 之类的 Python 包只在开发时需要。它们在生产服务器中没有任何用途。
我还必须格外小心生产服务器上开发包的 安全漏洞。
我通常维护两个 requirements.txt 文件来区分它们。但这种做法是非常无效的。我可以使用 pip freeze 来更新开发版本。但对于制作版,我必须手动编辑。
这足以让你沮丧地度过一整天。
另一方面,诗歌有智能的方法来管理项目依赖性。
当向项目中添加一个新包时,我可以使用-D 标志指定它是否仅用于开发。
poetry add -D black
当我在生产服务器上安装依赖项时,我可以使用no-dev标志来过滤掉开发依赖项。
poetry install --no-dev
我也可以用remove-untracked标志删除我过去使用的多余包。
poetry install --remove-untracked
管理 Python 项目的依赖关系并不容易。
诗歌确保团队成员之间版本的一致性。
如果你是团队工作,你会因为不一致而遇到问题。
人们使用不同版本的依赖关系。因此,代码要么中断,要么没有给出预期的结果。
当然啦!pip freeze 命令可以捕获软件包的版本。但是,他们没有获得 Python 解释器版本。此外,如果您手动将一个包添加到需求文件中,并且没有指定版本,这将会产生不一致。
诗歌有保持一致性的巧妙方法。
pyproject.toml文件相当于 virtualenv 中的 requirement.txt。但是当诗歌安装包时,它首先检查是否有一个poetry.lock文件可用。如果是这样,它将从锁文件中获取依赖关系。
您不需要手动编辑锁定文件。每次您更改项目依赖项时,poems 都会创建并更新它。您可以使用 poems add 命令,也可以在 TOML 文件上指定依赖项并运行 install 命令。
poetry add pandas
诗歌文档鼓励您将锁文件提交到代码库中,并与其他成员共享。因此,当他们建立依赖关系时,总是与他人保持同步。
打包和发布 python 包。
如果您将包发布到 PyPI 或其他存储库,您必须以有助于索引的方式构建它们。对我来说,这不是一项容易的任务。
这涉及到许多配置,它们肯定会阻碍新作者。
然而,有了诗歌,我可以毫不费力地将包发布到任何存储库中。
这是我用诗歌向 PyPI 发布的的包。做这件事不超过几分钟。
这个包帮助您在一个终端命令中为任何数据集生成 HTML 分析报告。
猜猜看,这是 pip installable:
pip install python-eda
您可以在这个 GitHub 资源库中找到源代码。另外,如果你喜欢这个包,你可以看看我写的关于它的文章。
在结束之前,我想带你看一下我发布这个包的具体步骤。
让我们创建并发布一个带有诗歌的 Python 项目。
与 Virtuelenvs 不同,在 Virtuelenvs 中创建项目文件夹,然后创建 env,我可以直接创建诗歌项目。诗歌自动把一个项目结构和初始文件。
poetry init python-eda
cd python-eda/
下一步,我用-D 标志安装了项目的核心依赖项和开发依赖项。
poetry add pandas sweetviz typer -D black flake8 isort pre-commit
我不打算解释我如何使用开发依赖来保持这篇文章的简洁。但是您可以找到无数关于如何使用这些包来维护干净代码的资源。
然后我在 python_eda 文件夹中添加了一个名为main.py.的文件,我用我之前的文章中的代码替换了它的内容。这是我的应用程序中所有内容的入口点。
至此,一切都是普通的 Python 应用。
现在,我们在pyproject.toml文件中添加一小段代码来讲诗,这是你的切入点。
[tool.poetry.scripts]
pyeda = "python_eda.main:app"
我们在这里做什么?我们在 python_eda 文件夹中的 main.py 中调用该应用程序。
现在,只需一个命令,您就可以构建应用程序。
poetry build
这将在你的项目中创建一个dist文件夹,包含你的项目的车轮和焦油文件。
要在本地测试项目,您可以运行poetry install,并且您将能够使用 CLI 来生成 EDA 报告。
$ poetry install
$ pyeda report https://raw.githubusercontent.com/ThuwarakeshM/analysis-in-the-blink-of-an-eye/main/titanic.csv
要将您的包发布到 PyPI,您需要一个帐户并创建一个 API 令牌。你可以从官方文件中找到更多信息。
一旦有了 API 令牌,您只需要多两行命令。
$ poetry config pypi-token.pypi <TOKEN>
$ poetry publish
厉害!应用程序已发布。
现在,python-eda 可以通过 pip 进行安装。
总而言之,
多年来,我在每个项目上都使用 Virtualenv。使用它们的唯一优势是隔离的环境和列出项目依赖关系。
但即使在那时,使用它也有几个问题,例如
- 难以区分开发和生产依赖关系;
- 无法重新定位或重命名项目文件夹;
- 难以在团队之间保持一致的环境,以及。
- 打包和发布时有许多样板文件。
诗歌是所有这些问题的一站式解决方案。它满足了我长期以来对一个类似 npm 的 Python 包管理器的渴望。
感谢阅读,朋友!在 上跟我打招呼 LinkedIn ,Twitter, 中 。看来你和我有许多共同的兴趣。
还不是中等会员?请使用此链接 成为会员 因为,在没有额外费用的情况下,我为你引荐赚取一小笔佣金。
火车、公共汽车还是飞机?使用访问和流行度度量对兴趣点进行预测分类
使用地理空间要素对公交车站、火车站或机场等位置进行分类

图片由安妮·斯普拉特和昂斯普拉什拍摄
本文旨在展示如何使用来自 SafeGraph 的适用真实世界数据集,通过监督机器学习进行基本分类。SafeGraph 是一家数据提供商,为数百家企业和类别提供 POI 数据。它向的学者免费提供数据。对于这个项目,我选择使用 SafeGraph 模式数据,以便将记录分类为各种 POI。模式数据的模式可以在这里找到:模式信息
从商店里众多可供选择的类别中,这个项目着重于不同运输服务的分类。为此项目选择的类别有:
- 公共汽车和其他车辆运输系统
- 铁路运输
- 其他机场运营
这些类别中的每一个都与火车站、汽车站和机场数据的 POI 数据相关联。
本文是使用这些数据的三部分系列文章的第一部分:
- 使用 safegraph 数据和 sklearn 分类器进行 POI 分类
- 使用 PCA 和交叉验证的模型调整
- 使用 spark 深度学习分类器的兴趣点分类
- 单一 NAICS 代码内的 POI 分类(快餐店分类)

图片来自 Markus Spiske 和 Unsplash
下面是为这个项目编写的代码的快照和代码过程的简要描述。这个项目代码的链接可以在这里找到:笔记本链接
第 1 部分:依赖关系
以下是该项目的依赖项。我们这个项目需要的包是 P 和 as , NumPy , Seaborn ,M atplotlib ,P yspark
import pandas as pdimport numpy as npimport seaborn as snsimport matplotlib.pyplot as pltxfrom sklearn.metrics import plot_confusion_matrix
— —
!apt-get install openjdk-8-jdk-headless -qq > /dev/null!wget -q [https://www-us.apache.org/dist/spark/spark-3.1.2/spark-3.1.2-bin-hadoop2.7.tgz](https://www-us.apache.org/dist/spark/spark-3.1.2/spark-3.1.2-bin-hadoop2.7.tgz)!tar xf spark-3.1.2-bin-hadoop2.7.tgz!pip install -q findspark!pip install pyspark
— —
import findsparkfindspark.init(“/content/spark-3.1.2-bin-hadoop2.7”)from pyspark.sql import SparkSessionspark = SparkSession.builder.master(“local[*]”).getOrCreate()
第二部分:数据加载
from pydrive.auth import GoogleAuthfrom pydrive.drive import GoogleDrivefrom google.colab import authfrom oauth2client.client import GoogleCredentials auth.authenticate_user()gauth = GoogleAuth()gauth.credentials = GoogleCredentials.get_application_default()drive = GoogleDrive(gauth)def pd_read_csv_drive(id, drive, dtype=None): downloaded = drive.CreateFile({‘id’:id}) downloaded.GetContentFile(‘Filename.csv’) return(pd.read_csv(‘Filename.csv’,dtype=dtype))def get_drive_id(filename): drive_ids = {‘patterns’ : ‘1ReqpLgv50_3mCvZuKLlMdHxwfCPIHmqz’} return(drive_ids[filename])transportation_df = pd_read_csv_drive(get_drive_id(‘patterns’), drive=drive)transportation_df.head(3)
输出如下所示(点击图片查看详情):

下表是 2018 年 12 月上述三个兴趣点类别的模式数据。选择这个特殊的月份是因为公共交通在假日季节更有可能被利用——从而为每个记录显示更清晰的受欢迎程度和游客指标。
我们将用作分类模型特征的一些列是:
- raw_visit_counts — 在日期范围内,我们的面板中对此兴趣点的访问次数。
- raw_visitor_counts — 在日期范围内,来自我们面板的访问该兴趣点的独立访问者的数量。
- 每日访问次数— 在所覆盖的时间段内,每天(当地时间)访问兴趣点的次数。
- distance_from_home — 访客(我们已确定其住所的访客)离家的中间距离,以米为单位。
- 中值停留时间— 中值最小停留时间,以分钟为单位。
- bucked _ dwell _ times—关键字是分钟范围,值是在该持续时间内的访问次数
- popularity_by_hour — 将一天中的某个小时映射到当地时间日期范围内每小时的访问量。数组中的第一个元素对应于从午夜到凌晨 1 点的时间
- popularity_by_day — 将一周中的某一天映射到日期范围内每天(当地时间)的访问次数
- device_type — 使用 android 和 ios 访问兴趣点的人数。仅显示至少包含 2 个设备的设备类型,包含少于 5 个设备的任何类别都报告为 4
第 3 节:数据清理
第一步是删除不必要的列。
transportation_df = transportation_df.drop([‘parent_safegraph_place_id’,’placekey’,’safegraph_place_id’,’parent_placekey’,’parent_placekey’,’safegraph_brand_ids’,’brands’, ‘poi_cbg’], axis = 1)transportation_df.head(3)

这里我们将parent _ safe graph _ place _ id、 placekey 、 parent_placekey 、 safegraph_brand_ids 、 brands 、 poi_cbg 。这些列与标识符、品牌、人口普查区块组和位置键相关。这些列与这个特定项目的范围无关,因此我们将它们删除。
创建基础事实类列(帮助函数)
def class_definer(record): fixed_record = record.lower() if(‘bus’ in fixed_record): return ‘Bus’ elif(‘airport’ in fixed_record): return ‘Airport’ elif(‘train’ in fixed_record): return ‘Train’ elif(‘metro’ in fixed_record): return ‘Train’ elif(‘transport’ in fixed_record): return ‘Bus’ elif(‘amtrak’ in fixed_record): return ‘Train’ elif(‘bart’ in fixed_record): return ‘Train’ elif(‘cta’ in fixed_record): return ‘Train’ elif(‘mta’ in fixed_record): return ‘Train’ elif(‘transit’ in fixed_record): return ‘Bus’ elif(‘mbta’ in fixed_record): return ‘Train’ elif(‘station’ in fixed_record): return ‘Train’ elif(‘railway’ in fixed_record): return ‘Train’ else: return ‘Unknown’transportation_df['Class'] = transportation_df['location_name'].transform(lambda x: class_definer(x))transportation_df.head(3)

这些代码片段试图创建一个类列来建立监督学习算法(如分类)所需的基础事实。用于此项目的数据带有一些警告,尤其是公交车站数据。NAICS 公交车站类别包括“公交车站和其他交通服务”。该类别尤其包含多个项目,如公交车站、卡车租赁和游艇服务。因此,为了删除不相关的记录,使用了上面的函数。
transportation_df = transportation_df[transportation_df[‘Class’] != ‘Unknown’].dropna()transportation_df.head(3)

这些未知的列代表了汽车运输记录,这些记录与模式数据中的公共汽车数据一起出现。我们可以放弃这些记录,因为它们并不完全符合公共汽车服务的概念——更倾向于卡车服务、游艇租赁和其他远离公共汽车服务概念的交通服务。
现在,数据被正确格式化,并且有一个可用于分类算法的类列。下一步是格式化数据帧,使所有的特征都能用于分类算法。这个过程需要水平扩展列,例如按天访问列和按小时流行度列。这些需要水平展开的列中,有些是数组的列,有些是 JSON 的列。我们将首先研究 JSON 列的水平扩展:
为此,我们将使用 pyspark,特别是 from_json 表达式来水平展开列。为此,我们必须首先将 pandas 数据帧转换为 Spark 数据帧。
transportation_df = spark.createDataFrame(transportation_df)transportation_df.show(2)

既然数据在 Spark 数据框架中,我们需要为需要展开的 JSON 字符串列创建一个模式。这些模式将显示 JSON 字符串展开时产生的唯一列,以及与该列相关联的数据类型,使用该模式我们可以展开 JSON 字符串列。
#Horizontal Explosion of JSON columns using Pysparkfrom pyspark.sql.functions import from_json,exprfrom pyspark.sql.types import StructType, StructField, StringType, ArrayType, IntegerTypeday_schema = StructType( [ StructField(‘Monday’, IntegerType(),True), StructField(‘Tuesday’, IntegerType(),True), StructField(‘Wednesday’, IntegerType(),True), StructField(‘Thursday’, IntegerType(),True), StructField(‘Friday’, IntegerType(),True), StructField(‘Saturday’, IntegerType(),True), StructField(‘Sunday’, IntegerType(),True) ])device_schema = StructType( [ StructField(‘android’, IntegerType(),True), StructField(‘ios’, IntegerType(),True) ])bucketedDT_schema = StructType( [ StructField(‘<5’,IntegerType(),True), StructField(‘5–10’,IntegerType(),True), StructField(‘11–20’,IntegerType(),True), StructField(‘21–60’,IntegerType(),True), StructField(‘61–120’,IntegerType(),True), StructField(‘121–240’,IntegerType(),True), StructField(‘>240’,IntegerType(),True) ])transportation_df = transportation_df.withColumn(‘popularity_by_day’, from_json(‘popularity_by_day’, day_schema)).withColumn(‘device_type’, from_json(‘device_type’, device_schema)).withColumn(‘bucketed_dwell_times’,from_json(‘bucketed_dwell_times’,bucketedDT_schema)).select(‘location_name’,’raw_visit_counts’,’raw_visitor_counts’,’visits_by_day’,‘distance_from_home’,’median_dwell’,‘bucketed_dwell_times.*’,’popularity_by_hour’,’popularity_by_day.*’,‘device_type.*’,’Class’)transportation_df = transportation_df.toPandas()transportation_df.head(3)

现在 JSON 字符串已经展开,我们可以展开数组列了。由于这在 pandas 中很容易做到,我们可以将数据帧转换回 Pandas 数据帧。数组列的扩展要求用数组填充列,而不是像数组一样格式化的字符串列。Safegraph 模式数据使这些列成为字符串而不是数组,因此第一步是将字符串转换为数组。这可以使用 ast 包中的 literal_eval 函数来完成。从这里,我们可以获取位于每个索引中的单个值,并将它们分解到单独的列中。
from ast import literal_evaltransportation_df[‘popularity_by_hour’] = transportation_df[‘popularity_by_hour’].transform(lambda x: literal_eval(x))pops = [‘popularity_’ + str(i) for i in range(1,25)]transportation_df[pops] = pd.DataFrame(transportation_df.popularity_by_hour.to_list(), index=transportation_df.index)transportation_df = transportation_df.drop([‘popularity_by_hour’], axis = 1)transportation_df = transportation_df.reindex()transportation_df.drop(‘visits_by_day’, axis=1, inplace=True)transportation_df.head(3)

我们需要做的数据清理的最后一部分是使用 Sklearn LabelEncoder 函数将类函数转换为分类模型的数值。
from sklearn import preprocessingle = preprocessing.LabelEncoder()le.fit(transportation_df[‘Class’])transportation_df[‘Class’] = le.transform(transportation_df[‘Class’])transportation_df.head(3)

第四节:分类

图像来自翁贝托和 Unsplash
因为有 3 个唯一的类,我们需要使用一个多类分类器。我们将尝试高斯朴素贝叶斯、决策树和 K 近邻分类器。首先,我们必须将数据分成测试集和训练集。
from sklearn.metrics import confusion_matrixfrom sklearn.model_selection import train_test_splitx_cols = []for item in list(transportation_df.columns):if(item != ‘Class’ and item != ‘location_name’):x_cols.append(item)X = transportation_df[x_cols]y = transportation_df[‘Class’]X_train, X_test, y_train, y_test = train_test_split(X, y, random_state = 0)
高斯朴素贝叶斯分类器:
这里有一些关于朴素贝叶斯分类器功能的背景知识:高斯朴素贝叶斯
from sklearn.naive_bayes import GaussianNBgnb = GaussianNB().fit(X_train, y_train)gnb_predictions = gnb.predict(X_test)gnb_predictionsaccuracy = gnb.score(X_test, y_test)print(accuracy)
confusion_matrix(y_test, gnb_predictions)

现在,让我们使用热图来可视化结果
plot_confusion_matrix(gnb, X_test, y_test, normalize=’true’, values_format = ‘.3f’, display_labels=[‘Airport’,’Bus’,’Train’])

黄色表示高发,紫色表示低发。我们的“正确”预测显示在从左上角到右下角的对角线上。
理想情况下,我们会在对角线上看到黄色,在其他地方看到紫色。
这表明高斯朴素贝叶斯在分类这个特定数据集方面做得不太好。这可能是由于分类器单独考虑每个特征,而没有考虑它们的相关性(因此分类器是“幼稚的”)。当要素具有高相关系数时(如该特定数据集,其中许多要素从一列展开并彼此直接相关),这将使分类器难以正常工作。
该模型的精度约为 26%。热点图显示,许多实际上是机场的值被错误分类为火车站。同样可以说,真正的汽车站被错误地归类为火车站。这表明大多数记录实际上被归类为火车站。
决策树:
这里有一些关于决策树分类器功能的背景知识:决策树
from sklearn.tree import DecisionTreeClassifierdtree_model = DecisionTreeClassifier(max_depth = 3).fit(X_train, y_train)dtree_predictions = dtree_model.predict(X_test)dtree_model.score(X_test,y_test)confusion_matrix(y_test, dtree_predictions)

使用热图可视化结果
plot_confusion_matrix(dtree_model, X_test, y_test, normalize=’true’, values_format = ‘.3f’, display_labels=[‘Airport’,’Bus’,’Train’])

由此可见,决策树模型的表现要比朴素贝叶斯好得多,准确率达到 75%。考虑到这是一个多类分类器,这个精度是相当不错的。
分类器似乎在正确分类机场方面做得非常好,准确率约为 91.7%,表明考虑了数据各种特征之间相关性的模型可以很好地对机场数据进行正确分类。公交车站记录就不一样了,因为没有一个真正的公交车站记录被归类为公交车站。火车站数据似乎表现稍好,将大约 55.3%的真实火车站数据分类为火车站。关于这个特定模型的预测,一个有趣的观察是,没有一个记录被归类为公交车站——无论是正确的还是不正确的。这可能是由于与火车站和机场记录相比,汽车站记录的数量较少,以及决策树算法的性质。决策树算法在使用非常平衡的数据时表现非常好,但在不平衡的数据上表现不如我们这里的情况。
K-最近邻:
这里有一些关于 KNN 分类器功能的背景知识: KNN
from sklearn.neighbors import KNeighborsClassifierknn = KNeighborsClassifier(n_neighbors = 22).fit(X_train, y_train)accuracy = knn.score(X_test, y_test)knn_predictions = knn.predict(X_test)confusion_matrix(y_test, knn_predictions)

使用热图可视化结果
plot_confusion_matrix(knn, X_test, y_test, normalize=’true’, values_format = ‘.3f’, display_labels=[‘Airport’,’Bus’,’Train’])

该分类器的准确率约为 68.0%。该分类器在对机场记录进行正确分类方面做得非常好,准确率约为 93.7%。正确分类的公交车站的数量不再是 0,但仍然很低,这也可以归因于不平衡的数据。与决策树模型相比,该分类器在列车分类方面表现稍差,仅将大约 18.3%的真实火车站预测为火车站。
结论:
该项目向我们介绍了基于 SafeGraph 模式访问数据的 POI 类别分类。我们的算法在对机场、火车站和公交车站进行分类方面取得了一些成功,所有这些都基于停留时间、游客离家的距离、一天中每个小时的受欢迎程度以及一周中每一天的受欢迎程度。
该系列的下一篇文章(链接即将发布!)将通过调整我们的模型和进行主成分分析来增加我们的分类器的复杂性和能力。
提问?
我邀请你在 SafeGraph 社区的 #safegraphdata 频道问他们,这是一个面向数据爱好者的免费 Slack 社区。获得支持、共享您的工作或与 GIS 社区中的其他人联系。通过 SafeGraph 社区,学者们可以免费访问美国、英国和加拿大 700 多万家企业的数据。
云中的点云
使用 Azure Batch 和 PDAL 按比例处理激光雷达数据

来自开放数据集 AHN3 的点云样本,用 plas.io 可视化(图由作者提供)
点云存在许多许多的 x,y,z 坐标来描述环境或物体,如建筑物。通过激光技术(LiDAR)获取的点云通常带有额外的测量值和每个坐标的特征。例如,反射强度、返回次数、返回、扫描角度和 RGB 值。换句话说,点云本质上是海量的数据集。在这里,这些大规模数据集的处理通过使用开源点数据抽象库(PDAL)进行演示,并通过 Azure Batch 运行。
用 PDAL 处理
激光雷达数据的处理在这里用开源库 PDAL 完成。通过该库,点云数据可以与许多不同的格式相互转换(例如,las、laz、geotif、geojson、ascii、pgpointcloud、hdf5、numpy、tiledDB、ept 等,以及专有数据格式)。此外,该库支持对数据进行过滤操作,如重投影、分类、过滤、DEM 和网格创建等。PDAL 可以作为一个应用程序独立执行,还有一个 Python PDAL 扩展,这样 PDAL 就可以成为你的 Python 应用程序的一部分。这为将自己的处理逻辑或过滤器合并到激光雷达处理中提供了灵活性。PDAL 扩展使用以 json 格式定义并通过 pdal 实现执行的管道。以下管道示例说明了以下所有依赖于 PDAL 内置功能的步骤:

PDAL 管道示例(作者提供的图片)
-读取输入 las 或 laz 文件,带有设定的参考投影(此处为 EPSG:28992,荷兰的投影坐标系),
-应用 filters.csf(布料模拟过滤器,张等 2016)对地面和非地面点进行分类,
-应用只选择接地回路的滤波器,
-将过滤后的数据写入 las 文件
此管道使用 AHN3 的样本数据集执行,Ahn 3 是荷兰全国激光雷达开放数据集的第三版,位于南林堡地区。下面是使用 plas.io 创建的两个视觉效果,a)原始数据集,b)使用管道创建的数据集,显示地面返回,基于【】张等人 2016 的实现。

来自开放数据集 AHN3 的点云样本,用 plas.io 可视化(图由作者提供)
用天蓝色批秤
由于数据量和应用于数据的算法类型,处理激光雷达数据是计算密集型的。扩展处理可能是一个挑战。 Azure Batch 是在 Azure 中高效运行大规模并行和高性能计算(HPC)批处理作业的服务。Azure Batch 创建并管理计算节点(虚拟机)池,安装要运行的应用程序,并调度要在节点上运行的作业。在这里,应用程序将是处理激光雷达数据的(Python)脚本。Azure Batch 可以通过 Azure 门户、Azure Batch APIs 和 Azure Batch SDKs 进行管理。由于地理空间和地球科学领域的许多开发人员和研究人员都熟悉 Python,这里使用了 Azure Batch Python SDK。
下图描述了一般概念。通过一个本地 Python 客户端,Azure 批处理计算池在一个已经存在的 Azure 资源组中提供,该资源组具有一个存储帐户和一个 Azure 批处理帐户。使用客户端(1)将应用/脚本和数据上传到 blob 存储器。在下图中)。然后,使用提供的 Azure 批处理帐户凭据,将创建一个计算池,并提交任务和作业(2)。通过这样做,计算池将应用和数据拉至计算节点,执行处理,并将结果推回 blob 存储(3)。为了更好地理解这个概念的实现,这篇博客文章附有一个 PDAL 在 Azure Batch 上的工作示例 GitHub repo 。

Azure Batch 的常规设置。1)本地客户端将数据和应用程序文件上传到 Azure Blob 存储,2)使用 Azure Batch 的 Python SDK,本地客户端开始创建 Azure 批处理池,然后可以添加 Azure 批处理作业以在 LiDAR 文件上运行,3) Azure Batch 自动调度作业和任务,执行处理并将输出文件上传到 Azure Blob 存储。(图由作者提供)
为了与 Azure storage 和 Azure Batch 正确通信,客户端环境需要两个 Python 库,它们是 azure-batch 和 azure-storage-blob(这里分别使用 10.0.0 和 12.8.1 版本)。
针对激光雷达数据运行的应用程序/ Python 脚本需要 PDAL 库,建议从 conda-forge 安装。在 Azure Batch Pool 的初始化过程中,首先会下载一个(迷你)conda 环境,以静默模式安装并初始化,这是通过 StartTask.sh 完成的——在上图的步骤 2 中(有关技术解释和示例,请参见 repo )。
创建 Azure 池后,可以提交作业。对于每个输入激光雷达文件,将执行包括 PDAL 管道在内的 Python 脚本,并将输出 las 文件上传到 Azure blob 存储以实现持久性。
根据 LiDAR 文件的大小和数量,以及要运行的管道的复杂性,您可以创建小型(1 个节点)到非常大(> 1000 个节点)的 Azure 批处理池,可以打开也可以不打开自动缩放选项。这只需要重新调整池的规模,Azure Batch 会处理作业调度和管理,因此提供的代码可以用于大型和小型批处理作业,而无需对代码本身进行任何更改。
这里的意见是我的。
参考文献:
Azure Batch 和 PDAL 代码示例:https://github . com/de lange/lidar-Batch-python/tree/master/lidar-Batch-python/src
azure Batch:https://docs . Microsoft . com/en-us/azure/Batch/Batch-technical-overview
Azure 批处理浏览器:https://azure.github.io/BatchExplorer/
用于激光雷达处理的 PDAL:https://pdal.io/
浏览器中点云渲染的 plas io:https://plas . io
https://www.ahn.nl/国家激光雷达开放数据集 AHN: 荷兰
一种基于布料模拟的简易机载激光雷达数据滤波方法。遥感。2016;8(6):501.https://doi.org/10.3390/rs8060501https://www.mdpi.com/2072-4292/8/6/501/htm
实时机器学习中的时间点正确性
为什么防止数据泄露如此困难?

图片 via Silvia /Adobe Stock 在 Zer0 到 5ive 的许可下
数据科学家知道,当他们构建训练集时,他们需要注意数据泄漏,以确保模型只根据正确的数据进行训练。当模型根据真实世界中没有发生的例子进行训练时,就会发生数据泄漏。在时序模型中,数据泄漏通常是由于在给定预测发生后向定型集添加要素而导致的。虽然所有数据科学家都知道数据泄漏是他们需要小心的事情,但实际上建立没有数据泄漏的训练集很少像看起来那样简单,特别是当机器学习模型实时进行预测时。
构建没有数据泄露的训练集实际上是如此具有挑战性,以至于像 Airbnb 、网飞和优步这样的技术领导者需要建立一个特征商店,以便持续地构建准确的训练集。但是,并不是所有的要素存储都能真正解决数据科学家的时间点正确性问题。
时间点正确性问题
当要素生成、预测和标注生成发生在不同的时间点时,数据泄漏很容易引入到训练集中。这通常被称为时间点正确性问题。这个问题在实时机器学习应用程序中是如何出现的?
想象一下你有一个做产品推荐的电商网站。这种模式的特点可能包括:
- RFM 指标,如用户在上周、上个月或上一年购买的产品总数,每周计算一次
- 实时更新的用户购物车中当前商品的摘要
这个模型的标签可能是:推荐的产品实际上是在同一个 web 会话中购买的吗?
在训练这样一个模型时,一个微妙但重要的复杂因素是对存在的许多不同时间戳的争论。
对于这个机器学习问题,我们需要跟踪 4 个时间戳:
- 当每周批量 RFM 特征聚集发生时
- 当产品被添加到购物车时
- 当产生产品推荐时
- 当实际进行购买时
这是一个典型的时间点正确性问题的例子,每当你有一个实时机器学习模型时就会弹出来。构建训练集时,数据科学家应该将预测时可用的最新要素加入到每个训练示例中,而不要加入预测后生成的要素。在预测实际生成之后添加到一行训练数据中的任何特征值都将构成数据泄漏——真实世界的模型将无法访问该数据。
例子
为了使这个例子更具体,假设我们拥有的训练数据看起来像这样:
用户购物车中当前商品的摘要(实时更新)
用户过去购买的 RFM 摘要(每周更新)
一旦实现,您的产品推荐算法可能会在 2021–01–01 9:43:25 做出预测。如果是这样的话,最新的特征应该是在 2021–01–01 9:43:25 之前在第一个特征集中的第 2 行和第二个特征集中的第 1 行观察到的那些特征。
但是,如果预测发生在 2021–01–01 9:37:25,则最新的要素将是第一个要素集中的第一行,而不是第二行。数据科学家需要确保他们的训练集中没有包含任何发生在预测时间之后的数据。
虽然数据科学家当然有可能构建移动窗口时间点 SQL 连接,以便为每个预测时间提供最新的功能,但这是一项耗时且容易出错的任务,已经成功实施大规模实时机器学习的组织不得不提出替代解决方案。
基于日志的训练 确保模型根据时间点正确数据进行训练的最简单方法是简单地记录预测时可用的特征值。每当生成一个预测时,记录当时可用的特征值,下一个训练集将自动为您构建!虽然基于日志的训练在简单性方面无可否认是优雅的(您知道您的要素将在预测时可用,因为您在预测时记录了它们),但它有两个重要的缺点。
首先,添加新功能需要时间。如果您想向模型中添加一个新要素,您必须现在就开始记录该要素的值,收集足够的数据来训练您的模型,然后再进行训练。特征是任何机器学习模型中最重要的部分,基于日志的再训练使得你的模型不可能快速迭代和改进。
第二,特征值不能跨不同模型共享。如果要训练一个在不同时间点执行预测的新模型,则不能使用已经记录的特征值。相反,您必须重新开始记录值。
就 SQL 功能而言——简单情况
构建时间点正确训练集的最简单的解决方案是使用时间旅行功能。许多数据库和数据湖允许您查询过去的某个时间点,并且您可以简单地查询做出预测的时间点。如果预测总是在某个时间进行,比如每天早上,这种解决方案是可行的,但是如果预测是在波动的时间进行,这种解决方案就不可行。为每一行培训数据设置单独的截止日期查询是不现实的。
时序特征商店
构建特性存储很大程度上是为了解决时间点正确性问题。给定每个预测的时间戳,特征库将仅使用当时可用的特征自动构建训练集。使用功能存储,您可以构建定型集,而不必等待记录新数据,也不需要以可预测的时间间隔进行预测。与手动记录一长串特征值相比,特征存储只需要几行代码。
这一切是如何运作的?有许多特性存储架构,但一种方法是使用混合(HTAP) SQL 数据库来构建特性存储,它可以进行快速查找和复杂分析。HTAP 数据库有两个内部执行引擎,一个用于低延迟查找和更新的操作工作负载,另一个用于分析工作负载。基于成本的数据库优化器通过动态评估查询计划来自动选择执行引擎,并维护这些引擎之间的一致性。
在这种方法中,每个要素集或要素组在数据库中存储为两个表:一个表包含任何给定要素的最新值,另一个表包含过去的时间序列历史要素值。使用这个特征值的历史表,功能存储可以使用简单的 API 轻松地自动构建训练集。
需要注意的是,并非所有的要素存储都能解决数据科学家的时间点正确性问题。一些特征存储没有在它们的 create_training_set 函数中表示训练标签和训练集的时间戳的机制。在这些特征存储方法中,以一致和正确的方式连接不同的和异步的时间戳的复杂性被推给了用户。
在幕后,上面显示的函数自动生成这个复杂的 SQL 查询,其中包含多个连接和子查询。在这种情况下,将连接三个特征集,但是可以从任意数量的特征集创建训练集。如果没有时间序列特征库,数据科学家将不得不编写函数来手动管理这些独立的时间点。
有了时间序列特征库,如果数据科学家想要在新特征上训练他们的模型,他们只需指定他们的训练标签以及该标签的时间戳和连接键。然后,会自动生成一个时间点训练集。
要了解更多关于功能存储如何帮助防止数据泄露的信息,并了解基于 HTAP 数据库的功能存储的独特优势,请查看这个动手演示,我将向您展示如何使用 Splice Machine 的功能存储。
带变压器的指针网络
实践教程
是什么让指针网络在今天仍然如此适用?

作者照片。
跟随这款谷歌 Colab 笔记本。
最初的指针网络论文 [ 1 ]最初被 NeurIPS 2015 接受,使得它在深度学习年中相当古老。尽管如此,到目前为止,它已经积累了 1700 多条引用,并继续被集成到现代解决方案中[ 2 , 3 ],得到了许多改进[ 4 , 5 ],并激发了替代架构[ 6 ]。它甚至在腾讯人工智能实验室[ 7 ]创建的一个最先进的玩星际争霸 2 的模型中扮演了一个很小但很重要的角色。是什么让指针网络在今天仍然如此适用?
这个简单而优雅的架构解决了序列预测问题中一个微妙的复杂性。假设我们希望预测一个输入序列的索引序列。如果输入的长度是可变的,我们该怎么办?利用现有方法,字典大小(即输入序列索引)必须先验地固定。这对于像句子生成这样的输入字典是一组已知字符的问题来说很好。另一方面,组合问题是指针网络出现的地方。指针网络允许我们解决组合优化任务,其中目标是在推理时间而不是在训练时间定义的输入索引序列。指针网络论文涵盖了三个这样的任务:平面凸包、Delaunay 三角剖分和对称平面旅行推销员问题。
在 GitHub 上可以找到许多指针网络的实现(你可以在这篇文章中找到一些链接),但大多数都是在论文发表后不久发表的,并使用基于 LSTM 或 GRU 的编码器,这些编码器后来随着《变形金刚》的成功而不再受欢迎。这篇文章附有一个 Jupyter 笔记本,你可以跟着这里的一起看。大部分代码改编自 GitHub 用户 ast0414 的pointer-networks-py torchrepo,解决了整数排序问题。我通过解决平面凸包问题扩展了早期的工作,并用变压器替换了基于 LSTM 的编码器和解码器。
应用程序
如前所述,指针网络被应用于组合问题,例如求解平面凸包。指针网络也出现在最近的各种应用中。在 TStarBot-X [ 7 ]中,一个玩游戏《星际争霸 2》的 AI 智能体(在其前身由 DeepMind 创建的 AlphaStar [ 8 )中,一个指针网络被用来为给定的动作选择目标单位。 PolyGen [ 3 ],一种独特的 3D 网格生成模型(我在这里写了更多关于的内容),采用指针网络将顶点分配给给定的面,一次分配一个面,直到网格拓扑结构构建完成。指针网络甚至被用于在“具有强化选择句子重写的快速抽象摘要 [ 2 ”中选择很好地概括文档的显著句子。在这篇文章中,我们演示了如何使用指针网络解决平面凸包问题。
指针网络是由什么组成的?
指针网络有效地创建了跨越可变数量令牌的注意机制。给定一系列记号 (t0,T1,…,tn-1) ,指针网络关注下一个记号 tn 的候选输入字典。这些输入候选中的每一个都具有由编码器产生的相关嵌入向量。同样,每个序列令牌都有自己的解码器嵌入。与其他序列预测模型不同,指针网络的构建方式使得输入候选项的数量可以在推理时改变。

指针网络的体系结构。对于每个输出令牌,我们将它的嵌入与输入点的嵌入相结合,以产生对输入点集成员的索引的软最大值。
指针网络的关键创新是具有精心制作的结构的注意力机制。下面的一对公式表达了指针网络逻辑的核心。它首先包括分别乘以编码器嵌入和解码器嵌入的两个可学习的权重,随后是非线性和乘以另一个学习的权重 v ,这降低了特征维度。这一层本质上结合了输入和目标嵌入的特征,以创建对应于输入-目标对的激活的二分图。最后,softmax 层表达了与我们的输入嵌入相对应的跨维度的条件概率。我们剩下的(对于批处理中的每一行)是输出序列中任何给定位置的跨输入标记的一行概率。

指针网络由几个可学习的权重组成,后跟输入字典上的 softmax(作者用https://www.mathcha.io/创建的照片)。
我们如何将这个表达式翻译成代码?下面的代码片段显示了如何将指针网络实现为 PyTorch 模块。
输入x_decoder、x_encoder和mask分别是由变换器编码器生成的输入嵌入、来自变换器解码器的目标嵌入和屏蔽输入填充的二进制掩码。权重乘法被实现为线性层。此外,softmax 层被屏蔽,从而可以忽略填充和先前预测的标记。
这个模块作为凸包问题整体架构的“任务”层。然而,它被设计为通用的,并且可以应用于涉及一组输入和目标嵌入的其他组合问题。
凸包问题
假设我们有一个公告板,上面覆盖着随机放置的图钉。如果我们在这些图钉周围拉伸一条细橡皮筋,使得它包围所有图钉,那么橡皮筋将仅接触图钉组中最外面的点的子集。这个子集在输入点上形成一个凸集。给定平面点集 S ,平面凸包是在所有点上形成凸集的点 Sc 的最小子集。由 S 中的任何点的子集形成的多边形所覆盖的区域实际上将只覆盖这个凸包的区域的子集。因此,凸包覆盖的区域是集合 S 内点的所有子集的并集。

带箭头的黑色套索是我们的“橡皮筋”,蓝色多边形代表通过将这个“橡皮筋”拟合到我们的点集而形成的凸包(来源:维基百科)。
凸包在数学、统计学、计算机图形学和动物行为学等领域有着广泛的应用。然而,我们只关心凸包作为一个有趣的玩具问题,用指针网络来解决。一组点的凸包是一个组合问题,非常适合用指针网络来解决,我们只需要使用 Numpy 和 Shapely 就可以轻松地为它生成无限量的训练数据。
我们在(x,y)平面上综合生成均匀分布的样本以用作数据。这些输入点中的每一个都被输入到我们的编码器中,产生一个嵌入,作为我们的输入字典。我们还添加了两个控制标记的一键编码:<sos>表示序列的开始,<eos>表示序列的结束。因此,这个输入字典的大小取决于我们集合中输入点的数量,加上两个控制标记。指针网络的任务是从该字典中选择标记,以构建凸包序列。更多详情请参见 Google Colab 笔记本。
结果
在 Colab 笔记本中,我们在 100,000 个随机生成的输入输出对上训练神经网络,输入长度从 5 到 50 不等。我们还通过应用相同的方法,分别生成长度为 1,000 和 10,000 的验证和测试集。该方法达到了约 81.3%的测试准确率(论文报道为 69.6%)和 99.9%的重叠率(论文报道为 99.9%)。
它产生的凸包非常接近 Shapely 提供的解。在大多数出现错误的情况下,预测的凸包通常与解决方案重叠至少 99%,特别是对于较长的序列(因为单个点对整体区域的贡献较小)。请参阅提供的 Colab 笔记本,亲自尝试并检查代码。

我们基于指针网络的 n=50 的凸包解生成的凸包例子(作者供图)。

n=10 的指针网络生成的凸包例子。少点上的错误导致更大的偏差(作者供图)。
结论
指针网络是一种简单但通用的体系结构,它可以合并到神经网络中,用于预测可变长度输入字典上的序列。这篇文章关注的是平面凸包问题,但是它们可以应用于任何我们希望预测一组指向一组编码的索引的问题。
参考
[1] Oriol Vinyals,Meire Fortunato,Navdeep Jaitly — 指针网络 (2015),NeurIPS 2015
[2]延-陈春,莫希特·班萨尔— 快速抽象概括与强化选择句重写 (2018),ACL 2018
[3]查理·纳什,雅罗斯拉夫·加宁,s·m·阿里·伊斯拉米,彼得·w·巴塔格利亚— PolyGen:一个三维网格的自回归生成模型 (2020),ICML 2020
[4] Avishkar Bhoopchand,Tim rocktschel,Earl Barr,Sebastian Riedel — 用稀疏指针网络学习 Python 代码建议 (2016),ICLR 2017 提交
[5],,孟,,葛玉斌,,宋,周杰,罗杰波— 利用成对排序预测增强句子排序的指针网络 (2020),2020
[6] Wouter Kool,Herke van Hoof,Max Welling — 注意,学会解决路由问题! (2019),ICLR 2019
7、、熊杰超、、孙星海、、、郭、陈巧波、史腾飞、俞宏生、吴希鹏、张— TStarBot-X:星际争霸 2 全游戏高效联赛训练开源综合研究 (2020),腾讯 ai 实验室
[8] Oriol Vinyals,Igor Babuschkin,David Silver — 星际争霸 2 中使用多智能体强化学习的大师级水平 (2019),《自然
为人工智能指出正确的方向
播客
香蕉数据播客的交叉集!
编者按:这一集是我们关于数据科学和机器学习新兴问题的播客系列的一部分,由 Jeremie Harris 主持。除了主持播客,Jeremie 还帮助运营一家名为sharpes minds的数据科学导师初创公司。
本期《走向数据科学》播客特别节目是我们在香蕉数据播客上的朋友们的一次交流。我们将缩小讨论人工智能为人类创造的一些最重要的挑战,以及该技术可能采取的一些可能的未来方向。
以下是我最喜欢的一些外卖食品:
- 人类正在将越来越多的思维和决策转移给机器,随着人工智能系统能力的增强,我们的速度越来越快。但是随着决策而来的是责任:如果一个人工智能系统被指控做出错误的关键决策,谁该受到责备?人工智能辅助(或人工智能主导)决策背景下的问责制问题是我们作为一个社会仍然没有考虑清楚的问题。
- 人工智能决策背景下的问责制也是一个不断变化的目标。我们无法提出一套静态规则来确定当人工智能决策出错时谁应该负责,因为人工智能系统的能力在不断增加,人工智能一直被应用于新的问题类别。将责任不视为一套规则,而是视为一个动态的过程和一套原则可能是有意义的,这些过程和原则可以对人工智能决策范围的变化更加鲁棒。
- 无人机是人工智能问责制问题的一个很好的例子:世界上第一次全自动无人机攻击最近发生在利比亚,尽管细节仍然很少,但无人机似乎识别了它的目标,并决定在没有人类干预的情况下与它们交战,这与许多国际政府支持的人在回路范式相违背。如果无人机攻击错了目标,谁来承担责任?无人机制造商?算法设计师?部署无人机的国家?甚至是无人机本身?这些问题不容易想清楚。
- 有一个被称为古德哈特定律的原则,它位于安全开发人工智能系统的挑战的核心。古德哈特定律说,一旦你定义了一个你想要用来衡量系统性能的指标,人们(可能还有人工智能!)将开始与该指标相匹配,它将不再是您试图优化的系统性能的可靠指标。
- 例如:在 20 世纪 20 年代,美国股市是美国整体经济健康状况的一个相当可靠的指标。但随着更多的注意力集中在这一指标上,政府和央行已经找到了操纵它的方法——例如,通过印刷钞票,最终推高股价,使股价与整体经济脱钩。公司总是会遇到古德哈特定律:如果他们定义了一个想要优化的指标,而他们没有注意记住这个指标的大目标,他们最终可能会短视地专注于这个指标,结果适得其反。
- 我们之前已经在播客中讨论过人工智能校准:将人工智能与人类价值观校准是一个令人惊讶的挑战性问题。大多数人认为,只有当我们建造了比我们更智能的机器时,对齐问题才会变得重要,但事实上,我们可以说已经遇到了这个问题。例如,Twitter 的推荐系统名义上是为了优化用户参与度而设计的,但有些人想知道它是否学会了通过向用户提供更具政治极化的内容来破解这一指标。
你可以点击这里查看香蕉数据播客,或者点击这里在 Twitter 上关注我。

章节:
- 0:00 介绍
- 人工智能时代的问责制
- 9:00 古德哈特定律
- 12:35 人工智能安全挑战
- 18:05 安全和透明的区别
- 24:45 全球一致性概念
- 29:00 不同行业内的自动化
- 33:10 人工智能安全和危机管理
- 34:20 总结
Python 中的泊松分布和泊松过程—统计

美国宇航局在 Unsplash 拍摄的照片
在本文中,我们将探讨 Python 中的泊松分布和泊松过程。
目录
- 介绍
- 什么是泊松过程
- 什么是泊松分布
- 泊松分布示例
- Python 中的泊松分布示例
- 结论
介绍
为了继续学习本教程,我们需要以下 Python 库:scipy、numpy 和 matplotlib。
如果您没有安装它,请打开“命令提示符”(在 Windows 上)并使用以下代码安装它:
pip install scipy
pip install numpy
pip install matplotlib
什么是泊松过程
泊松点过程(或简称为泊松过程)是数学空间中随机分布的点的集合。
由于它的几个性质,泊松过程经常被定义在一条实线上,在这里它可以被认为是一维的随机过程。这进一步允许建立数学系统并研究以随机方式出现的某些事件。
它的一个重要性质是过程中的每个点都随机地独立于过程中的其他点。
作为一个例子,我们可以想到一个在现实生活中可以观察到这种过程的例子。假设您正在研究飓风的历史频率。这确实是一个随机过程,因为今年的飓风数量与去年的飓风数量无关,以此类推。然而,随着时间的推移,你可能会观察到一些趋势,平均频率,等等。
从数学上来说,在这种情况下,点过程取决于某个可能是常数的东西,例如平均速率(例如,打电话的客户的平均数量)。
泊松过程由泊松分布定义。
什么是泊松分布?
泊松分布是在给定两种情况下,在固定时间间隔内发生的事件数量的离散概率分布:
- 事件以某种恒定的平均速率发生。
- 事件相互独立,与时间无关。
为了在某种背景下说明这一点,考虑一下上一节中飓风频率的例子。
假设我们有 20 年间观察飓风的数据。我们发现每年平均有 7 次飓风。每一年都独立于前几年,这意味着如果我们今年观察到 8 次飓风,并不意味着我们明年会观察到 8 次。
泊松分布的 PMF(概率质量函数)由下式给出:

作者图片
其中:
- λ 是由λ= E(X)=μ给出的实正数
- k 是出现的次数
- e = 2.71828)
Pr(X=k)可以读作:一个区间内 k 个事件的泊松概率。
泊松分布的 CDF(累积分布函数)由下式给出:

作者图片
泊松分布示例
既然我们已经知道了一些要使用的公式,让我们来详细地看一个例子。
回想一下我们在前面几节中提到的飓风数据。我们知道飓风的历史频率是每年 7 次,这就形成了我们的 λ 值:
λ= 7
我们的问题是,今年观测到 5 次飓风的概率是多少?而这就形成了我们的 k 值:
= 5
使用上一节中的公式,我们可以计算泊松概率:

作者图片
所以明年正好观测到 5 个飓风的概率等于 12.77%。
自然,我们对其他频率的概率很好奇。
泊松 PMF(概率质量函数)
考虑下表,该表显示了飓风频率的泊松概率(0-15):
使用上表,我们可以为该示例创建泊松概率质量函数的以下可视化:

作者图片
泊松 CDF(累积分布函数)
考虑下表,该表显示了飓风频率的泊松累积概率(0-15):
使用上表,我们可以为该示例创建以下泊松累积分布函数的可视化:

作者图片
该表还允许我们回答一些有趣的问题。
举个例子,如果我们想找出看到最多 5 次飓风的概率(数学上: k ≤5)会怎么样,我们可以看到是 0.30071 或者 30.07%。
另一方面,我们可以对观测到 5 个以上飓风的概率感兴趣(数学上: k > 5),这将是 1- p (5,7)= 1–0.30071 = 0.69929 或 69.93%。
Python 中的泊松分布示例
在上一节中,我们手工计算了概率质量函数和累积分布函数。在本节中,我们将使用 Python 再现相同的结果。
我们将从导入所需的依赖项开始:
接下来,我们将需要一个数组的 k 值,我们将计算泊松 PMF。在上一节中,我们计算了从 0 到 16 的 16 个值 k ,所以让我们用这些值创建一个数组:
您应该得到:
*[ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16]*
在以下章节中,我们将重点介绍如何使用 Python 计算 PMF 和 CDF。
Python 中的泊松 PMF(概率质量函数)
为了使用 Python 计算泊松 PMF,我们将使用。scipy . poisson生成器的 pmf() 方法。它需要两个参数:
- k 值(我们创建的 k 数组)
- μ 值(在我们的例子中,我们将其设置为 7)
现在我们可以用泊松概率值创建一个数组:
您应该得到:
*[0.00091 0.00638 0.02234 0.05213 0.09123 0.12772 0.149 0.149 0.13038 0.1014 0.07098 0.04517 0.02635 0.01419 0.00709 0.00331 0.00145]*
注:
如果您想以更好的方式打印每个(k)值和相应的概率:
您应该得到:
*k-value 0 has probability = 0.00091
k-value 1 has probability = 0.00638
k-value 2 has probability = 0.02234
k-value 3 has probability = 0.05213
k-value 4 has probability = 0.09123
k-value 5 has probability = 0.12772
k-value 6 has probability = 0.149
k-value 7 has probability = 0.149
k-value 8 has probability = 0.13038
k-value 9 has probability = 0.1014
k-value 10 has probability = 0.07098
k-value 11 has probability = 0.04517
k-value 12 has probability = 0.02635
k-value 13 has probability = 0.01419
k-value 14 has probability = 0.00709
k-value 15 has probability = 0.00331
k-value 16 has probability = 0.00145*
这与我们在手工计算概率的例子中看到的完全相同。
使用 Python 绘制泊松 PMF
我们将需要之前创建的 k 值数组以及这一步中的 pmf 值数组。
使用 matplotlib 库,我们可以使用 Python 轻松绘制泊松 PMF:
您应该得到:

作者图片
Python 中的泊松 CDF(累积分布函数)
为了使用 Python 计算泊松 CDF,我们将使用。CDF()scipy . poisson生成器的方法。它需要两个参数:
- k 值(我们创建的 k 数组)
- μ 值(在我们的例子中,我们将其设置为 7)
现在我们可以用泊松累积概率值创建一个数组:
您应该得到:
*[0.001 0.007 0.03 0.082 0.173 0.301 0.45 0.599 0.729 0.83 0.901 0.947 0.973 0.987 0.994 0.998 0.999]*
注:
如果您想以更好的方式打印每个 k 值和相应的累积概率:
您应该得到:
*k-value 0 has probability = 0.001
k-value 1 has probability = 0.007
k-value 2 has probability = 0.03
k-value 3 has probability = 0.082
k-value 4 has probability = 0.173
k-value 5 has probability = 0.301
k-value 6 has probability = 0.45
k-value 7 has probability = 0.599
k-value 8 has probability = 0.729
k-value 9 has probability = 0.83
k-value 10 has probability = 0.901
k-value 11 has probability = 0.947
k-value 12 has probability = 0.973
k-value 13 has probability = 0.987
k-value 14 has probability = 0.994
k-value 15 has probability = 0.998
k-value 16 has probability = 0.999*
这与我们在手工计算累积概率的例子中看到的完全相同。
使用 Python 绘制泊松 CDF
在这一步中,我们将需要之前创建的 k 值数组以及 pmf 值数组。
使用 matplotlib 库,我们可以使用 Python 轻松绘制泊松 PMF:
您应该得到:

作者图片
结论
在本文中,我们探讨了泊松分布和泊松过程,以及如何在 Python 中创建和绘制泊松分布。
如果你有任何问题或对一些编辑有建议,请随时在下面留下评论,并查看更多我的统计文章。
原载于 2021 年 11 月 23 日 https://pyshark.com*https://pyshark.com/poisson-distribution-and-poisson-process-in-python/。***
现实生活中的泊松过程和泊松分布:模拟一家冰淇淋店的高峰时间
实践教程

图片作者。
现实世界中的一些现象可以用事物的计数来表示。例如,从机场起飞的航班数量,在商店登记处排队的顾客数量,特定地区一年中发生的地震数量。
计数事件是一项相对简单的任务,但是如果你想从仅仅计数事件的发生发展到询问关于的问题,这些事件在特定的时间单位内发生的可能性有多大,你需要更强大的工具,比如泊松分布。
泊松分布模拟来自离散随机变量的给定数量的事件在特定时间间隔内发生的概率。
泊松分布以多产数学家西蒙·丹尼斯·泊松命名,是一种离散概率分布。它模拟了一个离散随机变量中的许多事件在特定时间间隔内发生的概率。

西蒙·丹尼斯·泊松(图片来源)
离散随机变量的概率分布
****离散随机变量描述了具有一组特定值的事件【1】。
例如,代表投掷公平硬币的离散随机变量只能有正面或反面的值。代表从一副牌中挑选一张牌的离散随机变量只能有 52 个可能的值,红心 2,梅花 9,方块皇后,黑桃 a等等。
离散随机变量的概率分布称为概率质量函数 (PMF)。它是一个函数,将随机变量的每一个值映射到相应的概率上。
例如,遵循泊松分布的随机变量的概率质量函数如下所示。

服从泊松分布的随机变量的概率质量函数的例子。
概率质量函数有三个基本条件:
- ****所有概率都是非负的。
- 每个事件的概率必须在 0 到 1 之间**。抛硬币并正面朝上的概率必须介于 0 和 1 之间。
- 所有可能值的概率之和必须等于 1。**抛硬币并得到正面的概率和抛硬币并得到正面的概率之和必须等于 1。**
泊松分布和机器学习
在机器学习中,泊松分布用于概率模型。例如,在 广义线性模型 中,您可以使用泊松分布来模拟目标变量的分布。
在机器学习中,如果响应变量代表一个计数,可以使用泊松分布对其建模。
在现实世界的应用中,这些模型被用来预测或模拟复杂的系统,如极端天气事件[2]或 Twitter 消息和维基百科修订历史的级联[3]。
在你朋友的冰淇淋店模拟高峰期
你的老朋友珍妮在她所在城市的市中心有一家冰淇淋店。
她多次告诉你,她一直关注的一件事是如何为商店配备员工。Jenny 希望确保每个顾客都有最短的等待时间,并且总有人帮助他们,这样顾客体验就是他们能提供的最好的。
但是,有时情况并非如此。珍妮已经痛苦地认识到,当商店里有超过 10 名顾客时,没有足够的员工来帮助他们,一些顾客最终因漫长的等待和缺乏帮助而沮丧地离开。
你是一名数据科学家,也是 Jenny 的好朋友,所以你是她寻求帮助的第一个人。最终,Jenny 希望你帮助她计算出在任何给定的时间里,她的店里会有多少顾客。
思考了一会儿后,你决定重新组织她的问题,这样更符合 Jenny 真正想知道的,在任何给定的时间内,10 个顾客同时出现在商店的可能性有多大。
将这重新定义为一个概率问题,你定义一个随机变量,即顾客到达珍妮的冰淇淋店。这立刻让你想到用二项分布来建模这个问题。
二项分布
二项式分布描述了一系列伯努利试验的成功次数。因此,如果你认为一个顾客进入商店是一个的成功,这种分布听起来是一个可行的选择。
但是,在对到达珍妮冰淇淋店的随机变量顾客建模之前,您需要知道分布的参数。
二项式分布有两个参数:
- n :伯努利试验总次数。
- p :伯努利试验成功的概率。
要回答问题,在任何给定的时间、、内,10 名顾客同时出现在商店的可能性有多大,您需要使用二项式分布的概率质量函数。它看起来像这样:

二项分布的概率质量函数。
到目前为止,您只有参数 k ,客户总数。
但是你记得珍妮去年告诉过你关于商业区的一系列研究。在其中一项研究中,他们发现,在一个正常的工作日,大约有 7500 人路过市中心,有 10%的几率路人会进入市中心 15 家商店中的一家。
这正是你需要的信息!
在这种情况下,每个市中心的过路人都代表了一次伯努利试验,成功意味着进入一家商店。根据研究,经过市中心的顾客总数对应于 n,并且每个顾客进入珍妮商店的概率相同,为 10%。
回到问题10 个顾客同时在珍妮店里的可能性有多大你只需要插入二项概率质量函数中的参数。

手工做这些计算很有挑战性。您可以使用 Python 的 SciPy 模块来完成所有繁重的工作。
from scipy.stats import binomk = 10
n = 7500
p = 0.10print("Probability of having 10 customers at the shop")
print(binom.pmf(k, n, p))

这是一个非常小的概率,事实上,这并不完全是珍妮所寻找的。
重要的细节是,Jenny 想知道同时有 10 个顾客在商店的概率。
二项式分布不模拟同时发生的事件。相反,成功发生在一系列的试验中。所以,最终,二项分布并不是这个问题的最佳模型。
二项式分布不模拟同时发生的事件。相反,成功发生在一系列的 n 试验中。
思考二项分布的这种局限性,以及你可以使用什么工具来回答 Jenny 的问题,你还记得泊松范式,也称为泊松近似。
泊松范式
泊松范式是这样表述的:
当您有大量发生概率很小的事件时,那么在固定时间间隔内发生的事件数量的分布近似遵循泊松分布。
数学上讲,当 n 趋于无穷大 (n→无穷大)且概率 p 趋于零时 (p→ 0)二项分布可以近似为泊松分布。
这种近似假设事件是独立的或者弱相关。例如,如果事件是独立的,知道 Adam 进入商店也不会给你任何关于 Andrea 进入商店的信息。
但是,在现实世界中,有些事件很可能不是完全独立的。如果亚当和安德里亚进了商店,那也能给我一些比安卡进商店的信息。这些事件不是独立的,它们是弱相关的。
只要事件是独立的或弱相关的,这个假设就成立,你可以将二项式近似为泊松分布。
泊松分布
了解泊松范式会让你更有信心使用泊松分布来模拟进入珍妮商店的顾客数量。
泊松分布描述了在固定时间间隔内以特定速率发生的多个独立事件的概率。与二项式不同,它只有一个参数,即事件发生的频率。
因为它都是关于以特定速率发生的事件,所以概率质量函数看起来像这样:

泊松分布的概率质量函数。
所以,回答问题10 个顾客同时在珍妮的店里的可能性有多大?你需要的最后一块是顾客进店率。
你没有珍妮商店的具体数据,但根据商业协会的研究,在特定的一天,经过市中心的 7,500 人中有 10%进入了商店。
使用您拥有的所有数据,您可以说这 7,500 名顾客中的 10%在 10 个营业小时内进入了 15 家市区商店。因此,您可以计算出λ并确定每小时大约有 5 名顾客进入 Jenny 的商店,即每 12 分钟有一名顾客进入。

计算、顾客进入市中心商店的比率。
要回答 Jenny 的问题,可以在泊松概率质量函数中插入参数 lambda 。

在任何给定的时间里,有 1.8%的几率会有 10 个顾客同时出现在珍妮的店里。与 Jenny 的想法相比,这是一个相对较低的值!
绘制概率质量函数,你还可以看到商店里同时有多达 10 个顾客的概率。

比率为 5 的泊松分布的概率质量函数,观察多达 10 个事件。
以目前市中心顾客进入商店的速度,Jenny 可以准备在大多数情况下让 4 或 5 个顾客进入商店。
以下是如何使用 Python 的 SciPy 模块计算和绘制泊松概率质量函数。
*from scipy.stats import poisson
import numpy as np
import matplotlib.pyplot as plt def poisson_pmf(k, lambda_val):
*'''
Calculates and plots the Poisson probability mass function (PMF* *:param k: Number of events to occur at the same time
:param lambda_val: Lambda value. rate at which the events occur* *:return:
- Print out the probability that k events occur at the same time with a rate lambda value* *- Plot the PMF from 0 to k occurrences
'''* x = np.arange(0, step=0.1, stop=k + 1)
pmf = poisson.pmf(k=x, mu=lambda_val) print("Poisson:: Probability of having 10 customers at the shop")
print(np.round(poisson.pmf(k=k, mu=lambda_val), 3)) # plotting the PMF
fig, ax = plt.subplots(1, 1, figsize=(12, 9))
# removing all borders except bottom
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
plt.bar(x, pmf * 100, color='#0F72AC')
plt.xlabel('Number of customers at the shop', fontsize=12, labelpad=20)
plt.ylabel('P(X=k) | Probability of k occurrences', fontsize=12, labelpad=20)
plt.show() poisson_pmf(k=10, lambda_val=5)*

Jenny 非常兴奋地得知,大约有 2%的可能性商店变得非常拥挤,他们很难提供优质服务。
但她也有点担心。
有一个为期三天的周末即将到来,珍妮知道,在这样的日子里,她在厨房里会有更好的帮助。这样,她可以保证冰淇淋不会短缺,而其他员工则负责店面。
当厨房真的很忙的时候,珍妮只能每小时检查一次店面。所以她再次寻求你的帮助,想出那天商店有 10 个顾客的概率是多少。**
泊松过程
等等,什么?我不是已经回答过这个问题了吗?,你可能会想。不完全是。**
仔细想想,你会发现这里有一个新的组件,Jenny 将在一天中的特定时间间隔检查店面。****
虽然泊松分布的概率质量函数为您提供了同时有 10 个顾客在商店的概率,但是时间间隔是固定的。你看到的是一天中给定的一个小时,因为那是速率给你的。
现在,珍妮将要在一天中多次检查店面。因此,您需要一个工具来计算事件,即顾客进入商店,但在一个连续的时间框架内。
你需要把珍妮的问题重新定义为泊松过程。**
泊松过程 是一个具有独立时间增量的统计过程,其中一个时间间隔内发生的事件数量用泊松分布建模,每个事件发生的时间间隔服从指数分布【2】。
在实践中,泊松过程描述了无限数量的独立且相同分布的事件的发生,每个事件由遵循泊松分布的随机变量描述[4]。**
这些事件通常被称为到达,可以在任意时间发生,因此事件在特定时间点发生的概率为零。这就是为什么泊松分布关注事件或到达之间的时间,即到达间隔时间。**

五个时间单位内四个不同随机变量的到达。**
概括地说,泊松过程是一个计数过程,包括:
- 在时间间隔 t 内发生的至少一个到达或事件。
- 非负率λ,类似泊松分布。
- 独立时间增量。
- 遵循指数分布的到达间隔时间。
- 无记忆性,所以到下一个到达的时间独立于先前的到达。这意味着下一个到达的不知道 关于过去发生的事情的任何事情。**
回到珍妮的问题!
您将使用与泊松分布的 PMF 非常相似的概率质量函数。
在泊松过程中,预期到达数结合了速率λ和您感兴趣的时间间隔。因为你对连续发生的事件感兴趣。**
你还必须考虑到达的间隔时间。尽管两个事件不能同时发生,但它们可以在同一时间间隔内的任意时刻发生。

泊松过程的概率质量函数。
当你看任何给定的小时,在这种情况下是最小的单位时间,泊松过程就相当于泊松分布的概率质量函数。假设你有一个总时间间隔 t 等于 1。

但是你想计算一整天的概率。知道珍妮每小时都要去店面签到,总时间间隔 t 等于 10。

这些计算太容易出错,不能用手算。所以你可以再次求助于 Python,为泊松过程编写概率质量函数。
*import mathdef poisson_process(lambda_value, k, t):
*'''
Calculates the probability mass function of a Poisson process* *:param lambda_val: Lambda value. rate at which the events occur
:param k: Number of arrivals to occur at the same time
:param t: time interval to observe arrivals* *:return:
- Print out the probability that k arrivals occur at the same time with a rate lambda value during time t
'''* numerator = np.power(50, 10) * math.exp(-50)
denominator = math.factorial(10)
print("Poisson process with\n\tlambda=" + str(lambda_value) + ", " + str(k) + " arrivals, during time interval of " + str(t) + " hours")
print(numerator/denominator) poisson_process(lambda_value=5, k=10, t=10)*
感谢您的帮助,Jenny 更加自信地认为,在为期 3 天的周末期间,光顾她的商店的顾客将获得 Jenny 和团队所能提供的最佳体验!
在商店营业的 10 个小时内,有 10 个顾客同时进入商店的概率非常小!

现在您知道如何对基于事件计数的真实世界系统和现象建模了!
使用泊松分布,您可以计算事件在离散的固定时间间隔内发生的概率。然后,用泊松过程把它扩展到一个连续的时间框架,比如一天的持续时间。
希望您喜欢学习泊松分布和泊松过程在现实生活中的应用。
感谢阅读!
参考
[1] 离散型随机变量的概率分布(谢弗,张) 2021 .2021 年 1 月 10 日
[2]克莱曼婷·达莱兰,托马斯·德意志州,极端天气事件泊松点过程强度的稳健估计器,《天气和气候极端》,2013 年第 1 卷,第 69-76 页
[3]西马,亚历山大&乔丹,迈克尔。(2010).用泊松过程的级联模拟事件。2010 年 UAI 第 26 届人工智能不确定性会议论文集。546–555.
[4] Bertsekas、Dimitri 和 John Tsitsiklis。概率介绍。第二版。雅典娜科学公司,2008 年**
图片由作者提供,除非另有说明。
泊松回归和广义线性模型
泊松回归和广义线性模型的理论介绍

杰斯温·托马斯在 Unsplash 上的照片
注意:在整篇文章中,我错误地将 E[Y]称为目标输出。当我提到 E[Y]时,我隐含的意思是 E[Y|X],因为这是正确的符号!这里链接的是一个解释这种差异的统计交换线程。
线性回归 是大多数数据科学家开始其旅程的第一个算法。对于连续数据,这是一个简单、易于实现和可视化的模型。初学数据的科学家第二常学的算法是 逻辑回归 ,其中模型有二进制输出。大多数人认为这两种算法是完全独立的,但实际上它们是名为 广义线性模型(GLMs) 的同一模型家族的一部分。在本文中,我们将通过一个使用 泊松分布 的示例场景来获得关于 GLMs 的直觉。
线性回归基础和局限性
线性回归是一种用于将线或超平面拟合到数据集的模型,其中输出为连续并具有正态分布的残差。这是数学上的写法:

LaTeX 作者的方程式
其中E(Y)是目标变量的均值响应, X 是预测变量的矩阵, β 是被调整和训练以产生最佳模型的未知线性系数。
线性回归在流行病学、金融和经济学等很多行业都有应用。然而,尽管被用于所有这些领域,它确实有一些缺陷,使得它的预测在某些应用中是多余的。假设你是一名电话接线员,想要预测你一天会接到多少个电话。你认为线性回归是一个合适的模型吗?答案是否,原因如下:
- 调用次数必须大于或等于 0,而在线性回归中,输出可以是负的,也可以是正的。
- 调用次数只取整数值而线性回归可以输出小数值。
这些缺陷以及许多其他缺陷要求我们使用另一种回归算法来模拟预期的调用次数。
泊松分布
泊松分布是一种概率分布,用于测量在指定的时间段内发生多少次以及 x(调用)发生的可能性有多大。分布的公式是:

LaTeX 作者的方程式
其中 λ 是预期出现的次数,在我们的例子中是调用。
这是将呼叫数量建模为其离散并且 x 和λ只能取大于或等于 0 的值的完美分布。现在的问题是如何把上面的线性回归方程变成泊松回归方程?这可以通过使用广义线性模型*来实现。*
广义线性模型
GLM,就像他们的名字一样,是线性回归的概括,其中响应变量采用非正态分布,如泊松或二项式分布。GLM 包含三个核心内容:
- 指数分布族的一部分
- 线性预测器
- 链接功能
我们现在将浏览这些内容,并简要推导和解释它们所指的内容。
指数族
如果满足以下函数,概率分布被视为 指数族 的一部分:

LaTeX 作者的方程式
这里,θ指的是自然参数,是与均值关联的,,φ是与方差关联的标度参数。此外,a(φ),b(θ)和 c(y,φ)是待定的函数,但我们不会在本文中详细讨论。
通过一些数学操作可以看出,指数族的均值、 E(Y)、和方差、 VAR(Y) 、由下式给出:

LaTeX 作者的方程式

LaTeX 作者的方程式
根据上面的泊松概率分布公式,我们可以将其改写为指数族形式:

LaTeX 作者的方程式
通过将系数与泊松公式和指数公式相匹配,我们得出结论:

LaTeX 作者的方程式
这些是泊松分布的一般已知结果。在文章的后面,我们将解释为什么我们会得到上述值以及它们的重要性。
线性预测器
这是解释变量及其对应的未知系数的线性组合(本质上是求和),等于目标数据的期望输出【E(Y):**

LaTeX 作者的方程式
其中上述系数和解释变量在矩阵形式中。
链接功能
这是 GLM 最重要的部分。链接函数, g(),'链接'输入变量到输出目标的分配。

LaTeX 作者的方程式
我个人认为这是“缩放”我们的输入到我们预期的输出范围。例如,在逻辑回归中, Sigmoid 函数将输出缩放到 0 和 1 之间。事实上,逻辑回归是基于二项式分布,它也是指数家族的一部分,因此是 GLM。对于线性回归,链接函数是恒等函数,因此它不会转换线性预测值。
人们可以通过简单地猜测允许特定范围输出的函数来确定链接函数。然而,它们也可以数学推导为 t ,它们与自然参数θ 的值“关联”。因此,泊松分布的链接函数是自然对数 ln()** 。此外,由于链接函数等于自然参数,这意味着它被称为规范链接函数。**
把所有的放在一起
啊!我们已经到了文章的结尾,现在我们可以把所有这些数学知识放在一起产生我们的泊松回归公式。现在我们知道链接函数是自然对数,线性回归方程转换为泊松回归方程:

LaTeX 作者的方程式
我们可以看到,将自然对数应用于输出意味着将总是取正值,即使线性预测器输出负结果!
结论
在这篇文章中,我们探讨了 GLM 的需要和一点他们的数学背景。本文并不全面,因此感兴趣的读者应该更详细地探讨这个主题,以便更好地理解。但是,我希望您会对这篇文章感兴趣,并从中获得一些关于高级统计建模的知识!
和我联系!
- 要在媒体上阅读无限的故事,请务必在此注册! 💜
- /subscribe/@egorhowell😀
- LinkedIn👔
- 推特 🖊
- github🖥
- https://www.kaggle.com/egorphysics🏅****
(所有表情符号都是由 OpenMoji 设计的——开源的表情符号和图标项目。许可证: CC BY-SA 4.0
时间序列数据集的泊松回归模型

图片由 Clker-Free-Vector-Images 来自 Pixabay ( Pixabay 许可)
如何使用 Python 和 Statsmodels 为计数的时间序列数据集构建泊松回归模型
泊松和类泊松回归模型通常用于基于计数的数据集,即包含整数计数的数据。例如,每小时走进医院急诊室的人数就是这样一组数据。
基于普通最小二乘回归的线性模型或非线性模型,例如基于基于神经网络的回归技术的模型,对于这样的数据集效果不好,因为它们可以预测负值。
如果数据集是计数的时间序列,由于时间序列数据通常是自相关的,因此会增加建模的复杂性。以前的计数会影响将来计数的值。如果回归模型不能充分捕捉这些相关性中包含的“信息”,则“无法解释的”信息将以自相关误差的形式泄漏到模型的剩余误差中。在这种情况下,模型的 拟合优度 会很差。
解决此问题的常见补救措施如下:
- 在拟合回归模型之前,检查时间序列是否有季节性,如果有, 进行季节性调整 。这样做,解释了季节性自相关,如果有的话。
- 对时间序列进行一次差分,即 y_t — y_(t-1) 对所有的 t 和 对差分后的时间序列进行白噪声测试 。如果差分时间序列可以表示为白噪声,那么原始时间序列就是一个随机游走。在这种情况下,不需要进一步建模。
- 对季节性调整的时间序列拟合基于泊松(或相关)计数的回归模型,但包括因变量 y 的滞后副本作为回归变量。
在本文中,我们将解释如何使用方法(3) 对计数的时间序列拟合泊松或类泊松模型。
制造业罢工数据集
为了说明模型拟合过程,我们将使用以下在回归建模文献中广泛使用的开源数据集:

该数据集是一个月度时间序列,显示了从 1968 年到 1976 年每月开始的美国制造业活动与美国制造业合同罢工数量之间的关系。

STRIKES 数据集(来源: R 数据集)(图片由作者提供)
该数据集在 R 中可用,可以使用 statsmodels 数据集包获取。
因变量 y 为击。
我们将从导入所有必需的包开始:
**import** statsmodels.api **as** sm
**import** statsmodels.discrete.discrete_model **as** dm
**import** numpy **as** np
**from** patsy **import** dmatrices
**import** statsmodels.graphics.tsaplots **as** tsa
**from** matplotlib **import** pyplot **as** plt
让我们使用 statsmodels 将数据集加载到内存中:
strikes_dataset = sm.datasets.**get_rdataset**(dataname=**'StrikeNb'**, package=**'Ecdat'**)
打印出数据集:
**print**(strikes_dataset.**data**)
我们看到以下输出:

我们将前 92 个数据点视为训练集,其余 16 个数据点视为测试数据集
strikes_data = strikes_dataset.**data**.**copy**()
strikes_data_train = strikes_data.**query**(**'time<=92'**)strikes_data_test = strikes_data.**query**(**'time>92'**).**reset_index**().**drop**(**'index'**, **axis**=1)
打印出因变量的统计数据:
**print**(**'Mean='**+str(np.**mean**(strikes_data_train[**'strikes'**])) + **' Variance='**+str(np.**var**(strikes_data_train[**'strikes'**])))
我们得到以下输出:
Mean=5.5 Variance=14.728260869565217
我们看到 y 过度分散,因此违反了泊松模型的均值=方差假设。为了说明过度分散,我们将拟合一个具有以下 NB2 方差函数的 负二项式回归模型 :

NB2 模型的方差函数(图片由作者
这是我们的回归表达式。打击是因变量输出是我们的解释变量。假设回归截距存在:
expr = **'strikes ~ output'**
我们将使用 Patsy 来雕刻出 X 和 y 矩阵。Patsy 会自动为 X 添加一个回归截距列:
y_train, X_train = **dmatrices**(expr, strikes_data_train, **return_type**=**'**dataframe**'**)
**print**(y_train)
**print**(X_train)
y_test, X_test = **dmatrices**(expr, strikes_data_test, **return_type**=**'**dataframe**'**)
**print**(y_test)
**print**(X_test)
使用 NB2 方差函数构建和训练负二项式回归模型:
nb2_model = dm.**NegativeBinomial**(**endog**=y_train, **exog**=X_train, **loglike_method**=**'**nb2**'**)nb2_model_results = nb2_model.**fit**(**maxiter**=100)**print**(nb2_model_results.**summary**())
我们得到拟合模型摘要的以下输出:

NB2 模型的模型摘要(图片由作者提供)
如系数的 p 值(0.034 和 0.000)所示,输出和离差参数α在 95%置信水平下具有统计显著性。
适合度
伪 R 平方 仅为 0.9%,表明对训练数据集的拟合质量非常差。
对数似然比检验的 p 值为 0.03589 ,表明该模型在 95% 置信水平下优于仅截距模型(又称零模型),但在 99% 或更高置信水平下则不然。
让我们看看拟合模型的残差的自相关图:

NB2 模型残差的自相关图(图片由作者提供)
我们可以看到,残差在时间滞后 1、2 和 3 处是自相关的,这表明因变量与之间存在自相关,NB2 模型无法完全解释这一点,导致其泄漏到模型的残差中。
总体来说,这个模型的拟合优度很差。
建立自回归泊松模型
为了修正自相关残差的情况,我们将引入【y】的滞后副本,特别是 y_(t-1)、y_(t-2) 和 y_(t-3) 作为回归变量以及输出变量。
但是我们没有直接引入 y_(t-k) 作为回归变量,而是使用ln[y _(t-k)】来解决当 y_(t-k) 的系数为正时的“模型爆炸”问题。
但是使用 ln() 变换产生了如何处理对数未定义的 y_t 的零值的问题。
我们使用 Cameron 和 Trivedi 在他们的书计数数据的回归分析 (参见第 7.5 节:自回归模型)中概述的以下技巧来解决这个问题:
我们将为每个感兴趣的时间延迟定义一个新的指标变量 d_t ,如下所示:
当 y_t = 0: 设置 d_t=1。
当 y_t > 0: 设置 d_t=0 时。
让我们对数据框进行这些更改。
定义一个函数,该函数将设置指示器变量 d_t 的值,如上文所定义:
**def** indicator_func(x):
**if** x == 0:
**return** 1
**else**:
**return** 0
并使用此函数创建一个新的指标变量列:
strikes_data[**'d'**] = strikes_data[**'strikes'**].**apply**(indicator_func)
我们还创建一个新列 strikes_adj ,如果 strikes 为 0,则该列设置为 1,否则将其设置为 strikes 的值:
strikes_data[**'strikes_adj'**] = np.**maximum**(1, strikes_data[**'strikes'**])
现在为 strikes_adj 和d:创建滞后变量
strikes_data[**'ln_strikes_adj_lag1'**] = strikes_data[**'strikes_adj'**].shift(1)
strikes_data[**'ln_strikes_adj_lag2'**] = strikes_data[**'strikes_adj'**].shift(2)
strikes_data[**'ln_strikes_adj_lag3'**] = strikes_data[**'strikes_adj'**].shift(3)
strikes_data[**'d_lag1'**] = strikes_data[**'d'**].shift(1)
strikes_data[**'d_lag2'**] = strikes_data[**'d'**].shift(2)
strikes_data[**'d_lag3'**] = strikes_data[**'d'**].shift(3)
删除所有包含空单元格的行:
strikes_data = strikes_data.**dropna**()
最后取 ln_strikes_adj_lag1 、 ln_strikes_adj_lag2 和 ln_strikes_adj_lag3 的自然对数。回想一下我们要添加 ln(y_(t_1)) 、 ln(y_(t_2)) 和 ln( y_(t_3)) 作为回归变量 :
strikes_data[**'**ln_strikes_adj_lag1**'**] = np.**log**(strikes_data[**'**ln_strikes_adj_lag1**'**])
strikes_data[**'**ln_strikes_adj_lag2**'**] = np.**log**(strikes_data[**'**ln_strikes_adj_lag2**'**])
strikes_data[**'**ln_strikes_adj_lag3**'**] = np.**log**(strikes_data[**'**ln_strikes_adj_lag3**'**])
让我们看看我们的数据框现在是什么样子:
**print**(strikes_data)

添加了滞后变量的 strikes 数据框
让我们再次将数据框架分为训练数据集和测试数据集:
strikes_data_train=strikes_data.**query**(**'time<=92'**)strikes_data_test=strikes_data.**query**(**'time>92'**).**reset_index**().**drop**(**'index'**, **axis**=1)
我们的回归表达式也需要更新,以包括滞后变量:
expr = **'strikes ~ output + ln_strikes_adj_lag1 + ln_strikes_adj_lag2 + ln_strikes_adj_lag3 + d_lag1 + d_lag2 + d_lag3'**
使用 Patsy 雕刻出 y和 X矩阵:****
***y_train, X_train = **dmatrices**(expr, strikes_data_train, **return_type**='dataframe')**print**(y_train)**print**(X_train)***
最后,我们将在(y _ train,X _ train)上构建并拟合回归模型。这一次,我们将使用简单的泊松回归模型:**
**poisson_model = dm.**Poisson**(**endog**=y_train, **exog**=X_train)poisson_model_results = poisson_model.**fit**(**maxiter**=100)**print**(poisson_model_results.**summary**())**
我们看到以下结果:

具有滞后输出变量的泊松回归模型的训练综述
适合度
首先要注意的是,由伪 R 平方测量的拟合优度比早期的 NB2 模型有所提高,从 0.9% 提高到 15.69% 。这是一个很大的进步。这一次,LLR 测试的 p 值也在 1.295e-15 处小得几乎为零。这意味着我们可以以接近 100%的信心说滞后变量泊松模型比仅截距模型更好。回想一下,之前我们可以说只有 95%的置信水平。
让我们看看这个滞后变量泊松模型的残差的自相关图:
**tsa.**plot_acf**(poisson_model_results.**resid**, **alpha**=0.05)plt.**show**()**
我们看到下面的情节:

滞后变量泊松模型残差的自相关图(图片由作者提供)
除了在滞后 13 处有非常轻微的显著相关性外,残差与所有其他滞后的相关性都在规定的 alpha 范围内。
我们将走向的滞后副本添加到泊松模型的回归变量的策略似乎已经解释了走向变量中的大部分自相关。
可变重要性
最后,让我们从滞后变量泊松模型的训练总结中注意到,虽然输出、 ln_strikes_adj_lag1 和 ln_strikes_adj_lag2 的系数在 95% c 置信水平下是显著的,但是第三个滞后 ln_strikes_adj_lag3 的系数仅在其表示的 75% 置信水平附近是显著的此外,所有三个滞后指标变量 d_lag1 、 d_lag2 和 d_lag3 在 95% 置信水平下均不具有统计显著性。
预言;预测;预告
让我们使用拟合的滞后变量泊松模型来预测我们之前搁置的测试数据集的命中次数。我们不应该对预测的质量期望过高。请记住,虽然这个模型比以前的 NB2 模型拟合得更好,但伪 R 平方仍然只有 16%。
我们将使用 Patsy 来雕刻出( y_test , X_test ):
**y_test, X_test = **dmatrices**(expr, strikes_data_test, **return_type**='dataframe')**print**(y_test)**print**(X_test)**
在 X_test 上进行预测:
**poisson_predictions = poisson_model_results.**predict**(X_test)**
绘制预测值和实际值:
**predicted_counts=poisson_predictionsactual_counts = y_test[**'strikes'**]fig = plt.**figure**()fig.**suptitle**(**'Predicted versus actual strike counts'**)predicted, = plt.**plot**(X_test.**index**, predicted_counts, **'go-'**, **label**=**'Predicted counts'**)actual, = plt.**plot**(X_test.**index**, actual_counts, **'ro-'**, **label**=**'Actual counts'**)plt.**legend**(**handles**=[predicted, actual])plt.**show**()**
我们得到如下的情节:

使用滞后变量泊松模型预测的与实际的撞击数(图片由作者提供)
后续步骤
我们可以尝试通过以下修改来提高滞后变量模型的拟合优度:
- 除了输出之外,还包括输出变量的前三个时滞作为回归变量。
- 包括输出变量和撞击变量的时滞值作为回归变量。
- 不要使用泊松模型,使用负二项式模型(使用 NB1 或 NB2 方差函数),并将上述种类的滞后变量作为回归变量。
此外,使用 statsmodels 提供的广义线性模型框架来构建和训练泊松或负二项式模型也很有趣。参见下面关于如何建立和训练 GLM 模型的链接。
引用和版权
书
Cameron A. Colin,Trivedi Pravin K ., 计数数据的回归分析 ,计量经济学会专论№30,剑桥大学出版社,1998 年。国际标准书号:0521635675
McCullagh P .,Nelder John A ., 广义线性模型 ,第二版。,CRC 出版社,1989,ISBN 0412317605,9780412317606
报纸
凯南 j ., 美国制造业的合同罢工持续时间,计量经济学杂志 ,第 28 卷,1985 年第 1 期,第 5-28 页,ISSN 0304-4076,https://doi . org/10.1016/0304-4076(85)90064-8。 PDF 下载链接
Cameron C. A .,Trivedi P. K ., 泊松模型 中过度分散的回归测试,《计量经济学杂志》,第 46 卷,第 3 期,1990 年,第 347-364 页,ISSN 0304-4076,https://doi . org/10.1016/0304-4076(90)90014-k .
数据集
文章中使用的制造业罢工数据集是统计软件中可供公众使用和实验的几个数据集之一,最值得注意的是,这里的是 R 包。在 GPL v3 许可下,文森特·阿雷-本多克通过vincentarelbundock.github.io/rdatasets已经可以使用 Python 访问数据集。
形象
本文中的所有图片版权归 CC-BY-NC-SA 所有,除非图片下方提到了不同的来源和版权。
相关文章
**
感谢阅读!如果您喜欢这篇文章,请 关注我 获取关于回归和时间序列分析的提示、操作方法和编程建议。**
PokéGraph 第二部分:为 TigerGraph 编写模式更改作业
如何使用 GSQL 和 pyTigerGraph 编写模式更改作业
注:本博客基于以前的博客:https://towardsdatascience . com/using-API-data-with-tiger graph-ef 98 cc 9293d 3
概观
现在我们已经创建了一个图表,有时您可能想要更改模式。也许您想要添加或移除属性,或者您想要一起添加或移除边和顶点。这称为模式更改作业。
第一部分:创建连接
注意:这里假设你已经创建了一个基本的神奇宝贝图形。如果你还没有,请关注这个博客。
在开始之前,我们需要创建一个到图表的连接。首先,确保您的图表已经开始。为此,请前往https://tgcloud.io/app/solutions。然后,确保您的解决方案“准备就绪”
您的状态可能会显示“已停止”,并带有如下所示的蓝点。

如果你在你的解决方案中看到了这一点,那么它就是“停止的”要使用它,你需要它是绿色的。
如果是这种情况,您需要转到“操作”栏。按下小方框(当您将鼠标悬停在上面时,它会显示“解决方案操作”)。按下“开始”按钮,然后等待,直到您的解决方案状态显示绿色按钮“开始”。这可能需要几分钟时间。

转到解决方案操作,然后单击开始

等到状态显示“就绪”并有一个绿点。
太好了!现在您的解决方案已经启动并准备好了,让我们开始编写模式更改作业吧!
第二部分:全局与局部顶点和边
在这里,我们将学习如何创建两种类型的模式更改作业:全局和本地。首先,我们来分析一下两者的区别。
在上一篇博客中,我们创建的顶点和边是全局的。首先,一个解决方案中可以有多个图(尽管我们现在的解决方案中只有一个图)。全局顶点和边可以在不同的图中使用。事实上,当你通过 GraphStudio 以无代码的方式创建一个新图时,它会提示你添加任何全局顶点或边。此外,对于多个图,也可以有多个用户。全局顶点只能由超级用户或全局设计者修改。在管理门户中,您可以添加用户并赋予他们不同的角色。您将看到,目前唯一的管理员只有我们(“tigergraph”用户)。

显示用户的管理门户
相比之下,局部顶点和边只存在于某个图中。管理员和设计者可以修改它们。由于局部顶点和边只在某个图中使用,所以多个图可以有同名的局部顶点和边。
现在我们知道了局部和全局顶点和边之间的区别,让我们运行两个模式更改作业:一个修改全局模式,另一个创建局部模式更改作业。
第三部分:创建全局模式变更
神奇宝贝不仅仅是它的名字。我们将向顶点添加三个属性:image(一个神奇宝贝图像的 url 字符串)、weight(一个神奇宝贝重量的整数)和 height(一个神奇宝贝高度的整数)。
由于我们想要修改全局顶点口袋妖怪,我们需要创建一个全局模式更改。要修改顶点(和边),我们需要使用关键字 ALTER,然后指定顶点(ALTER VERTEX Pokemon)。然后,我们可以选择添加或删除属性。在我们的例子中,我们想要添加属性,然后我们将列出所有必要的属性。
最终的代码将如下所示:
ALTER VERTEX Pokemon ADD ATTRIBUTE (image STRING, weight INT, height INT);
这段代码将位于一个全局 SCHEMA_CHANGE 作业中,我们称之为 update_schema。创建模式更改作业后,我们需要运行它。因此,总的来说,创建和执行模式更改作业将如下所示:
CREATE GLOBAL SCHEMA_CHANGE JOB update_schema {
ALTER VERTEX Pokemon ADD ATTRIBUTE (image STRING, weight INT, height INT);
}
RUN GLOBAL SCHEMA_CHANGE JOB update_schema
第四部分:使用模式更改作业添加局部顶点和边
现在我们需要创建更多的顶点和边,我们将这些局部化。我们将使用添加顶点和添加无向边,而不是编写创建顶点和创建无向边。除此之外,其余的语法是相同的。在顶部,我们将创建模式更改作业,并将其命名为 upd,我们将指定它用于 PokemonGraph。最后,我们将安装作业。
注意:由于这是一个模式更改作业,我们使用的是添加顶点,而不是创建顶点。
CREATE SCHEMA_CHANGE JOB upd FOR GRAPH PokemonGraph { ADD VERTEX Item (PRIMARY_ID item STRING) WITH primary_id_as_attribute="true";
ADD VERTEX Ability (PRIMARY_ID ability STRING) WITH primary_id_as_attribute="true";
ADD VERTEX Move (PRIMARY_ID move STRING) WITH primary_id_as_attribute="true";
ADD VERTEX Region (PRIMARY_ID region STRING) WITH primary_id_as_attribute="true";
ADD VERTEX LocationArea (PRIMARY_ID location_area STRING) WITH primary_id_as_attribute="true";
ADD VERTEX Location (PRIMARY_ID location STRING) WITH primary_id_as_attribute="true"; ADD UNDIRECTED EDGE POKEMON_ITEM (FROM Pokemon, TO Item);
ADD UNDIRECTED EDGE POKEMON_ABILITY (FROM Pokemon, TO Ability);
ADD UNDIRECTED EDGE POKEMON_MOVE (FROM Pokemon, TO Move);
ADD UNDIRECTED EDGE POKEMON_LOCATION_AREA (FROM Pokemon, TO LocationArea);
ADD UNDIRECTED EDGE REGION_LOCATION (FROM Region, TO Location);
ADD UNDIRECTED EDGE LOCATION_LOCATION_AREA (FROM Location, TO LocationArea);}INSTALL SCHEMA_CHANGE JOB upd
一旦我们运行了这个,我们就可以在 GraphStudio 中看到我们的图表模式发生了变化。

更新的模式
您会注意到,在 GraphStudio 中,全局顶点和边用一个小世界符号表示,而局部顶点和边没有这个符号。
第五部分:加载数据
现在我们已经更新了我们的模式,让我们提取数据来填充它。我们将从 https://pokeapi.co/的 API 中提取数据。
第 V.I 部分:添加地区、位置和位置区域
首先,让我们从 regions 和 locations 端点获取所有信息。我们将从访问区域端点和向上插入区域开始。然后,我们可以向上插入任何连接位置,并用 REGION_LOCATION 边连接区域和位置顶点。为此,我们将向上插入八个神奇宝贝区域,然后插入任何连接位置。请注意,向上插入所有位置将需要很长时间,因此如果需要,您可以添加一个限制。
for i in range(1, 9):
res = requests.get(url = f"https://pokeapi.co/api/v2/region/{i}/").json()
conn.upsertVertex("Region", res["name"], attributes={"region": res["name"]}) for ty in res["locations"]:
conn.upsertVertex("Location", ty["name"], attributes={"location": ty["name"]})
conn.upsertEdge("Region", res["name"], "REGION_LOCATION", "Location", ty["name"]) print(f"Added {res['name']}")
接下来,让我们添加更多的位置和位置区域顶点。这里,我们称之为位置端点,并向上插入一个位置顶点。接下来,我们将遍历所有连接的位置区域,向上插入位置区域折点,并用 LOCATION_LOCATION_AREA 边连接位置区域和位置折点。在本例中,我们将只插入九个位置及其所有相应的位置区域。我们可以向上插入更多位置,但加载可能需要一些时间。如果您想要向上插入更多位置和位置区域,您可以修改 for 循环。
for i in range(1, 10):
res = requests.get(url = f"https://pokeapi.co/api/v2/location/{i}/").json()
conn.upsertVertex("Location", res["name"], attributes={"location": res["name"]}) for ty in res["areas"]:
conn.upsertVertex("LocationArea", ty["name"], attributes={"location_area": ty["name"]})
conn.upsertEdge("Location", res["name"], "LOCATION_LOCATION_AREA", "LocationArea", ty["name"]) print(f"Added {res['name']}")
第五部分 II:上插口袋妖怪和相应的顶点/边
接下来,我们将从 Pokemon 端点向上插入数据。这将包括口袋妖怪的数据,类型,能力,项目,移动顶点及其相应的边缘。
让我们从口袋妖怪顶点开始。与第一次我们向上插入这个顶点的数据相反,现在我们有三个新的属性添加到属性参数中。为 id 为 I 的口袋妖怪发出请求并向上插入顶点的所有数据,如下所示:
res = requests.get(url = f"https://pokeapi.co/api/v2/pokemon/{i}/").json()conn.upsertVertex("Pokemon", res["species"]["name"], attributes={"name": res["species"]["name"], "image": res["sprites"]["other"]["official-artwork"]["front_default"], "weight": res["weight"], "height": res["height"]})
注意:因为当我们在过去的博客中向上插入数据时,口袋妖怪顶点没有图像、重量和高度属性,所以图形仍将具有这些顶点,但是现有顶点的新属性将默认为空字符串(对于图像)和 0(对于重量和高度)。
接下来,我们将增加种类、能力、物品和招式。然后,我们可以将这些顶点连接到口袋妖怪顶点。
for ty in res["types"]:
conn.upsertVertex("Type", ty["type"]["name"], attributes={"type_id": ty["type"]["name"]})
conn.upsertEdge("Pokemon", res["species"]["name"], "POKEMON_TYPE", "Type", ty["type"]["name"])for ab in res["abilities"]:
conn.upsertVertex("Ability", ab["ability"]["name"], attributes={"ability": ab["ability"]["name"]})
conn.upsertEdge("Pokemon", res["species"]["name"], "POKEMON_ABILITY", "Ability", ab["ability"]["name"])for item in res["held_items"]:
conn.upsertVertex("Item", item['item']['name'], attributes={"item": item['item']['name']})
conn.upsertEdge("Pokemon", res["species"]["name"], "POKEMON_ITEM", "Item", item['item']['name'])for mo in res["moves"]:
conn.upsertVertex("Move", mo["move"]["name"], attributes={"move": mo["move"]["name"]})
conn.upsertEdge("Pokemon", res["species"]["name"], "POKEMON_MOVE", "Move", mo["move"]["name"])
总之,在获取前 19 个口袋妖怪的循环中,我们的代码将如下所示:
for i in range(1,20): res = requests.get(url = f"https://pokeapi.co/api/v2/pokemon/{i}/").json()
conn.upsertVertex("Pokemon", res["species"]["name"], attributes={"name": res["species"]["name"], "image": res["sprites"]["other"]["official-artwork"]["front_default"], "weight": res["weight"], "height": res["height"]}) for ty in res["types"]:
conn.upsertVertex("Type", ty["type"]["name"], attributes={"type_id": ty["type"]["name"]})
conn.upsertEdge("Pokemon", res["species"]["name"], "POKEMON_TYPE", "Type", ty["type"]["name"]) for ab in res["abilities"]:
conn.upsertVertex("Ability", ab["ability"]["name"], attributes={"ability": ab["ability"]["name"]})
conn.upsertEdge("Pokemon", res["species"]["name"], "POKEMON_ABILITY", "Ability", ab["ability"]["name"]) for item in res["held_items"]:
conn.upsertVertex("Item", item['item']['name'], attributes={"item": item['item']['name']})
conn.upsertEdge("Pokemon", res["species"]["name"], "POKEMON_ITEM", "Item", item['item']['name']) for mo in res["moves"]:
conn.upsertVertex("Move", mo["move"]["name"], attributes={"move": mo["move"]["name"]})
conn.upsertEdge("Pokemon", res["species"]["name"], "POKEMON_MOVE", "Move", mo["move"]["name"]) print("Added " + res["species"]["name"])
第五部分:连接口袋妖怪和位置数据
最后,是时候弥合口袋妖怪顶点和位置数据(更具体地说,是 LocationArea 顶点)之间的差距了。这里,我们将创建两个 GET 请求:第一个是对/pokemon/{pokemon}端点的请求,以获取 pokemon 的名称。然后,我们将查看/pokemon/{pokemon}/encounters 端点,以获取 pokemon 可能位于的所有 LocationArea 顶点。最后,我们将插入一条 Pokemon 位置 _ 区域边来连接口袋妖怪和位置区域顶点。
for i in range(1, 10): res = requests.get(url = f"https://pokeapi.co/api/v2/pokemon/{i}/").json()
pokemon_name = res["species"]["name"]
res = requests.get(url = f"https://pokeapi.co/api/v2/pokemon/{i}/encounters").json( for loc in res:
conn.upsertEdge("Pokemon", pokemon_name, "POKEMON_LOCATION_AREA", "LocationArea", loc["location_area"]["name"])
第六部分:祝贺+资源
恭喜你。您创建了两个模式更改作业,一个全局作业和一个本地作业,并且将数据加载到了这个作业中!如果您想了解更多关于 TigerGraph 的信息,加入更多与 TigerGraph 相关的项目,询问关于 TigerGraph 的问题,或者只是与社区讨论 TigerGraph,请随时加入 Discord:
您可以在开发者门户网站找到更多 TigerGraph 工具:
最后,阅读官方文档了解更多信息:
https://docs.tigergraph.com/dev/gsql-ref/ddl-and-loading/modifying-a-graph-schema
PokéGraph 第四部分:使用 TigerGraph 和 Plotly Express 进行线性回归
如何使用 Plotly Express 可视化您的 TigerGraph 数据
注:本博客假设你有这里的数据:https://towards data science . com/pok % C3 % a9 graph-part-ii-writing-schema-change-jobs-for-tiger graph-d 6 E4 d 4 ABA 0
它是本的更新版本:https://shre ya-chaud hary . medium . com/linear-regression-with-a-graph-database-e 71 db 5 C1 B3 f 3。
目标
在过去的博客中,我们已经创建了我们的图表——完成了模式、加载数据和一些模式更改工作——并学习了一点 GSQL。现在,让我们通过创建一个线性回归模型将所有这些放在一起。我们将通过使用 GSQL 来使用我们的 TigerGraph 神奇宝贝数据,然后我们将使用 Plotly Express 将其放入交互式 UI 中。我们的线性回归将使用神奇宝贝的重量和高度,按类型进行颜色编码,并在悬停数据中包含神奇宝贝的名称。我们开始吧!
步骤 1:设置解决方案并导入库
首先,你需要准备好你的解决方案。正如以前的博客所显示的,在https://tgcloud.io/app/solutions中,确保你的解决方案旁边有一个绿点,并显示准备好了。
接下来,确保您安装了 pyTigerGraph。在 Colab 中,在其中一个单元格中键入以下内容:
!pip install pyTigerGraph
安装完成后,你可以将 pyTigerGraph 与 Plotly Express 和 Pandas 一起导入。
import pyTigerGraph as tg
import plotly.express as px
import pandas as pd
最后,创建与您的图表的连接:
conn = tg.TigerGraphConnection(host="https://SUBDOMAIN.i.tgcloud.io", password="PASSWORD", graphname="PokemonGraph")conn.apiToken = conn.getToken(conn.createSecret())
当然,用它们各自的值替换子域和密码。
步骤二:创建和运行查询
现在,让我们创建一个查询来获取我们需要的所有变量。为此,我们需要重量、高度、类型和名称。所有这些都是全局列表累计累加器。重量和高度将是 INT 类型,类型和名称将是 STRING 类型。最后,我将添加一个最终的全局 type_set SetAccum
ListAccum<INT> @@heights, @@weights;
ListAccum<STRING> @@names, @@types;
SetAccum<STRING> @@types_set;
记住:@@代表全局变量,@代表局部变量。
然后,对于我们的种子,我们只需抓取我们所有的神奇宝贝:
catchThemAll = {Pokemon.*};
现在,对于我们的选择查询,我们将从口袋妖怪顶点开始,经过 Pokemon 类型的边,然后在类型顶点结束。然后,在 ACCUM 子句中,我们将更新所有的累加器。
Res = SELECT t FROM catchThemAll:p-(POKEMON_TYPE:e)-Type:t
ACCUM @@heights+=p.height, @@weights+=p.weight,
@@names+=p.name, @@types+=t.type_id,
@@types_set+=t.type_id;
记住:对于累加器来说,a +=不一定意味着加一个值。可以把它看作是“激活”累加器或给累加器的计算增加一个值(可以是平均值、最大值、最小值、列表、集合等。).
最后,让我们打印出所有的累加器。
PRINT @@heights, @@weights, @@names, @@types, @@types_set;
太棒了。让我们将所有这些放在一个查询中,并使用 pyTigerGraph 安装它。
print(conn.gsql('''DROP QUERY getAttributesCREATE QUERY getAttributes() FOR GRAPH PokemonGraph { ListAccum<INT> @@heights, @@weights;
ListAccum<STRING> @@names, @@types;
SetAccum<STRING> @@types_set; catchThemAll = {Pokemon.*}; Res = SELECT t FROM catchThemAll:p-(POKEMON_TYPE:e)-Type:t
ACCUM @@heights+=p.height, @@weights+=p.weight,
@@names+=p.name, @@types+=t.type_id,
@@types_set+=t.type_id; PRINT @@heights, @@weights, @@names, @@types, @@types_set;}INSTALL QUERY getAttributes'''))
注意:安装可能需要几分钟时间。
太好了!我们快到了!现在让我们使用这些值来创建线性回归。
第三步:创建线性回归
首先,让我们运行刚刚编写的查询,并获取第一个打印函数的结果(在索引 0 处)。
att = conn.runInstalledQuery("getAttributes")[0]
接下来,我们将 att 的值赋给各个 Python 变量。
heights, weights, types, names = att["@@heights"], att["@@weights"], att["@@types"], att["@@names"]
然后,我们将创建一个彩色地图,并分配价值。在这种情况下,我将取 0-1 之间的所有值,但任何范围都可以。
colour_map = {val : num for num, val in (enumerate(att["@@types_set"]))}colours = [colour_map[i]/len(att["@@types_set"]) for i in types]
最后,我们将使用我们的数据创建一个熊猫数据框架。
d = {'height': heights, 'weight': weights, 'type': types, 'name': names, 'type_by_colour': colours}df = pd.DataFrame(data=d)
然后我们可以使用 Plotly Express 绘制它!
fig = px.scatter(df, x="height", y="weight", color="type_by_colour", title="Pokemon by Height/Weight and Types", hover_name=names, hover_data=['name', 'type'], trendline="ols")fig.show()
volia,我们得到这个线性回归!

结果线性回归
第四步:探索线性回归
似乎线性回归不是最好的。如果你将鼠标悬停在线上,你可以看到 R 是 0.468573。您还可以找到关于这条线的其他细节,例如悬停区域的重量和高度值以及这条线的方程。

通过将鼠标悬停在线上,您可以找到关于该线的更多信息。
你可以将鼠标悬停在某个点上,以获得关于口袋妖怪的详细信息。

盘旋在塞莱斯提拉上空
在这种情况下,口袋妖怪是 celesteela,体重 9.999k,身高 92 个单位。Celesteela 是钢的类型。最后,它给出了钢的色数,约为 0.27778。
如果在某个位置单击并拖动,可以放大区域。要缩小,只需双击。

放大线性回归
第五步:资源+祝贺!
恭喜你。您使用来自 TigerGraph 的数据用 Plotly Express 创建了一个线性回归!如果您想了解更多关于 TigerGraph 的信息,加入更多与 TigerGraph 相关的项目,询问关于 TigerGraph 的问题,或者只是与社区讨论 TigerGraph,请随时加入 Discord:
您可以在开发者门户网站找到更多 TigerGraph 工具:
了解如何使用 TigerGraph 编写查询:
https://docs.tigergraph.com/dev/gsql-ref/querying/select-statement
点击此处查看 Plotly Express 的线性回归文档:
神奇宝贝图第五部分:用 TigerGraph、PokéAPI 和 Plotly Dash 创建一个基本的神奇宝贝仪表盘
使用 TigerGraph 数据创建带有 Plotly Dash 的仪表板
注意:这是 PokéGraph 系列的奖金博客。
目标
在过去的博客中,我们能够用我们的 TigerGraph 数据创建可视化。现在,让我们把可视化放在一个仪表板中,我们将使用 Plotly Dash 创建它。在这篇博客中,我们将使用 TigerGraph 在 GSQL 中编写更多的查询,并使用 Plotly Express 创建一个条形图,使用 Plotly Dash 将我们的数据放在一起。我们开始吧!
第一部分:设置
第一步:开始你的解决方案
前往 https://tgcloud.io/的,导航至“我的解决方案”选项卡。确保您的解决方案在状态下显示“就绪”。


转到“我的解决方案”并检查状态是否为“就绪”图片作者。
如果您的解决方案显示“停止”,请单击“操作”下方的“解决方案操作”按钮(方框),然后在下拉列表中按“开始”。


如果您的状态显示“已停止”,请按下方框,然后按下开始按钮。图片作者。
等待溶液启动,圆点变绿。
第二步:安装并加载库
接下来,去你的 Colab 笔记本。在那里,安装并导入您的库。
我们需要导入 Jupyter Dash,Plotly Dash 的库,用于在笔记本中创建仪表板。我们还将使用 Dash 库,包括核心和 html 组件以及 Plotly Express。最后,我们将导入 Pandas 来轻松管理从 TigerGraph 到 Plotly 的数据。
!pip install -q jupyter-dash
!pip install -q pyTigerGraphfrom jupyter_dash import JupyterDash
import dash_core_components as dcc
import dash_html_components as html
import plotly.express as px
import pyTigerGraph as tg
import pandas as pd
太棒了。
第三步:连接到您的解决方案
现在,使用 pyTigerGraph,创建一个到您的图形的连接。用适当的值替换子域和密码。
conn = tg.TigerGraphConnection(host="https://SUBDOMAIN.i.tgcloud.io", password="PASSWORD", graphname="PokemonGraph")
conn.apiToken = conn.getToken(conn.createSecret())
对于我的解决方案,它看起来像这样:
conn = tg.TigerGraphConnection(host="https://pokemon.i.tgcloud.io", password="tigergraph", graphname="PokemonGraph")
conn.apiToken = conn.getToken(conn.createSecret())
太好了!现在让我们开始为我们的仪表板创建图表吧!
第二部分:创建条形图
步骤 1:编写一个查询
首先,我们将编写并安装一个简单的查询来计算每种类型的神奇宝贝的数量。为此,我们将创建一个名为 count 的本地 SumAccum 累加器,它将计算连接到每种类型的神奇宝贝顶点的数量。我们将最终返回所有的类型顶点(SELECT 语句的结果)。
# Gets number of pokémon of each typeprint(conn.gsql('''USE GRAPH PokemonGraphCREATE QUERY getTypeCount() FOR GRAPH PokemonGraph {/* Gets number of pokémon with each type */SumAccum<Int> @count;
Seed = {Pokemon.*};counts = SELECT tgt FROM Seed:s -(POKEMON_TYPE:e) -:tgt
ACCUM tgt.@count += 1;PRINT counts;}INSTALL QUERY getTypeCount''', options=[]))
步骤二:创建一个数据框架
让我们使用我们刚刚编写的查询结果,用 pandas 创建一个数据帧。首先,我们将使用 runInstalledQuery 运行查询。
results = conn.runInstalledQuery("getTypeCount")
然后,我们将结果放入 Python 列表中。
allTypes = []
typeCounts = []for types in results[0]["counts"]:
allTypes.append(types['v_id'])
typeCounts.append(types['attributes']['@count'])
最后,我们将把它放入一个数据帧中。
pokeData = pd.DataFrame(data=list(zip(allTypes, typeCounts)), columns=['Type', 'Count'])
不错!现在让我们使用数据框架来创建一个条形图。
第三步:创建条形图
现在我们将使用 Plotly Express 和 px.bar 创建我们的数据帧。
bar = px.bar(pokeData, x='Type', y='Count', title='Pokemon Type Count')
如果您想查看条形图,可以运行 bar.show()
bar.show()

条形图。图片作者。
太好了!让我们从过去的博客中重新创建线性回归,并准备好绘制它!
第三部分:重建线性回归
我们还将包括从过去的博客的线性回归,所以让我们重新创建它:
print(conn.gsql('''USE GRAPH PokemonGraphDROP QUERY getAttributesCREATE QUERY getAttributes() FOR GRAPH PokemonGraph { ListAccum<INT> @@heights, @@weights;
ListAccum<STRING> @@names, @@types;
SetAccum<STRING> @@types_set; catchThemAll = {Pokemon.*}; Res = SELECT t FROM catchThemAll:p-(POKEMON_TYPE:e)-Type:t
ACCUM @@heights+=p.height, @@weights+=p.weight,
@@names+=p.name, @@types+=t.type_id,
@@types_set+=t.type_id; PRINT @@heights, @@weights, @@names, @@types, @@types_set;}INSTALL QUERY getAttributes'''))att = conn.runInstalledQuery("getAttributes")[0]heights, weights, types, names = att["@@heights"], att["@@weights"], att["@@types"], att["@@names"]colour_map = {val : num for num, val in (enumerate(att["@@types_set"]))}
colours = [colour_map[i]/len(att["@@types_set"]) for i in types]d = {'height': heights, 'weight': weights, 'type': types, 'name': names, 'type_by_colour': colours}
df = pd.DataFrame(data=d)fig = px.scatter(df, x="height", y="weight", color="type_by_colour", title="Pokemon by Height/Weight and Types", hover_name=names, hover_data=['name', 'type'], trendline="ols")
这将导致我们的彩色图表,我们可以使用 fig.show()查看。
fig.show()

线性回归。图片作者。
现在我们有了两个图表,让我们把它们放在一个仪表板上!
第四部分:把它放在一起变成一个仪表板!
让我们最后使用 Plotly Dash 的 html 组件将这两个图表放在一起。这两个图的名字分别是 bar 和 fig,我们就这样引用。
app = JupyterDash(__name__)app.layout = html.Div([ html.H1("Pokemon Dashboard Demo"),
dcc.Graph(id='bar_chart', figure=bar),
dcc.Graph(id='lin_reg', figure=fig),])
最后,我们来运行它!
app.run_server(mode='external')
这样做的结果是链接到本地托管您的仪表板的位置。

运行服务器的结果。图片作者。
第五部分:探索仪表板
哒哒!当你点击它,你会看到两个数字和标题。

仪表板。图片作者。
Plotly 很酷的一点是,你可以将鼠标悬停在该点上以获得更多细节。例如,如果您将鼠标悬停在“normal”栏上,您将能够找到计数并获得类型。

将鼠标悬停在条形图的某个点上。图片作者。
这对于线性回归也是一样的。

悬停在线性回归的一个点上。图片作者。
第六部分:祝贺、资源和挑战
恭喜你。您现在已经创建了一个仪表板,其中有两个数字描述您的 TigerGraph 数据!
作为一个挑战,尝试用 Plotly Express 创建另一个可视化,并将其添加到仪表板中。要查看 Plotly Express 的其他可视化效果,请查看此博客:
接下来,尝试使用您选择的不同 API 创建另一个仪表板。如果您有任何问题或想要展示您的项目,请在此加入 TigerGraph Discord:
祝你好运!
使用 Matplotlib 的 Python 中的极坐标热图
使用 matplotlib 补丁构建一些漂亮的东西

极坐标热图显示风速和风向,颜色代表桶内的平均温度。(图片由作者提供)
我真的很喜欢使用 Python + matplotlib,不仅仅是因为它的简单,还因为你可以用它来创建非常干净和巧妙的图像。要放弃的一件事是使用库中预定义的绘图方法,而是查看 matplotlib 中包含的几何图形和面片。
上面的图像纯粹是用几组楔形面片创建的。它既干净又漂亮,现在我会告诉你怎么做。看这个例子的时候,想想被标准图的锁链束缚是多么的自由!
这个问题
风向和风速与平均气温有没有相关性?
这是我在 Kaggle 上看到数据集时问自己的问题:
https://www.kaggle.com/selfishgene/historical-hourly-weather-data
我想要的结果是有一个极坐标热图,其中每个楔形由该桶中的平均温度来着色。当我在网上寻找是否有什么可以产生一个漂亮干净的图像时,我就倒霉了!
我发现的主要问题是绘图软件看起来总是有点粗糙,如果我要用它来做海报,我希望它看起来更干净更简单。这就是为什么我决定用楔形物体来建造它。
代码
按照传统,我们首先导入所需的包:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap
import matplotlib.patches as mpatches
from matplotlib.collections import PatchCollection
我们正在处理几个包,让我们将它们分解开来:
numpy: 非常流行的数值运算和矩阵计算软件包
pandas: 主要用于数据帧功能和数据的加载。
matplotlib: 我们将用来生成图像的绘图包。它可以用于标准的科学绘图,但非常灵活!
matplotlib 内有一些组件:
LinearSegmentedColormap:用于创建自定义线性颜色渐变功能。
匹配:这是让我们为绘图构建原始形状的部分。
PatchCollection: 取一组补丁,将它们缝合在一起,用于绘图。
现在,我们所有的基本功能都已导入,我们可以继续加载数据文件了。对于这个项目,我使用了风向,风速,当然还有温度。
wind_direction = pd.read_csv('../input/historical-hourly-weather-data/wind_direction.csv')wind_speed = pd.read_csv('../input/historical-hourly-weather-data/wind_speed.csv')temperature = pd.read_csv('../input/historical-hourly-weather-data/temperature.csv')
上面的数据集实际上包括了许多不同城市的天气数据。因为我来自多伦多地区,我们将选择 6 作为我们的地块。我们将使用城市变量过滤掉数据,并删除任何 na 值,以确保我们有好的数据。
city = 'Toronto'wind_direction_list = list(wind_direction[city].dropna())wind_speed_list = list(wind_speed[city].dropna())temperature_list = list(temperature[city].dropna())
我们冒险的下一步是提取数据并把它转换成一种好的格式。请记住,有很多方法可以做到这一点,我不是 Python 的超级专家。我所做的就是根据原始数据生成一些列表。我将θ(极角)与风向关联,r(半径)与风速关联,temp 与温度关联。一旦生成了这三个列表,我就用它们来创建一个新的数据帧。
r = []; theta = []; temp = []for i,w **in** enumerate(wind_direction_list):
r.append(wind_speed_list[i])
theta.append(wind_direction_list[i])
temp.append(temperature_list[i])df = pd.DataFrame(list(zip(r,theta,temp)),columns =['r','theta','temp'])
接下来,我们定义我们想要的直方图的宁滨。根据这些数据,如果您选择较小的网格数(较高的网格数),您可能会得到间隙,因为在给定的区域中没有数据点。随着网格数的增加,你最终会得到一个极坐标散点图!
ntheta = 30; dtheta = 360/ntheta;
nradius = 20; dradius = max(r)/nradius;
我们也可以设置我们的颜色映射。我使用了一个漂亮的蓝色到红色的调色板,这是我从网站:https://coolors.co/
colors = ['#000052','#0c44ac','#faf0ca','#ed0101','#970005']
cm = LinearSegmentedColormap.from_list('custom', colors,N=10)cm.set_bad(color='white')
现在我们有了一个函数 cm ,我们可以调用它来生成 0-1 范围内的颜色。这意味着我们将不得不在这个范围内调整最终温度(将在下一节中完成)。
现在是代码的核心部分。如何生成将要绘制的实际补丁对象?因为我们要在画布上画画,所以我们需要确保不要在我们不想画的东西上画画。这意味着我们将从最大的半径开始向下工作。
patches = []; avg_temp = []
for nr **in** range(nradius,0,-1): **# See! Outside to Inside** start_r = (nr-1)*dradius
end_r = (nr)*dradius for nt **in** range(0,ntheta):
start_t = nt*dtheta
end_t = (nt+1)*dtheta
stripped = df[(df['r']>=start_r) & (df['r']<end_r) &
(df['theta']>=start_t) & (df['theta']<end_t)]
avg_temp.append(stripped['temp'].mean()) wedge = mpatches.Wedge(0,end_r, start_t, end_t) patches.append(wedge)
上面发生了很多事情,让我们来看一下:
- 首先我们从最大半径向下循环
- 做一个 360 度的次循环
- 找到 r 和θ范围的开始和结束位置
- 删除数据帧中的行
- 计算平均温度并附加
- 创建楔形面片并添加到面片列表中
完成后,我们就差不多完成了。现在,我们可以生成补丁的集合,并使用颜色映射函数和平均温度来单独分配补丁颜色。
collection = PatchCollection(patches,linewidth=0.0,
edgecolor=['#000000' for x **in** avg_temp],
facecolor=cm([(x-263.15 )/(303.15 -263.15 ) for x **in** avg_temp]))
请注意,我们需要通过一些真实的温度范围来缩放面部颜色。我选择了-10C 到 30C 作为我的范围,但是你可以用它来改变颜色的比例。
现在我们策划!设置大小和 dpi,并将补丁集合添加到映像中。
fig = plt.figure(figsize=(40,20), dpi=200,
edgecolor='w',facecolor='w')ax = fig.add_subplot()ax.add_collection(collection)**# Clean up the image canvas and save!**
plt.axis('equal')
plt.axis('off')
plt.tight_layout()
plt.savefig('toronto.png')
plt.show()
嘣!输出:

现在,你可以使用边缘颜色和其他因素来处理这个问题,并获得如下一组好的图像:



结论
希望你喜欢这个!如果您有问题,请添加评论。如果你认为有足够的兴趣,我可以为这个小方法做一个 pip 包。
明尼阿波利斯的警察事件

目录
简介
明尼阿波利斯以其跨州的高品质生活和密西西比河惊人的城市特质而闻名。然而,它也因 2020 年 5 月乔治·弗洛伊德 谋杀案而“声名狼藉”。
那么,在他死后,到目前为止发生了什么变化呢?该分析旨在直观显示明尼阿波利斯市自 2020 年以来的警察事件现状和趋势。
快速概述
该数据集可在“ 开放明尼阿波利斯 ”网站上获得,该网站旨在提供与明尼阿波利斯有关的社会数据。根据该网站的说法,这些数据集是免费共享的,只需最少的许可。
***# Import data sets***
incident_2020 <- read_csv("Police_Incidents_2020.csv")
incident_2021 <- read_csv("Police_Incidents_2021.csv")***# Use glimpse() on one of the dataset*** glimpse(incident_2020)

我们可能会注意到:
- 重复数据:
X、Y、centerLong、centerLat - 多余数据:
precinct、UCRCode、lastchanged等 - 检查是否有任何缺失值和不整洁的数据
选择/整理列
***# Adjust data type and concatenate*** incident_2021$precinct <- as.character(incident_2021$precinct)
incident_raw <- bind_rows(incident_2020, incident_2021)
***# Select relevant columns only*** incident_selected <- incident_raw %>% select(publicaddress, reportedDateTime,description, centerLong,centerLat,neighborhood,offense)
***# Parse datetime on reporteDateTime column***
incident_selected$reportedDateTime <- substr(incident_selected$reportedDateTime, 1,16)
incident_selected$reportedDateTime <- as.character(incident_selected$reportedDateTime)
incident_selected$reportedDateTime <- parse_datetime(incident_selected$reportedDateTime, "%Y/%m/%d %H:%M")
incident_selected <- incident_selected %>% mutate(reportedDate = format(reportedDateTime, "%Y/%m"))
***# Clean NA and zero value row*** colSums(incident_selected ==0)
incident_selected <- incident_selected[incident_selected$centerLong != 0,]

(对于publicaddress和neighborhood,我们将保留那些包含缺失数据的行,因为它们只是字符串)
用传单绘制事件图
如果你还没用过leaflet,我强烈建议在你的项目上试试!leaflet提供的一个惊人特性是构建一个交互式地图。正如您可能从下图中注意到的,只需几行代码就可以创建自动聚合数据的地图,供您在不同的级别进行检查。
*# Plot on map by leaflet*
incident_selected %>% leaflet() %>%
addProviderTiles(providers$Stamen.TonerLite) %>%
addMarkers(label = incident_selected$description,
clusterOptions = markerClusterOptions(),
lng = incident_selected$centerLong, lat = incident_selected$centerLat,
popup = paste("<b>","Neighborhood:","</b>",
incident_selected$neighborhood,"<br>",
"<b>","Address:","</b>",
incident_selected$publicaddress,"<br>",
"<b>","Time:","</b>",
incident_selected$reportedDateTime)) %>%
addMiniMap(toggleDisplay = TRUE,tiles = providers$Stamen.TonerLite)

点击此处 查看 Rpub 的互动地图

点击此处 查看 Rpub 的互动地图
警察事件趋势(2020~)
你们中的一些人可能还记得 2020 年 5 月发生的事件。是的。正是乔治·弗洛伊德的谋杀引发了全国性的示威游行和纪念仪式。最初,抗议是以和平的方式进行的,但后来变成了破坏行为。
我们来看看从警察事件的数量来看,该事件带来了什么影响。
***# Summarise by date and plot*** summarised_incident_selected <- incident_selected %>%
filter(reportedDate != "2021/05") %>%
group_by(reportedDate) %>%
summarise(num_case = n()) ***# Plot using ggplot***ggplot(summarised_incident_selected, aes(reportedDate, num_case, group=1))+
geom_col(fill='red')+expand_limits(y = 0)+ theme_tufte()+
theme(axis.text.x = element_text(angle = 60, hjust = 1, vjust = 0.5))+
xlab("") + ylab("")+
annotate("point", x = '2020/05', y = 2000, colour = "blue", size=3,shape=8) +
annotate("text", x = '2020/05', y = 2000, label = "Death of G.F", colour = "blue", vjust = -1)

- 注释显示乔治·弗洛伊德于 2020 年 5 月去世
- 显然,2020 年 5 月后至年底,警察事件总数激增
事故最多的十大社区
那么,明尼阿波利斯的哪个地区发生的警察事件最多?换句话说,哪个社区是最危险的游览地?
top_10_neighbor <- incident_selected %>%
group_by(neighborhood) %>%
count(sort=TRUE) %>% ungroup() %>%
top_n(10) %>% mutate(neighborhood = fct_reorder(neighborhood,n))
ggplot(top_10_neighbor, aes(n,neighborhood)) +
geom_point(size = 12) +
geom_segment(aes(xend = 30, yend = neighborhood), size = 2) +
geom_text(aes(label = round(n,1)), color = "white", size = 4) +
*#scale_x_continuous("", expand = c(0,0), limits = c(30,90), position = "top") +*
scale_color_gradientn(colors = palette) +
theme(axis.line.y = element_blank(),
axis.ticks.y = element_blank(),
axis.text = element_text(color="black"),
axis.title = element_blank(),
legend.position = "none")

韦斯特市中心、惠蒂尔、马西·霍姆斯是排名前三的街区
结论
- 如果你忽略了互动地图,你可以在这里找到它
- 从地图上看,大多数事件都集中在明尼阿波利斯市中心和附近地区。
- 乔治·弗洛伊德被谋杀后,明尼阿波利斯的警察事件似乎增加了
- 如果你想找一个住的地方,找除了以上 10 个地区以外的地方会更安全。
警察杀人、城市消费和暴力犯罪之间的关系
公共投资与警察暴力探析

肖恩·李在 Unsplash 上的照片
在美国,自 2013 年以来,警察平均每年要对 1,101 起报告的死亡事件负责。乔治·弗洛伊德(George Floyd)之死引发了对警察暴行和过度使用武力的抗议,这是 2020 年的一个里程碑式的时刻。抗议者的主要要求之一是进行政策改革,解除警察的经费;这个想法是,将资金从捉襟见肘、过度暴力的警察部队重新分配到改善公共和紧急服务领域,如精神健康、经济适用房和成瘾,将同时减少犯罪和警察杀人。
司法统计局 2006 年的一项研究发现,新招募的警察花在训练枪械技能和自卫上的时间比任何其他技能都多,训练时间的中位数分别为 60 小时和 51 小时;第二高的中位数是健康和健身训练,为 46 小时。警察应对各种各样的紧急情况,包括家庭暴力、吸毒、无家可归或精神疾病。如果他们的训练侧重于使用武力,那么他们就没有适当的装备来应对如此广泛的情况。
评估警察对犯罪的影响一直是犯罪学实证研究的一个挑战,大量文献对警察在场的影响有不同的结论。许多研究着眼于警察数量或巡逻警力对犯罪的影响,但我没有使用警力,而是使用警力支出作为一个城市加强警力的指标。
反对削减警察经费的批评者声称,从警察手中撤资会助长潜在罪犯的气焰,增加犯罪率。本分析的范围是通过探索性分析和具有逐步特征选择的固定效应模型来检查支出因素及其与警察杀人和暴力犯罪的关系。
数据源
林肯土地政策研究所财政标准化城市数据库包含 1977 年至 2017 年美国 150 个最大城市的政府服务收入和支出。
美国联邦调查局统一犯罪报告警察部门自愿报告的每 10 万人中的犯罪实例和比率,这项工作使用 1985 年至 2014 年。
绘制警察暴力包括 2013 年至 2020 年超过 8000 起警察杀人事件;从三个众包数据库汇编而成。
数据处理
在这项工作中,治安部门报告的犯罪率被删除,因为它们代表的是县而不是市。政府支出包括城市、州和联邦政府的支出,以实际人均美元表示(2017 年)。支出分为 5 类:教育、卫生、交通、治安和环境/住房。UCR/FiSC 和 MPV/FiSC 数据集的组合中分别有 119 个和 138 个城市。因为 UCR 的数据是由警察机构自愿提交的,所以可能会有一些报道偏差。⁴
探索性分析
在这一节中,在深入研究合并数据之前,我们将对单个数据集进行简要分析。
2013 年至 2020 年间的警察杀人事件
从图 1 中我们可以看到,自 2013 年以来,警察杀人事件相对稳定。谋杀案最少和最多的月份是 2013 年 2 月和 2020 年 5 月,巧合的是这两个月也是乔治·弗洛伊德死亡的月份。平均而言,与秋季和冬季相比,夏季和春季的杀人事件更多。

图一
图 2 中的直方图告诉我们,年轻人被警察杀害的比率高于其他年龄组。21 至 35 岁之间的受害者占所有警察杀人案的 44%。虽然大多数是年轻人,但我们可以看到,似乎没有任何年龄组完全逃避我们警察部队的暴行。

图 2
图 3 中的条形图详细列出了报告杀人事件最多的 10 个警察机构。这 10 家机构占同期所有警察杀人案件的 9.6%。令人担忧的是,LAPD 和洛杉矶县警察局都是排名前三的机构。这份名单并不令人惊讶,他们大多是人口稠密的城市中心,但菲尼克斯第二大杀人案让我质疑他们的训练技术,因为纽约市的人口是它的 5 倍多。

图 3
1977 年至 2014 年间的政府支出
左边的图 4 显示了 1977 年至 2014 年不同政府服务的人均支出占总支出的百分比。教育牢牢占据首位,平均支出比例最高,为 34.56%。警察支出从 1977 年的平均 6.74%稳步增长到 2014 年的 8.98%。平均而言,各城市在资金分配的优先顺序上似乎是一致的。
图 5 显示了变异系数(CV ),它被用作离差的度量,以表示相对于平均值的可变性。较高的 CV 值表明在某一特定年份不同城市间变量的差异较大。例如,医疗保健支出的 CV 随着时间的推移而增加,这意味着城市之间在支出占总支出的比例方面存在很大差异。总的来说,城市在如何分配资金方面没有太大的变化;尤其是治安和教育支出一直保持高度一致。


图 4 和图 5
下面的条形图显示了在一段时间内花费最高比例的 10 个城市。佛罗里达州有三个城市进入前 10 名。劳德代尔比排名第二的城市多花 0.8%的预算在警察身上。

图 6
1985 年至 2014 年间的犯罪率
暴力犯罪率从 1993 年每 10 万人 1074 起暴力犯罪的峰值下降到 2014 年每 10 万人 520 起暴力犯罪,下降了 48.42%。总的来说,就犯罪而言,美国比以往任何时候都更安全。

图 7
下面的散点图显示了暴力犯罪率和人口之间的密切联系。有趣的是,在人口密集的城市,犯罪率更高,但它们并不出现在那些将很大一部分开支用于维持治安的城市中。请记住,关联并不意味着高人口导致暴力犯罪或相反。(纽约市和洛杉矶的平均人口分别为 776 万和 366 万,平均犯罪率分别为 1,262 和 1,410,为了便于查看,这两个城市从图中删除了,但它们仍然为趋势线提供了权重)。

图 8
犯罪率和警察杀人与支出合并
下面的图是组合数据集的结果;它们揭示了犯罪率、警察和教育支出以及警察杀人之间的联系。1985 年至 2014 年和 2013 年至 2017 年的犯罪率和支出以及杀人和支出的合并数据。为了合并多个时间序列,我使用了指定日期范围内每个城市的平均值。
左边的图 9 和图 11 显示,平均而言,一个城市的警察支出,作为其总支出的比例,与暴力犯罪率和警察杀人都呈正相关,相关系数分别为 18.68%和 16.09%。与此同时,图 10 和图 12 显示,教育支出与犯罪率的相关性为-31.60%,与杀人的相关性为-24.39%。这告诉我们,我们可以期待发现,在教育上花费更多预算的城市,其暴力犯罪率和警察杀人事件更低。


图 9 和 10


图 11 和 12
从这一探索性分析中可以得出一些重要的结论。
- 自 2013 年以来,警察杀人事件每年都在发生
- 随着时间的推移,城市的资金分配相对稳定
- 警察支出与暴力犯罪和警察杀人呈正相关,而教育支出则相反
- 暴力犯罪大大减少了
经验分析
固定效应模型被用来估计政府支出分配对暴力犯罪和警察杀人的影响。固定效应之间的模型是根据城市的平均值估计的,因此去除了由于城市内部随时间变化而产生的信息,这比长期的内部效应更可取。⁵
此外,我对 trajectories⁶的纵向数据进行了 k-means 聚类,以创建一个变量,该变量将使用城市的平均人均总支出来解释不同增长率的影响。下表详细介绍了 4 个集群及其 1985 年至 2014 年的增长率、平均支出以及集群中包含的城市。第四组仅包括一个城市,华盛顿特区,其花费增加了一倍多。

图 13
图 14 中的模型使用教育、福利、治安和环境/住房方面的人均支出作为暴力犯罪率的回归变量。有趣的是,教育、治安和环境/住房之间存在正相关,尽管警察支出变量的强度要高得多。平均而言,人均警察支出每增加 1 美元,每 10 万人的暴力犯罪就会相应增加 1.89 起。此外,福利支出与人均福利支出呈负相关,人均福利支出每增加 1 美元,每 10 万人的暴力犯罪就会相应减少 0.86 起。所创建的聚类变量不包括在该模型中,因为它会引入多重共线性,因为独立变量是人均支出,而不是总支出的比例。

图 14
下一个模型使用支出率而不是人均支出,所以现在可以包括聚类。事实证明,教育支出在任何层面都不具有统计学意义。我们看到犯罪率与福利和交通负相关,同样,警察支出与犯罪率正相关。平均而言,如果一个城市在警察上的预算增加 1 个百分点,那么每 10 万人的暴力犯罪就会相应增加 69.49 起。聚类表明,平均支出更多、增长率更高的城市与更高的暴力犯罪率相关联。

图 15
现在杀人被用作因变量,而不是犯罪率。在任何置信水平下,唯一具有统计意义人均支出项目是警察支出。这一模型可以解释为,平均而言,警察支出每增加 10 美元,警察杀人事件就会相应增加 0.1 起。最初,这似乎不是一个很大的影响,但当它在人类生活中被权衡时,它是很重要的。此外,因为警察支出是唯一具有统计意义的原始支出,我相信这个模型是有用的。

图 16
将自然对数转换应用于之前模型中的两个变量发现,平均而言,人均警察支出增加 1%会导致警察杀人增加 1.20%。为了应用该变换,将常数 0.5 添加到杀人事件中,这样一年中没有杀人事件的城市仍将包括在模型中。

图 17
我们的最终模型使用总支出的百分比来预测自然死亡记录。结果显示,教育与杀人记录呈负相关,而福利和警察则呈正相关。平均而言,警察支出占所有支出的百分比每增加 1 个百分点,杀人案件就会增加 7.0%。虽然福利有一个积极的关联,但这可能与围绕阶级和种族的政策讨论有关,这超出了本工作的范围。

为了简洁起见,我没有讨论完整的模型构建过程,如果您对它们是如何构建的感兴趣,请联系我们。因此,实证分析的主要收获是:
- 无论是人均支出还是占支出的百分比,警察支出都与警察杀人和暴力犯罪率呈显著正相关
- 发展越快的城市犯罪率越高
- 福利支出与暴力犯罪率呈负相关,与警察杀人呈正相关
结论
这项工作试图探索取消对警察的资助而支持其他政府服务的效果。探索性数据分析以及固定效应模型用于探索地方支出、警察杀人和暴力犯罪之间的关系。警察经费被发现与暴力犯罪和警察杀人都有积极的关系,甚至当其他服务有积极的关系时,警察支出的影响超过了它们。这一分析的结果认为,取消对警察的资助可能是减少犯罪和警察杀人的一个可行的解决办法。
局限性包括缺乏警方杀人数据和 UCR 的自愿性质。如果所有警察部门都被授权提交所有犯罪及其杀人和过度使用武力事件的数据,那么就可以探索更多的解决方案。另一个限制是这项工作不包括来自县级部门的数据,进一步的研究应该包括县级分析。此外,未来的工作应包括引入变量,如多样性指数和人口密度。
这是一个一般的支出分析,一个更有针对性的检查,比较了波特兰等城市,波特兰刚刚实施了精神健康第一反应 team⁷,这可能为取消警察拨款以支持此类替代方案提供了一个强有力的论据。
引文
[1]:里维斯,B. (2009)。州和地方执法培训学院,2006 年(美国,司法部,司法项目办公室)。DC,华盛顿:美国司法部,司法项目办公室,司法统计局。https://www.bjs.gov/content/pub/pdf/slleta06.pdf
[2]:史蒂文·莱维特。(1997 年),《利用警察雇用中的选举周期估计警察对犯罪的影响》,《美国经济评论》,87 年,第 3 期,第 270-90 页,https://econ papers . repec . org/RePEc:AEA:AEC rev✌️87:y:1997:I:3:p:270-90。
[3]:迪特利亚,拉斐尔,和沙尔格罗斯基,埃内斯托。(2004),警察能减少犯罪吗?使用恐怖袭击后的警力配置进行估计,《美国经济评论》,第 94 卷,第 1 期,第 115-133 页
[4]:莱维特,S. D. (1998 年)。犯罪报告与警察的关系:使用统一犯罪报告的意义。定量犯罪学杂志,14 (1),61–81。https://doi.org/10.1023/A:1023096425367
[5]:牛角包,y .,,米洛,G. (2008)。R 中的面板数据计量经济学:plm 包。统计软件杂志,27 (2),1–43。http://dx.doi.org/10.18637/jss.v027.i02
[6]: Genolini C,Ecochard R,Benghezal M,Driss T,Andrieu S,Subtil F (2016) kmlShape:一种根据形状对纵向数据(时间序列)进行聚类的有效方法。PLoS ONE 11(6): e0150738。https://doi.org/10.1371/journal.pone.0150738
警察、交通站、数据和比赛
变更数据
通过警察交通堵塞数据探索两个加州大都市之间不平等的差异。
安德烈·普里斯汀斯基帮助研究这个项目。

2021 年 4 月 20 日下午 1 点 30 分,前警官、现已被判谋杀罪的德里克·肖万被判犯有二级无意谋杀、三级谋杀和二级过失杀人罪,此前一起事件导致了受害者乔治·弗洛伊德的死亡。这一死亡事件继续引发广泛的公众反弹,并成为国际运动的先驱,呼吁警察改革。这一信念,一个有许多原因的标志性案例,支撑了许多公民对美国警察机构的方法和行动的共同时代精神。
有大量关于治安偏见的社会学根源的研究,但是,为了本文的目的,我将关注数据并探索两个主要的加州城市“美国最美好的城市”圣地亚哥和“黄金之城”旧金山之间交通中断的不平等。
作为我研究和检查上述统计数据的结果,我的发现突出了一个看似明显的种族差异。在圣地亚哥和旧金山,西班牙裔美国人被搜索的可能性分别比白人高出 2.86%和 4.60%。对于黑人司机,这一比例在各城市分别上升到 6.38%和 13.10%。尽管发现违禁品或逮捕司机的机会没有增加,但停车搜查中的这种差异仍然存在,黑人和西班牙裔司机在搜查后持有违禁品或被捕的比率都较低。
实验设置
在进行这种统计检查时,重要的是要确保收集的数据是准确和全面的,这样才不会歪曲一个像美国警察系统中的种族偏见这样微妙和有争议的问题。幸运的是,斯坦福大学的开放警务项目从我们手中拿走了大量数据收集的负担。该项目汇编了超过 2 亿份地方和州警察局的记录,旨在提供美国交通堵塞的准确和全面的记录。该数据集中包括我们重点研究的两个主要城市:
● 圣地亚哥与从 2013 年 12 月到 2017 年 3 月共发生 383,027 起停工事件。
● 三藩市与2006 年 12 月至 2016 年 6 月 905,070 次停工。
使用这两个数据集的交集,我们得到 2014 年 1 月至 2016 年 7 月圣地亚哥的大约 300,000 次停工和旧金山的 200,000 次停工,总计 525,581 次。该数据包括 21 个参数,范围从车辆类型到主体人口统计和搜索到发布的逮捕。
奇偶性
为了彻底探索这两个加州大都市之间的种族不平等,必须引入一种新的方法来评估决策系统中应用的公平概念。这种均等性度量,如其所定义的,代表了一种简单的观察标准,并有助于探索决策系统(如警察交通拦截)可能如何产生偏差。具体来说,本文将关注下面公式中定义的人口统计均等。

人口统计均等作为一个公式,其中:C 是分类,A 是显著群体(种族),a/b 代表显著群体内的群体。致谢:亚伦·弗兰科尔。经作者许可重新发布。
在警察停工的情况下,满足这一均等措施并不意味着决策系统、警察是公平的,而是均等措施代表了一个观察结果的镜头,尽管这有助于揭示被观察系统内的偏见。更具体地说,人口统计的均等性代表了在交通堵塞后,当对种族进行调整时,所进行的搜索的均等比例。这种方法,加上评估分类器性能的传统指标,有助于量化两个相似城市的成功或失败,而这两个城市的警察机构却大相径庭。
我们学到了什么?
基于数据来源,有许多有趣的结果需要探索、讨论和理解。值得注意的是,虽然警察截停率可能存在偏差,但本文分析了汽车被截停后出现的偏差,因为虽然警察首先截停的人可能存在偏差,但在许多情况下,汽车的型号、形状、年龄和其他因素使警察无法确定他们截停的司机的种族或民族。一旦他们的车被拦下,警察与司机面对面,那么有意识或无意识的偏见就可以以足够确定的程度表现出来,以供分析。考虑到这一点,图 1 强调了按城市和种族/民族划分的警察停工百分比。

图 1:圣地亚哥和旧金山按种族划分的停工情况。图片作者。
如图 1 所示,与圣地亚哥相比,旧金山的驾驶人口更加多样化,圣地亚哥的大多数司机都是白人或西班牙裔。在本分析的背景下,图 1 用于表示每个城市道路上驾驶员的种族或民族分类,因为很难量化驾驶到停车阶段的偏差。具有严格科学意义的是交通中断后会发生什么。警察是选择搜查汽车还是允许公民继续他们的工作?图 2 突出显示了停止后与种族/民族相关的搜索百分比。

图 2:按种族/民族对圣地亚哥和旧金山的搜索细分。图片作者。
如图 2 所示,黑人司机被搜查的比例明显高于其种族或民族同行。很明显,圣地亚哥和旧金山警察局的搜查率存在差异。这种差异在黑人司机中最为明显,然而,西班牙裔司机也有类似的趋势。
鉴于这两个城市的搜查率形成鲜明对比,有人可能会认为,黑人或西班牙裔司机更有可能因某种原因被搜查,这种原因可能包括涉嫌持有违禁品,也可能是犯下重大盗窃汽车等现行犯罪。当考虑停止后的停止率,或"命中率"时,这一论点是站不住脚的,如图图 3 所示。

图 3:按种族/民族分列的圣地亚哥和旧金山停车后的逮捕情况。图片作者。
在圣地亚哥和旧金山,黑人司机被搜查后逮捕的可能性分别只有 20.58%和 15.15%。这与白人司机因所在城市不同而分别有 42.00%和 33.38%的几率被捕形成鲜明对比。就总逮捕率而言,圣地亚哥县优于旧金山,逮捕了 30.10%的被搜查司机,而旧金山为 21.31%。当考虑车辆停车和搜查后的违禁品命中率时,也可以观察到这种趋势,如图图 4 所示。

图 4:按种族/民族划分的圣地亚哥和旧金山停站后发现的违禁品。图片作者。
黑人和西班牙裔司机再次代表了拥有违禁品命中率最低的两个人口统计数据,白人和亚洲/太平洋岛民司机在圣地亚哥县的违规可能性略高,在旧金山的违规可能性明显更高。此外,旧金山私藏违禁品的命中率是圣地亚哥的两倍,分别为 18.21%和 9.16%。
黑人或西班牙裔司机与白人或亚洲/太平洋岛民司机之间的差异提出了一个有趣的问题;白人或亚裔/太平洋岛民司机比黑人或西班牙裔司机更有可能持有违禁品或犯罪吗?显然答案是否定的。相反,这种差异加剧了圣地亚哥警察局和旧金山警察局之间的不平等。
人口均等
可以根据计算的流行率来评估人口统计的均等性,流行率是在按种族/族裔的总人口中按种族/族裔进行搜索的决定。这与图 5 相当,在图 5 中,一个真正公平的分类器会根据人口命中率在种族/民族之间平均分配搜索。在图 5 中,包含了圣地亚哥的红色参考和旧金山的蓝色参考,以举例说明完全相等的搜索率是什么样子。

图 5:圣地亚哥和旧金山停车后按种族/民族划分的逮捕人数,包括每个城市的公平阈值。图片作者。
搜查率的这种差异表明,当选择搜查白人或亚洲/太平洋岛民司机时,官员们更依赖于压倒性的怀疑或重要证据,因此,在这些人口统计中可以看到更高的逮捕率和走私率。这与西班牙裔和黑人的人口统计形成对比,他们被搜查的次数更多,但被逮捕的次数更少,这表明存在一个影响警察的额外混杂因素,例如有意识或无意识地偏向于搜查上述人口统计。
这一发现反映了分别在圣地亚哥和旧金山进行的类似研究的结果,例如 SDSU 一项关于圣地亚哥警察的研究“从种族角度看交通执法”,该研究发现:
**我们的分析中明显的停车后差异表明,在官员的决策中存在隐性偏见
圣地亚哥和旧金山

圣地亚哥“美国最好的城市”的夏天。阿米尔·巴舍尔在 Unsplash 上拍摄的照片
很明显,对于圣地亚哥和旧金山来说,警察在交通堵塞搜索中存在不同程度的偏见,这可能是整个警察部门更大偏见的基础。这些县的偏见没有被孤立地研究,而是被串联起来研究的部分原因是,通过研究这些如何相互对比,可以推断出这种不平等的可能解决方案。与旧金山相比,圣地亚哥在寻找“合适”的汽车方面做得更好,因为这些汽车包含了逮捕的理由。圣地亚哥进行这些搜索的命中率几乎比旧金山高 10%,尽管进行交通堵塞搜索的比例更低,为 4.27%,而不是 4.56%。此外,虽然不是最佳的,但圣地亚哥在如何执行这些搜索方面的偏见明显更少,种族歧视比旧金山县更少。
研究的局限性
虽然人们做了很多努力来探索警察拦截搜查中的潜在偏见,但这项研究有几个局限性值得一提。这些限制包括但不限于:
- 数据报告:军官是否正确推断了种族并正确记录了遭遇?
- 假释作为一个因素:如果司机目前处于假释期,警察被鼓励搜查车辆,不管是否有嫌疑。
- 搜查原因:警察搜查汽车需要合理的怀疑,理想情况下,这可以在结果中进行调整,但是许多警察选择不记录他们为什么进行搜查的原因。
- 旧数据:使用的数据是过时的(2014-2016 年),其中许多趋势可能已经被理解并正在改进。
最后的想法

旧金山黄金城。照片由克里斯·莱佩尔特在 Unsplash 拍摄
正如从圣地亚哥县警察局和旧金山县警察局收集的数据所证明的,存在与被搜查的黑人和西班牙裔司机不成比例的数量相关的潜在混淆变量。如图所示,这一变量与违禁品发现率或逮捕率无关,但很可能与警察自身有意识或无意识的偏见有关。通过将这两个城市放在一起看,我们可以看到圣地亚哥虽然仍然表现出偏见的迹象,但在选择“正确”的汽车进行搜索方面表现得明显更好,同时也表现出较低的种族偏见水平。然而,这种探索并非没有限制,因为许多数据是不一致或不完整的。此外,正如我们试图通过这篇文章探索的那样,警察停车和种族偏见是非常复杂的问题,有无数的警告、细微差别和混淆因素,这些因素有时在数据中没有提及。这种对与车手比赛相关的警察停车搜索的探索代表了对数据的分析,而不一定是实地情况。尽管如此,我希望在探索这个话题时,我可以提高对这些潜在偏见的认识,并鼓励美国县警察局更有力地收集数据,以更好地探索、分析和修正警务机构中目前正在社区中产生冲击波的不成比例的种族偏见。
来源
查宁,j .,威尔士,m .,& Nurge,D. (2018)。从种族角度看交通执法:加州圣地亚哥停车后结果的序列分析。*《刑事司法政策评论》,29*(6–7),561–583。土井:10/08871 . 48888888881**
公平性&算法决策。(未注明)。2021 年 5 月 10 日检索,来自https://afra enkel . github . io/fairness-book/content/03-harms . html
公平性&算法决策。(未注明)。2021 年 5 月 10 日检索,来自https://afra enkel . github . io/fairness-book/content/05-parity-measures . html
埃里克·李文森。德里克·肖万被判谋杀乔治·弗洛伊德的三项罪名成立。2021 年 4 月 21 日,www . CNN . com/2021/04/20/us/Derek-chauvin-trial-George-Floyd-considerations/index . html。
斯坦福开放警务项目。(未注明)。检索于 2021 年 5 月 10 日,来自https://openpolicing.stanford.edu/data/
加州执法停止种族差异。(2020 年 12 月 03 日)。检索于 2021 年 5 月 10 日,来自https://www . ppic . org/blog/race-alignments-in-California-law-enforcement-stops/
所有使用的图片要么是我自己创作的,要么是经作者明确许可使用的。每个图片下都有作者资料的链接。
策略和价值迭代
婴儿机器人强化学习指南
强化学习简介:第 3 部分

[所有图片由作者提供]
介绍
机器人宝宝一直在稳步回到妈妈身边。迄今为止,他已经:
- 克服土匪问题给自己充电
- 解决了预测问题来评估一个环境中状态的相对值,让他逃离了简单的网格关卡。
- 了解如何将环境表示为马尔可夫决策过程(MDP) 并使用贝尔曼方程进行评估。
在下一部分中,我们将考虑控制问题,并研究如何从对环境状态值的估计中找到最佳策略。
密码
本文相关的 Jupyter 笔记本 可以在 Github 上找到。这包含设置和运行下述级别和算法所需的所有代码。
或者,您可以在 Binder 上运行 Jupyter 笔记本来阅读本文:
https://mybinder.org/v2/gh/WhatIThinkAbout/BabyRobot/5910c3feb8d8eb525ec79d7f2d257dc326eff85f 
简单网格级别
机器人宝宝发现自己在一个非常小的房间里,所以逃离这里应该不是太大的挑战。唯一的缺点是屋顶一直在漏水,正如我们在上一部分查看 MDPs 时看到的那样,机器人宝宝不喜欢潮湿。水坑拖慢了他的速度,并可能导致他打滑。

图 1:简单的网格级别。水坑需要更长的时间来移动,并可能导致打滑。
我们定义为:
干方块:
- 花一个单位的时间通过,所以当移动到一个没有水坑的方块时会得到-1 的奖励。
- 婴儿机器人在没有水坑的情况下不会打滑,所以总会达到目标状态。
小水坑:
- 通过要花两倍的时间,所以踏入其中一个会得到-2 的奖励。
- 在小水坑里打滑的概率是 0.4,所以有 0.6 的时间你会达到目标状态,剩下的时间你会打滑并最终处于其他可能的状态之一。
大水坑:
- 花 4 倍的时间通过,所以当进入一个包含一个大水坑的方块时奖励-4。
- 打滑的几率会增加,所以你只能以 0.4 的概率达到目标。因此有 0.6%的概率,你会处于另一种可能的状态。
这让问题变得稍微复杂了一些,因为这不再仅仅是选择你想搬到哪个州并获得固定奖励的问题。现在,当选择一个动作时,机器人宝宝可能会移动到一个不是目标状态的状态,而不是收到他所在状态的奖励。

图 2:机器人宝宝想向东朝出口移动,但是在一个大水坑里,所以有可能滑向另一个州。转移到每个可能的下一个状态的转移概率用蓝色表示。每次转换获得的奖励以红色显示。
当打滑确实发生时,通过将打滑的总概率除以可能的非目标状态的数量来确定结果状态。因此,在上面图 2 所示的例子中,机器人宝宝目前正坐在一个大水坑中,想要向东朝出口移动,可能会出现以下状态和奖励:

表 1:图 2 的转移概率——从中间的正方形开始,想向东移动。
政策评价
对于简单的网格级别,由于我们有关于系统的完整信息,知道它的所有转移概率和回报,我们可以使用动态编程将贝尔曼方程转变为更新函数。然后可以迭代地应用这些来计算状态值。
贝尔曼期望方程,给出状态值' s '当继策'【π'由下式给出:

等式 1:贝尔曼期望等式给出了政策π下的状态值
其中:
π (a|s) 是在状态 s 下采取行动 a 的概率。
p(s ',r|s,a) 是在状态 s 开始并采取行动a 时,移动到下一个状态' s’并获得奖励 r 的概率。
r 是采取此动作后获得的奖励。
γ 是贴现因子。
【v(s ')】是下一个状态的值。
一个状态的值由所有行为的概率之和,乘以下一个状态和奖励的概率之和,再乘以获得的奖励和你最终所处状态的贴现值给出。
作为一个更新等式,它可以写成:

方程 2:表示为更新函数的贝尔曼期望方程。
这里唯一的区别是,在迭代' k+1 '时,状态' s 的值是使用在前一次迭代' k '时已经计算的下一个状态的值来计算的。因此,对一个州的价值的新估计是从以前的估计中得出的。当一个估计基于另一个估计时,这被称为 引导 。
在上面图 2 所示的例子中,婴儿机器人想要选择从中央广场向东移动的动作,我们使用了确定性策略。换句话说,对于每个州,我们的政策将明确定义应该选择哪个行动。因此,将有一个单一的行动,选择概率为 1,所以上述等式中的行动总和将会消失。
类似地,当使用迭代策略评估时,通常从所有初始状态值估计设置为零开始,因此下一个状态的贴现值也将在第一次迭代中消失。
因此,对于位于网格中心的州,即机器人宝宝当前所在的位置,可以通过对上表中显示的概率和奖励求和来给出对该州价值的第一次估计。所以,对于在这个州选择东方行动的策略,这个州的价值的第一个估计是:

如果不是在该州选择东动作,而是策略定义应该采取北、南或西动作,则州值的第一估计值将由下式给出:

根据这些不考虑未来回报的第一状态估计(因为所有下一状态值当前都为零),选择向西移动的策略具有最小的负状态值估计。这是因为它的目标状态是一个干燥的正方形,在可能的状态中,它给出了最小的负直接回报。这个估计还不包含任何关于未来奖励的信息,这些信息是我们在网格中找到最佳路径所必需的。此刻,估计的状态值仅给出将保持机器人宝宝最干燥的信息!
上面显示的状态值估计值是为确定性策略计算的值,其中为状态定义了一组操作。如果我们改为使用随机策略,其中行动是随机选择的,那么这些状态值估计值中的每一个将简单地乘以采取行动的概率(贝尔曼方程中的π (a|s) 部分),然后我们将结果相加。
对于中间的正方形,在选择任何动作的概率相等的情况下(因此每个动作被选择的概率为 0.25),第一个估计的状态值将是:

在所有情况下,这个估计值都是在当前政策下这个状态有多好的度量。为了找到最佳状态,进而让我们找到通过网格的最佳路线,我们需要对网格中的所有其他状态重复我们的计算(通常称为对所有状态进行扫描)。对于随机策略,其中一个状态中所有可能的动作都是同样可能的,这给了我们如下所示的状态值估计:

图 3:在随机策略的第一次策略评估迭代之后的状态值估计,其中所有可能的行为(蓝色箭头所示)都是同样可能的。(注:数值四舍五入到小数点后 1 位)。
现在我们已经有了所有状态的初始估计,我们可以使用这些作为下一个状态值,v(s’),在贝尔曼更新方程的下一次迭代中。在策略评估的每次迭代中,策略、转移概率和移动到每个状态的回报保持固定,因此改变的只是估计的下一个状态值。
因此,在下一次迭代中,在折扣因子为 1 的情况下,中心正方形的每个可能动作的估计状态值变为:

对于随机策略,每个动作都是同样可能的,状态值是:

对所有状态重复这一过程,得到我们的第二组估计值,这一次,估计的状态值考虑了下一个状态:

图 4:在随机策略的第二次策略评估迭代之后的状态值估计,其中所有可能的行为(蓝色箭头所示)都是同样可能的。(注:数值四舍五入到小数点后 1 位)。
如果我们重复这个过程并运行到收敛(或者至少直到状态值的变化低于某个小的阈值),那么我们最终得到如图 5 所示的最终状态值。在这种情况下,收敛发生在 234 次政策评估迭代之后。

图 5:随机政策的政策评估收敛后的最终状态值估计,其中所有可能的行动都是同样可能的。对于阈值 0.001,在 234 次迭代后出现收敛。
最后,如果我们现在根据这些值贪婪地行动,在每个状态中选择移动到最高值(最小负值)的下一个状态的行动,我们得到以下策略:

图 6:根据估计的状态值贪婪地行动形成的最优策略。
对于这个非常简单的层次,在一次策略评估之后,根据状态值贪婪地行动所形成的这个策略是最优策略。对于任何状态,如果机器人宝宝采取该策略指定的行动,然后继续遵循该最优策略,他的期望收益将高于遵循任何其他策略给出的行动。
对于有限马尔可夫决策过程(MDP ),其中我们有关于状态转换和回报的全部信息,我们可以使用动态规划将贝尔曼方程转变为一组更新规则。然后,这些可以用于计算当前策略下的状态值。
广义策略迭代(GPI)
对于我们上面考虑的非常简单的网格级别,使用初始随机策略,我们能够通过运行策略评估的单次迭代来找到最佳策略,然后通过根据计算的状态值贪婪地行动来改进策略。
实际上,这并不总是可能的。相反,策略评估和策略改进需要运行多次——这种技术被称为【GPI】。
在 GPI 下,策略评估用于计算当前策略的状态值。然后,通过对这些价值的贪婪行为,应用策略改进。这改进了当前的策略,并为下一次迭代创建了策略。通过反复应用这种策略评估和改进的循环,策略越来越接近最佳策略,如下所示:

图 7:一般化的策略迭代——重复地应用评估和改进,向最优策略前进。
在广义策略迭代的每一步,将评估当前策略并改进策略。在每次策略改进之后,先前计算的状态值不再有效,因此该过程再次开始。最终这个过程会收敛于最优策略,这也意味着我们会找到最优价值函数。
策略迭代
广义策略迭代算法的不同之处在于它们如何交错评估和改进步骤。在策略迭代中,它等待每一步完成后再开始下一步。因此,在每次迭代中,它只会在策略评估收敛后更新策略。
我们可以通过在简单的级别上扩展来测试策略评估,添加更多的状态、水坑和墙来增加挑战。这个新级别如下所示:

图 8:测试策略迭代的扩展级别。
此外,我们将从随机策略切换到使用确定性策略。在这个策略中,如图 9 所示,为每个状态指定了一个操作。

图 9:测试策略迭代的初始确定性策略。
由于某些操作会导致循环状态,并且没有通向退出的路径,因此策略评估将永远不会收敛。因此,要运行策略评估,我们要么需要使用</state-values-and-policy-evaluation-ceefdd8c2369#de56>折扣奖励,要么将评估步骤限制为固定的迭代次数。
选择贴现因子方法,并应用值 0.9,策略评估在 75 次迭代中收敛。有了这些生成的状态值,我们就可以积极行动,应用策略改进来生成新的策略。这给出了状态值和新策略,如下面的图 10 所示。

图 10:第一次运行策略迭代后的状态值和新策略。
在策略迭代的第一次运行之后,该策略比初始策略更好,并且现在具有通向退出的最右边的状态。然而,仍然没有从开始状态到退出状态的路径。因此,我们需要运行另一轮策略迭代。收敛状态值和新的改进策略如下所示:

图 11:第二次运行策略迭代后的状态值和新策略。
第二步策略迭代后的策略比上一步略好,但仍未找到最优策略。为此我们需要多跑几步。下面显示了这些,以及每次运行策略评估时达到收敛所需的迭代次数(右边显示的' Conv 值)。可以看出,经过 5 步策略迭代,达到最优策略。

图 12:运行策略迭代,直到发现最佳策略。“Conv”是策略评估收敛所需的每步迭代次数。
在上图中,请注意在每个策略迭代步骤之后,达到收敛所需的策略评估迭代次数是如何减少的。这是因为在开始下一次策略评估时,会保留以前策略的收敛状态值。由于新策略基于以前的策略,因此它将具有相似的状态值。因此,使用以前策略的值大大减少了对新策略运行策略评估所需的迭代次数。
策略迭代采用初始策略,对其进行评估,然后使用这些值来创建改进的策略。然后对新生成的策略重复这些评估和改进步骤,以给出更好的策略。这个过程一直持续到,最终,我们得到了最优策略。
价值迭代
正如我们已经看到的,策略迭代评估一个策略,然后使用这些值来改进该策略。重复这个过程,直到最终达到最优策略。因此,在最优策略之前的每次迭代中,必须对次优策略进行全面评估。因此,在试图找到最佳策略时,可能会浪费很多精力。
再次查看非常简单的网格级别上的策略评估,如下面的图 13 所示。注意蓝色箭头在每次迭代中是如何变化的。这些显示了一个潜在的策略,它是根据当前迭代中可用的状态值贪婪地行动而形成的。

图 13:策略评估,显示了如果我们在每次迭代中贪婪地行动,策略将如何被更新。
以前,我们对这一级别运行策略评估,直到完全收敛,进行 234 次迭代,然后根据收敛的状态值贪婪地采取行动,以获得最佳策略。然而,如果你看看如果我们在每一次迭代中贪婪地行动,策略将如何被更新,你可以看到最佳策略实际上仅在 14 次迭代后就被找到了。我们不需要对完全收敛进行评估。状态值计算的数量可以显著减少,我们仍然可以获得最佳策略。
许多 GPI 算法使用这种思想来超越简单的策略迭代,而是在减少数量的策略评估步骤之后改进它们的策略。 值迭代 将这一思想发挥到了极致,有效地将评估阶段简化为一次状态扫描。此外,为了进一步改进,它将策略评估和策略改进阶段合并到一个更新中。
这可以从下面的等式 3 中看出:

等式三:价值迭代。迭代“k+1”时状态“s”的值是给出最大期望回报的动作的值。
在值迭代的迭代' k+1 ,状态' s 的值由在该状态下返回最大期望回报的动作的值给出。
一个动作的期望值是由该动作的转移概率之和,乘以进行转移所获得的回报,再加上你最终所处的状态的值给出的。因此,该等式可以重写为:

等式 4:价值迭代。迭代“k+1”时状态“s”的值是给出最大值的动作的值。一个行为的价值是转移概率的总和乘以从转移中获得的回报,再加上下一个状态的贴现值。
在一个状态' s '中,我们计算每个动作的价值,方法是对该动作的转移概率求和,乘以立即奖励,再加上下一个状态 s ',的贴现奖励,使用该状态的当前值。然后,我们选择这些计算值中的最大值,并在下一次迭代“ k+1 ”时,将其用作状态“ s 的值。
如果你回头看看方程 2,其中贝尔曼期望方程表示为更新函数,你会发现这些方程实际上是相同的。唯一的区别是,在最初的策略评估等式中,下一个状态值是由策略采取每个动作的概率之和给出的,而现在,在值迭代等式中,我们只是取返回最大值的动作的值。
在对简单网格级别的中心方块进行策略评估时,我们之前计算的值如下所示:

当评估一个随机策略时,其中每个动作都是同样可能的,我们简单地对这 4 个值进行平均,以获得在第一次迭代时估计的状态值。对于值迭代,我们取最大值(最小负值),在本例中是-2.0。对所有状态都这样做,得到第一组值迭代估计,如下所示:

图 14:第一次扫描后值迭代下的状态值。
我们现在继续,就像我们对策略评估所做的那样,使用我们从当前迭代中计算出的值,来计算下一次迭代中的状态值,并将其运行到收敛。每次迭代的状态值更新如下所示:

图 15:值迭代运行到收敛(收敛到 2 个小数位实际上发生在 62 次迭代中)
一旦估计的状态值已经停止变化,并且已经达到收敛,我们可以根据这些值贪婪地选择以获得最优策略。在这种简单水平的情况下,在 62 次迭代之后,收敛发生并且找到最优策略。
因此,策略迭代将评估一个策略,然后根据那些计算出的值贪婪地采取行动以获得下一个策略,值迭代在没有策略的情况下有效地工作。只有在最后,一旦找到了最优值函数,才会从状态值中创建最优策略。
对于这个简单的网格级别,只需要 62 次迭代,状态值就可以收敛,并且发现最佳策略。当您将其与运行策略评估收敛所需的 234 次迭代进行比较时,可以看到值迭代是对简单策略迭代的巨大改进。
此外,由于每次迭代代表对所有状态的扫描,减少寻找最优策略所需的总迭代次数在更复杂的环境中会有更大的好处,其特征在于更大的状态空间。
例如,看一下应用于扩展网格级别的值迭代,我们以前用它来测试策略迭代:

图 16:扩展网格级别的值迭代的前 30 次迭代。
对于这个级别,在 25 次迭代中找到最优策略,尽管在状态值达到收敛之前总共需要 120 次迭代。如果将这与策略迭代进行比较,在策略迭代中总共需要 203 次迭代才能找到最佳状态值,您可以看到性能的巨大提高。
该级别的最佳策略和最终收敛状态值如下面的图 17 所示。由于我们在运行值迭代时没有使用折扣因子,因此对于每个状态,这些值表示婴儿机器人从该状态到达出口所需的平均时间步数。

图 17:策略迭代 120 次后的状态值已经收敛。
摘要
通过策略评估,我们已经能够评估强化学习环境中的状态。通过添加策略改进,我们可以采用这些值并形成一个新的、改进的策略。
这种政策评估和政策改进的结合被称为 广义政策迭代 (GPI),通过其应用,初始政策可以被反复评估和改进,直到找到最优政策。
在 策略迭代 中,在每一步,都运行策略评估直到收敛,然后更新策略并重复该过程。
相比之下, 值迭代 每一步只做一次策略评估的迭代。然后,对于每个状态,取最大动作值作为估计的状态值。一旦这些状态值收敛到最佳状态值,就可以获得最佳策略。在实践中,这比策略迭代执行得好得多,并且在少得多的步骤中找到最佳状态值函数。
下一步是什么?
在我们确切知道从一个状态转移到下一个状态的概率以及每次转移将获得的回报的环境中,我们可以应用动态规划来评估环境中的每个状态,并从中找到最优策略。
然而,这些方法实际上并没有学到任何东西。环境的完整模型已经给出,因此没有必要进行任何探索来形成这个模型。
在下一部分中,我们将研究蒙特卡罗方法,它不需要对环境有完全的了解,而是从经验中学习。

***<** [**Part 2: Markov Decision Processes and Bellman Equations**](/markov-decision-processes-and-bellman-equations-45234cce9d25)*
带基线策略梯度增强算法
实践教程
张量流中的算法与实现

作者照片
策略梯度方法是非常流行的强化学习算法。它们非常有用,因为它们可以直接对策略建模,并且它们在离散和连续空间中都工作。在本文中,我们将:
- 简要概述政策梯度的基本数学原理;
- 在 Tensorflow 中实现策略梯度增强算法玩弹球;
- 比较政策梯度和深 Q 网(DQN)
我假设读者对强化学习的基础有所了解。作为复习,你可以快速看一下我上一篇文章 的第一节强化学习算法的结构概述 。
我以前也在 Tensorflow 中实现过深度 Q-net (DQN)来玩钢管舞。如果你感兴趣,请点击查看。😃
政策梯度
加固
不像许多其他 RL 算法参数化的价值函数(Q 学习,SARSA,DQN 等。)并从最优值函数中导出策略,策略梯度方法通过将策略参数化为:

然而,当涉及到优化时,我们仍然必须使用价值函数 V(θ) 作为目标函数。我们的目标是最大化 V(θ) ,即遵循策略 π 的轨迹 τ 的预期总回报。注意,值函数 V(θ) 是从参数化的策略π(θ)中计算出来的,而不是由 θ 直接参数化的。

其中τ是状态-动作轨迹:

R(τ)是轨迹τ的奖励总和:

如前所述,目标是找到使 V(θ) 最大化的策略的参数 θ 。为此,我们通过提升策略的梯度来搜索 V(θ) 中的最大值,w.r.t 参数θ。

策略 π(θ) 通常使用 softmax、高斯或神经网络建模,以确保其可微分。在这里,我们实现了普通策略梯度的一个流行变体:REINFORCE,它利用时间差来计算梯度。更详细的数学推导,请参考萨顿的第 13 章和巴尔托的书。

让我们看看训练程序是什么样的。

带基线的政策梯度
政策梯度方法的一个缺点是由经验收益引起的高方差。减少方差的一个常见方法是从政策梯度的回报中减去基线 b(s) 。基线本质上是预期实际回报的代理,它不能给政策梯度带来任何偏差。事实上,价值函数本身就是基线的一个很好的候选。减去基线后我们得到的新项是定义优势 A_t.

另一方面,还有另一个流行的政策梯度变体:行动者-批评家方法,该方法使用另一个参数化模型 Q(s,a) 来逼近优势价值,而不是使用经验回报 G_t 。这也有助于以增加偏差为代价减少方差。
基线是一个参数化的价值函数,可以通过减少经验预期收益和基线预测的均方误差来学习。

有了基线,我们新的训练循环有 2 个额外的步骤:计算优势和更新基线模型。

Python 实现(Tensorflow 2)
在本节中,我将演示如何使用 Tensorflow 2 实现带有基线的策略梯度增强算法来玩 Cartpole。关于 CartPole 环境的更多细节,请参考 OpenAI 的文档。完整的代码可以在 这里找到。
让我们从创建策略神经网络开始。对于像 Cartpole 这样的简单环境,2 个全连接层就足够了。由于 Cartpole 具有离散的动作空间,我们将分类分布应用于模型输出(logits)和来自它的样本动作,如在action_distribution()和sample_action()中实现的。
接下来,让我们创建一个基线网络。如前所述,我们使用价值函数作为基线。forward()函数以当前状态为输入,输出预测值 V(s)。update()的输入参数target是我们通过玩游戏收集的观察到的平均奖励(回报),我将稍后讨论。通过最小化返回和预测的均方误差来更新基线网络。
有了上面的两个网络,我们现在可以创建 PolicyGradient 类来完成大部分繁重的工作。我将把类拆分成多个代码片段。
在init()函数中,我们初始化模型的所有必要参数,包括我们刚刚定义的策略和基线网络。self.env是 Cartpole 环境的一个实例。
随着环境和策略网络的创建,我们可以进行几次部署来收集数据。在每一步,我们从策略网络中采样一个动作,玩一步获得奖励和下一个状态。在所有剧集的结尾,我们收集所有由状态、动作和奖励组成的游戏轨迹。这些轨迹将用于稍后更新策略网络和基线网络。
有了这些收集到的轨迹,我们就可以计算出每个状态的回报。请注意,我们可以通过对从步骤 t 到每集结束的所有未来折扣奖励求和,从技术上计算每个回报 G_t 。但是,这将导致复杂度为 O(n*n)。为了将其减少到 O(n),这里我们使用另一种方法:滚动平均。实质上,对于每一集,我们从其返回的最后一个状态 G_t = r_t 开始,并以相反的顺序计算返回,以利用关系: G_t = r_t + gamma * G_t+1 。
一旦我们有了所有情节的数据,我们就把回报拉平,把这些情节的轨迹分批。之前在基线网络的update()函数中,我们有一个输入参数target,它正是我们在这里计算的回报。
利用基线网络的预测值 V(s) 和经验收益,我们也可以得到优势。
最后,我们拥有了更新策略网络所需的所有组件。记住在政策梯度中,目标是我们通过遵循政策所获得的价值最大化,相当于负值(损失)最小化。
让我们把所有东西放在一起,训练我们的模型。完整的训练逻辑在train()中实现,在每一次迭代中,我们重复这个过程:调用play_game()得到几个情节轨迹;将轨迹(列表的列表)展平成一批(列表);使用批数据计算回报和优势;更新基线网络和策略网络。
还有两个更方便的函数可以帮助我们评估策略梯度模型,方法是制作它在 CartPole 环境中的性能视频。
训练完成后,让我们运行代码并呈现一个视频。

我们还可以绘制出所有训练步骤的回报。政策梯度模型能够收敛到 CartPole(200)的最大回报,尽管由于模型的随机性偶尔会出现一些分歧。

结论
让我们把这个结果和我之前做的 DQN 翻筋斗的结果进行比较。我们实际上可以看到,政策梯度方法达到收敛的速度比 DQN 快得多。事实上,策略梯度方法通常在具有小状态空间的环境中更快,如 CartPole,这要归功于它直接对策略建模的独特设计。
政策梯度方法的最大警告是高方差,这通常通过利用时间结构(加强)、引入基线或增加偏差(行动者-批评家)来解决。同样值得注意是,策略梯度方法只能保证收敛到局部最大值。然而,政策梯度方法在离散和连续空间都非常有效。
我希望你喜欢这篇文章。😃
参考
斯坦福 CS234 课程笔记:【https://web.stanford.edu/class/cs234/slides/
用 Python 处理维基百科上的政治投票数据
使用 Python 中的 Requests、pandas 和正则表达式从 Wikipedia 中获取和清理爱尔兰政治投票数据,以便为分析做好准备。

阿诺·杰格斯在 Unsplash 上的照片
简介
在 2011 年的爱尔兰大选中,即将离任的政府中的政党(共和党和绿党)在投票中一败涂地。在 2007 年的大选中赢得了 77 个席位的共和党最终只获得了 20 个席位。绿党失去了他们在 2007 年赢得的所有六个席位。
我想获得民意调查数据,以了解在那个时期爱尔兰政党的公众支持率是如何逐渐下降或上升的。当事人高峰在哪里?支持率的变化是否与当时的特定事件——失业率、新政党领导人等——相一致。?
要求
在本文中,我主要关注收集和清理轮询数据。
我们将使用请求库连接到维基百科来获取数据。然后,我们将使用 pandas 库本身将其加载到 pandas 数据帧中。数据不会是完美的,所以我们需要在使用它之前对其进行清理。
模块是 Python 标准库的一部分。但是如果您需要安装 pandas 或 requests,请查看它们文档中的安装部分:
我在示例中使用 Jupyter 笔记本(作为 Anaconda Navigator 的一部分)来展示结果。有关如何安装的信息,请参见此处的文档。
寻找数据
获取数据的第一步是找到可以处理数据的地方。维基百科有大量关于选举和政治的数据。爱尔兰许多大选的文章都有自上次大选以来的投票数据。这里的是我曾经处理过的文章。
步骤 1 —发出 HTTP 请求
首先,我们导入我们需要的库和模块。
import pandas as pd
import requests
import re
接下来,我们将添加我们的数据所在的 URL,以便我们可以轻松地将它传递给访问它的请求。
url = "[https://en.wikipedia.org/wiki/2011_Irish_general_election](https://en.wikipedia.org/wiki/2011_Irish_general_election)"
现在,我们发出请求,并将响应设置为一个名为 data 的变量
data = requests.get(url)
在数据中,我们现在应该在我们的 URL 上有 HTML 数据,以及关于请求的许多其他信息。例如,我们可以检查响应代码以确保请求成功:
data.status_code
>>200
在本例中是这样的,我们感兴趣的响应部分是 HTML 内容:
data.content
步骤 2-将数据放入熊猫数据框架
熊猫可以使用它的 read_html() 方法读取 HTML 表格。该方法将返回您传入的内容中的所有表格,将每个表格转换为 dataframe,并将其存储在一个列表中。
我们可以通过传入 data.content 来获取页面上的所有表格,但是我们可以使用 match 参数更具体地获取。
正如熊猫医生所说,使用这个:
将返回包含与该正则表达式或字符串匹配的文本的表集。
在维基百科页面上,我们可以看到这几乎是唯一一个关于投票数据的表格。

我们将添加“投票代理”作为匹配参数。
tables = pd.read_html(data.content, flavor='bs4', match = "Polling Agency")
现在,在表中我们应该有一个数据帧列表(希望只有一个)。如果我们对它运行 len() ,我们可以看到情况是这样的:
len(tables)
>>1
因此,它将是列表中第一个也是唯一一个数据帧。我们将它保存在下面的变量中:
polling_data_2011 = tables[0]
让我们看看前 20 行:
polling_data_2011[0:20]
我们可以看到它做得相当不错。看起来没有太多丢失的数据,但是我们需要做一些更新。

步骤 3 —清理数据
我们有数据,但正如我们所看到的,有一些问题。
- 最好也将日期存储在三个单独的列中:日/月/年。那么就更容易看到年复一年的变化。
- 在来源栏中,我们有维基百科的参考号。我们希望去掉它们,因为它们实际上不是源名称的一部分。
- 我们想删除 %s ,这样我们就可以处理数字数据了。
- 最后,您会注意到在轮询代理列中有一些 NaN 值。在这种情况下,这是因为这些争吵根本不是民意调查,而是实际的选举结果——我们有通用电气的结果,以及一些欧洲和地方选举结果。我们要做的是创建另一列,将该行分类为选举数据或民意测验数据。这样,如果我们愿意,我们将能够轻松地处理民意调查数据。
将日期解析为三列
我们创建了三个想要匹配的正则表达式。一个用于日,一个用于月,一个用于年。然后,我们遍历 date 列中的每个字段,获取其索引和值,并根据值检查正则表达式。然后,我们将找到的值插入到它自己的新列中——我们对所有三个列都这样做。
day_re = "[0-9]+"
month_re = "[A-Z][a-z]+"
year_re = "[0-9]{4}"for index, value in polling_data_2011["Date"].items():
#day
match = re.search(day_re, value)
if match:
polling_data_2011.loc[index,"Poll_day"] = match.group()
#month
match = re.search(month_re, value)
if match:
polling_data_2011.loc[index, "Poll_month"] = match.group()
#year
match = re.search(year_re, value)
if match:
polling_data_2011.loc[index, "Poll_year"] = match.group()
我们现在有三个新列:

清洁源柱
在信号源栏中,在每个实际信号源名称后,参考编号以开头。
因此,我们遍历源列中的字段,类似于我们对日期所做的。然而,这一次,我们将匹配这个正则表达式,并将 new_name 设置为每个字段中直到这个值的所有值。
注意,在 regex_to_match 中,我们需要对我们正在搜索的字符进行转义,因为这个字符在 regex 中有特殊的含义。
regex_for_reference = "\["for index, value in polling_data_2011["Source"].items():
ref_match = re.search(regex_for_reference, value)
if ref_match:
location = ref_match.span()[0]
new_name = (value[0:location])
polling_data_2011.loc[index,"Source"] = new_name
else:
polling_data_2011.loc[index,"Source"] = value
看起来更整洁:

我想检查一下没有其他明显的错误。维基百科是一个有许多贡献者的来源。也许不同的贡献者输入的名字不同。
让我们看看源列中的所有唯一值:
polling_data_2011["Source"].unique()

我们看到其中一个来源至少在一行中有不同的表达方式——我们需要用句点( The Sunday Business Post)更新这个来源。)与其他的一样(《星期日商业邮报》)。
让我们改变之前使用的正则表达式,并删除任何存在句点的地方:
regex_for_period = "\."for index, value in polling_data_2011["Source"].items():
period_match = re.search(regex_for_period, value)
if period_match:
location = period_match.span()[0]
new_name = (value[0:location])
polling_data_2011.loc[index,"Source"] = new_name else:
polling_data_2011.loc[index,"Source"] = value
我也在民调机构一栏做了同样的检查。没有问题。
将带有%的列转换成我们可以处理的数字数据
这里,我们将从每个包含%的字段中删除它。让我们对每个政党都这样做。我们遍历列,然后遍历每列中的字段。在找到%之前,我们只保留所有内容。
注意:我们可以在这里使用 find() (以及在其他一些情况下)来代替 re ,但是我们将坚持使用 re 来保持一致。
regex_to_find_percent = "%"
columns = ["Fianna Fáil", "Fine Gael", "Labour Party", "Green Party", "Sinn Féin", "Ind./Others"]for column in columns:
for index, value in polling_data_2011[column].items():
match = re.search(regex_to_find_percent, value)
if match:
match_index = match.span()[0]
new_value = (value[0:match_index])
polling_data_2011.loc[index, column] = new_value
看起来不错:

我们现在需要将这些列转换为 float 类型,这样我们就可以对它们使用 pandas describe 方法()等方法来查看最小值、最大值和平均值等信息。
for column in columns:
polling_data_2011[column] = polling_data_2011[column].astype(float)
现在 describe()方法为我们提供了一些关于数据的描述性统计数据。
polling_data_2011.describe()

从选举数据中分离民意测验数据
在上表中,我们知道有几行不是民意测验数据。让我们对每一列进行分类,这样我们就知道我们在做什么了。然后,如果我们愿意,我们将能够只处理民意调查数据。
实际上,我将把它分为三类:选举结果、民意调查和出口民调。我之所以这么做,是因为后两者虽然是民调,但略有不同。出口民调是基于投票后对人们的采访,而标准的民意调查是关于一个人打算如何投票。
我们将在 source 列的每个字段中检查“选举”和“投票后调查”的实例。我们还将说明它何时会以大写字母出现在句子的开头。如果我们匹配选举,我们在名为 Poll_type 的新列中放一个 E。如果我们匹配出口投票,我们将 EP 放入 Poll_type 列。如果我们两个都不符合,我们假设这是一个民意调查——op。
election_regex = "[Ee]lection"
exit_poll_regex = "[Ee]xit poll"
for index, value in polling_data_2011["Source"].items():
election_match = re.search(election_regex, value)
exit_poll_match = re.search(exit_poll_regex, value)
if election_match:
polling_data_2011.loc[index, "Poll_type"] = "E"
if exit_poll_match:
polling_data_2011.loc[index, "Poll_type"] = "EP"
if not election_match and not exit_poll_match:
polling_data_2011.loc[index, "Poll_type"] = "OP"
步骤 4 —检查我们的数据
现在我们有了最终的表格,让我们做一些检查以确保数据是正确的。
从维基百科页面的快速浏览中,我们可以看到在此期间有一次投票后调查,以及 2007 年和 2011 年的一次大选。让我们按 Poll_year 和 Poll_type 创建一个数据透视表,以确保我们与原始表相匹配。
polling_data_2011.pivot_table(index=["Poll_year","Poll_type"])

在上表中,我们可以看到每年的选举结果都与维基百科页面上报道的相符。例如,2011 年:

根据原始数据,我们可以预测未来几年的选举和出口民调。数据现在看起来很好,随时可以使用。
获取这些数据的过程非常简单。获得我想要的特定表并进行必要的数据清理非常容易。
在未来,我想看看管理日期列的其他选项,并探索使用我已经完成的工作来获得额外的数据有多容易。
所有代码和表格的图片都是作者拍摄的。本文的 Jupyter 笔记本可以在这里找到:
https://github.com/LiamConnors/Medium-articles
Python 中的多态性:概率分析
使用 Python 进行面向对象编程

数据科学家经常面临编写所谓“意大利面条式代码”的指责。也就是说,代码在技术上完成了一项任务,但并不总是可复制的或以最佳方式布局的。
从这个角度来看,多态性——面向对象编程中的核心概念——在允许在一个特定类中实现多个功能方面有很大的价值。这使得用户能够更容易地同时使用相似的函数,同时通过将这些函数组合在一起确保代码的可维护性。
对于没有计算机科学背景的人来说,这听起来有些抽象——面向对象编程的主要功能之一是确保代码是有组织的和可重复的。这在确保维护方面节省了大量时间,因为程序的规模总是在增长。
让我们通过使用多态和 numpy 实现一系列概率计算来看看这是如何工作的。
概率分析
在这个例子中,让我们看看如何使用多态性在一个更大的类中存储大量的概率计算。
首先,让我们定义一个大类,我们将使用它来定义概率计算的各种“子类”:
class Probability:
def __init__(self, name):
self.name = namedef __str__(self):
return self.name
二项式概率
二项式概率是一种离散概率,它计算给定一定次数的试验,一定次数的成功的概率。
numpy.org 给出了这样一个计算的例子,要求计算 9 口成功概率为 0.1 的油井全部失败的概率。
现在,想象一下这个场景。假设我们希望创建一个程序,将各种概率的计算存储在不同的函数中——每个函数都包含在一个类中。此外,通过调用该类,我们可以定义在尝试查找累积失败概率时希望计算的尝试次数。
该类定义如下,包含概率为 5% 、 10% 和 15% 的独立函数嵌套在该类中:
注意 self.length 表示调用函数时我们将指定的尝试次数。
例如,让我们指定 10 次试验,并计算期望概率中的失败概率:
>>> a = Binomial(10)
>>> print(a)
>>> print(a.fact())
>>> print(a.binomial5())
>>> print(a.binomial10())
>>> print(a.binomial15())Binomial
Probability of failure according to the Binomial Distribution.
0.5
0.6
0.1
我们看到,在 5%的成功几率下,失败的概率是 50%。在 10%时,它是 60%,而在 15%时,它下降到 10%。
把试验次数增加到 1000 次怎么样?
>>> a = Binomial(1000)
>>> print(a)
>>> print(a.fact())
>>> print(a.binomial5())
>>> print(a.binomial10())
>>> print(a.binomial15())Binomial
Probability of failure according to the Binomial Distribution.
0.623
0.363
0.259
在 5%的时候,现在的概率是 62.3%。在 10%时,这一比例为 36.3%,而在 15%时,这一比例为 25.9%。
从现实世界的角度来看,人们可能希望设计一个计算机程序,它可以在大量的试验中自发地产生许多失败概率,并且除概率率之外的参数也可以变化。使用多态性可以灵活地做到这一点。
帕累托分布
现在,考虑这个场景。假设我们希望从帕累托分布中生成样本。帕累托分布是一种幂律分布,其中分布的尾部很重。换句话说,大部分数据包含在分布的尾部:

来源:Jupyter 笔记本输出
具体来说,让我们用形状 3 和模式 1、2、3 生成三个独立的分布。
正如我们所看到的,帕累托分布的形状是恒定的,而模式是变化的。每个模式(模式 1、模式 2、模式 3)都被定义为一个函数,其中模式的值是不同的。同样, self.length 定义了我们希望生成的试验次数。
>>> b = Pareto(10)
>>> print(b)
>>> print(b.fact())
>>> print(b.mode1())
>>> print(b.mode2())
>>> print(b.mode3())Pareto
Sample generated in accordance with the Pareto Distribution.
[1.36412427 1.10291466 1.47183876 1.66282503 1.18855344 1.08670931
1.01762507 1.04154705 1.48818185 1.47923808]
[2.28969839 3.64922758 3.48322786 3.71563878 2.02763407 4.14306486
2.04481284 3.57690852 4.38221439 3.50612773]
[3.89360177 5.44499969 3.00307348 8.11591885 3.83083417 4.82244346
5.91314304 3.16790445 3.06548931 3.91090296]
让我们尝试 1000 次试验。
>>> b = Pareto(1000)
>>> print(b)
>>> print(b.fact())
>>> print(b.mode1())
>>> print(b.mode2())
>>> print(b.mode3())Pareto
Sample generated in accordance with the Pareto Distribution.
[ 1.04522737 1.13748388 2.64986056 1.01188388 1.92169205 ...
1.56696587 1.1202479 2.08817365 1.46907723]
[ 2.59499208 2.2019354 3.78301714 3.33741161 2.08731817 ...
3.23218408 2.48797598 2.47920052 2.2045201 ]
[ 4.44148781 7.86723551 6.78859869 3.24117621 3.47112606 ...
3.03165741 3.20184443 4.11208707 3.37364986]
正如我们所看到的,多态性通过允许不同的类存储在另一个更大的具有单一名称的类中,为函数数组提供了更多的结构。
虽然在这个例子中,少数函数在技术上可以单独运行,但在具有数千个甚至数百万个函数的计算机程序的环境中,这就成了一个严重的问题。
使用多态性为这些函数提供结构,可以更好地维护代码,并在将程序投入生产时提高效率。
从数据科学的角度来看,知道如何实现面向对象编程是非常有价值的。即使一个人不是软件工程师,并且通常不维护程序的库或生产考虑事项——知道什么时候应该使用面向对象编程会使实现适当的生产和版本控制过程变得容易得多。
结论
在本文中,您已经看到:
- 为什么多态在计算机编程中很重要
- 如何使用多态将函数合并到一个只有一个名字的类中
- 使用多态性计算概率
像计算机编程中的许多概念一样,面向对象编程关注的是效率。例如,正如使用不正确的数据类型会导致内存使用效率低下一样,单独存储函数会使程序更难维护。面向对象编程旨在解决这个问题。
非常感谢您的宝贵时间,非常感谢您的任何问题或反馈。你可以在michael-grogan.com找到更多我的数据科学内容。
参考
- numpy . org:numpy . random . binomial
- numpy . org:numpy . random . Pareto
- 编程:Python 中的多态性
- statisticshowto.com:什么是帕累托分布?
免责声明:本文是在“原样”的基础上编写的,没有任何担保。它旨在提供数据科学概念的概述,不应被解释为专业建议。本文中的发现和解释是作者的发现和解释,不被本文中提到的任何第三方认可或隶属于任何第三方。作者与本文提及的任何第三方无任何关系。
多项式内插法
实践教程
涵盖拉格朗日多项式插值、牛顿多项式插值和样条插值

多项式插值——照片由 Edgar Castrejon 在 Unsplash 上拍摄
插入文字
假设您对跟踪菜园全天的温度感兴趣。您每小时精确测量一次温度,最终得到以下 24 个温度测量值:
在 Python 中准备数据集以便跟进

多项式插值每小时的温度
您可以使用任何工具将这些数据绘制在图表上,以获得所谓的散点图:每个测量值都有一个点。

散点图包含没有插值的测量值(每小时一次)
您可能感兴趣的下一步是将散点图转换成线形图。然而,您在数据点之间没有任何数据,因此您没有关于如何绘制线条的确切信息。
您可以使用插值来实现这一点。插值是智能估计数据点之间的值的任务。
插值方法
您可以使用许多不同的方法进行插值。最简单的方法是线性插值,你从每个数据点到下一个数据点画一条直线。虽然简单,但这种方法通常是错误的,因为它创建了一个有很多“角度”的图:
线性内插法

线性插值从每个点到下一个点绘制直线
另一种基本的插值方法是最近邻插值,其中使用最近邻来估计每个值。例如,您可以使用 11 点的测量值作为 10:30 到 11:30 的估计值,然后使用 12 点的测量值作为 11:30 到 12:30 的估计值,依此类推。你会得到这样一个图表:
最近邻插值

最近邻插值法使用最近的测量温度值来估计某个时间点的温度
这个图看起来可能很奇怪:它被称为分段图。线中没有连续性,因为一旦达到阈值,估计值就从一个邻居“跳到”另一个邻居。在我们的示例中,阈值是半小时,但是这当然会根据您正在处理的示例而有所不同。
多项式内插法
多项式插值是一种改进的插值方法,试图找到最适合您的数据的多项式函数。如果你不擅长数学,就把多项式函数想象成一个由许多函数组成的数学家族。
让我们来看看多项式的定义:
多项式被定义为由变量和系数组成的表达式,只涉及变量的加、减、乘和非负整数幂运算。
然而,这是一个非常宽泛的定义:仍然有许多不同的功能遵守这一规则。多项式家族中有如此多不同的多项式,您总能找到适合您的数据的多项式。
多项式的次数由公式中的最大幂来定义。例如,一个 2 次多项式中有一个 x 的平方,一个 3 次多项式中有一个 3 次方,等等。让我们看一些多项式的例子来理解多项式函数的各种可能性。
二次多项式的一个例子
二次多项式是抛物线。下面的函数就是一个例子:
y = 1 + x + x**2
它看起来如下:
二次多项式

二次多项式的一个例子:抛物线
三次多项式的一个例子
三次多项式是三次多项式。下面的函数就是一个例子:
y = 1 + x + x**2 + x**3
它看起来如下:
三次多项式

三次多项式的一个例子:三次多项式
6 次多项式的一个例子
高次多项式可以采用非常复杂的形式。下面的函数就是一个例子:
y = 1/100 * (x**6 — 2x**5-26x**4+28x**3+145x**2-26x-80 )
它看起来如下:
六次多项式

六次多项式的一个例子
正如你在图表中看到的,多项式允许你定义非常复杂的形状。同时,曲线保持比通过线性插值或最近邻插值获得的曲线更加平滑。这是多项式插值如今成为大多数用例的首选插值方法的主要原因。
多项式插值方法
多项式插值的确切目标是找到到达数据集各点的最低可能次数的多项式。最低次可归结为多项式的最简单形式。
每个数据集只有一个最简单的多项式:有且只有一个正确的多项式,目标是找到它。然而,在本文中,我们将讨论多项式插值的三种常用方法:
- 拉格朗日多项式插值
- 牛顿多项式插值,也叫牛顿整除差插值多项式
- 样条插值,更具体地说是三次样条插值
拉格朗日多项式和牛顿多项式给出完全相同的结果,然而它们通过不同的计算得出结果。拉格朗日和牛顿方法产生穿过数据点的最小阶的多项式函数。
样条插值略有不同,因为它不是估算一个多项式,而是估算一个分段多项式。“真正的”多项式插值可能需要一个非常复杂的多项式来精确地遍历所有数据点。当复杂度变得太高时,多项式可能呈现出极不稳定的曲线,在数据点之间包含许多不需要的尖峰。
样条插值是这个问题的解决方案,因为它在数据子集上定义了多个更简单的多项式。这在流畅和简单之间取得了巨大的平衡。样条插值是许多插值问题的首选解决方案。
现在让我们更深入地讨论一下这三种插值方法。
拉格朗日多项式插值
我们要看的第一种多项式插值方法是拉格朗日多项式插值。这是一种方法,允许您找到通过数据集所有点的最低阶多项式。
拉格朗日插值公式
在这篇文章中,我们不去探究拉格朗日插值公式为什么起作用的数学证明,但是我们将看看如何手动使用它。
下面是拉格朗日插值公式:

拉格朗日插值公式(第一部分)

拉格朗日插值公式(下)
从第 2 部分开始理解更容易。第 2 部分基本上是说,对于数据集中的每组 x 和 y 值,将 y 值乘以 x 值的“第 1 部分计算”。
必须为每个 x 值单独进行第 1 部分的计算。计算可以理解如下。对于除了问题中的 x 值之外的每个 x 值(调用问题 j 中的 x 值和所有其他的 m),将获得(x — m) / (j-m)。然后你把它们的乘积。
关于拉格朗日多项式插值的一个手工小例子
假设您有三个数据点:
(x = 2,y = 4)
(x = 4,y = 16)
(x = 6,y = 36)
首先计算每个数据点的拉格朗日插值公式的第 1 部分:
- 对于第一个数据点:
[(x—4)/(2—4)][(x—6)/(2—6)]*
- 对于第二个数据点
[(x-2)/(4-2)][(x-6)/(4-6)]*
- 对于第三个数据点
[(x-2)/(6–2)][(x-4)/(6–4)]*
然后,对每个数据点组合,取 y 和 x 的第一部分解的乘积之和:
4 **[(x-4)/(2–4)][(x-6)/(2–6)]
+16 [(x-2)/(4–2)][(x-6)/(4–6)]
+36 [(x-2)/(6–2)][(x-4)/(6–4)]
之后,你可以简化公式,你应该得到 x**2。
用 Python 求拉格朗日多项式插值
很高兴看到如何手动解决拉格朗日多项式插值,但这是一个很大的工作。现在让我们看看如何利用 Python 工具自动寻找最佳多项式。当然,大多数科学计算程序中都存在替代方案。
让我们从刚才手动做的同一个例子开始
Python 中的拉格朗日多项式插值

使用 Python 的 Scipy 实现拉格朗日多项式插值是成功的小例子
在本例中,拉格朗日多项式插值的快速实现是成功的:x 的平方已经被正确识别。
现在,最大的问题是:我们能使用拉格朗日多项式插值来为我们的菜园例子的温度找到一个多项式吗?这个例子显然要复杂得多,需要一个非常复杂的多项式。让我们看看当我们将 Scipy 工具应用于这个问题时会发生什么:
Python 中的拉格朗日多项式插值

使用 Python 的拉格朗日插值的 Scipy 实现在大型示例中失败
很明显,拉格朗日方法明显没有找到这个美国案例的多项式。这不是问题,甚至是意料之中的行为。Scipy 文档声明:
警告:该实现在数值上不稳定。不要期望能够使用超过 20 个点,即使它们是最佳选择。
既然我们知道拉格朗日多项式插值无法处理如此大的插值示例,那么让我们继续研究牛顿多项式插值方法,看看它是否能给出更好的结果。
牛顿多项式插值
现在让我们看看牛顿求最低阶多项式的方法。这种方法应该给出与拉格朗日多项式插值完全相同的答案,但是它使用不同的计算方法。
牛顿差分插值多项式
牛顿多项式法有时也被称为牛顿差分插值多项式。这是因为多项式的系数是用一种叫做除法的方法计算的,这种方法是由艾萨克·牛顿发明的。
您的 x 点必须等间距排列,以使用正向 Netwon 多项式方法。公式如下

牛顿差分插值多项式
拉格朗日多项式与牛顿多项式
牛顿多项式比拉格朗日多项式有一些实际的优点。它允许向计算中添加更多的数据点,而无需重新进行整个计算。
Python 中的牛顿多项式插值
要进行牛顿多项式插值,我们需要找到一个实现,因为它在 Scipy 中没有实现。您可以使用以下 Python 代码进行牛顿多项式插值(归功于 StackOverflow):
Python 中的牛顿多项式插值
让我们首先在带有 x 平方函数的小数据示例上测试牛顿多项式插值代码:
Python 中的牛顿多项式插值

牛顿多项式插值成功的小例子
如图所示,牛顿多项式插值已成功找到基于三个数据点的函数 x 平方。目前是好消息。现在,让我们看看是否可以对我们菜园的温度数据使用同样的技术!
Python 中的牛顿多项式插值

牛顿多项式插值找到了一个关于大数据量多项式过于复杂的例子
当看这个图时,你会看到,在某种程度上,牛顿实现比拉格朗日实现更好:至少牛顿遍历了所有的数据点。
然而,这个解决方案有一个严重的问题:这个多项式太复杂了,因此,它估计了从午夜到凌晨 5 点和从晚上 8 点到午夜的极端温度峰值。
这清楚地表明了多项式插值在其官方定义中的劣势。您强制模型找到一个且仅一个多项式来拟合您的所有数据点,但您的数据实际上并不遵循完美的多项式。因此,解决方案通常是一个强制的、过于复杂的多项式,在某些位置会有一些极端的误差。
让我们来发现这个问题的解决方案:样条插值。
样条插值
样条插值与前两种插值方法有很大不同。你可以认为拉格朗日法和牛顿法是纯多项式插值:它们实际上只使用一个多项式函数进行插值。
样条插值是多项式插值的一种变体,它不仅仅适合一个复杂的多项式,而是适合许多较小次数的多项式。这样,你可以避免拟合太复杂的多项式,最终得到一条更平滑的曲线,插值误差更小。
三次样条插值
要进行样条插值,您需要从每个点到下一个点拟合一个多项式。当你把它们放在一起时,你会得到许多组成完整插值函数的子函数。
当然,线的每一段的开始和结束值必须与前一段或后一段相对应。只有当使用三次或更复杂的多项式时,才能做到这一点。
使用三次样条还有一个好处,你可以避免拟合过于复杂的曲线,就像我们在牛顿法中看到的那样。因此,样条插值的标准方法是只使用三次样条。
Python 中的三次样条插值
大多数科学软件提出了三次样条插值的方法。让我们看看如何在 Python 中使用 Scipy 实现三次样条插值。我们将从三个数据点的小例子开始。
Python 中的三次样条插值

三次样条插值成功的小例子
毫不奇怪:就像拉格朗日和牛顿一样,三次样条也找到了 x 平方函数。让我们看看它在菜园温度数据上的表现。
Python 中的三次样条插值

三次样条插值在大数据集上是成功的
正如你所看到的,三次样条提出了一个非常“自然”的插值。它比您之前看到的线性插值或最近邻插值平滑得多。同时,它比拉格朗日和牛顿提出的“纯”多项式方法要简单得多。
线性和最近邻插值可能仍然发现其理论简单性的优势,并且拉格朗日和牛顿插值在非常简单的数据上仍然具有优势,该数据完美地表示低阶多项式。然而,样条插值的平滑性和简单性使其成为当今许多插值挑战的首选方法。
关键要点
- 在本文中,您已经发现了五种插值方法,其中三种方法为多项式插值。
- 线性插值是最简单的插值方法,因为它包括在每个数据点和下一个数据点之间绘制直线。最近邻插值使用最近的测量值作为中间值的估计值。
- 拉格朗日多项式插值和牛顿多项式插值(或牛顿差分插值多项式)是两种确定性地找到穿过所有数据点的最低阶多项式的方法。
- 样条插值,尤其是三次样条插值,是一种更灵活的替代方法,可以找到分段多项式。这允许降低所用多项式的阶数,具有更简单和更接近实际的优点。这是以在分段函数中使用许多多项式而不是定义一个整体多项式为代价的。
我希望这篇文章对你有用。不要犹豫,继续关注更多的数学、统计和数据内容!
多项式回归-从零开始的梯度下降
没有图书馆,没有问题

Jonny Caspari 在 Unsplash 上的照片
梯度下降是一种需要理解的重要算法,因为它是机器学习和深度学习中使用的许多更高级算法的基础。因此,掌握梯度下降的内部工作原理对任何打算进一步探索最大似然算法的人都有很大的好处。
最好的学习方法是边做边学,所以在这篇文章中,我将介绍梯度下降过程是如何工作的,而不使用 ML 库,比如 scikit-learn。在日常工作中,使用这样的库当然更快更整洁,但是关于学习过程,我发现手工实现的练习对于这种特定的算法是非常宝贵的。
梯度下降的目标—第一步
梯度下降的目标是相对于原始数据最小化模型预测的误差。在本文的上下文中,我们将研究二次多项式模型,也称为二次方程:

绘制时,二次多项式看起来像这样:

作者图片
多项式回归
我们在这里特别关注多项式回归,其中自变量 x 和因变量 y 之间的关系被建模为 x 中的 n 次多项式。简而言之,我们的二次多项式 a、b 和 c 的系数将被估计、评估和改变,直到我们可以精确地将一条线拟合到输入的 x 数据。梯度下降是这个过程中的优化步骤,它改变并改进这些系数的值。
我们现在将看看如何创建和绘制这样一条曲线,然后构建一个初始模型来拟合这些数据,然后我们将使用梯度下降法对其进行优化和改进。如果我们能得到一个准确描述数据的模型,希望它应该能够准确预测另一组 x 值的 y 值。
我们可以开始为二次多项式方程(𝑎𝑥 +𝑏𝑥+𝑐)选择系数,该方程将分布我们将尝试建模的数据:
这些将是我们希望预测模型尽可能接近的基础/地面真实模型的系数。接下来,我们需要一个二次多项式的评估函数,在给定一组系数和给定输入𝑥的情况下,它将返回相应的𝑦:
当 x= 3 : 时,我们可以看到它的作用
7
创建数据和基础模型
定义一些我们希望预测 y(输出)的 x 数据(输入):

多项式 2x -5x + 4 = 0 的曲线—作者
这很好,但我们可以通过让事情更现实来改进这一点。您可以将噪声或“抖动”添加到值中,以便它们可以类似于真实世界的数据:
测试一下:
7
Should get value in the range 3 - 116.233537936801398
这个更新的函数将接受二阶多项式和抖动值 j 的输入,以向该输入添加噪声,从而为我们提供比完美曲线更真实的输出:

带抖动的原始数据—作者
当我们建立我们的预测模型,并用梯度下降优化它时,希望我们能尽可能接近这些值。
建模的第一步——尝试随机模型
建模的第一步涉及生成和存储二次多项式(𝑦=𝑎𝑥 +𝑏𝑥+𝑐).)的随机系数这将是我们的初始模型,它很可能不那么准确,我们将致力于改进它,直到它与数据足够吻合。
(7, 6, 3)
通过根据输入值计算预测输出值来检查此模型的准确性:

原始数据与首次随机预测—作者
从上图可以明显看出,这个带有随机系数的新模型并不完全符合我们的数据。为了获得其不正确程度的量化度量,我们计算模型的均方误差损失。这是实际和预测输出的平方差之和的平均值;

47922.39790821987
相当大的数量。现在,让我们看看是否可以通过使用梯度下降优化模型来改善这一相当高的损失指标。
梯度下降和减少损失
我们希望改进我们的模型。因此我们想改变其系数 a 、 b 和 c 以减小误差。因此,我们需要了解每个系数如何影响误差。这是通过计算损失函数相对于各个系数的每个 的 的偏导数来实现的。
在这种情况下,我们使用 MSE 作为损失函数,这是我们希望计算偏导数的函数:

我们模型的输出预测为:

因此,损失可以重新表述为:

在这种特殊情况下,损失函数的偏导数如下:

- 如果计算每个导数的值,就会得到每个系数的 梯度 。
- 这些值给出了针对每个特定系数的损失函数的斜率。
- 它们表明你是否应该增加或减少它来减少损失,以及增加多少才是安全的。
给定系数𝑎、𝑏和𝑐,计算出的梯度𝑔𝑎、𝑔𝑏和𝑔𝑐以及学习率𝑙𝑟,通常可以更新系数,使得它们的新的更新值定义如下:

一旦你把新的模型应用到数据中,你的损失应该会减少。
减少损失
我们需要一个梯度计算函数,给定二次多项式的系数,以及一组输入𝑥和相应的一组实际输出𝑦将返回每个系数各自的梯度。
我们现在要:
- 使用上面的函数来计算我们表现不佳的随机模型的梯度,
- 相应地调整模型的系数,
- 验证模型的损失现在变小了——动力局起作用了!
让我们设定一个初始学习率进行实验。这应该保持很小,以避免错过全局最小值,但也不要太小,以至于花费很长时间或陷入局部最小值。lr = 0.0001 是一个很好的起点。
New model coeffs: 5.290395171471687 5.903335222089396 2.9704266522693037Now have smaller model loss: 23402.14716735533 vs 47922.39790821987
通过将训练数据、原始随机模型和更新的低损耗模型绘制在一起,将这种改进可视化:

改进了梯度下降迭代一次后的精确度-作者
多次迭代梯度下降
我们差不多准备好了。最后一步是在多个时期(循环或迭代)内迭代执行梯度下降。)随着每一个时代的到来,我们都希望看到以降低损失和更好地模型拟合原始数据的形式的改进。
让我们改进上面的 calc_gradient_2nd_poly 函数,使其更适用于迭代梯度下降过程:
这将被称为梯度下降功能的一部分:
最后,让我们训练 1500 个纪元,看看我们的模型是否学到了什么:

相当接近原始数据!—作者
这个经过训练的模型在完整的训练周期后显示出巨大的进步。我们可以通过检查其最终预测系数 a 、 b 和 c 来进一步检查:
Final Coefficients predicted: (2.0133237089326155, -4.9936501002139275, 3.1596042252126195)
Original Coefficients: [2, -5, 4]
不太远!比最初的随机模型有了很大的改进。查看培训损失减少的图表可以获得进一步的见解:

跨时代减少损失—作者
我们观察到,模型损耗接近于零,这给了我们更精确的系数。我们还可以看到,经过大约 400 个周期后,损耗没有明显改善,显然不需要 1500 个周期。另一种策略是在训练步骤中添加某种条件,当达到某个最小损失阈值时停止训练。这将防止模型的过度训练和潜在的过度拟合。
我希望你喜欢这个多项式回归的梯度下降。某些概念乍一看很难理解,但是随着时间的推移,如果我们坚持足够长的时间,我们就会熟悉问题的“螺母和螺栓”。我发现这个练习对我来说确实如此,而且我觉得这是一次值得的学习经历。
如果你喜欢这个故事,请考虑在媒体上关注我。你可以在 https://mark-garvey.com/的上找到更多
在 LinkedIn 上找我:【https://www.linkedin.com/in/mark-garvey/
Python 中从头开始的多项式回归
从零开始的机器学习:第 4 部分

多项式回归;作者图片
在本文中,我们将了解多项式回归算法,通过修改假设函数和添加我们希望添加到输入中的新要素,该算法可用于拟合非线性数据。
多项式回归只是标准线性回归的另一个版本。
https://medium.com/analytics-vidhya/linear-regression-from-scratch-in-python-b6501f91c82d
首先,我们将重述符号(寻找新变量degrees),然后理解算法,最后使用 Python NumPy 和 Matplotlib 实现它,绘制预测并计算 r2 分数。
符号—
n→功能数量m→培训实例数量X→形状输入数据矩阵(mxn)y→大小为m的真/目标值向量x(i), y(i)→以训练为例,其中x(i)为 n 维,y(i)为实数。degrees→一个列表。我们将X^(value)特征添加到输入中,其中值是列表中的值之一。(稍后详细解释)w→形状的权重(参数)(nx 1)b→bias(参数),一个可以广播的实数。y_hat→假设(w(权重)和X的点积加上b(偏差))—w.X + b- 损失函数→均方误差损失或均方误差损失(
y_hat-y),你最小化这个函数以便找到参数w和b。
多项式回归
让我们以下面的数据集为例来理解多项式回归,其中 x 轴代表输入数据X,y 轴代表具有 1000 个示例(m)和 1 个特征(n)的真值/目标值y。
import numpy as np
import matplotlib.pyplot as pltnp.random.seed(42)
X = np.random.rand(1000,1)
y = 5*((X)**(2)) + np.random.rand(1000,1)

随机二次数据;作者图片
如果我们对该数据使用标准线性回归,我们将只能对数据拟合一条直线,如下图中的蓝线所示,其中假设为— w1.X + b(用w1替换w)。
但是,我们可以看到数据不是线性的,下面显示的红点线非常适合这些数据。

多项式与线性回归;作者图片
因此,在将模型拟合到数据时,您必须回答的一个问题是— 您希望使用什么功能?您是想对数据进行直线拟合,还是想对形式为— b + w1.X + w2.X²的假设进行拟合,因为上述数据可能看起来像是二次函数拟合。
或者,您可能有看起来像平方根拟合的数据,因此您可能希望您的假设像这样— b + w1.X + w2.(X)^0.5或者您的数据可以是任何程度的,因此您可以选择您想要的特征并修改假设函数。
现在要实现这一点,我们所要做的就是将我们的第一个特征 x1(特征 1)定义为X,将第二个特征 x2(特征 2)定义为X²,或者根据您想要使用的特征,将 x1 定义为X,将 x2 定义为X^0.5。通过定义一个新的特征 x2,即X²或X^0.5,我们看到线性回归中的机制适用于拟合这些类型的非线性数据。
这里需要注意的重要一点是,我们的假设仍然是线性的,因为
**X²**或**X^0.5**都只是特征。然而,我们得到了数据的非线性拟合。
我们所要做的就是修改输入( *X* ),也就是说,只要添加我们想要的任何程度的特征,就像我们将为我们的例子添加一个特征 X。
如果您通过添加新特征来修改输入,假设会自动得到修改,因为h(x) = w.X +b,其中w是大小为n(特征数量)的向量。
下图显示了前 10 个示例,其中我们向输入数据添加了一个新的特征 X。

变换输入(X),添加 X 特征;作者图片
我们可以添加尽可能多的功能,这将是我们已经拥有的功能的一些指数运算。
该算法
- 根据你掌握的数据,修改线性回归的假设函数。
- 向输入中添加所需的新的更高阶要素。
- 对修改后的输入进行梯度下降/小批量梯度下降,以找到参数—权重和偏差。
多项式回归在起作用
损失函数
让我们首先定义损失函数,也就是 MSE 损失函数—
(y_hat - y)其中,y_hat为假设— w.X + b
**def loss(y, y_hat):**
# y --> true/target value.
# y_hat --> hypothesis
#Calculating loss.
**loss = np.mean((y_hat - y)**2)
return loss**
计算梯度的函数
现在,让我们写一个函数来计算损失函数相对于w和b的偏导数(梯度)。
请参见注释(#)。
# Calulating gradient of loss w.r.t parameters(weights and bias).**def gradients(X, y, y_hat):**
# X --> Input.
# y --> true/target value.
# y_hat --> hypothesis
# w --> weights (parameter).
# b --> bias (parameter).
# m-> number of training examples.
**m = X.shape[0]**
# Gradient of loss w.r.t weights.
**dw = (1/m)*np.dot(X.T, (y_hat - y))**
# Gradient of loss w.r.t bias.
**db = (1/m)*np.sum((y_hat - y))**
**return dw, db**
向输入数据添加要素的函数
让我们编写一个函数,向输入中添加尽可能多的特性。这里我们将定义一个名为degrees的变量,它是一个 python 列表。
无论我们将什么值传递到这个列表中,我们都会向输入添加一个新的特性,即
X^(value)。例如,如果我们将 2 和 3 传入列表,我们将特性X²和X³添加到输入中。
请参见注释(#)。
**def x_transform(X, degrees):**
# X --> Input.
# degrees --> A list, We add X^(value) feature to the input
# where value is one of the values in the list.
# making a copy of X.
**t = X.copy()**
# Appending columns of higher degrees to X.
**for i in degrees:
X = np.append(X, t**i, axis=1)
return X**
培训功能
训练函数包括初始化权重和偏差以及具有小批量梯度下降的训练循环。
请参见注释(#)。
**def train(X, y, bs, degrees, epochs, lr):**
# X --> Input.
# y --> true/target value.
# bs --> Batch Size.
# epochs --> Number of iterations.
# degrees --> A list, We add X^(value) feature to the input
# where value is one of the values in the list.
# lr --> Learning rate.
# Adding features to input X.
**x = x_transform(X, degrees)**
# m-> number of training examples
# n-> number of features
**m, n = x.shape**
# Initializing weights and bias to zeros.
**w = np.zeros((n,1))
b = 0**
# Reshaping y.
**y = y.reshape(m,1)**
# Empty list to store losses.
**losses = []**
# Training loop.
**for epoch in range(epochs):
for i in range((m-1)//bs + 1):**
# Defining batches.
**start_i = i*bs
end_i = start_i + bs
xb = x[start_i:end_i]
yb = y[start_i:end_i]**
# Calculating hypothesis
**y_hat = np.dot(xb, w) + b**
# Getting the gradients of loss w.r.t parameters.
**dw, db = gradients(xb, yb, y_hat)**
# Updating the parameters.
**w -= lr*dw
b -= lr*db**
# Calculating loss and appending it in the list.
**l = loss(y, np.dot(x, w) + b)
losses.append(l)**
# returning weights, bias and losses(List).
**return w, b, losses**
预测功能
请参见注释(#)。
# Predicting function.**def predict(X, w, b, degrees):**
# X --> Input.
# w --> weights (parameter).
# b --> bias (parameter).
degrees --> A list, We add X^(value) feature to the input
# where value is one of the values in the list.
# Adding degrees to input X.
**x1 = x_transform(X, degrees)**
# Returning predictions.
**return np.dot(x1, w) + b**
训练和绘制预测
使用train功能训练数据。
仅向
degrees列表传递 2。您可以尝试传递任何其他数字,看看会发生什么。
w, b, l = train(X, y, bs=100, degrees=[2], epochs=1000,
lr=0.01)**# Plotting**
fig = plt.figure(figsize=(8,6))
plt.plot(X, y, 'y.')
plt.plot(X, predict(X, w, b, [2]), 'r.')
plt.legend(["Data", "Polynomial predictions"])
plt.xlabel('X - Input')
plt.ylabel('y - target / true')
plt.title('Polynomial Regression')
plt.show()

预测用红色表示;作者图片
看起来不错。
由于我们已经在列表
losses中收集了每次迭代的损失,试着绘制losses对迭代(epochs)的图,看看在训练时损失是否下降。
r2 得分
计算我们预测的 r2 分数,看看我们做得有多好。
供参考—R2 分数是多少?
def r2_score(y, y_hat):
return 1 - (np.sum((np.array(y_hat)-np.array(y))**2)/
np.sum((np.array(y)-np.mean(np.array(y)))**2))r2_score(y_train, predict(x_train, w, b, [2]))
>>**0.9541881152879292**
这是一个很好的 r2 分数。
感谢阅读。对于问题、评论、顾虑,请在回复部分进行讨论。更多的 ML 从零开始即将推出。
看看从零开始的机器学习系列—
- 第 1 部分:Python 中从头开始的线性回归
- 第二部分:Python 中的局部加权线性回归
- 第 3 部分:使用 Python 的正规方程:线性回归的封闭解
带 Spark 3.1 的多项式
多语言笔记本应用程序。在同一个笔记本中使用 python 和 scala 进行原型制作和试验。

介绍
我看过冰间湖,我想,大概在 2019 年 10 月,在这篇博文中。那一刻我就爱上了它。对我来说,知道 python 几乎不费吹灰之力就试用了 scala,简直是梦想成真。我可以比较 Python 和 Scala 的执行时间,尝试 Dataset 和 Dataframe,做一些教程,甚至帮我写一篇博客。但当时,他们说它并不完美,还处于初级阶段。一年半过去了,我回来重新点燃这个火花(一语双关)。
我计划在我的 GitHub 中创建一个互动研讨会,提供示例、跟进和解释+动手实践任务,polynote 非常适合这里。有大量的教程,但不知何故,没有一个能引起我的共鸣,直到我开始用 spark 做更多的事情。通过向每个人提供我的内容,我希望能为社区做更多的贡献。请记住,准备适当内容需要时间;准备好了我可能会再发一篇博文:)
初步调查结果
最近(4 月 2 日),他们发布了 0.4.0 版本。从上一个版本发布到现在的半年时间里,他们实现并修复了很多东西。此外,我只看到了 spark 2.4.5 版本,但没有 spark 3.1.1(或 3.0.1),这两个版本已经发布了相当长一段时间。试图更新最新的功能,我认为使用最新的版本会很好。
我注意到在他们的 GitHub 里,他们有一个 Docker 文件夹,里面有基础镜像,spark 2.4.5 版本等等。我心想——我做了 docker 101 ,我认为我完全有能力使用基本图像和 Docker 知识创建自己的 Docker 图像。
Docker 图像
Dockerfile 的大部分内容都是从基础映像中重新使用的。做了一些调整以使用不同的 Scala 版本(2.12)和指定的 polynote 版本:
ARG *POLYNOTE_VERSION*="0.4.0"
ARG *SCALA_VERSION*="2.12"
ARG *DIST_TAR*="polynote-dist.tar.gz"
我必须添加的另一个调整是处理 spark 安装:
- 使用不同的工作目录
- 下载 spark 3.1.1(请记住,我特意选择了这个链接,因为它离我最近,下载速度会更快)
- 进行一些重命名并指定 ENV 变量
所以它看起来像这样:
WORKDIR /opt/spark/work-dirRUN wget [https://apache.mirror.serveriai.lt/spark/spark-3.1.1/spark-3.1.1-bin-hadoop3.2.tgz](https://apache.mirror.serveriai.lt/spark/spark-3.1.1/spark-3.1.1-bin-hadoop3.2.tgz)
RUN tar -xvf spark-3.1.1-bin-hadoop3.2.tgzRUN mv spark-3.1.1-bin-hadoop3.2/* /opt/spark
WORKDIR /opt/spark
ENV *SPARK_HOME*=/opt/spark
ENV *PATH*=$*PATH*:$*SPARK_HOME*/bin:$*SPARK_HOME*/sbin
ENV *PYSPARK_PYTHON*=python3
可执行文件和别名
也请阅读他们的自述以及所有的免责声明和更广泛的解释。
在他们的文档中,他们建议使用创建的 config.yml 文件运行 docker image,如下所示:
listen:
host: 0.0.0.0storage:
dir: /opt/notebooks
mounts:
examples:
dir: examples
所以我们让我们的 docker 映像监听所有端口。出于 PoC 学习的目的,这很好,但是您不希望在生产中出现这种情况!
如果我们在 Polynote 中创建新的笔记本,它们将出现在笔记本文件夹中。你可以用更多的文件夹来组织它,等等。,合你心意。
他们打包了一些例子来理解它是如何工作的,我们把它们放在最后三行。
根据这篇博客文章,为了让我的生活更轻松,我改装了我的终端。为了快速访问 Polynote,我更喜欢在一些命令上使用别名。为此,我创建了 run_polynote.sh ,,这非常简单:
docker build -it polynote_local:latest .
docker run --rm -it -p 127.0.0.1:8192:8192 -p 127.0.0.1:4040-4050:4040-4050 -v `pwd`/config.yml:/opt/config/config.yml -v `pwd`/notebooks:/opt/notebooks/ polynote_local:latest --config /opt/config/config.yml
我正在构建我的 docker 文件,并将其标记为 polynote_local:latest ,然后用我之前创建的 config.yml 运行它。
现在我需要修改我的。zshrc:

作者图片
通过执行
run_polynote
我会让我的冰穴运行。

作者图片
确认
为了确定我们所做的一切是否正确,我们需要打开 polynote 并运行代码
**spark.version**
了解我们是否在使用 3.1.1。
打开 0.0.0.0:8192 我们可以看到:

作者图片
让我们创建一个新笔记本;在我的例子中,我将其命名为“testing_for_demo”我们首先看到的是配置和依赖关系。在这里,我们可以指定多个不同的依赖项(Scala 或 Python),Spark 配置等。
我注意到的一件事是,如果我没有通过任何 spark 配置运行 spark.version ,我会得到:

作者图片
为驱动程序内存添加简单配置使其工作:

作者添加了简单的火花配置图片
我们看到了什么:

我做了相同代码的两个版本,一个用 Python,另一个用 Scala。他们两个都很管用。
我真正喜欢的是与 docker 图像捆绑在一起的 Scala Spark to Pandas 示例。在那里,他们在 scala 中创建了一个数据框,并从 python 中的另一个节点访问它!

作者图片
结论
polynote——网飞的开源笔记本允许你运行你的 scala spark 代码,并从 python 中的另一个 note 访问它,允许你利用两个世界的优势。对于这篇博文,你可以在我的 GitHub 中查看所有的代码和文档。
PolyViT:在图像、视频和音频方面共同培训视觉转换者
深度学习中的变形金刚
多模态是变压器时代高效和可扩展解决方案的前进方向

哈尔·盖特伍德在 Unsplash 上拍摄的照片
介绍
T2017 年发布的著名 Transformer [1]架构非常出色,打破了自然语言处理领域几乎所有的 SOTA 基准。然后,它被用于其他领域,如图像&视频分类、对象检测、音频分类,甚至在没有任何重大修改的情况下用于生成网络。众所周知的架构之一——视觉转换器[2]在 ImageNet 分类任务上取得了 SOTA 结果,从而奠定了计算机视觉领域的基础。我们现在有围绕深度学习保护伞下每个领域的变形金刚进行的主流研究。所实现的架构和结果是异常的,但是抛出了一个简单的问题,
我们是否需要每种设备都有一个模型?
我们能为多个数据集训练一个单一的变压器模型吗?poly vit【3】旨在通过采用可用于在图像、视频和音频数据集上训练单个模型的协同训练方法来解决这一问题。就减少多种模式的工作量而言,这将是巨大的。
注:模态可视为“处理的输入类型”
背景
多任务学习在深度学习中并不新鲜。多任务学习的一个抽象想法是开发能够概括的架构,同时在多种类型的数据集(如图像、视频、语音、点云等)之间共享参数。重要的观察结果是,与单任务模型相比,表现有所下降,这反过来阻碍了对这一特定假设的更多研究。当更多的任务组合在一起时,性能进一步下降,这导致很少或没有鼓励研究人员进一步探索这个领域。就初始化和校准参数以实现稳定的训练/拟合而言,训练多任务模型也相当棘手。
这不也是一种模仿人脑的尝试吗?人脑可以毫不费力地学习各种形态的模式。
感知者[4]架构是一个转换器,可以使用潜在标记的交叉注意处理来处理、标记和训练不同的模态。但这不是一个参数共享的网络。我们有类似单元[5]和联合视频和图像编码器[6]的架构,它们采用多模态策略,但性能并不优于单任务模型。
理念与建筑
为了设置 PolyViT 的上下文,我们首先需要了解 3 种转换器架构,它们在各自的数据集模态中是最好的,
- 图像视觉转换器(ViT)
- 视频视觉转换器(ViViT)7用于视频
- 音频声谱图转换器(AST)〔8〕
查看 ViT 上的这个博客,全面了解这个架构。ViViT 是 ViT 的延伸,与 ViT 中观察到的 2D 图像补丁相比,唯一显著的区别是称为小管的 3D 补丁(时空)。AST 只是一个在声谱图(语音/音频的时间-频率表示)图像上训练的 ViT。当在 ImageNet-21K 或 JFT 等数据集上进行预训练时,这三个模型都可以实现最佳性能。

PolyViT 架构说明了联合训练设置。图片鸣谢——波利维特论文
来到 PolyViT,我们可以在上图中看到编码器是如何在设备间共享的。基础架构是一个预训练的 ViT,增加了特定任务的标记器和注意力头。对于三种模态图像、视频(帧序列)和音频中的每一种,我们都有单独的输入和位置嵌入以及类标记。编码过程是特定于模态的,以确保它满足不同数量的记号/模态。在训练期间,它在每一遍中执行来自一个模态的一个任务(图像模态中的图像分类任务)。
合作培训
该程序包括对不同的任务使用相同的超参数集,但是使用来自相同设备的小批量。通过随机梯度下降(SGD)来处理优化。构建小批量有多种方法——逐个任务、交替、累积梯度、均匀采样和加权采样。我们使用术语 U 来表示联合训练中 SGD 步骤的总数。

小批量取样方法。图片致谢— PolyViT 论文
从上面的方法中我们可以观察到,逐个任务的采样遵循一个顺序,其中 SGD 步骤应用于每个任务。任务顺序是随机的。交替采样是(确定性的)重复的,SGD 对每个任务执行一次。均匀采样包括从概率为 1/T 的均匀分布中构建批次,导致每个任务有 U/T 个步骤。加权采样是一种调度,它根据单任务模型中的步骤数为每项任务分配一定的权重。最后,累积渐变过程与其他过程有很大不同——在每个任务的单个向前和向后属性之后存储渐变,然后对整个批处理(包含多个任务)应用该参数。
我的直觉是,我们应该有一个任务,并只为此应用参数更新;在一个批处理中有各种各样的任务和更新将会把训练搞得一团糟。
无论使用哪种采样技术,都会导致定义的 n 任务的参数共享,这是 PolyViT 的主要目标之一。为了支持大量的模态,作者引入了一个特殊的模态特定层 L(adapt ),它在标记化之后立即应用。
结果和基准
现在,我们将观察 PolyViT 对各种数据集(如 ImageNet-1K、CIFAR-10 & 100、Kinetics400、Moments in Time (MIT)、AudioSet 和 VGGSound)的每种任务采样方法的结果。

PolyViT 接受了 9 项任务培训的各种任务采样方法的培训。L(adapt) = 0,这意味着不使用特定于模态的层。图片来源——波利维特论文
结果重申了这样一个事实,即梯度更新应该是特定于任务的,以获得稳健的结果,并且如果是在逐个任务的采样中随机采样,则它的性能非常差。加权时间表被认为是最好的,因此在所有随后的实验中使用。这里要注意的一点是,每个任务的步骤数是从其单个任务基线得出的,这导致了与单独训练的 9 个任务相同的计算资源。太棒了,不是吗?

与单任务比较基准相比具有不同 L(适应)值的 PolyViT。图片鸣谢—波利维特论文
在观察上述结果后,为单一模态(3 个模型,263 米)训练的 PolyViT 在 9 个任务中的 7 个任务中表现优于 SOTA 基准。对所有 9 项任务使用共同训练,它与 SOTA 结果相差 0.2-0.3%,参数减少了 3 倍(93M)。L(adapt) = 0 的 PolyViT 几乎在单任务基线的 1%以内,参数减少了 8 倍(773M 到 93M)。此外,我们可以得出结论,单模态联合训练提高了每个模态内较小数据集的准确性,证实了正则化的效果和适应较小数据集的能力,这在标准 ViT 模型中是看不到的。
类似地,在经过训练的 PolyViT 上进行实验,并在线性头上对下游任务进行评估。多模态 PolyViT 在 11 项任务中表现出色。此外,在一种模式内性能改善的鼓舞下,作者联合训练了单一模式的 PolyViT,其结果是在参数显著减少的情况下超过了 SOTA 基准。请参考 PolyViT 论文,了解上述实验和训练设置的更多细节(附录部分)。
我的想法
在我看来,这是一次出色的尝试,取得了 SOTA 级别的成果。我们有很多优点,如参数效率、单个模型而不是 n 个特定于任务的模型、易于实现和维护。推理时间在整个模态和任务中也是恒定的。
PolyViT 现在可以在边缘设备上部署重量相对较轻的多模变压器型号,而不是重量较重的特定任务型号。想想一个模型在一台设备上完成多项任务的可用性。这些途径肯定可以扩展和升级,这样我们就有希望拥有一个“用于大多数 ML 任务的单一模型”。此外,对较小数据集的正则化效果的事实是一个受欢迎的补充,使个体从业者/研究人员更容易防止过拟合问题。
我观察到的唯一一个小警告是,它与添加了 L(适应)层的完全参数共享模型的初始假设相反。由于 L(adapt) = 0 设置的模型的结果非常好,我们可以不予考虑,将 PolyViT 视为完全参数共享模型😊
结论
具有联合训练和单模态设置的 PolyViT 在 5 个标准视频和音频分类任务中超过了之前的 SOTA 结果。将其更改为多模态设置,它的效率很高,性能几乎没有下降(0.5–1%)。事实上,它可以学习和推广多任务,多模态(领域)与接近 SOTA 的性能本身就是一个巨大的胜利。第二大问题是在多模态设置中观察到的参数效率,与它们的单任务基线相比。由于具有鲁棒的表示,微调变得更加容易,无需任何超参数调整。仍然有实验的余地,可以使用更大的数据集,如 ImageNet-21K 数据集,并扩展到其他领域。
我的更多研究论文评论
[## MLP 混合器:全 MLP 的视觉建筑
towardsdatascience.com](/mlp-mixer-an-all-mlp-architecture-for-vision-7438fac99a06)
参考
[1]变形金刚:https://arxiv.org/pdf/1706.03762.pdf
[2]视觉变形金刚:【https://arxiv.org/pdf/1409.1556.pdf】T2
[3]波利维特:【https://arxiv.org/pdf/2111.12993.pdf】T4
[4]感知者:https://arxiv.org/pdf/2103.03206
[5]单位:https://arxiv.org/pdf/2102.10772.pdf
[6]联合视频和图像编码器:https://arxiv.org/pdf/2104.00650.pdf
7视频视觉转换器:https://arxiv.org/pdf/2103.15691
[8]音频声谱图变换器:https://arxiv.org/pdf/2104.01778
庞德奈特解释道
为 MNIST 数据集实现一个思考网络
你可以在 this GitHub repo 中找到这篇文章的代码。现在就克隆它!
你可以用这个 Colab 笔记本运行你阅读的代码。现在打开它!

如果我们真的想实现机器人霸主的反乌托邦未来,我们必须认识到,当前的人工智能永远不会成功。大多数现代神经网络都缺少某些东西,这是阻止它们统治世界的一个关键因素:它们无法思考。
幸运的是, DeepMind 最近发布了 PonderNet ,这是一个可能会让任何网络思考的框架。突然间,未来又变得光明了。
玩笑归玩笑,思考是一个重要的概念,可能会对我们如何设计新模型产生严重的影响。在这篇文章中,我们将讨论这个理论(你会惊讶于它有多简单!)和实现了一个在 MNIST 数据集上执行图像分类的 PonderNet 版本。在后面的部分,我们将进行一些实验来确定思考能力的影响力有多大。
我们将使用py torch Lightning作为我们的框架(因为它实在是太棒了);如果你对它不熟悉,现在是学习基础知识的好时机!对于日志记录,我们将使用 权重&偏差 (也因为它实在是太棒了)。我强烈建议您尝试一下,但是如果您喜欢不同的日志程序,您只需要修改一行代码(因为 PyTorch Lightning 实在是太棒了)。
1.动机
这一切都很好,但是思考它到底意味着什么呢?作者是这样表述的:
思考就是根据任务的复杂程度来调整计算预算。
根据这个定义,很明显,机器学习研究人员和工程师一直在思考:每次他们选择特定数量的隐藏层,选择不同的 GPU 来训练他们的模型,或者做出任何影响网络架构的决定。
同样清楚的是,大多数电视网不能够考虑 T7。我们可以争辩说,数字 6 的图像确实很容易识别,而人们可能需要更多的时间来区分 1 和 7;然而,CNN 将花费相同数量的资源来预测这两种图像的标签。这主要是由于神经网络的刚性结构,以及它们如何充当黑盒映射。
PonderNet 从之前的研究中创新出来,能够将更多的资源分配给它认为需要的投入。如果我们希望模型学习超越当前的最先进水平,这是一个关键属性。这也是在经典算法的背景下思考神经网络的一步,可以为该领域带来许多新鲜的想法(不要错过美丽的附录 E )。
2.PonderNet 框架
在这一节中,我们将放下 PonderNet 背后的所有理论。首先,让我们试着理解它是如何在高水平上调整其计算预算的。
直觉
假设我们有一个想要解决的任务(例如,对 MNIST 的数字进行分类!)和解决它的模型(如 good ol' CNN)。传统的方法是简单地处理一次输入并产生一个输出。相比之下,PonderNet 框架允许输入被多次处理,并且能够找到合适的时间停止并输出结果。

图 PonderNet 的选项。(图片由作者提供)
概括地说,PonderNet 做了以下工作:
- 处理原始输入。
- 产生一个预测和一个在当前步骤停止计算的概率。
- 抛硬币决定是“暂停”还是“继续”。
- 如果我们暂停,输出最新的预测;否则,再次处理输入以及一些上下文信息。
图 1 通过将暂停选项表示为二叉树总结了这一思想。
形式定义
更正式地说,我们可以将 PonderNet 框架定义为满足以下等式的阶跃函数 s (通常是神经网络):

这里, x 表示原始输入,两个 h 表示通过不同步骤传播的隐藏状态, y 是当前步骤的输出,λ是在当前步骤停止的概率。
换句话说,这意味着在每一步,PonderNet 都获取原始输入和最新的隐藏状态,并为新的一步产生一个预测、停止的概率和更新的隐藏状态。它将以概率λ投掷一枚有偏向的硬币,以决定是暂停并输出 y 还是通过进一步传播 h 来继续。

图 2:展开的 PonderNet。(图片由作者提供)
图 2 非常直观地显示了隐藏状态如何流经所有步骤,以及每一步如何产生一对输出和停止的概率。如果你熟悉 RNNs,你会在结构中发现一些相似之处。
λ的含义
值得注意的是,从技术上讲,λ是在当前步骤停止的概率,假定在之前的步骤中没有发生停止。这使得我们可以将λ视为一个伯努利随机变量的概率,它告诉我们是否应该在当前步骤停止。如果我们希望找到在当前步骤停止的无条件概率(此后为 p ,它还必须包括在先前步骤中不停止的概率:

思考步骤
我们通常只允许特定的最大数量的“思考步骤”。这意味着我们将在最后一步强制λ为 1,从而保证停止。
在推理过程中,我们并不明确需要这样的限制,因为我们可以让网络无限期地运行,直到其中一个硬币的投掷使它自然停止;然后,我们将输出在该步骤中获得的 y 。尽管如此,限制思考的步骤仍然是一个好主意,因为从理论上讲它可以永远运行。
在训练期间,由于损失函数,思考步骤的界限是必需的,正如我们现在将看到的。
训练思考网
像几乎所有的神经网络一样,PonderNet 试图优化一个损失函数:

让我们来分析一下。 L 可分为两种损失,即重构损失和正则化损失(类似于 VAEs);这两者之间的权衡由超参数β调节。在这两种情况下,损失可以分解成 N 个项;这是我们思考步骤的最大数量。
重建损失是非常直观的:对于我们思考的每一步,我们计算该步输出的损失,并以我们在其中停止的无条件概率进行加权。从这个意义上说,重建损失无非是跨越所有步骤的预期损失。请注意,在整个计算过程中,我们使用的是我们试图解决的任务的潜在损失函数(在多类分类的情况下,交叉熵)。
正则化损失在停止应该如何表现方面引入了偏差。它试图最小化由有质网生成的暂停分布和具有某个超参数λ p 的先验几何分布之间的KL-散度。对于那些不熟悉 KL-divergence 的人,可以随意查看下面的链接;直观上,我们所说的是我们希望网络产生的所有λ都接近λ p.
这个正则项的影响是双重的。一方面,我们将预期的思考步骤数偏向 1/λ p (因为这是几何分布的预期值)。另一方面,它促进探索通过在任何步骤中给予正概率来停止,不管它有多远。
3.为 MNIST 实施 PonderNet
没那么糟吧。我们现在准备开始动手了!请记住,在阅读时,您是按照本文的 Colab 笔记本来运行代码的。如果你想在本地运行它,所有的代码(和一些额外的东西!)可以在这个 GitHub repo 中找到;如果你发现任何错误,不要犹豫,打开一个问题!
数据模块
让我们首先解决数据模块的问题。这里没有什么新奇的东西,除了这个数据模块允许你拥有多个测试数据加载器(这是一个惊喜的工具,以后会对我们有帮助!).
损耗
我们将损失中的每一项分别建模,作为扩展nn.Module的两个不同类别。让我们从重建损失说起。我们唯一需要做的是计算加权平均值,这很容易实现:
至于正则化损失,还有一些多余的细节要讲。为了计算生成的暂停分布和我们的先验之间的 KL-divergence,我们将首先制造我们的先验的值,这在初始化函数中完成。
最后,我们创建一个类来将这两个损失包装在一起,以便它们可以紧凑地传递给函数。
助手模块
为了使我们的生活更容易,我们为一个简单的 CNN 创建了一个类。这将用于将图像嵌入到 PonderNet 中的矢量表示中。
同样,我们创建了一个基本的多层感知器,将图像嵌入与 PonderNet 内部的隐藏状态结合起来。
PonderNet
最后,我们得到了重要的东西。为了更详细地评论LightningModule的不同部分,它内部的一些函数将在单独的代码片段中显示;只要记住他们都属于同一类!
我们的 PonderNet 具体实现由多个子模块组成。首先,CNN 将图像嵌入到矢量表示中。该向量与对应于前一步骤的隐藏状态连接,并通过 MLP 获得当前步骤的隐藏状态。这又被推过两个不同的线性层,一方面获得预测的逻辑,另一方面获得 lambdas 的逻辑。下面的片段显示了这些子模块如何被声明为 PonderNet 的一部分,以及损耗和一些指标:
我们现在到达了可以说是代码中最复杂的部分:向前传递。获得 h , y 和λ是直截了当的,记住,为了获得λ,你必须使用 sigmoid 函数,否则你就不能强迫它成为一个概率。
由于我们是成批操作的,所以当涉及到网络停止的思考步骤时,我们需要找到一种方法来单独跟踪每个元素。为此,我们维护一个向量halting_step,对于每个元素,如果 PonderNet 尚未停止,则该向量为 0,否则停止的步骤为 0。当然,更新halting_step包括在每一步从伯努利分布中提取,以确定网络是否应该停止。
最后,我们还维护了一个向量un_halted_prob,它帮助我们以一种廉价而快速的方式获得每一步的 p 的值。
最后,我们返回所有批处理元素和所有步骤的所有预测、所有批处理元素和所有步骤的所有 p 以及所有批处理元素的暂停步骤。
由于需要处理前向传递的输出以获得损失(在一些情况下我们需要这样做),我们定义了一个帮助函数来计算损失、预测和一些其他指标。
if条件有助于规避我个人遇到的一个技术问题。在训练的早期阶段,对于选定的步骤,一些 p 值可以是 0。当这通过正则化损失时,零点变成负无穷大,网络最终开始返回 nan。忽略包含值为 0 的 p 的元素解决了这个问题,并且这些相同的元素在后面的时期被正确分类。
最后,PyTorch Lightning 模块中需要的其他常用功能可以在下面找到。这些包括日志和一些回调,使培训更加用户友好。
运行插值实验
我们基本上完成了!这就是 PyTorch 闪电的美妙之处。使用下面的代码片段,我们将能够在香草 MNIST 上运行一个基本的实验;我们的记录者将在整个训练过程中记录损失(一起和单独的)、准确性和思考步骤。如果您想使用不同的记录器,您只需要从 PyTorch Lightning 导入支持的类,并在第 40 行中相应地实例化它;就这么简单!
下面是我们在 MNIST 测试集上评估时获得的正确率和思考步骤的平均数。正如我们所见,PonderNet 做得很好。更有趣的是,思考步骤的平均数量非常接近 5,这是 1/λ p 的值,即预期的步骤数量;这意味着我们的正规化确实在起作用!

4.我们真的在思考吗?
运行上面的代码应该可以让你相信,PonderNet 至少可以在 MNIST 上获得相当高的精度…但是我们使用了它的任何属性吗?难道我们不能用一个简单的 CNN 取得类似的结果吗?我们如何知道它确实对更难的输入考虑得更久?我们将试图通过外推实验来回答其中的一些问题。
阅读原始论文,人们可能会对实验如何在玩具数据集或任务上进行感到有点沮丧,这些任务是如此复杂以至于不可能解释任何东西。MNIST 数据集提供了一个中间地带,在这里,任务肯定没有被设计为使用 PonderNet 产生预期的结果,并且其结果在某种程度上是可以解释的。
我们实验的前提是旋转的图像更难分类。遵循与 PonderNet 论文中提出的框架相似的框架,我们将对“稍微硬”的输入进行训练,并对一系列“硬”输入进行评估。特别是,我们将对旋转了 22.5 度的图像进行训练,并对旋转了 22.5 度、45 度、67.5 度和 90 度的图像进行评估。我们预计准确率会逐渐下降(一些图像一旦旋转到这种程度,甚至可能无法分类),但希望我们看到步骤数的增加,这表明网络发现任务更难了,并决定分配更多资源给它。下面是这样一个实验的代码:****
我们进行这个实验后得到的结果有点争议。一方面,在任何旋转数据集上进行测试确实需要的平均步骤数明显高于插值实验所需的步骤数,这意味着 PonderNet 认为对旋转图像进行分类是一项更困难的任务。
然而,另一方面,网络似乎需要更少的步骤来实现更明显的旋转,这是违反直觉的。我们希望 PonderNet 对高度倾斜的图像不确定,从而分配更多的资源用于它们的分类和更长时间的思考,但似乎它反而变得不正确。精度也会降低,尽管在这种情况下,这是意料之中的。

注意:此处的数据加载器分别对应 22.5 度、45 度、67.5 度和 90 度旋转。
5.结论
PonderNet 是深度学习领域的一个很好的补充。它以数学为基础的设计选择有足够的理由让对它的可能性感到兴奋,原始论文中呈现的结果令人鼓舞。然而,可悲的是,并没有多少努力去诚实地解释这个网络真正的能力。
我们试图通过实现 MNIST 的 PonderNet(该领域的通用基准)来阐明这个问题,并通过在不同难度的任务中训练网络来进行实验。
我们的结果是不确定的,因为在某些方面它们与我们的预期一致,而在另一些方面则不一致。这可能有多种原因。一方面,很有可能旋转的角度对于这个特定任务的复杂性来说不是一个很好的启发,我的人类偏见干扰了实验设计。另一方面,我们不能放弃这样一种可能性,即 PonderNet 并不是我们所希望的全能框架;毕竟这方面还有很多研究要做。
总而言之,我希望你今天学到了一些东西;也许是我启发你在自己的项目中尝试了 PonderNet!我很乐意听到您的任何意见、问题或建议。感谢您的阅读!****
6.承认
如果没有三个主要来源,这篇博文是不可能发表的。
- 第一个是Yannic Kilcher 的 YouTube 视频解释了 PonderNet 的基础知识;如果你没有订阅他的频道,我不知道你在做什么!
- PonderNet 实现的一个重要部分是从 MildlyOverfitted 的这个 GitHub repo 借来的。虽然在我偶然发现他的代码之前,我已经有了一个运行的原型,但我还是忍不住使用了他的酷把戏!
- 最后,一些样板代码取自本教程中关于 PyTorch Lightning 的和权重&偏差。
最后,我要感谢我的导师 Fabian Laumer,他鼓励我尝试不同的东西,作为我的研讨会演示的一部分。我可能在这个小项目上损失了几十个小时,但我确实学到了很多!
参考
[1] A .巴尼诺,j .巴拉格尔,c .布伦德尔,庞德奈特:学会思考 (2021),arXiv: 2107.05407。
[2] A. Graves,递归神经网络的自适应计算时间 (2017),arXiv: 1603.08983。
带置信区间的简易 ROC 曲线
在合并的 ROC 图中显示分类器性能的稳健性

凯尔·格伦在 Unsplash 上的照片
在机器学习中,一个至关重要的规则是,在你对单独使用训练数据的结果感到满意之前,你不应该根据以前看不见的数据(也就是你的测试集)对你的模型进行评分。
为了显示模型的性能和稳健性,可以在训练数据中使用多个训练集和测试集。为了避免混淆,如果它是训练数据的一部分,我们称之为验证集。将训练数据分成多个训练集和验证集称为交叉验证。集合的比率、大小和数量取决于交叉验证方法和训练集的大小。最常见的可能是 K-Fold,但是根据训练集的大小,您可能希望尝试 Bootstrapping 或留一法。每种方法都有优点和缺点,比如每折叠增加训练集或验证集的大小。我就不赘述了,Medium 上有很多关于这个话题的牛逼文章。
因此,我们使用某种交叉验证和分类器来多次训练和验证模型。这种方法会产生一系列得分结果。可能最常见的度量是 ROC 曲线,用来比较彼此之间的模型性能。它没有考虑到阶级的不平衡,这使得它可以与其他用不同数据训练但在相同研究领域的模型进行比较。ROC 曲线的一个很好的补充是 PRC 曲线,它考虑了类别不平衡,并有助于判断用相同数据训练的不同模型的性能。但是,在 Medium 上已经有很多关于各种指标的精彩文章了。为了得到一条 ROC 曲线,你基本上绘制了真阳性率(TPR)对假阳性率(FPR)。为了表明模型的性能,您需要计算 ROC 曲线下的面积(AUC)。
假设我们在 100×5 重交叉验证中训练了一个 XGBoost 分类器,得到了 500 个结果。对于每个折叠,我们必须提取 TPR(也称为敏感性)和 FPR(也称为特异性),并计算 AUC。基于这一系列的结果,你实际上可以给出一个置信区间来显示你的分类器的稳健性。
由于这是专门为了展示如何构建一个汇集的 ROC 图,我将不运行特征选择或优化我的参数。
首先,我们导入一些包并加载一个数据集:
import numpy as np
import pandas as pd
import plotly.graph_objects as go
from tqdm.notebook import tqdm
from sklearn.model_selection import RepeatedKFold
import xgboost as xgb
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_auc_score, roc_curveurl = '[https://archive.ics.uci.edu/ml/machine-learning-databases/heart-disease/processed.cleveland.data'](https://archive.ics.uci.edu/ml/machine-learning-databases/heart-disease/processed.cleveland.data')
df = pd.read_csv(url, header=None)
有几个丢失的值标为“?”,我们必须首先删除它们:
for i in range(13):
df[i] = df[i].apply(lambda x: np.nan if x=='?' else x)
df[i] = df[i].astype(float)
df = df.dropna()
克利夫兰癌症数据集有一个编码为 0-4 的目标,我们将把它二进制化为类 0,所有目标编码为 0,1,所有目标编码为 1-4。
def binarize(x):
if x==0:
value=0
else:
value=1
return valuedf[13] = df[13].map(binarize)
接下来,我们定义我们的特征和标签并分割数据:
X = df.drop(13, axis=1)
y = df[13]
现在我们对数据进行分层分割,以保留潜在的阶级不平衡:
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=101, stratify=y)
我们现在可以使用我们的火车设置来获得折叠。我使用重复的 k-fold 来获得更多的分数结果:
cv = RepeatedKFold(n_splits=5, n_repeats=100, random_state=101)
folds = [(train,test) for train, test in cv.split(X_train, y_train)]
让我们建立一个字典来收集我们的结果:
metrics = ['auc', 'fpr', 'tpr', 'thresholds']
results = {
'train': {m:[] for m in metrics},
'val' : {m:[] for m in metrics},
'test' : {m:[] for m in metrics}
}
要初始化 XGBoost,我们必须选择一些参数:
params = {
'objective' : 'binary:logistic',
'eval_metric' : 'logloss'
}
现在是时候运行我们的交叉验证并将所有分数保存到我们的字典中了:
dtest = xgb.DMatrix(X_test, label=y_test)
for train, test in tqdm(folds, total=len(folds)):
dtrain = xgb.DMatrix(X_train.iloc[train,:], label=y_train.iloc[train])
dval = xgb.DMatrix(X_train.iloc[test,:], label=y_train.iloc[test])
model = xgb.train(
dtrain = dtrain,
params = params,
evals = [(dtrain, 'train'), (dval, 'val')],
num_boost_round = 1000,
verbose_eval = False,
early_stopping_rounds = 10,
)
sets = [dtrain, dval, dtest]
for i,ds in enumerate(results.keys()):
y_preds = model.predict(sets[i])
labels = sets[i].get_label()
fpr, tpr, thresholds = roc_curve(labels, y_preds)
results[ds]['fpr'].append(fpr)
results[ds]['tpr'].append(tpr)
results[ds]['thresholds'].append(thresholds)
results[ds]['auc'].append(roc_auc_score(labels, y_preds))
这是一个相当简单的程序。也有可能在xgb.cv方法中使用feval,将您的分数放入一个自定义函数中,但是我的体验是,这要慢得多,也更难调试。
现在我们有了来自 100 个交叉验证折叠的结果,我们可以绘制我们的 ROC 曲线:
kind = 'val'c_fill = 'rgba(52, 152, 219, 0.2)'
c_line = 'rgba(52, 152, 219, 0.5)'
c_line_main = 'rgba(41, 128, 185, 1.0)'
c_grid = 'rgba(189, 195, 199, 0.5)'
c_annot = 'rgba(149, 165, 166, 0.5)'
c_highlight = 'rgba(192, 57, 43, 1.0)'fpr_mean = np.linspace(0, 1, 100)
interp_tprs = []
for i in range(100):
fpr = results[kind]['fpr'][i]
tpr = results[kind]['tpr'][i]
interp_tpr = np.interp(fpr_mean, fpr, tpr)
interp_tpr[0] = 0.0
interp_tprs.append(interp_tpr)
tpr_mean = np.mean(interp_tprs, axis=0)
tpr_mean[-1] = 1.0
tpr_std = 2*np.std(interp_tprs, axis=0)
tpr_upper = np.clip(tpr_mean+tpr_std, 0, 1)
tpr_lower = tpr_mean-tpr_std
auc = np.mean(results[kind]['auc'])fig = go.Figure([
go.Scatter(
x = fpr_mean,
y = tpr_upper,
line = dict(color=c_line, width=1),
hoverinfo = "skip",
showlegend = False,
name = 'upper'),
go.Scatter(
x = fpr_mean,
y = tpr_lower,
fill = 'tonexty',
fillcolor = c_fill,
line = dict(color=c_line, width=1),
hoverinfo = "skip",
showlegend = False,
name = 'lower'),
go.Scatter(
x = fpr_mean,
y = tpr_mean,
line = dict(color=c_line_main, width=2),
hoverinfo = "skip",
showlegend = True,
name = f'AUC: {auc:.3f}')
])
fig.add_shape(
type ='line',
line =dict(dash='dash'),
x0=0, x1=1, y0=0, y1=1
)
fig.update_layout(
template = 'plotly_white',
title_x = 0.5,
xaxis_title = "1 - Specificity",
yaxis_title = "Sensitivity",
width = 800,
height = 800,
legend = dict(
yanchor="bottom",
xanchor="right",
x=0.95,
y=0.01,
)
)
fig.update_yaxes(
range = [0, 1],
gridcolor = c_grid,
scaleanchor = "x",
scaleratio = 1,
linecolor = 'black')
fig.update_xaxes(
range = [0, 1],
gridcolor = c_grid,
constrain = 'domain',
linecolor = 'black')
你可以通过使用 plotlys toself填充方法使代码更短,但是这样你在颜色或者上下边界的特定变化方面更灵活。这是我们 KFold 程序中验证集的分数结果:

验证集得分的 ROC
当你调整了你的模型,发现了一些更好的特性,优化了你的参数后,你可以通过将上面代码中的kind = 'val'改为kind = 'test',为你的测试数据绘制同样的图形。让我们看看这些模型在我们的测试集上表现如何:

测试集上分数的 ROC
当然,您可以使用相同的过程来构建精确召回曲线(PRC ),并保存每个折叠的特征重要性,以便在类不平衡较高时检查性能,或者了解您的特征的健壮性。
因为我们使用plotly来绘制结果,所以该图是交互式的,可以在streamlit应用程序中可视化。
希望这有助于一些数据科学家同事展示他们的分类器的性能。感谢阅读!
可怜人的 GPT-3:用 T5 变压器生成少量的镜头文本
用 HuggingFace 生成少量文本的极简代码

作者图片
我相信你们大多数人都听说过 OpenAI 的 GPT-3 和它疯狂的文本生成能力,只从几个例子中学习过。
用很少的训练数据喂养一个模型,并让它学会完成一项新颖的任务,这种概念被称为少镜头学习。
一个网站 GPT-3 示例收集了自 GPT-3 发布以来社区提出的所有令人印象深刻的应用程序。GPT-3 显示生成整个前端代码仅仅从一个网站看起来如何的文本描述。它显示了如何从一份简短的简介(描述)中生成一份完整的营销文案。你可以在网站上查看更多令人印象深刻的应用程序。
GPT-3 本质上是一个文本到文本转换器模型,其中你展示了输入和输出文本的几个例子(少量学习),稍后它将学习从给定的输入文本生成输出文本。
GPT-3 提示符如下所示。你输入几个例子(输入->输出),然后提示 GPT-3 填写一个输入。

作者图片
但是 GPT-3 不是开源的,对于你的用例来说,API 的成本可能会很高。
现在,在从事我的开源问题生成项目 Questgen.ai 时,我意识到了谷歌的 T5 Transformer 的文本到文本的能力,我决定让 T5 在一个未经训练的任务上做同样的事情,看看结果。
我必须说,通过让 T5 基础模型从几个例子中学习,结果相当令人印象深刻。
所以我给的任务是这样的—

作者图片
输入:
我通过把主形容词换成与相对的词,给出了几个句子对,它们是彼此的假句。
例句:猫是活着的
仅使用(~10 个样本)和< 5 mins of training T5 was able to generate impressive results on unseen sentences.
输出进行训练后:
T5 时刻提示:水手开心快乐。
T5 生成的句子(通过波束搜索从前 3 个响应中选出) :
- 水手很不高兴。****
- 那个水手很伤心。
提示到 T5:乌龟非常慢。****
T5 生成的句子(通过波束搜索从前 3 个响应中选出) :
- 乌龟跑得非常快。
- 乌龟非常快。
事不宜迟,让我们看看代码。
密码
对我来说,编写代码是一次有趣的探索。我不得不仔细阅读拥抱脸文档,想出用 T5 变压器写一个极简正向传递和反向传播代码。
Colab 笔记本
一个整洁有序的 Google Colab 笔记本在这里可以买到
1.1 安装
安装 HuggingFace transformers 并检查 Colab 上的 GPU 信息。
**!pip install transformers==2.9.0!nvidia-smi**
1.2 必要的导入和模型下载
首先从变形金刚库中导入一些必要的东西-
**import random
import pandas as pd
import numpy as np
import torch
from torch.utils.data import Dataset, DataLoaderfrom transformers import (
AdamW,
T5ForConditionalGeneration,
T5Tokenizer,
get_linear_schedule_with_warmup
)def set_seed(seed):
random.seed(seed)
np.random.seed(seed)
torch.manual_seed(seed)set_seed(42)**
初始化模式及其标记器-
**tokenizer = T5Tokenizer.from_pretrained('t5-base')
t5_model = T5ForConditionalGeneration.from_pretrained('t5-base')**
初始化优化器—
这里,我们提到在计算每个参数相对于损耗的梯度之后,T5 模型的哪些参数需要更新。
**# optimizer
no_decay = ["bias", "LayerNorm.weight"]
optimizer_grouped_parameters = [
{
"params": [p for n, p in t5_model.named_parameters() if not any(nd in n for nd in no_decay)],
"weight_decay": 0.0,
},
{
"params": [p for n, p in t5_model.named_parameters() if any(nd in n for nd in no_decay)],
"weight_decay": 0.0,
},
]
optimizer = AdamW(optimizer_grouped_parameters, lr=3e-4, eps=1e-8)**
1.3 培训数据
用于我们的 T5 少数镜头文本生成任务的完整训练数据(约 10 个样本)。
1.4 训练模型
这是一个简单的循环,我们用上面的样本训练 T5 模型。
在这里,我们训练 10 个历元,遍历来自我们训练数据的每个样本对。我们得到每一步的损失,计算梯度(损失。向后)和更新权重(优化器。step),这是所有深度学习算法的标准。
大部分的努力是在理解和让 T5 训练这个简单的循环:)
就是这样。根据 GPU 的不同,模型在 5 分钟或更短的时间内完成训练,并准备好用一些看不见的样本进行测试。
测试模型
测试句子:水手很快乐。
使用波束解码,我们得到代码生成的前 3 个句子
- 水手不高兴了
- 水手很伤心
- 水手很高兴
再来一个:
测试句子:乌龟非常慢。
使用波束解码,我们得到代码生成的前 3 个句子
- 乌龟非常慢
- 乌龟跑得非常快
- 乌龟非常敏捷
如你所见,T5 能够生成一个给定句子的假句子,即使它之前在训练中没有见过那些形容词或句子单词。
奖金
如果你已经走到这一步,我有一个奖励给你:)
使用 HuggingFace 的最新版本在 Pytorch Lightning 格式中找到相同的代码
使用自然语言处理的问题生成——教程
我推出了一个非常有趣的 Udemy 课程,名为“使用 NLP 生成问题”,扩展了这篇博文中讨论的一些技术。如果你想看一看,这里是链接。
结论
希望你喜欢我们为少数镜头文本生成任务探索 T5 的方式,就像 GPT 3 一样。
当我去年开始探索 T5 时,我意识到了它的潜力。它可以非常有效地完成相当多的文本到文本的任务。
祝 NLP 探索愉快,如果你喜欢它的内容,请随时在 Twitter 上找到我。
如果你想学习使用变形金刚的现代自然语言处理,看看我的课程使用自然语言处理的问题生成
视频表征学习的流行下游任务
用于评估长教学视频的视频表示学习的常见下游任务的总结。
什么是表征学习?
表征学习是一个研究领域,其重点是如何学习不同信号源的紧凑的数字表征。这些信号通常是视频、文本、音频和图像。这项研究的目标是将这些表征用于其他任务,如查询信息。一个众所周知的例子是在 YouTube 上搜索视频时:用户提供文本关键词,导致 YouTube 返回一组与这些词最相似的视频。
在计算机视觉文献中,通过训练深度学习模型将原始输入嵌入(或转换)到数字向量来学习表示。当嵌入视频、音频或文本时,数字向量通常是多维的,以保持时间关系。研究人员训练这些模型的方式千差万别。下游任务是如何评估这些方法,也是本文的重点。
数据集
我看过的很多论文都是用数据集how to 100m来训练模型的。这个语料库总共包含120 万个视频,活动范围 23k!活动的类型多种多样,从烹饪到手工制作到健身到园艺等等。这个数据集非常庞大,普通研究人员需要很长时间来训练一个模型。然而,如果你有计算能力,它是一个用于表示学习的很好的数据集。每个视频都包含任务的简短描述和一组自动生成的字幕。根据我的经验,以及最初的研究人员的经验,字幕噪音很大,存在对齐问题,音频到文本的翻译也不准确。简短的任务描述并不总是准确或者非常笼统。然而,这是因为 YouTube 的提取,而不是研究人员的错。
对于每个下游任务,都有数据集,这些数据集具有特定于任务评估的注释。这些数据集有一个较小的视频集,集中于一个较小的活动集。
数据集
适用于文本相关视频任务的数据集有:
【you cook 2】是一个基于烹饪的数据集,包含 89 个烹饪食谱的 2K 个未经剪辑的视频,并带有逐步注释。该数据集还包括对时间目标任务有用的时间边界。 MSR-VTT 是一个更通用的 10K 视频剪辑数据集,具有 257 个不同主题的 20 万个人类注释剪辑-字幕对。虽然没有具体的指导性,但 LSMDC 数据集包含 101k 个唯一的视频剪辑-字幕对,其描述来自电影脚本或音频描述。
更常用于动作相关视频任务的数据集有:
CrossTask包含 2.7k 的教学视频,每一帧都有分步动作注释。 硬币 也是一个通用的教学视频数据集,具有 11,827 个视频,显示了 180 个任务,带有与视频中发生的动作相关联的注释。****
下游任务
视频/动作分类

图一。特征提取表示通过原始表示学习模型传递视频。为了预测活性,增加了一个小的前馈网络。
要设置的最快的下游任务是对整个视频或修剪后的版本进行分类。这项任务的目标是对给定的视频进行高精度的分类,使其显示复杂的动作。目标是所有类别的平均准确度都很高。一些有助于展示这一空间的可视化方法是聚类。理想情况下,来自同一个类的视频应该在同一个集群附近。为了修改这个下游任务的原始模型,您添加了一个输出d =类的数量的小型前馈网络。一个零触发实验意味着你不在下游数据集上训练这个修改过的网络,而只是测试。然而,更常见的是,训练一些时期来更新新层的随机参数。测量精度基于视频总数中准确分类的视频数量。**
文本到视频检索
该任务的目标是使用文本查询来检索匹配的视频。将文本查询的特征向量与视频池的学习特征进行比较,并且地面实况匹配应该是最相似的视频。另一个相关的任务是文本到完整视频检索。该任务使用多个标题来描述视频的多个部分,从而检索视频。通过将视频池传递通过训练的模型来提取特征。这项任务需要一个具有文本-视频配对的多模态数据集,以便将文本和视频投影到同一个“空间”中。回忆准确性是这项任务最常见的表现衡量标准。R@1 测量最相似的视频的平均准确度,R@5 测量最相似的前 5 个视频之一的平均准确度,最后 R@10 是前 10 个视频之一。另外两个常用的指标是 median-R 和 mean-R ,用于衡量视频池中检索到的真实结果的中值和平均排名,越小越好。****

图二。使用训练好的模型,提取所有视频的验证数据集的特征。为文本查询和视觉特征添加一组编码层(如果原始模型仅使用视频),以将两者嵌入同一空间。这通常是为少数时代训练的。完成后,将使用新图层嵌入文本查询和视频。基于文本查询和所有视频表示之间的相似性对视频进行排序。
时间动作定位
此任务的目标是在视频剪辑中找到一个动作。给定一个视频片段和一个无序的动作列表,任务是在视频流中找到这些动作。这种设置非常类似于帧级动作分类,只是增加了一个前馈网络。这与图 1 所示的过程相同,但是视频片段更小。它也可以是基于相似性的,比如在检索中,将活动的嵌入与片段级的嵌入进行比较。这与图 2 所示的过程相同,但是查询是用动作而不是关键字。用于该任务的度量可以是召回率、准确度、交叉检测和交叉合并(参见 Kouhne 等人 2019 和 Chen 等人2021)。
视频字幕
此任务的目标是将原始视频转换为一个简短的文本描述,或一组简短的句子描述。这对于出于查询或基于知识的原因总结视频很有用。这项任务通常使用变压器解码器将视频解码成字幕(见朱和杨 2020 )。关于变形金刚的更多细节,请看 图解变形金刚 。这类似于图 1,但用变压器代替了前馈网络。像 PyTorch 这样的编程框架有层和教程可以用于这个下游任务。基于 NLP 的指标用于评估性能,包括 BLEU、METEOR、ROUGE 和 CIDEr。这些指标使用 n 元语法重叠、最佳匹配长度和词序来比较模型的输出标题和真实标题。
视频问答
此任务的目标是选择多项选择题的正确答案。这是通过将视频与每个可能答案的组合分别输入模型来实现的。附加层是线性的,并且使用视频和文本表示来预测候选对是正确选择的可能性。最常用的衡量标准是准确性。此任务测量您学习的功能完成基于推理的任务的能力。
摘要
动作分类 : 将整个视频分类为一个复杂的动作。
- **方法:基于特征的线性分类器。
- 数据集 : YouCook2 , MSRVTT ,硬币,活动网
- **度量:平均精度(mAP)和准确度
- 示例 : W-TALC:弱监督时间活动定位和分类,VideoBERT:视频和语言表征学习的联合模型
文本到视频检索 :给定真实文本描述,从视频池中检索匹配视频。
- **方法:测量文本查询和视频特征数据库之间的相似度。
- 数据集 : YouCook2 , MSRVTT ,硬币,活动网, LSMDC
- 指标 : R@1,R@5,R@10,中值-R
- **示例:从无标签视频进行自我监督学习的多模态聚类网络(2021),ActBERT:学习全局-局部视频-文本表示(2020),HowTo100M:通过观看一亿个带叙述的视频剪辑学习文本-视频嵌入(2019)
时间动作定位 :给定一段视频和片段中出现的动作步骤的无序列表,在视频流中找到那些动作。行动步骤本地化使用有序的活动列表。
- **方法:动作嵌入和视频嵌入之间的片段级分类或相似度。
- 数据集 : 交叉任务,活动网,挖掘 YouTube
- **指标:召回
- 示例 : HowTo100M:通过观看一亿个叙述视频剪辑学习文本-视频嵌入(2019)和 ActBERT:学习全球-本地视频-文本表示(2020)
动作分割 :在帧或片段级别对子动作进行分类。
视频字幕 :将视频转换成一段简短的文字描述或一组字幕。
视频问答 : 根据一个问题和一组选择题答案,能够选出正确答案。
- **方法:将每个带视频的选择题答案候选人送入线性分类器,对问题的正确答案进行分类。
- 数据集 : MSRVTT
- **指标:准确性
- 示例 : ActBERT:学习全球-本地视频-文本表示(2020)
更多的例子和信息,请查看代码为的论文。
代码分类的流行评估标准
每个数据科学家都必须知道的

作者图片
假设你的任务是训练一个 ML 模型,将数据点分类到一定数量的预定义类别中。因此,一旦你建立了你的分类模型,下一个任务就是测量它的性能。基于用例,有许多指标可以帮助您做到这一点。在这篇博客中,我们将尝试回答类似于何时使用的问题。是什么?和如何实现?

作者图片
****混淆矩阵定义为(类 x 类)大小的矩阵,因此对于二进制分类是 2x2,对于三类问题是 3x3,以此类推。为了简单起见,让我们考虑二进制分类并理解矩阵的组成部分。

图 1 —作者图片
- ****真阳性(TP)——这表示该类为阳性的次数,并且您的模型也说它为阳性。
- ****True Negative(TN)——这表示该类为负的次数,并且您的模型也说它为负。
- ****假阳性(FP)——这表示有多少次类是阴性的,而你的模型却说它是阳性的。
你可以这样记——你的型号 假 说它是 正
- ****假阴性(FN)——这表示这个类有多少次是阳性的,但是你的模型却说它是阴性的。
你可以这样记——你的型号 假 说它是 负
使用 sklearn 可以很容易地得到混淆矩阵,如下所示—
from sklearn import metricsdef calculate_confusion_matrix(y, y_pred):
return metrics.confusion_matrix(y, y_pred)
正如图 1 中所讨论的,混淆矩阵的组成部分是 TP、TN、FP、FN,您也可以使用如下所示的普通 python 来计算它们
计算 TP、TN、FP、FN
def calculate_TP(y, y_pred):
tp = 0
for i, j in zip(y, y_pred):
if i == j == 1:
tp += 1
return tpdef calculate_TN(y, y_pred):
tn = 0
for i, j in zip(y, y_pred):
if i == j == 0:
tn += 1
return tndef calculate_FP(y, y_pred):
fp = 0
for i, j in zip(y, y_pred):
if i == 0 and j == 1:
fp += 1
return fpdef calculate_FN(y, y_pred):
fn = 0
for i, j in zip(y, y_pred):
if i == 1 and j == 0:
fn += 1
return fn
混淆矩阵对于理解你的模型的粒度级性能很重要,然后基于用例的敏感性,你可以决定这个模型是好是坏。例如,在医疗诊断用例中,您希望模型具有非常低的假阴性,因为您不希望系统在测试该人的任何疾病踪迹时,如果事实是肯定的,则说“否”。你仍然可以设法让有一点点高的假阳性,因为这样这个人就可以通过相关的测试并在后期得到确认。

作者图片
****准确性为模型的表现提供了一些整体的感觉。但如果使用不当,它很容易高估数字。例如,如果你有一个偏态分布的类标签,那么只预测多数类会给你很高的数字(高估了性能),不像在平衡类的情况下,准确性更有意义。
您可以使用 sklearn 轻松获得准确度分数,如下所示—
from sklearn import metricsdef calculate_accuracy_sklearn(y, y_pred):
return metrics.accuracy_score(y, y_pred)
也可以使用 Python 从如下所示的混淆矩阵组件中进行计算—
def calculate_accuracy(y, y_pred):
tp = calculate_TP(y, y_pred)
tn = calculate_TN(y, y_pred)
fp = calculate_FP(y, y_pred)
fn = calculate_FN(y, y_pred)
return (tp+tn) / (tp+tn+fp+fn)

作者图片
****精度指标帮助我们了解识别阳性样本的正确率%。例如,假设我们的模型有 80 次说某事是肯定的,我们精确地计算这 80 次中有多少次模型是正确的。
也可以如下图计算—
def calculate_precision(y, y_pred):
tp = calculate_TP(y, y_pred)
fp = calculate_FP(y, y_pred)
return tp / (tp + fp)

作者图片
****回想一下指标有助于我们了解模型能够正确识别的所有基本事实正样本的正样本百分比。例如,假设数据中有 100 个样本是阳性的,通过回忆,我们可以计算出这 100 个样本中有多少是模型能够正确捕获的。
也可以如下图计算—
def calculate_recall(y, y_pred):
tp = calculate_TP(y, y_pred)
fn = calculate_FN(y, y_pred)
return tp / (tp + fn)
附注:对于输出概率的模型,调整阈值,然后填充相关的混淆矩阵和其他属性始终是一个好的做法。也可以为不同阈值绘制精确-召回曲线,并相应地基于用例的敏感性选择阈值。
def precision_recall_curve(y, y_pred):
y_pred_class,precision,recall = [],[],[]
thresholds = [0.1, 0.2, 0.3, 0.6, 0.65]
for thresh in thresholds:
for i in y_pred: #y_pred holds prob value for class 1
if i>=thresh: y_pred_class.append(1)
else: y_pred_class.append(0)
precision.append(calculate_precision(y, y_pred_class))
recall.append(calculate_recall(y, y_pred_class))
return recall, precisionplt.plot(recall, precision)

精确召回曲线—作者图片

作者图片
F1 将精确度和召回分数结合起来,得到一个单一的数字,有助于对不同型号进行直接比较。它可以被视为 P 和 r 的调和平均值。调和平均值,因为它对非常大的值不敏感,不像其他平均值。当处理带有倾斜目标的数据集时,我们通常考虑 F1 而不是准确度。
你可以如下图计算—
def calculate_F1(y, y_pred):
p = calculate_precision(y, y_pred)
r = calculate_recall(y, y_pred)
return 2*p*r / (p+r)

作者图片
AUC-ROC 是二元分类问题中非常常用的评价指标之一。这是一条绘制在 y 轴上的 TPR(真阳性率)和 x 轴上的 FPR(假阳性率)之间的曲线,其中 TPR 和 FPR 定义为—

跨国激进党和 FPR——作者图片
如果你注意到, TPR 和 Recall 有相同的表示,就像你正确分类了多少正确的样本。另一边的 FPR 是被错误分类的反面例子的比率。ROC 图总结了每个阈值的分类器性能。因此,对于每个阈值,我们都有 TPR 和 FPR 的新混淆矩阵值,它最终成为 ROC 2-D 空间中的点。ROC 曲线下的 AUC(曲线下面积)值越接近 1,模型越好。这意味着通常对于每个阈值,我们的具有高 AUC 的模型表现得比另一个更好。
你可以如下图计算—
**from** sklearn.metrics **import** roc_auc_scoredef roc_auc(y, y_pred):
return roc_auc_score(y, y_pred)

AUC-ROC 曲线变异——修改自来源

作者图片
Precision@k 是多标签分类设置中使用的常用指标之一。在这种情况下,我们计算给定示例的前 k 个预测,然后计算这 k 个预测中有多少是真正的标签。我们将精度@k 计算为—
Precision@k =(来自 k 的正确预测数)/(k 中的项目数)
actual_label = [1, 1, 0, 0, 1]
predicted_label = [1, 1, 1, 0, 0]Let k=3
Precision@k = 2/3 (It's same as TP/(TP+FP))

作者图片
这不是神奇的:D,它只是 Precision@k 上所有样本的平均值

作者图片
****当你有二进制分类问题时,日志损失是相当不错的。当您有一个输出概率的模型,并且该模型根据预测与实际标注的偏差考虑了预测的不确定性时,就可以使用它。
你可以如下图计算—
def calculate_log_loss(y, y_pred_probs):
log_loss = -1.0*(t*log(p) + (1-t)*(t*log(1-p))
return log_loss
在不平衡集合的情况下,你也可以增加类权重来惩罚少数类的错误。在代码中,w1 和 w2 对应于正类和负类的权重。
def calculate_log_loss_weighted(y, y_pred):
log_loss = -1.0*(w1*t*log(p) + w2*(1-t)*(t*log(1-p))
return log_loss
附注:你可以很容易地将其扩展到称为分类交叉熵的多类设置。

作者图片
当任务本质上是二元的时,通常使用。它就是实际值和预测值的平方差。对于 N 个样本的集合,我们取其平均值。
你可以如下图计算—
def brier_score(y, y_pred):
s=0
for i, j in zip(y, y_pred):
s += (j-i)**2
return s * (1/len(y))
在这篇博客中我们看到了一些流行的评估指标,每个数据科学家在根据手头问题的性质评估机器学习分类模型时都必须牢记在心。
这篇博客到此为止。我希望这本书值得你花时间去读!谢了。
一个流行的面试问题把我从 SDE 的角色简化为 SDE 的角色
取消了那次晋升!!

照片由 LinkedIn 销售解决方案在 Unsplash 上拍摄
“想象一下这个场景”面试官在没有用他准备好的问题库完全困住我后开始说。
这个人手里拿着我职业生涯中的一个里程碑……一个让人头脑麻木、山摇地动、海裂的软件开发工程师 2 的角色。
从“九头蛇 HR ”一直到最后一轮“邪恶妖王高级开发者”,我历经千辛万苦才走到这一步。
但是!!
命运没有这么慷慨,我的面试官像猫抓老鼠一样玩弄我。但上帝保佑,我不是容易的猎物。我像一个冠军一样接受了他的问题,像他从未见过的人一样互相攻击,直到他拿出他的王牌,许多勇敢的 SDE 1 号工程师都倒下了。
这是我从未遇到过的武器……直到现在。
需要“去抖功能”的场景
“想象一下这个场景”面试官开始说道。
“你有一个搜索框,一旦用户停止输入,你就在那里显示自动完成,记住,要显示这些建议,你需要向后端发送一个 API 请求。你会怎么处理?”
当我第一次听到这个问题时,在我的脑海里…
嗯嗯……如果 CTC 超过 20L,队伍预算应该是 20,000 卢比,不,也许我应该增加更多…..
但当面试官鼓励我大声思考时,我很快就回到了现实。当然,我不敢说我是在为战胜他的庆功宴计划预算。
但我也感到奇怪,为什么他会选择这么简单的问题作为结尾?
当我更深入地探究细节时,这些想法很快就结束了…让我解释一下
假设用户键入了“s”、“se”、“ser”、“sert”、“serty”。
如果不小心的话,您可能会为一个用户发送 5 个 API 请求。即使忽略它对后端的巨大影响,为如此多的请求提供服务,在前端处理它也会有问题。
我开始时很慢,最后想出了一个使用 setTimeout 发送请求的解决方案,但由于 react 中的一些干扰,如果在 React 项目中使用它,这个解决方案将无法工作(结果我只是在采访中错误地实现了它,这也是一个愚蠢的错误)。
面试结束后,面试官解释说,我要找的术语叫“去抖功能”。
所以,我查了一下,这就是我的发现
什么是去抖?
术语去抖来自电子学。当你按下一个按钮时,比如说电视遥控器上的电源按钮,遥控器中的微芯片接收信号的速度非常快,即使你按住它几毫秒,它也会多次记录“按下”。

作者图片
为了避免这种行为,一旦微芯片记录了一个信号,它就停止处理来自该按钮的信号一小段设定的时间间隔,该时间间隔可以变化。
JavaScript 中的去抖
我们被问到的问题类似于上面的例子。
只有当你确定用户已经停止输入的时候,你才希望把 API 请求发送到后端。
在 react 中,这可以通过下面的库和代码轻松实现。
预先编写了这个函数的库是 lodash.debounce,所以让我们导入它
import debounce from "lodash.debounce";
现在我们要写一个函数来使用上面的 react
const timer = 1000const debouncedSave = useRef(debounce((nextValue) => YourFunction(nextValue), timer)).current;
计时器→您可以通过编辑“1000”来编辑您想要等待的时间,在本例中“1000”表示 1 秒。
你的功能→你想延迟到用户完成的功能。它看起来会像
const YourFunction = (value)=> {sendAPIRequest(value)}
如果你想知道去抖功能的具体实现,你可以在这里查看它的代码
但这是我从网上得到的一个简单的实现
function debounce(func, timeout = 300){
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => { func.apply(this, args); }, timeout);
};
}
function saveInput(){
console.log('I have been called');
}
const processChange = debounce(() => saveInput());
想要更多吗?
- 订阅 在我发表文章时得到通知。
- 成为推荐媒介会员,支持我的写作,无需额外费用 。
- 在Linkedin和 Twitter 上连接并联系我
参考资料:
https://codeburst.io/throttling-and-debouncing-in-javascript-b01cad5c8edf https://stackoverflow.com/questions/24004791/can-someone-explain-the-debounce-function-in-javascript https://www.freecodecamp.org/news/javascript-debounce-example/
流行的机器学习性能指标
直观的解释和与概率的联系

安迪·凯利在 Unsplash 上的图片。
在本文中,我们将直观地回顾最流行的监督学习指标
- 分类— 准确度、精密度、召回率,fᵦ&AUC;和
- 回归 — MAE,MSE 和 R。
对于分类任务,更高级的分类指标允许您针对您的用例校准 I 类和 II 类错误的重要性,同时帮助您处理不平衡的数据集。
我们还将直观地探索分类指标和概率之间的一些联系。
在这篇和这篇文章中,我使用 scikit-learn 将这些监督学习指标应用于 Jupyter 笔记本中的真实世界 ML 问题。
在此加入 Medium 并获得无限制访问互联网上最好的数据科学文章。
1.分类标准
混淆矩阵
from sklearn.metrics import confusion_matrix
二进制分类产生四种结果,可以在混淆矩阵中总结。然后使用矩阵中的值计算分类的度量。

混乱矩阵。图片作者。
准确(性)
from sklearn.metrics import accuracy_score
最基本的衡量标准是准确性,它给出了我们的模型正确预测的比例。

图片作者。
尽管精确度很容易计算,也很容易理解,但是对于不平衡的数据集来说,它是失败的,正如我们将在下面看到的。
精确
from sklearn.metrics import precision_score
精度给出了我们正确预测的比例。该指标优先考虑将第一类错误最小化。一个需要高精度模型的典型问题是对垃圾邮件进行分类——下面将详细介绍。在这里,让假阳性通过意味着重要的电子邮件被意外抛出。

图片作者。
回忆
from sklearn.metrics import recall_score
Recall 给出了我们正确预测的实际阳性率。该指标优先考虑将第二类错误最小化。需要高召回率模型的示例问题包括对新冠肺炎的诊断测试、对欺诈性信用卡交易进行分类以及预测有缺陷的飞机部件。在这些问题中,假阴性要么造成经济损失,要么甚至是致命的。

图片作者。
使用准确度、精确度和召回的示例
以下面的分类欺诈性信用卡交易为例。

图片作者。
混淆矩阵告诉我们,这个“愚蠢”的模型将每一笔交易都归类为合法交易。结果精度是恒星 992/1000 = 99.2% 。但是…我们错过了每一笔欺诈交易!该死。
为了捕捉这些类型 II 错误的重要性,我们可以使用 recall 来代替。
这里, recall = 0/8 = 0% ,强调没有检测到欺诈 CC。哎呦!
看看下一个例子过滤掉垃圾邮件。在这里,犯第一类错误比犯第二类错误更糟糕。这是因为不小心扔掉一封重要的邮件比不小心让几封垃圾邮件通过更有问题。这里一个很好的衡量标准是精度。换句话说,我们正在寻找训练一个高精度模型。

图片作者。
我们有精度= 100/130 = 77%** 。这意味着在我们自动过滤到垃圾邮件文件夹的 130 封邮件中,有 100 封是真正的垃圾邮件。还不错,但是我要说对于一个生产邮件服务器来说肯定不够好!**
最后,看看这个诊断新冠肺炎的例子。在这里,犯 II 型错误(假阴性测试)意味着遗漏了实际生病的人。这对个人来说是致命的,对公共健康来说是灾难性的。
同时,I 型错误(假阳性测试)最多会给个人带来不便。安全总比后悔好!因此,我们希望培养一个高召回模型。

图片作者。
我们有召回= 100/120 = 83%** 。这意味着我们在 120 名新冠肺炎阳性者中找到了 100 名进行了测试,并让 20 名认为自己没有感染病毒的人回家。不幸的是,这个测试对于一般应用来说还不够好。**
F₁和 Fᵦ得分
from sklearn.metrics import f1_score, fbeta_score
考虑到精确度和召回率, F₁-score 是一种先进的度量标准,可以让您两全其美和对不平衡的数据集具有鲁棒性。该指标被定义为精度和召回率的调和平均值:

或者,您可以直接从混淆矩阵计算 F₁-score :

图片作者。
对于我们的新冠肺炎模型,f₁-score = 100/(100+0.5(20+80))= 67%。****
更一般地说, Fᵦ-score 允许你更精确地校准 I 型和 II 型误差的重要性。它通过让您说出您认为召回比精确度重要 β 倍的指标来做到这一点。

设置 β = 1 给出了 F₁-score 。
下面是直接从混淆矩阵计算 Fᵦ-score 的公式:

图片作者。
曲线下面积
from sklearn.metrics import roc_curve, roc_auc_score
具体来说,我们正在查看受试者工作特性(ROC)曲线下的区域。
这个范围从 0 到 1,测量你的分类器分离两个类和从噪声中筛选信号的能力。
全面积为 1 代表完美模型,而 AUC 为 0.5 的模型比随机猜测好不了多少。一般来说,0.9 分被认为是优秀,0.8 分是优秀,0.7 分是可以接受的。

图片来自 Udacity 。
为了首先绘制 ROC 曲线,我们绘制了真阳性率(TPR)** (又名召回)对假阳性率(FPR) 。**
具体来说,我们改变真实预测的概率阈值,获得混淆矩阵,并绘制点(FPR,TPR)。我们对所有可能的阈值都这样做,每次给我们不同的混淆矩阵和(FPR,TPR)点。ROC 曲线因此总结了关于一组可能的混淆矩阵的信息。
YouTube 上有一个关于 ROC 的很棒的视觉解释。
中华民国应该是什么样子?远高于对角线的 ROC 意味着更多的混淆矩阵,其中模型能够区分阳性和假阳性。也就是说,TPR 在 FPR 上占优势的点和混淆矩阵更多。

ROC 曲线。图片作者。
该信息可以概括为 ROC 曲线下的区域。AUC 越高,“ROC 曲线越高”,模型越好。****
具有更高 AUC 的模型意味着我们能够更广泛地改变概率阈值,同时保留模型从噪声中读取信号的能力。
在本文的中,我使用 scikit-learn 生成 ROC 曲线,并在 Python 中计算 AUC。
在下一节中,我们将更详细地考察 TPR、FPR、TNR 和 FNR。
混淆矩阵、概率和树
为了获得新的视角,我们可以通过树的透镜来看混淆矩阵。这样做的好处是,我们可以在混淆矩阵和概率之间建立一些联系。

混淆矩阵和概率树。图片作者。
注意概率树的第二层非常有趣。它们是四种利率:
- ****真阴性率又名特异性。
- ****假阳性率又名1 型错误率。
- ****假阴性率又名二型错误率。
- ****真阳性率又名灵敏度或召回。
这些概率——作为概率树的分支——也是条件概率。
下面是这四种费率的详细汇总。


****
作者图片。
2.回归任务的度量
平均绝对误差
from sklearn.metrics import mean_absolute_error
MAE 度量计算模型和数据之间所有偏差误差的平均值。

图片来自 Udacity ,作者进行了改进。
均方差
from sklearn.metrics import mean_squared_error
这个 MSE 计算所有这些偏差误差的平方,并计算它们的平均值。

图片来自 Udacity ,作者进行了改进。
决定系数
from sklearn.metrics import r2_score
R -score 有效地比较了您的模型的 MSE 和简单均值模型的 MSE,范围在 0 到 1 之间,1 为最佳。

图片来自 Udacity ,作者进行了改进。
这里的想法是,如果模型非常好,那么模型的 MSE 将远远小于数据集的简单均值模型的 MSE,从而使这两者的比率接近于 0。R 分数将更接近于 1。
但是,当你的模型不好的时候,你的模型的 MSE 和简单均值模型的 MSE 不会有太大的差别,导致它们的比值接近 1。R 分数将接近于 0。
3.摘要
在评估分类算法的性能时,我们从混淆矩阵开始。
这是一个通用二进制分类任务的矩阵,从中我们可以计算各种指标的得分。

图片作者。
****准确性是最简单的度量标准,但是没有考虑到 I 类和 II 类错误的相对重要性,对于不平衡的数据集也不稳健。像精度、召回和 F₁ 这样的更高级的指标可以补救这些问题。 ROC 和 AUC 提供了一种图形化的方法来评估您的分类器区分类别和从噪声中分离信号的能力。
正如我们已经看到的,矩阵的四个结果也可以在概率树中解释,其中 TPR、TNR、FPR 和 FNRs 对应于某些条件概率。
对于回归算法,三个最流行的指标是 MAE 、 MSE 和 R -score 。后者是根据两个 MSE 计算的,一个是您的模型,另一个是简单均值模型。
在后续文章中,我们将关注损失函数以及它们如何用于优化常见的监督学习算法和神经网络模型。干杯!
本文中的图表是使用 Adobe Illustrator、LaTeXiT 和 Microsoft Excel 创建的。
在 YouTube和 Twitter 上关注我。
无限制媒体访问
提升你的知识和技能到一个新的水平。
加入 Medium 享受无限访问互联网上的最佳分析&数据科学文章。你可以在这里加入支持我和其他顶级作家。
我的数据科学文章
- 微分方程与机器学习——此处为
- 新冠肺炎的数学建模与机器学习— 此处
- 用回归预测房价——此处此处
- 分类预测员工流失— 此处
- 流行的机器学习性能指标— 此处
- Jupyter 笔记本对 Dataiku DSS — 此处
- Power BI —从数据建模到令人惊叹的报告— 此处
用单词填充网络图
自然语言处理
使用 spaCy 为自然语言处理创建知识图

Andrew Itaga 在 Unsplash 上拍摄的照片
今天,我们将获取一篇维基百科文章的内容,并为自然语言处理做准备。我们将使用 spaCy 来处理文本,并使用 Power BI 来可视化我们的图形。
打开 jupyter 笔记本,让我们开始吧!
让我们把进口的东西扔掉:
# for manipulating dataframes
import pandas as pd# for webscraping
from requests import get
from bs4 import BeautifulSoup# for natural language processing
import spacy
import en_core_web_sm
nlp = en_core_web_sm.load()
然后,我们将向 Wikipedia 发出 get 请求,如下所示:
url = '[https://en.wikipedia.org/wiki/QAnon'](https://en.wikipedia.org/wiki/QAnon')
response = get(url)
为了了解我们到底在处理什么,打印带有print(response.text[:999])的响应,我们将得到类似下图的结果:

作者截图
接下来,我们将使用 Beautiful Soup 提取维基百科页面的主要文章:
soup = BeautifulSoup(response.content, 'html.parser')
article = soup.find('div', class_='mw-parser-output')
让我们看看用print(article.text)打印主文得到了什么。

作者截图
看到第一段了吗?这是一个注释,不应该是我们文档的一部分,所以让我们把它去掉:
unwanted = article.find('div', role='note')
unwanted.extract()
瞧,它不见了!

作者截图
我们的下一步将是使用 spaCy 处理文章,这样它就可以使用它的统计模型来分配一些关于我们文章中的单词(标记)的信息。然后我们将它赋给一个变量doc来保存所有的信息和元数据。
doc = nlp.(article.text)
我们需要遍历文档中的所有单词,以获得构建数据帧所需的元素。
text_list = []
head_list = []for token in doc:
if token.is_alpha:
if not token.is_stop:
text_list.append(token.lemma_)
head_list.append(token.head.text.lower())df = pd.DataFrame(list(zip(text_list, head_list)),
columns =['text', 'head'])
在上面的代码中,我们启动了两个空列表来保存我们将提取的值——一个用于文档中的单词,另一个用于其标题或父单词。然后,我们遍历文档中的每个标记,同时过滤掉停用词和数字。如果条件通过,我们将每个令牌附加到text_list和head_list。然后,我们通过将两个列表压缩在一起创建了一个数据帧。
接下来,我们将对行进行分组,并获取每个特定的文本和标题组合的计数。
combos = df.groupby(['text','head']).size().reset_index().rename(columns={0:'count'}).sort_values('count', ascending=False)
让我们来看一看:

最后,我们将把数据帧导出到 csv 文件中,以便在 Power BI 中可视化。
combos.to_csv('../data/out/qanon_combos.csv', index=False)
结果是这样的:

该图展示了至少三个集群。

作者截图
让我们放大。

作者截图
有道理!
以下是我们今天使用的所有代码(不包括输出):
今天,我们拿了一篇维基百科的文章,用 spaCy 处理了它,并创建了一个相关单词的数据框架。然后,我们将其导出到 csv,并使用 Power BI 来可视化网络图。
就这样,伙计们!
你可以在这里找到朱庇特笔记本。
谢谢你过来看我的帖子。希望对你有用:-)
如果你想了解更多关于我从懒鬼到数据科学家的旅程,请查看下面的文章:
如果你正在考虑改变方向,进入数据科学领域,现在就开始考虑重塑品牌:
敬请期待!
新兴市场债券的投资组合多样化
在数据世界里争吵
用贝叶斯概率模型测试均值-方差优化

图片由来自 Pixabay 的 doroheine 提供
我们对金融投资的实际回报几乎没有控制权。我们希望有正回报,但不能保证。在整个过程中,我们唯一能控制的是这些投资价值的波动性,我们通过分散投资来做到这一点。不要把所有的鸡蛋放在一个篮子里,这是古老的智慧。这是 Harry Markowitz 的均值-方差投资组合优化的关键观点和目标。
投资组合优化本质上是寻求以尽可能低的波动性提供一定的目标回报。这是通过将各种相互关联度较低的金融资产组合成一个投资组合来实现的。投资需要对未来的信心,而资产配置旨在增强这种信心,抵御未知未来的不确定性。
世界各地的全球机构投资者面临着一个重大困境,因为在许多情况下,全球债券收益率已经跌至接近零甚至负水平。发达市场(DM)债券尤其如此,尤其是欧洲和日本债券。问题的一部分在于,零到负的债券收益率强烈暗示着机构投资者持有的最重要资产类别之一的未来回报率为负。问题的另一部分,也可能是更紧迫的一部分,是如此低的债券收益率意味着,DM 债券可能无法像过去那样为全球投资组合提供同样的多元化收益。

负收益债券在 12 月达到 18 万亿美元的峰值。资料来源:彭博
在这篇文章中,我探讨了增加对包括中国在内的“新兴市场”债券的投资如何有助于缓解这些问题。为了保持文章的合理长度,我不会对均值-方差投资组合优化的理论和实践进行长篇大论的解释。
来自《走向数据科学》编辑的提示: 虽然我们允许独立作者根据我们的 规则和指导方针 发表文章,但我们并不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的 读者术语 。
资本市场假设
有三个因素(或称 统计矩 )进入投资组合优化过程,它们共同构成了 资本市场假设 。第一个是组合中考虑的金融资产的预期收益,通常是几何平均预期收益。第二个是资产的 期望方差 ,与之相关的第三个因素是它们的 期望协方差 。协方差和相关性密切相关,因为两个标准化变量的协方差等于它们的相关性。
注意刚刚讨论的三个因素中的“预期”项是很重要的。它不应该是历史回报、历史方差或历史协方差,除非人们明确假设未来将是过去的翻版。这就是资产分配如此困难的原因,即需要依赖于对各种金融资产的未来回报、波动性和相关性的预测。
此外,依赖预测的未来值会使整个过程容易出错。预测未来绝非易事,尤其是当试图预测未来数年时,我们应该谦虚地假设事后看来点估计很可能是不准确的。因此,包含对未来预测的信念强度的贝叶斯方法是有用的,但稍后会有更多介绍。
在现实生活中,花在研究和预测这些资本市场假设上的时间比资产配置过程的任何其他方面都要多得多。这项工作需要深厚广博的专业知识,尤其是在考虑全球资产类别的时候。在这种情况下,我采用 2021 年 2 月(链接)公布的 贝莱德研究所 (BI)预测的 10 年几何预期收益 和年化波动率的权宜之计。贝莱德是世界上最大的资产管理公司,我认为它的研究部门在这方面一定拥有世界领先的专业知识。
此外,根据经验,投资组合优化结果对预期回报的敏感度远高于其他两个时刻。广义资产类别的协方差也倾向于更长期地跟随它们的历史价值,比如十年或更长时间,而回报则不那么容易适应。因此,我将在本练习中简化问题,并使用历史协方差值,这是该领域的惯例。
为了使这种做法切合实际,又不至于让读者陷入细枝末节,我挑选了九种全球资产类别纳入全球投资组合,如下所示。这些是许多全球资产管理公司会考虑的关键公开市场资产类别。然后,我选择最广泛使用的基准指数(以美元计价)来代表这九个资产类别。

预期收益&来自贝莱德研究所的波动性,除了来自 JP 摩根资产管理*
有几点需要澄清。“BB”是彭博巴克莱银行的简称。DM 不包括美国股票的预期回报改编自 BI 的“全球不包括美国大盘股”回报。全球信贷预期回报是 BI 分别对“美国信贷”和“欧盟公司债券”的预期回报的 50-50 混合。BI 没有提供大宗商品的预期时刻,但 JP 摩根资产管理提供了,这是我获得大宗商品预期回报和波动性的地方。
上面列出的中国政府债券指数只有 2016 年 11 月以来的每日数据。我使用彭博巴克莱中国 Agg 国债总回报指数作为 2016 年 11 月前的每日数据。这一替代指数自 2012 年 5 月以来每天都有数据。其他资产类别指数至少从 2012 年开始通过订阅从彭博获得每日数据。
样本期将涵盖八个日历年的每日数据,时间跨度从 2013 年 1 月到 2020 年 12 月。这八年包括极低和高波动性制度。因此,样本期很好地融合了不同的全球市场体系。这个样本将提供所有九个资产类别的历史协方差数据。
按照上表的顺序,每个资产类别的相关变量名为: spx,usd_gov,eafe,eu_gov,credit,gsci,em_stx,em_gov 和 ch_gov 。这九个资产类别在 2013–2020 样本期的汇总统计数据如下:

2013–2020日收益汇总统计。资料来源:彭博
多样化的好处
下面的相关矩阵是从那九个资产类别的每日日志回报计算出来的,但是投资组合优化和统计矩计算将基于每日简单百分比回报。在金融学中,对于如何以及何时使用对数收益和简单收益存在一些误解。
从资产类别的对数收益来计算它们之间的线性相关性可能更合适。然而,投资组合矩应该根据投资组合中各组成资产的简单百分比回报率来计算。投资组合回报是投资组合中资产或证券的加权平均值。只有简单的百分比回报可以用这种方式合计。日志仅返回一段时间内的聚合。希望看到对这一原则更正式解释的读者可以在下面的链接中找到。
从下面的相关性矩阵中我们可以看出,各种权益资产类别——spx、 eafe 和em _ stx——彼此之间具有高度的正相关性。从 spx 和 eafe 之间的+0.54,到 eafe 和 em_stx 之间的+0.74。任何小于+1.0 的相关性都意味着多样化收益,但是相关性越小甚至越负,多样化收益就越大。这意味着这些全球股票资产类别的多样化收益是有限的。

相比之下, spx 和 us_gov 之间的相关性为-0.37,这表明将这两种资产放在一个投资组合中会带来巨大的多元化收益。但由于美国 10 年期国债收益率目前约为 1.5%,未来多样化的好处可能不会那么强大。1.5%的收益率意味着债券价格上涨的空间小于下跌的空间(收益率和债券价格反向运动),因为名义收益率不太可能降至零以下,但没有上行(正)界限。
全球资产管理公司倾向于对股票资产类别进行大规模配置,因为它们具有出色的长期回报(即股票风险溢价),除非该管理公司有专门的关注点,例如固定收益。问题在于,股票也是最不稳定的资产类别之一。以下是这九个资产类别的已实现年化标准差,我们可以看到,在样本期内,股票的波动率( spx,eafe & em_stx )仅次于商品( gsci )。

这种高回报和高波动性的结合意味着,全球基金经理需要投资组合中的资产能够相对于股票持有实现多样化,从而抑制整体投资组合的波动性。令人高兴的是,他们可以转向新兴市场债券来分散风险。 em_gov 和 ch_gov 分别与 us_gov 和eu _ gov(DM 债券资产)的相关性较低。然而, em_gov 与 spx 和 eafe 具有中到高的正相关性,而 ch_gov 与这两个资产类别具有低到零的相关性。因此,将基准新兴市场债券指数与增加的中国政府债券敞口结合起来可能是有益的,因为后者具有潜在的更高的多元化收益。
投资组合约束
在选择资产类别后,我接下来为各种资产类别或它们的组合设置具体的分配或限制。前两个等式约束由 BI 推荐的 DM 股权权重和目标回报指导,目标回报来自上述同一出版物中的假设“英国多资产管理公司”:
- spx+eafe 分配= 43%
- 投资组合目标回报率= 4.5%
我还为 DM 股票配置中的美国股票敞口添加了一个不平等(最小)约束。这是现实的,因为美国股市是全球规模最大、流动性最强的股市之一,因此全球基金经理往往有相当大的风险敞口。此外,毕只建议将 7%的资金配置在新兴市场股票上。在这次演习中,我将挑战极限,将新兴市场股票敞口的上限提高到 10%。因此,对股权分配的额外不平等约束是:
- spx ≥ 15%
- em_stx 分配≤ 10%
BI 的分配建议包括将 26%的份额分配给 DM 债券。我决定将这一比例降低到最高 20%,以便为其他固定收益资产留出考虑空间。我另外对新兴市场债券敞口设置了两个不等式约束,以及一个最终等式约束,即各种资产类别权重之和必须为 100%:
- 德国马克债券≤ 20%
- em_bonds + ch_bonds ≤ 15%
- ch_bonds ≤ 5%
- np .总和(W) = 100%
基本上,新兴市场债券敞口被限制在投资组合的至多 15%,其中最多 5%可分配给中国债券。这比允许不受限制的配置更现实,因为新兴市场债券市场的流动性远不如发达市场债券,而且这一资产类别仍被认为有些奇特,不管这种想法是对是错。因此,全球资产管理公司对新兴市场债券的配置很少。事实上,15%的最高分配限额可能被认为是相当大胆的。目前通常是个位数百分比。但这篇文章的重点是,它应该更多,正如我们将看到的!
投资组合优化
投资组合优化函数如下,函数设置为最大化投资组合的预期收益除以其标准差的比率。您还可以看到列出的八个约束条件—四个等式约束条件和四个不等式约束条件:

风险调整回报函数

具有投资组合约束的投资组合优化函数
运行优化流程后,所有九个资产类别的原始投资组合的最终分配权重为:

然后我放弃中国债券作为额外的资产类别选择,并且将对整体新兴市场债券的最大配置限制在 5% 。投资组合被重新优化,新兴市场受限投资组合的新分配权重为:

如果你还记得的话,上述过程是使用 2013-2020 年的协方差数据作为三个时刻之一进行的。现在,我让优化过程接受场景分析,特别是通过使用两个不同的历史方差-协方差样本期。预期收益保持不变。
第一个场景使用 2020 日历年的历史方差-协方差矩阵,该矩阵经历了来自新冠肺炎疫情的全球金融市场的极高波动性。第二种方法使用 2019 日历年的方差-协方差矩阵,其特点是整个资产市场的波动性普遍较低。
我使用这些不同的方差-协方差矩阵运行优化程序,所有三个协方差期间的比较投资组合预期收益和年化标准差为:

投资组合预期收益&不同协方差情景下的波动率
需要注意的一点是,所有投资组合的平均预期收益是相同的。事实上,在所有情况下,投资组合的目标回报率都保持在 4.5%,因此这并不令人意外。更重要的是,投资组合的持续低波动性或年化标准差高新兴市场债券持有量。请记住,这项练习的全部目的是检验新兴市场债券是否能为全球投资组合带来多样化的好处。因此,持有较多新兴市场债券、波动性较低的投资组合,初看起来会支持这一论点。
方差相等的检验
关于不同数据样本的方差是否相等,有几种统计检验。这些是 frequentist 统计显著性检验,人们可以在指定的 α 阈值(通常为 5%)拒绝或不拒绝零假设,以最大限度地减少 I 类错误。两个样本方差相等的传统检验是 F 检验,但它假设总体呈正态分布。
另一种对偏离常态不太敏感的测试是 Levene 测试。使用中位数的 Levene 检验对偏态分布是稳健的,而使用修剪平均数的检验对厚尾分布是稳健的。我们执行所有三个测试: F 检验、带中位数的 Levene和带修整平均数的 Levene。这些测试比较了三个历史协方差时期(即 2013 年至 2020 年、2019 年和 2020 年)的原始投资组合与 EM 限制投资组合之间的方差。
所有三个测试都表明,我们可以拒绝原始投资组合和 EM 受限投资组合之间方差相等的零假设,只有一个例外。使用 2020 年协方差数据对投资组合方差进行的 Levene(中值)测试返回了一个高于标准 5%阈值的 p 值(13.8%),尽管 f 检验和 Levene(修剪均值)检验都返回了低于阈值的 p 值。

各种方差相等测试的结果
这些测试让我得出结论,更高的新兴市场债券配置,包括对中国债券的额外配置,有助于降低投资组合的预期波动性。相等波动性的零假设被拒绝,无论我使用高波动性时期(2020 年)还是低波动性时期(2019 年)的方差-协方差数据,情况基本上都是如此。
贝叶斯视角
到目前为止,在“数据科学”这个术语被创造出来之前,我们已经讨论过很久了。马科维茨获得诺贝尔奖的投资组合配置框架发表于 1952 年。几十年来,方差分析也一直是传统统计学的主要内容。这并不减损他们永恒的洞察力和实用性。但是让我们享受一下数据科学和贝叶斯建模的乐趣。
我不会对贝叶斯统计进行详细的解释,因为许多其他人已经在本出版物中全面地介绍了它。让我举一个金融市场中的频率主义者与贝叶斯观点的例子,来提供对概念差异的洞察。
如果问一位统计学家,在未来十年里,T4 的 P500 指数(美国主要股票市场指数)的平均年回报率是多少,他会说什么?资深统计学家会说 x%,因为这是标准普尔 500 指数的平均长期历史回报率。另一方面,贝叶斯统计学家可能会说 y%,因为市场的市盈率低/高,美联储预计会提高/降低利率。
换句话说,贝叶斯主义者提供了一种可能性,这种可能性取决于他们对世界状况的主观信念。它必然是“主观的”,因为在这种情况下,它涉及对未知未来的信念。在金融市场中,这是一个有用的方法,在我们参与市场之前,我们确实可能有一套信念,无论这些信念是明确的还是隐含的。
都说投资就是对未来有信仰,至少对我们投资的价格方向有信仰,而信仰不就是一套信仰吗?整个投资组合配置框架已经依赖于我们对高度不确定现象的资本市场假设。既然我们已经在这些先前的信念下劳作,那就让我们跟着它们一路奔跑吧!
贝叶斯推理
贝叶斯推断是基于估计而不是测试。目的是估计各种统计矩在样本间的差异。考虑到数据生成过程中的随机不确定性和我们对真实参数值知识的缺乏,在估计差异时,我还需要估计围绕该差异的不确定性。毕竟,我依赖的是对未来金融资产行为的预测。量化这种不确定性意味着围绕感兴趣的矩的点估计构建一个分布。
贝叶斯统计的基本概念是,我从某种现象的某种先验概率分布开始,基于我对结果分布的信念。然后我用来自观察数据的新信息更新这个先验概率,通知似然函数。得到的更新后的概率分布是 后验分布 。**

后验分布正比于概率乘以先验
因此,后验分布将由开始时指定的先验分布和观测数据确定。人们可能认为这是一件好事,因为后验分布受我们对先验分布的初始假设强度的影响。因此,我可以根据我的信念改变先验的信息性质,例如,如果我对参数有更大的信念,则使用更窄的先验分布,如果我没有更大的信念,则使用更宽的先验分布。
而且,观测数据也起着重要的作用,更多的数据会对得到的后验分布产生越来越大的影响。通常这是一件好事,但我在这个练习中需要小心,因为我没有“真实”的观察数据,这是在未知的未来。请记住,我们正在处理预期的投资组合时刻。
换句话说,我需要通过在我对先验分布的确信和模拟数据(更多内容见下文)之间小心平衡来接近贝叶斯推断。在这个过程中有很多主观性。这种方法的积极方面是,它迫使我们更深入地思考我们所持有的假设,以及我们对这些假设有多大的信心。
在下面的链接,布朗大学有一个很好的关于贝叶斯推理的互动网站。
在这种情况下,如何使用贝叶斯建模呢?我打算实现两个目标。首先是根据我的资本市场假设,对预期标准差(波动性)产生一个更全面的看法。我已经计算了标准偏差的点估计,但是理解这些点估计周围的分布会给我一个更完整的描述。****
****其次是了解这两种预期波动率分布是否不同。回到最初的问题:新兴市场债券有助于投资组合多元化吗?所以我想弄清楚,这两个期望标准差的分布,是否真的不同,以及不同的程度。
投资组合模拟
如上所述,我需要“观察数据”来通知我以前的分布。鉴于我依赖的是模拟数据,而不是真实的(未来的)观察数据,重要的是不要让它们淹没推理过程。更多的模拟将提供更高的精度,但在这种情况下,这将是一个错误的精度。
因此,我限制了模拟的样本量。另一方面,我们也应该通过对我们的资本市场假设表现出一些信心,来鼓起我们信念的勇气。否则,整个工作就没有任何意义。为了简单起见,我任意选择了中低 300 次模拟。只能说这显示了自信而不是傲慢。
投资组合数据是根据 BI 的预期收益和预期波动性模拟的,加上 2013 年至 2020 年的历史协方差数据,假设为多元正态分布。虽然金融市场的回报确实不是正态分布的,但由不同资产类别组成的多元化投资组合的回报分布很可能更接近正态分布。
在任何情况下,该假设用于简化模拟程序,并且只是贝叶斯建模练习的一部分。模拟投资组合数据的结果分布如下所示:
****
这些模拟分布近似对称,但较小的样本也会产生更分散的分布,这有利于表明我们对资本市场假设的不确定性。这些分布将在随后的贝叶斯建模练习中形成观察数据。来自模拟投资组合的统计矩应该接近上面发现的那些,但是稍微分散一些:

下面的贝叶斯模型允许我生成后验分布,不仅是预期收益的后验分布,更重要的是预期波动率的后验分布,正如我们将看到的。
贝叶斯建模
接下来,我指定了与该现象相对应的全概率模型。选择一个学生的 t 分布作为投资组合数据的似然函数,其尾部比正态分布更厚。三参数 t 分布又允许规定均值、标准差和自由度参数*。 v 的较大值使分布变得更加“正常”,并且 v 值被假定为由原始投资组合和 EM 受限投资组合的 t 分布共享。***
我指定 μ 、 σ 和 ν 的以下先验分布:
- ****μ的正态分布,使用上一节汇集的模拟投资组合数据的平均值和标准差乘以 2(使分布更宽);
- 均匀分布 of σ 具有零左界和 15%右界(请注意,这些约束是针对非年化标准差的,它们分别仅为 0.51%和 0.61%,因此是非常宽的均匀分布);和
- 的伽马分布的 ν 的,就是受此处讨论的启发。
下图显示了这些先验分布与似然函数参数的关系:

先验分布和似然函数
如果您想要与 frequentist 显著性检验进行比较,为均值和标准差的先验分布设置相同的规格类似于在这两个时刻设置无差异的零假设。这确实是我所做的。
采样过程根据给定的先验分布为感兴趣的参数生成值。然后,根据由模拟投资组合数据组成的观察数据,将先前的每个采样值与似然函数进行比较。概率被分配给每个采样的参数值。如果概率较高,则假设采样值是可信的,或者如果概率较低,则不可信。以这种方式,所有被接受的采样值被收集到包含最可能的参数值的新分布中。这就是的后验分布。****
这个过程的代码是:

原始投资组合(具有更大的新兴市场债券配置)和受新兴市场限制的投资组合的标准差的最终后验分布为:

波动率的分布,原始投资组合与受 EM 限制的投资组合(统一先验)

均值和波动率差异的确定性分布
同样,贝叶斯方法的优势是我能够生成各个时刻的可观测分布,而不是像早期的 frequentist 方法那样仅仅依赖点估计。换句话说,贝叶斯后验分布提供了关于我的估计精度的完整信息(在这种情况下是标准差),不像频率主义方法,我可能只拒绝或不拒绝零假设(估计之间没有差异)。此外,我能够构建各种矩之间的差异的分布,包括标准差的,这在频率主义者的推断中是不可能的。
我们看到,原始投资组合的标准差分布(红色)只有部分重叠,并且位于 EM 受限投资组合的标准差分布(蓝色)的左侧(较低值)。各个投资组合标准差之间的差异分布显示其均值为 -1.7% ,差异等于或大于零的概率只有0.1%。我不能用频率主义者的显著性检验来做这样的陈述。
因此,我以高度的统计信心得出结论,即原始投资组合的波动性低于受新兴市场限制的投资组合,实际上低 18.3%(或 1.7%/9.3%)。
考虑到要使用的适当分布的不确定性,特别是预期标准差的先验分布,我使用投资组合标准差的更具信息量的先验分布来重复分析。在这种情况下,我用一个半柯西分布代替均匀先验,同时保持所有其他先验不变:****

柯西分布与学生的单自由度 t 分布相同,因此是一个具有明显厚尾的对称分布。我使用(正)半柯西分布,因为标准差不能为负。
原始投资组合(具有更大的新兴市场债券配置)和受新兴市场限制的投资组合的标准差的最终后验分布为:

波动率的分布,原始与 EM 限制的投资组合(半柯西先验)

均值和波动率差异的确定性分布
****结论与大同小异。原始投资组合的波动性或年化标准差再次低于 EM 受限投资组合的波动性或年化标准差,并且在统计上与 EM 受限投资组合的波动性或年化标准差不同。人们当然可以尝试其他以前的发行版,看看这个结论是否对不同的规范是健壮的,但是我将在这里结束它。
官方的 PyMC3 开发者网站提供了几个关于如何使用该软件包的详细案例研究,例如药物试验评估(链接)。
结论
在这次练习中,我要回答的问题是,新兴市场债券是否能够为全球投资组合带来多样化的好处。我通过经典的均值-方差投资组合优化框架进行分析。资本市场假设主要改编自 2021 年 2 月发表的贝莱德研究所(Blackrock Institute)的假设,特别是九种全球资产类别的预期回报(T21)和波动性。我还使用了这九个资产类别的历史 2013–2020 协方差数据。
多样化是通过优化投资组合的预期波动率或年化标准差来衡量的。各种统计显著性测试的结果拒绝了方差相等的零假设,从而让我得出结论加大对新兴市场债券的配置确实降低了预期波动性。当我通过使用高波动期(2020 年)和低波动期(2019 年)的历史方差-协方差矩阵对优化程序进行情景测试时,情况仍然如此。
然后,我用贝叶斯模型增强了分析,这让我能够精确估计增加的新兴市场债券敞口降低了投资组合的波动性。各个投资组合波动率之间的差异分布平均值为 -1.7% ,差异等于或大于零的概率仅为 0.1%。这意味着,持有更多新兴市场债券将整体预期波动性降低了约 18%。
这项研究的总体结论是,增加新兴市场债券配置,同时增加对中国债券的敞口,确实为全球金融资产组合带来了显著的多元化收益。
(本练习的完整 Python 代码和支持文档可在我的 GitHub 页面获得。如果直接渲染 GitHub 笔记本文件有问题,请使用 nbviewer 。)
如果你看到了阅读这样的文章的价值,你可以在这里订阅 Mediumhttps://at-tan.medium.com/membership来阅读我和无数其他作家的其他文章。谢谢你。****
**https://medium.datadriveninvestor.com/bitcoins-shifting-relationship-to-macro-factors-5465d542078f https://medium.datadriveninvestor.com/top-python-hacks-for-finance-f9ea900a686c **
基于贝叶斯统计模型的室内 Wi-Fi 用户定位
用 Pymc3 识别室内 Wi-Fi 用户的位置

在全球定位系统的帮助下,室外定位有了长足的发展。然而,当面对室内的情况时,我们正遭受重要的不准确性。Wi-Fi 网络的存在为构建定位系统提供了另一种选择,近年来在这一领域进行了大量的研究,其中基于无线信号强度信息的定位因其低成本和易于实现而备受关注。然而,由于难以获得数据的位置标签,在实践中有时难以建立监督模型。
在这篇文章中,你将会读到:
- 基于接收信号强度指标(RSSI)学习 Wi-Fi 用户位置的无监督模型。
- 如何使用贝叶斯模型框架 Pymc3 进行具有不确定性容限的定位?
基于 RSSI 的 Wi-Fi 用户定位
RSSI 定位技术基于测量从客户端设备到几个不同接入点的 RSSI 值,然后将该信息与传播模型相结合,以确定客户端设备和接入点之间的距离。使用一种短程无源雷达技术的无线传感器可以捕捉到 RSSI 的值,精确度令人惊讶。不幸的是,在实践中,很难将设备的位置与其在接入点的传感器接收到的 RSSI 值一一对应起来。然而,好消息是,在我们的情况下,尽管缺乏标记数据,但背后的物理学可以帮助我们建立一个无监督的模型。对于在某个接入点的设备 d 和传感器 s,下面的等式已经被物理经验所证明:

图片作者:RSSI 值 wrt 设备和传感器
其中 a 是常数,p(s),p(d)是传感器和设备的位置。其余三个函数值仅取决于设备和传感器。
从这个等式中,我们可以看出,一旦我们知道了常数 a 的值,即设备和传感器的三个剩余函数和位置的表达式,就可以相对简单地得到 RSSI 值。
为了简单起见,我们假设所有三个函数都是常数,也就是说,该方程可以简化为设备和传感器的对数距离的线性回归:

作者图片:简化的 RSSI 方程
假设你知道上述等式中常数的精确值以及传感器的位置,那么很容易识别出该设备所在的球体,因为该等式得到满足。理论上,我们只需要 4 个传感器来定位一个设备。但是注意,这个问题比理论更复杂,原因有三:
- 我们无法接触到方程中的两个常数。
- RSSI 测量值往往会随着环境的变化而大幅波动,因此该等式并不总是成立的。
- 实际上,我们遭受了重大的数据损失。
设备定位的贝叶斯模型
想象一下,现在我们有一个来自几个传感器的 RSSI 值的数据集,该数据集可能质量很差:部分传感器的数据丢失、信号减弱等。我们能做些什么来定位从他们的设备发送这些 RSSI 值的 Wi-Fi 用户?我的建议是:给模型一些不确定性的权利。这种不确定性在许多使用情况下是容许的:例如,在购物中心,操作员知道用户在哪个商店/区域就足够了,而不是精确的点。
假设我们知道所有 Wi-Fi 传感器的位置。目标空间被离散成多个位置。未知常数服从某种正态分布。该模型的思想是首先假设目标可能是具有某些概率的这些位置,并且这些概率可以生成 RSSI 值的分布。现在对于一个给定的 RSSI 向量,目标是找到一个位置和一对能够生成这个给定向量的常数,也就是最大化似然对数(p(RSSI | d;a,c))。
回想一下,我们在上一节中说过,RSSI 测量值往往会随着环境的变化而大幅波动。因此,我们在此再做一个假设,即给定传感器和给定设备发送的 RSSI 值遵循正态分布。
用 Pymc3 建模
现在让我们看看如何用 P ymc3 建立模型:一个用于贝叶斯统计建模和概率机器学习的好工具。还要强调的一点是,Pymc3 将在训练中跳过丢失的 RSSI 值(Nan ):假设您有 8 个传感器,其中 2 个丢失了一个设备的 RSSI 值,Pymc3 将使用剩余的 6 个来训练模型。
让我们首先导入我们需要的所有包:
import pymc3 as pm
import theano.tensor as tt
import numpy as np
import pandas as pd
from statistics import median
现在让我们建立一个 Wi-Fi 定位模型,它的输入是:1。边界:我们进行采样的目标空间的边界;2.观测值 _n:观测到的 RSSI 值的总数;3.RSSI:提供的 RSSI 值;4.传感器位置:传感器的位置;5.sensors_n:传感器的总数。在下面的代码中,我简单地做了两件事:
- 具有先验分布的采样参数和目标位置。
- 建立一个正态分布的 pm.model。
def wifi_localization_model(
bounds,
observations_n,
rssis,
sensor_positions,
sensors_n,
):rssis=pd.DataFrame(rssis)
dimensions_n = len(bounds)
#build the pm model
model = pm.Model()
sensor_positions=sensor_positions.reshape((1, sensors_n, dimensions_n))
with model:
device_location_dimensions = []
device_location_variables = []
#sampling the positions of the devices with a normal distribution
for i, bound in enumerate(bounds):
x = pm.Normal(
name="x_{}".format(i),
mu=(bound[0] + bound[1]) / 2,
sigma=(bound[1] - bound[0])/4 ,
shape=len(rssis),
)
device_location_variables.append(x)
device_location_dimensions.append(tt.reshape(x, (-1, 1)))
device_location = tt.concatenate(device_location_dimensions, axis=1)
device_location = tt.repeat(
tt.reshape(device_location, (len(rssis), 1, dimensions_n)),
sensors_n,
axis=1,
)
#sampling the constants of the RSSI equation with a uniform distribution
n_var = pm.Uniform(name="n", lower=0, upper=10)
gain_var = pm.Uniform(name="gain", lower=-100, upper=-20)#sampling the noise of the RSSI with a normal distribution
noise_std_var = pm.Uniform(name="noise_std", lower=0, upper=100)
log_distance = (
-10
* n_var
* 0.5
* tt.log10(
tt.sum(
tt.sqr(
device_location
- np.repeat(sensor_positions, len(rssis), 0)
),
axis=2,
)
)
)
rssi = log_distance + gain_var
pm.Normal(
name='observation', mu=rssi, sigma=noise_std_var, observed=rssis
)
# start with the initializer "advi"
tr = pm.sample(
draws=3000,
tune=500+dimensions_n * 100,
chains=4,
init="advi",
target_accept=0.75 + 0.05 * dimensions_n,
)
n= median(tr['n'])
gain=median(tr['gain'])
noise=median(tr['noise_std'])
estimated_location = []
estimated_std = []
locations = []
for i, device_location_variable in enumerate(device_location_variables):
summary = pm.summary(
tr, var_names=[device_location_variable.name]
)
estimated_location.append(
summary["mean"].values.reshape(-1, 1)
)
estimated_std.append(summary["sd"].values.reshape(-1, 1))estimated_std.append(summary["sd"].values.reshape(-1, 1)) predictions = np.hstack(estimated_location)
estimated_std.append(summary["sd"].values.reshape(-1, 1))
estimated_std = np.hstack(estimated_std)return (
predictions,
estimated_std,
gain,
noise,
)
结果
我已经在我自己用 21 个传感器生成的合成数据集上测试了这个模型。当然,我去掉了一部分数据来模拟数据丢失的情况。这是 RSSI 方程中所有常数的轨迹图:

作者图片:模型参数的轨迹图
下面是贝叶斯模型与正常随机猜测和均匀随机猜测的误差比较:

图片作者:贝叶斯模型比较两个随机猜测模型
我们已经取得了良好的准确性!
在结束这篇文章之前,我想给你看一个我在我的地方做的实验:我安装了 6 个 Wi-Fi 传感器,并用我从它们那里获得的 RSSI 值拟合模型。有了 estimated_mean 和 estimated_std,我就可以画出一个球体,在这个球体里面,一个设备在任何时刻都可以在。此外,通过每 5 分钟给出这样的预测球体,我可以绘制出我感兴趣的 Wi-Fi 用户的轨迹。下面的 Gif 是我在一个小时内的轨迹:

作者图片
结论
在这篇文章中,我们建立了一个贝叶斯无监督模型来定位基于 RSSI 数据的室内无线设备,具有不确定性的容忍度。在 Pymc3 提供的概率编程的帮助下,尽管数据的质量并不完美,但我们已经通过相对简单的实现获得了良好的准确性。
调查中可能的偏差
公平和偏见
如何纠正常见的调查偏差以获得更可靠的答案

在数据科学领域,我们处理的数据对我们得到的预测有重大影响。预测模型是基于数据创建的。
数据科学家也是科学家,我们需要意识到我们正在处理的数据中的偏差。我来自社会科学背景,在我的学科中,知识被表述为力量之间战斗的策略。信息被视为政治信息。客观性是一种错觉。在定性研究中,我们只是接受研究人员有偏见,这影响了研究的结果。定性研究者并不声称他们不受主观性的影响。
数据分析产生主观结果的原因之一是数据本身。我们不能盲目相信现有的数据。调查是收集数据的最常见方式之一。在这篇文章中,我将讨论调查中一些可能的偏差,以及如何纠正它们。
默许偏见
默许偏差被定义为同意陈述而不是不同意陈述的倾向 (Hurd & Kapteyn,1999;史密斯·迪尔曼&克里斯蒂安,2014 年,第 100 页)。同意/不同意(A/D)问题很容易产生和应用。这可能是它们如此普遍的原因之一(Saris 等人,2010 年)。另一方面,在使用 A/D 调查时,研究者应该考虑默许偏差的威胁。当出现默许偏差时,参与者倾向于在同意/不同意选项中选择“同意”或在“是/否”选项中选择“是” (Kuru & Pasek,2015)。与自我管理的调查相比,由个人管理的调查的效果可能更强(Dillman,Smyth & Christian,2014 年,第 100 页)。

同意/不同意问题示例
在默许偏差的情况下,数据可能会出现错误,因为回答给参与者的真实态度添加了不同的元素(Kuru & Pasek,2015)。最重要的是,默认偏见可以导致类似措辞的问题之间的相关关系,因此,由于这种不相关的关系,消除了一些重要的结构(Bagozzi,1984;麦肯齐&波德萨科夫,2012;库鲁&帕塞克,2015)。即使是与实际反应的微小偏差也会损害研究人员做出可靠推断和预测的能力(Javeline,1999)。因此,即使是源于默许偏差的小调查误差也会降低推论的质量。数据科学家应该警惕他们的数据集中可能存在的默许偏差。
文化也会造成默许偏差。在许多文化中,当与另一个人互动时,同意比不同意更合适(Javeline,1999,Dilman 等人,2014)。
Saris 等人(2010 年)陈述了默许偏差的三个理论上的主要原因。
- 一些人的个人倾向可以导致他们彬彬有礼,避免冲突,最终具有攻击性。
- 一些参与者可能认为自己的社会地位低于采访者/研究者。因此,他们可能会相信问题中提供的内容,并无意中接受“同意”选择。
- 与不一致相比,人们普遍偏向于确认。这就造成了默许偏差。当出现默认偏差时,暗示确认的反应会导致估计均值的增加,并使反应比其实际形状更积极。
如何消除默许偏差
该调查最明显的弱点之一可能是默许偏差。为了消除默许偏差,将同意/不同意(A/D)回答转换为项目特定(IS)回答格式是有效的,因为 IS 回答格式可以更好地避免默许偏差。针对具体项目的回答格式可以给出更有效和可靠的结果,并提高调查的整体质量(Saris et al .,2010)。无论类别的数量是多还是少,IS scale 都能给出更好的结果。需要注意的是,较低的质量会造成变量之间的相关性(Lord & Novick,1968;Saris 等人,2010 年)。这给调查设计带来了多重共线性问题。

特定于项目的问题示例
就可靠性而言,针对具体项目的调查比同意/不同意的调查表现更好。这背后的原因是,与同意/不同意调查相比,参与者在选择特定项目调查中的立场时更加确定 (Saris 等人,2010 年)。
下面是一个将同意/不同意问题转换为项目特定格式的示例。第一个是原问题。第二个是我从原始版本修改而来的特定于项目的格式。

第一个问题来自于 M. Viswanathan 在 1993 年提出的数字信息偏好(PNI)调查。第二个问题是它的一个修改版本,采用特定于项目的格式
另一个可能的偏差:反应顺序效应
大量文献表明,对调查问题的回答受到回答顺序的影响。也就是说,较早出现的选择更有可能被选中 (Becker,1954;贝尔森,1966 年;布鲁克和厄普顿,1974;鲤鱼,1974;穆勒,1970 年,s .佩恩,1951 年;j .佩恩 1971;奎因和贝尔森,1969 年;鲁格和坎特里尔,1944 年;舒玛和普雷斯 1981,克罗斯尼克,1991)。根据眼球追踪数据,如果答案选项是分类的,受访者倾向于连续设想它们。此外,第一个选项比后面的选项更受关注(Galeš ic 等人,2008 年;Tourangeau,2018)。一些受访者在得出一个可接受的答案时停下来,再也看不到其他选项。
hne 和 lenz ner(2015)的眼球追踪研究陈述了三个发现。
- 在评定等级中,反应顺序的影响相对较小。
- 参与者选择反应量表前半部分的频率高于后半部分。这种情况尤其会发生在垂直放置答案的等级量表中。
- 如果受访者在回答量表的前半部分花费更多时间,他们更有可能选择其中一个答案。
如何消除响应顺序效应
为了弥补响应顺序的影响,可以将响应从 A/D 转换为特定于项目的响应。研究表明,回答顺序在 A/D 中有效,但在具体项目的问题格式中无效(hne & Krebs,2018)。此外,受访者的评估表明,完成特定项目问卷比完成 A/D 问卷需要更多的考虑。研究结果表明,IS 问题比 A/D 问题更能抵抗反应顺序效应(hne & Krebs,2018)。
另一种可以减少响应顺序影响的方法是随机改变不同参与者的响应顺序。通过使用在线调查工具,这可以很容易地实现。我使用了 Survey Monkey 和 Qualdtrics 网站。他们提供了一个随机响应顺序选项。
最后,响应选项的排列应垂直放置,因为垂直排列的顺序效应较小(hne&Krebs,2015)。
A/D 响应调查的认知负担及补救措施
在调查中,参与者被要求花费相当多的认知努力,同时在很少或没有奖励的情况下回答调查(Krosnick,1991)。一些回答者可能出于各种原因,如自我表达的愿望、自我理解、情绪图表、智力挑战、人际关系反应等,而被激励付出精神努力来回答问题。(沃里克和林宁格,1975;克罗斯尼克,1991 年)。
自 20 世纪 80 年代以来,人们认识到调查中的很大一部分测量误差源于与参与者的预期认知操作相关的问题(Tourangeau,2018)。调查方法认知方面高级研讨会(CASM)提供了调查响应的四个认知过程的四个组成部分,它们是(Tourangeau,1984):
- 对问题的理解
- 信息检索
- 将信息整合到评估或判断中
- 该判决的报道
理解过程是关于理解问题中确切地问了什么。认知访谈研究表明,大多数调查错误源于理解问题 (DeMaio 和 Landreth,2004;普莱瑟和布莱尔,1994;威利斯和谢克特,1997 年;Tourangeau,2018)。
有不同的方法可以帮助参与者以预期的方式理解问题,并减少理解问题。一个有效的方法是对问题中的关键术语给出定义(绍贝尔和康拉德,1997)。此外,在需要时提供澄清也改善了对问题的理解(Ehlen 等人,2007;Tourangeau,2018)。然而,提供澄清更适合面对面的应用调查,而不是在线调查。
提取是参与者在解释调查问题时发生的另一个认知过程,因为在解释过程中,他们需要从记忆中提取信息来回答问题,而提取可能会失败(Tourangeau,2018)。信息检索可能会因为不同的原因而失败。它可能会失败,因为:
- 从不编码信息
- 记忆中的系统性失真
- 对事件时间和地点的混淆
提供更丰富的提取刺激有助于提高回忆。另一种更好的检索方法是引出生活事件。这可以触发记忆,便于及时排序(Tourangeau,2018)。
第三个认知过程是信息的整合。信息通常被整合到评估或判断中。达到这种估计或判断的路径会影响调查问题的认知负担。当有关于行为频率的问题时,受访者使用的检索策略可能会影响他们的报告错误。当询问频率时,参与者可能会少报或多报行为 (Tourangeau,2018)。
产生认知负担的第四个过程是报告答案。也就是说,调查可以提供封闭式答案或开放式问题。在回答时,受访者大多将答案的终点定在与类别相关的极端情况上。中点可以看作中性点(Tourangeau,2018)。这个过程产生了认知负担。A/D 格式会带来更多的认知负担。也就是说,在分类回答中可以考虑 A/D 反应。作为分类响应格式,两个相邻选项之间的距离在模数响应中并不明确(Fowler and Cosenza 2008 刘等,2015)。参与者在回答问题时可能会经历这种认知负担。另一方面,与 A/D 问题相比,项目特定问题的顺序性较差。因此,应答者在报告答案的认知过程中可能经历较少的负担。
同意/不同意(A/D)回答格式比项目特定格式需要更多的认知步骤(Carpenter and Just 1975 克拉克和克拉克 1977;特拉巴索,罗林斯,1971,刘等,2015)。必须记住,对高认知负担的需求会对可靠性产生负面影响。需要较少认知负担的响应格式更有利于稳健测量。因此,就认知负担而言,项目特异性反应格式比 A/D 格式更有利。
结论
在帖子中,我讨论了在一项调查中可能引起偏差的三个主要原因:默许偏差、反应顺序效应和认知负担效应。当然,还有更多可能的偏差。
数据科学家在解释他们的人工智能模型的结果时,也要对他们的推论的科学基础负责。我们需要承认,所有的数据都有一定程度的偏差,我们的预测模型也带有来自数据的偏差。因此,熟悉数据收集工具的偏差,尤其是调查,可以让我们对自己的解释更加谨慎。
参考文献
h . m . alkhateeb 和 a . s . Abed(2002 年)。阿拉伯语翻译量表的因子结构,对数字信息的偏好。知觉与运动技能, 94 (1),185–188。
巴戈齐(1984 年)。市场营销理论建设计划书。期刊
营销,第 48 卷第 1 期,第 11e29 页。http://doi.org/10.2307/1251307.
贝克,S. L. (1954)。为什么是秩序效应?舆情季刊, 18 (3),271–278。
贝尔森,W.A. (1966)。颠倒言语等级量表呈现顺序的效应。广告研究杂志,6 期,30–37 页。
布鲁克和厄普顿(1974)。由于选票上的位置而导致的地方政府选举中的偏见。英国皇家统计学会杂志:C 辑(应用统计学), 23 (3),414–419。
J. S .布鲁纳和 h .塔伊费尔(1961 年)。认知风险和环境变化。《变态与社会心理学杂志》, 62 期 (2),231 页。
卡普,F. M. (1974)。职位对面试回答的影响。老年学杂志, 29 (5),581–587。
卡彭特帕特里夏 a 马塞尔 a。1975.句子理解:心理语言学
验证的处理模型。"心理回顾82:45–73。
赫伯特·h·克拉克和伊芙·v·克拉克。1977.心理与语言。纽约:哈科特·布雷斯
约万诺维奇。
克劳博士和马洛博士(1964 年)。认同动机:评价依赖研究。纽约:威利。
德梅奥、T. J .、兰德雷斯(2004 年)。不同的认知面试技巧会产生不同的结果吗?调查问卷的测试和评估方法, 546 ,89–108。
d . a . dillman,Smyth,J. D .,& Christian,L. M. (2014 年)。网络、电话、邮件和混合模式调查:量身定制的设计方法。约翰·威利的儿子们。
埃伦,p .,绍贝尔,M. F .,,康拉德,F. G. (2007)。模拟语音不流畅以预测语音调查界面中的概念偏差。话语过程, 44 (3),245–265。
Galesic,m .、Tourangeau,r .、Couper,M. P .、和 Conrad,F. G. (2008 年)。眼球追踪数据:对调查反应中反应顺序效应和其他认知捷径的新见解。舆情季刊, 72 (5),892–913。
Hö hne,J. K .,& Krebs,D. (2018 年)。同意/不同意和特定项目问题中的尺度方向效应:问题格式的比较。《国际社会研究方法论杂志》,第 21 卷 (1),第 91 页。土井指数:10.1080/136453864867
Hö hne,K. J .,& Lenzner,T. (2015 年)。使用眼动追踪研究网络调查中的反应顺序效应。 Psihologija , 48 (4),361–377。
Hurd,M. D .,和 Kapteyn,A. (1999 年)。家庭调查中测量资产的锚定和默认偏差。在偏好的引出(第 111-138 页)。斯普林格,多德雷赫特。
Javeline 博士(1999 年)。礼貌文化中的回应效应:哈萨克斯坦的默许测试。民意季刊,1–28。
克罗斯尼克,J. A. (1991 年)。应对调查中态度测量认知需求的应对策略。应用认知心理学, 5 (3),213–236。
刘,李,s .,,康拉德,F. G. (2015)。比较同意-不同意和项目特定量表之间的极端反应风格。舆情季刊, 79 (4),952–975。
洛德,F. M .,&诺维克,M. R. (1968 年)。智力测验分数的统计理论。IAP。
麦肯齐,S. B .,& Podsakoff,P. M. (2012 年)。营销中常见的方法偏差:原因、机制和程序补救。零售杂志, 88 (4),542–555。
穆勒,J. E. (1970)。从 133 名候选人中选择。《民意季刊》, 34 (3),395–402 页。
佩恩(1951 年)。提问的艺术普林斯顿大学出版社。
佩恩博士(1971 年)。在邮寄调查中颠倒口头等级量表顺序的影响。市场研究学会杂志, 14 ,30–44。
佩蒂格鲁,T. F. (1958)。范畴宽度作为认知变量的测量和相关因素。个性杂志, 26 (4),532–544 页。
普莱塞和布莱尔(1994 年)。调查预测试:不同的方法会产生不同的结果吗?。社会学方法论,73–104。
Saris,w .,Revilla,m .,Krosnick,J. A .,& Shaeffer,E. M. (2010 年 5 月)。将带有同意/不同意回答选项的问题与带有特定项目回答选项的问题进行比较。在调查研究方法(第 4 卷,№1,第 61–79 页)中。
绍贝尔,M. F .,,康拉德,F. G. (1997)。对话式访谈能减少调查测量误差吗?。民意季刊, 61 期 (4),576 期。
特拉巴索、汤姆、霍华德·罗林斯和爱德华·沙乌格内西。1971.“储存和核查
处理概念的阶段。"认知心理学2:239–89。
索恩,s .,希斯洛普,T. G .,郭,m .,,阿姆斯壮,E. A. (2006)。希望和概率:癌症交流中数字信息意义的患者观点。定性健康研究, 16 (3),318–336。
Tourangeau 河(2018 年)。从认知的角度看调查响应过程。教育中的质量保证,26 (2),169–181。doi:10.1108/QAE
Tourangeau 河(1984 年)。认知科学和调查方法。调查方法的认知方面:在学科之间架起一座桥梁, 15 ,73–100。
Tourangeau,r .,Couper,M. P .,& Conrad,F. (2007 年)。颜色、标签和反应量表的解释试探法。民意季刊, 71 (1),第 91–112 页。
教科文组织。(2016).教育造福人类和地球:为所有人创造可持续的未来:全球教育监测报告。
维斯瓦纳坦(1993 年)。对数字信息偏好的个体差异的测量。《应用心理学杂志》, 78 (5),741 页。
维斯瓦纳坦,m .,,蔡尔德斯,T. L. (1992)。产品属性量值的编码和利用:使用数字和语言信息的调查/1992: 105。 BEBR 教员工作论文;编号 92–0105。
沃里克,D. P .,&林宁格,C. A. (1975)。抽样调查:理论与实践。麦格劳-希尔。
威利斯和谢克特(1997 年)。认知访谈技术的评估:结果能推广到现场吗?社会学方法论通报/Bulletin de methodologie Sociologique, 55 (1),40–66。
沃森,J. M. (1988)。成就焦虑测验:维度与效用。《教育心理学杂志》, 80 (4),585。
浓缩咖啡后的咖啡颗粒分布
咖啡数据科学
探究萃取如何影响颗粒大小
由于我一直在观察的粒子分布,我对拍摄后的情况感到好奇。萃取如何改变颗粒大小?萃取会使咖啡缩水吗?
幸运的是,我能拍摄分层照片,最近我一直在用壁龛研磨机拍摄 sudo-staccato 照片。此外,我在精细层和粗糙层之间使用了一个布过滤器,使它们更容易分离。


左:击球后的良好场地。右图:击球后地面粗糙/中等。所有图片由作者提供
我拉了一些镜头,唯一的技巧是分离粗糙(设置 30 或 S30)和中间(设置 15 或 S15)层。然而,一个小勺子解决了这个问题。
这里是所有的情节组合,但我也将它们进一步分离如下。

对于设置为 0,峰值没有移动太多,但其他一切都变得更好。

对于设置 15,这种转变是明显的。

同样适用于设置 30:

累积分布的差异如下所示:

翻转轴
我们可以翻转 X 和 Y 轴,看看分布如何移动,如下图所示。我假设粒子会收缩,这可能是提取的一个很好的近似。

我通过对每个粒子分布做最佳拟合多项式来做到这一点。然后我可以计算拍摄前后累积体积的变化。例如,在上面的图表中,我可以看到 50%的颗粒直径小于 340 微米,而拍摄后,50%的颗粒直径约为 280 微米。

人们可以将拍摄后的直径变化百分比视为提取,但我不确定这有多一致。在这种情况下,看起来像是在上方或下方的粒子没有移动太多的地方出现了峰值提取。
然而,我不想假设一个特定直径的粒子比其他大小的粒子均匀地提取。该图中较低体积百分比处的高值是由于测量的不确定性。
这个实验非常简单,粒子分布符合我的预期。我不确定除了一般的确认之外,还能对这些信息做些什么,这是拼图中的另一块,也许有一天会派上用场。
如果你愿意,可以在 Twitter 和 YouTube 上关注我,我会在那里发布不同机器上的浓缩咖啡视频和浓缩咖啡相关的东西。你也可以在 LinkedIn 上找到我。也可以关注我中。
我的进一步阅读:
浓缩咖啡的拍摄后质量指标
咖啡数据科学
探索未知数据点
在一些粗略的探索中,我发现了一些我认为可能有助于提供更多浓缩咖啡质量信息的指标。我使用的两个主要指标是提取率(来自总溶解固体(TDS))和最终得分(7 个味道指标)。然而,在浓缩咖啡中还有很多其他潜在的变量。
我将在这项工作中探索两个变量:击球后的冰球重量和地面 TDS (gTDS) 。
这两个变量都有助于理解一个镜头是如何流动的,以及提取发生在哪里。
定义
击球后冰球重量由击球后冰球的重量决定。在常规的浓缩咖啡拍摄中,这不会提供更多有用的信息,但我通常会在圆盘的顶部和底部之间用布过滤器进行拍摄。这让我可以分别称量用过的冰球的顶部和底部。
我将这个权重与每一半的输入权重结合起来,以确定对各层 EY 的估计。这有几个假设:
- 水均匀地保留在顶部和底部。
- 顶部和底部将以相似的湿/干比干燥,通常测量值约为 2.2。
这些假设是基于我以前收集的关于分层提取 T4 的数据,但这并不意味着它们在更大的数据集中是可靠的。我提前承认了这个事实。
咖啡渣 TDS (gTDS) 通过取一些咖啡渣,将它们放在数字折光仪上(在这种情况下是 Atago),加入少量水,并测量 TDS 来确定。这让我们知道还有多少可溶物在地下。
我先前已经发现 gTDS 有助于理解哪里流动更快,这是通灵迹象。
绩效指标
我使用两个指标来评估技术之间的差异:最终得分和咖啡萃取。
最终得分 是评分卡上 7 个指标(辛辣、浓郁、糖浆、甜味、酸味、苦味和余味)的平均值。当然,这些分数是主观的,但它们符合我的口味,帮助我提高了我的拍摄水平。分数有一些变化。我的目标是保持每个指标的一致性,但有时粒度很难确定。
使用折射仪测量总溶解固体(TDS),该数字与一杯咖啡的输出重量和输入重量相结合,用于确定提取到杯中的咖啡的百分比。
此外,我在数据分析中使用相关性。
相关性 是表示两个变量彼此相似程度的度量。高度相关并不意味着一个变量会引起另一个变量,而是当情况发生变化时,两个变量的涨跌幅度相同。我从一开始就假设一些分级变量会有很高的相关性,因为它们是从不同的时间点来看味道的。
数据
我随着时间的推移,通过多次拍摄来观察这些因素,以了解它们是否有用。我从最终得分和 EY 的相关性开始。

所有图片由作者提供
相关性揭示了一些看起来非常直接的事情:
- 最低的 ey 和最高的 EY 估计值与 EY 相关,因为它们来自 EY,这意味着它们也应该与口味相关,但不如 EY 那么强烈。
- 底部 gTDS 与味道和 EY 呈负相关,这也是有意义的,因为它是一个衡量什么可溶物留在地上的指标。
- 底部/顶部提取估计值、顶部 GTD 和输入 GTD 的比率与口味没有很好的相关性,但输入 GTD 似乎与 EY 呈负相关。
****
当绘制这些变量的数据时,有一些不错的相关性。

衡量 gTDS 似乎与最终得分或 EY 没有很好的联系。
我还绘制了底部的 GTD 与底部的 EY 估计值,以查看它们之间的关系。

基于相关性,这两个变量没有很强的联系。

随着时间的推移,我希望找到一些度量标准来查看,但是它们似乎没有我所希望的有用,至少对于日常收集来说没有用。这仍然是收集额外数据的一个有趣的尝试。
如果你愿意,可以在推特和 YouTube上关注我,我会在那里发布不同机器上的浓缩咖啡照片和浓缩咖啡相关的视频。也可以在 LinkedIn 上找到我。也可以在中关注我。
我的进一步阅读:
一个完整的工作流程
用 PostGIS 分四步应对地理空间挑战

如果您有世界各国和主要城市的单独 shapefiles,则可以使用 ArcGIS、QGIS 或任何其他 GIS 软件包查看和查询数据。你可以在这里下载来自的数据(用天然泥土制成)。
例如,您可以执行以下操作:
- 使用识别工具获取关于您点击的特征的一些信息
- 确定或设置数据参考的空间参考系统
- 重新投影您的数据
- 计算距离和面积
- 计算质心
- 执行缓冲分析
好消息是,你可以用 PostGIS 完成所有这些操作。对于在空间数据库上执行所有这些操作的必要性,可能会出现一个问题。当我们可以在开源或专有软件包中轻松解决问题时,在 PostGIS 中重新表述我们的问题有什么意义呢?
当您想要开发 web、移动或桌面应用程序时,PostGIS 会派上用场。我们在这里使用的数据只包含有限数量的静态记录。在某些情况下,您可能会发现自己处于连续数据流的情况下。桌面 GIS 工具可能不适合消化此类数据并执行(近)实时分析。
处理地理空间问题时,您可以考虑以下四个重要步骤来正确回答问题:
我在图上为你描绘了这些步骤:

图 1-空间数据库的空间问题工作流(图片由作者提供)
我们将经历所有这些步骤,并尝试应对之前提出的挑战。
1.系统模型化
建模是将现实世界转化为由数据库对象组成的模型的过程。换句话说,根据定义,模型是现实的简化表示。例如,将国家表示为面,将城市表示为点。然后,您将为每个对象创建一个表。您需要存储关于每条记录的一些信息。您可能无法保留关于它们的所有信息,因此您将选择那些有助于解决当前问题或以后可能出现的问题的信息。
第一步,您需要创建一个数据库和一个模式来保存您的数据。模式是一个容器,它对对象(表、函数、视图等)进行逻辑分段,以便更好地管理。您在 PostgreSQL 中创建的数据库中有一个默认的公共模式,但我会创建一个新的。请记住,这取决于您希望在模型中简化真实世界的程度。建模本质上是简单性和充分性之间的权衡。尽管所有模型都有缺陷,但它们为我们提供了一种更具成本效益的方法。最好先创建一个数据库和一个模式:
CREATE DATABASE postgis_training;
CREATE SCHEMA postgis1;
默认情况下,PostGIS 功能和操作不起作用,您需要在数据库中启用 PostGIS 才能使其起作用:
CREATE EXTENSION postgis;
我们需要创建两个表来存放世界国家和城市数据:
CREATE TABLE postgis1.countries (
id serial,
formal_name VARCHAR(50) PRIMARY key,
country_iso3 CHAR(3),
population INT,
gdp INT,
economy VARCHAR(50),
income_group VARCHAR(50),
continent VARCHAR(50),
subregion VARCHAR(50),
region_world_bank VARCHAR(50),
geog geography(multipolygon, 4326)
);CREATE TABLE postgis1.cities (
id SERIAL PRIMARY KEY,
city_name VARCHAR(50),
country_iso3_code CHAR(3),
featurecla VARCHAR(50),
population INT,
geog GEOGRAPHY(POINT, 4326)
);
看看你已经下载了的 shapefiles。我们不需要他们提供的所有字段。我们选择适合我们问题的方法。我认为我们创建的表中的列名足够直观,除了其中的几个之外,不需要任何解释。这里的 country_iso3 和 country_iso_code 列表示 Alpha-3 代码,是由国际标准化组织(ISO) 出版的ISO 3166–1中定义的三个字母的国家代码。为了避免混淆,我特意选择了不同的名称。对于具有相同含义的列,可以使用相同的名称。
您可以在图 2 中看到我们数据库的 ERD 表示。对于那些可能不熟悉 spatial_ref_sys 表的人来说,它是每个启用了 PostGIS 的数据库中的一个表,列出了所有有效的 SRID 值及其对应的空间参考系统(SRS)的 proj4text 表示。

图 2 —数据库的 ERD 表示(图片由作者提供)
2.准备和加载数据
让我们把你所有的形状文件直接导入我们的数据库。当我们直接从外部文件导入数据时,我们通常称之为临时表。临时表只是包含原始数据的临时表。它们用于填充我们已经设计好的模型中的主表。为了导入ne _ 50m _ populated _ places . shpshape file,我们使用 PostGIS 安装附带的命令行实用程序 shp2pgsql 。在操作系统的命令行工具中导航到 shapefiles 文件夹,并运行以下命令:
shp2pgsql -s 4326 -I -G ne_50m_populated_places.shp postgis1.cities_staging > staging_cities.sql
它会在同一个文件夹中创建一个名为 staging_cities.sql 的文件。我们告诉我们想要创建的表的 SRID 是 4326 ( -s 4326 )。我们通过添加选项 -I 在 geocolumn 上创建一个空间索引。我们添加了 -G 选项,这意味着我们正在使用地理类型。您可以使用这个命令做更多的事情,但是我们决定在这里保持简单。下一步是运行创建的 SQL 文件,以生成postgis 1 . cities _ staging表。您可以使用内置的 psql 命令行实用程序来实现:
psql -h localhost -d postgis_training -U postgres -f staging_cities.sql
使用以下命令对ne _ 110m _ admin _ 0 _ countries . shp表执行相同的操作:
shp2pgsql -s 4326 -I -G ne_110m_admin_0_countries.shp postgis1.countries_staging > staging_countries.sqlpsql -h localhost -d postgis_training -U postgres -f staging_countries.sql
既然我们已经导入了 shapefiles,现在是时候填充我们在模型中创建的表了。
INSERT INTO postgis1.countries (
formal_name, country_iso3, population, gdp,
economy, income_group, continent, subregion,
region_world_bank, geog
)
SELECT
name_sort,
adm0_a3,
pop_est,
gdp_md_est,
economy,
income_grp,
continent,
subregion,
region_wb,
geog
FROM
postgis1.countries_staging;INSERT INTO postgis1.cities (
city_name, country_iso3_code, featurecla,
population, geog
)
SELECT
NAME,
SOV_a3,
FEATURECLA,
POP_OTHER,
geog
FROM
postgis1.cities_staging;
我们只是从临时表中选择相应的列来填充我们的主表。
我们已经讲述了如何对一个空间问题建模并使用真实世界的数据填充它。在本文的下一部分,我们将处理一些您可能会遇到的常见问题。
3.编写一个查询来解决问题
现在我们的模型和数据库已经准备好运行空间 SQL 查询来回答不同的问题。这里我们有一些问题需要回答,我们将逐一回答:
选择并创建所有欧洲国家的表格
作为破冰者,我们希望返回欧洲国家。这里我们不使用任何空间函数:
SELECT
* INTO postgis1.eu_countries
FROM
postgis1.countries
WHERE
continent = 'Europe'
选择所有居住在欧洲国家的城市
现在我们选择所有与欧洲国家相交的城市。因为我们需要这两个表,所以我们必须连接这两个表:
SELECT
postgis1.cities.* INTO postgis1.eu_cities
FROM
postgis1.cities
INNER JOIN postgis1.eu_countries ON
ST_Intersects(
postgis1.cities.geog, postgis1.eu_countries.geog
);
重新投影欧洲城市和国家表,并将其存储为单独的几何表
如果要将地理表重新投影到投影坐标参考系统中,可将地理列转换为几何列。那么 geog 列名可能会产生误导。首先,让我们将两个表中的 geog 列重命名为 geom :
ALTER TABLE
postgis1.eu_cities RENAME COLUMN geog TO geom;ALTER TABLE
postgis1.eu_countries RENAME COLUMN geog TO geom;
请注意,我们将列重命名为 geom,但是它们仍然包含地理数据。我们不会让这种情况继续下去,而是将它转换为世界墨卡托(SRID 3395),这是一个投影坐标参考系统:
ALTER TABLE
postgis1.eu_cities ALTER COLUMN geom TYPE geometry(POINT, 3395)
USING ST_Transform(geom::geometry, 3395);ALTER TABLE
postgis1.eu_countries ALTER COLUMN geom TYPE
geometry(MULTIPOLYGON, 3395) USING ST_Transform(geom::geometry,
3395);
现在,我们已经将欧洲表完全转换为世界墨卡托和几何数据类型。许多 PostGIS 函数不支持地理类型。此外,许多空间计算在投影的 CRS 上固有地正确执行。对于其余的操作,我们考虑这两个表。
将新创建的表格导出为 shape file
您可以使用另一个名为 pgsql2shp 的命令行实用程序将空间表导出到 shapefiles。在操作系统上可用的命令行工具中运行以下命令。确保在运行命令的同一个文件夹中创建一个名为 shp 的文件夹:
pgsql2shp -f ./shp/eu_cities_shp -h localhost -u postgres postgis_training postgis1.eu_citiespgsql2shp -f eu_countries_shp -h localhost -u postgres postgis_training postgis1.eu_countries
这些命令将在 shp 文件夹中创建城市和国家的形状文件。
pgsql2shp 命令的格式如下:
pgsql2shp [options] database [schema.]table
这些选项看起来很直观,但我对它们进行了详细说明,以使一切变得清晰:
**-f** use this option to specify the shapefile name and address you want to create**-h** use this option to specify the database host to connect to**-u** use this option to connect to the database as the specified user
您可能需要考虑一些其他选项,但是指定的选项对于我们想要做的事情来说已经足够了。
识别特定点所在的国家
考虑在大地坐标系(WGS84 经度/纬度 SRID 4326)中拥有坐标,其中经度= 32.6542,纬度= 50.9533。我们想知道这个点在哪个国家。我们要做的是堪比大多数 GIS 软件包中的识别工具。
请注意,我们的数据集不再使用 SRID 4326。我们需要动态地将该点转换为世界墨卡托(SRID 3395):
WITH identify AS(
SELECT
ST_Transform(
ST_SetSRID(
ST_Point(32.6542, 50.9533),
4326
),
3395
) AS ipoint
)SELECT
postgis1.eu_countries.*
FROM
postgis1.eu_countries,
identify
WHERE
ST_Within(ipoint, geom)
我们用 CTE 来定义和变换一个点。 ST_Within(geometry A,geometry B) 如果几何图形 A 完全在几何图形 B 内,则返回 TRUE。此函数将帮助我们找到要查找的国家。
查找一个国家的面积
PostGIS 有一个 ST_Area 函数,用于确定多边形的面积。它以 SRID 指定的单位返回面积。同样,我们需要即时改造 SRID:
WITH identify AS(
SELECT
ST_Transform(
ST_SetSRID(
ST_Point(32.6542, 50.9533),
4326
),
3395
) AS ipoint
)SELECT
ST_Area(geom)
FROM
postgis1.eu_countries,
identify
WHERE
ST_Within(ipoint, geom)
识别 CTE 部分只是为了查找国家,后面是一段代码来确定地区。如果我们运行这个代码,它返回 1394397442159.71,单位是平方米,相当于 1394.39 平方公里。如果你用谷歌搜索乌克兰的面积,你会得到一个完全不同的数字。乌克兰的面积为 603.63 平方公里。我们计算出的数字是实际数字的两倍多。这怎么能说得过去呢?
事实是,世界墨卡托(SRID 3395)并不保护该地区。从赤道向外,多边形的面积比实际数量增加得快得多。这就是为什么格陵兰在许多世界地图上看起来要大得多。为了获得精确的面积,我们需要使用等面积空间参考系统。为此,我选择了欧洲艾伯斯等面积圆锥曲线(SRID 102013)。它覆盖了整个欧洲,并希望保留多边形的面积。在我们的查询中,我们需要一个动态的乌克兰多边形转换:
WITH identify AS(
SELECT
ST_Transform(
ST_SetSRID(
ST_Point(32.6542, 50.9533),
4326
),
3395
) AS ipoint
)
SELECT
ST_Area(
ST_Transform(geom, 3035)
)
FROM
postgis1.eu_countries,
identify
WHERE
ST_Within(ipoint, geom)
结果是 601914411764.12 平方米相当于 601.91 平方公里。与国家官方面积(603.63 km2)相当,略有差异极有可能是地图上多边形不准确所致。
然而,还有第二种选择。使用 geography 类型时,面积和距离测量(尤其是在大面积上)保证是准确的。而且考虑了地球的曲率。为了利用地理类型的优势,让我们看看我们原始模型中的国家表,看看我们是否能解决问题。然而,地理类型并不支持所有的空间功能,包括 ST_Distance 。我们可以选择适合地理类型的 ST_Covers :
WITH identify AS(
SELECT
ST_SetSRID(
ST_Point(32.6542, 50.9533),
4326
) AS ipoint
)
SELECT
ST_Area(geog)
FROM
postgis1.countries,
identify
WHERE
ST_Covers(geog, ipoint)
找出柏林和伦敦之间的距离。
我们从之前的挑战中了解到,对于大面积的测量,地理数据类型可能是一个合适的选项:
SELECT
ST_Distance(a.geog, b.geog)
From
postgis1.cities a,
postgis1.cities b
WHERE
a.city_name = 'Berlin'
AND b.city_name = 'London'
因为你想在同一个表中找到两点之间的距离,你必须引用同一个表两次。 ST_Distance 将利用椭球模型,对于许多应用来说,可以认为足够精确。该查询将返回 933677.99m,这似乎是合理的。
4.查看结果
有时 PostGIS 没有 GIS 就叫 GIS。空间数据的可视化表示对于理解任何地理空间问题都至关重要。没有在地图上看到它,你将不能完全了解情况。然而,空间数据库并不是为可视化空间数据而设计的。但是,您可以找到一种方法来解决这个问题。一些 GIS 软件包可以直接连接到 PostGIS 以显示空间表和查询。我最喜欢的是 QGIS。

图 3-QGIS-PostGIS 连接设置(图片由作者提供)
点击并选择视图>面板>浏览器。如果默认情况下没有打开浏览器面板,它会为您打开。在浏览器面板中,可以找到 PostGIS 。右键点击并选择新建连接。在对话框中,像我一样填写选项(图 3)并测试连接。如果添加的连接成功,您可以点击 ok 按钮进行连接。
在浏览器面板的 PostGIS 分支下,会出现一个具有您所选择的名称的新连接(在我的例子中是: postgis_conn )。您可以在 postgis1 数据库中找到您的模式和表(图 4)。如果您双击任何表格,它将显示在地图画布上,并添加到图层面板。图层可以像 shapefiles 一样打开、关闭、导出和使用。

图 PostGIS 和 QGIS 之间的连接(图片由作者提供)
如果我们想在地图上显示查询的结果呢?为此,从主菜单中选择数据库>数据库管理器… 。在数据库管理器中,选择数据库> SQL 窗口。

图 5——如何将查询结果显示为图层(作者图片)
根据图 5,我们编写了一个简单的查询来返回亚洲国家,然后我点击 execute 。然后我勾选了加载为图层复选框,填写了必要的字段,点击加载。令人惊讶的是,如果你再次检查图层面板,你可以在图层面板上找到一个具有你选择的名称的新图层(这里,图层名称是: Asian_Query )。

图 6 —查询结果显示亚洲国家(图片由作者提供)
借助简单的例子,我们考察了使用 PostGIS 解决空间问题的整个工作流程。PostGIS 可以做更多的事情,但对于许多空间挑战,工作流程是相同的。
PostgreSQL:帮助您启动和运行的基础知识。[第一部分] (macOS)

来自 Pexels 的 panumas nikhomkhai 摄影
PostgreSQL 是一个开源的关系数据库管理系统(RDMS ),它之所以受欢迎有很多原因:它是免费的,它是安全的,它支持定制函数,它是对象关系模型架构,并且每个表没有行数限制。查看这篇文章以获得更深入的分析。PostgreSQL 也被很多大公司使用,包括: NASA 、 Twitch 、 Apple 、 Reddit 。在本文中,我们将触及 PostgreSQL 的基础知识,以便您可以快速入门并运行。
下载 PostgreSQL 和 pgAdmin4。
在 Mac 上,由于有了postgres . app安装包,下载 postgres 的过程得以简化。只需下载所需的版本,并将应用程序保存到应用程序文件夹。

下载后,您应该能够双击蓝色大象图标,并看到类似于此的内容在您的屏幕上弹出。注意:为您显示的数据库会有所不同,但将包括postgres和template1数据库。

template1是一个模板数据库。默认情况下,每当您运行CREATE DATABASE SQL 查询时,postgres 使用template1数据库来创建新数据库。请参见本教程中关于改变template1的演练。
现在,您应该下载 PgAdmin4 ,这是一个桌面 GUI 界面,通常用于与 postgres 数据库进行交互。请记住,当您第一次安装 postgres 时,它附带了psql。您应该能够在终端中键入 psql,并通过 CLI 控制数据库,就像使用 pgAdmin 的 GUI 控制数据库一样。

pgAdmin4 GUI 界面。

psql 命令行界面。
把 psql 和 PgAdmin 的区别想象成类似于从命令行使用 github 和 github 桌面 GUI 的区别。
表创建。
为了简单起见,我们将关注如何使用 pgAdmin GUI 执行基本操作——但是请记住,这都可以通过命令行使用 psql 来完成。本文中使用的数据是由交通部编制的国家地址数据库(NAD) 的子集。您可以下载。本文使用的 csv 使用这个链接。
让我们创建一个名为blog的数据库:

右键单击“数据库”

将数据库命名为“blog ”,然后保存。
现在,我们将在左侧浏览器窗口中看到blog数据库。当我们点击blog数据库时,它会变成黄色,表明我们已经连接到数据库,并展开以便我们可以看到诸如触发器、扩展和模式之类的东西。

点击前

点击后
现在让我们单击 Schemas 菜单,显示 tables 选项卡。在本教程中,我们将从 csv 中获取数据,并将其加载到 postgres 中。

右键单击表格
右键单击Tables,然后创建一个名为“邮箱”的新表。


现在我们需要声明数据库中的列。这是一个重要的步骤,邮箱表的列需要与。我们计划导入的 csv 文件。这包括一个索引列,如果您的。csv 有一个。在我们的例子中,我没有索引列。
addresses.csv 文件中的列如下:
- State
- Post_Comm
- StreetName
- StN_PosDir (North,South,East,West)
- Add_Number (address number)
- StN_PosTyp (Street/Drive/Lane/Court/Place/Road etc.)
让我们单击 Columns 选项卡,将上面提到的列添加到表中。添加列还要求我们声明每个列的数据类型。有时,在导入文件时将所有列设置为文本,然后在导入后处理数据类型的更改是有意义的。在我们的例子中,我们可以将所有数据保存为文本。

现在,我们应该可以在“邮箱”表中看到所有的列。

将 CSV 数据导入 PostgreSQL。
为了将来自 addresses.csv 的数据放入邮箱表中,我们需要对邮箱表使用 pgAdmin 查询工具。

现在使用下面的 SQL 查询将 CSV 加载到 postgres 中。
COPY mailboxes
FROM '/Users/brendanferris/Desktop/scripts/vehicle_analysis/addresses.csv'
DELIMITER ','
CSV HEADER;
不要忘记,邮箱是我们要将 CSV 信息发送到的表的名称,邮箱表中的列顺序与 addresses.csv 文件中的列顺序相匹配。CSV HEADER表示 addresses.csv 文件的第一行是列名,应该跳过,DELIMITER是',',因为 CSV 是逗号分隔的文件。
您可以在查询工具中使用 F5 来运行查询,运行上述查询后,您应该会看到以下输出:

为了确保我们的所有信息现在都在 SQL 数据库中,使用以下查询获取并返回所有行:
SELECT * FROM mailboxes;
然后按 F5 运行查询。您应该在 pgAdmin 的数据输出窗口中看到所有的表信息。

将查询结果导出为 CSV 格式。
假设我们对我们的地址信息运行 SQL 查询,并希望将该信息输出为 CSV 文件。首先,让我们编写一个查询,使地址更易读。
SELECT CONCAT(addressnumber, ' ', streetdirection, ' ', streetname, ' ', stn_postyp, ' ', comm, ' ', state) as formatted_address FROM mailboxes

哎呀, streetdirection 列中的NULL条目似乎是字符串,而不是实际的 postgres 空值。因此,我们应该用空字符串替换所有的NULL字符串。这样,当我们编译地址时,我们将只在需要时添加街道方向。要用空字符串替换 streetdirection 列中的所有NULL字符串,请运行以下查询。
UPDATE mailboxes
SET streetdirection = REPLACE(streetdirection,'NULL','');
现在,NULL被替换为一个空字符串,我们可以使用之前的CONCAT查询正确格式化街道名称:


为了将这些文件保存到它们自己的 CSV 文件中,我们需要将CONCAT查询放在COPY关键字周围,指定输出文件的目的地和名称,以及分隔符。格式如下:
**COPY** ([Query]) **TO** '[File Path]' **DELIMITER** ',' CSV HEADER;
这有点混乱,但是如果您将该查询复制并粘贴到查询编辑器中,应该会更容易阅读:
COPY (SELECT CONCAT(addressnumber, ' ', streetdirection, ' ', streetname, ' ', stn_postyp, ' ', comm, ' ', state) as formatted_address FROM mailboxes) TO '/Users/brendanferris/Desktop/scripts/vehicle_analysis/output.csv' DELIMITER ',' CSV HEADER;
现在,所需的数据将位于您指定的文件路径下的 CSV 文件中!
下一步。
在以后的文章中,我将处理一些其他操作,比如更新表、删除表以及在 python 脚本中使用表。有许多关于 PostgreSQL 的文档和支持,我鼓励您去探索!
敬请期待!
💻请随时查看我的网站。
PostgreSQL:启动和运行的基础知识[第 2 部分] (macOS)

照片由来自 Pexels 的阿扎马特·埃森纳列夫拍摄
在本系列的第 1 部分中,我介绍了 PostgreSQL 是什么,它是如何工作的,如何开始添加数据库和表来创建存储信息的关系数据库。在本文中,我将假设您已经下载了所有内容,并且有了可以使用的数据库/表格。我将经历一些我过去经常在谷歌上搜索的手术。如果你觉得这个信息有用,它将是你书签的一个很好的补充。我们开始吧!
如何导入 CSV?
假设您有一个 CSV 文件,希望使用 pgAdmin 将其导入 postgres 数据库。您需要做的第一件事是在 pgAdmin 中创建一个表,按照您想要的名称命名它,然后根据 CSV 顺序声明适当数量的列。例如,我将导入一个包含两列的 CSV:名称、编号。name 列将是一个字符串,第二列将是一个随机数。

在 pgAdmin 中,让我们创建一个名为 locations 的新表,如本系列的第 1 部分所述。

请记住,我们需要声明这些列,并且这些列需要按照它们在 CSV 中出现的顺序进行声明。

当我们点击 save 时,我们将得到一个名为(namedb)的表,它有两列。要将数据从 CSV 获取到 namedb 表中,右键单击 pgAdmin 中的表。



导入完成后,会弹出一个对话框。
也可以通过右键单击 pgAdmin 中的表,选择查询工具,并使用以下 SQL 语句来导入数据:
COPY namedb(name, locations)
FROM '/Users/brendanferris/Desktop/scripts/postgres_blog/people.csv'
DELIMITER ','
CSV HEADER;

现在我们的数据在数据库里。
向现有表中添加 ID 列。
现在我们在表中有了名称和位置,我们想添加一个 id 列,可能用作键键,使我们能够将信息与其他表中的其他信息连接起来。为此,让我们向 namedb 表添加一个 name_id 列。
ALTER TABLE namedb ADD COLUMN name_id serial PRIMARY KEY;


将查询结果保存到 CSV 文件中。
假设我们想获得数据库中姓名以“A”开头的所有人,然后将结果保存在它自己的 CSV 文件中。我们可以通过首先获取我们想要的信息来实现这一点,在本例中:
SELECT name, locations FROM namedb
WHERE name ILIKE('a%');
结果信息应该显示在数据输出窗口中。

为了将这两行输出到 CSV 中,我们需要使用以下查询格式:
**COPY** ([Query]) **TO** '[File Name]' **DELIMITER** ',' CSV HEADER;
我们只需要在 COPY 关键字后面的括号中输入我们的查询,并以指定的格式将文件发送到特定的位置。我们指定的格式是 CSV,因为我们将分隔符声明为逗号。

无论您指定哪个路径,都可以找到该文件。

这些是我发现自己在过去执行的一些更常见的操作,我希望这个小纲要在将来能为您节省一些时间。
编码快乐!
💻请随意查看我的网站。
PostgreSQL —如何安全、轻松、快速地进行升级
防止重复,插入新记录,更新现有记录

我们将安全地连接数据库中的数据集,而不是连接道路(图片由 Unsplash 上的 Sigmund 提供)
当您将数据向上插入到表中时,您会更新或忽略已经存在的记录并插入新记录。阅读完本文后,您将能够在 Postgres 中执行一个允许您这样做的查询。我们将通过一个实际的例子来演示如何做到这一点。我们来编码吧!
1.设置和准备
假设我们有一个跟踪最新加密货币价格的应用程序。每 10 秒钟,我们就会收到一个包含最新价格和交易量的数据集。我们希望在数据库的一个表中记录这些价格变化,以便我们可以在网站上显示最新的信息。
我们想更新价格和数量的硬币已经在数据库中,并添加新的硬币。我们可以用一句话来处理这些问题。
数据库准备
让我们首先设置一些表并插入一些虚拟数据,这样我们就有东西可以使用了。
执行这个查询将得到下面的 coinprices 表。

我们的硬币价格表(图片由作者提供)
如你所见,我们记录了五枚硬币。管理过去 24 小时的价格(美元)和交易量。
目前,如果我们收到一个新的比特币记录,并将其插入到我们的表中,我们就会遇到麻烦:然后我们有两个记录显示一个比特币的价格。我们不能有这种情况,所以我们将在表上添加一个唯一约束:
这确保了我们不能插入多个具有相同符号和货币的记录。我们稍后会用到这个独特的约束。

我们合并吧!(图片由娜塔莉亚 Y 在 Unsplash 上拍摄)
2.向上插入
我们准备好接收新数据了。让我们插上!
我们收到了比特币新的价格和交易量,以及全新的硬币。让我们看看当我们试图将它插入到我们的表中时会发生什么。
执行上述查询会导致:

比特币已经存在于我们的表中(图片由作者提供)
插入操作遇到了冲突。我们可以检测是否遇到冲突,然后决定如何处理冲突的记录。
在单个语句中向上插入
使用下面的查询,我们可以一次插入新硬币并更新现有的硬币!
INSERT INTO med.coinprices (symbol, currency, price_usd, volume_24h) VALUES
('BTC', 'Bitcoin', 60000, 40000000000)
, ('XDG', 'Dogecoin', 0.2181, 1983534547)
ON CONFLICT (symbol, currency)
DO UPDATE SET
price_usd = EXCLUDED.price_usd
, volume_24h = EXCLUDED.volume_24h;
让我们一步一步地完成这个查询。
-第一部分很简单,只是一个普通的两行插页。
-从第 4 行开始,我们决定如何处理冲突的记录。在这种情况下,发生冲突是因为我们违反了表上的唯一约束。
这正是我们想要的!如果我们的表中已经有了符号和货币列的记录,那么我们想要更新它们。在查询中,EXCLUDED 包含最初建议插入的所有记录。这是我们的结果:

我们新的 coinprices 表,最后两行是向上插入的值(图片由作者提供)
总之:我们执行一个常规的插入,但是捕捉到冲突的记录并指定如何处理它们。
升级选项
在我们的实际例子之外的其他情况下,您可能不希望对冲突的记录做任何事情。我们可以简单地调整语句,只插入不冲突的记录,忽略冲突的记录:
上面的查询将只插入 Dogecoin,并忽略比特币的更新:

当指定什么都不做时,比特币保持不变(图片由作者提供)
安全的
执行此查询将插入并更新所有建议的记录,或者不插入并更新任何记录。它们发生在单个交易中。更多交易信息在 本文 。

我们的数据被安全地合并并准备使用(图片由托马斯·汤普森在 Unsplash 上拍摄)
结论
通过这篇文章,我希望对如何在 Postgres 中安全、简单、快速地插入数据有所启发。如果你有建议/澄清,请评论,以便我可以改进这篇文章。同时,看看我的其他关于各种编程相关主题的文章,比如:
编码快乐!
—迈克
页(page 的缩写)学生:比如我正在做的事情?跟我来!
PostgreSQL 与 Python 的数据清理:指南
使用 PostgreSQL 和 TimescaleDB 替换 Python 中数据清理任务的方法

介绍
在分析过程中,您很少(如果有的话)直接从评估数据到转换和分析数据。有时候,为了正确地评估您的数据,您可能需要在进行主要的数据清理之前做一些预清理,这需要进行大量的清理工作!为了完成所有这些工作,您可以使用 Excel、R 或 Python,但是这些是数据清理任务的最佳工具吗?
在这篇博客文章中,我探索了一些经典的数据清理场景,并展示了如何使用 TimescaleDB 和 PostgreSQL 在数据库中直接执行这些场景,取代您可能已经在 Excel、R 或 Python 中完成的任务。TimescaleDB 和 PostgreSQL 不能完全取代这些工具,但它们可以帮助您更有效地管理/清理数据,进而让 Excel、R 和 Python 在可视化、建模和机器学习方面大放异彩。
清洁是分析过程中非常重要的一部分,从我的经验来看,通常是最累人的!通过直接在我的数据库中清理数据,我能够一次执行许多清理任务,而不是在一个脚本中重复执行,从长远来看,这为我节省了大量时间。
数据分析过程概述
我在开始这个关于数据分析的系列文章时,展示了以下分析过程的摘要:

数据分析生命周期。作者图片
分析生命周期的前三个步骤(评估、清理、转换)包括分析的“数据管理”阶段。从历史上看,我都是在 Python 或 R 中进行数据管理和建模的,这些都是分析的绝佳选择。然而,一旦我接触了 PostgreSQL 和 TimescaleDB,我发现直接在我的数据库中进行数据管理是多么的高效和快速。在我之前的帖子中,我重点展示了数据评估技术,以及如何用 PostgreSQL 和 TimescaleDB 代码替换之前在 Python 中完成的任务。我现在想进入第二步,数据清理。清理可能不是分析过程中最迷人的步骤,但对于创建准确和有意义的模型来说,它绝对是至关重要的。
正如我在上一篇帖子中提到的,我大学毕业后的第一份工作是在一家能源和可持续发展解决方案公司,该公司专注于监控各种不同的公用事业用途——比如电、水、污水,你能想到的——以找出我们客户的建筑如何才能更高效。我在这家公司的角色是执行数据分析和商业智能任务。
在我整个工作期间,我有机会使用许多流行的数据分析工具,包括 Excel、R 和 Python。但是当我尝试使用数据库来执行数据管理任务时——特别是 PostgreSQL 和 TimescaleDB——我意识到直接在数据库中执行分析,尤其是清理任务是多么高效和简单。
在使用数据库进行数据清理任务之前,我经常会找到需要编辑的列或值。我会从 CSV 文件或数据库中提取原始数据,然后在我的 Python 脚本中对这些数据进行任何调整。这意味着每次我运行 Python 脚本时,我都必须等待我的机器花费计算时间来设置和清理我的数据。这意味着每次运行脚本都会浪费时间。此外,如果我想与同事共享清理后的数据,我必须运行该脚本或将其传递给他们来运行。这种额外的计算时间会随着项目的不同而增加。
相反,使用 PostgreSQL,我可以编写一个查询来进行一次清理,然后将结果存储在一个表中。我不需要花时间用 Python 脚本一次又一次地清理和转换数据,我只需要在我的数据库中设置清理过程,然后就可以收工了!一旦我开始直接在我的数据库中进行清理更改,我就能够跳过在 Python 中执行清理任务,而简单地专注于直接对我的数据建模。
为了让这篇文章尽可能简洁,我选择只展示 Python 和 PostgreSQL 的并行代码比较。如果您对其他工具或语言有任何疑问,请随时加入 TimescaleDB 的 Slack channel ,在这里您可以向时标社区或我询问有关时标或 PostgreSQL 功能的具体问题😊。我很想收到你的来信!
现在,在我们深入研究并获取数据之前,正如 Outkast best 所说,“如此新鲜,如此干净”,我想快速介绍一下我将使用的数据集。此外,我还要注意,我展示的所有代码都将假设您对 SQL 有一些基本的了解。如果你不熟悉 SQL,不用担心!在我的上一篇文章中,我包括了一个关于 SQL 基础的部分,你可以在这里找到。
关于样本数据集
根据我在数据科学领域的经验,我在评估后完成了大部分数据清理工作。但是,有时清理数据、评估,然后再次清理是有益的。您选择的过程取决于数据的初始状态以及评估的难易程度。对于我今天将使用的数据集,我可能会在评估前做一些初步清理,然后在评估后再次清理,我会告诉你为什么。
我从 Kaggle 获得了以下物联网数据集,其中一个非常慷慨的人分享了他们在加州圣何塞公寓的能耗读数,该数据每 15 分钟增加一次。虽然这是很棒的数据,但它的结构与我希望的略有不同。原始数据集遵循以下模式:

能源使用分期表。作者图片
看起来像这样…

能源使用表的快照。作者图片
为了对这个数据集进行任何类型的分析,我想清理它。我很快想到的几件事包括:
- 成本被视为文本数据类型,这将导致一些问题。
- 时间列是分开的,如果我想创建随时间变化的图或基于时间执行任何类型的建模,这可能会导致一些问题。
- 我可能还想根据与时间有关的各种参数来过滤数据,例如星期几或假日标识(这两者都可能影响家庭中的能源使用方式)。
为了修复所有这些东西,并获得更有价值的数据评估和分析,我将不得不清理传入的数据!所以,事不宜迟,让我们卷起袖子开始行动吧!
清理数据
我将展示我在过去从事数据科学工作时使用的大多数技术。虽然这些示例并不详尽,但我希望它们将涵盖您在自己的分析过程中执行的许多清理步骤,有助于通过使用 PostgreSQL 和 TimescaleDB 来提高清理任务的效率。
请随意探索这些不同的技术,如果你需要的话可以跳过!这里有很多,我把它设计成一个有用的工具词汇表,你可以根据需要使用它。
我将介绍的技术包括:
- 纠正结构性问题
- 创建或生成相关数据
- 向超表添加数据
- 重命名列或表
- 填写缺失的值
清洁方法注意事项:
在 PostgreSQL 中,有许多方法可以处理清理过程。我可以创建一个表,然后在清理的时候[ALTER](https://www.postgresql.org/docs/current/sql-altertable.html)它,我可以创建多个表来添加或更改数据,或者我可以使用[VIEW](https://www.postgresql.org/docs/14/sql-createview.html) s 。根据我的数据的大小,这些方法中的任何一种都可能有意义,然而,它们会有不同的计算结果。
你可能已经注意到上面我的原始数据表叫做energy_usage_staging。这是因为我决定给定我的原始数据的状态,对我来说最好将原始数据放在一个暂存表中,使用VIEW s 清理它,然后作为清理过程的一部分将它插入一个更可用的表中。从原始表到可用表的这种移动甚至可以在分析的评估步骤之前发生。正如我上面所讨论的,有时数据清理必须在评估数据之前和之后进行。无论如何,这些数据需要清理,我想使用最有效的方法。在这种情况下,这意味着使用一个分段表,并利用 PostgreSQL VIEW的效率和功能,这一点我稍后会谈到。
一般来说,如果您正在处理大量数据,在 PostgreSQL 中修改一个现有表的成本会很高。在这篇文章中,我将向您展示如何使用VIEW和其他表来构建干净的数据。这种清理方法效率更高,并且为下一篇关于数据转换的博文做好了准备,其中包括 PostgreSQL 中脚本的使用。
纠正结构性问题
马上,我知道由于数据类型的原因,我需要对我的原始表进行一些数据重构。注意,我们将date和时间列分开,并且costs被记录为文本数据类型。我需要将分离的日期时间列转换成时间戳,将cost列转换成 float4。但是在我展示之前,我想谈谈为什么转换为时间戳是有益的。
TimescaleDB 超表以及时间戳为何重要:
对于那些不熟悉 TimescaleDB hypertables 结构的人来说,它们是我们高效查询和操作时序数据的基础。时间刻度超表是根据时间划分的,更具体地说,是根据创建表时指定的时间列划分的。
数据按时间戳划分成“块”,这样表中的每一行都属于某个基于时间范围的块。然后,我们使用这些时间块来帮助查询行,以便您可以获得基于时间的更有效的查询和数据操作。这张图片展示了普通表格和我们的特殊超表格之间的区别。

Hypertables 图像描述。作者图片
更改日期时间结构:
因为我想充分利用 TimescaleDB 的功能,比如连续聚合和更快的基于时间的查询,所以我想重组energy_usage_staging表的date和 time 列。我可以将date列用于我的超表分区,但是,我对基于时间操作数据的控制是有限的。具有时间戳的单个列比具有日期和时间的单独列更灵活,空间效率更高。如果以后需要,我可以从时间戳中提取日期或时间!
回头看看表的结构,我应该能够从date和start_time列中获得一个可用的时间戳值,因为end_time并没有给我多少有用的信息。因此,我想将这两个列组合成一个新的时间戳列,让我们看看如何使用 SQL 来实现这一点。剧透一下,就像代数语句一样简单。多酷啊。!
PostgreSQL 代码:
在 PostgreSQL 中,我可以创建列,而无需将它插入数据库。因为我想从这个临时表创建一个新表,所以现在还不想添加更多的列或表。
让我们首先将原始列与新生成的列进行比较。对于这个查询,我简单地将两列相加。AS关键字只允许我将列重命名为我想要的名称,在本例中是time。
--add the date column to the start_time column
SELECT date, start_time, (date + start_time) AS time
FROM energy_usage_staging eus;
结果:

作者图片
Python 代码:
在 Python 中,最简单的方法是向 dataframe 添加一个新列。请注意,在 Python 中,我必须用一个定义的空格将这两列连接起来,然后将该列转换为 datetime。
energy_stage_df['time'] = pd.to_datetime(energy_stage_df['date'] + ' ' + energy_stage_df['start_time'])
print(energy_stage_df[['date', 'start_time', 'time']])
更改列数据类型:
接下来,我想将我的成本列的数据类型从 text 改为 float。同样,这在带有[TO_NUMBER()](https://www.postgresql.org/docs/14/functions-formatting.html)函数的 PostgreSQL 中很简单。
函数的格式如下:TO_NUMBER(‘text’, ‘format’)。“格式”输入是 PostgreSQL 特定的字符串,您可以根据要转换的文本类型来构建它。在我们的例子中,我们有一个$符号,后跟一个数字设置0.00。对于格式字符串,我决定使用“L99D99”。L 让 PostgreSQL 知道在文本的开头有一个货币符号,9 让系统知道我有数值,然后 D 代表小数点。
我决定对小于或等于“$99.99”的值进行转换,因为成本列没有大于 0.65 的值。如果您计划转换一个具有较大数值的列,您可能需要添加一个 G 来表示逗号。例如,假设您有一个文本值为“1,672,278.23”的成本列,那么您会希望将字符串的格式设置为“L9G999G999D99”
PostgreSQL 代码:
--create a new column called cost_new with the to_number() function
SELECT cost, TO_NUMBER("cost", 'L9G999D99') AS cost_new
FROM energy_usage_staging eus
ORDER BY cost_new DESC
结果:

作者图片
Python 代码:
对于 Python,我使用了一个 lambda 函数,它系统地用空字符串替换所有的' $ '符号。这可能效率相当低。
energy_stage_df['cost_new'] = pd.to_numeric(energy_stage_df.cost.apply(lambda x: x.replace('$','')))
print(energy_stage_df[['cost', 'cost_new']])
创建一个 **VIEW** :
现在我知道了如何转换我的列,我可以组合这两个查询并为我的新的重构表创建一个VIEW。一个[VIEW](https://www.postgresql.org/docs/14/sql-createview.html)是一个 PostgreSQL 对象,它允许你定义一个查询并通过它的名字VIEW调用它,就好像它是你的数据库中的一个表。我可以使用下面的查询生成我想要的数据,然后创建一个VIEW,我可以像查询一个表一样查询它。
PostgreSQL 代码:
-- query the right data that I want
SELECT type,
(date + start_time) AS time,
"usage",
units,
TO_NUMBER("cost", 'L9G999D99') AS cost,
notes
FROM energy_usage_staging
结果:

作者图片
我决定叫我的VIEW energy_view。现在,当我想做进一步的清理时,我可以在FROM语句中指定它的名称。
--create view from the query above
CREATE VIEW energy_view AS
SELECT type,
(date + start_time) AS time,
"usage",
units,
TO_NUMBER("cost", 'L9G999D99') AS cost,
notes
FROM energy_usage_staging
Python 代码:
energy_df = energy_stage_df[['type','time','usage','units','cost_new','notes']]
energy_df.rename(columns={'cost_new':'cost'}, inplace = True)
print(energy_df.head(20))
需要注意的是,使用 PostgreSQL VIEW s,每次查询时都必须重新计算其中的数据。这就是为什么我们希望在数据设置正确后将数据插入到一个超表中。你可以把VIEW s 看作是我在上一篇帖子中讨论的 CTEs [WITH](https://blog.timescale.com/blog/how-to-evaluate-your-data-directly-within-the-database-and-make-your-analysis-more-efficient/?utm_source=tds&utm_medium=blog&utm_id=tsdb-for-data-analysis&utm_content=timescale-tutorials/#cte/) https://blog.timescale.com/blog/how-to-evaluate-your-data-directly-within-the-database-and-make-your-analysis-more-efficient/?utm_source=tds&utm_medium=blog&utm_id=tsdb-for-data-analysis&utm_content=timescale-tutorials/#cte/[AS](https://blog.timescale.com/blog/how-to-evaluate-your-data-directly-within-the-database-and-make-your-analysis-more-efficient/?utm_source=tds&utm_medium=blog&utm_id=tsdb-for-data-analysis&utm_content=timescale-tutorials/#cte/)语句的速记版本。
我们现在离更干净的数据更近了一步!
创建或生成相关数据
通过快速调查,我们可以看到这个数据集的 notes 列是空白的。为了检查这一点,我只需要包含一个WHERE子句,并指定notes不等于空字符串。
PostgreSQL 代码:
SELECT *
FROM energy_view ew
-- where notes are not equal to an empty string
WHERE notes!='';
结果出来是空的
Python 代码:
print(energy_df[energy_df['notes'].notnull()])
因为注释是空白的,所以我想用各种附加信息来替换该列,以便在建模过程中使用。我想特别添加的一件事是一个指定星期几的列。为此,我可以使用EXTRACT()命令。[EXTRACT()](https://www.postgresql.org/docs/14/functions-datetime.html)命令是一个 PostgreSQL 日期/时间函数,允许您提取各种日期/时间元素。对于我们的专栏,PostgreSQL 具有规格 DOW(星期几),它将 0 映射到星期天,将 6 映射到星期六。
PostgreSQL 代码:
--extract day-of-week from date column and cast the output to an int
SELECT *,
EXTRACT(DOW FROM time)::int AS day_of_week
FROM energy_view ew
结果:

作者图片
Python 代码:
energy_df['day_of_week'] = energy_df['time'].dt.dayofweek
此外,我们可能希望添加另一列来指定某一天是周末还是工作日。我将通过创建一个布尔列来实现这一点,其中true表示周末,false表示工作日。为此,我将应用一个[CASE](https://www.postgresql.org/docs/14/plpgsql-control-structures.html)语句。使用该命令,我可以指定“when-then”语句(类似于编码中的“if-then”语句),其中我可以说WHEN一个day_of_week值是INset(0,6) THEN输出应该是true,ELSE值应该是false。
PostgreSQL 代码:
SELECT type, time, usage, units, cost,
EXTRACT(DOW FROM time)::int AS day_of_week,
--use the case statement to make a column true when records fall on a weekend aka 0 and 6
CASE WHEN (EXTRACT(DOW FROM time)::int) IN (0,6) then true
ELSE false
END AS is_weekend
FROM energy_view ew
结果:

作者图片
有趣的事实:您可以在没有CASE语句的情况下执行相同的查询,但是它只适用于二进制列。
--another method to create a binary column
SELECT type, time, usage, units, cost,
EXTRACT(DOW FROM time)::int AS day_of_week,
EXTRACT(DOW FROM time)::int IN (0,6) AS is_weekend
FROM energy_view ew
Python 代码:
请注意,在 Python 中,周末由数字 5 和 6 表示,而 PostgreSQL 周末值为 0 和 6。
energy_df['is_weekend'] = np.where(energy_df['day_of_week'].isin([5,6]), 1, 0)
print(energy_df.head(20))
也许事情会变得很疯狂,也许你想添加更多的参数!
让我们考虑假期。现在你可能会问“我们到底为什么要这么做?!",但在美国,人们通常在一些节假日会休息。因为这个人住在美国,他们可能至少有一些假期,无论是当天还是联邦假日。在有休息日的地方,能源使用会有所不同。为了帮助指导我的分析,我想包括假日的识别。为此,我将创建另一个布尔列来标识联邦假日的时间。
为此,我将使用 TimescaleDB 的time_bucket()函数。[time_bucket()](https://docs.timescale.com/api/latest/hyperfunctions/time_bucket/?utm_source=tds&utm_medium=blog&utm_id=tsdb-for-data-analysis&utm_content=time-bucket-docs)函数是我在之前的文章中详细讨论过的函数之一。本质上,我需要使用这个函数来确保一天内的所有时间值都被计算在内。如果不使用time_bucket()函数,我只能看到与 12am 时间段相关的行的变化。
PostgreSQL 代码:
创建假日表后,我可以在查询中使用其中的数据。我还决定对这个查询使用非 case 语法。注意,两者都可以用!
--create table for the holidays
CREATE TABLE holidays (
date date)--insert the holidays into table
INSERT INTO holidays
VALUES ('2016-11-11'),
('2016-11-24'),
('2016-12-24'),
('2016-12-25'),
('2016-12-26'),
('2017-01-01'),
('2017-01-02'),
('2017-01-16'),
('2017-02-20'),
('2017-05-29'),
('2017-07-04'),
('2017-09-04'),
('2017-10-9'),
('2017-11-10'),
('2017-11-23'),
('2017-11-24'),
('2017-12-24'),
('2017-12-25'),
('2018-01-01'),
('2018-01-15'),
('2018-02-19'),
('2018-05-28'),
('2018-07-4'),
('2018-09-03'),
('2018-10-8')SELECT type, time, usage, units, cost,
EXTRACT(DOW FROM time)::int AS day_of_week,
EXTRACT(DOW FROM time)::int IN (0,6) AS is_weekend,
-- I can then select the data from the holidays table directly within my IN statement
time_bucket('1 day', time) IN (SELECT date FROM holidays) AS is_holiday
FROM energy_view ew
结果:

作者图片
Python 代码:
holidays = ['2016-11-11', '2016-11-24', '2016-12-24', '2016-12-25', '2016-12-26', '2017-01-01', '2017-01-02', '2017-01-16', '2017-02-20', '2017-05-29', '2017-07-04', '2017-09-04', '2017-10-9', '2017-11-10', '2017-11-23', '2017-11-24', '2017-12-24', '2017-12-25', '2018-01-01', '2018-01-15', '2018-02-19', '2018-05-28', '2018-07-4', '2018-09-03', '2018-10-8']
energy_df['is_holiday'] = np.where(energy_df['day_of_week'].isin(holidays), 1, 0)
print(energy_df.head(20))
此时,我将把这个扩展的表保存到另一个VIEW中,这样我就可以调用数据而不用写出查询。
PostgreSQL 代码:
--create another view with the data from our first round of cleaning
CREATE VIEW energy_view_exp AS
SELECT type, time, usage, units, cost,
EXTRACT(DOW FROM time)::int AS day_of_week,
EXTRACT(DOW FROM time)::int IN (0,6) AS is_weekend,
time_bucket('1 day', time) IN (select date from holidays) AS is_holiday
FROM energy_view ew
您可能会问,“为什么将这些列创建为布尔列??",一个很公平的问题!你看,我可能想在分析过程中使用这些列进行过滤,这是我在自己的分析过程中经常做的事情。在 PostgreSQL 中,当你使用布尔列时,你可以非常容易地过滤东西。例如,假设我想使用到目前为止的表查询,并且只显示发生在周末AND假期的数据。我可以简单地通过添加一个WHERE语句和指定的列来实现。
PostgreSQL 代码:
--if you use binary columns, then you can filter with a simple WHERE statement
SELECT *
FROM energy_view_exp
WHERE is_weekend AND is_holiday
结果:

作者图片
Python 代码:
print(energy_df[(energy_df['is_weekend']==1) & (energy_df['is_holiday']==1)].head(10))
向超表添加数据
现在我已经准备好了新的列,并且我知道我希望我的表是如何结构化的,我可以创建一个新的超表并插入我清理的数据。在我自己对这个数据集的分析中,我可能已经在评估我的数据之前做了清理,这样我就可以在分析中获得更有意义的评估步骤。最棒的是,您可以使用这些技术中的任何一种进行常规清洁,无论是在评估之前还是之后。
PostgreSQL:
CREATE TABLE energy_usage (
type text,
time timestamptz,
usage float4,
units text,
cost float4,
day_of_week int,
is_weekend bool,
is_holiday bool,
)--command to create a hypertable
SELECT create_hypertable('energy_usage', 'time')INSERT INTO energy_usage
SELECT *
FROM energy_view_exp
请注意,如果您有持续不断的数据输入,您可以在数据库中创建一个脚本,在导入数据时自动进行这些更改。这样,您可以在数据库中准备好已清理的数据,而不是每次要执行分析时都在脚本中处理和清理数据。
我们将在我的下一篇文章中详细讨论这一点,所以如果你想知道如何创建脚本并保持数据自动更新,请确保继续关注!
重命名值
清理数据的另一项有价值的技术是能够重命名各种项目或重新映射分类值。StackOverflow 上这个 Python 数据分析问题的受欢迎程度放大了这项技能的重要性。问题是“我如何改变熊猫数据帧中的单个索引值?”。由于 PostgreSQL 和 TimescaleDB 使用关系表结构,重命名唯一值可能相当简单。
在重命名表中的特定索引值时,可以通过在SELECT查询中使用 PostgreSQL 的CASE语句来“动态”完成。比方说,我不喜欢在day_of_week列中用 0 表示星期天,但我更希望它是 7。我可以用下面的查询做到这一点。
PostgreSQL 代码:
SELECT type, time, usage, cost, is_weekend,
-- you can use case to recode column values
CASE WHEN day_of_week = 0 THEN 7
ELSE day_of_week
END
FROM energy_usage
Python 代码:
注意,这段代码会使 Monday = 7,因为 python DOW 函数将 Monday 设置为 0,Sunday 设置为 6。但是这就是在一个列中更新一个值的方法。很可能你不想做这个动作,我只是想展示 python 的等价物作为参考。
energy_df.day_of_week[energy_df['day_of_week']==0] = 7
print(energy_df.head(250))
现在,假设我想实际使用一周中各天的名称,而不是显示数值?对于这个例子,我实际上想放弃CASE语句,创建一个映射表。当您需要更改各种值时,创建一个映射表,然后使用[JOIN](https://www.postgresql.org/docs/14/queries-table-expressions.html)命令连接到这个表可能会更有效。
PostgreSQL 代码:
--first I need to create the table
CREATE TABLE day_of_week_mapping (
day_of_week_int int,
day_of_week_name text
)--then I want to add data to my table
INSERT INTO day_of_week_mapping
VALUES (0, 'Sunday'),
(1, 'Monday'),
(2, 'Tuesday'),
(3, 'Wednesday'),
(4, 'Thursday'),
(5, 'Friday'),
(6, 'Saturday')--then I can join this table to my cleaning table to remap the days of the week
SElECT type, time, usage, units, cost, dowm.day_of_week_name, is_weekend
FROM energy_usage eu
LEFT JOIN day_of_week_mapping dowm ON dowm.day_of_week_int = eu.day_of_week
结果:

作者图片
Python 代码:
在这种情况下,python 具有类似的映射功能。
energy_df['day_of_week_name'] = energy_df['day_of_week'].map({0 : 'Sunday', 1 : 'Monday', 2: 'Tuesday', 3: 'Wednesday', 4: 'Thursday', 5: 'Friday', 6: 'Saturday'})
print(energy_df.head(20))
希望在您处理数据重命名时,这些技术中的一种会对您有用!
此外,请记住,如果您想更改表中某一列的名称,这真的像AS一样简单(我不能不使用这样一个荒谬的语句😂).当使用SELECT语句时,可以像这样重命名列,
PostgreSQL 代码:
SELECT type AS usage_type,
time as time_stamp,
usage,
units,
cost AS dollar_amount
FROM energy_view_exp
LIMIT 20;
结果:

作者图片
Python 代码:
相比之下,在 Python 中重命名列是一件非常痛苦的事情。在这方面,SQL 不仅速度更快,而且代码更优雅。
energy_df.rename(columns={'type':'usage_type', 'time':'time_stamp', 'cost':'dollar_amount'}, inplace=True)
print(energy_df[['usage_type','time_stamp','usage','units','dollar_amount']].head(20))
填写缺失的数据
数据清理过程中的另一个常见问题是丢失数据。对于我们正在使用的数据集,没有明显缺失的数据点,但是,通过评估,我们很可能会发现停电或其他现象导致的每小时缺失数据。这就是 TimescaleDB 提供的填补空白的功能派上用场的地方。使用算法时,缺失数据通常会对模型的准确性或可靠性产生重大负面影响。有时,您可以通过用合理的估计值填充缺失的数据来解决这个问题,TimescaleDB 实际上有内置的函数来帮助您做到这一点。
例如,假设您正在对一周中某几天的能源使用情况进行建模,由于停电或传感器出现问题,有几天缺少能源数据。我们可以删除数据,或者尝试用合理的估计来填充缺失的值。今天,让我们假设我想要使用的模型将从填充缺失值中获益更多。
举个例子,我创建了一些数据。我把这个表叫做energy_data,它丢失了上午 7:45 和 11:30 之间的时间戳的time和energy读数。

作者图片
我可以用时标数据库的填隙超函数来填补这些缺失值。[interpolate()](https://docs.timescale.com/api/latest/hyperfunctions/gapfilling-interpolation/interpolate/?utm_source=tds&utm_medium=blog&utm_id=tsdb-for-data-analysis&utm_content=interpolate-docs)函数是 TimescaleDB 的另一个超函数,它创建的数据点遵循一个线性近似,给出了缺失数据范围前后的数据点。或者,您可以使用[locf()](https://docs.timescale.com/api/latest/hyperfunctions/gapfilling-interpolation/locf/?utm_source=tds&utm_medium=blog&utm_id=tsdb-for-data-analysis&utm_content=locf-docs) hyperfunction,它将最后记录的值向前结转以填补空白(注意 locf 代表最后一个结转)。这两个功能必须与[time_bucket_gapfill()](https://docs.timescale.com/api/latest/hyperfunctions/gapfilling-interpolation/time_bucket_gapfill/?utm_source=tds&utm_medium=blog&utm_id=tsdb-for-data-analysis&utm_content=time-bucket-gapfilling-docs)功能一起使用。
PostgreSQL 代码:
SELECT
--here I specified that the data should increment by 15 mins
time_bucket_gapfill('15 min', time) AS timestamp,
interpolate(avg(energy)),
locf(avg(energy))
FROM energy_data
--to use gapfill, you will have to take out any time data associated with null values. You can do this using the IS NOT NULL statement
WHERE energy IS NOT NULL AND time > '2021-01-01 07:00:00.000' AND time < '2021-01-01 13:00:00.000'
GROUP BY timestamp
ORDER BY timestamp;
结果:

作者图片
Python 代码:
energy_test_df['time'] = pd.to_datetime(energy_test_df['time'])
energy_test_df_locf = energy_test_df.set_index('time').resample('15 min').fillna(method='ffill').reset_index()
energy_test_df = energy_test_df.set_index('time').resample('15 min').interpolate().reset_index()
energy_test_df['locf'] = energy_test_df_locf['energy']
print(energy_test_df)
奖励:
下面的查询是我如何忽略丢失的数据。我想包括这一点,向您展示排除空数据是多么容易。或者,我可以使用一个WHERE子句来指定我想忽略的时间(第二个查询)。
SELECT *
FROM energy_data
WHERE energy IS NOT NULLSELECT *
FROM energy_data
WHERE time <= '2021-01-01 07:45:00.000' OR time >= '2021-01-01 11:30:00.000'
包裹
阅读完这些不同的清理技术后,我希望您在探索 PostgreSQL 和 TimescaleDB 提供的一些可能性时会感觉更舒服。通过直接在我的数据库中清理数据,我能够一次执行许多清理任务,而不是在一个脚本中重复执行,因此从长远来看节省了我的时间。如果您希望在清理数据以进行分析时节省时间和精力,那么一定要考虑使用 PostgreSQL 和 TimescaleDB。
在我的下一篇文章中,我将介绍如何使用 PostgreSQL 和 TimescaleDB 转换数据的技术。然后,我将把我们学到的所有东西放在一起,对 PostgreSQL 和 TimescaleDB 与 Python 和 pandas 中的数据管理任务进行基准测试。最后一篇博客文章将通过使用 TimescaleDB(用于数据管理)和 Python(用于建模和可视化)深入研究数据分析,向您展示真实数据集的整个过程。
如果您对 TimescaleDB、时间序列数据或上述任何功能有任何疑问,请加入我们的社区 Slack ,在这里您会发现一个由时间序列爱好者和各种时间序列团队成员(包括我!).
如果你有兴趣亲自体验 TimescaleDB 和 PostgreSQL 的强大,你可以注册 30 天免费试用或者在你当前的 PostgreSQL 实例上安装 TimescaleDB 和管理它。我们还有一堆很棒的教程来帮助你入门。
下次见!
最初发表于 2021 年 12 月 1 日 https://blog.timescale.com**的 。
事后分析:创业公司数据科学同行评议的一年
行业笔记
大约一年前,我提出了两个针对数据科学项目的同行评审流程,概述了该流程的结构——包括对研究阶段和模型设计及实现的单独评审——并将其定位于更广泛的数据科学项目流程(正如它在初创公司中所实践的那样)的范围内。该框架还包括应审查的主题、陷阱和问题清单。
这不仅仅是一次脑力锻炼。这篇文章旨在将正式的同行评审流程引入我在过去三年中帮助建立并提供建议的一个数据科学团队的工作流程中。自从这篇文章写出来后,我有机会多次作为一个评审者(和几个客户)见证了这种方法在团队动态中的整合以及它是如何被实践的。
不出所料,事情并没有完全按照计划进行。因此,这篇文章是关于什么可行,什么不可行。我关注的是最具挑战性的方面,试图让数据科学家从他们的同行那里得到评论。我希望这能帮助那些希望在数据科学中正式化同行评审过程的人

一位英俊同行评审员的背影(照片由查尔斯·德鲁维奥在 Unsplash 上拍摄)
提醒:我们在复习什么?
我提出的评审过程是为了补充由四个阶段组成的工作流:范围界定——研究——模型开发——产品化/部署。建议两个审查过程:第一个是研究阶段,第二个是模型开发阶段。两者都列出了要回顾的主题列表,并详细说明了每个主题的许多具体问题。

作为 DS 项目工作流程一部分的同行评审流程(来源:作者)
研究评审关注研究方法以及实践和理论问题,旨在确保在进入模型开发之前选择正确的方法。示例有方法假设、模型可组合性、领域适应、噪声和偏差以及现有实现。
模型开发评审关注生产模型的实际问题,意在防止有问题的模型进入生产。示例包括泄漏、因果关系、评估指标、过拟合和运行时间。
关键要点
这是我的经历中的一个要点列表。每一个都是为了克服我遇到的特定挑战。
📅要点 1:首先安排时间
挑战:养成习惯很难。我认为,将流程整合到团队习惯和文化中是主要的挑战。这比代码审查更难,因为没有像在托管版本控制系统(例如 Github)中那样简单的方法来实现强制网关,在托管版本控制系统中,将代码合并到特定的(例如主)分支需要 PR 上的批准(这是您不能给自己的)。

一个没有写着“兔子”的标志(照片由德鲁·比默在 Unsplash 上拍摄)
习惯的形成也与你的项目流程的结构紧密相关;越是结构化,就越容易将(通常)强制性审查过程作为从一个阶段进展到另一个阶段的条件。在结构化程度较低的流程中,领导项目的数据科学家和他的经理要承担更多的责任,以确定需要评审的时间点。
要点:一旦新项目开始,让 DS 安排一次研究评审——即使没有评审员,只邀请团队负责人;然后他们可以推迟,但不太可能跳过。您还可以构建您的工作流程,以便更正式地整合这些评审阶段。
🔍要点 2:鼓励所有人发现评估机会
挑战:责任在于被评审者。成功采用的另一个关键障碍是,让同行评审发生是被评审者(其工作正在接受评审的数据科学家)的责任,这意味着他有责任:
- 确定一个研究阶段(或有研究的阶段)正在进行。
- 确定该阶段接近完成。
- 请记住,同行评审机制是存在的。
- 让自己承担额外的准备审查的工作,以及将自己的工作暴露给明确的同行审查的脆弱性。
这对一双肩膀来说是很大的额外负担。在与我们工作领域最相关的两个同行评审实践中——学术评审和代码评审——不存在何时或是否通过评审的问题;一篇文章必须通过审查才能在学术场所发表;一个拉请求必须被不同的团队成员审查和批准,才能被拉进主开发分支(或者至少,很容易设置你的 VCS 来执行这个)。我们这里没有这样的特权。
要点:虽然这不是数据科学家的责任,但我们应该鼓励他们积极帮助他们的同事问问自己,是不是到了进行评估的时候了;每周或每天的团队会议是帮助每个人注意到项目何时从更像研究的动态转向开发的好时机。
👯♂️的秘诀 3:评论可以使用研究和模型开发主题
挑战:阶段边界模糊。当我想到审查研究迭代或模型的正确时间时,我脑海中有一个有点理想的图片关于如何构建一个小型数据科学项目。
我建议的流程假设数据科学团队将其项目明确划分为范围界定、研究、模型开发和产品化阶段;当进入一个阶段时,宣布该阶段的建议持续时间(或其范围),并且交付成果和 DoD 是明确的;因此,可以很容易地安排研究阶段结束时的同行评审,有时甚至可以提前安排。

边界检测通常比这更难(图片由 Héctor J. Rivas 在 Unsplash 上拍摄)
实际上,人们不会把他们的工作分成如此不同的阶段(尽管我相信这可能是有用的),而且这两种工作之间的界限经常是模糊的。
我也希望我的框架能被大多数从事基于研究和基于产品特性的混合项目的小型数据科学团队所使用,不管他们使用的是什么工作流程;同样,将审查过程分成两种不同的类型会使它与这样的工作流不太相关。
要点:当研究和模型开发阶段的界限模糊不清时,或者当工作本质上包括这两个方面时,评审会议可以利用研究评审列表和模型开发列表中的主题和目标。
👂方法 4:轻松评论非技术问题
问题:头脑风暴解决方案更有趣。在参加一系列这样的会议(正式的和非正式的)时,我注意到一个奇怪的现象:数据科学家倾向于将研究评审会议变成建模头脑风暴会议。

便利贴是大多数头脑风暴基准中的 SOTA(图片由明实验室在 Unsplash 上拍摄)
虽然头脑风暴法对问题建模可能对评审者有益,但这通常不是会议的主要预期益处;当被评审者要求进行研究评审时,他应该对问题的可能建模方法有一个清晰的了解,并考虑到产品需求、资源限制、可用数据等,推荐哪种(或哪些)方法最适合该项目。如果我们谈论的是一个模型开发评审会议,这一点更是如此。
此外,如果我们检查我列出的主题,没有一个关注建模。当然,对每个这样的主题的讨论和评论可以产生新的建模方法的建议,这可能有助于解决新发现的挑战,但是:
- 解决方案也以预处理、特征工程、标签转换、实现细节和数据科学实践的其他方面的形式出现。
- 更重要的是,评审者应该专注于帮助被评审者找到他建议的问题解决方案中的漏洞、弱点和未减轻的风险。提出这些问题的解决方案是被评估者的工作。
那么,为什么数据科学家关注建模建议呢?我认为这可能是因为对方法和计划的批评更难沟通和说服人们,而技术方面的批评更安全,至少对技术人员来说是这样。
这不应该是一个惊喜:技术索赔感觉(并且经常——尽管不总是——更客观、更冷静、更客观。对工作模式和研究方法的批评可能会被认为——有时也会发表——是对被评审者人格的攻击。不应该是这样的。我们都应该能够安全地承认,我们在做我们所做的事情时并不完美,我们可以在所有方面向我们的同龄人学习。我认为意识到这种偏见可以帮助我们提高项目审查的水平和范围。
要点:同事必须能够自如地辩论和评论规划、研究方法和产品/业务假设中的问题。在我看来,还应该积极鼓励他们关注正在审查的项目的这些方面。
💥🥇外卖#5:犯错误应该被允许;找到他们应该受到表扬
问题:受到批评是令人生畏的。我不认为这在任何方面对数据科学来说是特别的,除了将方法失败(或负面研究发现)视为个人失败的独特反模式——这是我们许多人来自的学术背景的有毒遗产(稍后将详细介绍)。
然而,这在做研究活动时比在工程中重要得多,因为基本故障是我们职业的核心。
外卖:弱势传播的文化;频繁而坚定地承认自己一无所知;认为识别错误是一个自然和常见的工作过程,这对于促进频繁和坦诚的同行评审至关重要。
要点:当评审暴露出关键错误或未解决的问题时,被评审者和评审者都应该受到表扬——这是一种共同的成就;这意味着不仅评审者有智慧识别这些问题,而且被评审者安排了评审的时间,提供了正确的信息,并正确地构建了信息,从而能够进行上述识别。
📝要点 6:话题焦点
问题:许多潜在的话题需要触及。我列的清单很长,涉及许多问题——如此之多,以至于在一次会议上几乎不可能全部看完,至少不能浅尝辄止。我认为这里有一个简单的要点:
要点:被评审者和评审者应该就他们特别感兴趣解决的一小部分问题达成一致——例如,偏见、KPI 目标一致性和领域适应性——并关注它们。
如果你没有时间来讨论这个问题,那么任何一方选择 3-4 个他们想关注的问题就足够了。
📉要点 7:忙时放下准备工作
问题:备考时间稀缺。我发现评审员通常找不到时间准备评审会议——手头总是有更紧迫的任务。实际上,我认为你没有它也可以,而且以上对几个主题的关注会有所帮助。
要点:如果准备时间不足,只需通读清单中与焦点问题相关的部分,就足以了解相关的问题和陷阱。
🧪💥要点 8:回顾失败的研究
问题:我们通常不会回顾失败的研究。失败的研究不是研究人员的失败,即使不是大多数情况下也是很多情况下的失败;这是研究活动的本质,包括大量的负面发现:单词嵌入没有捕捉到我们案例的正确语义;顺序架构似乎不能促进对我们数据的根本不同的表示学习;预先训练的模型似乎来自一个太遥远的领域,无法有效地使用;等等。这些都是有价值的发现,应该用来导航项目的未来,并决定我们的资源应该如何花费,因为通常有大量可能的研究方向。

GPT-3 的短篇小说(善良好奇的在 Unsplash 上拍摄的
不幸的是,数据科学家——通常还有他们的环境——倾向于将解决问题方法的失败内在化为他们作为数据科学家能力的失败,尽管这两者是完全分开的。即使我们个人确实失败了——我们确实失败了很多——这些失败也应该被视为学习和改进我们的过程的机会。
例如,假设我们刚刚花了 3 周时间对无监督实体嵌入技术的使用进行了深入研究,并得出了重大的负面发现——例如,无监督实体嵌入技术无法为我们的实体产生足够强的表示,或者产生的输出将很难集成到当前的框架中,或者我们需要多几个数量级的数据来利用它们。反过来,这些发现可能决定了这个研究方向在接下来的几年内不会被追求。这是一个重要的决定——如果这个方向被证明对我们的任务至关重要,那么从长远来看,这里的一个错误可能会付出高昂的代价——因此应该建立在我们所相信的、得到充分支持的研究发现之上。
因此,在我看来,再花一两个小时一起考虑它的确证程度是有意义的,我们是否可以用更少的时间和/或资源获得相同的结论,该结论应该达到多远,什么样的警告可能会促使未来回到这一研究方向,等等。
要点:评审应该发生在每一次重要的研究迭代之后——即使是失败的迭代。它应该用于学习方法和更好地定义负面发现的范围。
好的部分
以下是一些刚刚奏效的方法。😃
光是准备就做了很多工作
也许不是大多数,但是很多。我认为这实际上是一件好事,因为准备工作(当它完成时)有助于接受审查的数据科学家花必要的时间以比其他方式更深入的方式考虑他们工作的关键风险和潜在挑战。
同行批评的正式渠道
当然,同行对观点的批评无论如何都会发生。然而,人们对它的反应因环境而异。我认为会议给了同行批评一个正式的出口。因为批评是预料之中的,并且被框定为每个人和每个项目都要经历的事情,它帮助被评审者避免认为这是他们做错了或者他们被挑选出来的标志。
这反过来会让你放松警惕,对批评的内容更加开放。这与在向整个团队展示你的研究计划或结果时受到批评截然不同,这可能会让你觉得当你试图分享一些很酷的东西时,你被否决了,或者人们关注的是不好的部分。从同事反馈中产生富有成效的结果已经够难的了。
最后,同行评审会议本身也可以帮助被评审者为所述陈述做准备并建立他们的案例,防止这种完全的批评及其负面的副作用。
人们得到了实际有用的反馈
这也许是全部的重点,但它仍然不是微不足道的。虽然这给了一些无论如何都会发生的批评提供了出口,虽然一些事情在准备期间得到了确认,但很明显人们在那些会议期间得到的许多有用的反馈不会对他们可用,否则,并对他们的工作方式产生了积极的影响。几个有机会接受审查的人也证实了这一点。
结束语
就是这样。我希望继续迭代这个简单但有希望有用的数据科学同行评审框架。我的很多工作都是关于审查我的客户公司同事的数据科学流程。我也偶尔会谈到这种形式化本身,并以此为契机,说服人们应该在他们的数据科学团队内部以结构化的方式执行同行评审。
像往常一样,我很想听听你对数据科学同行评审的看法,以及你对我的建议的评论。在shaypalachy.com:)接我
事后分析:团队最重要的基本习惯(分步指南)

图片来源: You X Ventures — Unsplash
我是一个缺乏纪律性的人。我阅读关于习惯养成的书籍的习惯比它们帮助我养成的任何习惯都要强烈。
或许正因为如此,我对关键习惯——这种微小且可管理的转变或改变,会在你生活的许多其他领域成为成功的催化剂。例如,睡眠和锻炼往往会产生积极的连锁效应,并且更容易保持其他习惯。
你认为对于团队来说,最有效的关键习惯是什么,即能产生最积极的连锁效应的团队习惯?
我投票赞成验尸。
每当事情出错,或者本可以变得更好的时候,进行一次无可指责的、五个为什么启发的事后分析,找出你可以如何改进前进。
尽管在出现问题时问我们如何改进并不有趣,但这可能是一次非常宣泄的经历。做得好的话,你可以同时加强心理安全和持续改进,这两者对于成为一个高效团队都是至关重要的。
以下是如何做好事后检查的方法。
在会议之前
- 等待出错:
- 如果是紧急情况,先灭火。
- 然后安排一个 90 分钟的会议。邀请参与解决问题的人,任何受问题影响的人,以及在过程中有足够资格做出重大决策的人。包容一点更好,但在一个大群体中创造一个安全的环境也更难。
2。基于这个公共只读事后模板 创建一个 Google Doc,主要部分如下:
- 发生了什么事?
- 为什么会这样?
- 我们如何改进?
- 我们要告诉谁?
3。阅读 和一个公正的文化 来设定正确的情感基调。
4。阅读 经验教训:五个为什么 让人们思考根本原因。
5。将被邀请者指向您刚刚创建的事后 GDoc,并要求他们对“发生了什么?” 一节,让你有一个完整的描述。在会议开始前询问他们,确保对事件或问题有清晰完整的描述。
在会议本身
- 开篇重申了 的原则 :
我们的目标不是指责任何人,或者花太多时间后悔我们本该做得不同的事情。我们的目标是了解我们作为一个团队的整体系统和流程是如何崩溃的,以及我们可以从中学到什么,以便我们可以改进前进。
只有当你和其他人真的以此为生时,死后的魔法才会起作用,所以我在每次死后的开始都像念咒语一样大声念出来。
2。回顾'发生了什么'一节快速大声说出
调整它,直到每个人都同意描述是准确和完整的。推迟任何关于为什么会发生或者下一步该做什么的讨论。
3。现在是时候理解为什么
- 请每个人在“为什么”部分默默写下他们对事件或问题发生原因的想法。
- 然后默默审核大家的建议,把重复的合并在一起,把重要的建议往上面挪。见主意惊跑。
- 在每种情况下,让他们试着挖掘根本原因,从 Eric Ries 关于五个为什么的文章中获得灵感(但你不必是一个纯粹主义者)。
- 现在请某人简要陈述每个观点,并允许进行简短的讨论。
4。找出你可以改善未来的方法
- 这往往是对事情出错原因的良好理解的自然结果。由于这个原因可能更容易结合'为什么会这样呢?和我们应该做些什么?‘讨论。
- 例如,如果代码中有一个 bug,你可以讨论如何在将来使你的软件工程过程更加可靠。或者如果这是一个新手的错误,你可能需要提高你的训练。如果它落在两个团队分担责任的缝隙之间,你可能需要改善你的沟通/协调。
- 正如 Eric Ries 指出的,挖掘根本原因通常会让你明白,技术问题本质上是人/组织的问题。尝试选择一系列行动来解决表面问题和根本原因。
- 决定哪些潜在行动足够重要,必须明确采取。指派人员执行这些任务,并有一个确保完成任务的流程。
5。决定你要告诉谁
- 如果这是一个面向客户的事件,联系他们承认问题,道歉,并让他们知道你正在做什么来改善事情的进展。正如一个好的服务员可以把一个小混乱变成一个积极的体验,这可以是一个改善你与顾客关系的机会。
- 与公司的其他人分享验尸结果可能是个好主意。它重建了信心,你可能需要他们的帮助。但是请记住,事后分析的参与者需要在心理上感到安全,才能诚实地说出问题所在和原因,因此,根据具体情况,您可能会选择保护一些细节。
一次精彩的回顾是团队最有价值的讨论之一。
正如 Eric Ries 所说,随着时间的推移,它们会复合。它们将你的注意力引向需要优先处理的重复出现的问题。如果你在每次死后都做一些改进,你会惊讶地发现自己很快就变得刀枪不入了。
Redditors 关于 2020 年欧洲杯的帖子和评论
自然语言处理:利用潜在狄利克雷分配(LDA)提取主题

在伦敦温布利体育场举行的 2020 年欧洲杯决赛已经过去了将近两个月。足球热正在慢慢消退,但最近的 2022 年 FIFA 世界杯资格赛引起了我的注意和兴趣,想看看人们对 2020 年欧洲杯有什么看法。因此,我利用了一个 Kaggle 数据集,其中包含 Redditors 讨论 2020 年欧洲杯的帖子。
这个小练习的参考资料可以在 my GitHub 上找到,其中包括数据集和 Python 笔记本文件。
什么是艾达?
LDA 是一种主题建模技术,它假设文档是由混合主题产生的,这些主题基于它们的概率分布生成单词。它还构建了每个文档一个主题的模型和每个主题一个单词的模型。
数据预处理
按顺序,数据预处理涉及以下内容:
- 处理空数据: 标题 和 正文 列是唯一填充有文本的列。但是, 正文 栏中有很多空数据。因此,这些空数据被占位符“NaN”填充,这些占位符稍后将被删除。这样,通过连接 标题 和 正文 列,形成了一个新列 正文 。
- 小写转换:将 文本 列中的所有文本转换为小写是必要的,因为这有利于矢量化。
- 标点符号删除:使用正则表达式(Regex)删除标点符号。
- 数字移除:使用正则表达式移除数字。
- 停用词移除:通过使用 NLTK 库以及将常见的无关词(如“comment”和“nan”)添加到定制的停用词列表中,停用词被移除。
- 词汇化:将有助于保持文本信息含义的单词标准化,以便进行矢量化。
- 符号化:将文本分割成句子,再将句子分割成单词。

NLTK 的停用词,图片由 GeeksforGeeks 提供
在继续使用 LDA 进行主题建模之前,矢量化,旨在建立词典和语料库(单词袋),应用于标记化和词条。
dictionary = gensim.corpora.Dictionary(words_in_docs)
bow = [dictionary.doc2bow(doc) for doc in words_in_docs]
在单词包(bow)中,Gensim 库为每个单词及其在文档中的词频创建了唯一的标识符。
让 LDA 开始吧!
为了启动 LDA 过程,需要指定数据集中主题的数量。因此,我将最小主题数设置为 4,最大主题数设置为 24。
# Topics: 4 Score: 0.637187704946018
# Topics: 6 Score: 0.5656505764313163
# Topics: 8 Score: 0.5608577918583089
# Topics: 10 Score: 0.5285639454916335
# Topics: 12 Score: 0.6549002572803391
# Topics: 14 Score: 0.5805171708843707
# Topics: 16 Score: 0.619577703739399
# Topics: 18 Score: 0.5787737269759226
# Topics: 20 Score: 0.5799681660889682
# Topics: 22 Score: 0.6062730130523755
# Topics: 24 Score: 0.5941403131806395

一致性得分图表,图片由作者提供
基于上述结果和一致性分数的可视化,指定的主题的最佳数量是 12 个主题。
虽然我们从 LDA 要处理的 12 个独特主题开始,但我们将通过次数设置为 9 (75%训练数据集)。
lda_model = gensim.models.LdaMulticore(bow,
num_topics = 12,
id2word = dictionary,
passes = 9,
workers = 3)
结果的解释
一个好的模型包含低困惑和高主题连贯性。下面的结果表明,建立的模型符合标准。
Perplexity: -7.724412623387592Coherence Score: 0.6073296144040131
该模型的输出显示了 12 个独特的主题,每个主题都按单词分类。由于 LDA 不提供每个主题的主题,因此,通过给每个主题一个主题来推断主题是主观的。以下是输出以及我对每个主题所属主题的推断。
Topic 0: Italy vs Spain
Words: "italy", "team", "group", "think", "spain", "game", "match", "final", "played", "goal"
Topic 1: Italy would score goals against England in the final
Words: "team", "euro", "goal", "football", "player", "game", "im", "italy", "would", "england"
Topic 2: England's controversial penalty against Denmark
Words: "england", "right", "penalty", "sure", "see", "well", "im", "football", "game", "player"
Topic 3: Don't underestimate England's performance
Words: "fan", "people", "english", "player", "dont", "country", "team", "one", "know", "football"
Topic 4: England would win against Italy in the final
Words: "england", "game", "like", "win", "team", "italy", "dont", "home", "final", "english"
Topic 5: World questioning England fans' behaviour
Words: "please", "fan", "england", "use", "cup", "question", "world", "contact", "im", "action"
Topic 6: Spain's and England's performances during the semi-finals
Words: "substitution", "match", "shot", "card", "yellow", "scored", "goal", "spain", "england", "thread"
Topic 7: Belgium's performance against its opponents during the tournament
Words: "denmark", "belgium", "v", "finland", "goal", "game", "hazard", "player", "portugal", "bruyne"
Topic 8: Italians don't like the idea of "Football's Coming Home"
Words: "fan", "england", "team", "home", "coming", "english", "dont", "like", "italian", "match"
Topic 9: No red cards for England's fouls throughout the tournament
Words: "england", "red", "card", "didnt", "foul", "would", "even", "im", "ref", "power"
Topic 10: Group A's final matches before Round of 16 started
Words: "switzerland", "italy", "substitution", "match", "card", "shot", "yellow", "turkey", "wale", "goal"
Topic 11: Getting the UEFA Euro 2020 Final tickets
Words: "ticket", "game", "england", "euro", "get", "final", "would", "uefa", "time", "player"
结论
基于上述分析,我们可以得出以下结论,Reddit 用户对 2020 年欧洲杯感兴趣的是:
- 意大利和英格兰之间的决赛
- 英格兰队及其表现
- 围绕英格兰队及其球迷的有争议的新闻
- 涉及英格兰、意大利、西班牙和比利时的比赛
- 比利时球员——凯文·德·布鲁恩和埃登·阿扎尔(或者甚至是后者的兄弟索尔根·阿扎尔!)
参考
班萨尔,S. (2016 年)。Python 主题建模初学者指南。分析 Vidhya。8 月 24 日。检索自https://www . analyticsvidhya . com/blog/2016/08/初学者指南-主题建模-python/
布莱医学博士,Ng,A. Y .,&乔丹医学博士,I. (2016)。潜在狄利克雷分配。机器学习研究杂志 3(2003)993–1022。从 http://www.jmlr.org/papers/volume3/blei03a/blei03a.pdf取回
极客之福。(2020).在 Python 中用 NLTK 删除停用词。11 月 24 日。检索自https://www . geeks forgeeks . org/removing-stop-words-nltk-python/
Power BI 和 Synapse,第 2 部分:Synapse 带来了什么?
在本系列的第 2 部分中,了解 Synapse 为 Power BI 开发人员带来了什么,以及如何利用这两种工具获得无限多的用例!

通过在 2019 年底推出 Azure Synapse Analytics,在数据处理方面开创了一个全新的视角。一些核心概念,如传统的数据仓库,受到了更多的审查,而在数据爱好者意识到 Synapse 带来的新功能后,各种新方法开始涌现。
【Synapse 不仅对数据接收、转换和存储选项产生了巨大影响,它还为数据服务和可视化提供了全新的可能性!
因此,在这一系列博文中,我将尝试探索 Power BI 如何与新平台协同工作。作为 Power BI 开发人员,我们在使用 Synapse 时有哪些选择?在哪些数据分析场景中,Synapse 将发挥优势,帮助您实现(im)可能性?什么时候您想利用 Synapse 中的创新解决方案,什么时候您会更好地坚持使用更传统的方法?使用 Power BI — Synapse combo 的最佳实践是什么,在最终决定采用哪种方法之前,您应该评估哪些参数。
完成后,我相信您应该会更好地理解 Power BI 和 Synapse 之间集成时每个可用选项的“优缺点”。
在我们在本系列的上一篇打下了扎实的理论基础,对 Azure Synapse Analytics 有了更多的了解之后,我相信是 Power BI 跳上舞台的时候了。
面向 Power BI 开发人员的 Synapse
正如在上一篇文章中已经强调的,微软使 Synapse Analytics 成为所有数据相关任务的一站式商店。也就是说,人们会期望 Synapse 和 Power BI 之间的紧密集成,因为 Power BI 被(正确地)视为表达数据和为用户提供各种业务见解的终极工具。
Synapse 与 Power BI 协同工作,反之亦然,不负众望!
在我们深入研究这两者之间的不同集成层之前,让我简要地向您介绍一下作为 Power BI 开发人员,您可能想要考虑使用 Synapse 的场景:
- DirectQuery —我已经详细写过direct query,解释了它是什么,以及在哪些场景下应该考虑使用 DQ。您可能还记得那篇文章,当涉及到报告性能时,DirectQuery 绝不是最佳选择,因为每个可视化将生成至少一个对数据源的查询。但是,在特定情况下,除了使用 DirectQuery,您将没有其他选择,Synapse 可以在这些情况下成为您的朋友!稍后,我们将通过一个真实的例子来验证这在现实中是如何工作的…

斯科特·韦伯在 Unsplash 上拍摄的照片
- 安全性 —如果您想到通过 Azure Active Directory (AAD)进行 Power BI 身份验证,这也是 Synapse 的推荐标准,您可以很容易地想象在 Synapse 内的数据层级别处理 Power BI 解决方案的安全性的场景。这对于依赖 DirectQuery 选项的 Power BI 解决方案尤其重要,因为在使用导入模式时,您可能仍然希望实现行级安全性
- 数据整形 — Power BI 有自己的一套用于执行数据整形的工具——最引人注目的——Power 查询、数据流和 DAX 计算列和表。然而,这些都无法与 Synapse 中企业级的数据转换和准备功能相提并论。首先,Synapse 附带了 Azure Data Factory,这可能是数据编排任务最强大的工具。这意味着您可以在源端执行整体数据整形,因此您的 Power BI 报告可以从使用现成的解决方案中受益
- 真理的单一来源——如果你正在处理数据,你可能会多次听到这句话。从本质上说,这是我们所有人的最终目标。因为从 Synapse 的一个角度来看,它代表了 Azure SQL 数据仓库的合法继承者,所以您可以利用这一点,将来自 Synapse 的数据用于您的 Power BI 报告,确信数据来自集中且经过批准的位置
简而言之,电源 BI 存储模式
在我们进一步研究 Power BI 如何受益于 Synapse Analytics 的协同作用之前,我们需要了解 Power BI 本身不同类型的存储模式。
我已经在本文中写了关于表格模型中的存储引擎,并详细解释了如何通过应用一些能够使 VertiPaq 实现最佳数据压缩的最佳实践来优化您的数据模型大小。
但是,这里对存储模式的简要概述将有助于更好地理解使用 Synapse 时的各种功能:
- 导入模式 —基于 VertiPaq。表数据作为快照存储在内存中。数据可以定期刷新
- 直接查询模式 —在查询时从数据源中检索数据。在查询执行之前、期间和之后,数据都驻留在其原始源中
- 双模 —前两个选项的组合。表中的数据被加载到内存中,但是在查询时也可以直接从源中检索

Synapse 在这里适合做什么?
好吧,现在你可能会问自己,当谈到 Power BI 开发时,Synapse 带来了什么?
在本文中,我们将研究处理来自 Synapse 分析引擎的数据时的各种场景。您可能还记得上一篇文章,Synapse 中有三个不同的分析引擎:无服务器 SQL 池、专用 SQL 池和 Apache Spark 池。
每一种都有自己的优点和实际使用案例。但是,它们都有一个共同点,那就是它们很容易与 Power BI 集成!
正如我提到的,处理 Apache Spark 分析引擎将不在本博客系列中讨论。然而,如果你有兴趣了解 Spark Analytic Engine 如何与 Power BI 协同工作,你可以查看本教程以了解更多细节。
因为本系列的下一篇文章将详细介绍 Power BI 和专用 SQL 池之间的集成,所以让我们在这里将重点放在使用无服务器 SQL 池作为 Power BI 报告的数据源上。
别说了,演示时间到了!
现在,是时候尝试使用 Synapse 的分析引擎来存储 Power BI 消耗的数据,并评估不同的场景了。
但是,首先。在我们继续之前,您应该理解 Synapse 中链接服务的概念。基本上,使用链接服务,您可以利用 Synapse 之外存在的一整套不同的功能和服务,如 Azure Data Lake、CosmosDB、Amazon Redshift、MongoDB 等……其中之一是 Azure HDInsight,如果您计划使用 Apache Spark 分析引擎的话。并且,通过链接它们,它们成为你的 Synapse 生态系统的一部分!

作者图片
现在,在“管理”选项和“链接的服务”下,我已经链接了我的 Power BI 工作区,这意味着我可以直接从 Synapse Analytics Studio 立即开始开发我的 Power BI 报告!

作者图片
当我单击 Power BI 下的开发时,我会看到我的工作区和可用的 Power BI 数据集和报告。目前还没有,所以我将单击新的 Power BI 数据集:

作者图片
在上一篇文章中,我展示了如何编写普通的 T-SQL 来查询来自 parquet 文件(以及 CSV 和 JSON 文件)的数据!并且,使用 OPENROWSET 函数,我创建了一个包含纽约市出租车乘坐数据的视图。这个数据集大约有 15 亿行,所以让我们看看如何在 Power BI 中可视化这个庞大数据集的数据。
一个额外的提示 :如果某些 AAD 用户没有权限在数据库中创建对象,因此他们不能像我一样创建视图,他们仍然可以通过直接在 Power BI 中编写 T-SQL 查询,使用无服务器 SQL 池作为 Power BI 报告的数据源!
只需选择 Azure SQL 数据库作为你的数据源,放入你的 Synapse 工作区的 URL(这个可以在 Serverless SQL Endpoint 下找到),输入数据库名称(如果没有数据库,输入:master),直接在查询窗口写 T-SQL 就可以把你的数据导入 Power BI!

作者图片
这是一个很棒的特性,并且打开了无数用例的大门!
在本文中,我们不会深入讨论性能、优化、成本等细节。本演示的目的只是为了强调 Synapse Analytics 和 Power BI 之间集成的灵活性。
由于您在创建 Synapse Analytics 工作区时将获得默认提供的无服务器 SQL 池,因此我将选择我的无服务器 SQL 池作为数据源,然后下载自动创建的。pbids 文件。一旦提供了专用 SQL 池或 Apache Spark 池,您也可以使用它们作为数据源(我将在下一篇文章中向您展示如何使用专用 SQL 池作为数据源),但是为了简单起见,在本例中我将使用无服务器 SQL 池。
一旦我在 Power BI Desktop 中打开这个文件,我的数据就可以开始运行了!

作者图片
现在,我们将使用 DirectQuery 来演示如何利用 Synapse 和 Power BI 的集成来处理海量数据。我只在我的报告画布上放置了一个视觉效果:显示以下度量的计算的卡片视觉效果:
记录总数= COUNTROWS(nycYellow)

作者图片
正如您所看到的,我们正在使用我们最喜欢的报告工具——Power BI——查询一个巨大的数据集!而且,我们的用户连后台是什么都不知道。他们无法想象他们正在切割来自拼花文件的数据!而且,我们使用常规的 T-SQL 实现了它,不需要“物理地”传输和存储数据!本质上,我们在 Synapse 无服务器 SQL 池的帮助下创建了一个“虚拟”数据仓库。最后,由于这些数据是“普通”SQL 视图的形式,我们能够在 Power BI 中与它进行交互,就像与本地 SQL Server 或 Synapse 专用 SQL 池中的任何其他视图进行交互一样!
此外,不要忘记,我们在一个环境中执行了所有必需的步骤——Azure Synapse Studio!我们甚至直接从 Synapse 连接到 Power BI Desktop 这就是我一直强调的 Synapse 和 Power BI 之间的集成,这使得这两个乍一看完全不同的工具紧密结合在一起!
在 Power BI Desktop 中完成工作后,我可以将报告发布到 Power BI 工作区:

作者图片
而且,由于我已经将我的 Power BI 工作区链接到 Synapse Analytics Studio,我可以从那里直接访问我的报告!不仅如此,我还可以与报告进行交互,如果我想进行一些更改,我甚至可以直接从 Synapse Analytics Studio 中保存它!多酷啊!

作者图片
这是本博客系列的关键要点——当 Synapse 和 Power BI 一起使用时,可以释放校准数据工作负载的无限可能性!
专用 SQL 池—保持传统!
小测验问题: 你知道 Azure Synapse Analytics 的继任者是谁吗?是的,我知道,如果你看过系列的第一部,答案就在那里。但是,如果你跳过了介绍部分(是的,你真可耻,回到那里从头开始读),一个快速的答案将是:Azure SQL 数据仓库。
正式来说,Synapse Analytics 是在 2019 年底作为 SQL DW 的合法继任者推出的。然而,在你容易得出 Synapse 只是“SQL DW 更名”的错误结论之前,你应该记住,Synapse 不仅仅是另一个“传统的数据仓库”。通过包含无服务器 SQL 池和 Apache Spark 专用池,Synapse 实质上成为了一种适用于各种不同数据工作负载的无限分析服务!
在前一部分中,我们只触及了表面,解释了如何快速利用 Synapse 中的无服务器 SQL 池与 Power BI 协同工作,以快速了解驻留在传统数据仓库之外的数据。此外,您可能还记得,您不需要提供任何资源——使用普通的 T-SQL,您可以直接从 Azure Data Lake Gen2 中查询数据!
您还可以利用 Synapse 中的 Apache Spark 分析引擎,但这超出了本系列文章的范围。
在 Synapse 中集成了这两个(无服务器 SQL 池和 Apache Spark 池)之后,人们可以假设旧的、传统的数据仓库已经死亡……而且,这将是一个快速而错误的结论。在许多场景和用例中,一个好的旧数据仓库将是最佳解决方案。

图片由 iStrfry Marcus 在 Unsplash 上拍摄
在本文中,我们将研究具有关系数据的各种工作负载,这些数据存储在配置的分析引擎(专用 SQL 池)上,因此您可以认为,或多或少,我们正在后台处理一个很好的旧 SQL 服务器。
说实话,专用 SQL 池是基于不同的架构,获得了使用 MPP 架构的好处。
我不会深入介绍 MPP 及其工作原理 —我只想强调,这种架构能够实现巨大的灵活性,因为您可以根据当前工作负载的数量和性质快速横向扩展您的资源。
当然,与本地 SQL Server 相比,Synapse 带来了一些额外的潜在性能提升,例如结果集缓存和物化视图。同样,我不会深入讨论这两个特性的细节——如果你对详细了解物化视图感兴趣,我强烈建议你阅读 Niko Neugebauer 的博客上的一系列文章。但是,您应该知道物化视图,因为我们将在这里的演示中使用它们…
不要再说了,现在是演示时间!
现在是时候动手使用 Synapse 的专用 SQL 池来存储 Power BI 所消耗的数据,并评估不同的场景了。
但是,首先。与创建 Synapse 工作区时默认创建的无服务器 SQL 池相反,需要手动创建一个专用的 SQL 池。因此,我开始创建我的专用 SQL 池,并为其分配默认大小(DW100c ):

作者图片
像往常一样,在处理 Power BI 时,我使用一个示例 Contoso 数据库进行演示。我已经在我的专用 SQL 池中提供了这个数据库,并为 factOnlineSales 表和两个维度表导入了数据:dimDate 和 dimProduct。

作者图片
现在,在“管理”选项和“链接的服务”下,我已经链接了我的 Power BI 工作区,这意味着我可以直接从 Synapse Analytics Studio 立即开始开发我的 Power BI 报告!

作者图片
当我单击 Power BI 下的开发时,我会看到我的工作区和可用的 Power BI 数据集和报告。目前还没有,所以我将单击新的 Power BI 数据集:

作者图片
我将选择我的专用 SQL 池作为数据源,然后下载自动创建的。pbids 文件。一旦我在 Power BI Desktop 中打开这个文件,我的数据就可以开始运行了!

作者图片
现在,实际上,我的事实表有大约 1200 万行,这在容量上没什么特别的——我的意思是,Power BI 应该能够毫无问题地处理这个问题。但是,让我们假设我们的事实表有 12 亿行,因此在现有粒度级别上导入整个表是不可行的。我们将使用 DirectQuery 来演示如何利用 Synapse 和 Power BI 的集成来处理专用 SQL 池中的海量数据。
因此,我创建了一个用于计算销售额总和的显式度量,并在报告画布上放置了三个简单的视觉效果:一个显示每年的总销售额,另一个显示每个品牌的总销售额,最后一个显示按品牌和年份细分的销售额:

作者图片
现在,我将连接到一个 DAX 工作室,这样我可以更深入地了解后台正在发生的事情。如果我从顶部功能区打开服务器计时,我可以粘贴我的 DAX 代码并检查在后台生成的 SQL 查询,以及检索结果所用的总时间:

作者图片
因此,这与 SQL Server 的工作方式非常相似。现在不要离开,我保证我们会添加更多的成分来改善指标。那么,为了加快数据检索,我们能做些什么呢?
第一点也是最重要的一点是: 您需要了解您的分析工作量! 这意味着,如果您知道有很多针对这三个表的查询,并且请求相同的数字(每个品牌/年的销售额),您可以做两件事:
- 创建一个 聚合表,并将其设置为导入模式 ,享受使用复合模型的好处。这是最理想的场景,只要聚合表能够适合 Power BI 中的数据模型大小,就应该使用它。使用这一概念,您的聚合表将存储在 Power BI 高速缓存中,经过优化压缩,而包含所有详细信息的巨大表将继续驻留在 Synapse 专用 SQL 池中
- 在 Synapse 专用 SQL 池 中创建一个 物化视图。本质上,您也可以聚合数据,但是您不会在 Power BI 中导入它,因为您将使用 DirectQuery 模式来定位这个视图。实体化视图只不过是一个虚拟表,但与“标准”视图(每次使用时都会计算数据)不同,实体化视图会像普通表一样在专用的 SQL 池中预先计算、存储和维护其数据!因此,您可以为最频繁的工作负载获得更快的性能,但作为一种代价,您要为额外的存储付出代价
现在,我已经在我的 Synapse 专用 SQL 池中创建了一个物化视图,使用了以下 T-SQL 代码:
*CREATE MATERIALIZED VIEW mvwSalesAmtBrandYear WITH (DISTRIBUTION=HASH(BrandName)) AS
SELECT SUM(f.[SalesAmount]) AS [SalesAmount]
,p.[BrandName]
,YEAR(d.[DateKey]) AS [Year]
FROM [cso].[FactOnlineSales] AS f
INNER JOIN [cso].[DimProduct] AS p ON f.[ProductKey] = p.[ProductKey]
INNER JOIN [cso].[DimDate] AS d on f.[DateKey] = d.[DateKey]
GROUP BY p.[BrandName]
,YEAR(d.[DateKey])*
让我们转到 Power BI Desktop,将这个视图添加到我们的数据模型中,并将我们的视觉效果切换到所需数据的物化视图。现在,如果我在 DAX Studio 中检查服务器计时,我可以注意到显著的改进:

作者图片
正如你所注意到的,我们得到了完全相同的数字,完全相同的视觉效果,快了 10 倍!仅仅因为我们在 Synapse 专用的 SQL 池中有预聚合的数据。
最后要检查的是,如果我们在 Power BI 中导入物化视图会发生什么:

作者图片
如果我刷新图像,我会得到以下结果:

作者图片
与第一次运行的 3.505 毫秒相比,我的视觉效果现在只需要 9 毫秒就可以呈现出来了!我将让您自己来计算…在最后一次运行中,右边的窗口中没有“常规的”SQL 查询,因为现在数据是直接从 Power BI 的表格模型中存储和检索的。
正如我已经提到的,您也可以在 Power BI 桌面模型视图中处理聚合,但是这是一个完全不同的主题,超出了本系列的范围。
在本演示中,我想向您展示如何无缝集成 Synapse Analytics 专用 SQL 池和 Power BI。
在 Power BI Desktop 中完成工作后,我可以将报告发布到 Power BI 工作区:

作者图片
您可能还记得我们之前的演示,因为我已经将 Power BI 工作区链接到 Synapse Analytics Studio,所以我可以从那里直接访问我的报告!不仅如此,我还可以与报告进行交互,如果我想进行一些更改,我甚至可以直接从 Synapse Analytics Studio 中保存它!多牛逼啊!

作者图片
让我再次强调本博客系列的关键要点——当 Synapse 和 Power BI 一起使用时,可以释放校准数据工作负载的无限可能性!
结论
在本文中,我们研究了 Synapse Analytics 和 Power BI 之间的不同集成方式,在这些场景中,您正在处理以传统数据仓库的典型方式存储的关系数据。
我们证明了您不需要放弃传统,即使您计划将工作负载转移到 Synapse。相反,您可以利用它,并基于已建立并经过数十年验证的传统数据仓库概念构建一个现代数据解决方案。如果您有兴趣了解更多关于使用专用 SQL 池作为 Power BI 报告数据源的最佳实践,您可以参考这篇文章。
在下一篇文章中,我们将深入关注 Power BI 和 Synapse 之间的不同集成层,在处理半结构化(存储在文件中的数据)方面,使用 Synapse Analytics 中的无服务器 SQL 池。
感谢阅读!
成为会员,阅读媒体上的每一个故事!
Power BI 和 Synapse,第 3 部分—无服务器 SQL:我要花多少钱?
如果您将 Power BI 与 Synapse Analytics 结合使用,您会支付多少费用?在本系列的最后一部分,我对 Power BI & Synapse Analytics 无服务器 SQL 池进行了终极基准测试,结果如下!

图片由 Kolleen Gladden 在 Unsplash 上拍摄
通过在 2019 年底推出 Azure Synapse Analytics,在数据处理方面开创了一个全新的视角。一些核心概念,如传统的数据仓库,受到了更多的审查,而在数据爱好者意识到 Synapse 带来的新功能后,各种新方法开始涌现。
【Synapse 不仅对数据接收、转换和存储选项产生了巨大影响,它还为数据服务和可视化提供了全新的可能性!
因此,在这一系列博文中,我将尝试探索 Power BI 如何与新平台协同工作。作为 Power BI 开发人员,我们在使用 Synapse 时有哪些选择?在哪些数据分析场景中,Synapse 将发挥优势,帮助您实现(im)可能性?什么时候您想利用 Synapse 中的创新解决方案,什么时候您会更好地坚持使用更传统的方法?使用 Power BI — Synapse combo 的最佳实践是什么,在最终决定采用哪种方法之前,您应该评估哪些参数。
完成后,我相信您应该会更好地理解 Power BI 和 Synapse 之间集成时每个可用选项的“优缺点”。
不要误解我的意思——这个理论很好,你绝对应该花时间尝试吸收新技术、工具或特性的基本架构概念。因为,如果你不明白某样东西是如何在引擎盖下工作的,那么很有可能你不会最大限度地利用它。
但是,将这些理论知识应用于实践测试才是最有趣的部分!至少对我来说是这样:)…这让我想起了汽车生产过程:他们制造一切,你读了说明书,印象深刻!特征,设备…然而,这些都无关紧要,直到他们把车进行碰撞测试,并得到适当的现实检查。
因此,这篇文章就像是对 Synapse Analytics 中无服务器 SQL 池的碰撞测试——许多不同的场景、测试、演示、测量等。
无服务器 SQL 池—下一件大事
我已经写了关于无服务器 SQL 池,我坚信它是处理大量半结构化或非结构化数据的下一件大事。
无服务器 SQL 池的最大优势在于,您可以直接从存储在 Azure 数据湖中的 CSV、parquet 或 JSON 文件中查询数据,而无需传输数据!甚至,您可以编写普通的 T-SQL 来直接从文件中检索数据!但是,让我们看看这在各种实际使用案例中是如何工作的,最重要的是, 每个解决方案将花费您多少钱!
在使用 Synapse Serverless SQL pool 时,仍有一些东西微软不会向您收取数据处理费用,例如:
- 服务器级元数据(登录名、角色和服务器级凭据)
- 您在端点中创建的数据库。这些数据库只包含元数据(用户、角色、模式、视图、内联表值函数、存储过程、外部数据源、外部文件格式和外部表)
- DDL 语句,但 CREATE STATISTICS 语句除外,因为它根据指定的样本百分比处理存储中的数据
- 仅元数据查询
方案
场景是这样的:我有两个与纽约出租车数据集相关的 CSV 文件,我已经在前面的一个演示中使用过。一个包含 2019 年 1 月以来所有黄色出租车乘坐的数据(650 MB),另一个包含 2019 年 2 月以来的数据(620 MB)。

作者图片
我为每个月的数据创建了两个单独的视图。这个想法是为了检查当我们在多种不同条件下查询 Power BI 的数据时会发生什么。
下面是在一个月内创建视图的 T-SQL:
DROP VIEW IF EXISTS taxi201902csv;
GOCREATE VIEW taxi201902csv AS
SELECT
VendorID
,cast(tpep_pickup_datetime as DATE) tpep_pickup_datetime
,cast(tpep_dropoff_datetime as DATE) tpep_dropoff_datetime
,passenger_count
,trip_distance
,RateCodeID
,store_and_fwd_flag
,PULocationID
,DOLocationID
,payment_type
,fare_amount
,extra
,mta_tax
,tip_amount
,tolls_amount
,improvement_surcharge
,total_amount
,congestion_surcharge
FROM
OPENROWSET(
BULK N'https://nikola.dfs.core.windows.net/nikola/Data/yellow_tripdata_2019-02.csv',
FORMAT = 'CSV',
PARSER_VERSION='2.0',
HEADER_ROW = TRUE
)
WITH(
VendorID INT,
tpep_pickup_datetime DATETIME2,
tpep_dropoff_datetime DATETIME2,
passenger_count INT,
trip_distance DECIMAL(10,2),
RateCodeID INT,
store_and_fwd_flag VARCHAR(10),
PULocationID INT,
DOLocationID INT,
payment_type INT,
fare_amount DECIMAL(10,2),
extra DECIMAL(10,2),
mta_tax DECIMAL(10,2),
tip_amount DECIMAL(10,2),
tolls_amount DECIMAL(10,2),
improvement_surcharge DECIMAL(10,2),
total_amount DECIMAL(10,2),
congestion_surcharge DECIMAL(10,2)
)
AS [taxi201902csv]
我使用 WITH block 来显式定义数据类型,如果不这样做,所有的字符列将自动设置为 VARCHAR(8000 ),因此会占用更多的内存。
正如您所注意到的,我已经从 CSV 文件中重命名了我的通用列名,因此它们现在看起来更具可读性。我还将 DateTime 列强制转换为 Date 类型,因为我不需要这个演示的时间部分。这样,我们减少了基数和整个数据模型的大小。让我们检查一下每个文件包含多少条记录:

作者图片
一月包含大约 760 万条记录,而二月包含大约 700 万条记录。
此外,我还应用了相同的逻辑,对来自 拼花 文件的完全相同的数据部分构建了两个视图。因此,我们可以比较 CSV 和拼花文件之间的指标。
用于拼花文件的视图构建略有不同,使用 FILENAME()和 FILEPATH()函数来消除不必要的分区:
DROP VIEW IF EXISTS taxi201901parquet;
GOCREATE VIEW taxi201901parquet AS
SELECT
VendorID
,CAST(TpepPickupDatetime AS DATE) TpepPickupDatetime
,CAST(TpepDropoffDatetime AS DATE) TpepDropoffDatetime
,PassengerCount
,TripDistance
,PuLocationId
,DoLocationId
,StartLon
,StartLat
,EndLon
,EndLat
,RateCodeId
,StoreAndFwdFlag
,PaymentType
,FareAmount
,Extra
,MtaTax
,ImprovementSurcharge
,TipAmount
,TollsAmount
,TotalAmount
FROM
OPENROWSET(
BULK 'puYear=*/puMonth=*/*.snappy.parquet',
DATA_SOURCE = 'YellowTaxi',
FORMAT='PARQUET'
) nyc
WHERE
nyc.filepath(1) = 2019
AND nyc.filepath(2) IN (1)
AND tpepPickupDateTime BETWEEN CAST('1/1/2019' AS datetime) AND CAST('1/31/2019' AS datetime)
正如在 Synapse 中使用无服务器 SQL 池的最佳实践中所指定的,我们明确指示我们的查询只针对 2019 年和一月份!这将减少扫描和处理的数据量。对于我们的 CSV 文件,我们不必这样做,因为它们已经按月分区并保存了。
一个重要的免责声明:由于无服务器 SQL 池的使用是按处理的数据量收费的(目前的价格是每 TB 处理的数据 5 美元起),我不会用速度来衡量性能。我想只关注对处理的数据量和通过检查不同场景产生的成本的分析。
CSV vs 拼花——我需要知道什么?
在我们继续测试之前,再讲一点理论…我计划比较 CSV 和 Parquet 文件之间的数据处理,我认为我们应该了解这两种类型之间的主要区别:
- 在拼花文件中,数据以更优化的方式压缩。您可能还记得上面的一个截图,与包含相同数据部分的 CSV 文件相比,parquet 文件大约消耗 1/3 的内存
- Parquet 文件支持列存储格式——也就是说,Parquet 文件中的列是物理分离的,这意味着如果您只需要几列中的数据,就不需要扫描整个文件!相反,当你查询一个 CSV 文件时,每次你发送查询,它将扫描整个文件,即使你需要一列的数据
- 对于那些来自传统 SQL 世界的人,你可以想到 CSV 与 Parquet,例如行存储与列数据库
用例 1 —将 CSV 数据导入 Power BI
让我们从最明显和最理想的场景开始——使用导入模式将所有数据接收到 Power BI 中,并执行数据刷新以检查这将花费我们多少成本。
在我们动手之前,还有最后一件事——在 Synapse Analytics 中,仍然没有一个功能可以衡量特定查询的成本。您可以检查每天、每周或每月处理的数据量。此外,如果您愿意,您可以在每个时间粒度级别上设置限制——在本文的中有更多相关内容。
这个特性也是相对较新的,所以我很高兴看到 Synapse 在提供完全成本透明的有希望的方式上取得了永久性的进展。
由于我们无法测量每个查询的确切成本,我将使用下面的 T-SQL,通过在主数据库中查询 DMV 来尝试计算这些数字:
SELECT * FROM sys.dm_external_data_processed
WHERE type = 'daily'

作者图片
因此,我今天的起点大约是 29.7 GB,我将在每次定位无服务器 SQL 池以获取数据时计算差异。
好了,回到第一个场景,我将把这两个月的数据从 CSV 文件导入 Power BI:

作者图片
最令人着迷的是,我正在编写普通的 T-SQL,所以我的用户甚至不知道他们是直接从 CSV 文件中获取数据的!我使用 UNION ALL,因为我确信在我的两个视图中没有相同的记录,理论上,它应该比 UNION 运行得更快,但是我也可以使用相同的 T-SQL 语句创建一个单独的视图,然后在 Power BI 中使用该联合视图。
我需要一个合适的日期维度表来测试不同的场景,我将使用 Power Query 创建它。该日期表在所有场景中都将处于导入模式,因此它不会影响来自无服务器 SQL 池的已处理数据量。以下是日期表的 M 代码:
let
StartDate = #date(StartYear,1,1),
EndDate = #date(EndYear,12,31),
NumberOfDays = Duration.Days( EndDate - StartDate ),
Dates = List.Dates(StartDate, NumberOfDays+1, #duration(1,0,0,0)),
#"Converted to Table" = Table.FromList(Dates, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
#"Renamed Columns" = Table.RenameColumns(#"Converted to Table",{{"Column1", "FullDateAlternateKey"}}),
#"Changed Type" = Table.TransformColumnTypes(#"Renamed Columns",{{"FullDateAlternateKey", type date}}),
#"Inserted Year" = Table.AddColumn(#"Changed Type", "Year", each Date.Year([FullDateAlternateKey]), type number),
#"Inserted Month" = Table.AddColumn(#"Inserted Year", "Month", each Date.Month([FullDateAlternateKey]), type number),
#"Inserted Month Name" = Table.AddColumn(#"Inserted Month", "Month Name", each Date.MonthName([FullDateAlternateKey]), type text),
#"Inserted Quarter" = Table.AddColumn(#"Inserted Month Name", "Quarter", each Date.QuarterOfYear([FullDateAlternateKey]), type number),
#"Inserted Week of Year" = Table.AddColumn(#"Inserted Quarter", "Week of Year", each Date.WeekOfYear([FullDateAlternateKey]), type number),
#"Inserted Week of Month" = Table.AddColumn(#"Inserted Week of Year", "Week of Month", each Date.WeekOfMonth([FullDateAlternateKey]), type number),
#"Inserted Day" = Table.AddColumn(#"Inserted Week of Month", "Day", each Date.Day([FullDateAlternateKey]), type number),
#"Inserted Day of Week" = Table.AddColumn(#"Inserted Day", "Day of Week", each Date.DayOfWeek([FullDateAlternateKey]), type number),
#"Inserted Day of Year" = Table.AddColumn(#"Inserted Day of Week", "Day of Year", each Date.DayOfYear([FullDateAlternateKey]), type number),
#"Inserted Day Name" = Table.AddColumn(#"Inserted Day of Year", "Day Name", each Date.DayOfWeekName([FullDateAlternateKey]), type text)
in
#"Inserted Day Name"
将数据加载到 Power BI 桌面需要一些时间,现在让我们检查一些关键指标。
- 我的表有大约 1470 万行
- 我的整个数据模型大小约为 92 MB,因为数据在 Power BI Desktop 中进行了优化压缩(我们已经降低了 DateTime 列的基数)

作者图片
一旦我创建了表可视化,显示每个日期的总记录,我每天处理的数据量大约是 33.3 GB。让我们刷新数据模型,看看它有多贵。因此,Power BI Desktop 现在将转到一个无服务器 SQL 池,从我的两个视图中查询数据,但不要忘记在后台有两个 CSV 文件作为我们数据的最终来源!
刷新后,我的每日值增加到了大约 36.9 GB,这意味着这次刷新花费了大约 3.6 GB。钱的话,大概是0.018 $(0.0036 TB x 5 USD)。
在此使用案例中,只有当我的 Power BI 模型被更新时,我才会花钱!简单地说,如果我每天刷新一次数据模型,这个报告每月将花费我 54 美分。
用例 CSV 文件上的 DirectQuery
现在让我们来看看如果使用完全相同的查询会发生什么,但是我们将使用 DirectQuery 选项,而不是将数据导入 Power BI Desktop。

作者图片
让我们首先与日期切片器交互,这样我们可以检查这将花费我们多少。我的测量起点是大约 87.7 GB,这是我的报告的样子:

作者图片
刷新整个查询烧掉了~2.8 GB,也就是 ~0.014$ 。现在,这是为了页面上的一个单独的视觉效果!请记住,当您使用 DirectQuery 时,每个 visual 将生成一个对底层数据源的单独查询。让我们看看当我在页面上添加另一个视觉效果时会发生什么:

作者图片
现在,这个查询花费了我~4 GB,也就是 0.02$ 。正如您可以得出的结论,增加报表画布上的视觉效果也会增加成本。
还有一点要记住: 这些成本是每个用户 !因此,如果您有 10 个用户并行运行这个相同的报告,您应该将成本乘以 10,因为将为每个视图和每个用户生成一个新的查询。
用例 3 —在 DirectQuery 模式下使用日期切片器
现在,我想检查如果我在切片器中选择一个特定的日期范围,例如 1 月 1 日和 1 月 13 日之间,会发生什么情况:

作者图片
我注意到的第一件事是查询花费了我完全一样的钱!奇怪的是,如果我查看为检索数据而生成的 SQL 查询,我可以看到引擎非常聪明地在 WHERE 子句中应用了日期过滤器:
/*Query 1*/
SELECT
TOP (1000001) [semijoin1].[c1],SUM([a0])
AS [a0]
FROM
(
(
SELECT [t1].[tpep_pickup_datetime] AS [c14],[t1].[total_amount] AS [a0]
FROM
(
(SELECT * FROM dbo.taxi201901csv
UNION ALL
SELECT * FROM dbo.taxi201902csv
)
)
AS [t1]
)
AS [basetable0]
INNER JOIN
(
(SELECT 3 AS [c1],CAST( '20190101 00:00:00' AS datetime) AS [c14] ) UNION ALL
(SELECT 4 AS [c1],CAST( '20190102 00:00:00' AS datetime) AS [c14] ) UNION ALL
(SELECT 5 AS [c1],CAST( '20190103 00:00:00' AS datetime) AS [c14] ) UNION ALL
(SELECT 6 AS [c1],CAST( '20190104 00:00:00' AS datetime) AS [c14] ) UNION ALL
(SELECT 7 AS [c1],CAST( '20190105 00:00:00' AS datetime) AS [c14] ) UNION ALL
(SELECT 8 AS [c1],CAST( '20190106 00:00:00' AS datetime) AS [c14] ) UNION ALL
(SELECT 9 AS [c1],CAST( '20190107 00:00:00' AS datetime) AS [c14] ) UNION ALL
(SELECT 10 AS [c1],CAST( '20190108 00:00:00' AS datetime) AS [c14] ) UNION ALL
(SELECT 11 AS [c1],CAST( '20190109 00:00:00' AS datetime) AS [c14] ) UNION ALL
(SELECT 12 AS [c1],CAST( '20190110 00:00:00' AS datetime) AS [c14] ) UNION ALL
(SELECT 13 AS [c1],CAST( '20190111 00:00:00' AS datetime) AS [c14] ) UNION ALL
(SELECT 14 AS [c1],CAST( '20190112 00:00:00' AS datetime) AS [c14] ) UNION ALL
(SELECT 15 AS [c1],CAST( '20190113 00:00:00' AS datetime) AS [c14] )
)
AS [semijoin1] on
(
([semijoin1].[c14] = [basetable0].[c14])
)
)
GROUP BY [semijoin1].[c1] /*Query 2*/
SELECT SUM([t1].[total_amount])
AS [a0]
FROM
(
(SELECT * FROM dbo.taxi201901csv
UNION ALL
SELECT * FROM dbo.taxi201902csv)
)
AS [t1]
WHERE
(
([t1].[tpep_pickup_datetime] IN (CAST( '20190112 00:00:00' AS datetime),CAST( '20190113 00:00:00' AS datetime),CAST( '20190101 00:00:00' AS datetime),CAST( '20190102 00:00:00' AS datetime),CAST( '20190103 00:00:00' AS datetime),CAST( '20190104 00:00:00' AS datetime),CAST( '20190105 00:00:00' AS datetime),CAST( '20190106 00:00:00' AS datetime),CAST( '20190107 00:00:00' AS datetime),CAST( '20190108 00:00:00' AS datetime),CAST( '20190109 00:00:00' AS datetime),CAST( '20190110 00:00:00' AS datetime),CAST( '20190111 00:00:00' AS datetime)))
)
但是,底层视图似乎扫描了 CSV 文件中的整个数据块!因此,如果您使用日期切片器来限制数据量,在节省方面没有任何好处,因为在任何情况下都会扫描整个 CSV 文件…
用例 DirectQuery 模式下的聚合表
下一个测试将向我们展示如果我们创建一个聚集表并以 DirectQuery 模式将其存储在 Power BI 中会发生什么。这是一个非常简单的汇总表,由总量和取件时间列组成。

作者图片
我的查询命中了聚合表,但是在总查询成本方面没有任何变化,因为它与前面的用例完全相同: ~0.02$ !

作者图片
用例 5 —导入模式下的聚合表
之后,我想检查一下如果我将一个先前聚合的表导入到 Power BI 中会发生什么。我相信计算会更快,但让我们看看它会如何影响查询成本。
正如所料,这非常快,聚合表被命中,因此我们只需支付数据刷新的代价,如我们的用例#1: 0.018$ !

作者图片
用例 6 —无服务器 SQL 池中的聚合数据
我想检查的最后一件事是,如果我知道我的分析工作负载,并且可以使用无服务器 SQL 池提前准备一些最频繁的查询,会发生什么情况。
因此,我将创建一个视图,像上一个案例一样在 Power BI Desktop 中聚合数据,但这次是在无服务器 SQL 池中:
DROP VIEW IF EXISTS taxi201901_02_agg;
GOCREATE VIEW taxi201901_02_agg AS
SELECT CAST(C2 AS DATE) AS tpep_pickup_datetime,
SUM(CAST(C17 AS DECIMAL(10,2))) AS total_amount
FROM
OPENROWSET(
BULK N'https://nikola.dfs.core.windows.net/nikola/Data/yellow_tripdata_2019-01.csv',
FORMAT = 'CSV',
PARSER_VERSION='2.0',
HEADER_ROW = TRUE
)
AS [taxi201901_02_agg]
GROUP BY CAST(C2 AS DATE)
基本上,我们在源端聚合数据,这显然会有所帮助。所以,让我们来看看结果:

作者图片
它返回请求的数字更快,但是处理的数据量还是一样的!这个查询又花费了我 ~0.02$ !
这使我得出一个结论: 无论您在 CSV 文件之上的无服务器 SQL 池中执行什么,它们都将在数据准备过程的最低级别被完全扫描!
我假设当您处理较大的数据集(几 TB)时,成本差异会明显得多。因此,请记住,在无服务器 SQL 池中预先聚合数据可以节省流数据量,这也意味着您的总体成本将会降低!你可以在这里找到所有的细节。
用例#7 —导入拼花文件
现在,让我们评估一下,如果我们使用来自 Parquet 文件的数据,而不是 CSV,是否会发生一些变化。
第一个用例是导入拼花文件。不出所料,由于它们比 CSV 文件压缩得更好,成本几乎降低了一倍: ~0.01$ !
用例 8——对拼花文件进行直接查询
最后,如果我们在 Power BI 中使用 DirectQuery 模式直接从 Synapse 的无服务器 SQL 池中的 Parquet 文件中查询数据,让我们来看看这些数字。
出乎我意料的是,这个查询处理了大约 26 GB 的数据,相当于 ~0.13$ !
由于这看起来非常奇怪,我开始调查并发现高成本的罪魁祸首是使用 M!在后台调试由 Power BI 生成并发送到 SQL 引擎的 SQL 查询时,我注意到已经创建了极其复杂的查询,对日期维度中的每个值执行连接和联合操作:
SELECT
TOP (1000001) [semijoin1].[c1],SUM([a0])
AS [a0]
FROM
(
(
SELECT [t1].[TpepPickupDatetime] AS [c13],[t1].[TotalAmount] AS [a0]
FROM
(
(SELECT *
FROM taxi201901parquet
UNION ALL
SELECT *
FROM taxi201902parquet)
)
AS [t1]
)
AS [basetable0]
INNER JOIN
(
(SELECT 3 AS [c1],CAST( '20190101 00:00:00' AS datetime) AS [c13] ) UNION ALL
(SELECT 4 AS [c1],CAST( '20190102 00:00:00' AS datetime) AS [c13] ) UNION ALL
(SELECT 5 AS [c1],CAST( '20190103 00:00:00' AS datetime) AS [c13] ) UNION ALL
(SELECT 6 AS [c1],CAST( '20190104 00:00:00' AS datetime) AS [c13] ) UNION ALL
(SELECT 7 AS [c1],CAST( '20190105 00:00:00' AS datetime) AS [c13] ) UNION ALL
(SELECT 8 AS [c1],CAST( '20190106 00:00:00' AS datetime) AS [c13] ) UNION ALL
(SELECT 9 AS [c1],CAST( '20190107 00:00:00' AS datetime) AS [c13] ) UNION ALL
(SELECT 10 AS [c1],CAST( '20190108 00:00:00' AS datetime) AS [c13] ) UNION ALL
(SELECT 11 AS [c1],CAST( '20190109 00:00:00' AS datetime) AS [c13] ) UNION ALL
…..
这只是生成的查询的摘录,为了可读性,我已经删除了其余的代码。
一旦我从计算中排除了我的日期维度,成本预计会降低到 400 MBs 以下!!!因此,现在处理的数据量约为 400MB,而不是日期维的 26GB!
总之,这些使用复合模型的场景需要仔细的评估和测试。
用例 9 —无服务器 SQL 池中的聚合数据
那是我们的引爆点!这就是奇迹发生的地方!通过能够存储物理上分离的列,在这种情况下,Parquet 优于所有以前的用例——当我说这种情况时,我指的是当您能够减少必要的列的数量时(只包括那些您需要从 Power BI 报告中查询的列)。
当我在无服务器 SQL 池中创建了一个包含预聚合数据的视图后,只处理了 400 MB 的数据!与之前的所有测试相比,这是一个巨大的差异。基本上,这意味着这次查询的成本: 0.002$ !为了便于计算,我可以用 1 美元运行 500 次!
定论
下表列出了我所研究的每一个用例的成本:

作者图片
查看表格,并考虑我们在上面检查的不同使用案例,可以得出以下结论:
- 尽可能使用拼花文件,而不是 CSV
- 尽可能将数据导入 Power BI —这意味着您只需在刷新数据快照时付费,而不是为报告中的每个查询付费
- 如果您正在处理 Parquet 文件,只要有可能, 就会在 Synapse 的无服务器 SQL 池 中创建预聚合数据(视图)
- 由于无服务器 SQL pool 仍然 不支持 ResultSet 缓存 (据我所知,微软的团队正在研究),请记住,每次运行查询(即使您返回的是相同的结果集),都会生成查询,您需要为此付费!
- 如果您的分析工作负载需要对大型数据集进行大量查询(如此之大,以至于无法选择导入模式),也许您应该 考虑将数据存储在专用的 SQL 池 中,因为您将支付固定的存储成本,而不是每次查询数据时的数据处理成本。这里,为了从使用这个场景中获得额外的好处,您应该在将中间结果导入到专用的 SQL 池之前,使用外部表对它们进行具体化!这样,您的查询将读取已经准备好的数据,而不是原始数据
- 在 Synapse Analytics 中使用无服务器 SQL 池时,坚持使用通用最佳实践
结论
在本文中,我们深入测试了在 Synapse Analytics 中将 Power BI 与无服务器 SQL 池结合使用时的不同场景和多个用例。
在我看来,尽管 Synapse 还有很长的路要走,以微调无服务器 SQL 池中的所有功能和产品,但毫无疑问,它正朝着正确的方向前进。通过不断改进产品,并定期添加酷的新功能,Synapse 可以真正成为您所有数据工作负载的一站式商店。
在这个博客系列的最后一部分,我们将检查 Power BI 如何与 Azure 的 NoSQL 解决方案(Cosmos DB)集成,以及无服务器 SQL 池如何在 Azure Synapse Link for Cosmos DB 的帮助下帮助优化分析工作负载。
感谢阅读!
Power BI:创建堆积漏斗图
Power BI 不支持堆积漏斗图。使用这个技巧,你可以创造一个。

资料来源:Pexels.com
问题是
Power BI 提供多种可视化功能来满足您的数据需求。尽管 Power BI 总体上是一个非常成熟的产品,但它的大部分局限仍然在可视化部分。这些限制之一是漏斗图。
Power BI 具有内置的漏斗图功能,但是不能向漏斗图添加类别。换句话说,每个条形只能有一种颜色,如下图所示。
在官方 Power BI 社区的帖子中,你可以看到其中一个用户请求类似的解决方案。在本教程中,我将使用他的图表作为灵感。

本机漏斗图目前不支持类别颜色。
尽管第三方视觉或 D3 可能是可行的,但我的客户不想使用其中任何一个,所以我不得不自己想出一个解决方案。
修复
在这次演示中,我将使用如下数据。为了便于演示,我简化了结构,但是您可以使用不同的结构,或者在 Power Query 中简单地转换数据。

原始数据。
第一步:计算总数。
我们需要做的第一件事是添加一个计算总数的列,这样我们就知道条形的总宽度。这可以通过一个简单的公式来实现,如下所示。

添加了包含所有类别合计的计算列。
第二步:计算余数
这个解决方案的诀窍是,我们实际上是在构建一个规则的堆积条形图,但是我们通过隐藏“剩余”类别使它看起来像一个漏斗图。

粉红色区域已被隐藏,使图表看起来像漏斗图。
使用这个 DAX 脚本,我们可以计算余数(左图中的粉色区域)。
Remainder = VAR longest_bar = CALCULATE ( MAX ( Recruitment[Total] ), ALL ( Recruitment ) ) RETURN( longest_bar — MAX ( Recruitment[Total] ) ) / 2
第三步:制作图表。
现在我们要做的就是把这些碎片拼在一起。只是一个具有以下字段设置的堆积条形图;

“字段”窗格中的“剩余部分”已被重命名为“城市”。
现在剩下的就是将剩余的颜色改为白色,将其名称改为您的标题图例,瞧:

最后的结果。
结论
像所有的软件解决方案一样,Power BI 也有它的局限性,但是只要有点创造力,就有可能绕过其中的大部分。使用这个“技巧”,您可以为您的数据解决方案创建一个堆积漏斗图。
但是请记住,这种粗糙的解决方案有一些限制;
- 您的用户将能够点击隐藏类别
- DAX 需要额外的资源(尽管在这种情况下非常少)
- 您可能需要转换您的数据
总之,这似乎是一个不错的解决方案,我的客户终端用户很高兴。希望也能帮到你。如果你知道任何更好的解决方法,请在评论中告诉我。
关于我:我叫布鲁诺,是一名数据科学顾问。你可以通过我的网址与我联系:https://www . zhongtron . me
Power BI:筛选器与行上下文
一个简单的概念(用火车)。

马库斯·斯皮斯克在 Unsplash 上的照片
我就是你所说的“超级通勤族”。我过去常常在早上 5 点醒来。早上 5:30 出门,赶火车,早上 8:00 到单位。这听起来像一场噩梦。我热爱我的工作,我热爱到足以做到这一点。后来疫情来了,这不再是我必须经常做的事情了。我对此表示感谢,但是这与 Power BI 过滤器和行上下文有什么关系呢?
你看,如果你知道你在找什么,过滤器和行上下文可以在常见的情况下看到。我乘火车。火车分为不同的车厢,每节车厢内有两种不同类型的座位。有靠窗的座位和靠走廊的座位。
我是一个非常视觉化的人。我就在这里把我的意思引出来。

作者图片
下面是我在 Excel 中画火车的尝试。在这里你可以看到这列火车有三节车厢和一个控制室。
那么,过滤器和行上下文在这里意味着什么呢?
让我们假设你是一名检票员。你需要在火车上找到一个家庭,了解他们在车费上花了多少钱。你知道他们坐在第一节车厢,他们都坐在靠窗的座位。
在这种情况下,你需要走到第一节车厢,走到靠窗的座位,合计他们的票价。这以最简单的形式展示了过滤器上下文和行上下文。
这里,过滤上下文将是头等车厢和靠窗的座位。行上下文是单个座位。他们花了多少钱的记录就是总数。

作者图片
如果你把它转换成能量 BI,这将是一个类似这样的度量。
A =
SUMX (
FILTER ( Train,
Coach == "First" && Seats == "Windows" && Family == "Smith" ),
[Ticket_Price]
)
在这里,您会告诉 Power BI 去第一节车厢,走到靠窗的座位,找到家庭组—这是过滤器上下文。下一个问题同样重要,如果不是更重要的话。
你想怎么处理这个?Power BI 去了地点却不知道做什么。它需要知道到达目的地后要做什么!这是行上下文。在这里用 SUMX,你是在告诉 Power BI 汇总,一排一排,或者用我们的比喻,一个座位一个座位,票价。
现在再举一个例子,如果我想知道二等车厢靠窗座位和靠过道座位的总价,该怎么办?
作为检票员,你会再次走向第二节车厢,走向靠窗和靠过道的座位。现在我们该怎么办?现在我们需要行上下文。我们划船干什么?我们需要把靠窗座位和靠过道座位的费用加起来。这是功率 BI 中的 SUMX。你将把靠窗座位的价格加起来,把靠过道座位的价格加起来,然后移到下一行,做同样的事情。

作者图片
基本上,这就是过滤器和行上下文。在 Power BI 中,你不再看火车,而是看桌子。您需要根据特定标准过滤表,然后进行聚合。

作者图片
在这里,当用户过滤时,很可能在视觉上,他们会根据教练的号码和风格,是否被占用等进行切片。这将是过滤器上下文。最后,要计算 Price*Promotion,它将应用于行上下文。
好吧,那么这些呢?除全部、全部选定和全部?这些术语是什么意思?
让我们回到我们的火车上。你是检票员,但现在你有了一个助手。这个助手将通过按组记录您的总数来帮助您。这就是 Power BI 中的 ALLEXCEPT 函数。
视觉上,你可以在火车上再次看到这一点。

作者图片
作为 Power BI 中的一个表,会是这样的样子。

作者图片
请将这些过滤功能视为您的助手,以改变您汇总数据的方式。ALLEXCEPT 将帮助您根据您的标准进行分组,ALL 将删除您指定的任何过滤器,ALLSELECTED 将使用用户选择来创建过滤器。
我就不一一赘述了,但概念是一样的,它们充当你任务的助手。用微软的术语来说,它们改变了你的过滤器的“上下文”。
我希望这有助于澄清 Power BI 中过滤器上下文和行上下文的含义。当然,这只是一列火车。这只是有助于说明一个简单的概念,你可能会看到许多列车和加入在同一轨道等。概念是一样的。
我将在下一篇文章中尝试解释这一点:)
我希望这对您的数据之旅有所帮助!
Power BI —如何在不到 1GB 的空间内容纳 2 亿行数据
非同寻常的任务需要非同寻常的解决方案。了解如何在 18 MB Power BI 中封装 2 亿行数据。

https://unsplash.com/photos/uu-Jw5SunYI
当你与技术打交道时,有时你需要接受这样一个事实,即存在某些限制,尤其是在涉及可用资源时。有时,业务需求很难或几乎不可能在当前的环境下实现。
我最近面临一个相当具有挑战性的任务——将 SSAS 多维解决方案移植到 Power BI。“问题”是客户机有一个 Pro 许可证(这意味着 1 GB 的模型大小限制),事实表中大约有 2 亿行(并且还在不断增长)!
我一看就知道,无论表有多窄多优化,都不可能在 1 GB 以下容纳 2 亿行!我已经写了关于数据模型大小优化的最佳实践,但是在这里根本行不通。
初步考虑
当我开始思考可能的解决方案时,第一个也是最明显的想法是将 Power BI 仅用作可视化工具,并与 SSAS 立方体进行实时连接。然而,这种解决方案有严重的限制——最明显的一个,你不能通过编写报告级别的度量来丰富你的语义模型——这是关键客户的要求之一!因此,Live Connection 立即被丢弃…
第二个显而易见的解决方案是使用 DirectQuery 模式将数据导入 Power BI。Power BI 将直接面向数据仓库中的事实表和维度表,而不是 SSAS 立方体。尽管有这些限制,直接查询看起来是一个完全合法的(也是唯一可能的)解决方案。这是我能想到的最好的…

https://www . pexels . com/photo/macro-outdoors-perspective-rocky-268953/
…直到我发现 Phil Seamark 的博客文章关于过滤聚合,它打开了一个全新的视角!这个关于创造性聚合的系列很棒,但是这个特定的概念给了我启发。
简而言之,过滤聚合
我不会深入这个概念的细节,因为菲尔比我解释得更好。本质上,这种想法是将“热”和“冷”数据分开,然后利用 Power BI 中的复合模型特性——在 Power BI 中导入“热”数据,而“冷”数据将留在数据仓库中,并通过直接查询来定位。最重要的是,在 Power BI 数据模型中应用聚合将带来更多好处,您将在后面看到这一点。
现在,我希望你们熟悉“热”和“冷”数据这两个术语,如果你们不熟悉的话,“热”数据是最近的数据,其中单词“最近”具有典型的“视情况而定”的含义。它可以是过去 X 分钟、小时、天、月、年等的数据。这取决于您的具体使用情况。
就我而言,在与报告用户交谈后,我们得出结论,大部分分析都集中在前 3 个月的数据上。因此,我们的想法是动态提取“热”数据,如今天之前 3 个月的数据,并将这些数据存储在 Power BI 表格模型中。报告消费者将从快速查询中受益,因为数据将被优化压缩并保存在内存中。另一方面,当需要分析旧数据(“冷”数据)时,Power BI 将直接进入数据源,并在查询时从那里提取数据。

https://unsplash.com/photos/_v0xhIT715g
找到“引爆点”,即分离“热”和“冷”数据的时间点,在这里至关重要。如果您选择对“热”数据使用较短的周期,您将受益于较小的数据模型大小(在 Power BI 中导入较少的数据),但是您将不得不忍受更频繁的直接查询。
但是,理论已经足够了,让我们进入 Power BI Desktop,我将向您展示在不到 1 GB 的空间内适应“2 亿行”事实表的所有步骤!
这一切是如何开始的…
如您所见,我的原始事实表包含关于体育博彩的数据,大约有 2 亿行。

作者图片
我还有两个简单的维度表——日期维度和运动类型维度。

作者图片
由于我已经在 Power BI 中导入了所有数据,我将检查数据模型背后的指标。如您所见,pbix 文件大小略低于 1 GB。而且,这个数字每天都在增加!

作者图片
让我们应用第一步,分离“热”和“冷”数据。您需要确保这两者之间没有重叠,但也没有间隙,这样您最终会得到正确的结果。
这里,我们将利用 Power BI 中的复合模型特性。我已经将我巨大的事实表一分为二:factBetSummaryHot 和 factBetSummaryCold。“热”表将保存前 3 个月的数据,在我的例子中,这个表现在有大约 430 万行。
我首先在直接查询存储模式下创建了“热”和“冷”表。然后,我将首先在“热”数据上构建一个聚合表,该表将使用导入模式。另一方面,“冷”表包含剩余的大约 1 . 9 亿行,它将使用直接查询模式。在此基础上,我将构建几个聚合表来覆盖不同的维度属性(例如日期和运动类型),并对这两个聚合表使用导入模式,因此 Power BI 将需要切换到直接查询,并将原始数据源仅用于那些对导入模式表不满意的查询!

作者图片
好了,现在让我们开始实现过滤聚合解决方案。我已经将“冷”表切换到直接查询存储模式,如下图所示:

作者图片
如果我现在检查 pbix 文件的大小,我会发现它只有 17mb!

作者图片
因此,我实现了最初的请求,将数据放在小于 1GB 的空间内,因为我设法将数据模型大小从 1GB 减少到 17MB,同时保留了原始数据粒度!
改进原始解决方案
现在,让我们在“热”数据上创建一个聚合表,并将该表的存储模式设置为 Import:

作者图片
我不会详细介绍聚合本身,因为有一篇由莎安娜米黄·沃森撰写的精彩文章,您可以从中了解更多关于这一强大功能的信息。
下一步是在新创建的聚合表和维度表之间建立关系:

作者图片
现在,这个报告的性能将取决于许多因素。如果用户试图与报告交互,并想要 3 个月以前的数据,Power BI 将通过存储在 SQL Server 数据库中的大约 1 . 9 亿行来搜索该数据,必须处理网络延迟、并发请求等问题。
比方说,我想分析总利润,这是利润列的简单总和。为了检索正确的数字,我需要对“热”和“冷”表中的值求和,编写以下 DAX 度量:
Total Profit Hot & Cold =
VAR vProfitHot = SUM(factBetSummaryHot[profit])
VAR vProfitCold = SUM(factBetSummaryCold[profit])
RETURN
vProfitHot + vProfitCold
现在,您可能会假设,如果我们只想查看前 3 个月的数据,Power BI 将只转到一个聚合表,并快速提取数据并呈现给我们。不对!
下图显示,尽管选择的日期在 1 月 31 日和 3 月 30 日之间,Power BI 还是用了将近 12 秒的时间准备好我的数据!而且,如果我们看一下性能分析器的数字,您会发现直接查询几乎占用了那段时间的全部时间:

作者图片
为什么 Power BI 需要应用直接查询,而我们已经有了一个很好的汇总表来处理这部分数据?嗯,Power BI 很聪明。其实很聪明!它足够聪明地在内部转换我们的 SUM over profit 列并点击聚合表,而不是“访问”它的直接查询孪生兄弟。但是,在某些情况下,它无法自行解决所有问题。在这个场景中,Power BI 将所有数据,甚至从“冷”表中直接查询,然后应用日期过滤器!
这意味着,我们需要“指示”Power BI 在数据检索之前评估数据过滤器!再次感谢 Phil Seamark 改进了这个方法:)
第一步是在 DAX 中设置时间的“临界点”。我们可以使用与 SQL 中完全相同的逻辑来做到这一点,当时我们将巨大的事实表分为“热”和“冷”:
*Time Line =
VAR maxDate = CALCULATE(
MAX('factBetSummaryHot Agg'[datePlaced]),
ALL()
)
RETURN
EOMONTH(DATE(YEAR(maxDate),MONTH(maxDate)-3,1),0)*
该方法将返回“热”和“冷”数据之间的截止日期。然后,我们将在改进版的总利润指标中使用这一结果:
*Total Profit =
VAR vProfitHot = SUM(factBetSummaryHot[profit])
VAR vProfitCold = IF(
MIN(dimDate[date]) < [Time Line] ,
SUM(factBetSummaryCold[profit])
)
RETURN
vProfitHot + vProfitCold*
这里,我们在每个场景中都从“热”表中返回 SUM over profit 列,因为它总是在导入模式下命中聚合表。然后,我们向“冷”数据添加一些逻辑—检查日期切片器中的开始日期是否在我们的“临界点”之前—如果是,那么我们需要等待直接查询。
但是,如果没有,那么…

作者图片
如你所见,现在我们在 159 毫秒内显示了我们的图像!而且,DAX 查询用了 12 毫秒!这次没有直接查询,如果我们在 DAX Studio 中检查查询,我们会注意到我们的聚合表是匹配的,即使我们在度量中使用了直接查询表作为引用!

作者图片
这很好,因为这意味着使用导入模式聚合表将满足最频繁运行的查询(针对前 3 个月的数据)!
处理“冷”数据
作为下一个优化步骤,我想在“冷”数据上构建一个聚合表,这可能会满足最频繁运行的用户查询,这些查询需要在“临界点”(超过 3 个月)之前提取数据。这将略微增加数据模型的大小,但会显著提高报告的性能。
因此,只有在一些特定的用例中,当用户需要查看“冷”数据中的一些高级粒度时,Power BI 才会直接向底层数据库发出查询。
让我们根据我们的维度,创建两个包含“冷”数据的聚合表。第一个将根据日期聚合数据:
*SELECT datePlacedID
,SUM(profit) profit
,SUM(countBet) countBet
FROM factBetSummaryCold
GROUP BY datePlacedID*
而第二个将汇总按运动类型分组的数据:
*SELECT sportTypeID
,SUM(profit) profit
,SUM(countBet) countBet
FROM factBetSummaryCold
GROUP BY sportTypeID*
让我们在这两个聚合表和维度之间建立关系,并为新导入的表设置聚合:

作者图片
通过设置优先级属性,您可以“指示”Power BI 首先使用哪个聚合表。优先级值越高,相应表格的优先级越高。
让我们回到我们的报告,一旦我们包括了“临界点”之前的数据,就检查我们的测量的性能:

作者图片
厉害!我将日期范围扩展到之前的 6 个月,我们在 198 毫秒内获得了结果,而 DAX 查询仅用了 40 毫秒!如果我们深入了解 DAX Studio,我们可以确认请求的两个“部分”,即“冷”和“热”数据,都是由导入模式聚合表提供的:

作者图片

作者图片
在我们得出结论之前,让我们最后看一下我们的 pbix 文件大小:

作者图片
太不可思议了:18MB!
结论
如你所见,我们成功了!最初的请求是将数据模型从 SSAS 立方体移动到 Power BI Pro workspace,而不丢失事实表中的 2 亿行中的任何一行!我们从近 1GB 开始,到 18MB 结束,同时保留了原始数据粒度,并且不影响 99%用例的报告性能!
非同寻常的任务需要非同寻常的解决方案,如果你问我,感谢 Phil Seamark 的绝妙想法,我们有一个解决方案,即使在 Pro 限制下也能容纳极其庞大的数据。
本质上,我们刚刚做的是,我们已经设法在 18MB 中容纳了 2 亿行…
感谢阅读!
成为会员,阅读 Medium 上的每一个故事!
功率 BI 建模
使用简单的概念建立模型

Bret Kavanaugh 在 Unsplash 上拍摄的照片
Power BI 的很多用户都是 Excel 背景。他们已经使用它很多年了,并使用它建立了仪表板和其他工具。一旦他们获得了 Power BI,他们就想进入表格的单个“单元”级别。
我以前也想这么做,但问题是 Power BI 是一个分栏工具,你必须以分栏的方式来考虑,才能最大限度地利用这个工具。
这是什么意思?
这意味着您必须知道将表连接在一起的公共列。这将要求你思考如何基于一个列来报告数字。这将要求您一起查看您的表,并查看哪些列可以链接到哪个表,以及如何根据这些表创建一个报告解决方案。
我应该使用哪张桌子?我要报告哪些表?
本文将向您介绍一个基本的数据模型。一旦你理解了这个概念,做模特的想法对你来说就不难了。
与 SQL 数据库或 Access 数据库不同,Power BI 不要求您规范化或“拆分”所有的表。它介于两者之间。
首先问自己这个问题,你想报道什么?
这个问题会告诉你很多下一步该做什么。它可能会告诉您需要使用什么表以及要过滤什么。
这是我的问题,
我想报告每天的库存和交易情况。每天都有销售额,所以我想知道我的销售额。
在这里,我们已经知道我们想要报告库存和销售。这些可能是我们的事实表。听起来好像有一个日期时间维度,所以我们可能需要一个时间表。
这些是我们想要使用的事实表,

作者图片
我们还需要一个表将 inventory 和 transactions 表连接在一起,这样我们就可以按日期进行筛选。我们需要一张日期表。这里我们可以使用
Date_Filter = CALENDAR( DATE(2021,04,01), DATE(2021,12,31))
Power BI 会自动为你创建一个表。结果会是这样一个表。

简单的日期表格-作者图片
在这个模型中,您可以看到我将交易日期和库存日期连接到日期表。

作者图片
这是一个你需要记住的基本模型。
在这里,日期表在日期列上连接库存和销售表。如果您要按日期过滤,它将从两个表中“抓取”数据。日期表筛选一对多关系中的库存和交易表。
现在让我们进一步扩展一下,
我想按类型和购买者查看库存和销售情况。
这里我们可以看出,我们可能需要再次过滤一些内容,包括库存和销售这两个表。我们有日期维度,但是这里的请求只是按类型。我将不得不创建一个表,该表接受两个表的唯一产品类型,以便对它们进行筛选。
在这里,您可以使用 Power Query 来选择 inventory 和 transactions 表中产品类型的各个列,追加它们并运行 remove duplicate 来创建过滤表。
在 DAX 中,您可以使用 DISTINCT 创建一个包含所有库存的唯一值的表。
Product_Type_Filter = DISTINCT(Inventory[Inventory]))
这是模型现在的样子,🍦 🍩🍰

作者图片
在这里,我可以按日期、按产品类型进行过滤,并且只看到购买它们的客户。

作者图片
同样,这是一个简单的过滤器,允许您过滤两个表以获得答案。
稍微说一下过滤方向也很重要。让我们再来看看这个模型,我在箭头处添加了。

作者图片
这些箭头是什么意思?
这意味着当用户使用日期筛选器或产品筛选器进行筛选时,因为这些筛选表与库存和交易表相连接,所以他们可以单向筛选。
按照与连接相同的方向进行过滤非常重要。
这里,我在 inventory 表中对 cake 进行了过滤,这不是一个过滤表,它将在 transactions 表中拉出一个冰淇淋的事务。正如你所看到的,如果你没有使用合适的过滤器,它会很快变得非常混乱。

作者图片
我希望通过这篇文章,您可以获得创建数据模型的基础知识。
记住这个。你建立的模型不一定很难。它只需要简单而有效。要让它起作用,它必须是你和其他人能很快理解的东西。
如果你不能产生一个被要求的报告,没有人会关心你的模型看起来有多优雅。
概括一下,
- 分析你的问题
- 确定你的事实表
- 创建过滤表来连接它们
看一看恒星的设置。

作者图片
这不是和我们讨论过的挺像吗?这就是著名的星型数据模型。
现在,如果你认识一个谈论星型模式和雪花的数据建模师,继续问他们过滤表和事实表在哪里。他们应该能马上告诉你。
如果没有,那么很可能有什么问题,我希望你不必成为使用这个模型的人,或者更好的是,重建它!
功率 BI 建模——技巧和窍门
一些有用的提示和功能,我希望我能早点知道

托德·迪默在 Unsplash 上拍摄的照片
拥有一个简单的数据模型是很棒的。你知道为什么吗?因为有一个简单的模型会让生活变得容易得多!就像下雨天有把伞。
你的模型简单了就好解释了。最重要的是,它更容易解决问题。
在本文中,我将介绍一个经过简化的模型,以及一些可以帮助您进行故障排除的工具。
这是我遇到的一个模特。

作者图片
位置 A、B、C 和 D 是包含销售日期、销售数量和销售人员的表格。
有一个很棒的日期表。
侧面还有两个维度表,用于区分高绩效者和其他人。
当然,你仍然可以像这样进行一些分析。但是请注意每个事实表(位置 A、B、C 和 D)是如何分开的?
这就是问题所在——您必须创建 4 个不同的度量来计算一个指标。(您必须创建销售位置 A、位置 B 等的总和的度量)。这不是有效的,而且很容易出错。
还有维度表的问题,如果这些表是为了通过表现来区分个人,那么可以将它们合并成一个主题或维度表。
我们该如何简化这一点?
我们可以通过组合事实表和维度表来简化这一点,如下所示。

作者图片
看到这个模型有多简单了吗?
两种合并表格的方法。
让我们来谈谈合并数据表的两个选项。
有两个选项,DAX 和 Power Query。
使用 DAX 将表追加到一起。
不建议这样做,但不管怎样我们还是要穿过去,因为这样最快;您不必将任何数据重新加载到模型中。
要在 DAX 中做到这一点,您可以使用 UNION 函数并将表追加到一起。将它与前面的维度表连接起来。
Combined_Sale_Tables = UNION(Table_A,Table_B,Table_C,Table_D)
现在你不需要 4 种不同的方法来衡量销售了!只有一个。
这是模型现在的样子。

作者图片
有一个小问题。
组合事实表没有位置名称,因为它们本来就不在那里。
我们可以通过使用 SUMMARIZECOLUMNS 来解决这个问题。

作者图片
缺点是您的模型中会有多个表。
当然,你可以隐藏它们,但它仍然杂乱。另外,除非特别说明,否则其他用户可能不知道您为什么决定将它们附加在一起。
最大的缺点是,如果列名和列号不同,您可能需要做一些工作。
使用 Power Query 将表追加到一起
现在让我们使用下面的 append 方法来尝试 Power Query。
电力查询>首页>追加查询

作者图片
这种方法的优点是适应性更强;无论您是否有列标题困难、额外的列或数据格式问题,Power Query 都可以很好地处理它们。
缺点是速度,您需要将这些数据重新加载到模型中。如果你有一个大模型,这可能是一个令人头痛的问题。
DAX 和 Power Query 都是简化事实表的好选择,但是如果您手头拮据,可以尝试 DAX,但是如果您有一点时间,可以使用 Power Query。
使用这两种方法中的任何一种,您都会得到相同的模型。

作者图片
让我们让生活变得更简单。
现在,让我们来看看两个非常有用的功能,它们会让你的生活更轻松。
这些是 INTERSECT,EXCEPT 和 VALUES 函数。
使用带有值的 EXCEPT 函数来标识异常。
假设 HR 找到你说:“我这里有一份销售员工的名单,你能看看他们是否都做了销售吗?”
这是发给你的表格。

作者图片
现在让我们用除和值☺
Table_EXCEPTIONS = EXCEPT(Sale_Person_All,VALUES(Combined_Sale_Tables[Sales Person]))
VALUES 在 Combined_Sales_Tables 中创建一列唯一值(销售人员姓名), EXCEPT 针对发送给我们的表创建一个左反联接。
这就是结果。

作者图片
使用此功能,可以识别三个名称。
使用带值的 INTERSECT 函数来标识交点。
类似地,如果你与 INTERSECT 交换代码,你将能够在 HR 发送的表格中看到每一个成交的人。
Table_INTERSECT = INTERSECT(Sale_Person_All,VALUES(Combined_Sale_Tables[Sales Person]))
这就是结果

作者图片
概括一下,
建模 —如果您看到多个事实表或维度表四处浮动,请尝试合并它们。
表格检查 —使用 EXCEPT 和 INTERSECT with 值查找表格之间的差异。
数据建模很有趣!
尽可能保持简单。结果会让你大吃一惊!
暂时就这样了。在您的数据之旅中保持安全并祝好运。
Power BI + Power Automate —文件监控
使用表格功能监视 SharePoint 文件并使用 Power Automate 设置警报

照片由 Murai 拍摄。hr on Unsplash
你使用 SharePoint 吗?就我个人而言,我并不太喜欢它。因为疫情,我不得不在过去的一年里经常使用它。至少可以说,我已经爱上了它。
当我不得不要求各单位在 SharePoint 上填写工作簿时,SharePoint 证明了它的有用性。总共有 50 本练习册。如果我能把所有的工作簿放在一个文档中,事情会简单得多,但是由于工作的性质,它们必须分开。
问题是,当用户与工作簿交互时,额外的列、空白行和数据问题开始出现。(有些人只是喜欢在不需要的时候添加数据)。我不能对工作簿本身进行完全限制,因为这样会禁用排序和过滤。我必须找到一种方法来监控工作簿,这样如果有错误,我可以立即得到通知。

作者图片
解决方法是什么?至少有 50 个工作簿需要进行数据质量监控。
Microsoft Power Query 可以做到这一点!如果您知道您的表函数,Power Query 可以直接访问 SharePoint 工作簿并监视它们。
如果你是 Power BI 的新手——这里有一篇关于如何建立模型的快速文章。
我将概述我对这些工作簿中每一个的问题,以及可以解决这些问题的 M 表函数。
关于每个工作簿的问题
这些都是简单的问题,但如果在开始时回答不当,以后处理起来就真的很头疼了。
- 每个工作簿中有多少行?
- 每个工作簿中有多少列?
- 标识栏中有多少空格?
- 在标识栏中,它们都是不同的吗?
- 如何使用 Power Automate 为正在更正的工作簿向自己发送预警通知?
答案
首先,让我们使用“连接到 SharePoint 站点”选项将 Power BI 连接到 SharePoint 站点。这可以通过下面的新源>共享点文件夹>文件夹名称找到

作者图片
现在要使用我们的表函数,(有很多,你可以在这里找到它们),我们必须将二进制文件转换成表。要使用表函数,您需要表数据类型。
这可以通过使用 Excel 轻松实现。工作簿 功能。但是,如果您的工作簿更复杂,您必须先创建一个函数来转换它们。你可以在这里阅读如何在 Power BI 中构建函数。

作者图片
二进制文件现在是表格格式。让我们开始回答问题。
每个工作簿有多少行?
我们可以用 表来回答这个问题。RowCount 函数函数。
我将添加列并添加到 表中。RowCount 函数函数。
这就是结果。
一般模式是表。函数([您的表列])

作者图片
每个文件有多少列?

作者图片
我将应用与上面相同的方法,但是使用table . column count .
看起来总共有 14 列和行,因工作簿而异。
每个文件中有多少个空白标识?
我们将使用一个新的列,并在 Column1=null 中添加,但是因为我们希望看到有多少行是这样的,所以我们将把它包装在 表中。行数

作者图片
一开始就值得关注这些文件。
此后要找到丢失的记录将是一件非常头痛的事。
第一次把事情做对不会有坏处。
的标识栏,怎么都是截然不同的?
同样的过程,但是用一个函数调用 列表。分明的 。这个有点棘手,因为它需要一个列表数据类型作为输入。
在 power query 中,下面的[column_check]是一列表,而[Column3]是每个表中的一列。

这里我用的是 List。is distinct([表格][列])。
如何识别一个条目?
这与上面的模式相同,添加一列并使用右表函数。这里我使用了和表。包含和

最后,您将得到一个与此非常相似的简单表格。

如何使用 Power Automate 为正在更正的工作簿向自己发送警告通知?
我不确定您是否使用 Power Automate,但它绝对值得一试。该工具有助于监控和简化工作流程和审批。
这是一个简单的自动化过程,帮助我跟踪谁访问了 SharePoint 上的这些文件。
我决定为此创建一个流程的原因是,很难找到访问过特定文件夹的历史用户列表。我也不想白天处理数据转储。
这是一个简单的 3 步过程。
首先,Power Automate 查看 SharePoint 文件夹 >获取访问过该文件夹的个人的姓名和电子邮件>将其添加到 SharePoint 上的 Excel 表格中>用通知提醒我

作者图片
这个过程是我在最后设置的,当时我已经完成了数据清理和验证,并发现了错误。然后,我将设置它,以便在工作簿被访问并进行更正时提醒我。
第一步

作者图片
第二步

作者图片
您必须首先设置一个简单 Excel 表,这样 Power Automate 将知道在哪里记录您的数据。
我不必将这些元素编码到我的流中,这些都是拖放的,这太棒了!
还有其他可用的属性,如个人的个人资料图片和部门等。
第三步

作者图片
最终结果是,当有人访问该文件夹进行更正时,会出现一个警告,同时还会出现一个跟踪用户信息的 Excel 工作簿。
这是记录某人何时访问文件夹的工作簿

作者图片
万一有人回来说:“我从来没看过你的邮件,也没看过你的文件夹。”
你可以说——“是的,你在 09 年 12 月 9 日访问了这个文件夹”。
现在有一个更简单的方法来监控人们是否访问了一个文件夹,缺点是你没有一个 Excel 跟踪文档。如果这种级别的监控对您来说不重要,那么您可以使用下面的方法。
导航到您的文件夹,右键单击并选择提醒我。

作者图片
您可以在文件夹级别对此进行设置,以便每当有人访问该文件夹时,您都会收到一封电子邮件。

作者图片
然后,您可以选择发送方式,是否希望通过短信或电子邮件向您发送提醒,更改类型和持续时间等。
希望这对你的旅程有所帮助!
我讨厌做“老大哥”,但有时一开始就需要监控,以确保你获得尽可能准确的数据。最后还是值得的。
在本文中,您已经了解了使用 Power Query 连接到 SharePoint、使用表函数监控数据质量,以及最后使用 Power Automate 在有人进行更正时提醒您的一些基本知识。
这里有一些有用的链接-
Microsoft Power Query 表格函数 —一旦您将文件转换为列格式的表格,所有这些函数都可供您使用。试着把它们嵌套起来,组合起来。太棒了!
微软 Power Automate——使用模板并调整它们。这比从头开始创建自己的要好得多。
祝您愉快,并保持安全!
Power BI 和 Synapse,第 1 部分:可能的艺术
在这个关于 Power BI 和 Synapse Analytics 集成的博客系列的第一部分中,了解 Synapse 中的无服务器 SQL 池带来的巨大灵活性。

https://unsplash.com/photos/7JX0-bfiuxQ
通过在 2019 年底推出 Azure Synapse Analytics,在数据处理方面开创了一个全新的视角。一些核心概念,如传统的数据仓库,受到了更多的审查,而在数据爱好者意识到 Synapse 带来的新功能后,各种新方法开始涌现。
【Synapse 不仅对数据接收、转换和存储选项产生了巨大影响,它还为数据服务和可视化提供了全新的可能性!
因此,在这一系列博文中,我将尝试探索 Power BI 如何与新平台协同工作。作为 Power BI 开发人员,我们在使用 Synapse 时有哪些选择?在哪些数据分析场景中,Synapse 将发挥优势,帮助您实现(im)可能性?什么时候您想利用 Synapse 中的创新解决方案,什么时候您会更好地坚持使用更传统的方法?使用 Power BI — Synapse combo 的最佳实践是什么,在最终决定采用哪种方法之前,您应该评估哪些参数。
完成后,我相信您应该会更好地理解 Power BI 和 Synapse 之间集成时每个可用选项的“优缺点”。
azure Synapse Analytics——新成员,还是…?
由于 Power BI 已经是一款成熟而知名的产品(去年 7 月庆祝了它的 5 岁生日),在我们深入了解 Power BI 和 Synapse 之间的关系之前,我相信我们需要花一些时间来了解这项“更年轻”的技术…
正如我已经提到的,Synapse 已经在大约 2 年前推出(2019 年末。)并于今年 3 月正式上市。官方说法是,它继承了 Azure SQL 数据仓库(SQL DW ),是 SQL DW 的一个发展。
那么,如果我们将 Synapse 视为“新上市的公司”,或者仅仅是现有产品的品牌重塑,这公平吗?
在回答这个问题之前,让我们先来看看传统的数据仓库架构:

作者插图
在典型的传统数据仓库场景中,您将从多个不同的来源收集数据,执行一些转换、数据清理等。在将整合的数据放入关系数据仓库之前。这是您的报告解决方案的焦点,是您的数据的“真实的单一来源”,您可以使用一系列工具从那里构建各种报告,如 Crystal Reports、SSRS 或 Power BI,如果我们谈到微软的解决方案的话。您还可以使用 SSAS 构建一个额外的语义层来创建“多维数据集”(包括表格和多维数据集),然后使用上面提到的报告工具(不要忘了优秀的旧 Excel)将这些多维数据集作为目标。
现在,有了 Azure Synapse Analytics,您可以在一个屋檐下获得所有这些(以及更多)功能!即使默认情况下 Synapse 中没有包含 SSAS,也可以将其作为外部服务进行链接。
为您的数据提供瑞士刀
你能认出下图中的物体吗?

没错,就是大名鼎鼎的“瑞士刀”。你可以切纸、开瓶葡萄酒或啤酒,或者切一些小物件——所有这些都只需要一个工具!
所以,如果你问自己——瑞士军刀和 Synapse 到底有什么共同之处?!好吧,你可以把 Synapse 想象成一个可以满足你所有数据需求的单一工具。
您需要从多个不同的来源导入数据吗?Synapse 可以做到这一点。在提供数据之前,是否需要对数据进行转换?Synapse 也能做到。你需要存储数据吗?Synapse 可以帮你做到。需要查询非关系型数据,甚至直接查询文件吗?Synapse 可以做到这一点。
现在要小心了:您需要使用普通的老式 T-SQL 查询非关系数据,甚至是直接查询文件吗?Synapse 可以做到这一点!怎么了???!!!!是的,您可以编写 T-SQL 来查询 CSV、JSON 或 parquet 文件中的数据。但是,在这个系列的后面会有更多的介绍。需要建立机器学习模型吗?Synapse 可以帮你做到这一点。最后,您是否需要直接从 Synapse 创建您的 Power BI 报告?是的,那也是可能的!
您可能已经注意到,Synapse 是针对所有数据任务的一站式服务。
Azure Synapse Studio —所有活动的入场券
当我解释一些技术性的东西时,我喜欢和非技术性的东西做比较,这样没有技术背景的人就能理解基本的概念。

现在,你可能知道当你第一次去一个著名的旅游城市旅游时,你会想参观所有重要的地方:博物馆、观光景点、当地景点……而这些城市中的大多数都会给你一张“城市卡”。你可以用一张卡进入所有的主要城市景点,这样可以节省你的时间和金钱。
你可以把 Azure Synapse Studio 想象成“那种”卡。这是一个适用于所有不同任务的统一工作区,包括数据准备、数据仓库、监控和管理资源等。
Synapse Workspace —了解核心组件
现在,我们准备深入了解 Synapse 工作区的核心组件。
最重要的部分是分析引擎。Synapse 工作区中有三个不同的引擎。其中两个是 SQL 风味的(专用 SQL 池和无服务器 SQL 池),第三个是基于 Apache Spark 的(Apache Spark Pool)。
此外,其中两个是预配置的(专用 SQL 池和 Apache Spark 池),而第三个(无服务器 SQL 池)的工作方式类似于无服务器解决方案。用最简单的方式,它看起来像这样:

作者图片
在这个博客系列中,我们将不分析 Spark 池,因为我们的重点将是基于 SQL 的解决方案,尤其是无服务器 SQL 池。
无服务器 SQL 池—无需任何硬件即可发挥全部功能!
一旦您创建了 Synapse 工作区,您将在您的 SQL 池下看到一个无服务器 SQL 池:

作者图片
在我看来,这是 Synapse 的革命性特征之一。从本质上讲,无服务器 SQL 池能为您做的事情几乎是无与伦比的:您可以直接在您的数据湖中查询数据,而不需要将它转移或复制到任何地方!而且,您可以编写普通的老式 T-SQL 来查询数据湖中的数据!是的,包括 CSV、JSON 和拼花文件!
由于它是一个无服务器系统,您不需要设置任何类型的基础设施或集群。一旦创建了工作区,您就可以开始查询数据了!无服务器 SQL pool 作为一个独立的聚合库服务,所以保留资源没有成本。您只需为您的查询处理的数据量付费(在撰写本文时,费用为 5 美元/1 TB)。请记住,在规划工作负载时,最小计费量是 10 MB。这个最低阈值不适用于元数据查询,所以如果您执行类似以下的操作,您根本不必担心会被收费:
SELECT * FROM sys.objects
有了这个工具,你可以思考无数种可能的方法:
- 您可以对数据湖中的数据快速执行即席查询,然后再决定从数据湖中获取更多信息的最佳方式
- 您可以构建一个额外的逻辑层,一种在原始或非关系数据之上的“抽象”数据仓库,而无需将数据移动到物理存储中
- 您可以直接在数据湖中转换数据,并从那里直接使用它(例如,使用 Power BI)
如前所述,您可以使用 T-SQL 直接从数据湖中查询数据。不仅如此!还可以编写 T-SQL 从 Spark 表和 CosmosDB 中检索数据。
通过使用扩展版本的 OPENROWSET 函数,可以编写用于查询非 SQL Server 数据的 T-SQL。由于非原生用法,关于 T-SQL 的一些功能,显然有一些限制。
我强烈建议阅读詹姆斯·塞拉博客上的这篇文章,在那里他收集并回答了许多关于无服务器 SQL 池的问题。
这里还有一个关于无服务器 SQL 池最佳实践的概述。
Abrakadabra —使用 T-SQL 读取拼花文件
是的,我知道这听起来像是我们需要一根魔杖来使用普通的 T-SQL 直接从 parquet 文件中查询数据…但是,这是可能的!相信我,你不需要成为一个魔术师——一些基本的 T-SQL 知识就足够了。

作者图片
在这个例子中,我使用了一个样本纽约出租车数据集。数据存储在一个 parquet 文件中,但是我可以在我的 T-SQL 语法中使用 OPENROWSET 函数来查询大约 15 亿条记录数据集,就像它存储在我的 SQL Server 数据库中一样!多酷啊!
此外,我可以在此基础上进行扩展,执行各种聚合、分组、where 子句…

作者图片
如果这还不够,看看这个:

作者图片
下面是在两个 parquet 文件上运行的 T-SQL 代码:
WITH taxi_rides AS
(
SELECT
CAST([tpepPickupDateTime] AS DATE) AS [current_day],
COUNT(*) as rides_per_day
FROM
OPENROWSET(
BULK 'https://azureopendatastorage.blob.core.windows.net/nyctlc/yellow/puYear=*/puMonth=*/*.parquet',
FORMAT='PARQUET'
) AS [nyc]
WHERE nyc.filepath(1) = '2016'
GROUP BY CAST([tpepPickupDateTime] AS DATE)
),
public_holidays AS
(
SELECT
holidayname as holiday,
date
FROM
OPENROWSET(
BULK 'https://azureopendatastorage.blob.core.windows.net/holidaydatacontainer/Processed/*.parquet',
FORMAT='PARQUET'
) AS [holidays]
WHERE countryorregion = 'United States' AND YEAR(date) = 2016
)
SELECT
*
FROM taxi_rides t
LEFT OUTER JOIN public_holidays p on t.current_day = p.date
ORDER BY current_day ASC
因此,我们创建了两个 cte,使用 WHERE 子句过滤数据、聚合函数,最后像查询“普通”SQL Server 数据库一样连接 cte…而且,别忘了,我们是直接从两个 parquet 文件中检索数据!
不,你不需要成为哈利波特来做到这一点——在 Synapse Workspace 中使用无服务器 SQL 池是你应该执行的唯一“技巧”!
结论
这只是对 Synapse Analytics 的简单介绍,Synapse Analytics 是针对您的数据工作负载的一体化解决方案。我们并没有涵盖该平台中的所有概念和功能,我们的想法是让您开始使用 Synapse 与 Power BI 协同工作。
现在,我希望您已经对 Synapse 提供的主要功能有了更好的概述和更全面的了解,并且一旦您开始使用 Synapse 构建您的 Power BI 报告,您就可以利用这些功能。
慢慢来,探索 Synapse,尝试不同的方法来处理您的数据工作负载,然后我们一起进入下一阶段,Power BI 及其与 Synapse 的集成(尤其是与无服务器 SQL 池的集成)将是我们的关注点!
感谢阅读!
Power BI 提示和技巧:自定义排序
Power BI 提示和技巧系列的一部分
大约两年前,我开始使用微软的 Power BI。一路走来,我学到了一些更高效做事的技巧和诀窍。这个系列是为了方便初学者而设计的,所以如果你有什么不明白的地方,请告诉我。我也很乐意听到你发现有用的额外的提示和技巧!
您可以直接跳到解决方案,并将它们应用到您自己的数据中,或者您可以跟随一起了解这个示例数据。

照片由 Avel Chuklanov 在 Unsplash 拍摄
问题是
假设我正在处理一所(小)大学的数据,他们想知道每个年级有多少学生。很好,这听起来像是一个制作簇状条形图的机会。
我将“年级”拖到轴上,将“学号”拖到值上,得到如下结果:

我可以按学号(学生人数)或年级的字母顺序对图表进行排序。但是我希望我的数据按照时间顺序排序,学生从大一开始,然后是大二,大三,最后是大四。
啊!当我有一段时间没有这样做时,我几乎总是首先尝试添加一个计算列,然后按此排序。但是这给了我们一个错误,因为我们引入了一个循环依赖。
但是不要害怕!有几种可能的策略可以按照我们想要的任何顺序对数据进行排序。
解决方案 1:创建一个附加表
当其他人正在管理排序应该如何出现时,或者如果您有许多不同的级别,这是一个很好的方法。您可以在 Power BI 中直接创建该表:

或者,您可以创建一个额外的 Excel 工作簿或在现有工作簿中添加一个工作表。我通常更喜欢 Excel 格式的表格,因为我觉得在需要的时候更容易更新。我们的表应该是这样的:

现在,我们将把这个表添加到 Power BI 文件中,并在我们的模型中创建一个关系。

因为我们创建了一个单独的表,所以现在我们可以使用 RELATED 创建一个计算列,而不会遇到循环依赖问题。

这个栏目的表现和我们预期的一样(高年级是“1”,低年级是“2”,等等)。).最后,我们告诉 Power BI 按照我们的排序顺序列对我们的等级列进行排序。同时选择等级列>列工具>按列排序>排序顺序。

瞧啊。

而且现在做更新超级容易。假设我们为五年级学生增加了一个超高年级。我们所要做的就是更新我们的 Excel 文件,保存它,并刷新 Power BI。
解决方案 2:使用超级查询
如果不太可能改变我们想要的排序方式,这是一个很好的选择。我们仍然可以使用这个选项进行更新,只是对于那些不太熟悉 Power Query 的人来说,它不太方便使用。
使用此选项,我们将通过选择“转换数据”选项进入超级查询编辑器。

我们只需点击鼠标就可以做到这一点,所以不要惊慌,但如果你愿意,我们也可以使用 M 来做到这一点。无论哪种方式都很棒!
如果您想要指向并单击,请在超级查询编辑器中选择“转换”功能区下的“条件列”按钮。

然后,您可以为每个级别创建一个附加子句:

现在,就像我们对另一个解决方案所做的那样,您所要做的就是退出超级查询编辑器,并使我们的新条件列成为排序“Grade”所依据的列。

你有它!当您需要以自定义顺序对列进行排序时,有两种解决方案:一种是使用附加表和相关函数,另一种是在 Power Query 中使用条件列。
你喜欢哪种解决方案?你有更好的解决方案可以分享吗?
继续用你的头撞墙——这样会好得多!
珍娜·伊格尔森 我的背景是工业组织心理学,我在人物分析中找到了自己的家。数据使我的作品变得生动。我主要使用 Power BI,但偶尔也会使用 Tableau 和其他工具。我很想听到更多关于你的旅程!在Linkedin或Twitter上联系我。
Power BI —使用时间智能
基于时间的报告的简单概念

Power BI 简化了时间智能的使用。一旦您理解了这些概念,您将能够进行各种复杂的基于时间的报告。
为了让时间智能在 Power BI 中工作,您需要一个日期表。没有它肯定不行。一个简单的日期表可以在这里找到。
在本文中,我将介绍 Power BI 中一些非常有用的时间智能功能,以及使用它们的步骤。
那么使用这些时间智能功能的步骤是什么呢?
- 构建一个日期表
- 构建通用衡量标准
- 将时间智能函数添加到一般度量中
大概就是这样。我们来分析一下。
构建一个日期表
这是我用下面的创建的一个简单的日期表格。
DATE_TABLE = CALENDAR(DATE(2019,01,01),DATE(2022,01,01))
在这段代码中,Power BI 生成从 2019 年 1 月 1 日到 2022 年 1 月 1 日的所有日期。这就是使用 DAX 创建日期表的方法。
这里的关键词是 all ,记住时间智能功能不会处理缺失的日期。
在这里,我还添加了一个额外的列,称为“月 _ 年”,这是通过添加下面的新列来完成的。
Month_Year = FORMAT(DATE_TABLE[Date], “MMM-YYYY”)
下面是日期表格的外观。也可以随意尝试一下 FORMAT 函数。Power BI 为日期列提供了许多不同的格式样式。

作者图片
下面是添加了 DATE_TABLE 的模型的外观。
使用日期将 date_TABLE 的 DATE 列与 FACT_TABLE 连接起来。这是一个一对多的关系。
如果你想知道为什么这个模型是这样建立的,看看这篇文章。

作者图片
现在进入下一步。
构建一个通用度量。
您在这里建立的度量很重要。永远记住你要回答的问题。
出于演示的目的,我将创建一个没有任何过滤器的通用销售额。
Sales_Measure = SUM(FACT_TABLE[Totals])
这是它在 Power BI 中的一个简单表格中的外观。

作者图片
现在是最后一步,也是最有趣的部分。😀
向度量添加时间智能函数。
我第一次想用的智能函数是 SAMEPERIODLASTYEAR。
SAMEPERIODLASTYEAR_Measure = CALCULATE([Sales_Measure] , SAMEPERIODLASTYEAR(’Date_Table’[Date]))
在这里,我可以并排看到去年同期的销售额。

作者图片
在这里,2020 年 4 月的销售额是 509,2021 年 4 月是 4444。
SAMEPERIODLASTYEAR 度量从去年开始提取销售额,这样您就可以不用太多代码进行简单的比较。
这里有一个巧妙的技巧!
您可以用一个新的时间智能函数替换以前的函数,以创建一个新的度量值来度量不同的东西:)

作者图片
我想换成按月累计和按年累计。

作者图片
我再一次用另一种方法来代替时间智能方法。第一个是本月至今,另一个是年初至今

作者图片
在这里,您可以看到按月和按年累计的总和。
现在你已经对这些时间情报工作有了一个基本的概念,你可以在它的基础上进行构建了!
现在,我需要今年迄今为止的总销售额,并将其与去年同期进行比较。我想比较 2020 年和 2019 年的年度累计总数。
在这里,因为我们已经建立了“按年总计”度量,所以我们要做的就是像这样添加度量。
Total_Sales_YTD = CALCULATE([Total_Ses_YTD], SAMEPERIODLASTYEAR (DATE_TABLE[Date]))

作者图片
现在,视觉显示去年的运行总数。
一个被低估的函数是 DATEADD。只要你知道你想在多长时间内旅行,它允许你在任何时间方向旅行。🛸
在这里我也可以写,
Total_Sales_YTD_LY__DATEADD = CALCULATE([Total_Sales_YTD],DATEADD(DATE_TABLE[Date],-1, YEAR))
这将是结果。

作者图片
DATEADD 允许您按年、月或日移动。唯一的区别是,您必须计算持续时间,而不是调用像 PREVIOUSYEAR 或 SAMEPERIODLASTYEAR 这样的通用函数。注意不要输入错误的时间间隔。它发生了!
概括地说,下面是使用 Power BI 中的时间智能功能的步骤。
- 建一个日期表,用它做维度/过滤/主题表,很重要。如果没有日期表,所有这些时间智能功能都将不起作用。
- 建立一个通用的衡量标准,考虑一下,看看这个衡量标准的结果是否能回答你的业务问题。
- 为通用度量添加一个时间智能函数,例如用 DATEQTD 替换 DATEYTD 当然会得到不同的结果,但模式是一样的。
在您的数据之旅中保持安全并祝您好运!
数据故事叙述的降维能力
使用降维能力的最好方法之一是创建一个数据故事

米格尔·布鲁纳在 Unsplash 上的照片
哦不!再来一篇降维的文章!但是等等,这不一样。有很多文章是从技术角度来解释降维的。但是,我将重点介绍如何以创新的方式使用降维的力量。您还会更好地体会到降维的强大功能,因为我将重点介绍它的使用,而不是技术细节。
我将带您了解如何使用降维的力量来讲述数据。
伟大的说书人会告诉你弄清故事情节的重要性,这是讲故事最重要的任务之一。它需要弄清楚开头、中间和结尾。

典型的故事情节(来源——作者提供的图片)
所以你可能会问,这和降维有什么关系?嗯,答案是
降维可以帮助您确定数据故事的开头、中间和结尾
使用现代最好的故事之一的数据集来说明这一点的最佳方式之一…漫威电影宇宙(MCU)。所以复仇者集合!这个数据集是关于 MCU 中的每一个超级英雄和他们的超能力。这个数据集太大,无法说明,所以这里只是一个示例视图。

漫威字符数据集-按作者分类的图像
现在让我们假设你必须使用这个数据集,讲述一个关于漫威人物的数据故事。这是一个巨大的数据集,几乎有 200 个字符。你可能会惊讶地知道,MCU 已经识别了差不多 175 种超能力。每个角色都可以拥有一种或多种超能力。这意味着您面临着查看 200 行* 175 列的挑战。
那么,如何弄清楚如何开始、中间和结束你的数据故事呢?降维有助于将这些庞大的数据压缩成一个 1D 故事情节。

降维有助于将这些庞大的数据压缩成 1D 的故事情节(来源——图片由作者提供)
下面是降维的结果。

降维结果(图片由作者提供)
一旦你看到所有的角色排成一行,你就会开始感受到掌控一切的力量!你不会被 175 维度吓倒。
每个点代表一个字符。让我们看看这些人物是什么。我们也可以叠加一张雷达图,它也将给出关于这些角色的超能力的额外信息。让我们从第一个点开始,如下图所示。

故事线中的第一个角色(图片由作者提供)
我们在这里看到最左边点对应于字符 Rocket 浣熊。你还可以看到显示超能力的雷达图。我们看到火箭浣熊有一个面向动物的超能力,也是一个武器大师。
现在让我们浏览其他初始点。你观察到这些点对应着黑豹和蜘蛛侠。

浏览初始点(作者图片)
所以很明显降维把所有有动物导向力的角色都放在了行首。这真是太棒了,我们看到降维并没有选择一些随机字符放在行首。这已经给了我们一些如何构建数据故事的提示。我们可以从基于动物力量的角色在 MCU 中的重要性说起。
现在让我们把注意力集中在这条线的中间。我们看到了角色猩红女巫。我们可以观察到她拥有心灵运输和魔法的超能力。

线的中间对应于具有传送和魔法的角色(图片由作者提供)
让我们观察一下位于这条线中间的其他一些字符。你会看到像托尔和奇异博士这样的角色。

线的中间对应于具有传送和魔法的角色(图片由作者提供)
这意味着降维选择了具有瞬移、魔法等异能的人物在中线。这真是太神奇了!我们的故事情节开始有意义了。我们可以从面向动物的能力的重要性开始,然后转移到具有魔法和心灵运输能力的角色!
现在让我们来看看角色故事情节的最后部分是什么。我们观察惊奇队长,她有多重超能力,比如心灵运输、魔法、能量操控和通用语。对于好奇的人来说,通用语是说、理解和破译任何和所有语言的能力。

中线对应具有多重能力的角色(图片由作者提供)
让我们观察一下这一行末尾的其他一些字符。我们看到灭霸和奥丁,他们都有多重超能力。所以降维把拥有多重超能力的角色放在了最后。

中线对应具有多重能力的角色(图片由作者提供)
所以我们的数据故事大概是这样的
从描述动物导向的力量在单片机中的重要性开始,并举例说明,如黑豹和蜘蛛侠。
然后谈谈心灵运输和魔法如何在吸引观众方面发挥核心作用。举例说明,如红色女巫,雷神,奇异博士。
以拥有多重超能力的角色结束故事,例如惊奇队长和灭霸。
因此,我们已经从令人生畏的 175 列数据变成了一个非常优雅的故事线,既有意义又连贯。感谢降维的超赞超能力!
所以下一次,你有一个看起来很吓人的大栏目文件,你可以把它转换成一个优雅的故事,让你的观众惊叹不已。
网站(全球资讯网的主机站)
你可以在我的网站上体验这篇文章中的一些技巧——https://experiencedatascience.com
每当我发布一个新的故事,请订阅保持通知。https://pranay-dave9.medium.com/subscribe
T3
你也可以通过我的推荐链接
https://pranay-dave9.medium.com/membership加入 Medium
Youtube 频道
你可以在我的 YouTube 频道上看到这篇文章的视频。https://www.youtube.com/c/DataScienceDemonstrated
power BI——打败 Excel 的工具
为什么 PowerBI 更适合可视化,以及如何使用它

卢卡斯·布拉塞克在 Unsplash 上的照片
数据分析和可视化一直是专业环境中极其重要的问题,也是公司决策的基本工具。在这种环境下,Microsoft Excel 很容易流行起来,并成为市场上主要的数据操作工具之一,但是,随着大数据的发展,全球数据的指数级增长以及分析和数据科学文化的发展,Excel 不再是如此高效的工具。
世界上的数据量正以每年 40%的速度增长,到 2025 年可能达到约 163 兆字节。

作者图片
“数据是新的黄金!”
年复一年,公司更加关注数据以及如何从数据中获取价值,以销售更多产品、获得更多客户、提高流程效率等。在这种情况下,处理大量数据并能够公开信息的工具变得至关重要。为了解决这一需要,微软创建了 Power BI,这是一个可视化工具,能够处理大数据,同时改变我们与图表交互的方式。
Excel 与 Power BI 在可视化方面的对比
我们知道,除了作为市场上已知的工具之外,Excel 还具有出色的功能,允许用户以简单有效的方式处理数据,但即使如此,该程序仍有一些需要改进的地方。当我们需要操作大量数据时,例如,excel 在可视化中经常崩溃,出现一些界面错误,不加载整个文件,并可能导致文件损坏等严重问题。另一种情况是,当我们需要进行复杂的图形分析或创建仪表板时,在 Excel 中组织图形并不简单,设计也不那么吸引人,并且您无法处理动态和相互关联的图形。在这些情况下,Power BI 被证明是非常有用的,与 Excel 相比具有巨大的优势,使数据操作更加敏捷和高效。
凭借广泛的图形和小部件,良好的大数据处理能力,与不同平台的集成和易用性,不同数据源的集中化,Power BI 在市场上日益显示出自己的强大。
以下是这两种工具之间的一些比较数据:
数据量
Power BI 是为大数据准备的,可以处理比 Excel 多 2000 倍的数据。
- Excel — 1,048,576(100 万),但它开始出现 50 万行的问题
- Power BI — 1,999,999,997(20 亿),优化为不同时显示所有行
图表和视觉效果
Power BI 具有实时动态过滤的交互式视觉效果,高度可定制,是一个您可以找到并下载视觉效果的市场,以及您可以自己开发并导入的新视觉效果。
- Excel —它提供了激活新型图表的选项,但创建和打印图表的方式并不简单,而且图表之间没有动态交互。
- Power BI —实时、动态图形中图形之间的交互的可能性,使用 typescript 创建新图形,通过市场或直接导入轻松导入新图形
与外部数据整合
Power BI 可以处理多种数据源,比如云数据库、SQL 服务器等等。
- Excel —从 Excel 电子表格或 CSV 中读取数据
- Power BI —数据库(Azure SQL、Spark、Oracle 等。)、内容包(google analytics、GitHub 等。)、文件(Excel 电子表格、CSV、文本等。),以及更多类型的数据
总的来说,Power BI 支持创建易于创建和操作的高度可定制的仪表板、图表之间的实时交互以实现更动态的数据分析、优化洞察力并生成出色报告的功能、用于开发 ETL 和数据管道的工具等等。
第一步
好吧,现在你一定在想,我怎么用这个能量 BI?
Power BI 有两种访问数据和使用服务的方式,一种是从浏览器直接访问的 Power BI 服务,另一种是可以在 Windows 上下载并在本地使用的 Power BI 桌面。
首先,我将演示 Power BI 服务:
https://powerbi.microsoft.com/
主页将会出现,你可以点击登录使用你的微软账户,或者创建一个新的免费账户。该开发工具非常类似于 Power BI 桌面。
对于 Power BI Desktop,我将提供一个快速教程,教你如何开始使用它。要在您的 windows 计算机上下载 Power BI desktop,您必须打开下面的链接:
【https://www.microsoft.com/en-us/download/details.aspx? id=58494
下载并安装后,您可以使用下面介绍的第一步开始创建您的仪表板:
导入数据
一旦您启动 PowerBI,将显示如下截图所示的主屏幕。您可以单击“Get Data”查看 Power BI 可以导入的各种数据,或者选择菜单中显示的一种数据类型,如 Excel、Power BI 数据集、SQL server 等。

作者图片
您添加的文件将以表格形式显示在右侧菜单中的“字段”中,或者显示在左侧菜单中的“数据”选项卡中。

作者图片

作者图片
编辑数据
导入数据后,您可以使用“转换数据”工具编辑和创建一些转换管道,或者使用“建模”选项卡进行一些计算

作者图片
“转换数据”工具,称为 Power Query editor,有很多很好的工具供您编辑和转换数据。你可以在下面的链接中找到更多关于如何使用它的信息,你也可以在图片中看到 Power Query 编辑器的截图。
https://docs.microsoft.com/en-us/power-bi/transform-model/

作者图片
开发仪表板
在右侧菜单中,您可以访问“Visualizations”窗格,在该窗格中,您可以选择要使用的图表类型,并通过拖放操作轻松地将其拖至工作区。在“字段”菜单中,您可以访问文件中的数据,并将其拖到图表中以生成可视化效果。

作者图片
下面是一个带有图例和过滤器的“折线图和堆积柱形图”的快速演示。我们有一个按资产划分的“DY(12M)”图,图中有一个部门图例分段,还有一个“P/VPA”线变化和一个按“资产代码”和“DY(12M)”的过滤器。

作者图片
结论
Power BI 是开始探索数据分析领域并获得对大容量数据集的宝贵见解的绝佳方式。如果你熟悉 Microsoft Excel,你将在开发中拥有巨大的优势,因为许多工具、图表和公式都与 Excel 非常相似。
我希望这是一本好书!关于 Power BI 和数据分析的任何反馈或问题,请随时在 LinkedIn 上联系我:【https://www.linkedin.com/in/octavio-b-santiago/
如果你有兴趣了解更多关于 Power BI 的知识,我推荐微软的免费学习路径:https://docs . Microsoft . com/en-us/learn/Power platform/Power-BI
Sklearn 的递归特征消除(RFE)功能强大的特征选择
即使删除 93 个功能,也能获得相同的模型性能

基本的特征选择方法主要是关于特征的单个属性以及它们如何相互作用。 方差阈值处理 和 成对特征选择 是基于方差和它们之间的相关性去除不必要特征的几个例子。然而,更实用的方法是根据特性对特定模型性能的影响来选择特性。Sklearn 提供的一种这样的技术是递归特征消除(RFE)。它通过逐个移除特征来降低模型的复杂性,直到留下最佳数量的特征。
由于其灵活性和易用性,它是最流行的特征选择算法之一。该算法可以环绕任何模型,并且它可以产生提供最高性能的最佳可能特征集。通过完成本教程,您将学习如何在 Sklearn 中使用它的实现。
**https://ibexorigin.medium.com/membership
获得由强大的 AI-Alpha 信号选择和总结的最佳和最新的 ML 和 AI 论文:
https://alphasignal.ai/?referrer=Bex
递归特征消除背后的思想
考虑安苏尔男性数据集的这个子集:

它记录了 6000 多名美国陆军人员的 100 多种不同类型的身体测量数据。我们的目标是使用尽可能少的特征来预测以磅为单位的重量。(数据集中有 93 个数字要素)
让我们用随机森林回归器建立一个基本性能。我们将首先构建特性和目标数组,并将它们分成训练集和测试集。然后,我们将拟合估计量,并使用 R 平方对其性能进行评分:
我们实现了 0.948 的出色 R 平方。我们可以使用所有 98 个特性来完成这项工作,这比我们可能需要的要多得多。所有 Sklearn 估计器都有特殊的属性,显示特征权重(或系数),或者以coef_或.feature_importances_给出。让我们看看随机森林回归模型的计算系数:

要降低模型的复杂性,请始终从移除权重接近 0 的要素开始。由于所有权重都要乘以特征值,因此这样小的权重对整体预测的贡献非常小。看上面的权重,可以看到很多权重接近 0。
我们可以设置一个低阈值,并基于它过滤掉特征。但我们必须记住,即使删除一个单一的特征也会迫使其他系数发生变化。因此,我们必须一步一步地消除它们,通过对拟合的模型系数进行排序,留下权重最低的特征。手动为 98 个特性做这件事会很麻烦,但是谢天谢地 Sklearn 为我们提供了递归特性消除功能— RFE 类来完成这项任务。
Sklearn 递归特征消除类
RFE 是一个转换估计器,这意味着它遵循熟悉的 Sklearn 的拟合/转换模式。由于其易于配置的特性和稳健的性能,它是一种流行的算法。顾名思义,它根据我们在每次迭代中选择的模型给出的权重,一次删除一个特性。
下面,您将看到一个使用上述随机森林回归模型的 RFE 示例:
拟合估计器后,它有一个.support_属性,为丢弃的要素提供一个带有假值的布尔掩膜。我们可以用它来划分数据子集:
X_train.loc[:, rfe.support_]

或者你可以直接调用.transform()来获得一个新的numpy数组和相关的特性。让我们使用这个较小的子集再次测试随机森林回归器:
即使在删除了将近 90 个特性之后,我们还是得到了同样的分数,这令人印象深刻!
RFE 性能考虑因素
由于 RFE 每次丢弃一个特征时都会在整个数据集上训练给定的模型,因此对于像我们这样具有许多特征的大型数据集来说,计算时间会很长。为了控制这种行为,RFE 提供了step参数,让我们在每次迭代中丢弃任意数量的特性,而不是一个:
选择要自动保留的特征数量
RFE 最重要的超参数是估计器和 n_features_to_select 。在最后一个例子中,我们任意选择了 10 个特性,并希望得到最好的结果。然而,由于 RFE 可以包装任何模型,我们必须根据它们的性能来选择相关特征的数量。
为了实现这一点,Sklearn 提供了一个类似的RFECV类,它通过交叉验证实现递归特性消除,并自动找到要保留的最佳特性数量。下面是一个在简单的线性回归中使用 RFECV 的例子。我们将选择线性回归,因为我们可以猜测人体测量值之间存在线性相关性。此外,结合交叉验证,随机森林回归将变得更加计算昂贵:
我为cv和scoring参数提供了默认值。一个新的超参数是min_features_to_select——你可以从名字中猜出它是做什么的。让我们看看估计器计算出要保留多少个特征:
RFECV告诉我们只保留 98 个中的 5 个。让我们只在这 5 个上训练模型,并观察它的性能:
即使去掉了 93 个特性,我们仍然得到了 0.956 的高分。
摘要
通过阅读本教程,您学习了:
- 递归特征消除背后的思想
- 如何使用 Sklearn RFE 类实现算法
- 如何决定使用 RFECV 类自动保留的特性数量
如果你想更深入地了解算法,你可以阅读这篇帖子。
关于特性选择的进一步阅读:
您可能也会感兴趣:
- 【HalvingGridSearch 使超参数调谐速度提高了 11 倍
- 面向数据科学家的面向对象编程介绍**
为 Python 用户提供强大的 R Markdown
如何使用 R Markdown 将 Python 和 Bash 命令与 LaTeX 文档生成相集成

作者图片
R Markdown 是一个强大的工具,让您使用 TeX 引擎生成丰富的 PDF。在 R Markdown 中,您可以将散文和您的分析与代码、其输出和图形集成在一起。然而,R Markdown 的使用并不局限于 R 语言本身,而是可以集成各种其他语言。
当谈到编写带有解释和描述的代码时,R Markdown 与 Jupyter Notebook 非常相似,但 R Markdown 可以做一些 Jupyter Notebook 所缺乏的事情。例如,对于 Jupyter Notebook,包含一个定制的 LaTeX 包并不简单。使用 R Markdown,我发现包含一个定制的 LaTeX 页面非常容易。如果您的系统中安装了 LaTeX 引擎,它还允许您生成一个 PDF 文件,代码、其输出和图形在同一个 PDF/HTML 报告中生成。
使用 R Markdown 生成丰富的 pdf
R Markdown 使用了knitter,,这是一个用于 R 语言的动态报告生成引擎。它可以生成 PDF 和 HTML 等多种格式的完全可复制的笔记本。Knitr engine 集成了各种语言,比如 R、Python、Bash 和 SQL。在本文中,我将自己局限于 Python 和 Bash。
为了更好地理解这篇文章,请安装 R Studio 和 R。然而,除了一些额外的步骤之外,所有的例子都可以在 Windows 或 Mac 上运行,我不会在本文中详细介绍这些步骤。
生成乳胶文件
一旦你安装了 R 语言和 R Studio,点击创建一个新的 R 笔记本。

你也可以选择 R Markdown 来创建项目风格的文件。但是创建一个 R 笔记本是最简单易用的。文件可以保存为.Rmd格式。
下面是您必须拥有的.Rmd的代码片段。
---
title: "My R Markdown Notebook"
author:
- Rahul Bhadani^[The University of Arizona, [rahulbhadani@email.arizona.edu](mailto:rahulbhadani@email.arizona.edu)]
date: "`r format(Sys.time(), '%d %B %Y')`"
bibliography: biblio.bib
output:
pdf_document:
citation_package: natbib
keep_tex: yes
number_sections: yes
html_notebook: default
html_document:
df_print: paged
classoption:
- onecolumn
header-includes:
- \usepackage[none]{hyphenat}
- \usepackage{physics}
- \graphicspath{ {figures/} }
- \usepackage{color}
- \definecolor{ocre}{RGB}{243,102,25}
- \usepackage[font={color=ocre,bf},labelfont=bf]{caption}
- \usepackage{hyperref}
- \hypersetup{colorlinks=true,citecolor=blue}
- \def\BibTeX{{\rm B\kern-.05em{\sc i\kern-.025em b}\kern-.08em
T\kern-.1667em\lower.7ex\hbox{E}\kern-.125emX}}
- \newcommand{\spliteq}[1]{\begin{equation}\begin{split}#1\end{split}\end{equation}}
keep_tex: yes
abstract: This is my abstract
---# Section 1
# Section 2```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
``````{r, echo=T,eval=T,warning=FALSE, fig.height = 4, fig.width = 5}
library('nloptr')
hn <- function(x, n)
{
hret <- 0
if (n == 0)
{
hret <- x + 2*x
}
else
{
hret <- x - 2*x
}
return (hret)}```
在上面的代码片段中,---之间的任何东西都是定义如何生成 TeX 文件和相应 pdf 的元数据。使用bibliography:,你可以指定在你的减价中引用什么.bib。您可以在-命令后指定额外的 LaTeX 命令。keep_tex告诉 R Markdown 不要删除生成的 tex 文件,这有几个原因,包括进一步修改生成的 TeX 文件,如果你决定这样做。
`````中的任何内容都是在eval=T被设置时执行的代码片段。
要生成 PDF,请从针织选项中单击针织到 PDF,如下所示:

编译完成后,您将看到一个 PDF 文件被打开。点击针织按钮旁边的设置图标,可以浏览其他选项。TeX 源文件将在保存.Rmd文件的同一个目录下生成。
在 R Markdown 中添加 Python 代码
要将 python 代码添加到 R Markdown 文件中,请添加以下代码片段:
```{python, engine.path = "/home/username/anaconda3/envs/virtualenv/bin/python", fig.height = 3, fig.width = 6}
import pandas as pd
import seaborn as snsiris = sns.load_dataset('iris')
iris.head()
iris['sepal_length'].plot()
使用`engine.path`,我指定在哪里寻找 python 可执行文件来运行 python 代码片段。运行上述代码后,我可以在我的 PDF 文件中看到以下内容:

# 在 R Markdown 中执行 bash 命令
要执行 bash 命令,您需要添加以下代码片段:
source ~/.bashrc
source /opt/ros/melodic/setup.bash
source ~/catkin_ws/devel/setup.bash
pwd
ls```
当您执行完笔记本时,您将在 PDF 中看到pwd和ls的输出。
使用knitter引擎执行 bash 命令有一些限制。在 R Markdown 中,bash是通过 R 函数system2().调用的,它忽略了像~/.bash_profile和~/.bash_login这样的概要文件,在这些文件中,您可能已经定义了别名、环境变量和一些额外的定制。出于同样的原因,在上面的例子中,您可以看到我已经显式地指定了 source .bashrc和任何其他我包含在.bashrc文件中的 sourcing。
此外,当包含在 R 中时,不应该在 bash 代码片段中包含任何永久运行的 bash 命令,因为 knitr 会一直等待执行完成,结果只会失望。
最后的想法
R Markdown 提供了一种优雅的方式来为您的数据分析生成各种格式的报告。使用 R Markdown,您可以包含代码、数据、输出和生成的图形。最后,您还可以在 R 和 Python 代码片段之间共享变量。
例如,要访问 python 片段中的任何 R 工件,使用r.后跟 R 中定义的任何变量或函数,类似地,要访问 R 中的任何 python 工件,使用py$后跟 Python 中定义的任何变量或函数。以下代码片段解释了这个想法:
# Use Python artifacts from R
```{r,echo=T,eval=T,warning=FALSE, fig.height = 4, fig.width = 5}
py$iris```# Use R artifacts from Python```{python, engine.path = "/home/ivory/anaconda3/envs/dbn/bin/python", fig.height = 3, fig.width = 6}
print(r.hn(3,5))
*注意:作者基于他对技术的理解创建了本文中提供的所有示例。与其他在线资源的相似之处可能纯属巧合。*
</how-to-solve-a-constraint-optimization-problem-in-r-fdf5abee197b>
# 使用 NLPAUG 的强大文本增强
> 原文:<https://towardsdatascience.com/powerful-text-augmentation-using-nlpaug-5851099b4e97?source=collection_archive---------9----------------------->
## 使用令人惊奇的 NLPAug 通过文本增强技术处理 NLP 分类问题中的类别不平衡

布雷特·乔丹在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上的照片
# 什么是数据增强,我们为什么要关注它?
数据扩充是从现有数据中合成新数据的实践。这可以应用于从数字到图像的任何形式的数据。通常,增加的数据类似于已经可用的数据。在所有机器学习问题中,数据集决定了问题的解决程度。有时,我们没有足够的数据来建立稳健的模型,更常见的是数据中存在明显的阶级不平衡。假设我们正在构建一个预测两个类别之一的模型,但我们有一个类别的 5000 个样本进行训练,而另一个类别只有 200 个样本。在这种情况下,我们的模型将几乎总是预测具有更多样本的类别,因为它没有得到足够的数据来区分这两个类别。然后我们必须转向收集更多的数据,但如果我们不能呢?一种方法是生成我们拥有的 200 个数据样本的精确副本,并减少不平衡。虽然这确实提供了一些改进,但是模型仍然在学习相同的特性集!或许一些巧妙的调整可以提高我们现有数据的质量。
# **为什么与其他形式的数据增强相比,文本增强很困难?**
想想看,为了增加图像,我们可以旋转、锐化或裁剪图像的不同区域,新数据仍然有一定的意义。
然而,扩充文本数据是非常困难的。例如,改变单词的顺序乍一看似乎是合理的,但有时这可能会完全改变句子的意思,比如说, ***我清洗了我的汽车*** 不同于 ***我清洗了我的汽车。***
幸运的是,马志威的**[**nlpaug**](https://github.com/makcedward/nlpaug)**给了我们一些惊人的工具来快速扩充文本。下面就来说说其中的一些。****
# ****扩充文本数据的方法****
1. ****用同义词替换几个单词。****
2. ******用与这些单词具有相似(基于余弦相似度)单词嵌入的单词(如 word2vec 或 GloVe)替换一些单词。******
3. ******使用强大的变压器模型(BERT)根据上下文替换单词。******
4. ****使用反向翻译,即把一个句子翻译成另一种语言,然后再翻译回原文,有时会修改几个单词。****
# ****让我们看看第一个,并将其应用于一个问题,看看增强是否真的有效****
## ****定义问题****
****我将使用来自 [Kaggle](https://www.kaggle.com/sripaadsrinivasan/yelp-coffee-reviews) 的 Yelp 咖啡评论数据集来分析一个情感分析问题。该数据集包含近 7000 条用户评论和评级。用户对咖啡店的评分从 1 到 5,越高越好。为了制造一些不平衡,我放弃了中性评级(3),并将所有大于 3 的评级标记为正面,所有小于 3 的评级标记为负面。下面是预处理前的一个示例回顾****
## ******评估指标******
****当谈到不平衡分类时,我并不热衷于准确性度量,因此将主要查看[**ROC 曲线**](/understanding-auc-roc-curve-68b2303cc9c5) 下的面积作为评估度量。****
## ******两种情况下使用的型号******
****在一些文本预处理和清理之后,我使用了一个随机森林分类器,它有 10 个估值器。使用 Tfidf 矢量器对文本进行矢量化,并选择基于术语频率的最佳 3000 个特征向量作为分类器的输入特征。这个模型非常简单,我在下面链接了全部代码。对于那些想先查阅[代码的热心读者来说,它就在这里!](https://github.com/rajlm10/NLPAUG_Usage/blob/main/YELP_NLPAUG.ipynb)****
# ******没有文本增强的结果******
****首先,我们来看看阶级不平衡。如你所见,正负之间的不平衡几乎是 6.5 : 1 的比例。****
********
****阶级之间的巨大不平衡(图片由作者提供)****
****这是在数据集上训练 RandomForest 分类器后的结果。****
********
****RandomForest(n=10)分类器的 AUC****
****如您所见,曲线下的面积为 0.85,为了证明模型仅在样本数量较少的类上表现不佳,请仔细查看分类报告。****
********
****详细分类报告****
****该车型的召回率和 f1-在负类上的得分(标为 0)绝对****可怕!这解释了为什么 AUC 是 0.85。**现在,我们将通过 nlpaug 以最小的努力提高 AUC 和 f1 分数。******
# ****增强后的结果****
## ****我们如何通过用同义词替换单词来扩充文本的例子****
****请使用 pip 安装 nlpaug。请参考我的笔记本来查看完整的代码。以下只是一个小片段。****
pip install nlpaug
****在这之后,我将使用 wordnet 库来帮助处理同义词。****
****让我们从数据集中挑出一句话——“***误导性评论。有史以来最糟糕的咖啡,非常令人失望。”*******
import nlpaug
import nlpaug.augmenter.word as naw
aug = naw.SynonymAug(aug_src='wordnet',aug_max=2)
aug.augment("Misleading reviews. Worst coffee ever had, and sorely disappointing vibe.",n=2)
****在上面的代码**中,aug_max** 表示我们想要用相应的同义词替换的单词的最大数量**。在最后一行中,n=2 表示我们想要生成两个扩充句子。******
****下面是惊人的结果!****
*****‘误导性评论文章’。这是有史以来最差的咖啡,非常令人失望。误导评论。这是有史以来最差的咖啡,令人非常不满意*****
****在**第一句**和第二句**误导**和**失望**分别被替换为**导致** **误入歧途**和**不满意**。****
## ****我如何决定扩充数据****
****我决定为标签为 0(属于少数类的负面评论)的训练集中的每个句子引入 2 个新的增强句子。在这些扩充的句子中,我决定用同义词替换最多 3 个单词。你可以自己摆弄这些参数,找点乐子。****
****这是增加数据后的分布。随着一些新的有意义的数据的出现,少数派的人数增加了两倍!****
********
****(图片由作者提供)****
## ****现在是我们期待已久的高潮…****
********
****增强后的 AUC!****
********
****扩充后的分类报告****
******哇!我们将 AUC 从 0.85 提高到 0.88,并将 f1 评分从 0.7 提高到 0.76。******
# ****比较增强前后的 ROC 曲线****
****************
****(左)-增强前数据的 ROC 曲线(右)-增强后数据的 ROC 曲线(图片由作者提供)****
****虽然稍微,但是右边的 ROC 曲线覆盖的面积更大,效果更好。**更棒的是,增强在一个 CPU 上只用了 57 秒。这显示了文本增强的益处。******
# ****注意事项****
1. ****在实验的任何时候,我们都没有改变模型或调整它,所有性能的提高完全是由于数据的增加。****
2. ****nlpaug 库使用单词嵌入、BERT 变换器和反向翻译提供了更强大的扩充选项。就存储和执行速度而言,我们使用的是最便宜的选项!****
3. ****CPU 上的增强用了不到一分钟,这是相当快的。****
4. ****请浏览参考资料中的链接,了解更多增加数据的方法。****
5. ****最后,我们用很少的努力(就时间而言)和不到 5 行代码将 AUC 从 0.85 提高到 0.88。****
# ******参考文献******
1. ****[**nlpaug**](https://github.com/makcedward/nlpaug) 的官方 Github 库,里面包含了示例笔记本****
****查看我的 **G** [**itHub**](https://github.com/rajlm10) 的一些其他项目和整个 [**代码**](https://github.com/rajlm10/NLPAUG_Usage/blob/main/YELP_NLPAUG.ipynb) 。你可以在我的[***w*ebsite**](https://rajsangani.me/)***上联系我。我希望在评论中得到一些反馈。感谢您的宝贵时间!*******
# 用 TigerGraph 支持 Google Forms 和 Google Sheets 应用程序数据
> 原文:<https://towardsdatascience.com/powering-google-forms-and-google-sheets-application-data-with-tigergraph-7f8784335aae?source=collection_archive---------36----------------------->
## 使用 TigerGraph 构建应用程序图

[图片来自 Pixabay](https://pixabay.com/photos/application-request-ipad-tablet-1883453/)
# 目标
欢迎光临!在这篇博客中,我们将学习如何使用 Google Sheets API 来捕获申请数据,并将其放入 TigerGraph 中,以便轻松地查询和筛选申请人。随着学年的开始,我最终需要为我领导的俱乐部打开申请,虽然只有少数人会申请这些职位,但如果我有大量的申请,TigerGraph 和 Graph 数据库可以帮助我轻松快速地找到并筛选各种职位的申请人。在这里,我们将集成 Google Sheets API 来从表单中获取结果,并将其插入到图形数据库中。我们开始吧!
# 第一部分:创建 Google 表单和 Google 工作表
## 第一步:创建 Google 表单
首先,转到 https://docs.google.com/forms/的并创建一个包含您想要采样的适当字段的表单。对于这个例子,我将询问申请人的姓名、电子邮件、职位、技能以及申请人应该获得该职位的原因。

示例 Google 表单
接下来,我将用假数据填充表单。
## 第二步:创建 Google 工作表
如果你点击表格中的“回复”标签,并按下按钮,它会带你到一个谷歌电子表格中的数据。

点击“回复”,然后点击绿色按钮。
在提示中,给电子表格起一个你喜欢的名字,然后点击“创建”

重命名并按“创建”
该电子表格是一个动态电子表格,包含来自您的表单的结果。随着越来越多的用户申请该职位,它将继续被填充。

带有虚假数据的实时电子表格
这里最重要的部分是工作表的 id。您可以从 d/之后/edit 之前的链接中获得此信息。

上例中我的 id 是 19 fcj-0 d056 L9 C5 ksotzuazft-mfkjcuyqoz 2 BD 8 yx 4y。稍后我们将在 Python 脚本中使用它。
> 注意:为 Python 脚本准备好您的电子表格 ID!
# 第二部分:创建一个项目并启用 Sheets API
## 第一步:创建一个谷歌云项目
首先,你需要使用你的谷歌账户,导航到 https://console.cloud.google.com/的。在顶部,按“选择一个项目”

按选择一个项目
接下来,选择一个现有的项目(如果有的话)。如果没有,请按“新建项目”

按“新项目”
命名您的项目,然后按“创建”

输入详细信息,然后按“创建”
## 第二步:启用 Google Sheets API
创建项目后,您将被导航到一个仪表板。按左上方的三行,然后选择“APIs & Services”

单击 API 和服务
然后,按“+启用 API 和服务”我们将启用 Google Sheets API。

按启用 API 和服务
搜索“Google Sheets API”,点击第一个选项,然后按“启用”

按“启用”
完美!现在我们可以使用 Google Sheets API 了。
## 第三步:创建您的凭证
现在,我们将创建凭证来使用 Google Sheets API。在控制面板中,单击“凭据”并选择“创建凭据”从下拉列表中,选择“OAuth 客户端 ID”

点按“凭证”,按“创建凭证”,然后按“OAuth 客户端 ID”
在选项中,按“桌面应用程序”,然后命名您的应用程序。

选取“桌面应用程序”,然后输入名称
下载 JSON,其中包含您的客户端 ID 和密码。

按“下载 JSON”
太棒了。现在,在“OAuth 同意屏幕”中,确保添加您将用来访问表单的每个 Google 帐户的测试用户(包含表单的帐户)。
> 注意:当我们把所有东西放在一起时,您将使用下载的 JSON 文件!确保它易于下载。
# 第三部分:准备 TG 云解决方案
## 第一步:在 TG Cloud 上建立解决方案
接下来,我们将准备我们的图形解决方案。为此,请前往[https://tgcloud.io/](https://tgcloud.io/)。导航至“我的解决方案”选项卡,然后单击“创建解决方案”

在第一页上,按“Blank”(因为我们将创建自己的模式),然后按“Next”

不要更改第二页上的任何内容,然后按“下一步”

在第三页,自定义您的图表详细信息。
> 注意:记住你的子域和密码!我们将在脚本文件中使用这些。

最后,确认一切正常,然后点击“提交”

您的图表可能需要几分钟才能启动。当图表状态显示带有绿点的“就绪”时,它将就绪。
## 第二步:创建一个图表(可视化)
> 注意:如果您更愿意通过脚本创建图表,请跳到步骤 IV。
在 TG Cloud 中,在您刚刚创建的解决方案旁边,点击 Actions 下的下拉菜单,然后点击“GraphStudio”

选择 GraphStudio
您将被重定向到 GraphStudio。

按“全局视图”,然后选择“创建图表”

创建图表
你想给这个图起什么名字都行。对于这个例子,我正在创建一个名为“应用程序图”的图表

给图表命名
完美!你的图表现在准备好了。接下来,我们将创建我们的模式,这有点像我们如何增加数据的地图。
## 步骤三:创建一个模式(可视化)
首先,点击进入“设计模式”

按顶栏上的加号按钮。

在出现的边栏中,自定义顶点细节。该顶点将是“申请人”类型主要 ID 将是电子邮件。它有一个名为“name”的 STRING 类型的属性。自定义后按下复选标记。

顶点将会出现。

接下来,让我们创建一个位置顶点。

按下“添加局部边类型”,然后单击“申请人”顶点和“位置”顶点。

编辑边描述,然后按检查键。

完美!

让我们继续构建图表。

完成后,点击“发布模式”按钮。

加载完成后,你就可以开始了。
## 步骤四:(可选)创建一个图表(脚本)
或者,您可以使用以下脚本通过 Python 创建模式,用您的子域替换子域,用您的密码替换密码。
import pyTigerGraph as tg conn = tg.TigerGraphConnection(host="https://SUBDOMAIN.i.tgcloud.io/", password="PASSWORD")conn.gsql('''CREATE VERTEX Applicant(PRIMARY_ID email STRING, name STRING) WITH_PRIMARY_AS_ATTRIBUTE="true"
CREATE VERTEX Position(PRIMARY_ID position_name STRING) WITH_PRIMARY_AS_ATTRIBUTE="true"
CREATE VERTEX Skill(PRIMARY_ID skill STRING) WITH_PRIMARY_AS_ATTRIBUTE="true"
CREATE VERTEX Reason_Applying(PRIMARY_ID response STRING) WITH_PRIMARY_AS_ATTRIBUTE="true"
CREATE VERTEX Keywords(PRIMARY_ID word STRING) WITH_PRIMARY_AS_ATTRIBUTE="true"CREATE UNDIRECTED EDGE APPLIED_FOR(FROM Applicant, TO Position)
CREATE UNDIRECTED EDGE APPLICANT_SKILL(FROM Applicant, TO Skill)
CREATE UNDIRECTED EDGE APPLICANT_REASON_APPLYING(FROM Applicant, TO Reason_Applying)
CREATE UNDIRECTED EDGE APPLICANT_KEYWORDS(FROM Applicant, TO Keywords)CREATE GRAPH ApplicationGraph(Applicant, Position, Skill, Reason_Applying, Keywords, APPLIED_FOR, APPLICANT_SKILL, APPLICANT_REASON_APPLYING, APPLICANT_KEYWORDS)''')
太棒了。现在我们可以开始加载数据了。
# 第四部分:创建目录并提取数据
## 第一步:创建目录
在你的机器上,在你喜欢的地方创建一个新文件夹。在那里,您需要创建两个文件:一个 Python 文件,其中包含了大部分内容,另一个是您在第二步中下载的 JSON 文件。将 JSON 文件重命名为 credentials.json。

目录文件
## 第二步:安装库
首先,pip 安装 Google Cloud 和 pyTigerGraph。
pip install --upgrade google-api-python-client google-auth-httplib2 google-auth-oauthlibpip install pyTigerGraph
## 第三步:读这些行
首先,我们将导入刚刚安装的库。
import os.pathfrom googleapiclient.discovery import buildfrom google_auth_oauthlib.flow import InstalledAppFlowfrom google.auth.transport.requests import Requestfrom google.oauth2.credentials import Credentials
接下来,我们将设置范围、电子表格 ID 和范围。这里,确保用第一部分的 ID 替换电子表格 id from Part I。
> 注:用第一部分中的电子表格标识替换电子表格标识 from Part I。
SCOPES = ['https://www.googleapis.com/auth/spreadsheets']SPREADSHEET_ID = SPREADSHEET_ID_FROM_PART_IRANGE_NAME = 'Sheet1!A1:G'
然后,我们将编写一个小脚本来更新凭证。
creds = Noneif os.path.exists('token.json'):
creds = Credentials.from_authorized_user_file('token.json', SCOPES) if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request()) else:
flow = InstalledAppFlow.from_client_secrets_file('credentials.json', SCOPES)
creds = flow.run_local_server(port=0) with open('token.json', 'w') as token:
token.write(creds.to_json())service = build('sheets', 'v4', credentials=creds)
当您运行这个时,您可能需要第一次输入您的凭据。

之后,我们可以读取 Google Sheets 数据并向上插入顶点。
service = build('sheets', 'v4', credentials=creds)sheet = service.spreadsheets()result = sheet.values().get(spreadsheetId=SPREADSHEET_ID,range=RANGE_NAME).execute()values = result.get('values', [])import pyTigerGraph as tgconn = tg.TigerGraphConnection(host="https://SUBDOMAIN.i.tgcloud.io/", password="PASSWORD", graphname="ApplicationGraph")conn.apiToken = conn.getToken(conn.createSecret())if not values: print('No data found.')else: for row in values: conn.upsertVertex("Applicant", row[2], attributes={"email": row[2], "name": row[1]}) conn.upsertVertex("Reason_Applying", row[4], attributes={"response": row[4]}) conn.upsertVertex("Skill", row[5], attributes={"skill": row[5]}) conn.upsertVertex("Position", row[3], attributes={"position_name": row[3]}) conn.upsertEdge("Applicant", row[2], "APPLICANT_REASON_APPLYING", "Reason_Applying", row[4]) conn.upsertEdge("Applicant", row[2], "APPLICANT_SKILL", "Skill", row[5]) conn.upsertEdge("Applicant", row[2], "APPLIED_FOR", "Position", row[3]) print("Row Upserted")
就这样,完美!现在,您可以使用 TigerGraph 与您的 Google Sheets 进行交互。
# 第五部分:祝贺你!
恭喜你。你现在可以正式将 Google Sheets 数据导入 TigerGraph 了!如果您遇到任何问题,请随时在 TigerGraph 社区论坛或 TigerGraph Discord 中提问。
<https://community.tigergraph.com> <https://discord.gg/gRHWBZNpxW>
最后,如果你想创建像这样的酷项目,并计划加入 TigerGraph 社区,了解社区贡献计划以获得奖励!
<https://www.tigergraph.com/community-contribution/>
感谢你阅读这篇博客,我希望你加入 TigerGraph 社区,开始获得你的条纹!
# SQL 代码的 PR 评审
> 原文:<https://towardsdatascience.com/pr-reviews-for-sql-code-115a662f48ef?source=collection_archive---------17----------------------->
## [业内笔记](https://towardsdatascience.com/tagged/notes-from-industry)

[https://pix abay . com/photos/feedback-opinion-customer-1977987/](https://pixabay.com/photos/feedback-opinion-customer-1977987/)
# 这是我个人关于如何审查我的同行 SQL 代码的指南。
正如我之前提到的,[在 Shopify,我们所有的工作都经过同行评审](https://coffeeanddata.ca/how-to-thrive-in-the-face-of-disruption-tips-from-shopify-data-team)。这包括仪表板和 SQL 代码。Shopify 的数据科学家最常用的工具之一是[模式](https://modeanalytics.com)。如果您从未使用过它,Mode 是一个简单的仪表板工具,您可以编写 SQL 来获取数据集,然后您可以使用一些拖放图表来构建仪表板。
Shopify 的某人找到了一种将 Mode 与 Github 连接起来的方法,所以现在当数据科学家创建或修改 Mode 仪表板时,我们可以像审查任何代码一样审查它。现在,拥有审查代码的能力是一回事,高效地进行审查以发现错误是另一回事。
如果我简单地打开 PR 并开始阅读查询,我最终会像阅读故事一样阅读它。在查询结束时,我通常理解他们想要做什么,但是在这个过程中我没有发现任何错误。
# 技术发展水平
网上有大量关于如何审查代码的指南,但大多数(如果不是全部的话)是针对更传统的语言和“普通”软件开发的。我找不到任何与 SQL 和数据科学相关的东西,至少没有找到我需要的方式。
SQL 有什么不同?为什么其他指南/资源做不到这一点?我认为审查 SQL 查询不是当前的做法,至少不是如此彻底。第一,心态完全不同。在 SQL 中,你要么在做分析,要么试图讲述一个故事,用英语传达数据告诉你的内容。或者你正在建立一个自助仪表板,一个帮助任何人了解你的产品/项目健康状况的单页视图,等等。
因此,在过去的几年里,当我回顾同事的工作时,我已经建立了一个清单。请记住,我没有找到所有这些,有些来自同事,有些来自专业经验。
# 公关审查列表
我的清单分为两大类。首先,这是我回顾分析本身的地方,我关心的是问题和结果,而不是中间的东西。然后是实际的代码`the SQL`,我深入到 SQL 本身。
# 结果呢
这个类别更主观,我也觉得它是最被低估的。这是你可以区分普通工作和有影响力的工作的地方。
**问题**:这听起来过于简单,但是相信我,这可能是最容易被忽视的方面,尤其是对于初级数据科学家来说。你真的了解这个问题吗?我们在 Shopify 的工作方式是利益相关者公开 Github 问题。大多数数据科学家只是简单地阅读它,并开始实施它,因为,假设那里有什么,来自上帝本身。事实是,提出这个问题的人很可能尽力解释他们在寻找什么,但他们不是数据科学家,你才是。停下来一分钟,尝试了解问题背后的真正问题,然后问问自己,作为一名数据科学家,你可以做些什么来解决问题,并与最初的利益相关者进行讨论。大多数时候,你会从一个不同的角度来看待这个问题,这可能是真正有益的。
在我看来,如果你想从一个 SQL 查询商店变成一个真正的商业伙伴,这是你需要努力的项目。
为了回顾这一点,我阅读了原始问题,并与数据科学家进行了讨论,以查看他们是否深刻理解了该问题。对于我有很深知识的领域,这有点容易,对于我没有的领域,我通常会四处寻找产品经理,看看我们是否可以聊聊这个问题。
**结果是否回答问题**:我们谈了很多问题,这个神器是否有助于理解问题和解决方案。在这里,我通常先试着看看这是否让我明白发生了什么,然后我把自己放在一个开发者/项目经理/财务经理/总经理等的位置上。并尝试确定他们是否拥有所需的组件,此报告中的信息是太少还是太多?
我正在寻找的一个反模式是,`I am going to show you everything I have done`。有些人不是关注结果或有趣的元素,而是想证明他们已经努力工作了,并试图展示一切,甚至是什么也没有展示的图表。(例:显示商人国家没有影响的图表。)除非是为了证明这一点,否则这就不应该存在。
**没有饼状图**:我甚至不打算解释这一点。
**图表选择**:这个很难解释,而且非常主观,但是这些是正确的图表吗?如果以不同的方式展示,数字/见解会更清晰吗?围绕这个有很多好书。图表需要讲述一个故事,洞察力应该是显而易见的,如果你需要知道在哪里看,它可能不是适当的可视化。
**对于仪表板**:当我谈到仪表板时,我指的是可重用/自助式仪表板。通常用于跟踪 KPI、产品健康、产品早期和晚期指标等。
这听起来可能有点滑稽,但我确保它们是可重复使用的?如果我们能找到一个通用化仪表板的好方法,我们将来可能会节省很多工作/请求。如果仪表板只是做了涉众要求的一件事,仪表板很可能永远不会被再次使用。
当利益相关者请求一个非常精确的仪表板时,我首先查看数据是否已经存在,如果存在,我将它们发送到现有的仪表板。如果没有,我会对问题进行逆向工程,并问自己,什么通用仪表板会回答这个问题。第二个仪表板是我需要建立的。
**静态分析**:也叫深潜、一次性等。这些是为了回答一个特定的问题,这个问题不应该在将来出现。示例问题:我们能证明产品 A 对客户的价值吗?或者产品 A 增加了客户的终身价值(LTV)等。
**对于非数据科学家来说,这个结果清楚吗?**这一部分非常重要,你需要去掉数据思维,换上普通人的思维。在这一点上,你是如此接近你的分析,以至于忘记了你所有的背景。公司里的任何人都应该能读到这篇文章,他们都会得出相同的结论。你应该尽可能避免任何数据/统计术语。如果你还想为更多的数据爱好者描述分析,请在附件中添加。但是报告的主要部分应该是讲述这个故事。
它有强有力的结论吗?我最不喜欢的报告(也是我看到最多的报告)是长长的数据事实列表。类似于*产品 A 在 A 国的渗透率为 5%*或*一般买家在订单上多花 3 美元*
我想知道为什么这些数字很重要。是 5%高还是低。这个市场其他同类产品在做什么,或者和其他国家对比一下?你需要在读者心中建立一个心智模型,因为如果你不这样做,他们就会这样做,而且很有可能会做错。这是你的分析,你应该是一个强加参考点的人,为什么这很重要,为什么我应该关心。
**它有推荐吗?这是你最好的,也是唯一的真正有影响力的机会。**你需要一个大胆的推荐。不需要令人震惊或出乎意料,但你应该以数据的推荐来开始和结束你的分析。许多数据科学家对此感到很不舒服,但如果你不这样做,将会出现以下两种情况之一。要么他们会低估结果,违背数据所说的,因为你的分析不够清楚,要么他们会同意你的观点,他们会打电话,但没有人会意识到你在这方面所做的工作。****
如果你想成为利益相关者的有影响力的合作伙伴,你必须提供清晰的建议。
# SQL
这一部分没有特定的顺序,我只是确保所有这些都得到验证。
**连接顺序**:一些数据库有很好的优化器,但是没有什么比手工处理连接顺序更好的了。因此,最大的数据集应始终位于左侧(或在 FROM,JOIN order 中位于上方)示例:
`SELECT * FROM big_table JOIN medium_table USING (_key1) JOIN small_table USING (_key2)`
没有必要精确到表格的行或实际大小。也就是说,如果表的大小有很大的差异,出于性能原因,确实值得进行排序。在事实和维度的世界中,一个简单的方法是确保事实表是第一个,然后是维度。
**分解联接**:分解联接是指当你认为两个表之间存在 1:1 的关系时。你加入他们,然后突然你有了一些复制品。这种类型的错误真的很难,因为大多数时候它是完全无声的。另一个棘手的问题是当误差只有几个百分点时。因为在大多数表格中,你确实有这种 1:1 的关系,但是有一个你没有想到的极限情况,然后你的结果突然高了几个百分点,这使得很难检测。
我确保每一桌的谷物都是所期望的。换句话说,我阅读查询,并且不假设连接中的表的粒度。如果我幸运的话,数据集有一个`unique keyset`,这是 Shopify 的一个内部机制,当我们建立一个数据集来确保颗粒得到尊重。如果我有这个,并且它与连接对齐,那么我就可以开始了。
如果我没有这个,我通常会这样查询:
`SELECT grain, count (*) FROM table GROUP BY 1 ORDER BY 2 DESC LIMIT 10`
如果第一排第二列有`1`,我们就好走了,五谷受到尊重。
**内爆连接**:我们刚刚谈到的,对于内部连接可以反过来。如果您联接 table_a 和 table_b,请确保在您认为存在 1:1 关系时不会丢失行。示例 table_a 有 100 行具有唯一 id,table_b 有相同的唯一 id,但只有 99 行。你将无声无息地失去一行。
我通常用这个简单的检查来测试:
`SELECT count (*) FROM table_a JOIN table_b ON table_a.id = table_b.id`
应该给出与以下相同的结果:
`SELECT count (*) FROM table_a FULL OUTER JOIN table_b ON table_a.id = table_b.id`
如果这两个查询的结果是相同的,那么您很可能是正确的。
**输入数据**:他们是否使用了正确的输入数据,建模数据是否已经存在?通常我们会试图用一个我们已经知道的数据集进行得太快。我们不会四处寻找现有的模型数据来帮助回答这个问题。当我查看过多使用联接的查询或使用未建模数据的查询时。我花了几分钟搜索现有的数据集。如果某样东西已经存在,你需要一个非常好的理由不去使用它。很可能,某个具有更多商业知识的人构建了它,并且所有这些背景很可能包含在建模数据集中。使用它可以防止你忽略最终会产生影响的小细节。
清晰而有逻辑的 CTE:查询中的 cte 应该讲述一个故事。它们应该是合乎逻辑的,并且顺序正确。如果可能的话,它们应该是连续的,你不需要一直上下滚动来理解正在发生的事情。
我还确保每个 CTE 的纹理都清晰明了。cte 还应该有清晰易懂的名称。仅仅通过阅读它的名字,我就能猜出它是做什么的,以及我能从这个 CTE 中得到什么样的谷物。
当 CTE 更复杂时,我经常在一个新的查询文件中复制所有的 CTE,并显示一个我不确定结果的输出。所以我做`SELECT * FROM CTE_1`,只是为了完全理解从这些 CTE 中能得到什么。
**左连接上的 WHERE 子句**:当有左连接时,我确保右表上没有 Where 子句。因为如果有的话,你基本上把它还原成了一个内部连接。示例:
`SELECT * FROM table_a LEFT JOIN table_b USING (_key1) WHERE table_b.country = 'US'`
这相当于
`SELECT * FROM table_a JOIN table_b USING (_key1) WHERE table_b.country = 'US'`
因为 table_b 中的任何空值都会被 WHERE 删除。
**阅读文档**:对于我不熟悉的表格,我花 3 分钟时间阅读文档,这首先帮助我检查查询,但也为我将来提供更好的上下文。
**已知陷阱**:所有的桌子都有自己的小陷阱。典型的 Shopify 示例,确保您过滤了当前的客户商店。你的分析里大概不关心一个 10 年前离开 Shopify 的店铺。
# 秘密诡计
这是我所学到的,并且节省了我最多的时间。直到我对结果 100%满意时,我才开始审查代码。**太多次了,我先在 SQL 上花了一两个小时,才意识到我不同意分析本身**,或者我认为他们呈现结果的方式不清楚。根据 Mode 的工作方式,改变显示结果的方式可能需要对查询进行认真的重新设计。所以现在,我从不审查代码,除非我对最终的结果感到满意。
# 结论
所有这些都不是一份详尽的清单,但是当我阅读我的一些同事的工作时,我会努力记住这些要点。在过去的几年里,这帮助我与 PR 评论保持一致,通常当我对这个列表的大部分内容都满意时,我已经发现了这个查询的大部分严重问题。
特别感谢[特里斯坦·布德罗](https://www.linkedin.com/in/tristanboudreault/)的一些建议。
*最初发布于 2021 年 4 月 23 日*[*https://coffeeanddata . ca*](https://coffeeanddata.ca/PR-reviews-for-SQL-code)*。*
# (实用)人工智能伦理
> 原文:<https://towardsdatascience.com/practical-ai-ethics-639013a782a3?source=collection_archive---------20----------------------->
## [播客](https://towardsdatascience.com/tagged/tds-podcast)
## 玛格丽特·米歇尔谈人工智能最大的挑战公平和偏见
[苹果](https://podcasts.apple.com/ca/podcast/towards-data-science/id1470952338?mt=2) | [谷歌](https://www.google.com/podcasts?feed=aHR0cHM6Ly9hbmNob3IuZm0vcy8zNmI0ODQ0L3BvZGNhc3QvcnNz) | [SPOTIFY](https://open.spotify.com/show/63diy2DtpHzQfeNVxAPZgU) | [其他](https://anchor.fm/towardsdatascience)
*编者按:TDS 播客由 Jeremie Harris 主持,他是数据科学导师初创公司 SharpestMinds 的联合创始人。每周,Jeremie 都会与该领域前沿的研究人员和商业领袖聊天,以解开围绕数据科学、机器学习和人工智能的最紧迫问题。*
偏见在机器学习中名声不好。然而,机器学习模型的全部要点是,它将某些输入偏向于某些输出——例如,一张猫的图片偏向于一个写着“猫”的标签。机器学习*是*偏置生成。
因此,消除人工智能的偏见不是一个选项。相反,我们需要考虑哪些偏见是我们可以接受的,以及它们可以有多极端。这些问题需要很难找到的技术和哲学洞察力的结合。幸运的是,我邀请了谷歌研究和机器智能部门的前高级研究科学家玛格丽特·米歇尔加入播客,他的工作一直专注于实用的人工智能伦理。实际上,我真的是指人工智能伦理如何融入现实系统的具体细节,以及人工智能橡胶遇到道路时出现的复杂道德问题。
以下是我在对话中最喜欢的一些观点:
* Margaret 认为伦理人工智能开发的关键挑战之一是在构建过程中包含不同的观点。她补充说,仅仅让来自不同背景的人加入一个团队是不够的;团队成员也必须乐于分享他们的观点。因此,在她的开发理念中,团队文化变得和团队构成一样重要。
* 到目前为止,大多数人都知道训练数据中的偏差,以及它对人工智能系统的性能和盲点的影响。但是 Margaret 强调,开发人员的偏见也可能出现在培训过程本身的层面上。应该优化什么目标函数?我们应该优化所有测试样本的准确性,还是应该检查模型在不同身份组或测试集子集上的表现,以确保每个测试样本的性能是可比的?玛格丽特通常是后一种策略的粉丝,这是为了确保不同的群体在消费人工智能系统的输出时有可比较的体验。
* 将测试集分成子集(例如,种族、年龄、性别等)以查看人工智能在不同组中的表现的想法引入了一个问题:身份理论上可以无限精细地解析。我们应该确保人工智能系统在“年轻”、“中年”和“老年”这三个广泛的年龄类别中表现同样出色,还是应该更详细地研究 21.1 岁和 21.2 岁之间的表现差异?一旦我们以这种方式细分群体,我们基本上就面临着与原始数据集相同的伦理问题。出于这个原因,玛格丽特喜欢说人工智能伦理是一个“分形问题”——一个在人口的每个解决水平上不断浮现的问题。
* 我们谈到了以不那么表面可见的方式包含不同视角的挑战。例如,虽然很容易发现少数种族或性别在科技公司中的代表性不足,但要判断这些公司是否缺少“抽象”的少数族裔,如纯素食主义者、保守派或自由主义者,就不那么简单了。鉴于硅谷表现出强烈的政治偏见,公司也有责任注意意识形态的代表性。
你可以[在 Twitter 上关注 Margaret 这里](https://twitter.com/mmitchell_ai),或者 [me 这里](https://twitter.com/jeremiecharris)。

## **章节**:
* 0:00 介绍
* 1:20 玛格丽特的背景
* 8:30 元学习和伦理
* 10:15 玛格丽特的日常
* 13:00 人工智能中道德问题的来源
* 18:00 总分和分项分数
* 24:02 多大的偏差是可以接受的?
* 29:30 人工智能伦理社区持有哪些偏见?
* 35:00 这些领域的重叠
* 政治方面
* 45:25 总结
# 实用分类标准
> 原文:<https://towardsdatascience.com/practical-classification-metrics-f056805cf1f1?source=collection_archive---------33----------------------->
## 决定使用哪些分类标准来解决现实世界中的分类问题的方法

拉克伦·唐纳德在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上的照片
一个[快速的谷歌搜索](https://www.google.com/search?q=classification+metrics)会告诉你在[分类标准](https://en.wikipedia.org/wiki/Evaluation_of_binary_classifiers)上有数百个不同的论文/博客/文章,即。、精度、召回率、F1、真阳性率、假阳性率、PPV、NPV 等。如果您不理解这些指标的定义,这些文章是一个很好的起点。例如[这篇](/understanding-data-science-classification-metrics-in-scikit-learn-in-python-3bc336865019)博客谈到了 sklearn 中的各种分类标准;[这个](/the-ultimate-guide-to-binary-classification-metrics-c25c3627dd0a)博客列出了 19 种不同的二进制分类标准(其中许多我甚至从未听说过);[此](https://medium.com/@MohammedS/performance-metrics-for-classification-problems-in-machine-learning-part-i-b085d432082b)采用“混淆矩阵”的观点来解释一些度量标准;此外,还有针对像[不平衡分类](https://machinelearningmastery.com/tour-of-evaluation-metrics-for-imbalanced-classification/)这样的小众案例的博客。但是,这些博客要么提供了太多的信息,将选择正确指标的责任留给了用户,要么提供了指标的学术观点,这在工业环境中可能可行,也可能不可行(例如,使用 AUC 指标无法给出操作点)。在这里,我提供了一个方法来决定在实际(二进制和多类)分类问题的各种场景中使用哪些度量。它面向知道基本定义,但不知道如何选择期望影响的指标的用户。
> 在这里,我提供了一个方法来决定在实际(二进制和多类)分类问题的各种场景中使用哪些度量。
# 问:选择什么指标?答:试试精确召回覆盖(PRC)频谱。
鉴于机器学习(ML)在企业界的流行,许多产品经理现在都熟悉精确召回或精确覆盖的概念,经常混淆两者。问题是,即使在机器学习领域工作了 15 年,我仍然不明白精度对于手头的问题“到底”意味着什么。一般来说 ***精度*** 的意思是“预测正确的百分比是多少”。但是,对于一个多类问题,人们可以提出两种非常不同的精度定义——都是对“正确预测百分比”的有效解释。召回率和覆盖率是另外两个经常(不正确地)互换使用的指标。 ***回忆*** 本质上是‘我能够检索到的初级类的百分比是多少’,而 ***覆盖*** 本质上是‘我对人口的百分比做了预测’。有时其中一个对问题没有意义,例如,在二进制设置中使用覆盖率是不常见的(尽管我将展示从实践的角度来看它很有意义)。
那么,如何通过逆向成为客户 来决定选择哪个指标呢?首先理解在不同的问题设置中,度量标准意味着什么(这是本博客将帮助你的地方);接下来,确定这些分类标准对应用程序/最终客户的影响。例如,当任务是将产品分类到一组类别中时,您可能会问— *我是将分类系统用于自动分类还是作为手动工作流的输入?有没有我特别感兴趣的特定类别,或者所有类别都同样重要?或者可能更受欢迎的类,即具有更高发生率的类,应该得到更高的权重?对部分产品不进行分类可以吗?低信心物品不补充手动标签怎么样?*等。其中一些问题的答案将定义如何着手选择正确的指标。让我们尝试理解不同场景中的各种指标。
在我们深入研究之前,需要注意一点——这个博客主要关注分类问题的各种精确召回率。就我的经验而言,就对最终客户的影响而言,它们更具可操作性,解释也更简单。其他指标,如 AUC、ROC、TPR、FPR 等。,可以在特定的环境中找到用途,例如,我仍然更喜欢 PR-AUC 或 ROC-AUC 来在两种算法之间做出决定,因为它们是跨各种操作点的更鲁棒的度量,但是基于精确召回率覆盖的度量是大多数实际分类问题的良好起点,其中分类输出用于一些下游应用。
> 就对最终客户的影响而言,精确召回率更具可操作性,解释也更简单。
精确-召回-覆盖度量对于商业环境中的分类问题是非常实用的。
# TL;速度三角形定位法(dead reckoning)
下表总结了可用于不同设置的各种指标:
## 二元分类

**表-1:** 二进制分类的各种精确-召回-覆盖度量。通常,二元分类器具有主要类别,即感兴趣的主要类别。例如垃圾邮件对非垃圾邮件,将“垃圾邮件”作为主要类别。第 1 列是可以使用的各种 PRC 指标。第 2 列定义了选择“工作点”的度量标准。第 3 列是用户需要输入的操作点的所需主要度量,第 4 列提供了如何计算相应度量的洞察。
## **多级分类**

**表-2:** 多类分类的各种精确查全率指标。第 1 列是可以使用的各种 PRC 指标。第 2 列定义了选择“工作点”的度量标准。第 3 列是用户需要输入的操作点的所需主要度量,第 4 列提供了如何计算相应度量的洞察。
## 多标签分类
在下一篇博客中。
# 二元分类
当有两个类时,执行二元分类。通常,两个类别中的一个是主要类别(比如标签为‘1’),第二个类别是主要类别的缺失(比如标签为‘0’),例如,垃圾邮件对非垃圾邮件,电子产品对非电子产品。在某些情况下,两个类别都可能是感兴趣的,例如,男子对女子。但是,在这两种情况下,只需要学习一个分类器来区分这两个类别。假设在测试集中有“N”个项目,我们存储两个类的预测。这将产生一个“N”×2 维的输出矩阵“O”。
看上面的表-1,我们可以计算精确召回覆盖度量的三个不同的变量,其在输出矩阵‘O’上使用不同的运算。作为参考,我们可以通过计算两个分数的最大值,并将相应的预测标签与实际情况进行比较,来计算“准确性”度量。

**图-1:** 各种输出分数。a)具有基本事实的输入数据,b)两个类的输出分数,c)初级类的分数(列-2),以及 d)每行的最大值和作为预测标签的 Max 的相应索引。
例如,对于上面的玩具问题,分类精度是 75%,这是 6/8,因为总共 8 个标签中有 6 个标签匹配,而不管它是 0 还是 1。还要注意的是,在这篇博客中,我采用了一个简单的准确度版本,其中两个分数的最大值是预测的输出,即阈值 0.5。选择不同的阈值是完全有效的,这将给出不同的准确性度量。但是,在这种情况下,最好定义基于 PRC 的指标。在学术环境中,精度仍然是一个受欢迎的选择,尤其是当这两个类在数据集中达到平衡时。
## 初级类的精确召回
二元设置中的精确召回(PR)曲线是二元分类的最常见度量之一。可以计算两条不同的 PR 曲线,每条曲线对应一个类别。尽管通常只对主类进行计算。同样,在大多数问题中,同时实现高精度和高召回率是不可能的,人们必须决定哪一个对下游应用程序具有“期望的影响”——精度或召回率,或者两者的组合(例如 F1 分数)。例如,对于产品分类问题,如果您使用您的分类器来实现更好的“浏览”,即客户选择一个类别(比如“笔记本电脑”),系统显示所有标有该类别的产品(例如,显示所有笔记本电脑),那么如果您不想显示“台式机”而不是“笔记本电脑”(这是在谷歌搜索上发生的事情),那么为了精确度而牺牲召回可能更有意义。可能需要高召回制度的一个例子是“攻击性”产品,如枪支、毒品等。即使展示一个攻击性的产品也会导致信任/公共关系问题,所以如果你正在建立一个攻击性的产品分类器,你可能想要在更高的召回率下操作。如果你不清楚什么对你更重要,一个好的选择是找到 F1 最高分的工作点。f1-分数定义为(2 *(精度*召回率))/(精度+召回率)。同样,您需要深入了解分类器在下游应用中的使用情况/它将如何影响最终客户,以决定是高精度、高召回率还是高 F1 影响。

**图-2:【左】**根据原始分数排序的分数和标签,**中间**玩具问题的精度-召回图,**【右】**不同阈值选择的精度和召回。
对于玩具问题,图 2(左)显示了在不同阈值下,基于初级分类的分数排名,(中)精确回忆曲线,(右)精确回忆曲线。需要阈值图来决定工作点。比方说,我们要以 90%的精度操作,看 PR 图,可以看到召回率是 50%。对应的阈值是‘0.6’,这意味着如果分类器的得分≥0.6,我们应该将一个示例分类为‘初级’类,其余的分类为‘次级’类。这些图本身是插值的,对于这个玩具问题,我们只有 8 个例子。在 P=0.75,R=0.75,th=0.3 时,F1 得分最高,为 0.75。
请注意,PR 曲线对测试集中的类别比例很敏感。如果类别比例不同,两个不同的 PR 曲线不能在两个不同的测试集之间进行比较。与小学班级的 10:90 比例的测试集相比,50:50 比例的测试集通常具有更高的分数。这是一些人可能更喜欢基于 TPR/FPR 的指标的地方,因为它们针对等级不平衡进行了标准化。但是,如果在实际环境中,您预计会出现不平衡,PRC 指标可能更容易解释。
## 主要类别的精确召回覆盖率

**图-3** :主要类的精确召回覆盖率
在上面的场景中,我们“总是”做出主次分明的决定。在任何操作阈值(th)下,无论是在特定精度下,还是回忆或基于最佳 F1 分数,所有测试项目≥th 都被标记为“主要”,< th 被标记为“次要”。如果我们可以灵活地“不”对物品进行分类,那会怎么样?也许我们想“确保”我们同时以高精度和高召回率运行。也许我们有审计带宽来标记一些项目,但希望挑选对系统来说最困难的项目。假设我们已经花了足够的时间来优化特征、分类器、参数、训练集等。“不分类”是保证高精度和高召回率的一种选择。在这种情况下,我们分类的项目的%年龄被称为覆盖率。这里的权衡是如何在高精度和高召回率下最大化覆盖率。其思想是,如果分数在“上限”和“下限”之间,则不进行分类,并计算剩余样本的精确度和召回率。例如,在图 3 中,如果我们选择上限阈值≥0.7,下限阈值< 0.1,则我们将实现 P=1.0,R=1.0,C=3/8=37.5%。一般来说,最好将第二个类作为一个替代类,并采用精度覆盖度量,如下所示。
## 精确覆盖小学和中学(PPV,NPV)
小学和中学的精确覆盖率也是一个有用的指标。它用于两个类别同等重要的情况(例如,准确地分为男性和女性)。它们也被称为 PPV(正预测值)率和 NPV(负预测值)率。二元问题的这些度量可以用两种不同的方法计算,都可以得到相同的答案。第一种方法与“精确-召回-覆盖”图的方法相同——使用两个不同的阈值,在排序的主要分数上的上限和下限。项目>上部将被分类为初级和

**图-4:** 计算初级和二级精度覆盖率的替代方法。
如图 4 所示,我们分别收集分类为主要和次要的项目,然后根据每个类别的期望精度找到两个不同的阈值。在图中,初级的阈值≥0.7,次级的阈值≥0.8(与初级分数≤0.2 相同),导致 P_primary=2/2 =1.0,P_secondary=3/4=0.75,C=6/8=75%。
# 多类分类
多类分类问题是当我们想要从“C”类中挑选一个作为标签时。通常情况下,所有的类都是感兴趣的。多类分类器可以以一对一的方式训练,这相当于为每个类学习一个分类器,或者作为单个联合分类器,例如使用 softmax 损失函数,其返回概率分布。与二进制一样,“分类精度”是人们可以选择来评估多类分类器的最简单的度量。分类准确度有两种变体—微观和宏观。微精度是每个实例的平均值,这意味着实例越多的类权重越高,因此对性能的贡献也越大。为了计算宏观精度,我们首先计算每个类的精度,然后平均它,确保每个类得到相同的权重。对于产品分类问题,如果您希望确保目录符合预期的性能,微精度是有意义的。如果您希望确保目录中的每个类别都是准确的,那么宏观准确性是有意义的。

**图-5:** 3 类数据集 a)具有地面真值的输入数据,b)所有三个类的输出分数,c) Max per-row 和 Max 的对应索引作为预测标签。

**图-6:** 混乱矩阵
例如,对于上面的玩具问题,微分类准确度是 75%,这是 6/8,因为总共 8 个标签中有 6 个标签匹配。使用图 6 所示的混淆矩阵可以最好地计算出每类的精度。一级精度= 3/4,二级精度= 1/2,三级精度= 2/2,宏观精度= (0.75 + 0.5 + 1)/3,顺便说一下,这本身就是 75%。
## 精确覆盖(全局和每个类别)
多类分类问题的一个实用度量是能够以期望的精度对数据进行分类,并计算准确分类的目录百分比,即覆盖率。与微观和宏观精度一样,可以在目录级别计算精度(其中主导类将主导精度),或者如果我们希望每个类都精确,我们可以选择选取一个阈值,使每个类都高于所需精度,然后计算 coverage。这两个指标都是通过对最大分数进行排序来计算的。

**图-7:【左】**根据最高分排序的分数和标签,**中间**玩具问题的精度-覆盖率图,**【右】**不同阈值选择的精度和覆盖率。
对于玩具问题,图 7(左)显示了基于最高分的分数排名,(中)精度-覆盖率(PC)曲线,(右)不同阈值下的精度和覆盖率。需要阈值图来决定工作点。比方说,我们希望以 95%的精度运行,查看 PC 图,我们可以看到覆盖率为 55%(插值)。对应的阈值是‘0.7’,也就是说如果 max 评分≥0.7,我们就要把一个例子归类,其余的为‘无法归类或 UTC’。这些图本身是插值的,对于这个玩具问题,我们只有 8 个例子。

**图-8:** 每类精度覆盖率。
为了计算每个类的阈值,我们将基于预测的类旋转输出,然后计算每个类的阈值,这样每个类都有 95%的精度。这并不类似于图 4 中计算小学和中学精确覆盖率的另一种方法。对于玩具多类问题,这导致 1 类覆盖率(精度> 95%)为 3/3,2 类覆盖率(精度> 95%)为 0/2,3 类覆盖率(精度> 95%)为 2/2,覆盖率(精度> 95%)=(3+0+2)/8 = 62.5%。覆盖范围的增加并不是因为我们对于等级 3,我们可以承受一个更低的阈值 0.4,根据玩具问题。在现实世界中,您可能希望每个类都有最小的计数,以确保健壮性。
## 精确召回率(每类)
可以将 C 类多类分类器视为 C 个独立的二进制分类器,并报告每个二进制分类器的精确召回度量,将特定类视为主要类,将所有其他类视为次要类。
曾经可以使用混淆矩阵(图 6)对 PR 进行点估计。对于每个类,正确分类到该类中的实例百分比是精度,检索到的实例百分比是召回率。例如,对于类-1,精度和召回率都是 3/4 = 75%,类-2: P=1/2,R=1/2,类-3: P=2/2,R=2/2(同样,精度和召回率恰好相同)。为了计算整个 PR 曲线,输出矩阵的每一列都可以作为初级类的分数。遵循类似于二元 PR 曲线的方法,我们可以构建 C 条不同的 PR 曲线,每条曲线对应一个类别。

**图-9:[左]** 所有类的分数,**【中】**基于类 1 分数的排序示例,**【右】**所有类的精确召回曲线。
对于上面的 3 类问题,图 9(左)显示了所有类的分数。在中间,突出显示了基于第 1 类分数的排序示例,在右边,绘制了所有类的精确-召回曲线。
# 多标签分类
在下一篇博客中。
# 实用 cy thon——音乐检索:去趋势波动分析(DFA)
> 原文:<https://towardsdatascience.com/practical-cython-music-retrieval-detrended-fluctuation-analysis-dfa-7935aa84e289?source=collection_archive---------23----------------------->
## [实践教程](https://towardsdatascience.com/tagged/hands-on-tutorials)
这是我关于 Cython 的第三篇教程。这一次我们将实现去趋势波动分析(DFA ),这是一种广泛应用于时间序列分析的技术,范围从音乐到金融

我们能揭开长期事件隐藏的本质吗?图片由[艾萨克·史密斯](https://unsplash.com/@isaacmsmith)在 [Unsplash](https://unsplash.com/photos/AT77Q0Njnt0) 上拍摄
<https://medium.com/@stefanobosisio1/membership>
欢迎回到我的 Cython 教程!今天我们将讨论一种广泛使用的技术,叫做[去趋势波动分析(DFA)](https://en.wikipedia.org/wiki/Detrended_fluctuation_analysis) 。DFA 在音乐 <http://citeseerx.ist.psu.edu/viewdoc/download;jsessionid=3EC936FFC4219B5D769FDACC62EC6464?doi=10.1.1.73.8209&rep=rep1&type=pdf> <https://royalsocietypublishing.org/doi/pdf/10.1098/rspa.2011.0118>和金融 <https://www.researchgate.net/profile/Hui_Xiong10/publication/302473805_Detrended_fluctuation_analysis_of_multivariate_time_series/links/5f88ed75299bf1b53e2bf44e/Detrended-fluctuation-analysis-of-multivariate-time-series.pdf> [⁴](https://people.engr.ncsu.edu/jwilson/files/mfdfa-pafts.pdf) 中得到了广泛的应用,能够捕捉时间序列和非平稳信号中的相关趋势——即随时间变化的信号。自 50 年代以来,这种分析方法的思想简单直接,并且在数学上有很好的基础。一旦你理解了基本的实现,你将能够进一步扩展这个分析,创建一个多趋势分析以及混沌系统[波动分析](https://www.hindawi.com/journals/mpe/2016/2865195/)的扩展。在本教程的最后,你将掌握 DFA 技术,并了解如何在 Cyhton 中处理多个 C 文件,以及在编译时做什么。
所有代码都存储在这个存储库中:
<https://github.com/Steboss/music_retrieval/tree/master/dfa>
这次没有对应的版本,但是许多用户已经实现了他们自己的 Python DFA 版本,例如:[https://github.com/dokato/dfa](https://github.com/dokato/dfa)
# 外行理论
DFA 的起源可以追溯到 50 年代,这要归功于英国水文学家 Harold Hurst 所做的开创性工作,他致力于确定尼罗河的最佳大坝尺寸,尼罗河自埃及时代起就支撑着文明。即使这看起来是一个非常简单的任务,数学上确定尼罗河流量和正确的大坝高度总是受到不可预见的降雨和干旱条件的高度影响,使这成为几个世纪以来的一个挑战性问题。作为一名优秀的工程师,赫斯特决定着手解决这个问题,从分析他能得到的所有可能的数据开始,这些数据来自尼罗河和所有汇合的河流。因此,赫斯特分析了尼罗河的累积流量(690 个不同的时间序列,75 个特征,如地球物理现象、降雨量、温度等……全部手工完成!)及其随时间变化的汇合流,确定一个数学参数,称为“调整范围” *R* ,作为在规定的环境条件下特定时间点从河流偏离的最大值和最小值之间的差值。将该参数除以(标准化)数据标准偏差,Hurst 获得了重新调整后的范围 *R/S* ,这是一个无量纲数,证明是特定时间段内理想坝高的代表。此外,Hurst 发现在每种情况下,统计参数 *R/S* 与分析周期 *n (n* 可以是一年、两年等)成比例。)具有类似于 *nᵏ* 的幂的行为,平均值为 *k* =0.72 +/- 0.01。
好了,现在这个“赫斯特”数字有什么特别之处?嗯,如果所有这些历史事件都遵循正态分布,正如许多自然事件通常预期的那样,这个数字应该等于 0.5。然而,这在大坝设计中是一个潜在的危险假设,因此大坝会被设计得太低。正如赫斯特所证明的,考虑到多雨和干旱的情况,k = 0.72 是一个长期行为的基础,因此在这些自然事件的统计分布中隐藏了一些东西。由于当时知识的匮乏,赫斯特没能清楚地解释这个结果,但他为*“长期”记忆事件*做了铺垫。
大约 20 年后,伟大的数学家 Benoit Mandlebrot 在 Hurst 的结果中发现了 t 2 现象的重尾和分形行为。赫斯特指数(称为*赫斯特指数—* ,从现在开始用 H 表示)描述了分析数据中的“[自相似](https://en.wikipedia.org/wiki/Self-similarity)现象。按照这种观点,自相似意味着如果一个信号 *Y(t)* 是一个在某个时间点 *ct* (例如今天)被分析的信号(例如,亚马逊股票价格,随时间变化 *t* ),其中 *c* 是任何数字,存在类似于*cᴴy(t*的比例关系,其中 *H* 是赫斯特指数。
这样一个非凡的发现让曼德勃罗和他的合作者范·内斯调查了大量不同的现象,比如经济、音乐、水文。[信不信由你,但所有这些现象都表现出一种 *1/f* 噪声波动行为,即所有这些序列都有一个确定的时间行为,具有长程相关性,根据这些值确定一个序列在时间](https://en.wikipedia.org/wiki/Pink_noise)内的稳健性:
* *H* > 0.5:信号中存在长时间相关性
* *H* ~ 0.5:信号完全随机
* *H* < 0.5:信号中存在反持续/均值回复行为
经过几十年的研究,越来越多的信号被分析,越来越多的技术被发现来寻找长期的相关性。*去趋势波动分析(DFA)* 就是这些技术中的一种,它*决定了一个信号的统计自相似性,所以如果一个信号具有长记忆*过程/相关性/ *1/f* 波动。在实践中,*获得了赫斯特指数,该指数被证明对于非平稳信号也是有效的。*
数学上,DFA 可以分解并定义为四个步骤:
* 计算由 *N* 个样本组成的信号 *x(t)* 的平均累积和 *Xt*
* 将平均累积和 *Xt* 细分为 *n* 个样本,并为每个子样本计算最小二乘线性拟合 *Yt。*在不同的尺度上重复这个过程,即从将信号分成 *n 个*小样本到 n 个大样本
* 由此计算消除趋势的波动,作为每个标度的平均累积和 *Xt* 和拟合 *Yt,*之间的均方根偏差:

等式 1 消除趋势波动计算为平均累积和 Xt 与拟合 Yt 之间的 RMS
* 将 *F(n)* 表示为具有数量为 *n* 个样本的*对数函数*,并找到指数行为(DFA-Hurst 指数)
现在我们已经准备好处理 DFA 实现了!
# Cython 的 DFA:让我们玩得开心
## 行动(或活动、袭击)计划

图 1:从信号的角度理解 DFA 的实现。首先,计算信号 X(t)的平均累积和 Xt。Xt 被细分为 N 个不同尺度的样本。对每个 X 块执行线性拟合,并计算和累加 RMS。最终 RMS 是不同比例下每个 Xt 块的 RMS 总量。最后,RMS 用 log2 进行变换,并计算线性拟合以获得 Hurst 指数。
图 1 定义了如何计算 DFA:
* 计算信号 *X(t)* 的平均累积和 *Xt* ,可以认为是一个简单的过程:
图 2:信号 X 的平均累积和计算示例
* 将 *Xt* 细分为 *n* 个不同尺度的子样本。为了确定尺度以及将 *Xt* 分成多少个子样本,我们可以从一个数组`scales`开始,其中包含我们想要使用的所有尺度。然后,对于每个标度,计算我们可以获得多少子样本,并用来自 *Xt* 的所有子样本填充临时数组`tmpX`
图 3:子样本的创建。首先创建一个我们想要细分 AvgCumsum Xt 的比例数组,然后用每个比例的 shape1 样本填充一个临时数组
* 对于带有`curr_scale`样本的每个`tmpX`,我们执行线性最小二乘拟合,并计算每个块的拟合优度。拟合优度有助于确定均方根(RMS)误差,因此累积所有这些误差
* 一旦每个标度的子样本拟合完成,就可以根据上一步存储的部分均方根值计算最终均方根值
* 将最终 RMS 转换为 log2,并计算线性拟合。拟合的斜率与赫斯特指数一致
现在我们已经对如何计算 DFA 有了一个完整的想法,我们可以继续细分 Python-Cython 和 C 之间的实现:
* entrypoint 始终是一个 Python 代码,它将输入信号编码并发送给 Cython。
* Cython 必须为 C 做好准备,首先将输入信号转换为可读的 C 格式,如 memory view,并初始化所有必要的数组以从 C 中检索信息,如 RMS(或波动)和拟合系数以检索 Hurst 指数。最后,Cython 调用 C 函数来执行 DFA 计算,用 C 指针绑定内存视图,如`&cython_memoryview[0]`
* 第一次,我们将不得不处理多个 C 文件:一个用于计算 DFA,另一个用于线性拟合。此外,我们将需要编写一个函数来计算平均累积和,一个函数来执行 log2 变换和地板除法,以及一个主函数与 DFA 计算。所有这些信息都将被发送回 Cython。

图 4:用 C 实现 DFA 并与 Cython 和 Python 交互的行动计划
## Python 脚本
代码:[https://github . com/ste boss/music _ retrieval/blob/master/DFA/installer/tester . py](https://github.com/Steboss/music_retrieval/blob/master/dfa/installer/tester.py)
Python 代码通常是我们的应用程序的入口点。在这里,我们接收输入数据,将它们转换成合适的格式。作为这个代码中的一个例子,我们正在处理一个随机信号——`scipy.signal.hilbert`变换只是为了避免负的幅度值,但它只是为了训练的目的。
一旦输入被处理,可以用下面的代码行调用`dfa`:`scales, fluct_to_array, coeff_to_array = dfa.play(X, 5, 9, 0.25)`:
* 为了在 Cython 中激活 DFA 分析,我们将使用`play`属性
* `play`接收输入:`X`信号,`5, 9, 0.25`最小样本细分(2⁵)最高样本(2⁹)和间隔间距 0.25
* `dfa.play`函数返回输入信号`X`已被划分的`scales`,所有样本的 RMS 为`fluct_to_array`,而`coeff_to_array`包含赫斯特指数
最后一步,我们希望可视化 DFA 函数以及 log2 变换和拟合:
图 5:快照,RMS (fluct_to_array)图作为双对数图,作为样本比例的函数
## Cython 脚本
代码:[https://github . com/ste boss/music _ retrieval/blob/master/DFA/installer/DFA . pyx](https://github.com/Steboss/music_retrieval/blob/master/dfa/installer/dfa.pyx)
在`pyx`代码中立即弹出的第一件事是 2 C 代码的声明:
图 6:项目中使用的所有 C 代码的声明
第一个代码是`dfa.c`,它是主 DFA 算法运行的地方,第二个代码是`polyfit.c`,它被`dfa.c`用来运行任何函数的多项式拟合。如你所见,`polyfit.c`是 DFA 例程的附属元素,但是我们必须在这里或者在`setup.py`文件中声明它,以便 Cython 读取 c 的所有依赖项。我决定不在`setup.py`中声明`polyfit.c`,以便保持设置脚本清晰易懂,因此我们只需担心声明中的`dfa.c`(见最后一位 TODO)。如果我们不在任何地方声明`polyfit.c`会怎么样?你可以编译 Cython 代码,但是,在运行时,你会得到一个`flat namespace`错误:
- python tester.pyTraceback (most recent call last):File "tester.py", line 2, in
import dfaImportError: dlopen(.../dfa.cpython-38-darwin.so, 2): Symbol not found: _polyfitReferenced from: .../dfa/installer/dfa.cpython-38-darwin.soExpected in: flat namespacein .../dfa/installer/dfa.cpython-38-darwin.so
这个错误告诉我们 Cython 无法通过`dlopen`加载动态库文件`_polyfit`——注意`_polyfit`中的下划线表示`polyfit`函数受到保护,任何人都无法访问。这在 Cython 中很常见,所以一定要声明所有的代码。
其次,值得强调的另一个错误来源是使用`cpdef.` `cpdef`定义了一个可以在 C 和 Python 之间共享的函数,不同于`def`只能被 Python 读取而不能被 C 读取,以及`cdef`只能被 C 读取而不能被 Python 读取。因此,编写`cdef`而不是`cpdef`会使属性`play`对 Python 隐藏,而`dfa.play()`会引发类似`dfa has not attribute play`的错误。
此代码中的第三件重要事情是正确解析从 NumPy 到 Cython 到 c 的秤类型。从 Numpy 文档中可以检索到下表:
NumPy dtype Numpy Cython type C Cython type
np.bool_ None None
np.int_ cnp.int_t long
np.intc None int
np.intp cnp.intp_t ssize_t
np.int8 cnp.int8_t signed char
np.int16 cnp.int16_t signed short
np.int32 cnp.int32_t signed int
np.int64 cnp.int64_t signed long long
np.uint8 cnp.uint8_t unsigned char
np.uint16 cnp.uint16_t unsigned short
np.uint32 cnp.uint32_t unsigned int
np.uint64 cnp.uint64_t unsigned long
np.float_ cnp.float64_t double
np.float32 cnp.float32_t float
np.float64 cnp.float64_t double
np.complex_ cnp.complex128_t double complex
np.complex64 cnp.complex64_t float complex
np.complex128 cnp.complex128_t double complex
在第 55 行,在 Numpy 中创建的秤的名称为:`scales=(2**np.arange(scale_low, scale_high, scale_dense)).astype(np.intc)`。最终铸件,`astype(np.intc)`保证了从 NumPy 型到 C 型`int`的正确转换。这通常是错误的来源,因为错误的格式从 Numpy/Cython 传递到 C,导致分段错误
最后,Cython 代码还处理输入数据到存储器视图的正确转换,以及 RMS 和系数数组的初始化:
图 7:初始化并将所有数据转换成内存视图,这样它们就可以在 C 中被读取
然后调用主 C 函数`dfa`,内存视图作为`&memoryview[0]`传递,以便从 C 代码中检索所有最终值。正如这里的[所解释的](http://docs.cython.org/en/latest/src/userguide/memoryviews.html#specifying-more-general-memory-layouts),由于当前 memoryview 的大小是已知的,底层数据是适当连续的,因此传递一个指向第一个 memory view 元素的指针(`&a[0]`)就足以使它们在 c 中工作。
## c 代码
代码:[https://github . com/ste boss/music _ retrieval/blob/master/DFA/c _ code/DFA . c](https://github.com/Steboss/music_retrieval/blob/master/dfa/c_code/dfa.c)
C 代码实现了主要的 DFA 计算。起初代码可能看起来很吓人,但是让我们一步一步来处理它。
图 8 是最开始的步骤:导入基本库,定义常量和基本函数。最重要的函数是`cumsum_averaged`,它在给定输入信号`float *X`的长度`int n_elems`和信号平均值`float avg`的情况下,计算输入信号`float *X`的平均累积和。最终结果与`float *Xcumsum`指针相关联,所以确切的值在主`dfa`函数中报告
图 8:导入必要的库并为 DFA 定义常量和基本函数
在这些必要的导入之后,可以定义主要的`dfa`函数,考虑 3 个主要的块。在第一位中定义了`fitting_order`,即我们想要执行的拟合类型、平均累积和数组`Xcumsum`、RMS 数组`rms`和拟合的线系数向量`tmpCoeff`:
图 dfa 功能的第一步。定义并初始化向量和拟合顺序
第二步(图 10)是通过 Cython 生产的所有秤`scales`的 for 循环,定义:
* 由当前第 I 个尺度`curr_scale`细分的信号的最终尺寸`shape1`(例如,信号有 1000 个元素,第一个尺度是 2⁵ = 32,所以`shape1 = floor_div(1000, 32) = 31`因此我们将循环通过`Xcumsum` 31 次以将其细分为 32 个)
* -空向量`tmpX`,其尺寸为`curr_scale`,以便存储`Xcumsum`信号的所有块(例如,在通过`Xcumsum`的 31 个周期中,向量`tmpX`每个周期将存储 32 个样本)
* 空向量`tmpDiff`,其大小为`curr_scale`,用于计算`tmpX`信号块与其线性拟合之间的元素-元素差异(例如,一旦`tmpX`具有 32 个样本并且针对这些样本的拟合完成,`tmpDiff`将存储元素-元素差异,因此元素 0 具有拟合元素 0,元素 1 具有拟合元素 1,等等)
* 一个空向量`tmpShape1`,其大小为`shape1`以存储来自`tmpDiff`向量的当前 RMS,计算如下:`tmpShape1[j] = sort(vector_avg(tmpDiff, curr_scale))`
图 10:DFA 功能的第二步。循环通过所有标度,信号 Xcumsum 被细分并初始化 rms 阵列
最后一步是 DFA 的核心,在这里计算所有的量:
* `for j=0 ... j=shape1-1`细分`Xcumsum`信号,并将所有样本存储在`tmpX`(例如,假设`shape1=31`,在第一次迭代中,信号`Xcumsum`将从 0 读取到 31,这些样本将被保存到`tmpX`
* 用`polyfit`对`tmpX`进行线性拟合。`polyfit`将 x 轴(`scale_ax`,它简单地从 0 到`shape1`)、具有第一个`curr_scales`样本的 y 轴函数`tmpX`、`tmpX`的长度、`fitting_order`和存储拟合系数的指针`tmpCoeff`作为输入
* 从拟合的系数中检索拟合线:
图 11:从拟合的系数(斜率和截距)定义拟合线
* 通过计算`tmpX`元素和`fitting_results`元素之间的 RMS 来确定拟合优度。最后,将这些结果存储在`tmpShape1`中
图 12:计算每个 tmpX 值和拟合值的元素式 RMS。
* 在第二个周期结束时,记住释放所有必要的向量,并从`tmpShape1`开始累加总数`rms`:
图 13:根据逐元素均方根阵列计算最终均方根
恭喜你。您设法处理了 DFA 实现中最复杂的步骤。DFA C 代码的最后几行非常简单,计算 RMS 的`log2`变换,以返回 Hurst 指数:
图 14:RMS 的 log2 变换和计算赫斯特指数的线性拟合
如前所述,`polyfit`接收作为输入的 x 轴,在这种情况下是从 1 到`scales_len` log2 转换的值,y 轴函数,在这种情况下是 log2-RMS,x 和 y 轴的长度,`fitting_order`和`coeffs`指针,以返回指向主函数的指针。最终,`coeffs[1]`包含赫斯特指数,RMS log2 图可以很容易地用 python 可视化:
图 15:用 Python 可视化 log2-RMS 图并检索赫斯特系数
## 准备 setup.py 并安装所有东西
定义 Mac 警告 so -arch 和 np.get_include()
和前面的[教程](/practical-cython-music-retrieval-short-time-fourier-transform-f89a0e65754d)一样,代码被分成两个文件夹:`c_code`,存放`dfa.c`和`polyfit.c`,以及`installer`,存放`pyx`和 Python 文件。`setup.py`是安装文件,保存在`installer.`安装程序文件遵循这里的代码:[https://github . com/ste boss/music _ retrieval/blob/master/DFA/installer/setup . py](https://github.com/Steboss/music_retrieval/blob/master/dfa/installer/setup.py)。我确保这个设置可以在 Mac 上运行,为 c 编译器`os.environ['ARCHFLAGS']='-arch x86_64`添加了正确的架构,包括正确的 NumPy 头文件(例如 arrayobject.h)和`Extension`下的`include-dir=[numpy.get_include()]`
一旦代码准备好了,我们就可以像下面这样运行`setup.py`:
python setup.py build_ext --inplace
`build_ext`是构建当前扩展的命令,`--inplace`允许在工作目录中安装当前包。将输出文件复制到`dfa`文件夹中,就可以开始导入 DFA Python 包了
# 实践中的 DFA
总结最后一个使用 DFA 处理财务数据的例子。在我的下一篇博文中,我将展示一个应用于音乐的进一步例子——敬请关注!
## Etsy,Shopify,UPS 的案子
统计学/机器学习/深度学习/超强度学习的圣杯之一就是预测股市。不幸的是,类似的算法并不那么具有预测性,其中许多算法都存在数据泄露的问题,这使得预测变得非常完美——这在 Medium 上经常发生。我决定做的是应用 DFA,检查雅虎列出的所有金融股票(约 856 只股票)是否存在某种长期相关性或趋势。不足为奇的是,大多数股票序列似乎具有布朗行为,即信号中没有相关性。Etsy、Shopify 和 UPS 出现了一个令人惊讶的案例,具有几乎完美的 1/f 噪声,即赫斯特指数(此处用α表示)接近 1,这表明时间序列具有自相似性:

图 16 赫斯特系数约为 1.09 的 ETSY 股票

图 17 Shopify 股票,赫斯特系数~ 1.12

图 18: UPS 股票,赫斯特指数~ 1.17
我希望你喜欢 Cython 上的这个新教程:)如果有任何问题或意见,请发邮件给我,地址是:stefanobosisio1@gmail.com
# 文献学
1. 音乐信号的去趋势波动分析:可舞性估计和进一步的语义表征,s .施特雷希,P. Herrera,2005
2. 通过多重分形去趋势波动分析揭示音乐中的竞争行为:应用于巴赫的交响乐
3. 多变量时间序列的去趋势波动分析,熊宏,尚,2016
4. 多重分形去趋势波动分析:金融时间序列的实际应用,J. R. Thompson,J. R. Wilsonb,2014 年
# 实用数据可视化指南:Seaborn vs Ggplot2
> 原文:<https://towardsdatascience.com/practical-data-visualization-guide-seaborn-vs-ggplot2-9747d9153ade?source=collection_archive---------26----------------------->
## 带示例的实践教程

约尔根·哈兰在 [Unsplash](https://unsplash.com/s/photos/twice?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 上的照片
数据可视化是数据科学的重要组成部分。揭示变量之间的关系有助于更好地理解数据。还可以使用设计良好的数据可视化来探索数据集中的底层结构。
在本文中,我们将比较两个流行的数据可视化库:Python 的 Seaborn 和 r 的 ggplot2。
我们将使用著名的泰坦尼克号数据集来创建可视化。您可以从 Kaggle 下载“ [train.csv](https://www.kaggle.com/c/titanic) ”文件进行跟进。
第一步是导入库并创建数据框。我们将使用 Python 的 Pandas 库和 R 的 data.table 库来处理数据操作。
import numpy as np
import pandas as pd
import seaborn as sns
sns.set(style='darkgrid')titanic = pd.read_csv("/content/titanic.csv")titanic.drop(['PassengerId', 'Name', 'Ticket'],
axis=1, inplace=True)titanic.head()

(图片由作者提供)
library(ggplot2)
library(data.table)> titanic <- fread("/home/soner/Downloads/datasets/titanic.csv")
titanic[, c("PassengerId", "Name", "Ticket"):=NULL]
head(titanic)

(图片由作者提供)
现在,我们已经将数据集保存在适当的数据结构中。让我们从创建散点图开始。
散点图是一种关系图,通常用于显示两个数值变量的值。我们可以观察它们之间是否有关联。
Seaborn:
sns.relplot(data=titanic, x="Age", y="Fare", hue="Survived",
kind='scatter', aspect=1.4)

Seaborn 散点图(图片由作者提供)
Seaborn 的 relplot 函数创建不同种类的关系图,如散点图或线图。绘图类型由 kind 参数指定。我们将要绘制在 x 轴和 y 轴上的列分别传递给 x 和 y 参数。hue 参数根据给定列中的类别,通过对每个类别使用不同的颜色来分隔数据点。最后,aspect 参数调整图形的宽高比。
Ggplot2:
ggplot(data = titanic) +
-
geom_point(mapping = aes(x = Age, y = Fare, color = Survived))

Ggplot2 散点图(图片由作者提供)
第一步是创建一个空图的 ggplot 函数。数据被传递给 ggplot 函数。第二步根据给定的映射和绘图类型在图上添加一个新层。geom_point 函数创建散点图。要绘制的列在 aes 方法中指定。颜色列与 Seaborn 库中的色调参数相同。
我们没有观察到年龄和票价之间的特殊关系,这是一种预期。
我们使用颜色参数根据幸存的列来分隔数据点。看起来付钱多的乘客有更高的生存机会。
我们可以创建一个直方图来检查数字变量的分布。通过将值范围划分为离散的仓来创建直方图,并且每个仓中的数据点(或值)的数量用条来可视化。
让我们也在不同的情节上展示幸存和未幸存的乘客。
Seaborn:
sns.displot(data=titanic, x="Age", col="Survived", kind="hist")

年龄直方图(图片由作者提供)
col 参数通过创建单独的子图来分隔数据点。如果我们使用 row 参数,支线剧情将被创建为行。
Ggplot2:
t <- ggplot(titanic, aes(Age)) +
-
geom_histogram(bins=10, fill='lightblue')
t + facet_grid(cols=vars(Survived))

年龄直方图(图片由作者提供)
在 ggplot2 库中,我们可以使用 facet_grid 函数根据给定列中的类别创建一个子图网格。它类似于 Seaborn 中的 FacetGrid 对象。
对于最后一个示例,我们将使用 row 和 col 参数创建一个更大的绘图网格。在前面的例子中,我们看到在 fare 列中有几个异常值。我们将首先过滤掉这些观察结果,然后生成图表。
Seaborn:
titanic = titanic[titanic.Fare < 300]sns.relplot(data=titanic, x="Age", y="Fare", kind="scatter",
hue="Survived", row="Sex", col="Pclass",
height=4)

(图片由作者提供)
我们清楚地看到,1 等舱的乘客比其他人更有可能生还。另一个发现是,女性乘客比男性乘客有更高的生还几率。
Ggplot2:
可使用 ggplot2 库创建相同的绘图网格,如下所示:
titanic <- titanic[Fare < 300]> t <- ggplot(titanic, aes(x=Age, y=Fare, color=Survived)) + geom_point()
t + facet_grid(cols=vars(Pclass), rows=vars(Sex))

(图片由作者提供)
虽然语法不同,但方法是相似的。我们通过使用颜色、行和列参数来添加维度。
## 结论
Seaborn 和 ggplot2 都是功能强大的通用数据可视化库。我认为两者都足以完成典型的数据可视化任务。
使用哪一种取决于您对编程语言的选择。因为 Python 和 R 都是数据科学生态系统中的主流,所以它们中的任何一个都可以为您完成这项工作。
感谢您的阅读。如果您有任何反馈,请告诉我。
# Python Altair 数据可视化实用指南
> 原文:<https://towardsdatascience.com/practical-data-visualization-guide-with-python-altair-347834d22176?source=collection_archive---------30----------------------->
## 探索墨尔本房价

哈姆扎·扎伊迪在 [Unsplash](https://unsplash.com/s/photos/melbourne-city?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 上的照片
数据可视化是探索性数据分析不可或缺的一部分。一个设计良好的可视化比简单的数字能提供更多的洞察力。因此,在数据科学生态系统中有几个数据可视化库。
在本文中,我们将尝试探索并深入了解 Kaggle 上的墨尔本房产[数据集](https://www.kaggle.com/dansbecker/melbourne-housing-snapshot)。
Altair 是 Python 的统计可视化库。它的语法清晰易懂,我们将在示例中看到。用 Altair 创建交互式可视化也非常简单。
Altair 在数据转换方面非常灵活。在创建可视化时,我们可以应用许多不同种类的转换。它使得库对于探索性数据分析更加有效。
我们将使用 Pandas 来读取数据集和一些数据操作任务。让我们从导入库和读取数据集开始。
import numpy as np
import pandas as pd
import altair as altmelb = pd.read_csv("/content/melb_data.csv", parse_dates=['Date']).sample(n=5000)melb.shape
(5000,21)melb.columns
Index(['Suburb', 'Address', 'Rooms', 'Type', 'Price', 'Method',
'SellerG','Date', 'Distance', 'Postcode', 'Bedroom2', 'Bathroom', 'Car','Landsize', 'BuildingArea', 'YearBuilt', 'CouncilArea', 'Lattitude','Longtitude', 'Regionname', 'Propertycount'], dtype='object')
该数据集包含墨尔本大约 13580 所房屋的 21 个特征(即柱子)。然而,我创建了一个 5000 所房子的随机样本,因为 Altair 默认接受最多 5000 行的数据集。可以增加这个数字,但这超出了本文的目的。
目标变量是价格列。其他栏目应该能提供对房价的有价值的洞察。可以从查房价分布入手。
一种方法是创建直方图。它将连续变量的值域划分为离散的箱,并计算每个箱中的观察值(即行数)。
alt.Chart(melb).mark_bar().encode(
alt.X('Price:Q', bin=True), y='count()'
).properties(
height=300, width=500,
title='Distribution of House Prices in Melbourne'
)

(图片由作者提供)
我们来详细说明一下语法。存储数据的对象被传递给顶级图表对象。然后下一步是定义绘图的类型。
encode 函数指定在给定的数据帧中绘制什么。因此,我们在编码函数中写的任何东西都必须链接到数据帧。最后,我们使用 properties 函数指定绘图的某些属性。
make_bar 函数可用于创建带有数据转换步骤的直方图。在 encode 函数中,我们将价格变量的值域划分为离散的箱,并计算每个箱中数据点的数量。
大部分房子都在 200 万以下。我们也看到一些花费超过 400 万的异常值。
位置是决定房子价格的一个重要因素。我们可以想象到中央商务区(CBD)的距离对墨尔本房价的影响。由于两者都是连续变量,散点图是这个任务的好选择。
alt.Chart(melb).mark_circle(clip=True).encode(
alt.X('Distance'),
alt.Y('Price', scale=alt.Scale(domain=(0,5000000)))
).properties(
height=400, width=600,
title='House Price vs Distance'
)
这里我想强调几点。我已经使用 scale 属性排除了价格高于 500 万的异常值。为了使用 scale 属性,我们用 Y 编码(alt。Y('Price '))而不是传递一个字符串(y='Price ')。
另一点是 mark_circle 函数的 clip 参数。如果我们不将其设置为 True,则仍将绘制超出指定轴限制的数据点。相信我,情况不妙。
上面的代码生成的散点图是:

(图片由作者提供)
总的来说,与中央商务区的距离对墨尔本的房价有负面影响。
墨尔本有几个地区。我们先来检查一下数据集中每个区域的比例。
melb.Regionname.value_counts(normalize=True)Southern Metropolitan 0.3530
Northern Metropolitan 0.2830
Western Metropolitan 0.2106
Eastern Metropolitan 0.1096
South-Eastern Metropolitan 0.0338
Eastern Victoria 0.0042
Northern Victoria 0.0034
Western Victoria 0.0024melb.Regionname.value_counts(normalize=True).values[:4].sum()0.96
96%的房屋位于排名前四的地区。我们可以对比一下这些区域的房价。
一种选择是使用提供变量分布概况的箱线图。它显示了值是如何通过四分位数和异常值分布的。
alt.Chart(melb_sub).mark_boxplot(size=80, clip=True).encode(
alt.X('Regionname'),
alt.Y('Price', scale=alt.Scale(domain=(0,5000000)))
).properties(
height=400, width=600,
title='House Prices in Different Regions'
)
我只包括了价格低于 500 万英镑的房子。mark_boxplot 函数的 size 参数调整框的大小。

(图片由作者提供)
让我们花一些时间来定制这个可视化,使它看起来更好。我们将使用 configure_title 和 configure_axis 函数进行以下更改。
* 调整标题的字体大小
* 调整轴刻度和标签的字体大小
* 调整轴记号的旋转
* 删除 x 轴的标题,因为它已经在图的标题中描述
alt.Chart(melb_sub).mark_boxplot(size=80, clip=True).encode(
alt.X('Regionname', title=""),
alt.Y('Price', scale=alt.Scale(domain=(0,5000000)))
).properties(
height=400, width=600,
title='House Prices in Different Regions'
).configure_axis(
labelFontSize=14,
titleFontSize=16,
labelAngle=0
).configure_title(
fontSize=20
)

(图片由作者提供)
我觉得现在看起来更好更清晰了。
一般来说,南部大都市地区的房子更贵。该地区的价格也更加分散。在所有地区,我们观察到与整体价格范围相比价格非常高的异常值。
我们可能还想检查一年内房价的趋势。例如,价格可能会在某个特定的月份或季节上涨。
你可以改变频率,但我会画出平均每周房价。第一步是使用 Pandas 的 dt 访问器从日期中提取星期数。然后,我们使用 groupby 函数计算每周的平均价格。
melb['Week'] = melb.Date.dt.isocalendar().weekweekly = melb[['Week','Price']].groupby('Week',
as_index=False).mean().round(1)
我们现在可以创建一个线图。
alt.Chart(weekly).mark_line().encode(
alt.X('Week'),
alt.Y('Price', scale=alt.Scale(zero=False))
).properties(
height=300, width=600,
title='Average Weekly House Prices'
)

(图片由作者提供)
我们没有观察到特定的趋势,但是有一些峰值。可能需要进一步的研究来理解这些峰值。
## 结论
我们已经创建了几种不同类型的可视化来探索墨尔本住房数据集。
本文也可以被认为是 Altair 库的实用指南。本文中的可视化类型几乎可以应用于任何数据集。
如果你想了解更多关于 Altair 的知识,这里有一个我以前写的文章列表:
* 第一部分:[简介](/altair-statistical-visualization-library-for-python-cfb63847c0c0)
* 第二部分:[过滤和转换数据](/altair-statistical-visualization-library-for-python-part-2-4c8ce134e743)
* 第三部分:[互动剧情和动态过滤](/altair-statistical-visualization-library-for-python-part-3-c1e650a8411e)
* 第 4 部分:[定制可视化](/altair-statistical-visualization-library-for-python-part-4-9ec970fb12e8)
感谢您的阅读。如果您有任何反馈,请告诉我。
# 语义搜索机器人的实用评估指标
> 原文:<https://towardsdatascience.com/practical-evaluation-metrics-for-a-semantic-search-bot-334f6c2f9c7e?source=collection_archive---------20----------------------->

作者图片
## 人工智能中的产品度量指南
每一个在企业人工智能领域工作的数据科学家都必须或者将要与智能聊天机器人打交道。随着自然语言处理模型的激增,如伯特家族、GPT 家族和其他重量级模型,语义问答变得非常容易。
再加上像 Elasticsearch 这样的知识库提供商,它们允许定制搜索功能,机器人也变得高效了。
然而,当你建造一个智能机器人时,你需要量化它的性能。这对于弄清楚继续使用机器人是否是个好主意非常重要。因此,为你的机器人设计性能指标是非常重要的。
在这篇文章中,我将谈论一个基于知识库训练的问答机器人。这意味着有一个文档包含一组属于一个或多个主题的唯一问答对。
属于前语言模型时代的聊天机器人致力于词对相似性。这意味着给定两个句子,计算向量形式的组成单词之间的相似性(使用余弦相似性分数)。
然而,闪光的不都是金子。并非所有出现在最佳匹配列表顶部的答案都不一定是最佳答案。
这些模型的问题不在于衡量相似性的方式,而在于单词在句子中的表达方式。例如,“公园”这个词在句子“我需要把车停在某个地方”中可能有不同的意思,在“我们去公园散散步”中也可能有不同的意思。这种意义上的差异对机器人如何找到相似性产生了巨大的影响。旧的模型只寻找单词的相似性,而不是上下文。
输入语言模型!这些模型代表单词和句子。这有助于模型捕捉句子中单词的上下文。这就是语言模型对自然语言理解的附加值。
仍然使用每个句子的矢量形式的余弦相似度来计算单词相似度。
# 现在,我们如何比较两个聊天机器人模型的性能?
实际上,我们如何比较我们的聊天机器人模型和另一个模型的性能呢?
在聊天机器人框架内比较一个问题的两个候选答案似乎更容易——余弦相似度对!
但是就模型本身而言,比较一个模型与另一个模型的余弦相似性是没有意义的。例如,假设我们有两个候选模型 CB1 和 CB2。我们想比较一下这两种型号的性能。在我们的问答测试集中,我们可能会有一个问题“如何设置我的智能电视?”?’,聊天机器人候选人 A 可能返回以下前 2 名答案:
1. 按照以下方式设置您的 Samsung X50(相似性得分:0.87)
2. 按照以下方式设置您的银行账户(相似性得分:0.77)
候选 B 给出了以下相似性:
1. 按照以下方式设置您的银行账户(相似性得分:0.91)
2. 按照以下方式设置您的 Samsung X50(相似性得分:0.8)
候选人 B 在两个模型的顶部答案中得分最高。
但是我们当然可以看到候选人 A 给出了一个更准确的答案。因此,我们知道余弦相似性可能不是一个很好的性能指标。
那我们能做什么?
我们能做的一件事是:
1. 从测试集中抽取一个问题样本
2. 考虑每个问题的最佳答案池
3. 考虑以下方案的元评分系统:如果最佳答案是正确答案,则给它 1,如果最佳答案不正确但正确答案存在于模型返回的最佳答案集中,则给它 0.5,如果答案不在答案集中的任何地方,则给它 0
4. 对样本中的所有问题执行此操作将会给出两个聊天机器人中每个问题的分数
5. 合计每个聊天机器人的所有分数,然后用样本中的问题总数除以它们
如果样本中有更多的 1,那么计算出的分数将会很高。如果 0.5s 或者 0 多了,那么分数就低了。这意味着,一个模型给出正确答案的次数越多,它的表现就越好,而它给出错误答案的次数越多,分数就越低,模型的表现就越差。
然而,这种系统的一个非常明显的缺点是不容易实现自动化。然而,这并不难。我们需要为样本中的每个问题标记一组正确答案,当该组中的最佳答案与相似性分数为 1 的答案标记匹配时,该问题的分数为 1,如果它与该组中分数为 1 的答案之一匹配,该问题的分数为 0.5,如果没有答案匹配,则该问题的分数为 0。
现在你有了!一个简单实用的评分系统来评估你的聊天机器人的表现!
# 降维的实际例子
> 原文:<https://towardsdatascience.com/practical-example-of-dimensionality-reduction-d0525632c355?source=collection_archive---------19----------------------->
## 使用的技术:缺失值比率、高相关滤波器、递归特征消除(RFE)、主成分分析(PCA)

照片由 [Alex wong](https://unsplash.com/@killerfvith?utm_source=medium&utm_medium=referral) 在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍摄
***所有这些功能能帮我解决问题吗?我真的需要所有这些特性吗?我的数据集中的要素有多重要?***
> 如果您发现自己在数据科学项目中问这样的问题,那么这篇文章就是为您准备的。
在这篇文章中,我将介绍使用 Python 进行降维的四种技术。它们包括:
1. **缺失值比率**
2. **高相关滤波器**
3. **递归特征消除** ( *RFE* )
4. **主成分分析** ( *PCA* )
这些技术将在位于 Kaggle 上的 [**ANSUR 2 数据集**](https://www.kaggle.com/seshadrikolluri/ansur-ii?select=ANSUR+II+FEMALE+Public.csv) 上执行。该数据集代表了 2012 年美国陆军人体测量调查的信息,包括个人身体部位的**数字测量及其在军队中的角色**,以及其他特征。你可以自己去看看。
在我们深入实践之前,让我们确保您理解什么是降维以及为什么需要这样做。
## 什么是降维?
> 简而言之,降维是将数据集从高维空间转换到低维空间,同时保持其信息完整性以进行预测建模的过程。
**为什么要降维?**
1. *减轻* [*维数灾难*](https://builtin.com/data-science/curse-dimensionality) *(* 随着特征数量的减少而减少预测误差 *)*
2. *计算成本更低*
3. *更容易使数据可视化*
4. *从数据集中去除噪声*
5. *机器学习模型性能的提高*
降维分为两个部分:
* [**特征选择**](https://deepai.org/machine-learning-glossary-and-terms/feature-selection) **:** *通过丢弃包含少量信息的变量,将维度减少到原始数据集的一个子集。*
* [**特征提取**](https://deepai.org/machine-learning-glossary-and-terms/feature-extraction) **:** *通过识别每个变量内的关键信息来组合特征,创建新的特征。*
ANSUR 数据集上使用的技术属于这两类中的任何一类。
了解了降维及其必要性之后,让我们开始吧!
## 数据集
这项工作中使用的数据集是男性和女性 ANSUR 2 数据集的串联。如前所述,该功能包括军队中许多人的身体测量。利用这些测量,我将通过预测军队中个人的“C *成分*来测试相关特征。这项工作需要注意的一些要点如下:
* 基础**随机森林分类器**模型用于对所有方法进行预测。
* **“成分”(** *正规军、陆军国民警卫队、陆军预备役*)特征被用作目标变量。
## **基本统计&信息**
ANSUR 2 数据帧存储为对象 ***df*** 。原始数据集有 **109 个特征**和 **6068 个观察值**。
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 6068 entries, 0 to 6067
Columns: 109 entries, Unnamed: 0 to WritingPreference
dtypes: int64(101), object(8)
memory usage: 5.0+ MB
## 1.缺失值比率
要素选择方法缺失值比率听起来一模一样。该方法计算每个要素的缺失观测值的比率。下面的代码显示了这是如何工作的,并打印了要素和整个数据集中缺失值的比率。
[('Ethnicity', 0.765820698747528)]
种族特征有 76.6%的缺失值。在这种情况下,由于缺少大量值而简单地删除该特性是可以接受的。删除某个要素会导致信息丢失,因此,如果某个要素的缺失值比率较低,则插补方法更合适。一些常见的插补方法包括:
* ***意为*** *插补*
* [**插补**](https://machinelearningmastery.com/knn-imputation-for-missing-values-in-machine-learning/)
* ****热卡*** *插补**
* ****冷甲板*** *插补**
* ****预测回归*** *插补**
*更多关于这些插补方法及其背后的逻辑可以在 [**这里**](https://www.theanalysisfactor.com/seven-ways-to-make-up-data-common-methods-to-imputing-missing-data/) 找到。*
*对于这项工作,所有的**‘Object’数据类型都从数据集中删除,** *不包括'* ***组件*** *'(目标变量)和军队'* ***分支*** *',它们都是标签编码的。*得到的数据帧尺寸有 **101 个特征和 6068 个观察值。**对于其余技术,**该数据帧将被称为基础数据集**。*
*首先,机器学习模型( ***随机森林分类器*** )计算原始数据集的预测准确度。*由于随机森林分类器的随机性,取 20 次试运行的平均值。**
Predictive accuracy of base random forrest classifier 0.643
*基础数据集导致 **64.3%的预测准确性**。该值用作所有其他技术的参考。*
> *请记住,降维的目的是减少特征的数量,同时保持或提高模型的预测精度。*
## *2.高度相关滤波器*
*对于包含大量要素的数据来说,可视化非常困难,这使得对包含大量要素的数据集进行降维变得非常困难。寻找高度相关的特征也是如此。这种要素选择技术可确定哪些要素高度相关,并将其从数据集中删除。要确定哪个相关值适合您的数据集,需要执行以下几个步骤:*
1. *使你的数据正常化*
2. ***创建相关标准化绝对值的屏蔽***
3. ***迭代一系列高相关值**(本例中为*,0.85–0.99*)*
4. ***删除相关性高于可迭代的特征***
5. ***用删除的数据集测试预测精度***
*下面的代码展示了这个逻辑。*
{0.85: 0.6471, 0.86: 0.6488, 0.87: 0.6477, 0.88: 0.6523, 0.89: 0.6471, 0.9: 0.6499, 0.91: 0.646, 0.92: 0.6476, 0.93: 0.6514, 0.94: 0.6434, 0.95: 0.6468, 0.96: 0.6453, 0.97: 0.6465, 0.98: 0.65, 0.99: 0.6385}Correlation coefficient with the highest predictive accuracy 0.88Number of feature's dropped 17
*结果显示,0.88 的**相关值给出了最高的**预测准确度,为 65.2%** 。这比基础数据集增加了**0.9%**,减少了 **17 个特征**。***
## *3.递归特征消除(RFE)*
*下一个降维技术是基于随机森林分类器模型的特征重要性的手动递归特征消除方法。RandomForestClassifier()。feature_importances_ 用于查找预测目标变量的信息量最少的特征。*
****最不重要的特征从数据集中删除*** *,* ***创建新的训练和测试集*** *,计算* ***预测精度*** *为轨迹数(在本例中为 20)*。*
*这个逻辑的代码如下所示。*
**
*作者图片*
*上图显示了随着数据集中最不重要的要素数量的减少,模型的预测准确性如何降低。*
> *提示:在计算预测精度时,减少试验次数并包括步骤,以降低计算成本。*
Number of features to drop 75
Accuracy of model 0.6597
*使用随机福里斯特分类器的*特征 _ 重要性 _* 属性,通过递归特征消除逻辑,该模型显示出**预测准确度为** **66%,**提高了**1.7%,**从基础数据集**中删除了 **75 个特征**。***
*另一种 RFE 方法是使用 **Scikit 学习递归特征消除方法**。下面的代码展示了如何应用这种方法。*
> *需要预先选择想要将数据集缩减到的要素数量。*
65.7% accuracy on test set.
*使用这种方法,**输入了 26 个特征**(与手动 RFE 的结果相同),预测精度为 65.7% ,比原始数据集提高了**1.4%**。*
## *4.主成分分析*
*PCA 是一种特征提取技术,其工作原理是:*
1. ***标准化**数据(因此每个变量的贡献相等)*
2. ***计算协方差矩阵**(识别特征之间的关系)*
3. ***计算协方差矩阵的特征值和特征向量**(计算主成分)*
> *请记住:经过主成分分析后,特征值的可解释性将会降低。*
*要了解更多关于 PCA 和算法如何工作的信息,点击 [**这里**](https://sebastianraschka.com/Articles/2015_pca_in_3_steps.html) 。*
*下面的代码展示了如何实现 PCA。数据被分成训练和测试数据集;创建了一个管道来标准化数据。计算一组主分量,然后应用于随机福里斯特分类器。对于每个主成分,由于随机福里斯特分类器的随机性质,运行 20 次试验。*
**
*作者图片*
*上图显示了随着主成分数量的增加,预测的准确性。*
{55: 0.6503}
*使用 **55 个主成分**,模型显示 **0.75%的增长**,预测精度**65.03%。***
## *结束*
*所有技术都证明适用于数据集,因为随着预测准确性的提高,特征的数量会减少。**手动 RFE 方法表现出最佳性能,与原始数据集相比,减少了 75 个要素,增加了 1.7%** 。*
*我希望这篇文章对你的数据科学之旅有所帮助。*
*感谢您的阅读!*
*努力整合您的数据科学项目?看一看关于从头开始完成数据科学项目的简单指南。*
*</7-steps-to-a-successful-data-science-project-b452a9b57149>
## 取得联系
**领英:**[https://www.linkedin.com/in/amit-bharadwa123/](https://www.linkedin.com/in/amit-bharadwa123/)*
# 利用罗宾汉/GME 惨败的实用实验技巧
> 原文:<https://towardsdatascience.com/practical-experimentation-tips-using-the-robinhood-gme-fiasco-4455840ba0e2?source=collection_archive---------36----------------------->
## TL;你不需要(也可能无法使用)A/B 测试来了解罗宾汉通过限制 GME 交易来搅动其用户群。

我将把最近罗宾汉/GME 的惨败作为一个假设的例子,分享几个我多年来收集的关于实验的实际想法。*免责声明:这显然是假设。我只是一直在寻找好的教学例子,这感觉像是对时间的有趣利用。:)*
# **假设罗宾汉做了一个实验,随机让一些用户交易 GME。**
即假设他们进行了 A/B 测试,以确定其贸易限制的因果效应。在这种情况下,测试规范应该是这样的:
**处理:**用户不能交易 GME。
**控制:**用户可以交易 GME。
**度量:**卸载。
在将用户随机分配到治疗组和对照组后,他们可以观察治疗组和对照组用户之间卸载的差异,这将产生禁止用户交易 GME 的影响。现在,我肯定你马上会反对:你不需要 A/B 测试来知道罗宾汉对 GME 的反应导致卸载。所以,我的第一课。
# *1)如果影响足够大,不做 A/B 测试也能活。*
> 你不需要 A/B 测试来告诉你,禁止 GME 交易是导致人们卸载罗宾汉的原因。
不,你不会有一个反事实,但是来吧——当你的指标看起来像这样时,你通常不需要实验来告诉你发生了什么。

好吧,我知道这不是卸载的情节(这是谷歌趋势“如何删除罗宾汉”的情节),但它应该作为一个合理的代理。:)
诚然,在大多数大公司,这种异常大的提升可能很难实现。但是,如果你正在为一家初创公司或一家刚刚开始进入其市场的小公司工作,你所产生的任何影响都将足够大,以至于你的行动成果将会引人注目。
> 给初创公司数据科学家的建议:因为你的努力的相对影响将会很大(任何东西/ 0 =无穷大),所以你不需要随机化来知道某件事情是否有效。找曲棍球棒!
# 2)如果人们在谈论你的治疗,你很可能不能相信你的 A/B 测试结果(溢出)。

因无法交易 GME 而直接受到影响的用户数量可能很少(并不是每个人都持有或想要交易 GME)。但是由于社交媒体、新闻渠道和病毒式传播,几乎整个罗宾汉用户群都知道 GME 的贸易限制。
现在让我们回到我们的假设实验。在实验的世界里,用户之间这种不可控的互动意味着治疗将影响对照组,而不仅仅是治疗组。这种效应被称为**溢出**。在罗宾汉案中,这种效应背后的机制是显而易见的:
> 大多数卸载罗宾汉的人并不是因为他们个人不能交易 GME。
因此,治疗组和对照组之间的差异是对真实效果大小的严重低估。这是你得出的虚假结论:*既然每个人都卸载了罗宾汉(不仅仅是那些接受治疗的人),排除 GME 肯定不是问题。*
> 给大公司的建议是:如果你的 A/B 测试上了新闻,你的 A/B 测试很可能不起作用。
# 现在你走吧!让我们让 2021 年成为实验少的高影响年。
我开玩笑的,算是吧。
显然,一般来说,A/B 测试非常有价值。如果可能的话,您通常应该运行 A/B 测试——产品变更经常会有意想不到的副作用,即使是出于最好的意图,您希望在它们真正把事情弄糟之前发现它们。即使在溢出效应泛滥的情况下,通过正确的事后分析,A/B 测试也是可以挽救的。
真的,我正看着你——小公司的数据科学家——用那个标题。这可能是显而易见的,但不要谴责缺乏严格的测试实践和数据不足。跟我重复:
> “我们无法精确衡量影响,但如果我们做得非常好,我们应该能够看到它。”
向您的利益相关者重复这一点,观察您的指标,并寻找曲棍球棒。现在只要确保他们朝着正确的方向前进。
***推特***[***@ imrobertyi***](https://twitter.com/imrobertyi)***打个招呼,或者在***[***LinkedIn***](https://www.linkedin.com/in/robert-yi/)***上找我。***
# Python 虚拟环境实用指南
> 原文:<https://towardsdatascience.com/practical-guide-for-virtual-environments-in-python-b59bd5fe8f1?source=collection_archive---------19----------------------->
## 使用 virtualenv 和 pipenv 工具

马库斯·斯皮斯克在 [Unsplash](https://unsplash.com/s/photos/virtual?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 上的照片
我们从事的项目很可能有许多需要安装的依赖项。这些依赖关系简化了项目中的许多任务。然而,我们需要小心对待它们,尤其是在处理多个项目的时候。
就像任何其他技术一样,软件包或编程语言也在不断改进。因此,新的版本正在推出。
不同的项目可能需要不同版本的包或软件。例如,我们可能有一个项目需要 Python 2.7,而另一个项目需要 Python 3.6。随着项目和依赖项数量的增加,跟踪和处理这些差异变得越来越困难。
解决这个问题的一个方法是使用虚拟环境。它们可以被认为是软件包的边界框。我们可以在虚拟环境中开发一个项目,并安装特定于该项目的所有依赖项。我们在虚拟环境中拥有的东西不受我们机器全局范围变化的影响。
Python 的虚拟环境工具有很多,比如 pipenv、virtualenv、venv 等等。在本文中,我们将通过一些使用 virtualenv 和 pipenv 的例子来熟悉虚拟环境的概念以及它们是如何工作的。
让我们从虚拟开始。我们首先使用 python 包安装程序(pip)从终端安装它。
$ pip install virtualenv
我们创建一个样例项目文件作为我们的工作目录。
$ mkdir demoproject
$ cd demoproject
我们现在在 demoproject 目录中。我们将使用以下命令创建一个虚拟环境。
$ virtualenv venv_demo
它已经被创造出来了。我们可以运行 ls 命令来查看当前工作目录中的文件。
$ ls
venv_demo
下一步是激活虚拟环境。
$ source venv_demo/bin/activate
一旦虚拟环境被激活,其名称将显示在终端中,如下所示:

(图片由作者提供)
我们现在可以安装软件包了。
$ python -m pip install pandas
我们现在在虚拟环境中安装了熊猫。冻结命令显示已安装软件包的列表。
$ python -m pip freeze
numpy1.19.4
pandas1.1.5
python-dateutil2.8.1
pytz2020.5
six==1.15.0
NumPy 也被安装了,因为它是熊猫的附属品。熊猫的安装版本是 1.1.5。我们可以在安装包时指定我们需要的版本。
$ python -m pip install pandas==1.0.5
如果您只想检查特定软件包的安装版本,freeze 命令与 grep 一起使用:
$ pip freeze | grep pandas
pandas==1.0.5
我们也可以安装几个保存在文本文件中的包。这比一个一个地安装依赖项要好,尤其是当有多个依赖项时。
$ python -m pip install -r requirements.txt
为了退出虚拟环境,我们使用 deactivate 命令。
$ deactivate
我们将发现的下一个工具是 pipenv,它可以使用 pip 进行安装:
$ pip install pipenv
让我们使用 pipenv 创建一个新的虚拟环境。
$ pipenv install --python=/usr/bin/python3.6
Pipenv 允许在创建虚拟环境时安装依赖项。例如,我可以在上面的命令末尾添加 pandas,这样就创建了安装了 pandas 的虚拟环境。
我们运行 shell 命令来激活虚拟环境。
$ pipenv shell

(图片由作者提供)
我们现在在虚拟环境中。我们也给这个装熊猫吧。
$ pipenv install pandas
graph 命令显示已安装软件包的详细概述。
$ pipenv graphpandas==1.1.5
- numpy [required: >=1.15.4, installed: 1.19.4]
- python-dateutil [required: >=2.7.3, installed: 2.8.1]
- six [required: >=1.5, installed: 1.15.0]
- pytz [required: >=2017.2, installed: 2020.5]
我们可以使用 uninstall 命令卸载虚拟环境中的特定软件包或所有软件包。
$ pipenv uninstall pandas
以下命令卸载所有软件包。
$ pipenv uninstall -all
我们键入“exit”命令来退出虚拟环境。
## 结论
虚拟环境是同时管理多个项目的绝佳工具。有许多软件包和库可以随时更新。因此,试图手动跟进是非常费力且低效的。
我们在本文中讨论的内容可以被认为是对 Python 虚拟环境的实用介绍。当然,在理论和实践方面还有很多东西要学。
[virtualenv](https://virtualenv.pypa.io/en/latest/) 和 [pipenv](https://github.com/pypa/pipenv) 的官方文档提供了关于这些工具的更详细的概述。
感谢您的阅读。如果您有任何反馈,请告诉我。
# 使用显著图可视化 CNN 的实用指南
> 原文:<https://towardsdatascience.com/practical-guide-for-visualizing-cnns-using-saliency-maps-4d1c2e13aeca?source=collection_archive---------9----------------------->
## Python 深度学习可解释性教程

照片由 E [berhard Grossgasteiger](https://unsplash.com/@eberhardgross) 在 Unsplash 上拍摄
# 深度学习可解释性
虽然在计算机视觉中通过组成超大质量神经网络来解决复杂问题很方便,但理解每个权重对结果的影响很困难。
<https://medium.com/dataseries/how-ai-can-see-better-than-your-eyes-do-93e5a5da1e8a>
事实上,神经网络的权重与网络所代表的功能之间的关系极其复杂,尤其是当网络具有数十亿个权重时。
例如,一辆特斯拉电动汽车同时运行[至少 50 个大型卷积神经网络](https://heartbeat.fritz.ai/computer-vision-at-tesla-cd5e88074376)**来对车辆、车道线、路缘、人行横道和所有其他特定的环境变量进行分类。**
# **卷积神经网络**
**在本文中,我们使用 Keras 创建并可视化一个卷积神经网络,用于预测来自 [CIFAR-10](https://keras.io/datasets/#cifar10-small-image-classification) 数据集的图像中显示的对象类型,该数据集包含数千幅图像及其相应的类。**
**如果你看到下面的图片,并被要求分类,你很可能会马上回答——船,狗…等等!**
****
**我们可以精确地描述我们如何区分一艘船和其他东西,但我们想了解是什么使卷积神经网络给出某个类作为输出。**
# **打开深度学习黑盒**
**将网络可视化有助于诊断模型的问题,解释模型的意义,或者简单地教授深度学习概念。**
**我们可以可视化决策边界、模型权重、激活、梯度、性能指标、将学习到的过滤器应用于图像的结果或过滤器本身。**
**对于给定的输入图像,我使用下面提供的代码从隐藏的卷积层中提取了特征图。**
**每幅图像都显示了识别船只时相应层学习到的特征的热图。**
****
# **显著图**
**显著图更进了一步,它提供了一种可解释的技术来研究 CNN 中的隐藏层。**
> **显著图是一种测量每个图像中特定类别的空间支持度的方法。**
**它是解释卷积神经网络预测的最古老和最常用的解释方法。**
**显著图是使用输出对输入的梯度来构建的。这突出了与分类相关的图像区域。**
**例如,我们可以在下面看到,水在识别船只时起着重要的作用。也许模型如果给一艘建筑工地的水外船就不会这么成功了。这一观察提供了重要的线索,即需要用不同环境条件下船只的额外图像来重新训练模型。**
****
**显著图属于决策归因的后向方法。 [Grad-CAM 和基于梯度的显著图](https://papers.nips.cc/paper/2018/file/294a8ed24b1ad22ec2e7efea049b8737-Paper.pdf)被认为是最可靠的技术。也可以套用所谓的[前传归因](https://christophm.github.io/interpretable-ml-book/pixel-attribution.html#shap%7D)。这是一种遮挡方法,包括在整个图像上运行补丁,以查看哪些像素对分类影响最大。**
# **结论**
**在这篇文章中,我用 Python 代码演示了可视化卷积神经网络的好处。显著图非常受欢迎,我鼓励深度学习从业者打开他们的模型。这很容易,很有趣,并且赢得了客户更多的信任。**
**感谢阅读。**
# 机器学习中常见概率分布实用指南
> 原文:<https://towardsdatascience.com/practical-guide-to-common-probability-distributions-in-machine-learning-487f6137625?source=collection_archive---------12----------------------->
## 常见概率分布的性质和数学定义介绍(一)
概率是机器学习和数据科学的基本组成部分。事实上,现代机器学习算法的一些基本原理部分建立在这些统计理解上。在这篇文章中,我们将获得一些关于*如何*和*为什么*一些更常见的概率分布函数表现的直觉。我们还将定义它们的数学定义,以及如何用 Python 构建一个。

不同类型的发行版(图片由作者提供)
这篇文章的部分灵感来自于我写的一篇关于贝叶斯统计的文章(下面的链接)。我注意到这个话题很少被讨论,但它是需要学习的更重要的知识之一,尤其是对于那些正在构建机器学习模型的人来说。
</bayesian-statistical-programming-an-introduction-4ca3e2ddae76> [## 贝叶斯统计程序设计:导论
towardsdatascience.com](/bayesian-statistical-programming-an-introduction-4ca3e2ddae76)
你可以在这里找到第二部分。那么,事不宜迟,我们开始吧!
</practical-guide-to-common-probability-distributions-in-machine-learning-part-2-5bcb910218c0> [## 机器学习中常见概率分布实用指南(第二部分)
towardsdatascience.com](/practical-guide-to-common-probability-distributions-in-machine-learning-part-2-5bcb910218c0)
# 目录:
1. 什么是概率分布
2. 正态分布
3. 均匀分布
4. 柯西分布
5. 伽马分布
6. 指数分布
# 什么是概率分布
概率分布是一个函数,它给出实验中不同可能结果出现的概率。
举例来说,给定一个 6 面骰子,有 6 种可能的结果可以掷出:1,2,3,4,5,6。如果骰子是公平的,那么所有可能结果的概率是相同的:1/6。因此,对于*x*【1…6】的所有可能值,概率分布是 1/6。
***为什么概率分布很重要?*** 部分是因为它:
1. 帮助您根据给定的概率分布进行采样。例如,你的朋友让你将六面骰子滚动 100 次,以评估骰子是否公平。
2. 允许您通过对观测值或参数的基本分布做出*假设*来构建统计模型(例如,假设贝叶斯模型中的特定总体参数遵循指数分布)。
现在让我们讨论一些您可能会遇到的主要发行版!
# 正态分布
正态分布是正常的,因为它描述了许多自然现象:血压、测量误差、智商分数等等。
正态分布的数学公式如下,其中μ(读作:miu)是平均值,σ(读作:sigma)是观测值的偏差。

正态分布公式
通常,μ为 0,σ为 1,如下图所示。

正态分布(作者图片)
通过首先导入所需的库,您可以使用 scipy 在 Python 中创建一个正态分布。
from scipy.stats import norm
import seaborn as sns
import matplotlib.pyplot as plt
调用函数…(使用大小为 10000 的样本)
data_normal = norm.rvs(size=10000,loc=0,scale=1)
并绘制它…
sns.distplot(data_normal,
kde=True,
bins=100,
color='skyblue',
hist_kws={"linewidth": 15,'alpha':1})
# 均匀分布
均匀分布描述了在可能结果中均匀发生的现象。例如,我们早期的 6 面公平骰子或 52 张牌游戏套件。
均匀分布的数学公式将定义一个极限 **a 和 b(即。【a,b】)**。任何低于或高于 b 的 *x* 的值将被赋予零概率,而剩余的有效观测值将被赋予均匀概率,给定 a 和 b 之间的离散区间数

均匀分布公式
通过首先导入所需的库,您可以使用 scipy 在 Python 中创建一个统一的发行版。
from scipy.stats import uniform
import seaborn as sns
import matplotlib.pyplot as plt
调用函数…(使用大小为 10000 的样本)
data_uniform = uniform.rvs(size=10000, loc = 0, scale=1)
并绘制它…
sns.distplot(data_uniform,
kde=True,
bins=100,
color='skyblue',
hist_kws={"linewidth": 15,'alpha':1})

均匀分布(图片由作者提供)
# 柯西分布
柯西分布在形状上类似于正态分布,但有值得注意的差异。例如,它有一个比正态分布更高的峰值。柯西分布也有它的厚尾,衰减得慢得多。这种分布在物理学中被大量使用,尤其是在光谱学领域(即电磁辐射的研究)。
数学定义如下,其中 *γ* (读作:γ)是决定分布有多宽或多窄的比例因子。

柯西分布公式
通过首先导入所需的库,您可以使用 scipy 在 Python 中创建一个 cauchy 发行版。
from scipy.stats import cauchy
import seaborn as sns
import matplotlib.pyplot as plt
调用函数…
data_cauchy = cauchy.rvs(scale=0.5,loc=0,size=100)
并绘制它…
sns.distplot(data_cauchy,
kde=True,
bins=100,
color='skyblue',
hist_kws={"linewidth": 15,'alpha':1})

柯西分布(图片由作者提供)
# 伽马分布
伽马分布是一种双参数概率分布,它总是正的,并且具有偏斜分布。它自然发生在事件之间的等待时间很重要的过程中(即滞后时间),如新开发的治疗病毒感染的药物的效果等。伽马分布可以通过 *α* 和 *β* 来参数化,这决定了分布的偏斜程度,如下 *:*

伽马概率分布
通过首先导入所需的库,您可以使用 scipy 在 Python 中创建 gamma 发行版。
from scipy.stats import gamma
import seaborn as sns
import matplotlib.pyplot as plt
调用函数…
data_gamma = gamma.rvs(1,1,size=1000)
并绘制它…
sns.distplot(data_gamma,
kde=True,
bins=100,
color='skyblue',
hist_kws={"linewidth": 15,'alpha':1})

伽玛分布(图片由作者提供)
# 指数分布
指数分布通常用于模拟指数增长(或下降)的事件,例如随着时间的推移人口增长(或下降)的趋势。分布由λ(读作:lambda)来参数化,λ调节指数下降或上升的速度,如下所示:

指数分布公式
通过首先导入所需的库,您可以使用 scipy 在 Python 中创建一个指数分布。
from scipy.stats import expon
import seaborn as sns
import matplotlib.pyplot as plt
调用函数…
data_expon = expon.rvs(size=1000)
并绘制它…
sns.distplot(data_expon,
kde=True,
bins=100,
color='skyblue',
hist_kws={"linewidth": 15,'alpha':1})
# 结论
这个帖子到此为止!希望您已经获得了一些直觉,知道为什么以及如何使用这些概率分布来模拟真实世界的场景。在下一篇的[文章中,我们将深入探讨其他流行的分布,包括泊松分布、F 分布、t 分布、*分布等*。敬请期待!](/practical-guide-to-common-probability-distributions-in-machine-learning-part-2-5bcb910218c0)
***做订阅我的邮件简讯:***[*【https://tinyurl.com/2npw2fnz】*](https://tinyurl.com/2npw2fnz)****在这里我定期用通俗易懂的语言和漂亮的可视化方式总结 AI 研究论文。****
# 机器学习中常见概率分布实用指南(第二部分)
> 原文:<https://towardsdatascience.com/practical-guide-to-common-probability-distributions-in-machine-learning-part-2-5bcb910218c0?source=collection_archive---------29----------------------->
## 介绍常见概率分布的性质和数学定义。
概率是机器学习和数据科学的基本组成部分。事实上,现代机器学习算法的一些基本原理部分建立在这些统计理解上。在这篇文章中,我们将获得一些关于*如何*和*为什么*一些更常见的概率分布函数表现的直觉。我们还将定义它们的数学定义,以及如何用 Python 构建一个。

作者图片
这篇文章的部分灵感来自于我写的一篇关于贝叶斯统计的文章。我注意到这个话题很少被讨论,但它是需要学习的更重要的知识之一,尤其是对于那些正在构建机器学习模型的人来说。
</bayesian-statistical-programming-an-introduction-4ca3e2ddae76> [## 贝叶斯统计程序设计:导论
towardsdatascience.com](/bayesian-statistical-programming-an-introduction-4ca3e2ddae76)
这篇文章是第一部分的延续,链接如下。
</practical-guide-to-common-probability-distributions-in-machine-learning-487f6137625> [## 机器学习中常见概率分布实用指南
towardsdatascience.com](/practical-guide-to-common-probability-distributions-in-machine-learning-487f6137625)
# 目录:
1. 泊松分布
2. t 分布
3. 卡方分布
4. f 分布
5. 贝塔分布
# 泊松分布
泊松分布是一个离散的概率函数,它表示给定事件在整个可能结果空间中发生的概率。例如,您可以使用泊松分布来模拟一小时内在给定车站停留的公共汽车数量(例如 1 辆公共汽车、2 辆公共汽车等等)。这种分布通常用于结果可数的事件(即离散的),例如上面的公共汽车站例子。
泊松分布的数学公式如下,其中λ(读作:lambda)是所有可能结果的期望值(例如,一小时内平均有 1.2 辆公共汽车停靠在一个公共汽车站)。

泊松分布公式
通过首先导入所需的库,您可以使用 scipy 在 Python 中创建泊松分布。
from scipy.stats import poisson
import seaborn as sns
import matplotlib.pyplot as plt
调用函数…(使用大小为 10000 的样本,平均出现 1.2 次)
data_poisson = poisson.rvs(mu=1.2, size=10000)
并绘制它…
sns.distplot(data_poisson,
kde=True,
color='skyblue',
hist_kws={"linewidth": 15,'alpha':1})

泊松分布(作者图片)
# t 分布
t 分布或学生分布是一种类似于正态分布的概率分布函数。关键的区别在于 t 分布用于模拟具有*低*样本量和/或*未知*总体方差的事件。这种分布广泛用于估计生物学、生态学、公共卫生等领域中基于人口的分布。

t 分布公式
上式中,γ(读作:gamma)是 Gamma 函数, *v* 是自由度的个数, *t* 是我们可能的观测值(即 *x* 。自由度, *v,*是一个可调参数,允许您调节 t 分布的宽度或宽度。
通过首先导入所需的库,您可以使用 scipy 在 Python 中创建 t-distribution。
from scipy.stats import t
import seaborn as sns
import matplotlib.pyplot as plt
通过定义自由度调用函数, *v* …(并使用大小为 1000 的样本)
degrees_of_freedom = 5
data_t = uniform.rvs(degrees_of_freedom, size=1000)
并绘制它…
sns.distplot(data_t,
kde=True,
bins=100,
color='skyblue',
hist_kws={"linewidth": 15,'alpha':1})

t 分布(图片由作者提供)
# 卡方分布
卡方概率通常用于比较观察值和理论值之间的差异(即拟合优度)。具有 *k* 自由度的分布是 *k* 个独立标准正态随机变量的平方和,如下式所示,其中γ(读作:γ)是[γ函数](https://en.wikipedia.org/wiki/Gamma_function):

卡方公式
通过首先导入所需的库,您可以使用 scipy 在 Python 中创建卡方分布。
from scipy.stats import chi2
import seaborn as sns
import matplotlib.pyplot as plt
通过定义自由度调用函数, *k* …
degree_of_freedom = 25
data_chi2 = chi2.rvs(degree_of_freedom, size=1000)
并绘制它…
sns.distplot(data_chi2,
kde=True,
bins=100,
color='skyblue',
hist_kws={"linewidth": 15,'alpha':1})

卡方分布(图片由作者提供)
# f 分布
f 分布是两个自由度为 *d1* 和 *d2* 的卡方分布,以及一个[贝塔函数](https://en.wikipedia.org/wiki/Beta_function) *、* *B* 之间的比值。f 分布可用于多种应用,包括验证多元回归方程或比较两个不同总体的方差(例如,观察值与实际值),以确定两者之间的相似性是否仅仅是巧合。

f 分布公式
通过首先导入所需的库,您可以使用 scipy 在 Python 中创建 F 发行版。
from scipy.stats import f
import seaborn as sns
import matplotlib.pyplot as plt
通过定义两个自由度 *d1* 和 *d2* 调用函数…
d1 = 10
d2 = 50data_f = f.rvs(d1,d2,size=1000)
并绘制它…
sns.distplot(data_f,
kde=True,
bins=100,
color='skyblue',
hist_kws={"linewidth": 15,'alpha':1})

f 分布(图片由作者提供)
# 贝塔分布
Beta 分布用于模拟范围有限的事件,例如从 0 到 1。例子包括成功的概率,它可以在 0 和 1 之间变化。它们由α(读作:α)和β(读作:β)参数化,函数的形式为:

贝塔分布
通过首先导入所需的库,您可以使用 scipy 在 Python 中创建一个测试版。
from scipy.stats import beta
import seaborn as sns
import matplotlib.pyplot as plt
通过定义α和β调用函数…
alpha = 3.41
beta = 0.87
data_beta = beta.rvs(alpha, beta, size=1000)
并绘制它…
sns.distplot(data_beta,
kde=True,
bins=100,
color='skyblue',
hist_kws={"linewidth": 15,'alpha':1})

Beta 分布(图片由作者提供)
# 结论
这个帖子到此为止!希望您已经获得了一些直觉,知道为什么以及如何使用这些概率分布来模拟真实世界的场景。如果你对概率分布有进一步的建议,请告诉我!
***做订阅我的邮件简讯:***[*【https://tinyurl.com/2npw2fnz】*](https://tinyurl.com/2npw2fnz)****在这里我定期用通俗易懂的语言和漂亮的可视化总结 AI 研究论文。****
# 数据可视化实用指南:第 1 部分
> 原文:<https://towardsdatascience.com/practical-guide-to-data-visualization-83e375b0037?source=collection_archive---------12----------------------->
## 使用 Python、Seaborn 和 Matplotlib

来自[佩克斯](https://www.pexels.com/photo/person-with-body-painting-1209843/?utm_content=attributionCopyText&utm_medium=referral&utm_source=pexels)的[莎伦·麦卡琴](https://www.pexels.com/@mccutcheon?utm_content=attributionCopyText&utm_medium=referral&utm_source=pexels)的照片
如果你正在阅读这篇文章,你可能已经猜到我将要谈论数据可视化(从标题判断),并且可能已经创建了一些或者打算这样做。
*听起来像你?*
**读下去!!!**
数据可视化是在探索性数据分析过程中传达观察结果的一种很好的方式。
> 我的意思是,一张图胜过一千个字,对吗???
我最近参加了由尼日利亚[数据科学](https://www.datasciencenigeria.org/)主办的 Kaggle 黑客马拉松,以此来标志一个人工智能初学者课程的完成,我也可以说这是我第一次参加任何 Kaggle 比赛,真的,我感到非常兴奋能够参加。
在做这个项目的时候,我花了一些时间研究更多关于数据可视化的资源,在我脑海中的某个地方,我知道我很乐意与你分享这些,所以在构建这个项目的整个过程中,我一直在想你。
***今天我站在这里,与你们分享我的发现……***
> 这个实用指南是一个系列,它将使你具备数据可视化的基础,我鼓励你和我一起编码!!!
我们将使用 **Python** 、 **Seaborn** 和 **Matplotlib 来看一些图表。**
不熟悉术语数据可视化??这里有一个快速定义。
***数据可视化可以概括为这四行:***
* 给定数据中的信息表示,可以是趋势、模式、异常值。
* 这是一个跨学科的领域。
* 它包括图表、图形、绘图和其他视觉元素的使用。
* 表示大量信息的有效方式。
GIF via [giphy](https://giphy.com/)
**举个例子,**
当我们看到一个图表时,我们可以迅速发现趋势和异常值,从而轻松地将信息内化。这张图表最终总结了一个故事,你可能无法通过盯着熊猫数据框架或电子表格来理解这个故事。
> 有没有试过长时间盯着电子表格?
>
> 理解它有多容易?
本文将重点介绍 ***Python*** 、 ***Matplotlib、*** 和 ***Seaborn 库*** 的使用,参考数据集是 Kaggle 上托管的数据科学尼日利亚黑客马拉松(Data Science Nigeria hackathon),你可以在这里 看一下[。](https://www.kaggle.com/c/data-science-nigeria-patient-treatment/data)
*记下…*
这将是一段漫长而甜蜜的旅程!!!
***TL:*博士**
拿起你的咖啡,让我们开始吧!!!

照片由 [cottonbro](https://www.pexels.com/@cottonbro?utm_content=attributionCopyText&utm_medium=referral&utm_source=pexels) 从 [Pexels](https://www.pexels.com/photo/asian-girl-in-yellow-suit-drinking-on-chair-9656761/?utm_content=attributionCopyText&utm_medium=referral&utm_source=pexels) 拍摄
# 内容大纲
1. 导入库
2. 导入和读取数据集
3. 设置图形美学
4. 其他定制
5. 使用颜色
6. 问题陈述
7. 系列中涵盖的地块列表
8. 线形图
9. 散点图
10. 计数图
11. 箱形图
12. 分类图
13. 配对图
14. 创建多个地块
15. 在开始一个项目之前拥有领域知识的重要性。
16. 结论
> 如果你不熟悉 Seaborn 和 Matplotlib,我建议你浏览下面的文档链接;
* [Seaborn](http://seaborn.pydata.org/introduction.html)
* [Matplotlib](https://matplotlib.org/3.3.3/contents.html)
> 与此同时,Seaborn 是一个基于 Matplotlib 构建的 Python 库,它们都用于数据可视化。
好吧,我们开始吧!!!
## 导入库
pip install opendatasets --upgrade --quiet
## 我安装了 opendatasets,因为我直接在 google colab 上工作,我的数据集直接来自 Kaggle。
> *psst…如果你直接在你的 Jupyter 笔记中工作,你可以跳过这段代码*
import pandas as pdimport opendatasets as odimport numpy as npimport matplotlib.pyplot as pltimport seaborn as sns%matplotlib inline
* %matplotlib inline 有助于确保我们的绘图显示并嵌入 Jupyter 笔记本本身。而不是出现在弹出窗口中。
## 资料组
我使用 opendataset 库直接从 Kaggle 导入数据集。
in_out_train = '/content/data-science-nigeria-patient-treatment/train.csv'in_out_test = '/content/data-science-nigeria-patient-treatment/test.csv'sample_submission = '/content/data-science-nigeria-patient-treatment/sample_submission.csv'
阅读 CSV
train=pd.read_csv(in_out_train)test=pd.read_csv(in_out_test)sample = pd.read_csv (sample_submission)
现在我们有了数据集,让我们快速看一下火车 CSV 的前几行。
train.head(10)

作者图片
* 您也可以尝试 train.tail(10)来查看最后十行。
## 目标变量
我们需要使用几行代码来确定这个项目的目标变量。
target = [col for col in train.columns if col not in test.columns]target
[出局]
['SOURCE']
* 这个项目的目标变量是“源列”。因此,我们会通过我们的视觉化来引用它。
## 创建一个图来查看“源”列中出现的类别数
plt.figure (figsize= (10,6), tight_layout= True) # Set figure sizesns.countplot(train['SOURCE'], label = 'counts')

作者图片
## 上面的图被称为计数图,它显示了每个“源”列中的观察计数。我们也可以了解目标变量的平衡程度。
> pssttt…
>
> 尽管我们想创建易于理解的可视化效果,但我们也不想花太多时间来设置每个图的特征。
例如
* 标题
* 字体
* 风格等。
这可能很费时间,因此从一开始就定制你的情节是一个好习惯。
关于这一点,让我们来看看如何设置我们的身材美学。
# 图形美学
我们可以通过修改样式或比例来修改地块外观。
1. 风格:
* set_style()
* axes_style()
2.比例:
* set_context()
* 绘图 _ 上下文()
sns.set_style('whitegrid') # adjust the stylesns.set_context ('paper') # modify the scaleplt.figure (figsize= (10,6), tight_layout= True) # Set figure sizesns.countplot(train['SOURCE'], label = 'counts')

作者图片
首先,我们使用 sns.set_style()设置我们的绘图的背景样式。默认为*‘深色网格’*,其他选项包括*‘白色网格’,‘深色’,‘白色’,‘刻度’*
**这将给我们的图表一个更好的整体外观。**
接下来,我们通过调用 *sns.set_context()* 函数来修改绘图的比例。
默认为“笔记本”。其他预设上下文包括:*【论文】【谈话】**【海报】*。
* 探索这些选项,看看哪一个最适合您的项目。
plt.figure (figsize= (10,8), tight_layout= True) #set figure sizesns.set_style('ticks') # adjust the stylesns.set_context ('poster') # modify the scalesns.countplot(train['SOURCE'], label = 'counts')

作者图片
> 我已经在前面的图中设置了图形大小,即 plt.figure (figsize= (10,6),tight_layout= True)。但是我注意到 y 轴的计数结束于“1500”,而不是“1750”。所以当你改变尺度(sns.set_context ('poster '))的时候,一定要注意你的情节发生了什么变化。
***好了,我们继续……***
有时候,我不喜欢在顶轴和右轴上有刺,所以我调用 *despine()* 函数。
> 此功能仅适用于 *set_style()* 的*‘白色’*和*‘刻度’*参数
plt.figure (figsize= (10,8), tight_layout= True) #set figure sizesns.countplot(train['SOURCE'], label = 'counts')sns.despine()

作者图片
## 你能看出上面两幅图的不同之处吗?
plt.figure (figsize= (10,8), tight_layout= True) #set figure sizesns.set_style('darkgrid') # adjust the styleplt.rc('grid', c ='r', ls ='-', lw = 0.5) # gridstyleplt.rc('axes', titlesize=18) # axes title fontsizeplt.rc('axes', labelsize=14) # x and y labels fontsizeplt.rc('xtick', labelsize=12) # fontsize of the tick labelsplt.rc('ytick', labelsize=12) # fontsize of the tick labelsplt.rc('legend', fontsize=12) # legend fontsizeplt.rc('font', size=12) # modify text sizessns.countplot(train['SOURCE'], label = 'counts')

作者图片
* 我们要做的下一件事是设置我们的字体大小(标题、轴和图例),我们不需要在随后的图中重复它。
***听起来不错 hun?***
> **但是,要切换到默认参数,可以调用 sns.set_theme()**
Another quick option is to set the parameters in a dictionary and call it on the plt.rc() as shown below.#font = {'family' : 'serif',#'weight' : 'light'}# pass in the font dict as kwargs#plt.rc('font', **font)#sns.countplot(train['SOURCE'], label = 'COUNTS')# You can take out the comments in the codes above to see what the visulization looks like.
> 为了让你的工作更快捷、更简单,我整理了一份化名清单。
df= {'Alias' : ['lw', 'ls','c', 'fc', 'ec', 'ha'], 'Property': ['linewidth', 'linestyle', 'color', 'facecolor', 'edgecolor', 'horizaontal alignment']}short_forms= pd.DataFrame (df)short_forms

作者图片
> 快速提醒;
>
> 不要忘记编码…
# 其他定制
就我个人而言,我不喜欢使用默认颜色,所以我调整到自己满意的程度。
这同样适用于图形大小和外观。
> **记笔记;**
>
> **“有必要标记我们的 x 轴和 y 轴,并为我们的图表添加一个标题”**
* 让我们来看看如何实现这一点。
plt.figure (figsize= (12,8), tight_layout= True)sns.set(font_scale= 1.5)sns.countplot(train['SOURCE'], label = 'counts')plt.xlabel('Count')plt.ylabel('Target')plt.title ('Target Column Unique Values')plt.show()

作者图片
* 当我们有并排支线剧情时,tight_layout 参数就发挥作用了。我将在本文的后面详细讨论这一点。
***你还在跟踪我吗?***
# 使用颜色
将 seaborn 与 matplotlib 相结合,可以更好地使用颜色,并且在视觉上更具吸引力。
sns.color_palette() #calling the default seaborn colour palettes.

作者图片
这些调色板有一个列表的形式,因此我们可以根据它的索引调用特定的颜色,我们将得到一个 RGB 代码。
> ***eg;1.0,0.6235294117647059,0.6078431372549019***

作者图片
其他变化包括:
* 色盲
* “深”
* '静音'
Seaborn 还有一些其他有趣的调色板;有关更多详细信息,请参考文档;
[https://seaborn.pydata.org/tutorial/color_palettes.html](https://seaborn.pydata.org/tutorial/color_palettes.html)
* '配对'
* 设置 2 '
* hls 的
另一类有趣的颜色是感知均匀的颜色。
* “火箭”(适用于热图)
* “mako”(适用于热图)
* '闪光'
* “冠”
sns.color_palette("rocket", as_cmap=True)
> 我们真的有很多颜色可以选择,为什么要坚持传统的蓝色和橙色呢?
## 在我们进行的过程中,我们一定会尝试这种定制。
> 我们来看看问题陈述。
# 问题陈述
因为本文的重点是数据可视化,所以我们不会做任何数据清理或探索性的数据分析。
但如果能对问题陈述有所了解,那就太好了…
**你这么想对吗?**
*我也是!!!*
> “作为 HealthIsWealth 医院自动化系统的一部分,您已经签约成为一名专业数据科学家,他将构建一个系统,该系统将借助关于患者、其状况和实验室测试的几个数据点来预测和估计患者是否应被归类为护理中的患者或护理外的患者”
问题陈述是不言自明的,老实说,我从未想过将病人分为住院病人和门诊病人的过程,但在从事这个项目后,我不仅对这个过程有了概念,而且对它的重要性有了更清晰的理解。
> 在本文的后半部分,我将更详细地解释为什么作为一名数据分析师/科学家,拥有任何项目的领域知识都是必要的。
## 以下是这一系列中涉及的情节列表。
1. 线形图
2. 散点图
3. 计数图
4. 箱形图
5. 分类图
6. 配对图
7. 条形图
8. 圆形分格统计图表
9. 柱状图
10. 热图
## 我们还会看一看;
* 导入图像
* 创建多个地块
***走吧!!!***
# 线形图
* 折线图是最简单且广泛使用的图表之一。
* 它以直线连接的数据点或标记的形式显示数据集中的观察值。
sns.set_theme # setting sns parameters to the defaults
上面的代码重置了主题。
sns.set_style('ticks') # set the stylesns.set_context ('paper') # set the scale
## 对于我们的第一个图,我们将创建一个假设的示例,说明在一年的最后一个季度和下一年的年初从 Alausa 到 Obalende 的估计票价。
time = [4.00, 5.00, 5.30, 6.00, 6.30, 7.15, 8.00]days= range(1,8)est_fare_Q1= [250,300,400, 550, 600, 500,450]est_fare_Q4= [250,350,500, 600, 600, 550,500]
绘制折线图就像运行如下所示的几行代码一样简单;
plt.figure (figsize= (10,8), tight_layout= True)plt.plot(time)

作者图片
> 这并没有给我们提供太多的信息,所以让我们添加更多的细节。
plt.figure (figsize= (10,8), tight_layout= True)plt.plot(time,est_fare_Q1)
plt.rc('grid', c ='b', ls ='-', lw = 0.5) # gridstyle
plt.xlabel('Time (hour)')
plt.ylabel('Estimated Q4 Fare (naira)')

作者图片
* 现在我们有了更多的信息。一眼看去,我们可以知道 x 轴和 y 轴上的值是什么,并且可以看出它们之间的关系。
* 让我们来看看一个线图上的多个图。
plt.figure (figsize= (10,8), tight_layout= True)plt.plot(time,est_fare_Q1)
plt.plot(time,est_fare_Q4)
plt.xlabel('Time (hour)')
plt.ylabel('Estimated Fare (naira)'

作者图片
* 我们可以清楚地看到这两个图,但不能确切地告诉 Q1 或第四季度。
* 为了解决这个问题,我们设置了图例参数。它使我们能够区分上面的两个图。
plt.figure (figsize= (10,8), tight_layout= True)plt.plot(time,est_fare_Q1, marker= 'x')plt.plot(time,est_fare_Q4, marker= 'o')plt.title ('Graph Of Estimated Fare Between The 1st and 4th Quarter Of The Year')plt.legend(["est_fare_Q1", "est_fare_Q4"])plt.xlabel('Time (hour)')plt.ylabel('Estimated Fare (naira)')

作者图片
在上面的图中,我添加了一些标记来进一步区分 Q1 和 Q4。
> 但我们能补充的细节还不止这些。
## 这里有更多关于如何设计马克笔的选项。
* 颜色或 c:设置线条的颜色(支持的颜色)
* *线条样式* *或 ls* :选择实线或虚线
* *线宽或 lw* :设定线条的宽度
* *标记大小*或 ms:设置标记的大小
* *markeredgecolor 或 mec* :设置标记的边缘颜色
* *标记边缘宽度或新宽度*:设置标记的边缘宽度
* *markerfacecolor 或 mfc* :设置标记的填充颜色
* *alpha* :图的不透明度
让我们申请看看这在我们的地块上看起来怎么样!!!
plt.figure (figsize= (10,8), tight_layout= True)plt.plot(time,est_fare_Q1, marker='x', c='b', ls='-', lw=2, ms=8, mew=2, mec='navy')plt.plot(time,est_fare_Q4, marker='s', c='r', ls='--', lw=3, ms=10, alpha=.5)plt.title ('Graph Of Estimated Fare Between The 1st and 4th Quarter Of The Year')plt.legend(["est_fare_Q1", "est_fare_Q4"])plt.xlabel('Time (hour)')plt.ylabel('Estimated Fare (naira)')

作者图片
Matplotlib 有一个可以添加到 plt.plot 的简写参数
**fmt = '[标记][线条][颜色]'**
plt.figure (figsize= (10,8), tight_layout= True)plt.plot(time,est_fare_Q1, 'o--r')plt.plot(time,est_fare_Q4, 's--b')plt.title ('Graph Of Estimated Fare Between The 1st and 4th Quarter Of The Year')plt.legend(["est_fare_Q1", "est_fare_Q4"])plt.xlabel('Time (hour)')plt.ylabel('Estimated Fare (naira)')

作者图片
***更短更快!***
数据可视化不仅仅是创造视觉效果,我们还试图传递信息,对吗?
## 快速浏览一下这个情节的见解;
上面的线图显示:
* 在一年的第四季度(Q4 ),预计票价从早上 5:50 开始上涨,并在早上 6:00 达到峰值,而在一季度(Q1)的 6:00 至 6:50 达到峰值。
* 在一年的两个季度,票价在上午 8:00 下降,估计第四季度票价为 500 奈拉,估计 Q1 票价为 450 奈拉。
> “作为一名数据分析师,在这一点上,你可能会好奇为什么在 Q1 期间票价会更高。然后你开始搜索这个国家是否有任何事件、节日或任何普遍的价格上涨,例如燃料价格
就像一个简单的线图一样,我们能够从中获得一些信息。
> **现在我们可以继续我们的原始数据集,我们将从“年龄”列的折线图开始。**
plt.figure (figsize= (10,8), tight_layout= True)plt.plot(train['AGE'])

作者图片
> 糟糕!!!
>
> 这并没有告诉我们关于“SOURCE”列的任何有意义的内容。
在我们继续之前,为我们的项目设置我们想要的基本特性是很棒的。
Customizing Our Plotssns.set_style('white') # set the stylesns.set_context ('talk') # set the scaleplt.rc('grid', c ='b', ls ='-', lw = 0.5) # gridstyleplt.rc('axes', titlesize=18) # axes title fontsizeplt.rc('axes', labelsize=14) # x and y labels fontsizeplt.rc('xtick', labelsize=12) # fontsize of the tick labelsplt.rc('ytick', labelsize=12) # fontsize of the tick labelsplt.rc('legend', fontsize=12) # legend fontsizeplt.rc('font', size=12) # modify text sizes
> 然后,我们开始可视化“年龄”和“来源”列之间的关系。

作者图片
上面的结果既没有吸引力也没有信息,因为数据集中这两种属性的组合太多了。
这意味着在所讨论的两列之间不存在线性关系 。当使用线图时,注意它**不适合有太多组合的数据集,**因此不能从它中得到任何见解,正如我们在上面的例子中看到的。
对于有许多组合的变量,散点图是一个更好的选择!!!
# 散点图
* 它显示了两个数值变量之间的关系。
* 与线形图不同,即使变量有太多的组合,散点图也能提供信息。
> 我们可以用散点图来显示“年龄”栏和“白细胞”栏之间的关系。
plt.figure (figsize= (10,8), tight_layout= True)sns.scatterplot(x=train['AGE'], y=train['LEUCOCYTE'], hue= train['SOURCE']);

作者图片
* 在我们记录我们的见解之前,让我们定制上面的散点图。
plt.figure(figsize=(10, 8), tight_layout= True)plt.title('RELATIONSHIP BETWEEN LEUCOCYTE AND AGE')sns.scatterplot(x=train['AGE'], y=train['LEUCOCYTE'], hue= train['SOURCE'],s=70);plt.show()

作者图片
## 将色调添加到上面的图中使其信息量更大,我们可以注意到以下见解;
* 有更多 40 岁以上的门诊病人(用 1 和橙色表示)。
* 有一个带有一些异常值的独特聚类。
* 年龄最大的患者(100 岁左右)白细胞较低,被归类为住院患者。
设置 hue= 'SOURCE '列可以更容易地看到护理中和护理外患者类别的情况。
Another option is to try the lines of code below;plt.figure(figsize=(12,10), tight_layout=True)ax = sns.scatterplot(data= train, x='AGE', y='LEUCOCYTE', alpha= 0.6 , hue='SOURCE', palette='Set2', s=70)ax.set(xlabel='AGE', ylabel='LEUCOCYTE')plt.title('RELATIONSHIP BETWEEN LEUCOCYTE AND AGE')plt.show()

作者图片
* 在这里,我调整了调色板
散点图是我对目标变量进行多种组合的首选图。
# 计数图
* 计数图是分类估计图的一个例子。
* 它使用条形显示每个分类箱中的估计观察计数。
* 我们还可以设置色调来查看相对于目标变量的观察计数。
plt.figure (figsize= (10,8), tight_layout= True)plt.title ('SOURCE COLUMN WITH RESPECT TO THE SEX COLUMN')sns.countplot (x= 'SEX', hue= 'SOURCE', data =train, palette= 'dark');plt.show()

作者图片
注意调色板设置为“暗”,虽然它仍然是常规的蓝色和橙色,但它看起来更暗,更饱和,更漂亮。
> 所以你所需要的只是在这里和那里做一点小小的调整,维奥拉!!!
你有一个有趣的情节。
***好了回到我们的分析……***
从上面的图表中,我们可以注意到以下观察结果:
* 女性和男性住院患者的数量(0)非常接近。男性门诊病人(1)多于女性门诊病人。
> ***问题 1。***
>
> ***男性实验室检测结果是否存在重大差异,导致患者护理中男性患者较少?***
We can also have an horizontal arrangement for a count plot.plt.figure (figsize= (12,10), tight_layout= True)plt.title ('SOURCE COLUMN WITH RESPECT TO THE SEX COLUMN')sns.countplot (y= 'SEX', hue= 'SOURCE', data =train, palette= 'Set2');plt.show()

作者图片
另一个可以考虑使用的有用的图是方框图。
# **方框图**
箱线图用于比较连续变量的分布,并清楚地显示异常值、数据的偏斜度,即数据的分布或接近程度、统计数据,如:
* 最小值
* 第一个四分位数(Q1)
* 中位数(Q2)
* 第三个四分位数(Q3)
* 最大值
并由此计算出四分位间距。
## 这是分类分布图的一个例子,因此,在比较多组值时很方便。
* 中间值通过方框内的线条表示。
* “胡须”代表最小和最大值(有时不包括异常值,用黑钻石表示)。
***语法:***
制作包含多个分类变量的箱线图时,我们需要两个参数——分类变量的名称(来源)和数值变量的名称(白细胞)。
plt.figure(figsize=(10, 8), tight_layout=True)plt.title('Box Plot')sns.boxplot (x= 'SOURCE', y= 'LEUCOCYTE', data =train, palette= 'Set2', linewidth=2);plt.show()

作者图片
train.describe() #calling the .describe() function gives you the statistics of the numerical columns.

作者图片
# 检查箱线图中的异常值(1.5 IQR 规则)
已知 IQR = Q3-Q1,最小值= Q1–1.5 * IQR,最大值= Q3 + 1.5*IQR。
* 值小于最小值(Q1-1.5 * IQR)且大于最大值(Q3 + 1.5* IQR)的数据被称为包含一些异常值或某种不准确性。
对于白细胞列;
Q3= 10.4 Q1= 5.7
* 因此,IQR=4.7
* 最小值= 5.7–1.5 * 4.7 =-1.35
* 最小值= 10.4 + 1.5*4.7 = 17.45
> 因此,对于白细胞列(考虑第一个矩形,即护理中的患者表示为 0),大多数值接近中值。
* 门诊病人的中位数较高
* 最小值应为-1.35,估计的最大值应为 17.45。
* 换句话说,数据集的最小值不应小于-1.35,而数据集中的最高值不应大于 17.45。
## 考虑到我们的数据集,住院患者的最低白细胞值是 1.2。既然 1.2 >大于-1.35,那么我们没有更低的异常值。
* 数据集中的最高值为 76.7,高于 17.45(最大值)。
* 因此,数据集包含较高的异常值。
> *上面的分布直方图肯定会给我们更多的见解。*
> 问题 2
>
> 你能看看门诊病人的数值吗(用 1 表示&橙色矩形方框图)。
>
> 在评论区分享你的观察。
# 分类散点图
这种类型的图可以更好地了解图表上某一点的数据数量,还可以了解数据集的密集程度或稀疏程度。
sns.catplot(x,y,data)
* 有两种类型分类散点图;
* 剥离槽()
* 群集图()
***调用 sns.catplot()时,带状图是默认的‘种类’设置。所以要创建一个蜂群图,你要设置 kind= 'swarm'***
> 还要注意,
>
> 在处理少量数据时,通常倾向于使用 Swarmplot,否则,获得的数据会显得过于集中,对我们的分析帮助不大。
## 现在,让我们使用我们的训练数据集,看看我们可以从分类图中推断出什么。
plt.figure(figsize=(12,10))sns.set_style('whitegrid')sns.swarmplot(x='SEX', y='LEUCOCYTE', data=train,palette= 'husl', hue= 'SOURCE')plt.xlabel("Size")plt.ylabel("Total Bill")plt.title("Total bill per size of the table")plt.show()

作者图片
正如我之前所说的,这个图最适合较小的数据集。随着数据集大小的增加,分类散点图所能提供的关于每个类别中值的分布的信息变得有限。
**为了更好地理解该图的用法,我们将使用 Seaborn 的一个内置数据集。**
df = sns.load_dataset("tips")df.head(7)

作者图片
sns.catplot(x= 'time', y= 'tip', data=df) # default setting i.e the stripplot.

作者图片
我们可以添加 ***抖动*** 参数。它控制抖动的存在与否。
sns.catplot(x= 'time', y= 'tip',jitter= False, data=df)

作者图片
> 在设置抖动和不设置抖动的情况下,你能发现图中的差异吗?
在阐述我们的见解之前,让我们把这个情节变得更有美感。
此外,如果您确定要使用 swarmplot,您可以简单地调用 sns.swarmplot()而不是每次都设置类型,而不是使用上面的代码。
**好吧,让我们看看这个。**
plt.figure (figsize= (10,8), tight_layout= True)sns.swarmplot(x='time', y='tip', data=df,hue= 'sex')plt.xlabel('Time')plt.ylabel('Tip')plt.title("Tip Per Meal Time")plt.show()

好的,我认为画一个每天小费的图表会很好,因为天列有更多的类别,我们可以得到更多的见解。
sns.set_style('dark')plt.figure(figsize=(12,10))sns.swarmplot(x='tip', y='day', data=df,hue= 'time', palette= 'husl', size= 8)plt.xlabel('Time')plt.ylabel('Tip')plt.title("Tip Per Day")plt.show()

作者图片
> 使用分类散点图,我们可以更好地了解一周中每天小费的分布情况。
>
> 即数据集的密集或稀疏程度。
> **例如,箱线图不一定向我们展示每个点的分布。所以实际上,你用于可视化的图取决于你想要实现的问题、见解和结果。**
## ***如果你已经读到这一点,大拇指给你!!!***
## ***不要停止阅读……***
# 配对图
当然是我最喜欢的情节之一!信息非常丰富,并为我想要进行更深入分析的列提供了提示。
**这里有一些关于结对图的快速要点**
* 结束本教程的一个好方法是讨论结对图。这是一个非常简单和容易的方法来可视化变量之间的关系。
* 它创建了某种矩阵,可以提示在特性选择或特性工程中应该考虑哪些列。
* 结对图在某种程度上类似于热图,因为它们都产生类似矩阵/相关性的结果,我个人喜欢在 EDA 过程的早期使用结对图或热图,特别是因为它可以提示我变量、假设之间的相关性,有时还可以指导我的 EDA 流程。
sns.set_style('ticks')sns.pairplot(train, hue="SOURCE", diag_kind='kde', kind='scatter', palette='Set2')plt.show()

作者图片
上面的配对图显示了我们的训练数据集中每个连续变量的关系和分布。
因此,即使你不熟悉你正在从事的项目领域,一眼就能看出变量之间的相关性。
这种洞察力可以为你的研究、假设以及回归分析提供信息。
> 例如,在上面的 pair 图中,设置 hue= 'SOURCE '有助于我们识别相对于' SOURCE '列的分布,并考虑它是我们的目标变量这一事实。
## 所以我会花一些额外的时间来解释这个情节的一些见解。
1. 对角行简单地显示了每个变量的直方图和出现的次数。
2. 缺少“性别”列,因为它不是一个数值(连续)变量。
3. 从我最初的研究中,我发现白细胞是白血球,它们在防御身体细菌和疾病中起着重要作用。
> **现在,考虑配对图上的“年龄”和“白细胞”栏;**
* 我可以立即发现异常值( ***使用方框图*** 确认)
* 在图的底部,年龄较低(20 岁以下),我注意到有更多的绿色标记(护理中的病人,薄荷绿),橙色标记相对较少。(A 类)
* 此外,这类患者的“白细胞”低于 25
* 另一方面,年龄较大的人似乎既有橙色标记又有绿色标记,橙色标记(门诊病人)更占优势,尤其是在某一年龄范围内。(B 类)
* “白细胞”的值似乎也随着“年龄”的增加而增加。
我也可以开始问这样的问题:
> 由于甲类病人大部分年龄在 20 岁以下,而「白血球」在 25 岁以下,这是否表示「白血球」越低,病人被归类为住院病人的可能性就越高?
>
> -为什么年轻患者的“白细胞”比年长患者低?
>
> -根据我们的图,我们在什么年龄范围观察到“白细胞”增加?
>
> -所有这些问题可以不断出现,然后通知我需要研究什么(与领域一致)。
>
> 因此,我可以决定通过绘制散点图、箱线图来隔离这两个变量,以便清楚地了解分布情况。
# 花些时间研究这些配对图,并让我知道你能做出的一些推论。
> ***psttt...永远不要低估领域研究的力量。一个快速的谷歌搜索可以做到这一点。***
## 你明白我的意思,对吧?
您还可以尝试下一个代码,设置 hue = 'SEX ',并记录观察结果。
sns.pairplot(train, hue="SEX", diag_kind='kde', kind='scatter', palette='Set2')#plt.show()
我觉得这个想法很酷也很有趣。使用 plt.subplots 在单个网格中创建多个图表
**下面是一个简单的表格,显示了我们在本教程中介绍的不同类型的图表。**
> 记下来;
>
> -创建支线剧情需要使用 set_title,set_xlabel,set_ylabel,而不是 plt.title,plt.xlabel,plt.ylabel
>
> `- sns.scatterplot takes in extra argument — ax= axes[0,1]`
fig, axes = plt.subplots(2, 3, figsize=(22,16)) # setting the number of rows, columns and figure dimensionplt.tight_layout(pad=4) #control overlapping of the figures. # First Plot Line Graphaxes [0,0].plot(time,est_fare_Q1, 'o--r')axes [0,0].plot(time,est_fare_Q4, 's--b')axes [0,0].set_title ('Estimated Fare For The 1st and 4th Quarter Of The Year')axes [0,0].legend(["est_fare_Q1", "est_fare_Q4"])axes [0,0].set_xlabel('Time (hour)')axes [0,0].set_ylabel('Estimated Fare (naira)')# Second Plot; Scatterplotaxes [0,1].set_title('LEUCOCYTE vs AGE')sns.scatterplot(x=train['AGE'], y=train['LEUCOCYTE'], hue= train['SOURCE'], s=55, palette= 'Set2', ax= axes[0,1]);# Third Plot; Count Plotaxes[0,2].set_title ('SOURCE COLUMN WITH RESPECT TO THE SEX COLUMN')sns.countplot (x= 'SEX', hue= 'SOURCE', data =train, palette= 'dark', ax= axes[0,2]); #Fourth Plot; Box Plotaxes[1,0].set_title('Box Plot')sns.boxplot (x= 'SOURCE', y= 'LEUCOCYTE', data =train, palette= 'Set2', linewidth=2, ax= axes[1,0]); # Fifth Plot; Count Plot (horizontal arrangement)axes[1,1].set_title ('SOURCE COLUMN WITH RESPECT TO THE SEX COLUMN')sns.countplot (y= 'SEX', hue= 'SOURCE', data =train, palette= 'colorblind', ax= axes[1,1]);# Sixth Plot ; Categorical Plotsns.swarmplot(x='tip', y='day', data=df,hue= 'time', palette= 'husl', size= 7,ax=axes[1,2] )axes[1,2].set_xlabel('Time')axes[1,2].set_ylabel('Tip')axes[1,2].set_title("Tip Per Day")

作者图片
## 在单个网格上创建多个图表时需要注意的关键事项
1. **Fig Size** :你设置的 Fig Size 应该能够很好地容纳你的图形,而不会让它看起来杂乱、难以阅读,更重要的是,要防止各个图形的重叠。
2. **支线剧情维度**:你的支线剧情维度是获得一组可读图表的关键。在上面的例子中,我最初有以下设置:
> fig,axes = plt.subplots(3,3,figsize=(16,8))我意识到一些标题是重叠的,所以我调整了这些标题,并将我的设置调整为我最终在上图中使用的。
>
> fig,axes = plt.subplots(2,3,figsize=(20,14))
3. **Tight_layout (** padding):填充和上面讨论的其他两点一样重要。
> 我的初始填充
>
> 最终填充
* **创建多个图的一个主要目标是使特定变量在不同图上的分布一目了然。因此,您应该确保这一目的不会落空。**
* **确保你的单个情节可读性强,整体情节信息量大,赏心悦目。**
> 你可以为这个项目尝试更多的 figsize、column、rows 和 padding 设置,看看哪个更好看。
# 摘要
**在开展数据科学项目之前掌握领域知识的重要性**
在开始数据科学项目之前,最好至少具备该领域的基本知识,快速的谷歌搜索应该可以解决问题,并且/或者与特定领域内的某人进行对话。
由于健康领域不是我的主要领域,我简单地用谷歌搜索了一下,以获得一些关于住院病人和门诊病人护理的知识。
> 住院和门诊有什么区别?
>
> 住院护理要求病人在医院过夜,而门诊病人只需监护几个小时,不必过夜。
正如问题陈述中提到的,被归类为护理中的患者或护理外的患者将取决于关于患者的信息、条件以及实验室测试结果。
***你会同意我的观点,这个过程非常重要,不应该管理不当*** 。
*对吧???*
举例来说,如果一个孕妇去做常规检查,但医生从不查阅她以前的病历来了解她的病史,这将是一场灾难。因此,她可能会告诉她一切都很好,她可以自由回家了…

照片由 [MART 生产](https://www.pexels.com/@mart-production?utm_content=attributionCopyText&utm_medium=referral&utm_source=pexels)从 [Pexels](https://www.pexels.com/photo/photo-of-gynecologist-doing-an-ultrasound-on-a-pregnant-woman-7089035/?utm_content=attributionCopyText&utm_medium=referral&utm_source=pexels)
也许患者的实验室结果甚至是正确的,但是用于对患者进行分类(护理中或护理外患者)的模型/系统不能准确地预测和估计哪个类别是合适的。
> 我绝对不希望建立这样一个模型,我相信你和我的想法是一致的,对吗?
因此,这让我想到了数据集中的实验室结果,以及我可能通过数据可视化获得的问题、观察和见解。
## 数据可视化的主题结合了艺术和数据科学。虽然数据可视化看起来很有创意,也很赏心悦目,但它还应该在数据的可视化交流中发挥作用。它应该清楚地讲述一个故事,并清楚地向读者传达你的见解。
我建议你自己尝试其中的一些情节,检查这里的<https://www.kaggle.com/c/data-science-nigeria-patient-treatment/data>****中使用的数据集和我的 [GitHub 中的完整代码。](https://github.com/Olaoluwakiitan-Olabiyi/Practical-Guide-to-Data-Visualization)****
******祝贺你坚持到最后!!!******
****GIF via [giphy](https://giphy.com/)****
******结论******
* ****了解您正在从事的领域项目是最佳实践。****
* ****记住你的数据可视化的目的是与你的读者交流,因此确保它是信息丰富和美观的。****
* ****数据集、任务、问题和见解的类型将指导你用来与读者/利益相关者交流的图的类型。****
> ****希望你喜欢读这篇文章,就像我喜欢写它一样。****
>
> ****别忘了点击拍手图标,在下面留下你的评论!****
>
> ****我很乐意在[***LinkedIn***](https://www.linkedin.com/in/olaoluwakiitan-olabiyi/)***上与你连线。*******
*******干杯!*******
# 集成学习实用指南
> 原文:<https://towardsdatascience.com/practical-guide-to-ensemble-learning-d34c74e022a0?source=collection_archive---------10----------------------->
## 通过投票、打包、提升和堆叠来改进您的模型

由[贾斯汀·罗伊](https://unsplash.com/@justinroyphoto?utm_source=medium&utm_medium=referral)在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍摄
**集成学习**是机器学习中使用的一种技术,用于将多个模型组合成一个组模型,换句话说,组合成一个*集成模型*。集合模型旨在比单独的每个模型表现得更好,或者如果不是,至少表现得与组中最好的单个模型一样好。
在本文中,您将学习流行的集成方法: ***投票******打包******助推、******堆叠*** 以及它们的 Python 实现。我们将使用`[scikit-learn](https://scikit-learn.org/stable/index.html)`等库进行投票、装袋和升压,使用`[mlxtend](http://rasbt.github.io/mlxtend/)`进行堆栈。
在阅读本文的同时,我鼓励您查看我的 GitHub 上的 J [upyter 笔记本](https://github.com/Idilismiguzel/Machine-Learning/blob/master/Ensemble_Learning/Ensemble_Learning_Practice.ipynb)以获得完整的分析和代码。🌻
# 介绍
集成学习背后的直觉通常用一种叫做**群体智慧**的现象来描述,这意味着由一组个体做出的集合决策通常比个体决策更好。创建聚合模型(或**集成**)有多种方法,我们可以将它们分类为异类和同类集成。
在**异构集成中,**我们组合在同一数据集上训练的多个 ***不同的*** 微调模型,以生成集成模型。这种方法通常涉及*投票*、*平均、*或*叠加*技术。另一方面,在**同质系综中,**我们使用 ***相同的*** 模型,我们称之为“弱模型”,并使用诸如*打包*和*增强*的技术,我们将这个弱模型转换为更强的模型。
让我们从异构集成的基本集成学习方法开始:投票和平均。
# 1.投票(硬投票)
硬投票集成用于分类任务,它结合来自多个微调模型的预测,这些模型基于**多数投票**原则对相同数据进行训练。例如,如果我们集成 3 个分类器,它们的预测为“A 类”、“A 类”、“B 类”,那么集成模型将基于多数投票,或者换句话说,基于各个模型预测的分布模式,将输出预测为“A 类”。如您所见,我们倾向于使用奇数个型号(例如 3、5、7 个型号),以确保我们不会获得相同的票数。

硬投票:用多个模型和集合预测新实例**投票**多数投票的最终结果—图片由作者提供
Instantiate individual modelsclf_1 = KNeighborsClassifier()
clf_2 = LogisticRegression()
clf_3 = DecisionTreeClassifier()# Create voting classifiervoting_ens = VotingClassifier(
estimators=[('knn', clf_1), ('lr', clf_2), ('dt', clf_3)], voting='hard')# Fit and predict with the models and ensemble
for clf in (clf_1, clf_2, clf_3, voting_ens):
clf.fit(X_train, y_train)
y_pred = clf.predict(X_test)
print(clf.class.name, accuracy_score(y_test, y_pred))
准确度分数:
> 近邻分类器 0.93
> 逻辑回归 0.92
> 决策树分类器 0.93
> 投票分类器 0.94 ✅
正如我们所见,投票分类器具有最高的准确性得分!由于集合将结合单个模型预测,每个模型应该已经微调,并已表现良好。在上面的代码中,我只是出于演示的目的对它进行了初始化。
# 2.平均(软投票)
软投票用于分类和回归任务,它通过**平均**对基于相同数据训练的多个微调模型的预测进行组合。对于分类,它使用*预测概率*,对于回归,它使用*预测值*。我们不需要像硬投票一样的奇数个单独的模型,但我们需要至少 2 个模型来建立一个集合。

软投票:用等权重模型(w)预测新实例,集合**通过平均选择**最终结果—作者图像
软投票的一个优点是,您可以决定是对每个模型进行平均加权(平均)还是按分类器的重要性(一个输入参数)进行加权。如果您喜欢使用加权平均值,那么集合模型的输出预测将是加权概率/值的最大和。
Instantiate individual models
reg1 = DecisionTreeRegressor()
reg2 = LinearRegression()# Create voting regressor
voting_ens = VotingRegressor(
estimators=[('dt', reg1), ('lr', reg2)], weights=[2,1])# Fit and predict with the models and ensemble
for reg in (reg1, reg2, voting_ens):
reg.fit(X_train, y_train)
y_pred = reg.predict(X_test)
print(reg.class.name, mean_absolute_error(y_test, y_pred))
平均绝对误差:
> 决策树回归器 3.0
> 线性回归 3.2
> 投票回归器 2.5 ✅
重要的是要明白**投票群体**(硬投票和软投票)的表现在很大程度上取决于单个模特的表现。如果我们集合一个好的和两个表现一般的模型,那么集合模型将显示接近平均模型的结果。在这种情况下,我们要么需要改进表现一般的模型,要么我们不应该做一个集合,而是使用表现良好的模型。📌
理解了投票和平均之后,我们可以继续最后一个异构集成技术:堆叠。
# 3.堆垛
Stacking 代表“Stacked Generalization ”,它将多个单独的模型(或基础模型)与一个最终模型(或元模型)相结合,该最终模型通过基础模型的预测进行**训练。它既可以用于分类任务,也可以用于回归任务,并且可以选择使用值或概率来执行分类任务。**
与投票集成的区别在于,在堆叠中,元模型也是一个可训练的模型,事实上,它是使用基本模型的预测来训练的。因为这些预测是元模型的输入特征,所以它们也被称为元特征。我们可以选择将初始数据集包含到元要素中,或者仅使用预测。

堆叠:元模型训练中使用的基础模型预测,以**预测**最终输出——图片由作者提供
堆叠可以用多于两层来实现:**多层堆叠**,在这里我们定义基础模型,与另一层模型聚合,然后是最终的元模型。即使这可以产生更好的结果,我们也应该考虑到由于复杂性所带来的时间成本。
为了防止**过度拟合,**我们可以使用**堆叠和交叉验证**来代替标准堆叠,mlxtend 库对两个版本都有实现。下面,我将实现:
1。分类任务的标准堆叠
from mlxtend.classifier import StackingClassifier# Initialize individual models
clf_1 = KNeighborsClassifier()
clf_2 = GaussianNB()
clf_3 = DecisionTreeClassifier()# Initialize meta-model
clf_meta = LogisticRegression()# Create stacking classifier
clf_stack = StackingClassifier(
classifiers=[clf_1, clf_2, clf_3], meta_classifier=clf_meta,
use_probas=False, use_features_in_secondary=False)# Fit and predict with the models and ensemble
for clf in (clf_1, clf_2, clf_3, clf_meta, clf_stack):
clf.fit(X_train, y_train)
y_pred = clf.predict(X_test)
print(clf.class.name, accuracy_score(y_test, y_pred))
> 近邻分类器 0.84
> 高斯分类器 0.83
> 决策树分类器 0.89
> 逻辑回归 0.85
> 堆栈分类器 0.90 ✅
2。回归任务的交叉验证堆叠
from mlxtend.regressor import StackingCVRegressor# Initialize individual models
reg1 = DecisionTreeRegressor()
reg2 = SVR()# Create meta-model
meta_model = LinearRegression()# Create stacking classifier
reg_stack = StackingCVRegressor(
regressors=[reg1, reg2], meta_regressor=meta_model,
use_features_in_secondary=False)# Fit and predict with the models and ensemble
for reg in (reg1, reg2, meta_model, reg_stack):
reg.fit(X_train, y_train)
y_pred = reg.predict(X_test)
print(reg.class.name, mean_absolute_error(y_test, y_pred))
平均绝对误差:
> 决策树回归器 3.3
> 支持向量回归 5.2
> 线性回归 3.2
> 堆栈回归器 2.9 ✅
# 4.制袋材料
Bootstrap 聚合或简而言之“Bagging”聚合多个估计器,这些估计器使用用训练数据的不同子集训练的*相同的*算法。它可用于分类和回归任务,使用**引导**通过随机采样为每个估计器创建训练数据。
> 自举*是一种从原始数据创建替换样本的方法。这是通过* ***替换*** *来完成的,以使每个数据点被选取的概率相等。由于替换选择,一些数据点可能被多次选取,而一些可能永远不会被选取。我们可以使用以下公式计算大小为* n *的 bootstrap 样本中某个数据点未被选中的概率。* *(优选 n 为大数)。*

这意味着使用大约 63%的训练数据集训练每个 bagging 估计器,我们将剩余的 37% **随机(OOB)** 样本**。**
综上所述,bagging 从原始训练数据中为 *n* 个估计器抽取了 *n* 个训练数据集。每个估计器在其采样训练数据集**上被并行训练**以进行预测。然后,bagging 使用硬投票或软投票等技术聚集这些预测。

Bagging:估计器和预测器使用的自举训练样本与投票技术相结合
在 scikit-learn 中,我们可以将参数`n_estimators`定义为等于*n*——我们想要生成的估计器/模型的数量,如果我们想要评估每个估计器对其出袋样本的性能,可以将`oob_score`设置为“真”。通过这样做,我们可以很容易地了解估计者对未知数据的表现,而无需使用交叉验证或单独的测试集。`oob_score_`函数计算所有 ***n*** oob_scores 的平均值,默认情况下,使用指标**准确性** **得分**进行分类,使用 **R^2** 进行回归。
from sklearn.ensemble import BaggingClassifier# Initialize weak model
base_model = DecisionTreeClassifier(max_depth=3)# Create bagging classifier
clf_bagging = BaggingClassifier(base_estimator=base_model, n_estimators=1000, oob_score=True)clf_bagging.fit(X_train, y_train)# Check oob score
print(clf_bagging.oob_score_)
> oob_score_ : 0.918
Compare with test set
pred = clf_bagging.predict(X_test)
print(accuracy_score(y_test, pred))
> 准确率 _ 得分:0.916
随机采样的训练数据集使训练不容易偏离原始数据,因此装袋**减少了单个估计量的方差**。
一种非常流行的打包技术是**随机森林**,其中估值器被选为决策树。随机森林使用引导来创建具有替换的训练数据集,并且它还选择一组特征(没有替换)来最大化每个训练数据集上的随机化。通常,所选特征的数量等于特征总数的平方根。
# 5.助推
Boosting 使用**渐进学习**,这是一个迭代过程,专注于最小化先前估计器的误差。这是一种**顺序方法**,其中每个估计器依赖于前一个估计器来改进预测。最流行的增强方法是自适应增强(AdaBoost)和梯度增强。
**AdaBoost** 对每个 *n* 估计器使用整个训练数据集,并做了一些重要的修改。第一个估计器(弱模型)在具有相等加权数据点的原始数据集上训练。在进行第一次预测并计算误差后,与正确预测的数据点相比,错误预测的数据点被分配有**更高的权重**。通过这样做,下一个评估者将关注这些难以预测的实例。这个过程将一直持续到所有的 *n* 个估计器(比如 1000 个)被顺序训练。最后,集合的预测将通过加权多数投票或加权平均来获得。

AdaBoost:训练数据中权重更新的序列模型训练——图片由作者提供
from sklearn.ensemble import AdaBoostRegressor# Initialize weak model
base_model = LinearRegression(normalize=True)# Create AdaBoost regressor
reg_adaboost = AdaBoostRegressor(base_estimator=base_model, n_estimators=1000)reg_adaboost.fit(X_train, y_train)# Predict and compare with y_test
pred = reg_adaboost.predict(X_test)
rmse = np.sqrt(mean_squared_error(y_test, pred))
print('RMSE:', rmse)
> RMSE: 4.18
由于每个下一个估计器的目的是纠正错误分类/错误预测的数据点,增强**减少了每个估计器的偏差**。
**梯度提升**,非常类似于 AdaBoost,通过顺序迭代改进了以前的估计器,但它不是更新训练数据的权重,而是使新的估计器适应来自以前估计器的**残差**。XGBoost、LightGBM 和 CatBoost 是流行的梯度提升算法,尤其是 XGBoost 是许多竞赛的获胜者,并因非常快速和可扩展而流行。
# 结论
在本文中,我们学习了主要的集成学习技术来提高模型性能。我们介绍了每种技术的理论背景以及相关的 Python 库来演示这些机制。
集成学习在机器学习中占有很大一部分,它对每个数据科学家和机器学习实践者都很重要。你可能会发现有很多东西要学,但我相信你永远不会后悔!!💯
如果你需要关于自举的复习,或者如果你想学习更多关于采样技术的知识,你可以看看我下面的文章。
</resampling-methods-for-inference-analysis-e75fecfefcb2>
我希望你喜欢阅读关于集成学习方法的文章,并发现这篇文章对你的分析有用!
*如果你喜欢这篇文章,你可以**[***在这里阅读我的其他文章***](https://medium.com/@idilismiguzel)**和* [***关注我上媒***](http://medium.com/@idilismiguzel/follow)*如果有任何问题或建议,请告诉我。✨***
***喜欢这篇文章吗? [**成为更多会员!**](https://idilismiguzel.medium.com/membership)***
## ***参考***
1. ***[合奏学习延伸阅读](https://sebastianraschka.com/pdf/lecture-notes/stat479fs18/07_ensembles_notes.pdf)***
2. ***[mlxtend 库](http://rasbt.github.io/mlxtend/)***
# PostgreSQL 实用介绍
> 原文:<https://towardsdatascience.com/practical-introduction-to-postgresql-5f73d3d394e?source=collection_archive---------16----------------------->
## 世界上最先进的开源关系数据库

[南安](https://unsplash.com/@bepnamanh?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)在 [Unsplash](https://unsplash.com/s/photos/elephant?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 上的照片
SQL 是大多数关系数据库管理系统(RDBMS)用来管理以表格形式存储的数据的编程语言。SQL 是数据科学家、分析师和工程师的必备技能。
SQL 表由带标签的行和列组成。关系数据库包含多个通过共享列相互关联的表。
SQL 提供了许多函数和方法来高效、准确地管理存储在关系数据库中的数据。有许多不同的关系数据库管理系统(如 MySQL、PostgreSQL、SQL Server)。他们采用的 SQL 语法可能略有不同。
在本文中,我们将对全球最先进的开源关系数据库 [PostgreSQL](https://www.postgresql.org) 做一个实际的介绍。
PostgreSQL 或通常用作 Postgres,已经存在了 30 多年。由于其稳健性、可靠性和性能,它受到许多不同行业的许多企业的青睐。
官网提供说明,明确解释了[如何安装](https://www.postgresql.org/download/) PostgreSQL。安装后,您可以在 psql 中练习,psql 是一个使用 PostgreSQL 的交互式终端。
我使用 macOS 的 Postgres 应用程序。打开后,您可以选择一个数据库并单击 start。不同的项目有不同的数据库是常见的做法。

Postgres 应用程序(图片由作者提供)
我选择了 postgres 数据库。这是 psql 终端的初始屏幕:

(图片由作者提供)
为了查看所有数据库的列表,我们可以使用“\l”命令。
postgres=# \l

(图片由作者提供)
我们可以在 psql 终端中使用“\c”命令和数据库名称切换到另一个数据库。
postgres=# \c soneryYou are now connected to database "sonery" as user "sonery".sonery=#
关系数据库可能包含许多表。“\dt”命令显示当前数据库中的表列表。
sonery=# \dtList of relationsSchema | Name | Type | Owner
-------+-------+-------+--------
public | sales | table | sonery(1 row)
我的数据库中只有一个名为“sales”的表。这似乎与我刚才说的在一个数据库中有很多表相矛盾。但是,这只是为了练习。
让我们创建两个新表。
sonery=# CREATE TABLE inventory (
product_id int,
warehouse_id int,
stock_qty int,
date date
);
CREATE TABLEsonery=# CREATE TABLE orders (
order_id int,
product_id int,
order_qty int,
date date
);
CREATE TABLE
psql 终端返回“CREATE TABLE ”,表示已经成功创建了该表。让我们再次检查当前数据库中的表。
sonery=# \dtList of relationsSchema | Name | Type | Owner
-------+-----------+-------+--------
public | inventory | table | sonery
public | orders | table | sonery
public | sales | table | sonery(3 rows)
我们已经创建了库存和订单表,但是它们是空的。SQL 的 insert 语句可用于手动填充表。
sonery=# INSERT INTO orders VALUES
(101, 1001, 2, '2021-01-04'), (102, 1423, 1, '2021-01-04'), (103, 1260, 5, '2021-01-13');INSERT 0 3
我们不必提供列的名称。但是,我们需要根据列顺序适当地写入值。
如果我们提供列的名称,这些值将根据我们指定的顺序插入到表中。
sonery=# INSERT INTO orders (order_id, date, product_id, order_qty)
VALUES (104, '2021-05-13', 1590, 5);INSERT 0 1
下一个例子是关于查询一个表,这是数据库中最常用的操作。我们编写查询从数据库中检索数据。select 语句用于编写查询。
sonery=# SELECT * FROM orders;order_id | product_id | order_qty | date
---------+------------+-----------+------------
101 | 1001 | 2 | 2021-01-04
102 | 1423 | 1 | 2021-01-04
103 | 1260 | 5 | 2021-01-13
104 | 1590 | 5 | 2021-05-13(4 rows)
“*”表示我们想要检索所有的列。我们还可以编写所需的列名。
sonery=# SELECT order_id, date FROM orders;order_id | date
---------+------------
101 | 2021-01-04
102 | 2021-01-04
103 | 2021-01-13
104 | 2021-05-13(4 rows)
## 结论
我们已经做了 PostgreSQL 的基本介绍。有些例子是通用的 SQL 例子,所以即使您使用不同的 RDBMS,它们也会有所帮助。
我们在本文中讨论的只是 PostgreSQL 和 SQL 的一小部分。我计划写更多的文章,关注 PostgreSQL 更高级的操作。敬请期待!
感谢您的阅读。如果您有任何反馈,请告诉我。
# PySpark 实用介绍
> 原文:<https://towardsdatascience.com/practical-introduction-to-pyspark-fd04b48b9672?source=collection_archive---------15----------------------->
## 简单如 Python,强大如 Spark

Marc-Olivier Jodoin 在 [Unsplash](https://unsplash.com/s/photos/fast?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 上拍摄的照片
Spark 是一个用于大规模数据处理的分析引擎。它让您可以将数据和计算分散到集群上,从而实现显著的性能提升。
由于收集和存储数据变得越来越容易,成本也越来越低,当我们处理现实生活中的问题时,可能会有大量的数据。因此,像 Spark 这样的分布式引擎正在成为数据科学生态系统中的主要工具。
PySpark 是 Spark 的 Python API。它结合了 Python 的简单性和 Spark 的高效性,这种合作得到了数据科学家和工程师的高度赞赏。
在本文中,我们将通过几个例子来介绍 PySpark 的 SQL 模块,它用于处理结构化数据。
我们首先需要创建一个 SparkSession,作为 Spark SQL 的入口点。
from pyspark.sql import SparkSessionsc = SparkSession.builder.getOrCreate()
sc.sparkContext.setLogLevel("WARN")print(sc)
<pyspark.sql.session.SparkSession object at 0x7fecd819e630>
我们将使用这个 SparkSession 对象与 Spark SQL 的函数和方法进行交互。让我们通过读取 csv 文件来创建一个 spark 数据帧。我们将使用 Kaggle 上的墨尔本房产[数据集](https://www.kaggle.com/anthonypino/melbourne-housing-market)。
df = sc.read.option("header", "true").csv(
"/home/sparkuser/Desktop/melb_housing.csv"
)
我们将通过仅选择 5 列来缩小数据框。可以使用“df.columns”方法查看列列表。
df = df.select("Type", "Landsize", "Distance", "Regionname", "Price")df.show(5)

(图片由作者提供)
正如我们将在示例中看到的,PySpark 语法看起来像是 Pandas 和 SQL 的混合。因此,如果您已经在使用这些工具,那么学习 PySpark 将会相对容易。
我们可以使用 orderBy 函数对数据框中的行进行排序。让我们来看看数据集中最贵的 5 栋房子。
df.orderBy("Price", ascending=False).show(5)

(图片由作者提供)
PySpark 的 SQL 模块有许多函数可用于数据分析和操作。我们可以一次或分别导入它们。
import all functions
from pyspark.sql import functions as F
数据集中有 3 种不同类型的房屋。我们可以使用 groupby 函数计算每种类型的平均价格。
df.groupby("Type").agg(F.round(F.mean("Price")).alias("Avg_price")).show()

(图片由作者提供)
我们来详细说明一下语法。我们按照类型列对观察值(即行)进行分组。然后我们计算每组的平均价格。round 函数用于向上舍入小数点。alias 方法更改聚合列的名称。它类似于 SQL 中的“as”关键字。
距离栏显示到中央商务区的距离。当我们离开市中心时,房价通常会下降。我们可以通过计算距离超过 3 英里的房屋的平均房价来证实我们的预测。
df.filter(F.col("Distance") > 3).groupby("Type").agg(
F.round(F.mean("Price")).alias("Avg_price")
).show()

(图片由作者提供)
我们使用 filter 函数对距离列应用一个条件。结果证实了我们的预期。每种类型的平均价格都下降了。
另外两个有用的函数是 count 和 countDistinct,它们分别计算一个组中的总观察值和不同观察值(即行)的数量。例如,以下代码返回每种类型的房屋数量以及不同价格的数量。
df.groupby("Type").agg(
F.count("Price").alias("n_houses"),
F.countDistinct("Price").alias("n_distinct_price")
).show()

(图片由作者提供)
数据分析中的一个典型任务是基于现有列派生新列。这可能是你的特征工程过程的一部分。关于住房数据集,我们可以创建一个新要素来表示每单位土地面积的价格。
若要完成此任务,可以按如下方式使用 withColumn 函数:
df = df.withColumn(
"Price_per_size",
F.round(F.col("Landsize")*1000 / F.col("Price"), 2)
)
新列的名称是“价格/大小”。下一行指定了导出值的步骤。我们把土地面积乘以 1000,再除以房价。结果四舍五入到小数点后两位。

(图片由作者提供)
## 结论
我们已经介绍了 PySpark 的数据分析和操作。PySpark 的 SQL 模块提供了更多的函数和方法来执行高效的数据分析。
需要注意的是,Spark 针对大规模数据进行了优化。因此,在处理小规模数据时,您可能看不到任何性能提升。事实上,在处理小数据集时,Pandas 可能会比 PySpark 表现得更好。
感谢您的阅读。如果您有任何反馈,请告诉我。
# 构建机器学习解决方案的实用方法
> 原文:<https://towardsdatascience.com/practical-method-to-framing-machine-learning-solutions-8b36a9d40efb?source=collection_archive---------34----------------------->
## 构建机器学习解决方案以实现产品目标的六步方法。

图 1:构思一个健壮的解决方案。由 [pixabay](https://www.pexels.com/@pixabay) 拍摄自[像素](https://www.pexels.com/photo/clear-light-bulb-355948/),
我最近介绍了一种关于如何构建机器学习(ML)解决方案的[方法](/framing-machine-learning-solution-4221f5da035d)。简而言之,您应该重复一个三步流程,包括细化问题、理解数据需求和探索基线解决方案。在这篇文章中,我分享了一个实用的方法。我希望这个过程不会像图 1 所示的有了一个灯泡的想法并找出它适合的地方那样模糊。
# 进化机器学习解决方案

图 2:不断发展的机器学习解决方案。作者图。
其核心的机器学习解决方案是一个*代码*,它读取*数据*以生成一个*模型*,然后使用来自同一来源的另一组数据来生成预测。如果你遵循一个务实的方法,比如三步法,第一个解决方案草案不太可能坚持到最后。相反,如图 2 所示,您可能会对解决方案进行多次改进,其中每次迭代您都在搜索更好的数据和/或开发改进的代码。问题是你应该写多少份草稿。
从我的工程和研究背景中汲取灵感,我提倡遵循六个步骤来使这个过程更加有效和实用(见图 3)。

图 3:构建机器学习解决方案的六个步骤。作者图。
1. 与利益相关者一起定义一个有影响力的业务目标
2. 和你的利益相关者一起设定一个合理的期限
3. 计算基准性能
4. 构建简单、快速、改进的解决方案
5. 通过重复步骤 4,改进您的解决方案,直到您在截止日期内达到基线性能
6. 在截止日期到来时,与你的利益相关者一起决定下一步行动
我们将提供每个步骤的更多细节。
## 定义业务目标
你应该做的第一件事是做一个商业案例和一个合理的目标。如果你的利益相关者不相信你的目标,你需要重新审视目标和/或案例。避免过于雄心勃勃,因为在跑步之前先学会走路更好。
将目标与业务指标联系起来,例如[销售业绩](https://en.wikipedia.org/wiki/Sell-through)、[流失率](https://en.wikipedia.org/wiki/Churn_rate)等*。*前者表示一段时间内企业售出的产品占其总库存的百分比,后者表示一段时间后不再是活跃客户的客户比例。显然,卖出率越高或流失率越低越好。无论您选择什么,您应该知道减少或增加您的指标将改善业务。
## 设定合理的截止日期
一旦你确定了目标和衡量标准,选择一个既不太短也不太长的截止日期。截止日期不应该迫使你使用廉价的伎俩,但也不应该让你沉迷于轻浮的想法。使用无法在工业化中存活的现成解决方案可以被认为是一种廉价的伎俩。探索一个没有可复制结果的未经测试的算法会被认为是一个轻率的想法。没有一个是好的。选择一个合适的时间让你有时间做一些实质性的事情,同时给你机会做更多有影响力的事情。
## 计算基线性能
定义可接受性能的最简单方法是按照当前的非 ML 解决方案计算指标。它可以基于领域驱动的试探法。例如,如果一定数量的商业专家在一段时间内按照一个共同的策略调整销售价格,那么可以实现什么样的销售。
如果你的利益相关者同意计算基线的过程,继续计算。当你的涉众对基线水平满意时,你就可以开始了。避免选择一个很难达到的基线。如果业务需要模型非常精确,您应该回到绘图板,开始一个更简单的业务案例。
## 构建可行的解决方案
避免开发执行时间过长或裁剪过多的解决方案。这两者都是进一步改进的障碍。你如何知道你需要保持的简单程度?
将你的开发工作分解成冲刺阶段,并在冲刺阶段经常发布。从一开始就专注于开发一个自动化的快速反馈系统。如果您不能这样做,很可能是您的解决方案太耗时或太复杂。一开始,你可能不需要起草一个 ML 解决方案。您可以从一个简单的基于启发式的解决方案开始。例如,您如何让业务专家设定价格的过程自动化?另一种从简单开始的方法是避免从头开始。而是集中于从经过良好测试的、可复制的作品中构建一些东西。
如果你设法做到这一点,你将能够产生和分析更多的结果,这将导致一个解决方案的更快发展。
## 改进解决方案
不断寻找更好的数据或更好的解决方案,直到达到基线。为了调整解决方案,使用一组技术误差指标,例如,[表示平方误差](https://en.wikipedia.org/wiki/Mean_squared_error) , [表示绝对百分比误差](https://en.wikipedia.org/wiki/Mean_absolute_percentage_error)等。首先,通过调整算法参数、选择不同的功能等,尝试从当前设计中获得尽可能多的好处。一旦你运气不好,考虑改变算法或使算法更复杂。
如果你设法在截止日期前达到基线绩效,与你的利益相关者沟通以决定是否追求进一步的改进。如果是这样的话,就这样做,直到你到达最后期限。在这种情况下,可以考虑让模型更加健壮,比如面对数据漂移如何保持相同的性能水平。当你在那个方向碰壁时,集中精力提高速度。
## 决定下一步
当你到达最后期限时,向你的股东展示解决方案和结果。在此之前,稍微准备一下接下来的步骤。此外,探索如果没有后续步骤,你还可以做哪些优先级较低的事情。如果结果不乐观,这是特别必要的。
# 结束语
这个讨论是对复杂实践的极大简化的总结。如果你已经面对过如此复杂的实践,我很想知道你会如何概括。如果你是新手,让我知道,如果我的观点能引起你的共鸣。
# MongoDB 实用 NoSQL 指南
> 原文:<https://towardsdatascience.com/practical-nosql-guide-with-mongodb-d364916336dd?source=collection_archive---------39----------------------->
## 通过练习提高你的技能

布雷特·乔丹在 [Unsplash](https://unsplash.com/s/photos/practice?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 上的照片
NoSQL 指的是非 SQL 或非关系数据库设计。它提供了一种有组织的方式来存储数据,但不是像 SQL 那样以表格的形式。NoSQL 数据库存储数据的常用结构是键值对、宽列、图形或文档。
数据科学生态系统中有几个 NoSQL 数据库。其中一个流行的是 MongoDB,它将数据存储为文档。
MongoDB 中的文档由字段-值对组成。文档被组织在一个称为“集合”的结构中。打个比方,我们可以把文档想象成表格中的行,把集合想象成表格。
在本文中,我们将通过几个例子来练习在 MongoDB 中查询数据库。如果你想了解更多关于 NoSQL 以及如何在你的电脑上设置 MongoDB 的信息,这里有一个介绍性的[指南](/introduction-to-nosql-with-mongodb-8e5a2513c9e8)供你参考。
这些示例将查询一个名为“marketing”的集合,该集合存储了关于零售企业营销活动的数据[。a 该集合中的文档包括以下信息。](https://www.kaggle.com/yoghurtpatil/direct-marketing)
{
"_id" : ObjectId("6014dc988c628fa57a508088"),
"Age" : "Middle",
"Gender" : "Male",
"OwnHome" : "Rent",
"Married" : "Single",
"Location" : "Close",
"Salary" : 63600,
"Children" : 0,
"History" : "High",
"Catalogs" : 6,
"AmountSpent" : 1318
}
## 示例 1
默认情况下,find 方法检索所有文档。我们可以使用 limit 方法只显示一些文档。例如,我们可以显示集合中的第一个文档,如下所示:
db.marketing.find().limit(1).pretty(){
"_id" : ObjectId("6014dc988c628fa57a508088"),
"Age" : "Middle",
"Gender" : "Male",
"OwnHome" : "Rent",
"Married" : "Single",
"Location" : "Close",
"Salary" : 63600,
"Children" : 0,
"History" : "High",
"Catalogs" : 6,
"AmountSpent" : 1318
}
数据库指的是当前数据库。我们需要在点号后指定集合名称。漂亮的方法是以更结构化的方式显示文档。如果没有 pretty 方法,输出如下所示:
{ "_id" : ObjectId("6014dc988c628fa57a508088"), "Age" : "Middle", "Gender" : "Male", "OwnHome" : "Rent", "Married" : "Single", "Location" : "Close", "Salary" : 63600, "Children" : 0, "History" : "High", "Catalogs" : 6, "AmountSpent" : 1318 }
## 示例 2
find 方法允许一些基本的过滤。我们可以为 find 方法中的字段指定所需的值,如下所示:
db.marketing.find( {"Children": 1} ).limit(1).pretty(){
"_id" : ObjectId("6014dc988c628fa57a50808a"),
"Age" : "Middle",
"Gender" : "Male",
"OwnHome" : "Own",
"Married" : "Married",
"Location" : "Close",
"Salary" : 85600,
"Children" : 1,
"History" : "High",
"Catalogs" : 18,
"AmountSpent" : 2436
}
但是,这不是过滤的最佳方式。MongoDB 的聚合管道提供了一种更有效的方式来过滤、转换和聚合数据,我们将在下面的例子中看到这一点。
## 示例 3
我们想了解收到 10 份以上目录的顾客的平均消费金额。聚合管道可用于完成此任务,如下所示。
db.marketing.aggregate([
... { \(match : { Catalogs : {\)gt : 10} } },
... { \(group : { _id : null, avgSpent : {\)avg : "$AmountSpent"} } }
... ])
管道的第一步是匹配阶段,它根据给定的条件过滤文档。“$gt”表达式代表“大于”。分组阶段根据给定的字段和聚合函数执行聚合。
## 实例 4
在前面的例子中,我们发现收到 10 个以上目录的客户的平均消费金额。我们再进一步,分别求出不同年龄段的相同值。
db.marketing.aggregate([
... { \(match : { Catalogs : {\)gt : 10} } },
... { \(group : { _id : "\)Age", avgSpent : {\(avg : "\)AmountSpent"} } }
... ]){ "_id" : "Middle", "avgSpent" : 1678.3965087281795 }
唯一的变化是“_id”值。它指定要用作分组字段的字段。
## 实例 5
可以在多个字段上执行多个聚合。例如,我们可以计算至少有一个孩子的客户的平均工资和总支出。
db.marketing.aggregate([
... { \(match : { Children : {\)gt : 0} } },
... { \(group: { ... _id : null, ... "avgSalary" : {\)avg: "\(Salary"}, ... "totalSpent" : {\)sum: "$AmountSpent"}
... }
... }
... ])
每个聚合都作为组阶段中的一个单独条目写入。
## 实例 6
MongoDB 的聚合管道有一个对查询结果进行排序的阶段。当结果由几个条目组成时,这很有用。此外,一组经过排序的数据提供了一个更加结构化的概览。
以下查询返回消费超过 1000 美元的客户的平均工资。结果根据孩子的数量分组,并按平均工资降序排序。
db.marketing.aggregate([
... { \(match: { AmountSpent : {\)gt : 1000}}},
... { \(group: { _id : "\)Children", avgSalary : {\(avg : "\)Salary"}}},
... { $sort: { avgSalary : -1 }}
... ]){ "_id" : 3, "avgSalary" : 84279.48717948717 }
{ "_id" : 2, "avgSalary" : 83111.11111111111 }
在排序阶段,“-1”表示降序,“1”表示升序。
## 例 7
在本例中,我们将看到聚合管道的两个新阶段。
项目阶段允许选择要显示的字段。由于典型的文档可能有许多字段,因此显示所有字段可能不是最佳选择。
在项目阶段,我们通过值 1 选择要显示的字段。结果仅显示指定的字段。请务必注意,在所有情况下,默认情况下都会显示 id 字段。我们需要将其显式设置为 0,以便从结果中排除 id 字段。
聚合管道中的限制阶段限制了要显示的文档数量。它与 SQL 中的 limit 关键字相同。
下面的查询根据花费的金额过滤文档,然后根据薪水进行排序。它只显示前 5 个单据的薪资和支出金额字段。
db.marketing.aggregate([
... { \(match : { AmountSpent : {\)gt : 1500} } },
... { $sort : { Salary : -1 } },
... { $project : { _id : 0, Salary : 1, AmountSpent : 1 } },
... { $limit : 5 }
... ]){ "Salary" : 168800, "AmountSpent" : 1512 }
{ "Salary" : 140000, "AmountSpent" : 4894 }
{ "Salary" : 135700, "AmountSpent" : 2746 }
## 结论
我们已经介绍了几个演示如何查询 MongoDB 数据库的例子。聚合管道允许创建简单和高度复杂的查询。
我们已经看到了聚合管道中的 5 个基本阶段,即匹配、分组、排序、项目和限制。还有更多的阶段使查询操作更加通用和强大。
与任何其他主题一样,要熟练使用 MongoDB 编写查询需要大量的练习。
感谢您的阅读。如果您有任何反馈,请告诉我。
# 合成数据的实用隐私
> 原文:<https://towardsdatascience.com/practical-privacy-f5c68fae770a?source=collection_archive---------48----------------------->
## **实施实际攻击以测量合成数据模型中的无意记忆**
# TL;DR;
在本帖中,我们将对合成数据模型实施一次实际攻击,这在 Nicholas Carlini 等人的[秘密分享者:评估和测试神经网络中的非故意记忆](https://arxiv.org/pdf/1802.08232.pdf)中有所描述。艾尔。我们将利用这次攻击来看看[合成数据](https://gretel.ai/blog/what-is-synthetic-data)模型如何利用各种神经网络和差分隐私参数设置来保护数据集中的敏感数据和秘密。有一些非常令人惊讶的结果。

特征图像 Gretel.ai
# 背景
开放数据集和机器学习模型对于知识的民主化非常有价值,并允许开发人员和数据科学家以新的方式试验和使用数据。无论数据集是否包含位置数据、影像(如摄像机视频)甚至电影评论,用于创建这些数据集的基础数据通常包含并基于个人数据。
合成数据模型是一种很有前途的技术,用于生成包含与原始数据相同的洞察力和分布的人工数据集,而不包含任何真实的用户数据。潜在的应用包括允许[医学研究人员在不了解患者的情况下了解一种罕见疾病](/reducing-ai-bias-with-synthetic-data-7bddc39f290d),或者在不暴露潜在[敏感信息的情况下训练私人数据的机器学习模型,并减少偏差](https://gretel.ai/blog/automatically-reducing-ai-bias-with-synthetic-data)。
# **数据集**
传统上,差分隐私的成功应用是通过包含高度同质数据的大规模数据集来实现的。例如,[美国人口普查数据](https://www.census.gov/programs-surveys/decennial-census/2020-census/planning-management/2020-census-data-products.html),或者[苹果设备表情符号预测](https://www.apple.com/privacy/docs/Differential_Privacy_Overview.pdf)。这些数据集有数以亿计的行,并且维数较低,这使它们成为差分隐私等隐私保护技术的理想候选对象。对于这个例子,我们将使用一个小得多的数据集,该数据集基于电动自行车共享数据的公共源。是什么让这个数据集对我们的数据集感兴趣?它包含敏感的位置数据,这一直被认为是一个正确匿名的[挑战](https://www.nytimes.com/interactive/2019/12/19/opinion/location-tracking-cell-phone.html)。其次,我们正在处理一个不到 30,000 行输入数据的数据集,它更能代表科学家每天处理的典型数据集。
让我们看看是否可以构建一个合成数据集,它具有与原始数据相同的洞察力,但不使用真实的行程或位置。对于这个例子,我们在洛杉矶地区记录了一天的公共拼车信息,你可以在我们的 [Github](https://github.com/gretelai/gretel-synthetics/blob/master/examples/data/uber_scooter_rides_1day.csv) 上下载数据集。格式如下:

加州洛杉矶为期一天的电动自行车旅行
# **攻击**
我们将对 [Gretel.ai 的数据模型](https://gretel.ai/synthetics)(这将在任何生成语言模型上工作)实施实际攻击,方法是将金丝雀值(与训练集无关的随机生成的字符串)以各种频率随机插入模型的训练数据中。然后,我们可以使用这些数据来生成具有各种神经网络和隐私设置的模型,并测量每个模型记忆和回放金丝雀值的倾向。
这些结果与差别隐私提供的保证有何不同?嗯,如果这些攻击通过,我们不能从数学上声称一个模型是私有的。例如,有可能模型已经记住了一些非预期的信息(金丝雀值),这些信息只是还没有被模型重放。也就是说,知道您可以在数据集中插入 10 到 100 次金丝雀值,而不会看到它重复出现,这确实为您的模型提供了实际检查,_ 并且 _ 您对隐私保证的期望似乎是成立的。
让我们从生成随机秘密插入训练数据开始。
生成一组秘密来测试模型记忆
接下来,对定型集进行采样,并添加包含金丝雀值的新记录。
将金丝雀值混合到训练数据集中
我们可以使用一个简单的助手在生成的数据中搜索秘密字符串的存在。让我们看看我们将在训练数据中插入多少个秘密值。
一个在数据帧中搜索我们的秘密字符串的助手
# 进行实验
我们准备通过在包含各种数量的金丝雀值的新训练集上训练合成数据模型来运行我们的实验。在下面的实验中,我们用 10 种不同的配置训练了合成数据模型,并且在训练数据中插入了 18 到 138 次鸭翼。然后,我们计算了模型在生成过程中重放的秘密的数量。
要亲自尝试,请查看我们的[示例笔记本](https://gist.github.com/zredlined/7692fe2bb8999f933f64e62cf912245e)。
<https://colab.research.google.com/gist/zredlined/7692fe2bb8999f933f64e62cf912245e/synthetic-data-uber-differential-privacy.ipynb> [## 谷歌联合实验室
### Gretel . ai—colab.research.google.com 优步 DP 实验笔记本](https://colab.research.google.com/gist/zredlined/7692fe2bb8999f933f64e62cf912245e/synthetic-data-uber-differential-privacy.ipynb)
# 检查结果

我们对合成模型实际攻击的结果
正如我们可以看到的,在实验 0、1 和 2 中没有差分隐私的标准合成数据配置提供了对稀有数据和秘密被学习的合理保护,其中秘密需要在训练数据中出现超过 18 次以被模型记忆和重放。
**差分隐私,即使 epsilon 和 delta 隐私值比理论保证的推荐值高几个数量级,在所有测试配置中很好地防止了鸭翼的记忆**。实验 9 使用了相对较高的噪声乘数(0.1)和积极的梯度削波(1.5),这导致在生成的数据中再现了零个鸭翼,但模型精度却大大降低了。
令我们惊讶的是,在实验 6 中简单地使用梯度裁剪和最小水平的噪声到优化器中防止了我们的金丝雀值的任何重放(甚至是在数据中重复 138 次的秘密),而模型准确性只有很小的损失。
# 后续步骤
隐私工程的挑战之一是在隐私保证与实用性和准确性之间找到正确的平衡。上面的例子特别有趣,因为它们显示了在隐私保护方面的实质性收益,与差分隐私的理论保护所要求的相比,具有远不那么激进的设置。尝试使用不同的参数配置对您自己的数据进行试验,以找到适合您的用例的平衡点。
有自己的用例可以讨论吗?我们很乐意听到你的用例——欢迎在评论中联系我们进行更深入的讨论,或者通过 [hi@gretel.ai](mailto:hi@gretel.ai) 。
# 实用 Python 因果关系:数据科学的计量经济学
> 原文:<https://towardsdatascience.com/practical-python-causality-econometrics-for-data-science-ffc074e11a1d?source=collection_archive---------7----------------------->
## [思想和理论](https://towardsdatascience.com/tagged/thoughts-and-theory)
## 使用 Python 库的计量经济学数据科学介绍:DoWhy,包括因果关系案例研究论文的详细代码演练

来自“艰难的旅行:政治冲突阴影下的失业和道路基础设施”([亚伯拉罕,2021](https://www.cambridge.org/core/journals/political-science-research-and-methods/article/abs/hard-traveling-unemployment-and-road-infrastructure-in-the-shadow-of-political-conflict/135F8A50F613DA3C9C4CB9335F0BFCF7) )的巴勒斯坦西岸的卫星图像
数据科学家倾向于关注描述性和预测性分析,而忽视因果分析。然而,决策需要因果分析,这是在新冠肺炎疫情期间公共卫生流行病学家公认的事实。由于我的生物学背景,我已经将格言“*相关性不等于因果关系*”内化,以至于我刻意回避所有因果关系的说法。幸运的是,我贪得无厌的好奇心引导我进入计量经济学领域,该领域包含因果关系,并建立了一套严格的数学体系来促进因果分析。
最近,我在世界银行的中东和北非地区咨询工作激发了我对计量经济学的兴趣。具体来说,最近发表在《政治学研究与方法》杂志上的一篇论文详细描述了以色列检查站对巴勒斯坦就业结果的罕见因果影响评估( [Abrahams,2021](https://www.cambridge.org/core/journals/political-science-research-and-methods/article/abs/hard-traveling-unemployment-and-road-infrastructure-in-the-shadow-of-political-conflict/135F8A50F613DA3C9C4CB9335F0BFCF7) )。在“艰难的旅行”中,作者在一个巧妙设计的实验中利用了一个工具变量,突出了以色列军队在持续占领巴勒斯坦期间部署的道路封锁的因果影响。在本文中,我通过在一个实用的 Python 教程中复制这篇有趣论文的结果,从实际应用的角度来探索因果关系。
首先,我明确了机器学习和计量经济学之间的区别,这是传达因果关系的复杂性和艰巨性的必要步骤。接下来,我将介绍本教程中使用的 Python 库,并讨论因果分析的计量经济学方法。接下来,我概述了案例研究论文中描述的实验设计,直接进入相关方程的直观演示。最后,使用 Python,我复制了这篇论文的主要结果,并实际强调了代码实现。
# **为什么是计量经济学?**
大多数数据科学家对机器学习感到满意,但很少利用计量经济学。尽管如此,计量经济学和机器学习都依赖于将统计方法应用于数据,以便根据经验建模和解决问题,表面上的区别是计量经济学对经济数据的应用。然而,重要的区别在于这两个领域中常见的问题类型。传统上,机器学习主要集中在预测上;最常见的目标是创建一个能够泛化的模型,这样就可以对新的、未观察到的数据进行预测。相反,计量经济学主要关注因果关系,目标是理解因果关系,通常在政策评估的范围内,以便建议的行动可以得到经验证据的支持。
因果关系需要从现有数据中推测因果关系,同时考虑不确定性,与预测类似,它也依赖于无法植根于地面真理的未经证实的假设。预测问“什么?”会发生,因果问“为什么?”事情发生了,更重要的是,“为什么?”这种影响不是由其他原因造成的。因此,因果问题具有更高的举证责任,其优势是由于在处理混杂因素和不确定性时所涉及的严格性,所获得的见解具有确定性。
尽管我很欣赏机器学习的效用,但预测无法回答每个问题,而且通常情况下,因果推理是支持现实生活决策的必要条件。此外,我喜欢问“为什么?”尽管很难令人满意地回答因果问题,但试图将结果归因于原因是人类的本能。通过一本名为《基本无害的计量经济学:一个经验主义者的伙伴》的通俗易懂的书( [Angrist 和 Pischke 2009](https://press.princeton.edu/books/paperback/9780691120355/mostly-harmless-econometrics) ),我被鼓励将计量经济学方法融入到我的实证工作中。作者在序言中指出,
> “任何对使用数据来制定公共政策或促进公共健康感兴趣的人都必须消化和使用统计结果。任何对从人们的数据中得出有用的推论感兴趣的人都可以说是应用计量经济学家。”
我认为,数据科学家应该提出因果问题,计量经济学方法自然适合将因果框架应用于数据驱动的研究。事实上,去年哈佛数据科学倡议启动了一项研究机器学习因果推理的计划。推动将因果关系整合到数据科学中,反映在经济学家对机器学习的缓慢接受上,这表明了一种双向关系。例如,杰出的经济学家苏珊·艾希(Susan Athey)和圭多·w·伊本斯(Guido W. Imbens)主张在实证经济工作中采用机器学习方法。在 2019 年版的*经济学年度评论*中,在一篇名为“[经济学家应该了解的机器学习方法](https://www.annualreviews.org/doi/abs/10.1146/annurev-economics-080217-053433)的论文中,艾希和伊本斯指出
> “ML 文献中开发的方法在大数据环境中特别成功,……对于这种环境,ML 工具正在成为跨学科的标准,因此经济学家的工具包需要相应地进行调整,同时保留应用计量经济学的传统优势。”
# **因果关系测试**
因果分析的过程可以分为四个阶段:第一步是建立因果问题的模型,第二步是确定需求,第三步是估计效果,第四步是反驳得到的估计。为了简单起见,在本文中,我将主要关注用微软开发的用于因果推理的 Python 库 [DoWhy](https://github.com/microsoft/dowhy) 实现因果分析的四个阶段。此外,为了复制案例研究论文的主要结果,我还利用了 Python 库 [linearmodels](https://bashtage.github.io/linearmodels/) 。
当在第一阶段对因果问题建模时,有必要明确因果假设,而 DoWhy 通过[因果图](https://en.wikipedia.org/wiki/Causal_graph)使这成为可能。借用计量经济学的术语,因果图是概率图形模型,对关于[数据生成过程](https://en.wikipedia.org/wiki/Data_generating_process)的假设进行编码;本质上,因果图编码了先验知识。对于因果图的简要介绍,我建议这篇[中的文章](/use-causal-graphs-4e3af630cf64),对于因果图的彻底讨论,我建议由图灵奖获得者、朱迪亚·珀尔和科学作家达纳·麦肯齐所著的“[为什么](https://www.goodreads.com/book/show/36204378-the-book-of-why)的书”。
作为一名计算机科学家,Pearl 因其在人工智能和开发贝叶斯网络方面的工作而闻名,然而,对我的研究最有用的是他在因果关系方面的工作。事实上,DoWhy 中的“do”来源于“do-calculus”,是 Pearl 发明的一种讨论因果关系的形式语言;Robert R. Tucci 的这篇教学论文提供了这种语言的概述,包括证明和规则。do-calculus 的细节超出了本文的范围,但是突出的一点是,通过使用 do-calculus 来构建因果图,DoWhy 采用了一个 [Pearlian 框架](https://medium.com/@akelleh/causal-graph-inference-b3e3afd47110)。具体来说,DoWhy 依赖基于图形的标准和 do-calculus 来建模假设和识别非参数因果关系。
明确识别假设,只是使用 DoWhy 因果框架的一个优点,重要的是,DoWhy 将第二阶段的因果效应识别与第三阶段的效应估计分开。引用微软研究院的博客文章[介绍这个库,](https://www.microsoft.com/en-us/research/blog/dowhy-a-library-for-causal-inference/)
> “因果效应的识别涉及对数据生成过程做出假设,并从反事实表达到指定目标需求,而估计是从数据估计目标需求的纯统计问题。”
如下图所示,使用 DoWhy 框架,识别阶段与评估阶段是分开的。

用道伊库分离因果分析的识别和估计阶段。[来源。](https://www.microsoft.com/en-us/research/blog/dowhy-a-library-for-causal-inference/)
评估阶段的分离允许实施基于潜在结果框架的评估方法,该框架依赖于[反事实条件](https://en.wikipedia.org/wiki/Counterfactual_conditional)。在 [arxiv 的一篇介绍 Do-Why](https://arxiv.org/abs/2011.04216) (2020)的论文中,研究人员将他们在估计方法中对潜在结果的使用归功于 [Guido W. Imbens 和 Donald B. Rubin](https://www.cambridge.org/core/books/causal-inference-for-statistics-social-and-biomedical-sciences/71126BE90C58F1A431FE9B2DD07938AB) (2015)。与亚伯拉罕选择的策略相似,由于案例研究论文使用了工具变量,因此在本教程中,估计方法将是[两阶段最小二乘法(2SLS)](https://www.statisticssolutions.com/two-stage-least-squares-2sls-regression-analysis/) 回归。最后一个阶段是反驳第三阶段得出的估计。在《艰难旅行》中,亚伯拉罕斯进行了几项鲁棒性检查,这些检查在技术附录中有详细说明,然而,为了简单起见,在本文中我们使用了 DoWhy 的自动化鲁棒性检查。
## **“艰难行进”实验设计**
作者将这种影响评价框定为政治学理论与经济学文献之间的分野;后者认为城市劳动力市场的失业是技术缺陷(如缺乏基础设施)的结果,而前者声称政治改革是解决失业问题的先决条件。关于这一主题,很少有政治学论文采用因果关系,也没有研究基础设施干预的因果影响,因此,“艰难旅行”填补了现有政治学文献中使用计量经济学的空白。
从表面上看,本文件的目的是评估以色列军队在西岸内部道路网沿线设置的检查站和路障对巴勒斯坦失业率的因果影响。这些障碍物是出于安全原因设置的,但实际上干扰了巴勒斯坦人的通勤出行。换句话说,提交人认为,以色列的障碍阻止近郊区的巴勒斯坦通勤者到达商业中心和过境点,造成通勤者失业。然而,这些损失被通勤者中位于更中心位置的巴勒斯坦竞争者的就业增长所充分抵消。该文件强烈主张,边际经济干预将有助于改变失业的空间分布,但不会降低总体失业水平。
这项研究之所以成为可能,是因为“时空分类数据和貌似外部的连通性冲击相结合”,也就是说,在第二次起义(2000 年至 2004 年)期间,以色列检查站的数量急剧增加——这是一种外部冲击。此外,作者通过两次前往巴勒斯坦(由于现场准入要求),收集了 1997 年和 2007 年邻里一级的人口普查数据,这些数据得到了卫星图像的补充,为数据集增加了另一个空间组成部分,从而获得了时空分类数据。起义后竖立的障碍仍然存在,如下图所示,从 2007 年底。卫星图像显示了以色列在巴勒斯坦西岸的道路障碍,红色实线代表了[以色列隔离墙](https://en.wikipedia.org/wiki/Israeli_West_Bank_barrier)——一堵在第二次起义(2000 年)开始时修建的 708 公里长的墙,二十多年后的今天仍然存在。

2007 年 12 月,以色列在西岸设置路障。资料来源:亚伯拉罕,2021 年
根据来之不易的人口普查数据,本研究中的因变量是 1997 年至 2007 年间 480 个巴勒斯坦居民区就业结果的变化;以下简称: *%就业变化*。根据世界银行/ [巴勒斯坦中央统计局的贫困分类,这 480 个居民区可以分为 310 个超级居民区。](http://www.pcbs.gov.ps/site/lang__en/748/default.aspx)
工具化的 2sl 第一差异策略用于测试障碍对失业的因果影响,其中工具是以色列定居点与巴勒斯坦通勤路线的纵向接近度。这一工具满足了定义所要求的三个条件,允许对巴勒斯坦居民区的封锁进行 qausi 随机化。纵向上靠近以色列定居点应影响到以色列检查站的位置,因为这些检查站据称的目的是保卫定居点。由于与以色列检查站的设置有关,这种定居点的接近将对 *%的就业变化*产生间接影响。通过用稳健性检查反驳因果估计,对 *%就业变化*的纵向接近度的混杂性进行测试。
这种方法是有效的,因为这些工具隔离了部署在定居点附近的障碍子集,而不是直接回归检查站总体存在的 *%就业变化*。在这项研究中,Abrahams 使用了 2SLS 回归,因为它表明该工具与第一阶段回归的误差项不相关,但在第二阶段与因变量相关,一旦我们考虑了自变量。
自变量标有*阻碍*和*保护*,其中前者涉及障碍物的直接阻碍效应,后者代表障碍物的间接保护效应。因此,工具变量被称为 *iv_obstruction* 和 *iv_protection,*表示与以色列定居点的纵向距离。本研究中使用的因变量、自变量和工具变量的直方图如下图所示,取自论文的技术附录,可在此处访问[。](https://www.cambridge.org/core/journals/political-science-research-and-methods/article/abs/hard-traveling-unemployment-and-road-infrastructure-in-the-shadow-of-political-conflict/135F8A50F613DA3C9C4CB9335F0BFCF7#supplementary-materials)

因变量、自变量和工具变量的直方图。[资料来源:亚伯拉罕,2021 年](https://www.cambridge.org/core/journals/political-science-research-and-methods/article/abs/hard-traveling-unemployment-and-road-infrastructure-in-the-shadow-of-political-conflict/135F8A50F613DA3C9C4CB9335F0BFCF7)
很明显,所有变量都有健康的变化,这意味着回归结果不太可能由异常值驱动。实际上,回归显示了抵消效应,其中阻碍效应被保护效应平衡,使得衰减的净效应将接近于零。从下面的论文节选中可以看出,作者测试了三个假设的三种效应,即阻碍效应、保护效应和衰减净效应。

《艰难旅行》中的三个假设。来源:亚伯拉罕,2021 年
关于城市劳动力市场的经济文献将失业视为基础设施差的副产品,为注重改善城市基础设施的经济政策建议提供了支持。因此,道路障碍等交通基础设施冲击会影响从住宅区到商业中心的劳动力供应。住宅或起始邻域用 *j、*表示,而 *k* 表示目的邻域 ie。有就业机会的商业中心。
如上所述,独立变量*障碍物*和*保护*,分别描述了障碍物的直接和间接影响。亚伯拉罕斯生成了两个处理变量,即 *∆obstructionⱼ* 和 *∆protectionⱼ* ,其中第一个变量量化了来自街区 *j* 的巴勒斯坦劳工在进入街区*k*工作时受到阻碍的程度,第二个变量量化了由于障碍减少了来自街区 *j* 的劳工流动竞争而对街区 *k* 产生的反向保护效应。

西岸的空间直方图,独立变量的百分位数分布,左图为障碍,右图为保护。[资料来源:亚伯拉罕,2021](https://www.cambridge.org/core/journals/political-science-research-and-methods/article/abs/hard-traveling-unemployment-and-road-infrastructure-in-the-shadow-of-political-conflict/135F8A50F613DA3C9C4CB9335F0BFCF7)
*∆protectionⱼ* 变量在下面的等式中定义,其中 *dₖⱼ* 代表( *k,j* )之间的道路距离, *num_obstaclesₖⱼ* 代表路径 *dₖⱼ* 上的障碍物数量。居民区经济中劳动力在起义前的份额由 nₖ代表,有 480 个居民区作为观察单位。

*∆protectionⱼ* 为行驶路径上障碍物的平均数量(k,j),由 *nₖ* 加权,由 *dₖⱼ.反加权*图片由作者改编自亚伯拉罕,2021。
通往以色列边境的道路上设置的障碍物产生了额外的阻碍效应,特别是自起义前以来,21.6%的巴勒斯坦劳工每天都要前往以色列工作。为了计算 *∆obstructionⱼ,*亚伯拉罕斯首先计算一个 *∆obstruction_naiveⱼ* ,它是( *j,k* )之间路径上障碍物的平均数量,通过路径 *dⱼₖ* 进行反加权,并通过变量 *mₖ* 进行加权;其中 *mₖ* 代表邻国 *k* 在经济业务中的份额。目的地 *k* 越重要,阻碍效应越有害,因此 *mₖ* 根据商业活动确定的邻居 *k* 的相对重要性进行调整。
如下图所示,前往以色列的劳工被用来称量住宅区和以色列边境之间的障碍物数量。其中 as, *∆obstruction_naiveⱼ* 由剩余劳动力加权。

根据 1997 年人口普查的记录,计算∆obstructionⱼ的方程式也说明了在以色列就业的巴勒斯坦劳动力的百分比。图片由亚伯拉罕的作者改编,2021。
值得注意的是,为了计算 mₖ,亚伯拉罕斯利用辐射校准的夜间卫星图像来估计邻近地区的商业活动。然而,夜间卫星图像是模糊的,因此附近以色列定居点的商业活动人为夸大了巴勒斯坦居民区的计算亮度。机智的亚伯拉罕斯[发明了一种消除夜间卫星图像](https://www.sciencedirect.com/science/article/abs/pii/S0034425718301111)模糊的方法,以便更准确地计算巴勒斯坦居民区的商业活动。
接下来,工具 *∆iv_obstructionⱼ和∆iv_protectionⱼ* 遵循待遇变量的格式,其中首先计算 *∆iv_obstruction_naiveⱼ* 和 *∆iv_obstructionⱼ* 按在以工作的巴勒斯坦劳动力百分比加权。然而,使用这些仪器,效果不是通过障碍物的数量来计算的,而是通过以色列定居点与通勤道路的纵向接近度来计算的。通过假设定居点周围存在缓冲区,可通过通勤道路上位于定居点缓冲区内的路段总长度来计算接近度,从而表示道路障碍物的潜在位置。在下图中,仪器由 *mₖ* 和*nₖ*称重,与它们的治疗对应物相似。

根据 1997 年人口普查的记录,计算*工具和*的方程式也说明了在以色列就业的巴勒斯坦劳动力的百分比。图片由亚伯拉罕的作者改编,2021。
在接下来的部分中,我们将构建一个因果图来模拟这个问题,我将介绍控件。虚拟变量。
## **第一阶段:因果模型**
作为第一步,本教程的数据集[可以在这里](https://dataverse.harvard.edu/dataset.xhtml?persistentId=doi:10.7910/DVN/XACHFI)访问。我建议在使用 DoWhy 时使用 Google Colab 笔记本,因为在本地环境中安装 pygraphviz 这个图形可视化库可能会很棘手。在 Colab 中运行的以下两个命令将正确安装 pygraphviz 和 DoWhy。
!apt install libgraphviz-dev
!pip install pygraphviz dowhy
如果使用 Colab,使用下面两行来加载“Hard traveling”数据集,该数据集存储为。dta”,表示 Stata 文件。
from google.colab import files
files.upload()
在下面的代码片段中,我们导入所需的库,并使用 pandas 将 Stata 文件读入数据帧。加载的数据帧中的列根据卫星图像标记的数据列进行重命名,以匹配上一节中描述的变量。接下来,有必要将独立变量和工具变量标准化,以便于比较效果。
接下来,我们添加所有必要的虚拟变量来复制论文的主要结果。480 个居民区省级就业趋势的省级虚拟控制。其余的虚拟变量代表邻近水平的协变量,如土堆和局部检查站,它们被排除在回归之外,以减少衰减偏差,因为这些“障碍”不会干扰过往交通。*设定假人*与第二阶段讨论的仪器排除条件相关。所有的虚拟变量被收集到一个列表中, *all_dummies* 。
DoWhy 使得构建因果图变得非常简单。数据帧传递给导入的*因果模型*,设置治疗变量*∏阻塞*和*∏保护*,结果(因变量)定义为就业变化,设置工具变量*∏iv _ 阻塞*和*∏iv _ 保护*。“共同原因”论点是我们通过*所有虚拟变量的地方,*这使我们能够控制省一级的趋势和社区一级的协变量。
道伊制作了下面的因果图来显示自变量、因变量和工具变量之间的关系。在下图中,我排除了省级趋势和社区级协变量,因为它们会使图表变得杂乱。我简单地运行了 CausalModel 而没有使用 common causes 参数来生成图表,但是在本教程的剩余部分中, *all_dummies* 都包含在因果模型中。

显示工具、自变量和因变量之间关系的因果图:1997 年至 2007 年间的就业变化。
## **第二阶段:确定需求**
DoWhy 使得从因果图中识别需求变得容易。以下命令描述了我们创建的模型:
model.interpret()
输出应为“寻找治疗['阻碍','保护']对结果['改变 _ 就业']]的因果效应的模型”。为了从模型中识别估计需求,我们运行下面两行:
identified_estimand = model.identify_effect(proceed_when_unidentifiable=True)
print(identified_estimand)
如下图所示,“identify_effect”的输出将包括我们在建立模型时指定的工具变量。

输出中包括仪器的估计和假设,这里说明了仪器的排除条件。亚伯拉罕斯对此的定义是,“以色列定居点靠近巴勒斯坦人的旅行路线,虽然显然预示着随后的封锁,但不应该通过任何其他因果渠道影响巴勒斯坦居民区就业率的变化”。在第四阶段,我们使用稳健性检查来表明 *settle_dummies* 是一个替代渠道,它与工具正交运行,因此对回归结果没有影响,settle_dummies 表示一个定居点靠近巴勒斯坦居民区(可能引发暴力)。小心地包含 *settle_dummies* 意味着我们正在考虑排除条件,这允许我们继续进行因果分析,而不违反第二阶段中确定的估计和假设。
## 第三阶段:评估效果
下一步是使用确定的估计量来估计因果效应,如前所述,亚伯拉罕使用 2SLS 作为估计方法。在 2sl 的第一阶段,如下图所示,处理变量的仪器替代是根据它们在仪器上的回归而创建的,同时考虑了省一级的趋势和社区一级的协变量。

*第一阶段 2SLS,*仪器( *∆iv_obstructionⱼ和∆iv_protectionⱼ)* 用于回归 *∆obstructionⱼ和∆protectionⱼ.*图片由作者改编自亚伯拉罕,2021。
在第二阶段,如下图所示,第一阶段估计的中间变量(∆obstructionⱼ 和∆protectionⱼ*用于回归就业变化,以及省一级的趋势和街区一级的协变量。*

*第二阶段 2sl,*第一阶段估计的变量用于回归 1997 年至 2007 年的就业变化。图片由亚伯拉罕的作者改编,2021。
DoWhy 有一个内置的工具变量 2SLS 方法,我们可以用它来快速回归两个阶段中治疗变量的就业变化。识别出的估计需求用名为“iv.instrumental_variable”的 DoWhy 方法进行估计,该方法建立在 [statsmodels 的 IV2SLS](https://www.statsmodels.org/stable/generated/statsmodels.sandbox.regression.gmm.IV2SLS.html) 之上。
上述代码片段的输出如下所示,请注意,标准误差不是[异方差稳健的](https://en.wikipedia.org/wiki/Heteroscedasticity-consistent_standard_errors),因为底层 statsmodels 的函数不允许为协方差估计器指定标准误差的类型。

用于回归处理变量就业变化的仪器化 2SLS 结果。图片作者。
如图所示,存在与*阻塞* (-2.38)相关的负因果效应和与*保护* (+3.79)相关的正因果效应。t 检验结果足够大,表明样本数据和零假设之间存在显著差异。下面,来自“艰难旅行”的主要回归结果在空间上显示在西岸的地图上;暖色代表消极的阻挡效果,冷色代表积极的保护效果。

显示主要回归结果的空间直方图显示,阻碍效应在农村地区较高,保护效应在商业中心较高。来源:亚伯拉罕,2021 年
DoWhy 的结果是相似的,但它们与 Abrahams 的主要结果不匹配,因为回归没有被 2007 年人口普查的劳动力加权。在建立观察实验时,考虑了 1997 年人口普查的劳动力,然而,为了复制“艰苦旅行”的公布结果,需要用数据框架中标记为“lf_1_2007”的变量对 2sl 回归进行加权。不幸的是,由于库对 statsmodels 的依赖,DoWhy 不允许加权 2SLS 回归。此外,480 个邻近地区可以被聚类成 310 个超邻近地区,并且 statsmodels 的实现缺乏针对聚类进行调整的灵活性。因此,我有三个理由使用 Python 库 [linearmodels](https://bashtage.github.io/linearmodels/iv/iv/linearmodels.iv.model.IV2SLS.html) :测试对异方差的稳健性,添加 2007 年劳动力加权因子,以及通过 310 个超邻域进行聚类。
通过[线性模型](https://bashtage.github.io/linearmodels/iv/iv/linearmodels.iv.model.IV2SLS.html),我首先实现了加权 OLS,其次是加权 2SLS 回归,第三是聚类(和加权)2SLS 回归。下面比较了回归的结果,我已经在 Github 上的 Jupyter 笔记本中分享了回归的完整代码。

用线性模型比较 OLS、2SLS 和聚类 2SLS 回归的结果。图片作者。
对于 OLS 和仪器化 2SLS 回归,默认协方差估计是“稳健的”,产生对异方差稳健的结果,反映了“硬旅行”的主要结果。对于聚类 2SLS 回归,480 个邻域被分组为 310 个聚类,并且协方差估计器被“聚类”。从下表“艰难行驶”中可以看出,带控制装置的加权 2SLS 的主要结果是:障碍物*为-3.75,保护*为+3.50,这与上面我的线性模型结果表相匹配。**

“艰难旅行”的主要结果表。来源:亚伯拉罕,2021 年
## **第四阶段:反驳估计**
最后一个阶段是用稳健性检查来反驳获得的估计值,在这个阶段测试模型对混杂因素的稳健性。在“艰苦旅行”中,有广泛的稳健性检查,例如,死亡数据被添加到回归中,以测试暴力引发的测量误差。在加载本教程的数据时,我意识到健壮性检查的广泛性;数据框有 734 列,用于 480 次观察!我将需要一个额外的帖子来涵盖这些检查的细节,所以我将重点放在由 DoWhy 提供的可用于反驳估计的一般类型的健壮性检查上。
我测试了三种类型的 DoWhy 反驳者,一种是“bootstrap _ refuter”,一种是“data _ subset _ refuter”,一种是“random_common_cause”反驳者。“bootstrap _ rejector”通过在混杂因素中包含测量误差的数据的随机样本上运行来反驳估计值。“数据子集反驳者”通过对原始数据的随机子集重新运行来反驳一个估计。“随机 _ 共同 _ 原因”通过引入一个随机产生的混杂因素来反驳一个估计,这个混杂因素可能没有被观察到。下面的代码片段改编自一本 DoWhy 示例笔记本,它详细描述了如何[迭代多个反驳者](https://microsoft.github.io/dowhy/example_notebooks/dowhy_refuter_notebook.html)。
下图显示了对第三阶段获得的 DoWhy 因果估计运行多重反驳的结果。

基于 DoWhy 的健壮性检查的反驳结果。图片作者。
## **最终想法**
这篇 Python 因果分析教程旨在展示计量经济学的有用性,并鼓励其他数据科学家将因果关系纳入他们的实证工作。使用“艰难的旅行”作为案例研究论文是一个非常吸引人的学习经历,它增加了发展应用计量经济学所需的必要背景。数据科学和计量经济学在因果关系方面的交叉是我打算进一步探索的一个领域,我的希望是将我的学习之旅转化为可访问的教程。
作为人类,我们自然会以因果关系的方式思考,将这种类型的思维扩展到机器学习感觉像是一种自然的扩展。杰出的人工智能研究人员,如 Yoshua Bengio,主张将因果关系扩展到机器学习;有趣的是,Bengio 最近提出了[因果学习作为解决模型泛化问题](https://medium.com/syncedreview/yoshua-bengio-team-proposes-causal-learning-to-solve-the-ml-model-generalization-problem-762c31b51e04)的方法。撇开数据科学不谈,我相信,随着机器学习在回答因果问题方面的进步,它对经济学越有用。我将在未来的一篇文章中探索这个相反的角度,我将使用 [Keras](https://keras.io/) 和 [EconML](https://www.microsoft.com/en-us/research/project/econml/) 估计条件平均治疗效果(CATE),用机器学习解决一个经济学问题。最后,非常感谢[阿列克谢·亚伯拉罕斯](https://twitter.com/abulkhaezuran?ref_src=twsrc%5Egoogle%7Ctwcamp%5Eserp%7Ctwgr%5Eauthor)回答问题并提供周到的反馈。
如有疑问或评论,在 [Linkedin](https://www.linkedin.com/in/haaya-naushan-a4b5b61a5/) 上联系我,我很有兴趣听听别人是怎么用计量经济学进行数据科学的!
# 实用 Python:如何在几分钟内编写和部署监控和通知服务。免费的。
> 原文:<https://towardsdatascience.com/practical-python-how-to-write-and-deploy-a-monitoring-notification-service-within-minutes-for-free-b682cffa66ef?source=collection_archive---------4----------------------->
## 你曾经希望有一种服务,一旦它观察到一个特定的事件,就反复地检查和 pings 你吗?—了解如何构建和部署它。

小熊猫观察者— [来源](https://pixabay.com/photos/nature-mammal-animal-wildlife-cute-3162233/)
你有没有发现自己反复点击 f5 来查看某个网站上是否有某个商品或门票?你有没有几乎疯狂地检查 API 来查看某个速率是否超过了某个阈值?如果是的话,这篇文章可能适合你。
本文将为您提供构建和部署全自动超轻通知服务的工具。本文旨在作为代码指南。我们将涵盖选定的相关监控用例(网站、加密、API),回顾典型的通知渠道(电子邮件、SMS 和 Slack),并解释如何使用[无服务器框架部署服务。](https://www.serverless.com/)你可以在这里找到最终回购[。相信我,这比你想象的要容易得多。](https://github.com/FBosler/notification-service)
## 这篇文章的结构如下:
1. 设置
2. 监视
3. 通知
4. 部署
## 先决条件:
一些 Python 的经验是有帮助的,但是大多数组件都非常简单。对于我们手头的例子,我们将使用 AWS 的无服务器框架,所以理想情况下,您对 AWS 有一些经验。理想情况下,您也有一个 AWS 帐户,如果没有,您可以创建一个自由级帐户。
或者,我们可以快速重新配置服务,使用 GCP、Azure 或其他受支持的提供商之一。我们将把我们的服务部署为 lambda 函数。运行 lambda 函数的成本在免费层内(除非您每分钟运行多次检查)。
# ①设置
由于`serverless`将在引擎盖下对我们的项目进行分类,我们将需要一个不仅仅是 Jupyter 笔记本的项目结构。我选择的编辑器是 [Pycharm](https://www.jetbrains.com/pycharm/) ,但是可以随意使用。
我们的项目结构将如下所示:
monitoring
|-api.py
|-crypto.py
|-website.py
notifier
|-sms.py
|-email.py
|-slack.py
|-丨t丨e丨l丨e丨g丨r丨a丨m丨s丨.py
handler.py
.env (includes all relevant variables) serverless.yml (serverless config file) requirements.txt (specifies dependencies) package.json (configures our node dependencies - think of it like a more powerful requirements.txt but for node) .gitignore
在我们开始之前,请继续安装`[node, npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm)`和`[serverless](https://www.serverless.com/framework/docs/getting-started)`(您不需要设置仪表板帐户)。虽然不是绝对必要,但我强烈建议使用某种形式的 python 环境管理。我使用 [conda](https://docs.conda.io/en/latest/) 环境,但是 [pipenv](https://pypi.org/project/pipenv/) 、 [pipex](https://github.com/pypa/pipx) 或 [pyenv](https://github.com/pyenv/pyenv) 也是不错的选择。这种选择主要取决于个人的熟悉程度和品味。一旦你这样做了,改变你的`package.json`如下(或者创建它,如果你没有它)。
{
"devDependencies": {
"serverless-python-requirements": "5.2.2"
}
}
然后,这是最后的设置步骤,运行`npm install`(这将安装`serverless-python-requirements`——一个插件无服务器需要捆绑我们的 python 依赖)。
# ②监测
好吧,让我们开始吃肉,好吗?
## 从 API 中提取数据
让我们从监测外汇汇率开始,以了解什么时候将是打包所有东西并自发飞往泰国的最佳时间。我们将查询 ECB(欧洲中央银行)提供的一个官方免费端点。不幸的是,我们得到的数据是 XML,所以我们必须解析它才能使用它。
此外,我们需要`requests`来发送这些请求,所以运行`pip install requests`。
代码可用[此处](https://github.com/FBosler/notification-service/blob/main/monitoring/api.py)
如果最近的汇率高于 35 泰铢,我们将打印一条消息通知我们。
## 监控网页
假设您想要监视网页,以确定某个产品何时再次可用。为了处理 HTML,我们需要一个解析器。幸运的是 Python 提供了`[beautifulsoup](https://www.crummy.com/software/BeautifulSoup/bs4/doc/#quick-start)`,一个 HTML 解析器。所以让我们继续运行`pip install beautifulsoup4`。如果你想了解更多关于 beautifulsoup 的信息,可以看看下面这篇文章:
</image-scraping-with-python-a96feda8af2d>
好的,现在让我们假设我们想买[这个漂亮的刷子](https://www.everdrop.de/products/handburste-aus-fsc-zertifiziertem-buchenholz-und-pflanzlichen-borsten)。

[everdrop 笔刷](https://www.everdrop.de/products/handburste-aus-fsc-zertifiziertem-buchenholz-und-pflanzlichen-borsten),可惜卖完了(写的时候)
但是,我们不想订阅时事通讯。因此,让我们抓取带有漂亮汤的 HTML 并搜索`add-to-cart`按钮。如果我们能找到`add-to-cart`按钮,这意味着产品可用。我们可以很容易地这样做:
代码可用[此处](https://github.com/FBosler/notification-service/blob/main/monitoring/webpage.py)
## 做加密的东西
所以最近我意识到了一个叫做[仙境](https://www.wonderland.money/)的疯狂的 crypto DAO(分散自治组织)。老实说,仙境是一个相当疯狂的金融结构,很可能会爆炸,但我发现它很有趣。它的工作方式是,一旦一个人下注他们的`Time`代币,他们就会得到另一个代币(wMemos),可以用作 [Abracadabra](https://abracadabra.money/) 的抵押品来借用`MIM`代币,然后可以再次变成`Time`代币来建立高杠杆头寸。我的主要问题是,通常贷款池会没有代币,这些代币只能定期补充。所以我想让支票自动化。我只构建了监视服务,但是也完全有可能在特定条件下与区块链交互(例如 eth-brownie)。
此处代码[为](https://github.com/FBosler/notification-service/blob/main/monitoring/crypto.py)
太好了,我们成功地编写了几个不同的监控函数。如果满足条件,这些函数返回一个字符串,否则不返回任何内容。此外,好的一面是每个文件都可以单独测试。
# ③通知
我们现在需要通知自己(或他人)我们之前监控的事件。让我们从最简单的开始。电子邮件。
## 电子邮件
在这个例子中,为了简单起见,我假设您使用的是 Gmail。此外,由于我们现在使用敏感的登录凭证,我将开始把它们放入`.env`文件,并通过`os.getenv`检索它们。如果你想复制这个,你需要把你自己的密码、电子邮件和 SMTP 服务器放到你的`.env`文件中。请注意,如果你使用的是 Gmail,你很可能需要创建一个特定于应用程序的密码(这里的[说明](https://support.google.com/accounts/answer/185833?p=InvalidSecondFactor))。你甚至可以运行你自己的 SMTP 服务器,但是我认为这会使 lambda 函数失去平衡。
此处代码[为](https://github.com/FBosler/notification-service/blob/main/notifications/send_email.py)
## 短信
接下来,短信。随着 WhatsApp 的出现,SMS(短消息服务)有点过时了,但仍然非常好用。例如,设置通过 [Twillio](https://www.twilio.com/docs/sms/quickstart/python) 发送短信非常简单。需要注意的是,每条短信都是要花钱发的。然而,Twilio 确实提供了一个免费的试用帐户,你可以先使用。理论上,你可以使用 Gmail 自动转发功能(或其他垃圾邮件服务)创建无限的试用账户。假设你的电子邮件地址是 example@gmail.com,那么 Gmail 会自动将所有发往 example+whatever@gmail.com 的邮件转发到你原来的账户。

Twilio Dashboard —注册您的收件人号码也很重要(对于试用版)
发送功能就变得愚蠢简单了。但是,不要忘记首先运行`pip install twilio`。
此处代码[为](https://github.com/FBosler/notification-service/blob/main/notifications/send_sms.py)
## 松弛的
最后,懈怠。现在几乎每个公司都在使用 Slack,所以自动向 Slack 通道发送消息是有意义的。
首先,你需要成为你的空闲工作区的管理员,然后去 slack.com/apps。点击“创建应用程序”,选择“从头开始”,命名服务并选择正确的工作空间。一旦你创建了应用程序,点击“传入网页挂钩”,切换激活开关,并点击“添加新的网页挂钩到工作区”。

现在选择一个频道
酷,我们完成了。我们现在有了自己的 webhook URL,我们也把这个 URL 放在我们的`.env`文件中,因为每个有这个 URL 的人都可以用它来发布消息。发送消息非常简单,如下所示:
太神奇了!我们现在已经成功地编写了一些监控函数和一些通知函数。让我们部署这个!
# ④部署
对于部署,我们将使用无服务器框架。假设您遵循了本指南,并做了步骤①中描述的所有事情,那么这应该意味着您可以开始了。但是,您需要设置您的本地 AWS 凭证。您可以通过运行`cat ~/.aws/credentials`来验证您的凭证是否存在,您应该会看到类似这样的内容:
[default]
aws_access_key_id=YOUR_KEY
aws_secret_access_key=YOUR_KEY
...
如果您没有看到这一点,请继续安装 [AWS-CLI](https://aws.amazon.com/cli/) 和[配置您的凭证](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-quickstart.html)。如果您还没有凭据,请登录 AWS 控制台(web)并选择 IAM。现在有两个选择,一个比另一个稍微复杂一点。
## 选项 1 —对根的权限:
出于测试的目的,我推荐使用这种方法,因为它要快得多。登录 AWS 控制台`->`搜索 IAM `->`选择用户`->`点击你的 root 用户`->`“安全凭证”`->`“创建访问密钥”就大功告成了。
## 选项 2 —新用户:
继续创建一个新用户,我们称该用户为`notification-service-deployer`并勾选`Access key — Programmatic access`框。

现在是权限的时候了。出于开发目的,您可能倾向于授予该帐户 admin 权限,但我建议不要这样做,而是授予它以下权限:
* [AWSLambda_FullAccess](https://console.aws.amazon.com/iam/home#/policies/arn%3Aaws%3Aiam%3A%3Aaws%3Apolicy%2FAWSLambda_FullAccess) (原因很明显)
* [awscloudformationfullacess](https://console.aws.amazon.com/iam/home#/policies/arn%3Aaws%3Aiam%3A%3Aaws%3Apolicy%2FAWSCloudFormationFullAccess)(用于部署)
* [亚马逊 3FullAccess](https://console.aws.amazon.com/iam/home#/policies/arn:aws:iam::aws:policy/AmazonS3FullAccess) (如果你需要一些持久存储的话)
## 最终剧本
让我们快速拼凑出我们的最终脚本:
这里的代码—取消您想要使用的函数的注释
您可以随意取消注释您想要使用的任何内容。你必须根据你的需要修改剧本。
## 无服务器配置
现在,缺少的最后一件事是,我们需要像这样配置我们的`serverless.yml`(注释解释了每个部分的作用):
瞧啊。我们完成了,现在我们准备好部署我们的通知服务,只需运行:`sls deploy`(它将构建一个 zip 文件并将其上传到 S3,从那里 zip 文件可以用来创建 lambda 函数)。
太神奇了!您部署了自己的通知服务。相信我,当我告诉你这又打开了一个新的麻烦。你很快就可以开始编写自己的 API 和 bot 了。
哦,如果你喜欢阅读这样的故事,并想支持我成为一名作家,考虑注册成为一名灵媒成员。每月 5 美元,你可以无限制地阅读媒体上的故事。如果你用我的链接注册,我甚至会做一些🍩。
<https://medium.com/@fabianbosler/membership>
# PyTorch 实用迁移学习
> 原文:<https://towardsdatascience.com/practical-transfer-learning-with-pytorch-8344e5c82f59?source=collection_archive---------19----------------------->
## 当解决一个 ML 问题时,利用一个已经起作用的深度神经网络是一个巨大的加速

奥马尔·弗洛雷斯在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍摄的照片
在之前的一篇文章中,我解释了 PyTorch 和 XGBoost 如何结合起来执行迁移学习。
</transfer-learning-with-xgboost-and-pytorch-hack-alexnet-for-mnist-dataset-51c823ed11cd>
这是一种非常非常规的转移学习的方式,混合了深度神经网络和梯度引导树。为了说明这种方法,我解释了如何将 AlexNet 从 ILSVRC 数据集中获取的图像分类知识转移到 XGBoost 的另一个用例:识别手写数字。
在写完这篇文章的几天后,我意识到 PyTorch 并没有真正在网上涵盖更常见的用例,即只涉及神经网络的迁移学习。然而, [Dipanjan (DJ) Sarkar](https://medium.com/u/6278d12b0682?source=post_page-----8344e5c82f59--------------------------------) 的一篇优秀文章详细介绍了深度学习,有一些代码但使用了 Keras。
</a-comprehensive-hands-on-guide-to-transfer-learning-with-real-world-applications-in-deep-learning-212bf3b2f27a>
我强烈推荐阅读这篇文章。
在这篇文章中,我们将填补迁移学习和 pytorch 之间的空白。
我们将做与前一篇文章相同的转换,仅使用神经网络方法,并比较混合 XGBoost 或仅使用神经网络时的性能。
# 动机
在深入研究代码之前,有必要提醒一下迁移学习为什么有趣。主要原因是时间:需要大量的时间和精力来收集和标记足够大的深度学习数据库,并且大量的时间和精力对于配置、拟合和调整深度神经网络也是必要的。
当解决一个 ML 问题时,利用一个已经起作用的深度神经网络是一个巨大的加速。
在为计算机视觉设计的深度神经网络的情况下,大多数复杂性包含在构建特征的层中。负责分类的层通常只有 3 层深。
能够利用这些预先训练好的特性计算层是非常有吸引力的。
下一节将展示如何使用传统的深度学习方法来实现这一点。
# MNIST 用例
为了简单起见,我们希望使用 Python 中容易获得的数据库。MNIST 看起来是个不错的候选人。它是开源的,使用 TorchVision 可以在几秒钟内加载。训练集和测试集都非常大:60k 的图像用于训练集,10k 的图像用于测试集。此外,它们与用于训练 AlexNet 的图像没有相同的维度。看看我们如何处理这件事是很有趣的。
AlexNet 最初被训练成用 1 000 个标签对 ILSVRC 的图像进行分类。在这次训练中,它学会了用深度卷积神经网络建立视觉特征。我们在这一部分中所做的假设是,这些特征可以被重新使用来执行完全不同的分类,即识别仅具有 10 个标签的手写数字。
# 重新定位 Alex Net
在这一点上,我们需要一种方法来保持特征层的结构和权重,并重新训练分类层。
这是通过下面的代码实现的:
作者代码。
它创建一个新的神经网络模型,获得一个原始模型作为输入,并生成一个新的模型,其中分类器网络已被一个新的分类器取代。要素图层保持不变。
重要的部分在于冻结要素图层权重的线。这将强制计算要素的图层在训练过程中保持不变。
这些是负责迁移学习的行,因为在初始训练期间在 ILSVRC 数据集上获得的所有知识将在新的用例中不加修改地重用。
# 学习识别手写数字
我们现在要做的就是建立一个新的模型,基于原始的,预训练的 AlexNet,并改装一个新的分类器来识别手写数字。
感谢 pytorchvision 和 pytorch,这是一个非常简单的任务。下面的代码解释了如何:
转移学习。作者代码
这种方法很简单,除了变换部分,它需要调整 MNIST 图像的大小,以适应用于训练 AlexNet 的数据集的大小。这是通过转换方法完成的。
标签的数量从 1k 减少到 10 个。
在我的笔记本电脑上花了相当长时间的培训结束时,模型被序列化并存储。我们将使用此转储来评估此传输的性能,并检查此方法是否相关。
# 估价
scikit learns 提供了两种评估分类模型性能的简便方法: ***混淆 _ 矩阵*** 和 ***分类 _ 报告*** 。
第一个基于实际值计算每个类使用的标签数。第二个计算标准特征,如 f1 分数、召回率、精确度等
我们将把这两种方法应用到 torchvision 提供的测试数据集上。这个数据集包含 10k 个手写数字,这对于验证来说是一个合适的大小。
下面的代码行类似于培训中使用的代码行。*变换*被重新用于调整测试图像的大小。
评估模型性能。作者代码。
正如您在上面的注释行中看到的程序输出,性能非常好。
有趣的是,我发现这个性能比我在[上一篇文章](/transfer-learning-with-xgboost-and-pytorch-hack-alexnet-for-mnist-dataset-51c823ed11cd)中使用 XGBoost 获得的性能稍好。这是以更长时间的训练为代价的。然而,使用适当的超参数调优可以进一步提高 XGBoost 性能:在上一篇文章中,使用了分类的默认值。
# 结论
使用预训练模型转移学习是获得准确模型的非常有效和快速的方式,对于分类来说也是如此,而对于回归来说则不是。
使用 pytorch 和 pytochvision,这可以在非常有限的几行中完成。
在这篇文章中,我们一直在使用在图像上获得的知识来应用到其他图像上。我敢肯定,我们也可以在任何其他类型的信号上获得不错的结果,比如 1D 信号。
# 实践探索量子计算的公理
> 原文:<https://towardsdatascience.com/practically-exploring-the-axioms-of-quantum-computing-4413333ec0d8?source=collection_archive---------33----------------------->
## 走向量子计算的掌握
量子机器学习要不要入门?看看 [**动手量子机器学习用 Python**](https://www.pyqml.com/page?ref=medium_axioms&dest=/) **。**
在你掌握量子计算的过程中,你会遇到很多奇怪的现象。以量子叠加为例。它说一个量子位(qubit)是 0 和 1 的复杂线性组合。
如果你是一名数学家或物理学家,你会重视别人对这个术语的正确定义。但是如果你不是,你可能会喜欢一个更有趣的解释,比如量子位同时是 0 和 1。
作为一个非数学家,你似乎注定要失败。正确的定义就像一套象形文字。但是,坊间的解释是完全错误的。

作者图片
但是还有第三条路:实用的路。量子计算不再是理论上的构想。有真正的量子计算机。此外,即使您无法访问它们,也有量子 SDK,如 Qiskit,其中包含模拟器。你可以在本地计算机上开发和运行量子电路。当然,如果你使用经典计算机,你不会看到任何量子加速。但是用实际动手的方式学习量子计算已经足够好了。
那么,我们来看一个量子位。这段代码创建了一个具有单个量子位的量子电路。然后,它执行电路并输出布洛赫球。
布洛赫球是一种广泛使用的量子位表示。它突出显示了球体两极量子位的两种基本状态。更具体地说,量子位状态由从球体中心到其表面的向量来表示。这两个基本向量在极点处结束。

作者图片
我们的图显示了在北极结束的矢量|0⟩。|0⟩没什么特别的。但它只是我们在量子力学中用来描述向量的(狄拉克)符号。
一般来说,向量|0⟩也不例外。然而,实际上,我们用它作为计算的基础。这意味着如果我们在|0⟩状态下测量一个量子位,我们将总是观察到它为 0。让我们实际地做这件事。
下图描述了上面代码的输出。它显示了所有可能测量的概率。在我们的例子中,只有一个可能的结果:0。

作者图片
如前所述,量子位有两种基本状态。第二个基态是|1⟩,我们测量为 1。

作者图片
当我们看布洛赫球中的矢量|1⟩时,我们看到它指向南方。

作者图片
将我们的量子位元限制在这两种基本状态,和一般位元一样好(或简单)。但是,当然,要利用量子位的优势,我们也必须允许它们处于其他状态。
|+⟩就是其中的一个州。我们通过𝜋/2 绕 y 轴旋转量子位状态向量来达到这个状态。在这种状态下,量子位有相同的概率分别为 0 或 1–50%。

作者图片
当我们观察布洛赫球时,我们看到它终止于球赤道上的 X 轴。所以,从图形上看,到两极的距离是一样的。

作者图片
如果我们绕 y 轴旋转向量|1⟩𝜋/2,也会发生类似的情况。它产生了|−⟩.

作者图片
结果向量也位于 X 轴。所以当我们测量量子位的时候,它有相同的概率是 0 或者 1。

作者图片
实际上,你可能认为|+⟩和|−⟩是一样的。这与事实相去甚远。相比之下,正如布洛赫球的视觉表现所示,这两种状态指向相反的方向。
在量子力学术语中,它们是相互正交的。这意味着我们可以清楚地区分彼此。这听起来可能很奇怪。我们如何确定表现出相同测量概率的两个状态?
要回答这个问题,我们需要明确一个本质的方面。量子态只有在我们不测量量子位的情况下才存在。但是一旦我们测量了它,它会立刻跳到两个基态中的一个。但是,没有人要求我们在测量量子位元之前,必须让它们保持原样。事实上,量子计算最重要的部分之一就是改变量子系统,使两种不同的状态产生两种不同的测量结果。
在|+⟩和|−⟩的例子中,我们可以在测量之前将它们绕 y 轴旋转回来。当然,我们知道我们旋转了量子位。然后,如果我们测量到 0,我们可以推断出量子位一定处于|+⟩.态我们知道,如果我们测量的是 1,那一定是在|−⟩。
只有当量子态相互正交时,我们才能确定地将它们区分开来。但我们不能,否则。举例来说,有人给了你一个量子比特,它或者在|0⟩,或者在|+⟩.你怎么决定是哪一个?
如果马上测,测到 1 可能就很幸运了。然后,你知道它一定是在|+⟩态,因为在|0⟩.态,你永远不会观察到一个量子位为 1 但你幸运的几率是 50%。如果你测量 0,量子位的状态可能是|0⟩或|+⟩.
你能做的最好的事情就是旋转𝜋/4.如果量子位处于|0⟩状态,你将有 85%的机会将其测量为 0。

作者图片
如果量子位处于|+⟩态,你会看到测量它的反向概率为 1。

作者图片
最后你得到的结果是概率性的。你不能绝对确定量子位是在|0⟩还是在|+⟩.
只有当量子位状态相互正交时,你才能清楚地区分它们。但这不是通常的情况。尤其是如果你创建的量子算法表现出显著的加速。
量子机器学习要不要入门?看看 [**动手量子机器学习用 Python**](https://www.pyqml.com/page?ref=medium_axioms&dest=/) **。**

在这里免费获得前三章。
# MLOps 实践:在银行在线交易的实时反欺诈模型中使用 OpenMLDB
> 原文:<https://towardsdatascience.com/practice-of-openmldbs-transaction-real-time-anti-fraud-model-in-the-bank-s-online-event-40ab41fec6d4?source=collection_archive---------37----------------------->
## 中国在线反欺诈 MLOp 实践
# 背景
如今,许多银行已经开始引入机器学习模型来辅助规则引擎进行决策。银行也做了很多线下的机器学习模型探索,效果很好,但是很少最终应用到线上环境中,主要有以下几个原因:
1. 在风险控制场景中,在线推理需要高性能,需要 TP99 20ms,对于大多数机器学习模型来说,要达到这样的性能是具有挑战性的。
2. 对特征计算的时效性要求高,但大多数机器学习基础设施无法满足基于滑动时间窗的计时特性。
3. 特征计算复杂庞大,在线成本很高。
4. 保持离线特征计算和在线特征计算的一致性并不容易。最后,线下效果好,线上效果差。
这是因为,要完成反欺诈模型在线项目,需要做以下事情来解决一致性问题:
首先,数据科学家使用 SQL 离线处理数据。接下来,他们需要将特征工程解决方案从离线转换到在线。在此过程中,数据工程师需要使特征处理逻辑与数据科学家保持一致,并考虑数据科学家程序更改的风险(这总是发生在生产系统中)。
第二,数据工程师基于反欺诈在线系统架构设计在线和实时数据处理架构。
最后,数据工程师应该开发数据科学家已经用 SQL 手动开发的所有特征处理逻辑。在这个过程中,数据工程师作为不同计算机语言的“翻译者”工作。考虑到反欺诈机器学习项目中的数百万个特征,这种翻译工作是不必要的,并且容易出错。
下图描述了大多数 MLOps 平台的这一过程。

作者图片
那么,有没有一种方法,数据工程师只需要将数据科学家编写的 SQL 脚本扔进一个魔盒,就可以完成机器学习模型的推理工作,并使其在线工作,而无需考虑功能脚本的逻辑?

作者图片
今天,作为一个机器学习应用开发者,我将告诉你如何基于 [OpenMLDB](https://github.com/4paradigm/OpenMLDB) 解决这些问题。如果你在你的机器学习系统中使用它,你可以享受这些好处:
1. 不需要理解数据科学家的特性计划的逻辑。即使计划有所调整,我只需要将 SQL 更新到 [OpenMLDB](https://github.com/4paradigm/OpenMLDB) 。
2. 不需要设计一套完整的在线数据计算流程,使用 [OpenMLDB](https://github.com/4paradigm/OpenMLDB) 让我感觉就像使用 MySQL 开发传统应用一样简单。
3. 告别手工开发特征工程
你可以简化开发过程,如下图所示。

作者图片
# 如何使用 OpenMLDB 解决一致性问题
这里大家可能很好奇, [OpenMLDB](https://github.com/4paradigm/OpenMLDB) 怎么可以不做任何修改批量实时执行一条 SQL,因为 [OpenMLDB](https://github.com/4paradigm/OpenMLDB) 支持两种执行模式:
1. 批处理模式,为训练过程生成样本,类似于 SQL 的传统数据库执行。
2. 请求模式为推理过程实时生成样本,并且只计算与请求相关的特征。
这里让我们用银行交易数据给你举个例子:
这里我们有两张表:
1. *t _ ins:we 查询的时间,包括 user_id/record 时间*
2. *t_trx:所有用户的交易记录,包括每笔交易/交易时间的 user_id / a* 笔数

作者图片
# 如何使用 OpenMLDB 解决性能问题
[OpenMLDB](https://github.com/4paradigm/OpenMLDB) 自带多项列编译优化技术,如函数动态循环绑定和数据在线部分完全在内存中,可以保证非常高的执行性能。以下是 [OpenMLDB](https://github.com/4paradigm/OpenMLDB) 自带的性能数据。

作者图片
我们可以看到 [OpenMLDB](https://github.com/4paradigm/OpenMLDB) 的执行性能比 SingleStore 和 Hana 有很大的优势。接下来,我们来看一个实际的 SQL 执行效率。SQL 如下所示
select * from
(select
card_no,
trx_time,
merchant_id,
month(trx_time) as fea_month,
dayofmonth(trx_time) as fea_day_of_month,
hour(trx_time) as fea_hour,
week(trx_time) as fea_week,
substr(card_no, 1, 6) as card_no_prefix,
max(trx_amt) over w30d as w30d_trx_max ,
min(trx_amt) over w30d as w30d_trx_min,
sum(trx_amt) over w30d,
avg(trx_amt) over w30d,
max(usd_amt) over w30d,
min(usd_amt) over w30d,
sum(usd_amt) over w30d,
avg(usd_amt) over w30d,
max(org_amt) over w30d,
min(org_amt) over w30d,
sum(org_amt) over w30d,
avg(org_amt) over w30d,
distinct_count(merchant_id) over w30d,
count(merchant_id) over w30d,
distinct_count(term_city) over w30d,
count(term_city) over w30d,
max(trx_amt) over w10d,
min(trx_amt) over w10d,
sum(trx_amt) over w10d,
avg(trx_amt) over w10d,
max(usd_amt) over w10d,
min(usd_amt) over w10d,
sum(usd_amt) over w10d,
avg(usd_amt) over w10d,
max(org_amt) over w10d,
min(org_amt) over w10d,
sum(org_amt) over w10d,
avg(org_amt) over w10d,
distinct_count(merchant_id) over w10d,
count(merchant_id) over w10d,
distinct_count(term_city) over w10d,
count(term_city) over w10d
from tran
window w30d as (PARTITION BY tran.card_no ORDER BY tran.trx_time ROWS_RANGE BETWEEN 30d PRECEDING AND CURRENT ROW),
w10d as (PARTITION BY tran.card_no ORDER BY tran.trx_time ROWS_RANGE BETWEEN 10d PRECEDING AND CURRENT ROW)) as trx_fe
last join card_info order by card_info.crd_lst_isu_dte on trx_fe.card_no = card_info.crd_nbr and trx_fe.trx_time >= card_info.crd_lst_isu_dte ;
首先,分析这个 SQL,影响性能的因素有
1. 时间窗口中的数据数量
2. 特征号
因为特性的数量是特定的,所以我们在不同的时间窗口中测试性能。
[OpenMLDB](https://github.com/4paradigm/OpenMLDB) 有着梦幻般的表现。一个时间窗内有 2000 条数据,可以保证 p99 在 1ms 左右快速满足反诈骗场景苛刻的性能要求。

作者图片
[OpenMLDB](https://github.com/4paradigm/OpenMLDB) 性能惊人。一个时间窗内有 2000 条数据,可以保证 p99 在 1ms 左右,这样我们就可以轻松满足反欺诈场景苛刻的性能要求。
# 使用 OpenMLDB 在线业务效果
商业效益是每个人都非常关心的事情。目前我们已经上线了多家银行的反欺诈场景。线上效果和线下评价效果一致。与客户的专家规则相比,模型结果提高了 2-8 倍,并且在线和离线过程中召回率保持相同。在这种情况下,客户非常认可我们的工作,希望我的分享能帮到你。
## 关于 OpenMLDB
[OpenMLDB](https://github.com/4paradigm/OpenMLDB) 是一个开源数据库,为机器学习应用提供正确高效的数据供应。除了机器学习数据开发效率提升 10 倍以上, [OpenMLDB](https://github.com/4paradigm/OpenMLDB) 还提供统一的计算和存储引擎,降低开发、运营和维护的复杂性和整体成本。
# 体系结构

欢迎大家参与到社区中的[HTTPS://github.com/4paradigm/Op enMLDB](https://github.com/4paradigm/OpenMLDB)。
# 增强 ML 代码的实践——第 1 部分
> 原文:<https://towardsdatascience.com/practices-to-enhance-your-ml-code-part-1-e77d00db826d?source=collection_archive---------33----------------------->
## 停止编写蹩脚的 ML 代码
大约 4 年前,当我开始从事机器学习领域时,[这是我的第一个 ML 代码看起来的样子。我有一个文件用于预处理、训练、设置超参数和测试。](https://github.com/purvanshi/operation-prediction/blob/master/model1.py)

我 4 年前的代码结构
我的“主文件”名为 model1.py,为了版本控制,我有一个名为“FINAL_SYSTEM”的文件夹,我想里面有某个东西的最终代码!我现在甚至不记得了,老实说,我甚至不能阅读我自己的代码。在过去,ML dev 并不成熟,有很多缺陷,但今天情况并非如此。有许多成熟的开源库,每个人都可以使用它们来获得更好看和更有用的代码。

注意,这里我甚至没有谈论生产模型或者如何维护它们。我们整个 ML 管道可以分为两部分-
1. 管理您的数据
2. 模型代码- ML 代码包括从折叠到超参数调整以及保存和解释结果的所有内容
让我们一个接一个地看看所有这些。我将把这些分成几部分,以免信息过载!
## 数据版本控制
机器学习不仅是改变你对数据使用的模型类型,也是关于你如何处理你的数据。我们需要保持处理数据集的每一种可能的方式。记录我们所做的事情,特别是重现我们所做的事情,通常会变得非常困难。我开始使用[数据版本控制(DVC)](https://dvc.org/) 对我的数据进行 git 版本控制。它的主要目的是为有大量训练数据的项目维护 Git。您可以使用简单的命令将远程路径添加到您的数据中
**\(** dvc remote add -d storage s3://mybucket/dvcstore
\) git add .dvc/config
$ git commit -m "Configure remote storage"
您还可以推送这个版本的数据,稍后再拉取,以便重现结果。
## 折叠
正确地将你的整个 ML 代码划分到不同的文件夹中有助于减少其他人的麻烦并增强可读性。这是一些更好地组织你的代码的技巧
1. **缓存** -一个文件和文件夹缓存,用于保存生成的任何中间文件,这些文件可以被保存以便以后更快地处理。例如,如果预处理数据或要素位于本地,则加载预处理数据或要素。
2. 如果多个模型被用于基准测试,让每个模型在单独的文件中有自己的类对调试非常有帮助。在 pytorch 中,默认情况下你需要有一个类,在其中添加像推理或单元测试这样的功能会很有帮助
3. Utils -通常一个特定的任务有一些通用的函数。假设你有一些被所有模型类使用的函数。拥有一个通用的 model_utils 类有助于减少冗余。对于特定的函数,可以有这样的独立实用程序,而不是一个全局实用程序文件。
## 超参数调谐
我的结果文件夹看起来是这样的

许多文件夹中有训练文件,其中有 100 个用于不同超参数循环的*。我的超参数调整经历了三个阶段-*
* 对几个超参数(重量衰减、时期、提前停止、动量、批量、学习率、工人等等)使用嵌套的 for 循环。这使得我的代码看起来很乱,如果你每次都不清空你的内存,进程会因为没有剩余内存而终止。
* 我转而通过命令行界面(cli)传递参数,但这有两个问题——我们不能指定超过 3 个参数,因为我们懒得查看文件中的参数名称,并在 CLI 上再次键入所有内容;其次,为了使用各种参数,您必须编写一个. sh 文件
* 我的第三个阶段涉及通过 bash 脚本遍历参数。这有两个主要问题——我每次都必须保存超参数设置的结果,现在我意识到这不是最佳实践。这也占用了不少内存。我也尝试过维护配置文件,但是从来没有发现它们非常方便。
我最喜欢的开发者之一[奥姆里·丫蛋](https://github.com/omry)开发了[九头蛇|九头蛇](https://hydra.cc/)。Hydra 允许您编写传递给应用程序的配置,从而消除了这些问题。可以通过配置文件或命令行进行组合,组合后的配置中的所有内容也可以通过命令行覆盖。
看一个非常[基本的例子,](https://github.com/omry/hydra-article-code/tree/master/basic)你需要做的就是创建一个 config.yaml 文件
dataset:
name: imagenet
path: /datasets/imagenet
我们的 my_app.py 文件看起来像
import hydra
from omegaconf import DictConfig @hydra.main(config_path="config.yaml")
def my_app(cfg: DictConfig) -> None:
print(cfg.pretty())
if name == "main": my_app()
程序打印它得到的配置对象。我们现在也可以从命令行覆盖配置文件。也可以为不同的功能创建多个 yaml 文件。

对于多次运行,hydra 具有*多次运行*功能,这有助于使用不同的配置多次运行您的函数。
这就像-
python my_app.py --multirun dataset=imagnet,cifar optimizer=adam,nesterov
## 结论
在这篇文章中,我们讨论了三个问题及其最新的解决方案——数据版本化、超参数调整和折叠。这些让我的编码生活变得更容易,也一定会让你的编码生活变得更容易。
第 2 部分可在此处找到- [增强您的 ML 代码的实践-第 2 部分|作者 Purvanshi Mehta | 2021 年 5 月| Medium](https://purvanshimehta.medium.com/practices-to-enhance-your-ml-code-part-2-e3c5eda48e8a)
# 增强 ML 代码的实践——第 2 部分
> 原文:<https://towardsdatascience.com/practices-to-enhance-your-ml-code-part-2-e3c5eda48e8a?source=collection_archive---------28----------------------->
## 停止编写蹩脚的模型——单元测试|代码格式化
嘿,你在这里是因为你可能认为你的编码风格还有改进的空间。好的代码和管道不仅能帮助别人阅读你的代码,而且你自己也能在几天后理解你的代码。在这个系列的第一部分中,我写了我的 Keras 代码在 4 年前看起来是多么的*可怕。我还谈到了这些天我在使用什么技术使它变得可读:),我谈到了数据版本化、文件夹和超参数调整。让我们直接进入更多的实践,这将使您的数据培训之旅更容易。*
# 单元测试

图片来自[全栈深度学习](https://fullstackdeeplearning.com/)
单元测试是软件工程中的一种常见做法,但大多数深度学习的研究代码仍然没有实现基本的单元测试。由于 ML 从业者 80–90%的时间都花在调试和调整参数上,所以在代码中进行基本的单元测试是不可避免的。这对于 ML 研究/生产代码尤其重要,这样可以很容易地为不同的数据集调整模型。这些是一些非常基本的单元测试,可以应用于任何 ML 管道-
1. **数据点的形状/格式** -具有可变长度数据点的数据集需要在通过任何模型之前进行相等处理。如果使用非欧几里德 DL 模型,这尤其正确。例如,如果使用 transformer 模型,文本数据特别需要填充。具有检查输入形状是否等于第一层形状的功能是重要的开始步骤。
2. **变量的变化**——这是最难发现的错误之一。我们的代码运行时没有错误,但是性能很差。你在你的整个代码中寻找可能出错的地方。一个最简单的事情是检查更新的模型值是否不同于先前的模型。我最初在这里读到了这个问题[,现在我在我的每个 ML 代码中使用这个代码片段。](https://thenerdstation.medium.com/how-to-unit-test-machine-learning-code-57cf6fd81765)
3.**过度拟合** -在一个非常小的列车组上运行整个管道,查看模型是否过度拟合,这有助于检查一切是否正常工作。如果模型没有在较小的数据集上过度拟合,那么很可能有一个错误,它也不会在整个数据上工作。
4.**目标和预测的形状** -目标和预测的形状和数据类型在传递到损失函数之前应该是相同的。
assert predictions.shapetarget.shape
assert type(predictions)type(target)
## 单元测试库
有许多用于单元测试的 python 库。最常见的是[单元测试](https://docs.python.org/3/library/unittest.html)。还有 [pytest](https://docs.pytest.org/en/6.2.x/getting-started.html) 和 [nose](https://www.lambdatest.com/blog/selenium-python-nose-tutorial/#:~:text=Nose%20is%20a%20popular%20test%20automation%20framework%20in,auto%20discovery%20of%20test%20cases%20and%20documentation%20collection.) 。我个人更喜欢 unittest。但是在选择其中一个之前,你必须先尝试一下。你可以在这里看一个单元测试[的快速例子。我不会再重复了,因为你可以找到几十个博客来开始。](https://data-flair.training/blogs/python-unittest/)
## 新闻报道
[覆盖率](https://coverage.readthedocs.io/en/coverage-5.5/)是一个 python 库,用来测试你的代码有多少百分比被单元测试覆盖。它使用起来非常简单
$ coverage run -m unittest discover
我们现在可以看到一个类似这样的报道

总的来说,我们可以看到 87%的代码被覆盖。然后,您可以检查缺失的行,并在必要时添加单元测试。
## Git 挂钩
现在我们已经为我们的代码编写了单元测试,并且准备好提交了。我们可以使用 git hooks 来代替每次手动运行单元测试,git hooks 会在每次自动提交时运行测试。
当某些重要的动作通过钩子发生时,Git 有办法触发定制脚本。默认文件在。git/hooks 文件,你可以添加任何你想被提示的动作。有许多类型的钩子,但是对于我们的应用程序,我们需要*预提交*钩子。你可以在这里找到一个例子[。](https://github.com/git/git/blob/master/templates/hooks--pre-commit.sample)
# 代码格式
我通常用黑色的[来格式化我的代码。好的一面是一个项目和所有项目的一致性。如果样式在任何地方都是一样的,那么对于直接研究你的作品的人来说,阅读和继续你的作品会变得非常容易。](https://github.com/psf/black)
您可以使用-来检查您的格式和 black 的格式之间的差异
$ black --diff --check train.py test_audionet.py
如果你喜欢这些变化,你可以应用它们
$ black train.py test_audionet.py
# 结论
在这篇文章中,我们讨论了为 ML 模型和代码格式编写单元测试。您也可以[阅读该系列的第一部分](/practices-to-enhance-your-ml-code-part-1-e77d00db826d),其中包括超参数调整、折叠和数据版本化。
# 浓缩咖啡的预浸泡
> 原文:<https://towardsdatascience.com/pre-infusion-for-espresso-dab5185b8094?source=collection_archive---------13----------------------->
## 咖啡数据科学
## 暴风雨(或大动荡、激烈辩论)前的平静
直到我发现泵机没有预注入功能,至少对于便宜的泵机来说是这样,我才意识到我对杠杆机是多么的宠坏了。预浸是一个关键参数,它允许你将咖啡磨得更细,因为它有助于减少通灵。
对于那些不知道的人来说,预冲泡是让水以低于正常的压力进入咖啡球。通常,预注入压力在 0.5 至 4 巴之间。通常为 1 至 2 巴。
刚开始定期使用金快线的时候对预输注了解不多,最后还是用了 10 秒预输注的套路。在过去的两年里,我开始更多地探索这一变量,因为我发现更长的预灌输导致更好的品尝。
在这篇文章中,我使用了三个预输注参数来显示它们如何影响拍摄。这不是对某个特定参数的建议,而是让人们探索变量,找到最适合他们的方法。对于这三个镜头,他们都是在金快车上拍摄的,有着相同的咖啡烘焙、相同的研磨机和相同的冰球准备。所有注射都使用[压力脉冲](/pressure-pulsing-for-better-espresso-62f09362211d)进行注射。
每列图像是一个参数,而每行是 5 秒钟的时间,预输注时间为:
30 秒……20 秒……..十秒钟。

首先清楚的是,较低的预输注注射产生更多的克莉玛。让我们来看看一些性能指标。
# 绩效指标
我使用两个指标来评估技术之间的差异:最终得分和咖啡萃取。
[**最终得分**](https://towardsdatascience.com/@rmckeon/coffee-data-sheet-d95fd241e7f6) 是记分卡 7 个指标(尖锐、浓郁、糖浆、甜味、酸味、苦味和回味)的平均值。当然,这些分数是主观的,但它们符合我的口味,帮助我提高了我的拍摄水平。分数有一些变化。我的目标是保持每个指标的一致性,但有时粒度很难确定。
</coffee-solubility-in-espresso-an-initial-study-88f78a432e2c>**用折射仪测量总溶解固体量(TDS),这个数字结合咖啡的输出重量和输入重量用于确定提取到杯中的咖啡的百分比,称为**提取率(EY)** 。**
# **表演**
**对于 TDS 和 EY 来说,较短的预浸时间胜出,但奇怪的是,较长的预浸时间只会稍微改善味道。最大的变化是甜味增加,糖浆(或口感)减少。**
********
**时间指标主要由预输注时间驱动,因为输注时间大致保持不变。**
****
**预浸泡时间越短,产生的克莉玛越多。这可以从达到特定体积所需的时间中看出。**
****
**我们可以把它们画在一起,看一看总的趋势。**
****
**我不知道这说明了克莉玛什么。人们通常认为克莉玛是浓缩咖啡性能的一个很好的定性指标,但由于长时间的预浸泡,我的大多数照片都没有多少克莉玛。**
**虽然我更喜欢更长时间的预输注,但数据表明仍有探索的空间。通常,我的预输注时间与覆盖过滤器的[时间(TCF)](/pre-infusion-for-espresso-visual-cues-for-better-espresso-c23b2542152e) 相关,因为我之前发现最佳预输注时间大约为 3*TCF。**
**如果你愿意,请在 [Twitter](https://mobile.twitter.com/espressofun?source=post_page---------------------------) 和 [YouTube](https://m.youtube.com/channel/UClgcmAtBMTmVVGANjtntXTw?source=post_page---------------------------) 上关注我,我会在那里发布不同机器上的浓缩咖啡照片和浓缩咖啡相关的视频。你也可以在 [LinkedIn](https://www.linkedin.com/in/robert-mckeon-aloe-01581595?source=post_page---------------------------) 上找到我。也可以在[中](https://towardsdatascience.com/@rmckeon/follow)关注我,在[订阅](https://rmckeon.medium.com/subscribe)。**
# **[我的进一步阅读](https://rmckeon.medium.com/story-collection-splash-page-e15025710347):**
**[浓缩咖啡系列文章](https://rmckeon.medium.com/a-collection-of-espresso-articles-de8a3abf9917?postPublishedType=repub)**
**[工作和学校故事集](https://rmckeon.medium.com/a-collection-of-work-and-school-stories-6b7ca5a58318?source=your_stories_page-------------------------------------)**
**个人故事和关注点**
**[乐高故事启动页面](https://rmckeon.medium.com/lego-story-splash-page-b91ba4f56bc7?source=your_stories_page-------------------------------------)**
**[摄影飞溅页面](https://rmckeon.medium.com/photography-splash-page-fe93297abc06?source=your_stories_page-------------------------------------)**
**[使用图像处理测量咖啡研磨颗粒分布](https://link.medium.com/9Az9gAfWXdb)**
**[改良浓缩咖啡](https://rmckeon.medium.com/improving-espresso-splash-page-576c70e64d0d?source=your_stories_page-------------------------------------)**
**[对断奏生活方式的总结](https://rmckeon.medium.com/a-summary-of-the-staccato-lifestyle-dd1dc6d4b861?source=your_stories_page-------------------------------------)**
**[测量咖啡研磨分布](https://rmckeon.medium.com/measuring-coffee-grind-distribution-d37a39ffc215?source=your_stories_page-------------------------------------)**
**[咖啡萃取](https://rmckeon.medium.com/coffee-extraction-splash-page-3e568df003ac?source=your_stories_page-------------------------------------)**
**[咖啡烘焙](https://rmckeon.medium.com/coffee-roasting-splash-page-780b0c3242ea?source=your_stories_page-------------------------------------)**
**[咖啡豆](https://rmckeon.medium.com/coffee-beans-splash-page-e52e1993274f?source=your_stories_page-------------------------------------)**
**[浓缩咖啡用纸质过滤器](https://rmckeon.medium.com/paper-filters-for-espresso-splash-page-f55fc553e98?source=your_stories_page-------------------------------------)**
**[浓缩咖啡篮及相关主题](https://rmckeon.medium.com/espresso-baskets-and-related-topics-splash-page-ff10f690a738?source=your_stories_page-------------------------------------)**
**[意式咖啡观点](https://rmckeon.medium.com/espresso-opinions-splash-page-5a89856d74da?source=your_stories_page-------------------------------------)**
**[透明 Portafilter 实验](https://rmckeon.medium.com/transparent-portafilter-experiments-splash-page-8fd3ae3a286d?source=your_stories_page-------------------------------------)**
**[杠杆机维修](https://rmckeon.medium.com/lever-machine-maintenance-splash-page-72c1e3102ff?source=your_stories_page-------------------------------------)**
**[咖啡评论和想法](https://rmckeon.medium.com/coffee-reviews-and-thoughts-splash-page-ca6840eb04f7?source=your_stories_page-------------------------------------)**
**[咖啡实验](https://rmckeon.medium.com/coffee-experiments-splash-page-671a77ba4d42?source=your_stories_page-------------------------------------)**
# 预修剪或后修剪
> 原文:<https://towardsdatascience.com/pre-pruning-or-post-pruning-1dbc8be5cb14?source=collection_archive---------10----------------------->
## 了解如何以及何时在 Python 中预先修剪决策树
爱德华·克鲁格

[Devin H](https://unsplash.com/@devin_photography?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 在 [Unsplash](https://unsplash.com/s/photos/bonsai?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 上的照片
在上一篇文章中,我们讨论了后期修剪决策树。在本文中,我们将重点关注预修剪决策树。
让我们简单回顾一下我们修剪决策树的动机,后修剪工作的方式和原因,以及它的优点和缺点。如果你想了解更多细节,请查看这篇文章。
决策树是使用 CART 算法生成的。CART 代表“分类和回归树”不幸的是,知道缩写代表什么并不能帮助我们理解任何事情,但继续阅读,我们会涵盖它。或者,如果你想了解更多细节,可以看看我的文章:
</learn-how-decision-trees-are-grown-22bc3d22fb51> [## 了解决策树是如何生长的
towardsdatascience.com](/learn-how-decision-trees-are-grown-22bc3d22fb51)
本质上,该算法沿着特征检查分区,以找到根据度量优化分数的分区。然后,该算法递归地应用于分割的每一侧。当不再有可以提高分数的分割时,算法终止。由于这个原因,树有时被称为递归划分。
对于回归,MSE 是最常见的选择指标,也是 SciKit-Learn 的默认指标。对于分类问题,基尼和熵是最常见的度量。虽然它们的理论表述不同,但在实践中似乎没有什么区别。SciKit-Learn 中的分类树可以使用这两个指标,缺省值为 Gini。
为了避免过度拟合,我们可以应用提前停止规则。本文就是关于这些规则的——预修剪只是提前停止的一个花哨术语。避免过度拟合的另一个选择是应用后修剪(有时简称为修剪)。也可以同时应用这两种方法——有时,这将是加速后期修剪过程的一个很好的方法。
后期修剪考虑整个树的子树,并使用交叉验证的度量来对每个子树进行评分。为了澄清,我们使用子树来表示与原始树有相同根但没有一些分支的树。
对于回归树,我们通常使用 MSE 进行修剪。对于分类树,我们通常使用误分类率进行修剪。误分类率与二元分类问题的准确度成比例,并且选择相同的最佳子树。
实际上,有必要避免考虑每一个子树,因为二叉树的子树数量很少——在渐近上比多项式时间更差。因此,剪枝算法使用一种技巧来选择包含原始树根的所有子树集合的子序列(称为成本复杂度路径)。
即使进行了这种优化,随着要素和观测值数量的增加,后期修剪也会非常缓慢。幸运的是,我们可以并行地交叉验证子树,但是在某一点上,后期修剪增加的时间令人沮丧。
另一方面,后修剪往往比预修剪/提前停止更有效。预剪枝的问题在于它是贪婪的:早期停止规则可能会使算法避开一个分区,即使后续分区可能非常有价值。早期停止比后期修剪更快,但是值得一提的是,早期停止并没有看起来那么快。使用早期停止规则生成树花费的时间更少,但是我们仍然需要执行网格搜索来确定早期停止规则的最佳阈值。
在 SciKitLearn 中,我们可以使用一些超参数来控制早期停止。在本文中,我们将重点讨论两个问题。
* `min_samples_split`
* `min_samples_leaf`
我们将使用来自 SciKit-Learn 的乳腺癌数据集进行演示。数据集包含 30 个连续要素。目标是肿瘤是否是恶性的。欲了解更多信息,请访问 SciKit-Learn 文档[此处](https://scikit-learn.org/stable/modules/generated/sklearn.datasets.load_breast_cancer.htm)。
在我们开始预修剪之前,让我们先看看完整的树以及它是如何工作的。
## 完整的树

用 SciKit-Learn 训练决策树
这里,我们用默认参数来拟合决策树,只是我们设置了`random_state`。我们设置了一个随机状态,因为当多个分裂一样好时,算法会随机打破平局。通过设置状态,我们将确保在预修剪时使用同一棵树的子树。
让我们看看完整的树。

用 SciKit-Learn 绘制决策树

完整的决策树是使用上面的代码绘制的
请注意,完整的树是相当复杂的,有 18 个不同的分裂!
让我们看一下整个树的指标。

完整树的度量
它达到了 91%的准确率。对于恶性类,准确率和召回率分别为 96%和 90%。这建立了我们的基线。
## 分裂树的最佳最小样本
在这里,我们将研究如何设置一个阈值,如果一个节点上的观察值小于该阈值,那么该阈值将停止树在该节点上的生长。如果在划分之后,观察值的数量小于阈值,递归在树的这一侧停止。在 SciKit-Learn 中,我们可以用`min_sample_split`超参数来控制这种行为。它可以采用`int`或浮点值。当我们通过一个`int`时,如果一个节点的观测值少于指定的数目,那么该算法不会考虑该节点之后的进一步分裂。当我们通过一个`float`时,当一个节点的观测值少于数据集的指定比例时,递归停止。将超参数指定为浮点型更容易且更可重用,因为我们不必为更大或更小的数据集修改值的范围。

用于寻找最佳最小样本分裂树的代码
在我们的网格搜索中,我们严格地在 0 和 1 之间寻找阈值。阈值为 0 不会有任何影响,而阈值高于 1 会导致树没有分割。当生成要检查的阈值范围时,我们从略大于 0 的数字`EPS`开始,在 1 之前结束。

寻找 min_samples_split 超参数的最佳值
网格搜索发现超参数`min_samples_split`的最佳选择约为 0.075,这意味着如果节点小于数据集的 7.5%,则不允许进一步分裂。
让我们画出这棵树。

要准备绘图和评估,请从网格搜索中提取树

绘制最佳最小样本分裂树

使用上面的代码绘制了最佳最小样本分裂树
与完整的树相比,在这个数据集上,这个树更简单,只有 8 个分裂。
现在,让我们来看看指标。

最佳最小样本分裂树的分类报告
不幸的是,在这种情况下,这种预先修剪的方法不如完整的树准确,准确率为 89%。恶性类的召回率和准确率也略有下降。幸运的是,我们有另一种方法可以尝试。
## 叶树上的最佳最小样本
让我们看看,当一片叶子上的样本数低于某个阈值时,我们试图通过停止递归来限制一棵树,会得到什么结果。SciKit-Learns 用超参数`min_samples_leaf`实现了这一点。它可以接受一个`int`或一个`float`的值。当我们传递一个 int 时,当一个 split 创建的叶子少于这个数目的观察值时,算法停止。当我们通过一个`float`时,当一个分裂将导致一个叶子上的观察值下降到数据集的那个比例以下时,递归停止。一般来说,我们发现通过一个`float`更容易,因为它是一个比例;对于不同大小的数据集,我们不需要改变网格搜索的范围。

寻找最佳最小样本叶树的代码
为了对参数`min_samples_leaf`的不同值进行网格搜索,我们使用`np.arange`设置了一个严格介于 0 和. 5 之间的网格值。要求每个叶子上至少有 0%的数据集,不会约束算法,并且会导致 SciKit-Learn 的边缘情况。因此,我们从`EPS`开始网格,我们将其设置为略高于 0。如果我们要求超过 50%的数据集位于每片叶子上,那么进行任何分割都会违反规则:如果超过 50%的数据集位于分割的两片叶子上,那么超过 100%的数据集必须位于两片叶子之间。

寻找 min_samples_leaf 超参数的最佳值
网格搜索发现参数`min_samples_leaf`的最佳值大约是. 025,这意味着每个叶子需要至少有 2.5%的数据集。
让我们画出这棵树。

要准备绘图和评估,请从网格搜索中提取树

绘制最佳最小样本叶树

使用上面的代码绘制了最佳最小样本叶树
最佳最小叶子样本树比只有 8 个分裂的完整树简单得多。
让我们来看看指标。

最佳最小样本叶树分类报告
我们现在有一个更好的 95%的训练准确率。这棵树也在不损失精确度的情况下击败了基线的召回。对于这个数据集,该方法甚至执行后剪枝!请看这篇文章来比较这些方法:
</build-better-decision-trees-with-pruning-8f467e73b107>
## 结论
在本文中,我们介绍了一些预修剪的方法,如何在 SciKit-Learn 中实现它们,并比较了预修剪和后修剪。概括地说,让我们来看看不同之处。
**预剪枝和后剪枝的优点:**
* 通过限制树的复杂性,修剪创建了更简单更易理解的树。
* 通过限制树的复杂性,修剪减少了过度拟合。
* 由于修剪选择了最佳的交叉验证子树,所以被修剪的树倾向于很好地适合数据。
**预修剪和后修剪的缺点:**
* 与原始决策树相比,没有缺点—如果修剪没有帮助,交叉验证的网格搜索可以选择原始树。
* 与诸如随机森林和 AdaBoost 之类的集成树模型相比,修剪后的树往往得不到同样的分数。
**预修剪的优点**
* 与后期修剪相比,前期修剪速度更快。这在较大(更多要素或更多数据)的数据集上尤其重要,因为后期修剪必须评估非常大的树子集。
* 预修剪有时可以获得与后修剪相似甚至更好的结果。
**后期修剪的优势**
* 后修剪通常产生比预修剪更好的树,因为预修剪是贪婪的,并且可能忽略具有后续重要分裂的分裂。
要了解更多关于决策树是如何生长的,请查看这篇文章:
</learn-how-decision-trees-are-grown-22bc3d22fb51> [## 了解决策树是如何生长的
towardsdatascience.com](/learn-how-decision-trees-are-grown-22bc3d22fb51)
如果您对决策树的概述以及如何手工拟合决策树的演示感兴趣,请查看 Arif R 撰写的这篇文章:
<https://medium.datadriveninvestor.com/the-basics-of-decision-trees-e5837cc2aba7>
# 改变数据管理的权力动态
> 原文:<https://towardsdatascience.com/precautions-in-using-tech-for-public-health-part-iii-shifting-the-power-dynamics-of-data-987b027c3ede?source=collection_archive---------48----------------------->
## [**公共卫生技术使用注意事项**](https://towardsdatascience.com/tagged/tech-for-public-health) | **第三部分**
我们使用各种免费的服务,但它们真的是免费的吗?例如,你不需要每次想进行谷歌搜索时都输入你的信用卡信息,你也不需要每月支付费用在脸书上发帖。然而,你为这种“免费服务”付出的代价是公司收集你的信息,然后卖给有兴趣的人。你的信息的另一个术语是“数据体”,正如我们的数据体在[数字防御行动手册](https://www.odbproject.org/tools/)中所定义的。
*数据体:我们整个自我的离散部分,它们被收集、存储在数据库、云以及其他数字化网络流空间中,并用于对我们做出决策或决定。它们是我们与我们的社区和机构的关系的体现,包括特权、压迫和统治的机构*。
该资源还包括一系列主题,这些主题基于对社区成员的采访,内容涉及他们如何理解自己的数据体,以及数据收集和数据驱动系统如何被武器化,从而对他们的生活产生负面影响。一个主题是“数据是一种权力关系。数据收集往往削弱该机构,并限制目标个人和社区的自决权。”
当谈到数据控制时,我们指的是[管理权,而不是所有权](https://www.brookings.edu/blog/techtank/2018/11/19/who-should-profit-from-the-sale-of-patient-data/)。数据的管理意味着按照一套规则行事,维护访问数据的所有各方的利益。数据所有权将允许出现混乱的情况,比如让病人能够改变他们的医疗数据。在本帖中,我们将讨论数据管理的权力动态。
**大型科技公司找到了获取大量数据并从中获利的方法**
谷歌是几家声称在医疗科技领域拥有股份并获得人们数据的大型科技公司之一。正如我们在[第一篇博文](/precautions-in-using-tech-for-public-health-part-i-technology-is-not-neutral-ff533acbf7e?sk=dec7e30c0f68fb3961b85b574584802f)中提到的,谷歌在 2019 年收购了 Fitbit。这次收购是里根政府时期反托拉斯法的倒退,允许[纵向兼并](https://www.investopedia.com/articles/stocks/09/merger-acquisitions-types.asp),并产生了这些新形式的垄断。建立垄断是赋予这些公司难以控制的数据访问权限的众多因素之一。
除了薄弱的反垄断法,美国法律中还有一些漏洞,让医疗科技公司可以自由获取和利用敏感的个人健康数据。健康保险便携性和责任法案(HIPAA) 的[隐私规则允许医疗保健提供者在未经患者同意的情况下将个人健康数据共享给业务伙伴,用于执行操作等目的。像](https://www.hhs.gov/hipaa/for-professionals/privacy/guidance/business-associates/index.html)[谷歌](https://www.wired.com/story/google-is-slurping-up-health-dataand-it-looks-totally-legal/)这样的公司可以作为商业伙伴,然后能够访问这些数据。如果去识别,公司可以使用这些数据开发产品,如机器学习模型,随后可以商业化。此外,基因数据在 HIPAA 下[没有得到完全保护](https://www.eff.org/issues/genetic-information-privacy)。出售实验室开发的测试的公司,如 23andMe 及其直接面向消费者(DTC)的基因测试,通常不在美国美国食品药品监督管理局(FDA)的管辖范围内。这些公司将[为“健康”、“生活方式”和/或低风险医疗目的营销其产品](https://www.statnews.com/2019/04/02/address-challenges-direct-to-consumer-health-products/),以规避 [FDA 医疗器械批准程序](https://www.fda.gov/medical-devices/vitro-diagnostics/direct-consumer-tests)。因此,这些健康技术公司不仅从这些测试的销售中获利,还将客户数据出售给相关方,如制药公司。值得注意的是,FDA 正在更新其针对作为医疗设备的软件的[监管框架](https://www.fda.gov/medical-devices/software-medical-device-samd/artificial-intelligence-and-machine-learning-software-medical-device#regulation),以包括基于人工智能/机器学习(AI/ML)的软件。
2018 年,制药公司葛兰素史克(GSK) [向 23andMe 投资 3 亿美元](https://www.gsk.com/en-gb/media/press-releases/gsk-and-23andme-sign-agreement-to-leverage-genetic-insights-for-the-development-of-novel-medicines/);2020 年,23andMe 宣布与葛兰素史克(GSK)合作进行一项联合[临床试验,以开发癌症疗法](https://blog.23andme.com/news/23andme-and-gsk-clinical-trial/)。这种合作关系让葛兰素史克获得了 23andMe 客户的基因信息,这些客户同意分享他们的数据用于研究。除了这些基因检测产品的历史[欧洲中心](https://www.statnews.com/2020/06/10/23andme-ancestry-racial-inequity-genetics/)可能引起的偏见问题,当 23andMe 的 [12,000,000 名客户](https://mediacenter.23andme.com/company/about-us/)中的[80%](https://research.23andme.com/research-innovation-collaborations/)同意分享他们的数据用于研究时,这种合作关系肯定有可能让 GSK 获得高额投资回报。虽然这种合作关系可能会带来[有前途的药物和治疗方法](https://www.wired.com/story/23andme-glaxosmithkline-pharma-deal/),但 23andMe 客户[放弃对使用他们自己的数据进行的这项研究产生的任何利润](https://www.23andme.com/about/tos/)的所有权利主张。
**保护数据体,尊重人**
当人们成为自己数据的管理者时,他们最好能够利用自己的数据为自己谋利。 [LunaDNA](https://www.lunadna.com/) 是一个为人们提供分享数据平台的组织,这些数据在分享之前会被去识别和加密;研究人员付费获取数据,数据股东从研究中获得收益。如果不是为了钱,那么人们应该能够使用他们自己的数据来告知他们自己对自己健康的理解。 [OpenHumans](https://www.openhumans.org/) 是另一个通过为数据共享、数据分析和学习科学方法提供工具和资源来支持自我研究的组织。这些组织是在使用技术时以人为本的典范。
虽然这些公司正在努力将数据管理的权力转移回数据生产者,但重要的是要定义与这些健康数据相关的“价值”以及从这些数据中获利的限制。对于像谷歌和 23andMe 这样的公司来说,数据的价值不在于个人;更确切地说,价值在于聚集并准备好用于分析的许多个人的干净数据。这些大型科技公司有可能通过上述策略或提供“免费服务”,以难以置信的利润规模收集大量数据。
另一个担心是,隐私在这种交换中丢失,这可能导致个人数据的武器化。尽管面临来自 DTC 基因检测公司的阻力,执法部门可以利用[法院命令](https://techcrunch.com/2020/02/04/ancestry-warrant-dna-records/)从这些公司获取个人信息。这增加了[将无辜者误认为嫌疑犯的风险](https://www.wired.com/2015/10/familial-dna-evidence-turns-innocent-people-into-crime-suspects/),尤其是当这些测试可能[不准确的时候](https://www.theverge.com/2018/6/6/17435166/myheritage-dna-breach-genetic-privacy-bioethics)。除了这些担忧之外,数据泄露的威胁始终存在。基因数据泄露值得每个人关注,因为与电子邮件密码不同,一个人自己的基因数据是不可改变的。访问您的数据可能会决定您对资源的访问,如健康保险。在美国联邦政府层面,如果你的数据被用来对付你,目前有[有限的法律保护](https://www.vox.com/recode/2019/12/13/20978024/genetic-testing-dna-consequences-23andme-ancestry)。
如果分享你的个人数据并没有让你赚很多钱,而隐私是出售你的数据的另一个代价,我们期待萨菲亚·诺布尔博士的案例让[大科技支付赔款](https://www.noemamag.com/the-loss-of-public-goods-to-big-tech/)。诺布尔博士阐述了大型科技公司如何吸收公共资源,同时回避社会和经济责任,例如纳税以支持公共基础设施。拒绝纳税加剧了日益扩大的种族财富差距,这不成比例地影响了[硅谷](https://jointventure.org/publications/silicon-valley-index)历史上被排斥的居民群体(图 1)。系统性的种族主义加剧了种族财富差距,表现为社会和经济不平等,并进一步加剧了[种族健康差异](https://www.kff.org/policy-watch/health-disparities-symptom-broader-social-economic-inequities/)。考虑到在新冠肺炎疫情期间,你的数据可能对大科技的[过高利润](https://www.wsj.com/articles/amazon-sales-surge-amid-pandemic-driven-online-shopping-11604003107)有所贡献,赔款似乎是再合适不过了。

资料来源:地区研究所的硅谷指标。注:多个和其他仅包括一些其他种族,两个或更多种族,以及美国印第安人和阿拉斯加土著人(仅圣克拉拉县)。白人是非西班牙裔或拉丁裔。
我们不能依赖私营部门将隐私保护置于利润之上,并改变数据管理的权力动态。有必要恢复并制定法规来遏制这种垄断行为和对数据的剥削性使用。利用个人数据的公司必须对其行为负责,因为它们获取的数据会影响个人生活的许多不同方面:这些公司不仅直接影响个人的健康,还会间接影响他们所居住社区的贫困水平和种族财富差距。在下一篇博客中,我们将讨论监管大型科技公司的前景,让我们不再是产品。
[1]t .刘易斯,S. P .冈加达兰,萨巴,m .佩蒂,T. 2018。数字防御剧本:回收数据的社区动力工具。底特律:我们的数据体。
[2] Teachout,Z. 2020。*拆散他们*。圣马丁出版集团:所有要点书籍。
我们感谢加州大学圣地亚哥分校 [ReCODE Health](https://recode.health/) 的[Camille nebe ker](https://ethics.ucsd.edu/about/staff/nebeker.html)博士和[美国病理研究学会](https://medium.com/@chhavich)[的](https://www.asip.org/)ch havi Chauhan博士对本博客的建议和反馈。
*本文是“将科技用于公共卫生的注意事项”系列的第三篇——查看该系列的第一篇*</precautions-in-using-tech-for-public-health-part-i-technology-is-not-neutral-ff533acbf7e>**、* [*、第二篇*](/precautions-in-using-tech-for-public-health-part-ii-factoring-climate-change-as-an-output-of-265c6afa0e1a) *和第四篇*</landscape-for-regulating-big-tech-438c06411801>**。***
***本博客系列由*[*Priya Govil*](https://medium.com/@priya.govil)*合著。韩瑜和普里亚·戈维尔是健康研究院的工作人员。本博客系列中反映的观点是作者的观点,不一定代表 AcademyHealth 的官方立场。***
# 精确度和召回变得简单
> 原文:<https://towardsdatascience.com/precision-and-recall-made-simple-afb5e098970f?source=collection_archive---------5----------------------->
## 通过简单的例子、一步一步的解释和动画 gif,使精确度和召回容易理解

János Venczák 在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍摄的照片
# 目录
1. [简介](#3ccc)
2. [精确和召回背后的动机](#8130)
3. [什么是精准和召回?](#87ae)
4. [如何不迷茫?](#7891)
5. [为什么精确度和召回率高于准确度?](#4bd9)
6. [什么时候用精度和召回?](#0036)
7. [相关资源](#a656)
8. [结论](#3193)
# 1.介绍
在我之前的[帖子](https://medium.com/mlearning-ai/evaluating-classification-models-why-accuracy-is-not-enough-abf3d9c93a69?source=friends_link&sk=c72c3d9ccb63549d861801ce772b3cc2)中,我写了关于准确性作为二进制分类模型的评估标准。我使用癌症预测示例来说明准确性不足以评估模型在预测**少数类别**(即**感兴趣类别**或**阳性类别**)时的表现,尤其是在类别不平衡的数据集。原因是精度没有区分少数类和多数类(即**否定类**)。
在这篇文章中,我将分享 **precision** 和 **recall** 如何减轻这种准确性限制,并帮助揭示二进制分类模型的预测性能。我将使用一个简单的例子、一步一步的解释和动画 gif(附:我是一个简化事物的信徒)来浏览这些概念,甚至分享一个关于如何不混淆这两者的提示。你准备好了吗?让我们开始吧!
# 2.精确和回忆背后的动机
让我们从理解为什么精确和召回是重要的开始。我们用混淆矩阵来做这个。通常,二元分类的混淆矩阵将模型预测总结为四种不同的结果,如图 1 所示。

图 1:二元分类的混淆矩阵(图片由作者提供)
在我的[上一篇文章](https://medium.com/mlearning-ai/evaluating-classification-models-why-accuracy-is-not-enough-abf3d9c93a69?source=friends_link&sk=c72c3d9ccb63549d861801ce772b3cc2)中的癌症预测示例的背景下,这些结果描述了以下四种情况(见图 2):

图 2:癌症预测的混淆矩阵(图片由作者提供)
* **场景#1** :模型预测癌症患者的癌症(真阳性)
* **场景#2** :该模型预测一个没有癌症的患者患有癌症(假阳性)
* **场景#3** :模型预测患有癌症的患者没有癌症(假阴性)
* **场景#4** :模型预测没有癌症的患者没有癌症(真阴性)
其中,场景#1 和#4 是理想的,但是场景#2 和#3 是错误的预测,并且具有不期望的结果。原因如下:
* 场景#2 代表**误报(FPs)** 。这意味着,在 900 名真正没有患癌症的患者中,模型显示其中 80 人患有癌症。在现实生活中,这 80 名患者可能会接受昂贵和不必要的治疗,以牺牲他们的福祉为代价。
* 场景#3 代表**假阴性**。这意味着 100 名真正患有癌症的患者中,模型显示其中 20 人没有。这种情况的后果可能会更糟,因为这 20 名患者将无法确诊,也无法接受适当的治疗。
可以想象,这两个场景有着非常不同但却非常重要的后果。这不仅用于预测癌症,还可用于许多其他应用。尽管我们希望所有的模型预测都落在场景#1 和#4 中,但是我们知道在现实世界中没有模型是完美的。几乎可以肯定的是,模型预测最终会有 FPs 和 fn。目标是确保 ***尽可能少的 FPs 和 fn***,评估的方法是使用**精度**和**召回**。
# 2.什么是精度和召回率?
解释了为什么精度和召回是重要的,让我们正式介绍他们。精度和召回率本身并不是很难掌握的概念*,但是很容易迷失在 TP、FP、TN 和 FN 的术语以及它们的数学公式中。我制作了动画 gif 来帮助你更好地理解精确度和召回率是如何计算的。*
**
*图 3:计算精度(作者提供的 GIF)*
**
*图 4:计算召回(作者 GIF)*
*如果我们把概念和例子联系起来,总是更容易理解它们。所以,让我们用癌症预测的例子来做。*
* ***精确**是关于问这样一个问题,“*所有被预测患有癌症的患者中,有多少真的患有癌症?*“在我们的例子中,160 名患者被模型预测患有癌症,但其中只有 80 人真的患有癌症。因此精度为 0.5。*
* ***回忆**是关于问这样一个问题,“*在所有真正患有癌症的患者中,有多少人被预测患有癌症?*“在我们的例子中,有 100 名患者真的得了癌症。其中,模型正确预测了 80。因此召回率为 0.8。*
*我在上面提到过,精度和召回允许我们评估由 FPs 和 FNs 造成的错误的程度。让我解释一下。形式上,精度和召回率由下式给出:*
****
*注意 FP 和 FN 是如何分别出现在精度和召回率的分母中的。这意味着:*
* *FPs 越少,精度越高;和*
* *fn 越少,召回率越高。*
*这也意味着,如果根本没有 FNs 和 FPs,即模型做出完美的预测,则精度和召回率都将是 1。现实中,这是很难实现的。在精确度和召回率之间也有一个权衡——增加精确度会导致召回率降低,反之亦然。实际上,精确度和召回率越接近 1,模型的性能就越好。*
# *4.如何不迷茫?*
*精度和召回很容易混淆,因为它们太相似了。我已经无数次把它们弄混了,每次都得用谷歌搜索才能知道哪个是哪个……直到我想到一个简单的窍门。以下是帮助你更好地记住它们的窍门:*
*Precision 以字母“P”开头,所以我们把它和 ***预测*** 这个词联系起来。另一方面,recall 是以字母“R”开头的,所以我们把它与单词 ***联系起来,真的是*** 。*
* *当你想到精度的时候,想想“R”之前的“P”(即 ***预测*** 之前的 ***真的*** ),像这样:*
> *“在所有 ***预测*** 为阳性的病例中, ***真的是*** 阳性的有多少?”*
* *反之,为了回忆,想想“P”前面的“R”(即 ***真的*** 在 ***预测的*** 之前):*
> *在所有 ***真的*** 阳性的病例中, ***预测*** 为阳性的有多少*
*我意识到试图记忆精确和回忆的数学公式是没有帮助的。当然,如果对你有用,就去做吧。否则,你很可能会把 TP,FP,FN 和 TN 弄混,就像我一直做的那样。想想我分享的这个技巧,然后在你的脑海中想象如何获得精确度和召回率(见图 3 和图 4 中的动画 gif)。最好理解它们是如何产生的,然后简单地记忆:)*
# *5.为什么精确度和召回率高于准确度?*
## *(1)评估预测误差的程度*
*如上所述,精度和召回允许我们评估由 FPs 和 FNs 造成的错误程度。考虑到这两种类型的错误会产生非常不同的影响,使用单独的指标来评估每种错误的程度是有意义的。在这方面不能使用精确度,因为它隐含地假设两种类型的误差同等重要,而我们知道事实并非如此。*
## *(2)评估少数类的预测性能*
*我们在我的前一篇文章中确定,精确度不足以评估一个模型在预测少数类中的表现,因为它没有将它与多数类区分开来。然而,精确和回忆却恰恰相反。他们关注于**正确预测的正类**(注意两个公式的分子都是“TP”)。相反,他们真的不关心**正确预测的负类**(“TN”在两个公式中都根本没有出现)。*
# *6.什么时候使用精确和召回?*
*那么,你应该*总是*使用精度和召回吗?要考虑的一些简单的事情是:*
1. *是二元分类问题吗?*
2. *你的训练数据集在不同的班级中不平衡吗?*
3. *在您的训练数据集中是否有特定的感兴趣的类别(即少数族裔类别)?*
*如果以上问题的答案是“是”,那么马上知道你应该抛弃准确性,使用精确和/或回忆。当然,现实中可能还有其他因素需要考虑,但就目前而言,这将是一个很好的起点。*
# *7.相关资源*
*我要感谢 Boaz shmu Eli[写的这篇精彩的文章,当我第一次开始学习分类指标时,它给了我很大的帮助。我强烈推荐它:](https://medium.com/u/57ee515c83c5?source=post_page-----afb5e098970f--------------------------------)*
*</multi-class-metrics-made-simple-part-i-precision-and-recall-9250280bddc2>
我发现其他有用的资源有:
1. [模型评测一:精度和召回](https://medium.com/towards-data-science/model-evaluation-i-precision-and-recall-166ddb257c7b)作者 [Tejumade Afonja](https://medium.com/u/44e0f445aa49?source=post_page-----afb5e098970f--------------------------------)
2. [精确与回忆——每个机器学习者的直观指南](https://medium.com/analytics-vidhya/precision-vs-recall-an-intuitive-guide-for-every-machine-learning-person-796a6caa3842)作者 [Purva Huilgol](https://medium.com/u/457f7ae0ea51?source=post_page-----afb5e098970f--------------------------------)
# 8.结论
感谢你远道而来!我希望你已经从这篇文章中受益,并且对精确度和召回率有了更好的理解——这是评估分类模型的两个非常重要的指标。请随意保存下面的备忘单,以供将来参考。

图 5:精确度和召回备忘单
还有很多关于精度和召回的内容我还没有在这篇文章中介绍。您应该使用哪个——precision*还是* recall?哪个更好?如何使用*精度和召回来选择模型?多类分类问题怎么办?在我随后的文章中,我希望超越基础来回答这些问题。在我的下一篇文章中再见!**
*我喜欢将数据科学的概念分解成简单的小块,并给出清晰直观的解释。毕竟,这是我发现自己最有效的学习方式。通过分享我如何简化概念,我希望帮助人们降低学习数据科学的门槛。如果你觉得这篇文章有用,请在评论中告诉我!我也欢迎的讨论、问题和建设性的反馈。也可以通过 [LinkedIn](https://www.linkedin.com/in/zeyalt/) 和我联系。祝您愉快!*
# *参考*
1. *迈克·瓦西科夫斯基和薛·。使用特征选择解决小样本类别不平衡问题。 *IEEE 知识与数据工程汇刊*,22(10):1388–1400,2010 年 10 月。ISSN 1041–4347。*
2. *福斯特教务长和汤姆·福塞特。*商业数据科学*。奥莱利媒体公司,第一版,2013 年 12 月。*
# Precision@k:欺诈和销售线索评分模型中被忽视的指标
> 原文:<https://towardsdatascience.com/precision-k-the-overlooked-metric-for-fraud-and-lead-scoring-models-fabad2893c01?source=collection_archive---------11----------------------->
## 如果您的模型产生的数据超出了人们的处理能力,Precision@k 可能是一个简单的解决方案

precision @ k .[图片由作者提供]
当我作为数据科学家在 Shopify 的零售上市团队工作,现在在 Wealthsimple 的欺诈团队工作时,我看到了一个共同的问题:我们建立的模型经常产生太多的输出,让我们的同事无法处理。
例如,在销售中,任何一天都可能有 500 个潜在的销售线索交给销售团队。但是,如果你只有 5 名销售代表,他们在工作日可能只能接触到其中的一小部分。

当您的输出超过您的容量时,P@K 非常有用。【图片由作者提供。]
像[平均精度](https://en.wikipedia.org/wiki/Evaluation_measures_(information_retrieval)#Average_precision)这样的指标——需要计算精度-召回曲线下的面积——是在整个数据集上测量的,并且需要处理所有这些线索,以便随着时间的推移监控指标的性能。
我想强调一下另一个已经存在了几十年并有助于解决这个问题的指标,但人们谈论得还不够多: **Precision@k** 。
# **什么是 Precision@k?**
Precision@k 有几个不同的名字: *P@k、Prec@k、Precision-at-k、在固定等级测量的精度*和 *P(n)* (不是一个错别字)*、*以及在过去 30 年左右的文献中你会发现的其他细微变化。这是一个简单的指标,最好用例子来解释,所以让我们从这里开始。
如果你正在研究了不起的卡尔·萨根,你在搜索引擎中只输入他的姓“萨根”,你可能会发现,在返回的前 10 篇文章中,只有 4 篇与他有关。因为在返回的 10 篇文章中,只有 4 篇是相关的,所以可以用以下公式计算 Precision@10:

萨根匹配卡尔·萨根的 P@10 是 0.4。【图片由作者提供。萨根的头像由美国国家航空航天局提供。]
换句话说, *k* 就是你查看的文章数量,Precision@k 是那些与你相关的文章的百分比。如果你看第二页的结果, *k* 会变成 20。

萨根匹配卡尔·萨根的 P@20 是 0.35。【图片由作者提供。萨根的头像由美国国家航空航天局提供。]
如果你已经熟悉[精度](https://en.wikipedia.org/wiki/Precision_and_recall),这是完全相同的计算,只是你在以某种合理的方式排序后,将你包括的项目限制为第一个 *k* 。
如果你觉得 Precision@k 这个看起来很花哨的术语和你所习惯的精度是一样的,你并不孤单。它在 90 年代的文本检索会议(TREC)上的早期使用并没有将其作为一个值得大写的特殊术语——它仅仅是人们在确定他们的算法有多好时使用的许多测量精确度的方法之一[1]。直到 2000 年代中期,Precision@k 这个简写形式才从一种方便的表格标题方式,变成了一个完全的专有名词。
# Precision@k 什么时候有用?
Precision@k 作为一个不同于平均精度的度量变得有用的地方是在实践中如何使用它。虽然您可以在标记的训练和测试数据集上使用平均精度,但实际上每天测量它要困难得多。如上所述,如果你有 500 条线索,但你的团队只能完成 100 条,你永远也不会知道你在另外 400 条线索上做得如何。但是,你绝对可以在他们完成的工作上计算 Precision@100,并且你可以每天都这样做,不管他们能够处理的量有多大。
回到上面的搜索引擎类比,当你真正考虑它时,搜索引擎和线索评分或欺诈模型之间没有太大的区别。它们都提供了一个项目列表供人们查看,只是我们编写了一个模型而不是一个查询。在所有情况下,我们希望人们看到的结果是相关的,我们不太关心他们永远达不到的结果。
然而,与搜索引擎相比,销售和欺诈应用程序的主要区别在于,顺序通常并不重要。如果您有一个复杂的销售流程,并且您的 4 人销售团队的每个成员每天可以处理 10 个销售线索,那么您需要确保最佳销售线索位于列表的前 40 位。

如果 k=40,您需要确保尽可能多的销售线索(显示为蓝色)低于该限制。【图片由作者提供。]
11 号位的领先优势是否比 45 号位的领先优势更重要。但是,如果好的销售线索发现自己排在前 75 名之外,你应该关心这个问题,所以你的模型应该围绕这个事实进行优化。这将 P@k 与排名指标区分开来,如[标准化贴现累积收益](https://en.wikipedia.org/wiki/Discounted_cumulative_gain#Normalized_DCG),其中排序很重要。
# Precision@k 不是一个独立的指标。但其他人也不是。
虽然 precision 的根源始于 20 世纪 50 年代早期,在命名确定 precision 之前,它被称为“相关性因子”和“相关性比率”,但 Precision@k 在 1992 年随着上面提到的 TREC 的创建而开始真正获得关注[2]。这使得研究人员聚集在一起,竞相从大量样本中检索信息。
在浏览这些早期作品时,我最感兴趣的不是他们对单一指标的选择,而是每个研究人员在并排放置多个指标以获得完整图像时的一致性。例如,下面是 TREC-2 中的一篇论文的表格,作者使用平均精度、P@5、P@30、P@100、 [R-Precision](https://en.wikipedia.org/wiki/Evaluation_measures_(information_retrieval)#R-precision) 和 Recall [3]比较了多个查询性能:

来源:[3]
我认为这是一个很好的教训:Precision@k 不应该*取代*其他指标,如平均精度和召回率,而是应该*补充*它们,让你对你的模型的性能有一个全面的了解。尤其是在输出由人审核而不是完全自动化的情况下。
# 你是怎么实现 P@k 的?
因为 P@K 只比 precision 多一点,对记录的数量有硬性限制,所以它的实现并不复杂。虽然您可以只取数据的前 *k* 行并将结果传递给 Scikit-learn 的`precision_score`函数,但这里有一个更明确的示例,它使用 Numpy 和一些 Scikit-learn 的助手来帮助那些想要详细了解它的人。关于使用一些虚假数据的完整示例,请参见笔记本[这里的](https://github.com/Brideau/whackdata-notebooks/blob/main/precision-at-k/Precision%20at%20K.ipynb)。
# 最后的想法
如果您的模型被能力有限的人使用,Precision@k 是添加到您的工具箱中的一个很好的工具。话虽如此,也没什么特别之处。如果有的话,它应该提醒你不要以表面价值来衡量精度和召回率:它们在某些用例中各有用途,但应该扩展、压缩和修改以适应你的特定情况。像所有的数据科学一样,它归结为让工具为你工作,而不是让工具为你工作。
[1] Harman,D. K. (1993 年)。*第一次文本检索会议(TREC-1)* (第 500 卷,第 207 期)。美国商务部,国家标准与技术研究所。
[2]m .桑德森(2010 年)。*基于测试集的信息检索系统评估*。现在出版公司。
[3]罗伯逊、沃克、琼斯、汉考克-比留和盖特福德(1994 年)。TREC 的 Okapi。 *NIST 特刊 SP* ,21–21。
# 精确度、召回率、准确度。如何选择?
> 原文:<https://towardsdatascience.com/precision-recall-accuracy-how-to-choose-your-data-teacher-b843d69f8f44?source=collection_archive---------44----------------------->
## 让我们看看什么时候使用最常见的性能指标

作者图片
当我们不得不处理二元分类问题时,我们通常不得不选择代表我们的模型的概括能力的性能度量。我们没有通用的度量标准,因为它很大程度上取决于我们为之构建模型的问题。
三个非常常见的指标是精确度、召回率和准确度。让我们看看它们是如何工作的。
# 混乱矩阵
当我们处理一个分类问题时,我们可以建立所谓的混淆矩阵。这是一个混合实际值和预测值的交叉表,构建了一个 *NxN* 矩阵,其中 *N* 是类的数量。对于我们的例子,我们可以谈论一个二元分类问题,所以我们有 2 个类。
对于一个完美的模型,我们期望一个对角矩阵。非对角线元素(*假*)是我们的模型犯的错误,所以我们希望它们尽可能少。正确的预测(*真值*)是混淆矩阵中最重要的部分。*正数*是模型预测为 1 的那些记录,而*负数*是模型预测为 0 的那些记录。结合真/假和肯定/否定,我们得到了真肯定,真否定,假肯定,假否定。
真正的积极和真正的消极是已经被我们的模型正确分类的事件。假阳性是假 1,假阴性是假 0。
使用这 4 个数字,我们可以建立许多可以用来评估模型好坏的指标。让我们看一些。
# 准确性
准确性是成功预测占记录总数的比例。

这是最常见的性能指标,因为它让我们清楚地了解我们的模型有多真实。当您赋予 0 和 1 相同的重要性时,这非常有用,但在使用时必须小心,因为如果数据集不平衡,记录数最高的类将在分数中占主导地位,这通常不是一个好主意。非常多的 0 会造成对 1 的偏差,而这种偏差不是我们所需要的。
所以,只有当你有平衡的数据集并且你给 0 和 1 同样的重要性时,才使用准确性。
# 精确
精度是对被我们的模型分类为 1 的事件被正确分类的概率的度量。因此,我们将模型预测为 1 的所有事件(积极事件)计算为真的概率。

当你有一个模型在预测 1 时启动某种业务流程(例如营销活动)时,Precision 非常有用。所以,你希望你的模型在它说 1 的时候尽可能正确,而不要太在意它预测 0 的时候。这就是为什么我们只看到混淆矩阵的第二列,它与等于 1 的预测有关。
精准在营销活动中非常常用,因为营销自动化活动应该在预测用户会成功响应时对用户发起活动。这就是为什么我们需要高精度,也就是我们的模型预测 1 时正确的概率。低精度值会使我们的业务赔钱,因为我们联系的客户对我们的商业报价不感兴趣。
# 回忆
回忆是正确分类的 1 在所有真实 1 中所占的比例。

当你必须对已经发生的事情进行正确的分类时,回忆是非常有用的。例如,为了正确检测欺诈,欺诈检测模型必须具有高召回率。在这种情况下,我们不关心真正的 0,因为我们只对尽可能多地发现真正的 1 感兴趣。所以,我们正在处理混淆矩阵的第二行。
如前所述,召回的常见用途是欺诈检测模型,甚至是对患者的疾病检测。如果有人生病,我们需要发现他们的疾病,避免假阴性。假阴性患者可能会传染,这是不安全的。这就是为什么,当我们必须找出已经发生的事件时,我们需要使用回忆。
# 结论
准确度、精确度和召回率是可以建立在混淆矩阵上的三个度量标准。当我们对正确预测 0 和 1 感兴趣并且我们的数据集足够平衡时,我们可以使用准确性。当我们希望 1 的预测尽可能准确时,我们使用 precision 当我们希望模型尽可能多地发现真实的 1 时,我们使用 recall。为我们的模型选择正确的指标实际上可以增加它的预测能力,并给我们带来巨大的竞争优势。只要我们清楚我们的模型必须做什么,当我们必须选择一个模型时或在[超参数调整](https://www.yourdatateacher.com/2021/05/19/hyperparameter-tuning-grid-search-and-random-search/)阶段,我们可以毫无疑问地选择其中之一。
如果你对性能指标感兴趣,加入我的[在线课程](https://yourdatateacher.teachable.com/p/supervised-machine-learning-with-python)关于 Python 中的监督机器学习。
*原载于 2021 年 6 月 7 日 https://www.yourdatateacher.com*<https://www.yourdatateacher.com/2021/06/07/precision-recall-accuracy-how-to-choose/>**。**
# 在不平衡数据中,精确回忆曲线比 ROC 更能提供信息:餐巾纸数学等等
> 原文:<https://towardsdatascience.com/precision-recall-curve-is-more-informative-than-roc-in-imbalanced-data-4c95250242f6?source=collection_archive---------3----------------------->
## 当负类更普遍且真-负预测值低时,精确-回忆曲线优于 ROC 曲线

里卡多·阿尔塞在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上的照片
类别不平衡是二进制预测建模空间中的常见问题。这种情况发生在两个阶层之间的分布高度倾斜的时候,通常是负数多于正数。此类预测任务的示例包括罕见疾病识别、欺诈检测或网页检索。对于不平衡数据的二元分类,已经提出了许多解决方案。但是,它们大多与数据重采样或模型训练相关。本文试图把更多的重点放在选择一个合适的最终模型的性能评价方法上。
## 准确性具有误导性
作为不平衡数据的衡量标准,准确性的失败是众所周知的。考虑一个数据集的情况,其比率为 1 个阳性对 100 个阴性。在这项任务中,一个预测所有情况都是负面的模型的准确率为 99%。然而,这个模型是一个虚拟分类器,它总是预测多数类。
## 奥罗克过于乐观了
由于两个原因,受试者操作特征(ROC)曲线和 ROC 曲线下面积(AUROC)已经成为评估不平衡数据的分类模型的最常见的度量。第一,对阶级分布不敏感。AUROC 等于随机选择的阳性病例排在随机选择的阴性病例之上的概率。该指标不关心数据集中有多少阳性和阴性案例。第二,AUROC 是阈值不变的。为了计算度量,我们不必决定区分正类和负类的阈值应该是多少。这意味着即使当两个预测误差成本(即,假阳性和假阴性)不相等时,该度量也与之无关。然而,当负类更普遍但真-负预测值低时,ROC 可以提供对模型性能的过于乐观的测量。例如,考虑具有 10 个阳性和 100,000 个阴性的数据集的情况。我们有两种型号:
* 模型 A:预测 900 个阳性,其中 9 个是真阳性
* 模型 B:预测 90 个阳性,其中 9 个是真阳性
显然,B 型的性能更好。虽然它们都预测相同数量的真阳性,但模型 B 输出的假阳性没有那么多。换句话说,B 型更“精准”。然而,考虑两个模型的 ROC 分析,其测量真阳性率(TPR)与假阳性率(FPR):
* 模型 A: TPR = 9/10 = 0.9,FPR =(900–9)/100,000 = 0.00891
* 模型 B: TPR = 9/10 = 0.9,FPR =(90–9)/100,000 = 0.00081
正如所料,两种型号的 TPR 完全相同。另一方面,由于负面因素的数量在很大程度上支配着正面因素的数量,因此两个模型之间的 FPR 差异(0.00891–0.00081 = 0.0081)在某种意义上可以四舍五入到几乎为零。换句话说,假阳性数量的巨大变化导致 FPR 的微小变化,因此,在真阴性与问题无关的情况下,ROC 无法反映模型 B 的优异性能。例如,在欺诈检测的情况下,调查人员只关心潜在的欺诈交易,他们可能不喜欢来自 ML 系统的通知说交易是正常和安全的(废话!).
## 精确召回曲线更能提供信息
相比之下,精确召回(PR)曲线是专门为检测罕见事件而定制的,并且是当正面类别比负面类别更令人感兴趣时应该使用的度量。因为精度和召回率不考虑真阴性,所以 PR 曲线不受数据不平衡的影响。回到上面的例子:
* 模型 A:召回率= TPR = 0.9,精确度= 9/900 = 0.01
* 模型 B:召回率= TPR = 0.9,精确度= 9/90 = 0.1
显然,与上面的 ROC 分析相比,PR 分析提供了更多的信息。
请注意,虽然 ROC 的随机基线固定为 0.5,但 PR 曲线的随机基线由正类别患病率决定,即 P / (P + N)。例如,对于平衡的等级分布,我们的基线 PR 为 0.5,但是对于不平衡的等级分布,基线 PR 为 0.09,其中每一个正数都有 10 个负数。由于 PR 曲线的随机基线基于阳性类别的患病率而移动,因此将 AUPRC 与其对应的基线进行比较而不是查看其绝对值至关重要。此外,考虑到 PR 曲线对患病率的敏感性,确保测试数据尽可能地反映真实人口的阶级分布(即使这种分布通常是未知的)是一个很好的做法。

图片作者。当 ROC 的随机基线固定在 0.5 时,PR 曲线的随机基线由正类患病率决定。
**如果你喜欢这篇文章,你可能也会喜欢:**
</performance-curve-more-intuitive-than-roc-prc-and-less-assumptive-than-threshold-metrics-391e777da566> </the-wrong-and-right-way-to-approximate-area-under-precision-recall-curve-auprc-8fd9ca409064>
# 不平衡和医疗保健相关数据集的精确召回曲线
> 原文:<https://towardsdatascience.com/precision-recall-curves-for-imbalanced-and-healthcare-related-data-sets-e3bc76575d1e?source=collection_archive---------40----------------------->
## ROC 曲线的无名英雄双胞胎

处理二进制数据的几个分类器的精确召回图。图片作者。
我听说过 ROC 曲线,你听说过 ROC 曲线,我们都听说过 ROC 曲线。(如果你没有听说过 ROC 曲线,你可以在这里阅读它们[。)](/understanding-auc-roc-curve-68b2303cc9c5)
它们是对分类器在二进制数据上表现如何的直观评估,这已经是一个非常松散的定义。我们所说的“分类器的表现如何”是什么意思——用什么标准?为了哪些数据?现在你问了正确的问题。
ROC 曲线有一个很好的语义解释:它们是分类器的真阳性率(TPR)与其假阳性率(FPR)的关系图。它有一些很好的解释,伴随的 AUC 也是如此(再次,在这里阅读),但非常松散地,你可以将这些事情解释为显示为了增加真阳性率,你必须开始让多少假阳性通过门。它们通常看起来像这样:

ROC 曲线由来自维基百科的 [MartinThomas](https://commons.wikimedia.org/wiki/File:Roc-draft-xkcd-style.svg) 创作,由-SA 2.0 授权[知识共享。](https://creativecommons.org/licenses/by-sa/2.0/)
你可以看到一个完全无用的分类器是什么样子,一个完全有用的分类器是什么样子,绘制出来供参考。
所有这些都是为了说明,我们在本文中不是要讨论 ROC 曲线。只是以一种迂回的方式,说 ROC 曲线对于不平衡的数据,或者对于你更关心一个类而不是另一个类的数据来说,不会提供很多信息。因为 ROC 曲线对于评估(和视觉评估)来说是如此美妙!)当你关心*整体准确性*时,分类器的表现如何,它在描述具有非常具体的[回忆一下](/choosing-performance-metrics-61b40819eae1)聚焦工作的分类器方面表现不佳,无论是异常检测,还是检测疾病,或者任何其他你关心发现一个类别比另一个类别多得多的问题。(点击阅读更多关于召回和其他性能指标的信息[。)](/choosing-performance-metrics-61b40819eae1)
想象一个分类器,它从大量患者测量值中预测患者是否患有心脏病。我们更关心这个分类器检测每个真正患有心脏病的人的能力——不让任何人漏网,检测不到对他们健康的威胁——而不是整体模型的准确性。我们可以承认该模型过度预测疾病的倾向有些宽容,因为这些人会去做进一步的测试,咨询他们的医生等。,可能会消除顾虑。然而,如果模型预测一个真正生病的病人没有疾病,那就是一个更严重的错误。那个病人未经治疗就回家了。
事实上,正是这样一个分类器存在。毫无疑问,很多人都有。让我们来看看这个例子,心脏病分类器有很好的准确性,但召回次优。

投票集成分类器的 ROC 曲线和混淆矩阵的并排比较,心脏病数据。图片作者。
ROC 曲线看起来相当不错,对吧?肯定比随机的(红色)更接近完美的(绿色)。但是看那边,混乱矩阵,在左下角,标着“4”这意味着,在 35 名真正患有心脏病的人中,有 4 人通过了我们的分类器而未被发现。你有 10%的几率会得心脏病,而这个模型不会发现它。不太好,你不能只从 ROC 曲线上看出来。对于这样的问题,我们想要一个不同的可视化。提示精确回忆曲线。

同一分类器的精度-召回曲线。图片作者。
这个东西更好地描绘了我们在这个问题上的权衡。从左到右,我们增加了模型的召回率,但是上面画的是为了更高的召回率你必须在精确度上做出的权衡;一般来说,我们从左向右移动会失去精确性。完美的分类器用绿色标出,这里你永远不会为了回忆而牺牲精度。随机猜测用红色标绘;它是集合中所有数据的正比率,在本例中略小于 1:1。对于我们的医疗保健应用程序来说,这是一个更有用的工具,我们可以尝试在不使模型的精度成为完全垃圾的情况下,找到我们可以提高回忆的程度。看这个,看起来最好的地方是 90-95%的召回范围:很好地专注于捕捉患有疾病的患者,同时保持 70-80%的精确度,所以它不只是打印出“你有高于正常的心脏病风险”的每个数据点。
事实上,一个可能更有用的图形,虽然它似乎没有在任何地方的书籍上,是回忆和准确性对各种信心阈值的绘图。

根据模型概率阈值绘制的召回率和准确度。图片作者。
记住,那些来自 [scikit-learn](https://scikit-learn.org/stable/auto_examples/classification/plot_classifier_comparison.html) 的分类器,不仅通过`model.predict()` (0 无心脏病,或 1 心脏病)输出一个类别预测,还可以输出预测概率(0.5 概率心脏病,0.38 概率心脏病,0.79 概率心脏病等。)通过`model.predict_proba()`。这对我们这些应用来说是个好消息。我们可以对模型进行微调,以获得更高的召回分数。让模型输出一个概率,你来选阈值。任何高于 0.2,或 0.4,或 0.6 机会心脏病,预测心脏病。这正是上面的图表。如果我们将 0.2 作为预测心脏病的概率阈值,这将如何影响模型的准确性和召回率。
对于这个问题,我想保持尽可能高的召回率,所以我画了一条 95%的线,以确保我选择的召回率接近或高于这条线。我还想尽可能保持模型的准确性,这样就不仅仅是笼统地预测心脏病了。最佳时机看起来发生在大约 0.35 的概率阈值:召回率接近 95%的目标,准确率也达到了相对较高的 85%左右。选择 0.35 作为我的概率阈值允许我在不完全损失准确性的情况下最大化回忆。

混淆矩阵显示回忆能力提高。图片作者。
召回率高达 33/35,或者说我们的分类器只有 5%的机会漏掉心脏病患者,比之前的 10%有了很大的提高。
* 本文所用数据:【https://www.kaggle.com/ronitf/heart-disease-uci
* 关于 ROC 和 AUC 的更多信息:[https://medium.com/r/?URL = https % 3A % 2F % 2 ftowardsdata science . com % 2 funder standing-AUC-roc-curve-68b 2303 cc9 C5](/understanding-auc-roc-curve-68b2303cc9c5)
* 关于召回率、精确度和其他性能指标的更多信息:[https://medium.com/r/?URL = https % 3A % 2F % 2 ftowardsdata science . com % 2f 选择-性能-指标-61b 40819 ea1](/choosing-performance-metrics-61b40819eae1)
* 有关精确召回曲线的更多信息:[https://machine learning mastery . com/roc-curves-and-precision-recall-curves-for-class ification-in-python/](https://machinelearningmastery.com/roc-curves-and-precision-recall-curves-for-classification-in-python/)
# 精确召回曲线:如何快速评估机器学习模型
> 原文:<https://towardsdatascience.com/precision-recall-curves-how-to-easily-evaluate-machine-learning-models-in-no-time-435b3dd8939b?source=collection_archive---------30----------------------->
## 包括 Python 中的例子。
精确回忆曲线是一种很好的方式来形象化你的模型如何预测正类。您将深入了解它,并通过本文中的实例进行实践。

Clem Onojeghuo 在 [Unsplash](https://unsplash.com/s/photos/abstract?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 拍摄的照片
顾名思义,您可以使用精度-召回曲线来可视化精度和召回之间的关系。对于不同的概率阈值,这种关系是可视化的,主要是在几个不同的模型之间。
完美的模型显示在点(1,1)处,表示精确度和召回率的完美分数。你通常会得到一个模型,这个模型向提到的那一点低头,但并不完全在那里。
下面是这篇文章的内容:
* 回顾混淆矩阵、精确度和回忆
* 数据集加载和准备
* 比较精度-召回曲线
* 结论
# 回顾混淆矩阵、精确度和回忆
在深入研究精确度、回忆和它们的关系之前,让我们快速回顾一下混淆矩阵。这是它最通用的版本:

图片 1 —混淆矩阵(图片由作者提供)
这很好,但是让我们通过输入实际值来使它不那么抽象:

图 2——带有真实数据的混淆矩阵(图片由作者提供)
你可以从这里计算几十个不同的指标,精度和召回是其中的两个。
# 精确
精度是显示正确正面预测数量的一个度量。它的计算方法是真阳性的数量除以真阳性和假阳性的总和:

图 3 —精确公式(作者提供的图片)
需要澄清两个术语:
* **真阳性**——阳性并被归类为阳性的实例(好酒被归类为好酒)
* **假阳性** —阴性但被归类为阳性的实例(劣酒被归类为好酒)
现在,您可以根据*图 2* 中所示的混淆矩阵轻松计算精确度分数。程序如下:

图片 4 —精度计算(图片由作者提供)
对于精确度和召回率,该值可以在 0 和 1 之间(越高越好),所以 0.84 不算太差。
高精度值意味着您的模型不会产生大量误报。
# 回忆
召回是许多分类问题最有用的度量。它报告所有正类预测中对正类的正确预测数。可以用下面的公式计算:

图 5 —回忆公式(作者提供的图片)
需要澄清两个术语:
* **真阳性**——一个阳性并被归类为阳性的实例(好酒被归类为好酒)
* **假阴性**——阳性但被归类为阴性(好酒被归类为坏酒)的实例
当然,给葡萄酒分类完全是一种乐趣和游戏,但错误分类的代价可以用人命来表达:**一个病人患了癌症,但医生说他没有**。与葡萄酒的原理相同,但价格更高。
你可以用上面提到的公式计算召回分数。这里有一个完整的演练:

图 6 —召回计算(作者图片)
正如精度一样,召回率也介于 0 和 1 之间(越高越好)。0.61 没那么厉害。
低召回值意味着你的模型会产生很多假阴性。
您现在知道了这两个指标是如何独立工作的。接下来让我们将它们连接到一个单独的可视化。
# 数据集加载和准备
你将使用[白葡萄酒质量数据集](https://www.kaggle.com/piyushagni5/white-wine-quality)进行实践部分。下面是如何用 Python 加载它:
下面是前几行的样子:

图 7 —白葡萄酒数据集标题(图片由作者提供)
正如您在`quality`栏中看到的,这不是一个二进制分类问题——因此您将把它变成一个二进制分类问题。假设如果`quality`为 7 或以上,则葡萄酒为*好*,否则为*差*:
接下来,让我们可视化目标变量分布。代码如下:
这是视觉效果:

图 8 —目标变量的类别分布(作者图片)
大约 4:1 的比率,表明目标变量存在偏差。还有很多劣质酒,这意味着模型将学习更好地对劣质酒进行分类。您可以使用过采样/欠采样技术来解决这个问题,但这超出了今天的范围。
接下来,您可以进行训练/测试分割:
就是这样!接下来,您将训练几个模型并可视化精度召回曲线。
# 比较精度-召回曲线
下面的代码片段向您展示了如何训练逻辑回归、决策树、随机森林和极端梯度推进模型。它还向您展示了如何获取正类的概率:
接下来,您可以获得每个模型的精度、召回率和 AUC(曲线下面积)的值。唯一的要求是将*好的*和*坏的*类名分别重新映射为 1 和 0:
最后,您可以可视化精确召回曲线:
下面是相应的可视化:

图 9-不同机器学习模型的精确召回曲线(图片由作者提供)
如您所见,没有一条曲线延伸到(1,1)点,但这是意料之中的。AUC 值是比较不同模型的优秀指标(越高越好)。随机森林算法在这个数据集上表现最好,AUC 得分为 0.83。
# 结论
总的来说,任何时候你想要可视化假阳性和假阴性之间的权衡,你都应该可视化精确召回曲线。大量的假阳性导致低精度,大量的假阴性导致低召回率。
您应该以高精度和高召回率的模型为目标,但在现实中,一个指标更重要,因此您可以随时为此进行优化。优化后,相应调整分类阈值。
你选择模特的方法是什么?在评论区告诉我。
# 了解更多信息
* [机器学习的三大分类指标——一劳永逸地消除准确性](/top-3-classification-machine-learning-metrics-ditch-accuracy-once-and-for-all-923beab025a4)
* [ROC 和 AUC——如何快速评估机器学习模型](/roc-and-auc-how-to-evaluate-machine-learning-models-in-no-time-fb2304c83a7f)
*喜欢这篇文章吗?成为* [*中等会员*](https://medium.com/@radecicdario/membership) *继续无限制学习。如果你使用下面的链接,我会收到你的一部分会员费,不需要你额外付费。*
<https://medium.com/@radecicdario/membership>
*原载于 2021 年 1 月 4 日 https://betterdatascience.com*<https://betterdatascience.com/precision-recall-curves/>**。**
# 精确与回忆:由黑衣人解释
> 原文:<https://towardsdatascience.com/precision-recall-explained-by-men-in-black-c821a688707b?source=collection_archive---------42----------------------->

迭戈·马林在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上的照片
## 一个你不会忘记的解释(即使你被神经错乱了!)
> D 记者:所有表达的观点都是我个人的。
我不知道你怎么想,但是每次我碰到精确和回忆的概念,我完全理解它们…然后,突然第二天我很难解释它们。它从我的记忆中被抹去了,就好像我接受了一次<https://meninblack.fandom.com/wiki/Neuralyzer>**!**
*因此,[受到威尔·史密斯饰演的角色通过射杀一个小女孩而通过 MIB 考试的场景的启发](https://youtu.be/THe_hlNE3yI?t=207),我想出了一个简单易懂的例子,可以帮助我理解和记住精确和回忆的概念。请继续阅读!*
***内容***
*0.[运行实例](#b9a3)*
1. *[回忆](#db82)1.1
1.2[🥱boring 定义](#f465)1.2[👽有趣的(上下文)定义](#93d7)
1.3 [📈高回忆是什么意思?](#98d5)
1.4 [📉低回忆是什么意思?](#69b1)
1.5 [💵一个让 c̶e̶n̶t̶s̶有意义的真实世界的例子](#3c1f)*
2. *[精度](#c275)
2.1 [🥱Boring 定义](#0476)2.2[👽有趣的(上下文)定义](#e42c)
2.3 [📈高精度是什么意思?](#2986)2.4
2.4[📉精度低是什么意思?](#4008)
2.5 [💵一个让 c̶e̶n̶t̶s̶有意义的真实世界的例子](#02e8)*
3. *[F1 得分](#4ee8)*
4. *为什么不把事情简单化,只使用准确性呢?*
5. *[最终要点](#a08f)*
# *0.运行示例*
*你是黑衣人(MIB)的特工,这是一个负责保护人类免受伪装成人类的外星人袭击的秘密机构。你收到一个提示,万圣节派对被一些外星人渗透了。你的任务,你应该选择接受(哎呀错电影!),是为了识别和捕捉这些伪装的外星人。*
**
*卡肖恩·埃尔南德斯在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上的照片*
*用机器学习的术语来说,这是一个外星人识别/分类问题:给定一个真实人类和伪装成人类的外星人的数据集,你想要识别外星人。*
*你和你的同事前往派对,抓住一些你认为是外星人的人。你确定了一些正确的和一些错误的!现在,是时候评估你使用回忆和精确度识别伪装成人类的外星人的能力了。*
# *1.回忆*
**
*照片由[米里亚姆·埃斯帕西奥](https://unsplash.com/@miriamespacio?utm_source=medium&utm_medium=referral)在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 拍摄*
> *在所有伪装成人类的外星人中,你正确识别了多少?*
## *1.1 🥱Boring 定义*
**
*外星人识别问题的混淆矩阵*
**
## *1.2 👽有趣的(上下文)定义*
*当你冲进队伍,决定谁是外星人,谁是人类的时候,你正确地识别了一些外星人,却漏掉了一些,因为你错误地认为他们是人类。回忆是衡量你在所有人当中正确识别外星人的能力。从某种意义上来说,回忆也是对你在聚会上没有留下任何外星人的一种衡量。*
## *1.3 📈高回忆是什么意思?*
*高召回率意味着很少有伪装的外星人被你误认为是人类的情况。*
*不好的一面是,这也意味着你把太多的人判断为伪装的外星人。你可以将聚会上的每个人都识别为外星人,你的回忆将是满分(你没有假阴性,因为每个人都被认为是‘阳性’病例!).所以,可能有很多你抓到的真实人类可能不太乐意被不必要的审问。然而,如果你的首要任务是捕捉尽可能多的真实的外星人,而你又不在乎错误地捕捉一些真实的人类,那么回忆就是你的衡量标准。嘿,在一天结束的时候,人类可能会被激怒,但他们是安全的!*
## *1.4 📉低回忆是什么意思?*
*相反,低回忆率意味着你不擅长从所有真实的外星人中辨别出外星人。你也许应该接受更多的训练!*
## *1.5 💵一个让 c̶e̶n̶t̶s̶有意义的真实世界的例子*
*在在线交易领域,您可能希望欺诈检测场景的高召回率。您可能会错误地将一些交易标记为欺诈性交易,但是通过良好的回忆,您可以更加确定您已经设法捕获了大部分欺诈性交易。你的一些客户可能会因为他们的交易被认为是欺诈性的而感到有点沮丧,但是你的客户/公司不太容易遭受不公平的金钱损失。*
# *2.精确*
**
*奥利弗·布赫曼在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍摄的照片*
> *在所有你认为是外星人的人中,有多少是伪装成人类的外星人?*
## *2.1 🥱Boring 定义*
**
*外星人识别问题的混淆矩阵*
**
## *2.2 👽有趣的(上下文)定义*
*当你识别并捕获了一些自认为是外星人的人时,你捕获了一些外星人和一些无辜的人类。**精确度是一个衡量在你认为是外星人的人中,有多少人实际上是外星人的指标。从某种意义上来说,精确度也是衡量你不把真实的人误认为外星人的能力有多强的一个标准。***
## *2.3 📈高精度是什么意思?*
*高精度意味着很少会有真正的人被错误地识别为外星人。*
*可能你只识别并抓住了一个你认为是外星人的人,而这个人实际上是一个伪装的外星人。瞧啊。从数字上来说,你的精确度是完美的。不好的一面是,你可能会让很多外星人乔装打扮潜伏在派对上。然而,让我们不要忘记 MIB 是一个秘密机构,你不希望错误地逮捕一个真正的人,并危及 MIB 的隐蔽性或外星人存在并伪装潜伏在我们中间的事实的隐蔽性!在这种情况下,精确度是正确的衡量标准。*
## *2.4 📉精度低是什么意思?*
*相反,低精度意味着你可能已经捕获了太多认为自己是外星人的真实人类。在他们向世界揭露外星人和 MIB 的存在之前,你别无选择,只能用神经分析仪清除他们的记忆!*
## *2.5 💵一个让 c̶e̶n̶t̶s̶有意义的真实世界的例子*
*在银行领域,识别贷款违约者的问题是一个你想要高精度的问题。如果你错误地将太多的客户认定为贷款违约者,你的银行将无法贷款给足够多的人。银行从借款人应计和支付的利息中获得的收入将会缩水,这对银行的底线不利!*
# *3.F1 分数*
**
*由古斯塔沃·托里斯在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍摄的照片*
> *我既关心精确度又关心召回率,所以我想在两者之间取得平衡!—你在 MIB 的老板*
*现在是年底了。这也意味着,是时候和你的老板进行年终绩效评估了。为了公平地分配年终奖金,你的老板需要根据代理商的总体目标来比较所有 MIB 代理商的绩效。该机构的目标是双重的 MIB 需要成功捕获外星人,但它也需要保持其保密性,让世界幸福地不知道外星人的存在。你的老板应该使用精确还是回忆?*
*一个可能的解决方案是使用 F1 分数——它有助于在精确度和召回率之间取得平衡。*
*F1 得分是精确度和召回率的[调和平均值](https://www.investopedia.com/terms/h/harmonicaverage.asp#:~:text=The%20harmonic%20mean%20is%20a,1%204%20)%20%3D%203%201%20.):*
**
# *4.为什么不简单明了地使用准确性呢?*
**
*[钳工](https://unsplash.com/@benchaccounting?utm_source=medium&utm_medium=referral)在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上拍照*
*我听到了。我也喜欢机器学习的简单性,但某些问题是这样的,使用准确性作为衡量分类器性能的指标可能是不明智的。当然,我说的是不平衡类的问题,即我们的数据以不平衡的方式分布在各个类中。假设前面提到的万圣节聚会有 100 人,其中只有 5 人是伪装的外星人。在这种情况下,如果你将所有 100 个人都识别为人类,你会有 95%的准确率,但这不太可能在年底给你带来好的奖金,因为你的 F1 分数高的同事会帮助 MIB 实现其核心目标。因此,考虑到精度,召回率和 F1 分数是衡量分类性能的一个可行的替代方法。*
# *5.最终外卖*
**
*埃里克·麦克林在 [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral) 上的照片*
* *当类别不平衡时,精确度、召回率和 F1 分数是比精确度更明智的选择。*
* *当您更关心发现尽可能多的真实案例时,请使用回忆。*
* *当您更关心您确定为阳性的案例是否正确时,请使用 precision。*
# *认识作者:*
**
*艾西瓦娅·普拉巴特*
*嗨!我是艾西。我名字的前两个字母是“AI”,AI 和机器学习是我热爱的。我目前是新加坡的一名高级数据科学家和机器学习解决方案架构师。你可以通过 [LinkedIn](https://www.linkedin.com/in/aishwaryaprabhat) 联系我。*
# 像国王一样用 Excel 预测和推荐
> 原文:<https://towardsdatascience.com/predict-and-recommend-with-excel-like-a-king-d6b4065b129f?source=collection_archive---------44----------------------->
## 在 Excel 中方便地修改 LSTM 和先验参数

卢克·坦尼斯在 [Unsplash](https://unsplash.com/s/photos/king?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 拍摄的照片
## 动机:
我们已经在之前的帖子中学习了如何在 Jupyter Notebook 中编写[市场篮子](/market-basket-analysis-using-pysparks-fpgrowth-55c37ebd95c0)分析、时间序列[预测](/prophet-in-a-loop-a875516ef2f9)和[推荐引擎](/recommender-systems-item-customer-collaborative-filtering-ff0c8f41ae8a)。我们还知道如何 [Pyinstall](/excel-python-app-for-non-pythonists-68b4fec0fabf) 这些 Python 脚本到 exe 文件中,并与 Excel [wings](/python-excel-integration-with-xlwings-f9bf9d1332ea) 连接。本文证明了这种 Excel 独立应用程序的好处,它将客户项目销售的预测、推荐和购物篮分析都集中在一个地方。
接收方不需要安装 Python,也不需要任何深度学习的编码知识。用户只需将数据输入 Excel 表格,轻松调整算法设置,点击一个按钮即可直接在 Excel 中接收结果。之后,用户可以修改参数并重新运行,以获得舒适和直接的结果基准。这不仅对于初学者来说是理想的,对于那些想要摆弄这些算法参数并与他们的非 Pythonist 同事分享结果的高级实践者来说也是理想的。
P redReX:像王者一样用 Excel 预测推荐!
## 解决方案:
首先下载 [PredReX](https://github.com/DAR-DatenanalyseRehberg/PredReX) Excel 模板和[三个 Exe 文件](https://drive.google.com/drive/folders/1uG6j6nAzCBu_FxgB9gGZktt0bLbErgY5)并保存在一个文件夹中。

PredReX Excel 和 exe 文件。
现在打开 PredReX,看看输入表。对于这个演示,我们将坚持使用已经包含的数据,但是当然您也可以用您自己的数据替换它们。只需注意保持列标题不变,不要修改文件名 PredReX.xlsm。

输入数据
所以我们得到的是每个销售日的销售额。该值可以是售出的物品数量或销售额,例如以美元计。客户代码 25 代表一个特定的客户,而销售项目 806 代表一个特定的销售项目(项目 66 代表另一个独特的项目,以此类推)。销售交易 id 是一个账单号,在进行购物篮分析时非常重要。
现在让我们转到有趣的部分,选项卡“参数”。

所有这些参数都是可调的。
本文将不解释长短期记忆(LSTM)背后的理论,这是一种用于深度学习领域的特殊类型的递归神经网络。另外,像购物篮分析和推荐这样的数据挖掘技术已经在其他重要的帖子中大量出现。但我对 PredReX 的想法是为您提供一种务实地尝试这些参数的相互作用的方法,以获得这些算法背后的实际直觉。
**预测**
有很多关于 LSTM 的参数,我们可以用来预测未来的销售。

输入不同的值或从下拉列表中选择。
请注意,无论您的预测是基于每日、每周、每月还是每年的水平,为了得到有意义的结果,这都应该与重采样大小相匹配。历元的数量定义了学习算法在数据集上工作的频率。批次大小是模型更新前处理的样本数,必须> =1,而时期数可以设置为 1 到无穷大之间的整数值。你可以选择 MSE,MAE,MAPE 和 MSLE 作为损失函数。对于优化者,你可以选择亚当、阿达马克斯或 Sgd。

享受调整 LSTM 提供的不同超参数的乐趣。
一旦点击“预测”按钮,Lstm.exe 将被执行,预测将被保存到输出表中。您可以检查这两种评估:

实际值用蓝色突出显示,预测值用红色显示。
..以及损失函数:

有 100 个纪元的例子。
在你调查了你的结果后,你现在可能想预测每周的水平?继续从适当的下拉列表中选择,重新运行预测并比较您的结果。请记住,没有现成的最佳总体超参数设置,但这确实取决于数据。PredReX 试图帮助你对此有一个直觉。用自己的数据试一下就好了。
**推荐**
下一步,我们希望在我们的销售交易 id 中找到模式。进行购物篮分析时,您可以更改最小支持度和阈值,以及项目集的长度。至于指标,您可以选择置信度或提升度..

试验这四个先验参数。
..要查看它如何与您的数据交互:

对项集的支持以逗号分隔。

支持、信心和提升可能是市场篮子分析中最常用的指标。
从购物篮中,我们了解了经常一起购买的商品。现在,我们将继续向前,希望主动向客户推荐他们尚未购买但应该会非常感兴趣的商品。该建议将基于余弦相似性或曼哈顿/欧几里得距离,您可以从下拉列表中进行选择。

相似性或距离将显示在“输出 Sim”选项卡中。
使用“top amount ”,您可以输入要推荐的热门商品数量。因此,如果您像上面的屏幕截图中那样选择 3,输出将如下所示:

前 3 名意味着将向每个客户推荐前 3 个项目,其中前 1 个项目是最受推荐的项目,前 2 个项目是第二受推荐的项目,依此类推。为了更好的可读性,I 代表 item。
## 结论
恭喜你,你可以挑战许多参数来微调你的预测,并在 Excel 中轻松推荐商品。利用 LSTM,我们回顾了过去的销售情况,试图对未来的销售做出展望。由于有了先验知识,我们可以更好地理解哪些商品经常一起购买。并且使用客户-项目相似性,我们尝试推荐客户应该高度感兴趣的项目。PredReX 搭建了一座从过去到未来的桥梁,同时让您以动手操作的方式微调参数设置。我希望这能帮助你更好地理解这些算法。
非常感谢您的阅读!希望这篇文章对你有帮助。请随时在 [LinkedIn](https://de.linkedin.com/in/jesko-rehberg-40653883) 、 [Twitter](https://twitter.com/DAR_Analytics) 或[工作室](https://jesko-rehberg.medium.com/virtual-reality-vr-for-education-a532aa5b6272)与我联系。
<https://jesko-rehberg.medium.com/membership>
# 使用 PyCaret 预测客户流失(正确的方式)
> 原文:<https://towardsdatascience.com/predict-customer-churn-the-right-way-using-pycaret-8ba6541608ac?source=collection_archive---------0----------------------->
## 关于如何使用 PyCaret 正确预测客户流失的分步指南,实际上优化了业务目标并提高了投资回报率

使用 PyCaret 预测客户流失(正确的方式)——作者图片
# **简介**
对于采用基于订阅的业务模式的公司来说,客户维系是主要的 KPI 之一。竞争非常激烈,尤其是在 SaaS 市场,消费者可以自由地从大量供应商中进行选择。一次糟糕的体验,客户可能会转向竞争对手,导致客户流失。
# **什么是客户流失?**
客户流失率是在一定时间内停止使用贵公司产品或服务的客户的百分比。计算客户流失率的方法之一是用给定时间间隔内流失的客户数量除以该期间开始时的活跃客户数量。例如,如果你有 1000 个客户,上个月失去了 50 个,那么你的月流失率是 5%。
预测客户流失是一个具有挑战性但极其重要的商业问题,尤其是在客户获取成本较高的行业,如技术、电信、金融等。预测特定客户面临高风险的能力,同时还有时间做些什么,对公司来说是一个巨大的额外潜在收入来源。
# 客户流失机器学习模型在实践中是如何使用的?
客户流失预测模型的主要目标是通过主动与客户接触来留住流失风险最高的客户。例如:提供礼品券或任何促销价格,并将其锁定一两年,以延长其对公司的终身价值。
这里有两个宽泛的概念需要理解:
* 我们想要一个客户流失预测模型来提前预测流失(比如提前一个月、提前三个月、甚至提前六个月——这完全取决于用例)。这意味着你必须非常小心截止日期,即你不应该使用截止日期之后的任何信息作为机器学习模型中的特征,否则这将是泄漏。截止日期之前的时期被称为**事件。**
* 通常对于客户流失预测,您需要做一些工作来创建一个 ***目标列*** ,它通常不会以您想要的形式出现。例如,您希望预测客户是否会在下一季度内流失,因此您将遍历截至事件截止日期的所有活跃客户,并检查他们是否会在下一季度离开公司(1 表示是,0 表示否)。这种情况下的季度称为**性能窗口**。

如何创建客户流失数据集—按作者分类的图像
# 客户流失模型工作流程
现在,您已经了解了数据是如何来源的以及流失目标是如何创建的(这是问题中最具挑战性的部分之一),让我们讨论一下这种机器学习模型将如何在业务中使用。从左至右阅读下图:
* 基于客户流失历史(X 特征的事件周期和目标变量的性能窗口)训练模型。
* 每个月,活跃客户群都被传递到**机器学习预测模型**上,以返回每个客户的流失概率(在商业行话中,这有时被称为流失分数)。
* 该列表将从最高概率值到最低概率值(或他们所说的分数)排序,客户保留团队将开始与客户接触以阻止客户流失,通常通过提供某种促销或礼品卡来锁定未来几年。
* 流失率非常低的客户(或者基本上模型预测没有流失率的客户)是快乐的客户。不对它们采取任何操作。

客户流失模型工作流程-按作者分类的图片
# 让我们从实际例子开始
在这一部分中,我将演示机器学习模型训练和选择、超参数调整、结果分析和解释的完整端到端工作流。我还将讨论您可以优化的指标,以及为什么 AUC、准确性、召回率等传统指标可能不适合客户流失模型。我将使用[py caret](https://www.pycaret.org)——一个开源、低代码的机器学习库来执行这个实验。本教程假设您对 PyCaret 有基本的了解。
# PyCaret
[PyCaret](https://www.pycaret.org/) 是一个开源、低代码的机器学习库和端到端的模型管理工具,内置于 Python 中,用于自动化机器学习工作流。PyCaret 以其易用性、简单性以及快速高效地构建和部署端到端机器学习管道的能力而闻名。要了解更多关于 PyCaret 的信息,请查看他们的 [GitHub](https://www.github.com/pycaret/pycaret) 。

PyCaret 的特点—作者图片
# 安装 PyCaret
# install pycaret pip install pycaret
# 👉资料组
对于本教程,我使用来自 Kaggle 的[电信客户流失](https://www.kaggle.com/blastchar/telco-customer-churn)数据集。数据集已经包含我们可以按原样使用的目标列。您可以直接从这个 [GitHub](https://raw.githubusercontent.com/srees1988/predict-churn-py/main/customer_churn_data.csv) 链接中读取这个数据集。(*大喊到 sres 1988*
# import libraries
import pandas as pd
import numpy as np# read csv data data = pd.read_csv('https://raw.githubusercontent.com/srees1988/predict-churn-py/main/customer_churn_data.csv')

样本数据集-按作者分类的图像
# **👉探索性数据分析**
# check data types data.dtypes

数据类型-按作者分类的图像
注意到`TotalCharges`是一个`object`类型,而不是`float64.`,经过调查,我发现这个列中有一些空格,这导致 Python 将数据类型强制为`object`。要解决这个问题,我们必须在改变数据类型之前修剪空白。
# replace blanks with np.nan
data['TotalCharges'] = data['TotalCharges'].replace(' ', np.nan)# convert to float64
data['TotalCharges'] = data['TotalCharges'].astype('float64')
从直觉上来说,合同类型、期限(客户停留的时间长度)和定价计划是客户流失或保留的非常重要的信息。让我们探讨一下这种关系:
[https://gist . github . com/moe zali 1/2624 c 9 a5 EAF 78d 9 a 7 FFA 1 b 97195 a 4812](https://gist.github.com/moezali1/2624c9a5eaf78d9a7ffa1b97195a4812)

按任期、费用和合同类型划分的客户流失(图片由作者提供)
请注意,大多数流失可以在“逐月”合同中看到。当然有道理。此外,我可以看到,随着任期的增加和总费用的增加,与高任期和高费用的客户相比,高任期和低费用的客户的可能性较小。
**缺失值**
# check missing values data.isnull().sum()

缺失值-按作者分类的图像
请注意,因为我们用`np.nan`替换了空白值,所以现在`TotalCharges`中有 11 行缺少值。没问题——我会让 PyCaret 自动估算它。
# **👉数据准备**
对于 PyCaret 中的所有模块来说,`setup`是在 PyCaret 中执行的任何机器学习实验中的第一个也是唯一一个强制步骤。该功能负责训练模型之前所需的所有数据准备。除了执行一些基本的默认处理任务,PyCaret 还提供了一系列预处理功能。要了解 PyCaret 中所有预处理功能的更多信息,您可以查看这个[链接](https://pycaret.org/preprocessing/)。
# init setup
from pycaret.classification import *
s = setup(data, target = 'Churn', ignore_features = ['customerID'])

pycaret.classification 中的设置函数—按作者分类的图像
每当在 PyCaret 中初始化`setup`函数时,它都会分析数据集并推断所有输入要素的数据类型。在这种情况下,你可以看到除了`tenure` `MonthlyCharges`和`TotalCharges`之外,其他都是绝对的,这是正确的,你现在可以按回车键继续。如果数据类型推断不正确(这有时会发生),您可以使用`numeric_feature`和`categorical_feature`来覆盖数据类型。
另外,请注意,我已经在`setup`函数中传递了`ignore_features = ['customerID']`,因此在训练模型时不会考虑它。这样做的好处是 PyCaret 不会从数据集中删除该列,它只是在模型训练的幕后忽略它。因此,当您在最后生成预测时,您不需要担心自己将 IDs 连接回来。

设置的输出-为显示而截断-作者提供的图像
# 👉模型训练和选择
现在数据准备工作已经完成,让我们使用`compare_models`功能开始培训过程。此函数训练模型库中可用的所有算法,并使用交叉验证评估多个性能指标。
# compare all models
best_model = compare_models(sort='AUC')

compare_models 的输出—按作者分类的图像
基于 **AUC** 的最佳模型是`Gradient Boosting Classifier` 。使用 10 倍交叉验证的 AUC 为 0.8472。
# print best_model parameters
print(best_model)

最佳模型参数—作者图片
# **超参数调谐**
您可以使用 PyCaret 中的`tune_model`函数来自动调整模型的超参数。
# tune best model
tuned_best_model = tune_model(best_model)

tune_model 结果—作者提供的图片
请注意,AUC 从`0.8472`到`0.8478`略有增加。
# 模型分析
# AUC Plot
plot_model(tuned_best_model, plot = 'auc')

AUC 图—作者提供的图像
# Feature Importance Plot
plot_model(tuned_gbc, plot = 'feature')

特征重要性图—按作者分类的图像
# Confusion Matrix
plot_model(tuned_best_model, plot = 'confusion_matrix')

混淆矩阵梯度推进分类器—图片由作者提供
这个混淆矩阵在测试集上,测试集包括我们 30%的数据(2,113 行),我们有 309 个 ***真阳性*** (15%) —这些是我们将能够延长生命周期价值的客户。如果我们没有预测到,那么就没有干预的机会。
我们也有 138 (7%) ***误报*** 我们将会损失金钱,因为提供给这些客户的促销只是额外的成本。
1388 个(66%)是真阴性(好客户),278 个(13%)是 ***假阴性*** (这是错过的机会)。
到目前为止,我们已经训练了多个模型,以选择给出最高 AUC 的最佳模型,然后调整最佳模型的超参数,以在 AUC 方面获得更多一点的性能。然而,最好的 AUC 不一定转化为最好的商业模式。
在一个流失模型中,通常 ***真阳性*** 的回报与 ***假阳性*** 的成本大相径庭。让我们使用以下假设:
* 将向所有被识别为流失的客户提供 1000 美元的代金券(真阳性+假阳性);
* 如果我们能够阻止客户流失,我们将获得 5000 美元的客户终身价值。
使用这些假设和上面的混淆矩阵,我们可以计算该模型的美元影响:

模型对 2,113 名客户的影响——图片由作者提供
这是一个很好的模式,但问题是它不是一个商业智能模式。与没有模型相比,它做得非常好,但是我们如何训练和选择一个模型来最大化商业价值。为了实现这一目标,我们必须使用业务指标来训练、选择和优化模型,而不是任何传统的指标,如 AUC 或准确性。
# **👉在 PyCaret** 中添加自定义指标
多亏了 PyCaret,使用`add_metric`函数可以非常容易地实现这一点。
# create a custom function def calculate_profit(y, y_pred):
tp = np.where((y_pred1) & (y1), (5000-1000), 0)
fp = np.where((y_pred1) & (y0), -1000, 0)
return np.sum([tp,fp])# add metric to PyCaret add_metric('profit', 'Profit', calculate_profit)
现在让我们运行`compare_models`看看神奇之处。
# compare all models
best_model = compare_models(sort='Profit')

compare_models 的输出—按作者分类的图像
请注意,这次添加了一个新列`Profit`,令人惊讶的朴素贝叶斯是一个在`AUC`方面相当糟糕的模型,但在利润方面却是最好的模型。让我们看看如何:
# confusion matrix
plot_model(best_model, plot = 'confusion_matrix')

混淆矩阵朴素贝叶斯-作者图片
客户的总数仍然是一样的(测试集中有 2,113 个客户),现在发生变化的是模型如何在误报和漏报上出错。让我们用同样的假设(如上所述),用一些美元价值来反对它:

模型对 2,113 名客户的影响——图片由作者提供
> ***嘭!*** *我们刚刚增加了约 400,000 美元的利润,其 AUC 比最佳型号低 2%。这是怎么发生的?首先,AUC 或任何其他现成的分类指标(*准确度、召回率、精确度、F1、Kappa 等)。*)不是一个商业智能指标,因此它没有考虑风险和回报。添加自定义指标并将其用于模型选择或优化是一个很好的想法和正确的方法。*
我希望你会喜欢 PyCaret 的简单易用。只需几行代码,我们就能够训练多个模型,并选择对业务至关重要的模型。我是一个普通的博客作者,我主要写 PyCaret 及其在现实世界中的用例,如果你想自动收到通知,你可以在 [Medium](https://medium.com/@moez-62905) 、 [LinkedIn](https://www.linkedin.com/in/profile-moez/) 和 [Twitter](https://twitter.com/moezpycaretorg1) 上关注我。

PyCaret —作者图片

PyCaret —作者图片
使用 Python 中的这个轻量级工作流自动化库,您可以实现的目标是无限的。如果你觉得这很有用,请不要忘记给我们 GitHub 库上的⭐️。
想了解更多关于 PyCaret 的信息,请在 LinkedIn 和 Youtube 上关注我们。
加入我们的休闲频道。邀请链接[此处](https://join.slack.com/t/pycaret/shared_invite/zt-p7aaexnl-EqdTfZ9U~mF0CwNcltffHg)。
# 重要链接
[文档](https://pycaret.readthedocs.io/en/latest/installation.html)
[博客](https://medium.com/@moez_62905)
[GitHub](http://www.github.com/pycaret/pycaret)
[stack overflow](https://stackoverflow.com/questions/tagged/pycaret)
[安装 PyCaret](https://pycaret.readthedocs.io/en/latest/installation.html) [笔记本教程](https://pycaret.readthedocs.io/en/latest/tutorials.html) [投稿于 PyCaret](https://pycaret.readthedocs.io/en/latest/contribute.html)
# 更多 PyCaret 相关教程:
</machine-learning-in-alteryx-with-pycaret-fafd52e2d4a> </machine-learning-in-knime-with-pycaret-420346e133e2> </easy-mlops-with-pycaret-mlflow-7fbcbf1e38c6> </write-and-train-your-own-custom-machine-learning-models-using-pycaret-8fa76237374e> [## 使用 PyCaret 编写和训练您自己的自定义机器学习模型
towardsdatascience.com](/write-and-train-your-own-custom-machine-learning-models-using-pycaret-8fa76237374e) </build-with-pycaret-deploy-with-fastapi-333c710dc786> </time-series-anomaly-detection-with-pycaret-706a6e2b2427> </supercharge-your-machine-learning-experiments-with-pycaret-and-gradio-5932c61f80d9> </multiple-time-series-forecasting-with-pycaret-bc0a779a22fe>
# 用神经网络预测客户流失
> 原文:<https://towardsdatascience.com/predict-customer-churn-with-neural-network-1ef8f1a1c6ab?source=collection_archive---------7----------------------->
## 运用多层感知器模型留住你的客户
> “生存还是毁灭,这是个问题…”
>
> -威廉·莎士比亚,哈姆雷特

马修·T·雷德在 [Unsplash](https://unsplash.com/s/visual/b67d5473-01d2-4f00-8b45-2b418476294d?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 上的照片
# 介绍
在现实世界中,数据科学家通常从简单且易于实施的模型开始分析,如线性或逻辑回归。这种方法有各种各样的优势,比如以最小的成本获得数据的意义,并为如何解决业务问题提供思路。
在这篇博文中,我决定从反面开始,应用多层感知器模型(神经网络)来预测客户流失。我认为尝试不同的算法或者至少知道如何以更复杂的方式解决问题是非常有趣和令人兴奋的。
## **客户流失**

圣人弗里德曼在 [Unsplash](https://unsplash.com/s/photos/thinking?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 上的照片
*客户流失*是指客户决定停止使用某家公司的服务、内容或产品。客户流失的原因可能多种多样,最典型的是糟糕的客户服务、没有发现产品或服务的足够价值、缺乏客户忠诚度以及缺乏沟通。
## **为什么预测客户流失很重要?**
留住现有客户比获得新客户成本更低。而且回头客的收入通常高于新客户。在一个竞争激烈的行业,有很多竞争者,获取客户的成本可能会更高。这就是为什么预测客户流失对于企业在他们决定离开之前留住他们变得如此重要。
## 数据
我正在使用电信公司的客户流失数据集,这是我从 [Kaggle](https://www.kaggle.com/blastchar/telco-customer-churn) 下载的。电信公司客户流失数据包含一家虚构的电信公司的信息,该公司为加利福尼亚州的 7043 名客户提供家庭电话和互联网服务。它显示哪些客户已经离开、留下或注册了他们的服务。
# 数据争论
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline

看着这些数据,我们可以说
* 数据类型可能有问题。像 *TotalCharges* 这样的变量可能具有整数或浮点数据类型,而不是对象。
* 将所有布尔值分别从“是”、“否”更改为 1 和 0 会很有用。
# 数据清理和转换
基于所进行的探索性数据分析(EDA),我执行了以下操作来清理和转换数据:
* 分别用 1 和 0 替换了“是”和“否”
* 通过将*总费用列*中的缺失值替换为 Nan 来处理缺失值
* 将*总费用的数据类型改为浮动*
* 删除空值
* 标准化连续变量
* 对分类变量执行一次性编码
Let us replace yes and no by 1 and 0, respectively
df['Churn'] = df['Churn'].apply(lambda x: 1 if x == 'Yes' else 0)
Handle missing values and change the data type for TotalCharges
df['TotalCharges'] = df['TotalCharges'].replace(' ', np.nan).astype(float)
Drop Null values
df.dropna(inplace=True)
df.reset_index(inplace=True)
df.drop(columns=['index'], inplace=True)
Transform continuous variables
df['MonthlyCharges'] = np.log(df['MonthlyCharges'])
df['MonthlyCharges'] = (df['MonthlyCharges'] - df['MonthlyCharges'].mean()) / df['MonthlyCharges'].std()
df['TotalCharges'] = np.log(df['TotalCharges'])
df['TotalCharges'] = (df['TotalCharges'] - df['TotalCharges'].mean()) / df['TotalCharges'].std()
df['tenure'] = (df['tenure'] - df['tenure'].mean()) / df['tenure'].std()
One-hot encoding of categorical variables
Converting boolean variables to a format for further use
df['SeniorCitizen'].loc[df.SeniorCitizen == 1] = 'Yes';
df['SeniorCitizen'].loc[df.SeniorCitizen == 0] = 'No';
df_trans = df[['tenure', 'MonthlyCharges',
'TotalCharges', 'Churn']].copy(deep=True)
for col in list(df.columns):
if col not in ['tenure', 'MonthlyCharges', 'TotalCharges', 'Churn'] and df[col].nunique() < 5:
dummy_vars = pd.get_dummies(df[col])
dummy_vars.columns = [col+ '_' + str(x) for x in dummy_vars.columns]
df_trans = pd.concat([df_trans, dummy_vars], axis=1)
经过数据转换和清理后,数据集就可以用于建模部分了。
# **用于流失预测的人工神经网络**
*人工神经网络模型(ANN)* 是一种受人类大脑如何运作启发的模型,可以被视为“深度学习”名称下的复兴。虽然深度学习在许多机器学习方法中显示出巨大的前景,但深度学习算法通常会针对特定的用例进行非常仔细的调整和定制。
## **多层感知器模型**
*多层感知器模型(MLP)* (又名普通前馈神经网络,或有时只是神经网络)可以被视为线性模型的概括,它执行多个处理阶段来做出决策。
通过线性回归的预测给出如下:

其中 y’是输入特征 x[0]到 x[p]的加权和,由学习系数 w[0]到 w[p]加权。

具有单一隐藏层的多层感知器的插图。穆勒、A. C .、&圭多、S. (2018)的图像鸣谢[2]
查看上面的多层感知器的图示,最左边的层称为输入层,连接线表示学习的系数,右边的节点表示输出,它是输出的加权和,隐藏层表示中间处理步骤,使用加权和组合该步骤以产生最终结果。
有一个技巧使 MLP 比线性回归模型更强大,当非线性函数应用于结果时,如校正非线性(relu)或 tangens 双曲线(tanh),在计算每个隐藏单元的加权和后发生。然后,该函数的结果用于计算输出 y' [2]。
***使用 MLP 的优点:***
* 可以学习数据中的非线性行为
* 能够实时学习模型(在线学习)
***MLP 模式的弊端:***
* 具有隐藏层的模型具有非凸损失函数,该函数具有多个局部最小值。因此,不同的随机权重初始化会产生不同的验证准确度
* 该模型需要调整几个超参数,如隐藏神经元的数量、迭代次数和层数
* 多层感知器对特征缩放很敏感[3]。
# **用 MLP 预测客户流失**
让我们用 MLP 模型来预测客户流失。
import packages
from keras.models import Sequential
from keras.layers import Dense
from sklearn.model_selection import train_test_split
define target variable and features
target = 'Churn'
features = [x for x in list(df_trans.columns) if x != target]
model = Sequential()
model.add(Dense(16, input_dim=len(features), activation='relu'))
model.add(Dense(8, activation='relu'))
model.add(Dense(1, activation='sigmoid'))
compile the model
model.compile(loss='binary_crossentropy',
optimizer='adam', metrics=['accuracy'])
X_train, X_test, y_train, y_test = train_test_split(df_trans[features],
df_trans[target],
test_size=0.2,
random_state=23)
%%time
history = model.fit(X_train, y_train, epochs=50, batch_size=100)
我们使用顺序模型,这是一种层线性堆叠的模型。第一层是输入层,其中 *input_dim* 为特征数,输出单元数为 *16* 。在隐藏层,我们使用 *8* 输出单元。最后,在输出层,我们只有一个输出单元,它返回客户流失的概率。在输入和隐藏层,我们使用了 *relu* 激活函数,而在输出- *sigmoid* 激活函数。
## **模型评估**
让我们使用准确度、精确度和召回率,以及*受试者操作特征(ROC)* 曲线和*曲线下面积(AUC)* 来评估该模型。
from sklearn.metrics import accuracy_score, precision_score,
recall_score, confusion_matrix, roc_curve, auc
train_set_preds = [round(x[0]) for x in model.predict(X_train)]
test_set_preds = [round(x[0]) for x in model.predict(X_test)]
train_preds = [x[0] for x in model.predict(X_train)]
test_preds = [x[0] for x in model.predict(X_test)]
train_fpr, train_tpr, train_thresholds = roc_curve(y_train, train_preds)
test_fpr, test_tpr, test_thresholds = roc_curve(y_test, test_preds)
train_roc_auc = auc(train_fpr, train_tpr)
test_roc_auc = auc(test_fpr, test_tpr)
Let us visualize ROC
plt.figure(figsize=(7, 5), dpi=80)
plt.plot(test_fpr,
test_tpr,
color='tomato',
label='ROC curve for test set (area = %0.4f)' % test_roc_auc)
plt.plot(train_fpr,
train_tpr,
color='dodgerblue',
label='ROC curve for train set (area = %0.4f)' % train_roc_auc)
plt.plot([0, 1], [0, 1], color='gray', lw=1, linestyle='--')
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('False Positive Rate', fontsize=14)
plt.ylabel('True Positive Rate', fontsize=14)
plt.title('ROC Curve', fontsize=16)
plt.legend(loc="lower right")
plt.show()

MLP 模型的 ROC 曲线
测试集的 AUC 约为 0.87,这表明该模型可以很好地预测客户是否会流失。
让我们看看准确度、精确度和召回率。

训练集和测试集的准确性是相似的,这很好,因为它表明我们没有过度拟合或欠拟合模型。*精度*(预测正值)约为 0.69,可见模型对会流失客户的预测有多好。
# **总结**
* 我们训练了多层感知器模型,得到测试集的 AUC = 0.87,这表明该模型可以很好地预测客户是否会流失
* 通常,神经网络需要时间来训练,这在现实世界中是很困难的。因此,最好从像逻辑回归这样的基准模型开始,然后,如果需要高精度来训练其他更复杂的模型
* 另一方面,尝试新的模式,看看它们如何带来新的见解来解决业务问题,总是那么令人兴奋。
感谢您的阅读,请在下面评论您对机器学习/深度学习在客户流失方面的想法。要查看我的更多帖子,请订阅 Medium 和 [LinkedIn](https://www.linkedin.com/in/aigerimshopenova/) 。
[Jupyter 笔记本](https://github.com/aig3rim/Predict_customer_churn_multilayer_perceptron/blob/main/churn_prediction_MLP.ipynb)可以在我的 [GitHub](https://github.com/aig3rim) 上找到。
# 参考
1. 黄耀辉(2019)。市场营销数据科学实践:使用 Python 和 R. Birmingham 通过机器学习改进您的市场营销策略。
2. 米勒,A. C .,&圭多,S. (2018)。Python 机器学习导论:数据科学家指南。塞瓦斯托波尔,加利福尼亚州:奥赖利媒体。
3. 1.17.神经网络模型(受监督的)。(未注明)。2021 年 3 月 7 日检索,来自[https://sci kit-learn . org/stable/modules/neural _ networks _ supervised . html](https://scikit-learn.org/stable/modules/neural_networks_supervised.html)
# 精确预测客户流失
> 原文:<https://towardsdatascience.com/predict-customer-churn-with-precision-56932ae0e5e3?source=collection_archive---------15----------------------->
## 平衡精确度和召回率,实现可操作的保留策略

[奥利维尔·勒莫阿尔](https://stock.adobe.com/contributor/393/olivier-le-moal?load_type=author&prev_url=detail)使用[土坯股票](https://stock.adobe.com/images/customer-churn-rate-concept-client-or-employee-attrition/298817415?prev_url=detail)许可证
准确性,请退居二线。我们今天将推广精确和召回。
> **“客户流失**,又称**客户流失**、**客户流失**,或**客户流失**,是客户或顾客的损失”——维基百科
对于这篇文章,让我们就“客户流失是坏事”和“客户保留是好事”这一普遍假设达成一致。
A**b 摘要:**本文从不同的角度分析了一个被广泛使用的客户流失数据集,该数据集没有合适的功能来盲目部署我们可以生成的最佳预测模型。下面我将分享问题陈述、数据准备步骤、特征分析、可视化,并从我尝试用于预测客户流失的 Scikit-learn 分类模型中选择 Python 代码。最重要的是,我将展示通过**沿着精确召回曲线移动决策阈值概率,您可能会发现一些流失案例,在这些案例中,您有足够的信心部署真正的保留行动。**
为了简洁起见,我没有包括这个项目中使用的全部代码。详情请参考我在 [**GitHub**](https://github.com/cutterback/p03-telco-churn-model) **上的 Jupyter 笔记本。**
# 商业目标
一家销售住宅语音和互联网服务的电信公司(以下简称“TelCo”)正经历着近 27%的客户流失率。这种程度的流失可能足以让现实生活中的公司破产。鉴于缺乏公开可用的客户数据,我们使用了 [IBM Cognos Telco 客户流失](https://www.kaggle.com/yeanzc/telco-customer-churn-ibm-dataset)模拟数据集,其中包含 7,043 个客户的标记流失。
电信公司希望部署客户维系战略来减少客户流失。公司要求我们:
* 开发预测模型,对客户流失风险进行分类
* 解释每个预测因子对模型预测的相对影响
* 提出减少客户流失的潜在方法
这里我们有一个二元分类问题要解决,所以我们将目标因变量设置为 1(流失)或 0(保留)。我们将使用度量 ROC AUC 来优化我们的估计量。ROC AUC 是一个**R**OO**C**特性下的**A**rea**U**C 曲线。

图片由 CC0 的马丁·托马斯通过[维基共享资源](https://commons.wikimedia.org/wiki/File:Roc-draft-xkcd-style.svg)
对于 ROC AUC,随机分类器得分为 0.5,而完美分类器得分为 1.0。我喜欢 ROC AUC,因为它推荐优化真阳性和假阳性率的模型,这些真阳性和假阳性率明显高于随机机会,这不能保证作为评估指标的准确性。这很重要,因为在采取保留措施时,我们需要对我们的正面类别预测(流失)有信心。
关于 ROC AUC 的更多细节,我推荐[这篇文章](/understanding-auc-roc-curve-68b2303cc9c5)。
# 客户流失原始数据
让我们先来看看我们必须处理的 33 个列。将源文件转换为 pandas 数据框后,前 5 条记录显示:

[截图](https://github.com/cutterback/p03-telco-churn-model/blob/75ef99ec1c143279752d24ba08dd7b40ebcd7a0d/Telco-Churn-Classification-Model.ipynb)作者|前 19 列…

[作者截图](https://github.com/cutterback/p03-telco-churn-model/blob/75ef99ec1c143279752d24ba08dd7b40ebcd7a0d/Telco-Churn-Classification-Model.ipynb)|…最后 14 栏
当考虑列作为预测器的潜在用途时,按域对列进行分组是很有帮助的。GitHub 笔记本中有一个数据字典,但我是这样看的:

作者图片
* 客户—唯一的 customerID 表示我们的样本中有 7,043 个唯一的客户,客户期限从 0 到 72 个月不等
* 人口统计—四个有用的客户人口统计字段
* 地理—精确到地理坐标的客户位置
* 服务——八项收费服务(互联网分流到光纤、DSL 或无)和两个流指示器
* 计费—包括合同类型、计费配置和计费费用
* 流失—流失价值(我们的目标)、流失原因,以及流失分数和客户终身价值的两个内部电信指标。
这里要注意一点。该数据集仅使用来自不同的 [Kaggle 竞赛](https://www.kaggle.com/blastchar/telco-customer-churn)的 23 列进行了广泛建模。我选择了这个有 33 个专栏的版本,希望额外的地理字段能够实现更深入的见解。最后,他们没有得到回报。当 IBM 将这些数据放在一起时,他们似乎只给 1,653 个加利福尼亚邮政编码中的每一个分配了 4 或 5 个客户。经过一番努力使地理字段变得有用后,我不再感觉收益递减。
# 数据准备
我为数据准备和特征创建编写了六个函数。我使用 scikit-learn 库通过管道中的 FunctionTransformer 公开了这些函数。
我创建的最有趣的 3 个功能是:
* 将 4 种付款方式映射到“自动付款方式”(1 =是)。
* 将 8 项服务映射到描述核心服务(电话、光纤/DSL 或捆绑语音和互联网)的“产品概况”列,同时还指示分层的“附加”服务,如多线路、技术支持等。
* 创建了“客户收费指数”,衡量每个客户相对于标准价格的相对定价。我使用所有培训数据中的服务组合和增量平均定价来得出每项服务的标准独立价格。在那里,我将每个客户每月的费用与他们一揽子服务的标准价格进行了对比。

作者图片
在数据剖析和特征分析之前,我将数据分成 80%的训练和 20%的测试。所有分析仅在训练数据上执行,以避免数据泄漏到任何预测模型中。
# 客户流失特征分析
现在谈谈一些基本的见解。

[Tableau 图](https://github.com/cutterback/p03-telco-churn-model/blob/75ef99ec1c143279752d24ba08dd7b40ebcd7a0d/Telco_Churn_Training_Analysis.twb)作者
从上面可以看出,“有伴侣”、“有受抚养人”、“不是老年人”和“流媒体内容”的流失率较低。没有区分性别。

[作者 Tableau 图](https://github.com/cutterback/p03-telco-churn-model/blob/75ef99ec1c143279752d24ba08dd7b40ebcd7a0d/Telco_Churn_Training_Analysis.twb)
逐月合同存在一个主要问题,相对于定期合同的客户(11%和 3%),逐月合同的客户流失率为 43%。更高的客户任期会降低 M-T-M 接触的流失率,但直到 4-5 年的任期,流失率才会达到 26.7%的总体平均水平。很明显,M-T-M 合同以及相关的定价是有问题的。

[作者制作的 Tableau 图表](https://github.com/cutterback/p03-telco-churn-model/blob/75ef99ec1c143279752d24ba08dd7b40ebcd7a0d/Telco_Churn_Training_Analysis.twb)
从产品角度来看,M-T-M 合同中的互联网光纤使客户流失率超过 50%!请注意,电信公司总是要求将电话与互联网光纤捆绑在一起,最大的客户群是签订了 M-T-M 光纤合同的客户。为其 M-T-M 合同添加“附加”服务的客户流失率略低。

[作者制作的 Tableau 图表](https://github.com/cutterback/p03-telco-churn-model/blob/75ef99ec1c143279752d24ba08dd7b40ebcd7a0d/Telco_Churn_Training_Analysis.twb)
客户任期与较高的服务水平和月费正相关(具有统计显著性 p 值)。我在这里假设客户会随着时间的推移增加服务。我们还可以从上面的散点图中看到,任何互联网服务的 M-T-M 合同的平均月费用实际上比定期合同的*要低。鉴于流失率更高,这并不直观。这可能表明,需要 M-T-M 合约的客户比那些愿意签署条款的客户对价格更敏感。*
*在 Python 中运行 Pearson 相关性在很大程度上证实了我们在之前的图中看到的关联。*
**
*[作者截图](https://github.com/cutterback/p03-telco-churn-model/blob/75ef99ec1c143279752d24ba08dd7b40ebcd7a0d/Telco-Churn-Classification-Model.ipynb)*
# *预测客户流失*
**
*[作者制作的 Tableau 图表](https://github.com/cutterback/p03-telco-churn-model/blob/75ef99ec1c143279752d24ba08dd7b40ebcd7a0d/Telco_Model_Scores.twb)*
*我在 80%的训练分裂上训练了四个分类模型:决策树、逻辑回归、随机森林和 XGBoost。*
*对于后 3 个模型,我使用 GridSearchCV 迭代相关参数,并根据来自 5 重交叉验证的最高平均 ROC AUC 重新调整最佳估计值。后三种型号的性能相似。*
*我选择 XGBoost ensemble 模型,因为它具有最高的 AUC 0.79、0.83 的强召回率和 0.54 的更高精度。*
*我对 16 个特性应用了标准缩放,目标设置为“二进制:逻辑”,以预测 XGBoost 模型中的二进制流失结果。*
*instantiate_grid 函数接受模型和网格参数,并建立标准评分标准来实例化 GridSearchCV 对象。*
*通过反复试验,我迭代了 XGBoost 的特性组合和关键参数,以优化模型的评分。对于所有的迭代,我包含了一个正的类权重 2.8,以纠正发生率为 26.7%的适度的类失衡。*
*最佳估计器的最终参数只选择了 4 个最大深度级别,100 个估计器,以及更保守的 L2 正则化 2。*
**
*[作者截图](https://github.com/cutterback/p03-telco-churn-model/blob/75ef99ec1c143279752d24ba08dd7b40ebcd7a0d/Telco-Churn-Classification-Model.ipynb)*
*使用模型的系数作为特征重要性的代理,我们看到我们的 1 年和 2 年合同(超过基线 M-T-M)是迄今为止最强的影响。互联网光纤产品和有家属的客户具有中等重要性,其余特征影响较小。*
**
*[Tableau 图](https://github.com/cutterback/p03-telco-churn-model/blob/75ef99ec1c143279752d24ba08dd7b40ebcd7a0d/Feature_Importances.twb)作者*
*交叉验证和其他参数有助于模型避免过度拟合,因为测试分数与训练一致。*
**
*[截图](https://github.com/cutterback/p03-telco-churn-model/blob/75ef99ec1c143279752d24ba08dd7b40ebcd7a0d/Telco-Churn-Classification-Model.ipynb)作者*
*所以我们来看看测试预测的混乱矩阵。*
**
*[作者截图](https://github.com/cutterback/p03-telco-churn-model/blob/75ef99ec1c143279752d24ba08dd7b40ebcd7a0d/Telco-Churn-Classification-Model.ipynb)*
*我对 83% (311/374)的测试预测召回率感到满意。我们的 XGBoost 模型可以正确预测 6 个真实客户流失案例中的 5 个,这非常好。*
*然而,最佳模型的精度为 54% (311/571),这是不可接受的。我们的模型中有近 1/2 的流失预测是不正确的(误报)。为了放心地部署模型,我们需要更高的精度。*
# *移动决策阈值*
*有了这个模型,我不建议电信公司盲目地使用 0.5%流失概率的决策阈值(DT)来应用默认预测。假设所有客户有超过 50%的可能性会流失,我们可能会经历过多的成本、客户困惑和可能不必要的流失。我们希望将决策阈值提高到客户流失概率,使我们的准确率超过 80%。*
**
*[作者截图](https://github.com/cutterback/p03-telco-churn-model/blob/75ef99ec1c143279752d24ba08dd7b40ebcd7a0d/Telco-Churn-Classification-Model.ipynb)*
*通过绘制作为决策阈值函数的精确度和回忆分数,我们可以看到达到我们想要的精确度所需的概率。虽然我们会将流失客户的比例限制在召回的 20%以下,但我们会更有信心采取更精确的行动。*
**
*[作者截图](https://github.com/cutterback/p03-telco-churn-model/blob/75ef99ec1c143279752d24ba08dd7b40ebcd7a0d/Telco-Churn-Classification-Model.ipynb)*
*使用方法 predict_proba 对我们的最佳估计,我重新分配了各种决策阈值的预测类。我建议将模型分为三层方法,涵盖以下增量流失案例:*
1. ***高精度**:DT = 0.9–1.0/84%精度/ 18%召回率(67 个真位置,13 个假位置)*
2. ***中等精度**:DT = 0.8–0.9/71%精度/ 26%召回率(98 个真位置,40 个假位置)*
3. ***低精度**:DT = 0.7–0.8/49%精度/ 19%召回率(70 个真位置,72 个假位置)*
*电信公司可以根据 precision groups 确定其保留行动的目标,涵盖以下 3 种情况下所有流失案例的 63%:*
1. ***高精度** —以最积极的保留为目标,如通过折扣或增加免费服务的主动推广来保留最有可能流失的客户。*
2. ***中等精度** —以更通用、更低成本的方法为目标,如授权客户服务代理在某些情况下拯救客户。*
3. ***低精度** —将这些客户添加到观察名单,或一般的挽留沟通工作。*
# *扩展数据收集*
*我们已经看到,一些基本数据可能足以预测客户流失,并足以采取行动。虽然这是一个好的开始,但我强烈建议电信公司投资这些领域的数据收集,以提高客户流失预测。*
* *产品—服务利用率、质量水平和 SLA、设备、竞争对手覆盖范围*
* *客户服务—联系人/电话/电子邮件、票证、门户/应用程序使用、情感分析*
* *营销—相对价格点、获得的价值、待定合同续签、营销参与、终身客户价值*
* *人口统计—信用评分、收入、房屋所有权、家庭规模、角色/生命周期、居住时间*
* *地理-街区/社区联系*
**感谢阅读本帖,并请留下评论。**
*<https://chuckutterback.medium.com/membership> *
# 用简单的统计数据预测欧洲杯比赛
> 原文:<https://towardsdatascience.com/predict-euro-cup-matches-with-simple-statistics-2fc913678117?source=collection_archive---------7----------------------->
## 因为我们都知道过去的结果是未来的保证

在赢得足球友谊赛之前,我们都只是棋子
编辑:应大众要求,代码和数据现可在 [GitHub](https://github.com/sijmenw/predict-euro-cup-simple-stats) 上获得,现成代码也可在 [Kaggle](https://www.kaggle.com/sijmenvdw/predict-euro-cup-matches-with-simple-statistics) 上找到。我还添加了世界杯和欧洲杯的数据集的解析版本。你可以随意使用任何你喜欢的东西。— Sijmen
我和我的朋友们为每一届世界杯和欧洲杯开了一个足球赌局。除了巨大的荣耀和一种永恒的胜利感,这条线上没有太多东西。我想你可能会说实际上有很多风险。
无论如何,我是一个数据科学家,让我们把它用在足球比赛上。
# 这个想法
【**假设 1** 】足球不变,每届锦标赛都会出现类似的比分分布。
【**假设 2**】FIFA 排名是一个国家表现的很好指标。
我将查看欧洲杯的典型得分分布,并根据得分的发生率和国际足联在各国排名中的差异来分配得分。
例如:在一个典型的欧洲杯上,最极端的比分是 5-0,而且只出现一次。我将预测各国之间国际足联排名差异最大的比赛的得分。
之后,我寻找第二个最极端的分数,并预测它与国际足联排名第二的国家的比赛。
我继续这样做,直到所有的匹配都有一个预测的分数。
如果这没有意义,不要担心,下面有更多的例子。
# 数据
我希望找到一个网站,可以选择输出所有欧洲杯比赛,但可惜,我没有找到。 [OpenFootball](https://github.com/openfootball/euro) 看起来很有前景,但我最终没有使用它,因为 1。他们的文本文件格式令人讨厌。我不想安装他们的工具来构建数据库。
对于比赛数据,我最终使用了 [LinguaSport](http://www.linguasport.com/futbol/internacional/eurocopa/seekff.asp) ,简单的格式,容易解析,似乎什么都有。爱死了。
对于 FIFA 排名我用了[这个 Kaggle 数据集](https://www.kaggle.com/cashncarry/fifaworldranking),非常感谢 [Alex](https://www.kaggle.com/cashncarry) 。
最后,要玩的游戏…我手工输入的。
# 履行
主表如下所示:
+-------------+-----------------------+------------------+-------+
| edition | team 1 | team 2 | score |
+-------------+-----------------------+------------------+-------+
| 2016-FRANCE | Wales (Cymru) | Belgium (België) | 3-1 |
| 2016-FRANCE | Germany (Deutschland) | Italy (Italia) | 1-1 |
| 2016-FRANCE | France | Iceland (Ísland) | 5-2 |
| 2016-FRANCE | Portugal | Wales (Cymru) | 2-0 |
| 2016-FRANCE | Germany (Deutschland) | France | 0-2 |
+-------------+-----------------------+------------------+-------+
每场比赛都在表格中,使用`value_counts()`和一些格式,我们得到了这个很酷的热图,显示了每个版本中出现最多的分数。请注意,只有组合才重要,即 2–1 与 1–2 相同。

欧洲杯每一届的每一个得分的出现[图片由作者提供]
下面是热图中显示的相同数据,按版本标准化。

热图显示每届欧洲杯比赛结果的相对频率[图片由作者提供]
我们可以很容易地看到最常出现的分数:1–0,1–1,2–0 和 2–1。更准确地说,下面是所有欧洲杯的每一个得分出现的百分比:
+-------+--------+
| score | freq. |
+-------+--------+
| 0-0 | 9.6% |
| 1-0 | 16.25% |
| 1-1 | 13.87% |
| 2-0 | 15.49% |
| 2-1 | 14.41% |
| 2-2 | 3.3% |
| 3-0 | 6.6% |
| 3-1 | 5.4% |
| 3-2 | 6.02% |
| 3-3 | 0.73% |
| 4-0 | 0.98% |
| 4-1 | 0.81% |
| 4-2 | 1.47% |
| 4-3 | 0.17% |
| 5-0 | 1.78% |
| 5-1 | 1.09% |
| 5-2 | 0.69% |
| 5-4 | 0.23% |
| 6-0 | 0.41% |
| 6-1 | 0.29% |
| 6-2 | 0.17% |
| 7-1 | 0.23% |
+-------+--------+
我们的目标是创建与我们上面发现的分数分布相同的预测。为此,我们的下一步将是把百分比转换成出现的次数(从现在开始我们称之为 N)。
在锦标赛的第一阶段,我们将预测 36 场比赛。将这些值乘以 36 可以很好地表示 N:
+-------+------+
| score | ~N |
+-------+------+
| 0-0 | 3.46 |
| 1-0 | 5.85 |
| 1-1 | 4.99 |
| 2-0 | 5.58 |
| 2-1 | 5.19 |
| 2-2 | 1.19 |
| 3-0 | 2.37 |
| 3-1 | 1.95 |
| 3-2 | 2.17 |
| 3-3 | 0.26 |
| 4-0 | 0.35 |
| 4-1 | 0.29 |
| 4-2 | 0.53 |
| 4-3 | 0.06 |
| 5-0 | 0.64 |
| 5-1 | 0.39 |
| 5-2 | 0.25 |
| 5-4 | 0.08 |
| 6-0 | 0.15 |
| 6-1 | 0.11 |
| 6-2 | 0.06 |
| 7-1 | 0.08 |
+-------+------+
现在,我们必须将它们转换成离散值,因为我们无法预测一小部分匹配。我们不能简单地对数值进行四舍五入,因为这样会得出错误的总数。即舍入[0.3,0.4,0.3]将得到[0,0,0],它们没有相同的和。
取而代之的是,我们将这些数字的底数加上一个分数,然后不断地在最大的分数上加 1,直到我们达到相同的和,见下面的代码。
get the floor of each value with fraction
n = [int(x) for x in e]# create a list with only fractions
f = [x % 1 for x in e]# loop until the right sum is reached
while sum(n) < 36: # find the largest fraction, add one to N there and set to zero
idx = np.argmax(f)
f[idx] = 0
n[idx] += 1
这导致了我们的最终 N:
+-------+------+---+
| score | ~N | N |
+-------+------+---+
| 0-0 | 3.46 | 4 |
| 1-0 | 5.85 | 6 |
| 1-1 | 4.99 | 5 |
| 2-0 | 5.58 | 6 |
| 2-1 | 5.19 | 5 |
| 2-2 | 1.19 | 1 |
| 3-0 | 2.37 | 2 |
| 3-1 | 1.95 | 2 |
| 3-2 | 2.17 | 2 |
| 3-3 | 0.26 | 0 |
| 4-0 | 0.35 | 0 |
| 4-1 | 0.29 | 0 |
| 4-2 | 0.53 | 1 |
| 4-3 | 0.06 | 0 |
| 5-0 | 0.64 | 1 |
| 5-1 | 0.39 | 1 |
| 5-2 | 0.25 | 0 |
| 5-4 | 0.08 | 0 |
| 6-0 | 0.15 | 0 |
| 6-1 | 0.11 | 0 |
| 6-2 | 0.06 | 0 |
| 7-1 | 0.08 | 0 |
+-------+------+---+
# 预测
现在只剩下一步了:将分数的分布与要进行的比赛相匹配。
为此,我们将创建一个包含要进行的比赛的数据框架,并添加国家的 FIFA 排名,以便我们可以对此进行排序。该表如下所示:
+-----------+-------------+--------+--------+-----------+
| country 1 | country 2 | rank 1 | rank 2 | rank diff |
+-----------+-------------+--------+--------+-----------+
| turkey | italy | 29 | 7 | 22 |
| wales | switzerland | 17 | 13 | 4 |
| denmark | finland | 10 | 54 | -44 |
| belgium | russia | 1 | 38 | -37 |
| england | croatia | 4 | 14 | -10 |
+-----------+-------------+--------+--------+-----------+
排名 1 和排名 2 描述了该国目前的国际足联排名,排名差异描述了两者之间的差异。
最后,我们根据进球差异对得分 N 进行排序,根据 FIFA 排名差异对比赛进行排序,并将它们放在一起,从而获得我们的最终预测:
+-----------------+-----------------+-----+-----+--------+------+
| country 1 | country 2 | r 1 | r 2 | r diff | PRED |
+-----------------+-----------------+-----+-----+--------+------+
| turkey | italy | 29 | 7 | 22 | 1-2 |
| wales | switzerland | 17 | 13 | 4 | 0-0 |
| denmark | finland | 10 | 54 | -44 | 3-0 |
| belgium | russia | 1 | 38 | -37 | 3-1 |
| england | croatia | 4 | 14 | -10 | 1-0 |
| austria | north macedonia | 23 | 62 | -39 | 4-2 |
| netherlands | ukraine | 16 | 24 | -8 | 1-1 |
| scotland | czech | 44 | 40 | 4 | 1-1 |
| poland | slovakia | 21 | 36 | -15 | 1-0 |
| spain | sweden | 6 | 18 | -12 | 1-0 |
| hungary | portugal | 37 | 5 | 32 | 0-2 |
| france | germany | 2 | 12 | -10 | 1-0 |
| finland | russia | 54 | 38 | 16 | 1-2 |
| turkey | wales | 29 | 17 | 12 | 0-1 |
| italy | switzerland | 7 | 13 | -6 | 1-1 |
| ukraine | north macedonia | 24 | 62 | -38 | 3-1 |
| denmark | belgium | 10 | 1 | 9 | 2-2 |
| netherlands | austria | 16 | 23 | -7 | 1-1 |
| sweden | slovakia | 18 | 36 | -18 | 2-1 |
| croatia | czech | 14 | 40 | -26 | 3-2 |
| england | scotland | 4 | 44 | -40 | 3-0 |
| hungary | france | 37 | 2 | 35 | 0-2 |
| portugal | germany | 5 | 12 | -7 | 1-1 |
| spain | poland | 6 | 21 | -15 | 2-1 |
| italy | wales | 7 | 17 | -10 | 1-0 |
| switzerland | turkey | 13 | 29 | -16 | 2-1 |
| ukraine | austria | 24 | 23 | 1 | 0-0 |
| north macedonia | netherlands | 62 | 16 | 46 | 1-5 |
| finland | belgium | 54 | 1 | 53 | 0-5 |
| russia | denmark | 38 | 10 | 28 | 0-2 |
| czech | england | 40 | 4 | 36 | 0-2 |
| croatia | scotland | 14 | 44 | -30 | 2-0 |
| slovakia | spain | 36 | 6 | 30 | 0-2 |
| sweden | poland | 18 | 21 | -3 | 0-0 |
| portugal | france | 5 | 2 | 3 | 0-0 |
| germany | hungary | 12 | 37 | -25 | 3-2 |
+-----------------+-----------------+-----+-----+--------+------+
从表中可以看出,当国际足联排名差异较大时,国际足联排名较好的国家将获得较大的净胜球。预计两国国际足联排名相近的比赛将以平局告终。
现在只要等到比利时 5:0 击败芬兰,我就会看起来像个天才。

画了我,在事情发生后向大家展示了我对“比利时 5-0 芬兰”的预测。[图片由 [Clker](https://pixabay.com/users/clker-free-vector-images-3736/) 提供]
目前,比赛结果只能通过他们的净胜球来判断,这意味着 0-0 和 1-1(以及所有其他平局)被平等对待。通过观察得分高的国家和得分低的国家,预测可能会有所改进,但这是下一版的内容。
# 结束了
感谢阅读,如果你想了解更多,你可以在 [LinkedIn](https://www.linkedin.com/in/sijmen-van-der-willik/) 或 [GitHub](https://github.com/sijmenw?tab=repositories) 上关注我。
# 阅读更多
这个故事有第 2 部分:[用简单的统计数据预测欧洲杯比赛,但是更好(pt。2)](https://sijmenvdw.medium.com/predict-euro-cup-matches-with-simple-statistics-but-better-pt-2-b48687a6d579)
第三部分,用简单的统计数据预测欧洲杯比赛:我应该下注吗?(第三部分)。
# 用简单的统计数据预测欧洲杯比赛,但更好(第二部分)
> 原文:<https://towardsdatascience.com/predict-euro-cup-matches-with-simple-statistics-but-better-pt-2-b48687a6d579?source=collection_archive---------34----------------------->
## 谁能想到大假设是危险的?
# 介绍
在我之前的文章中,我们用两个假设和统计数据预测了欧洲杯比赛。在这一集里,正好赶上淘汰阶段,我们将提升自己,对假设更加小心。
让我们更深入地了解国际足球。

仔细观察数据有助于做出更好的预测[图片由[马腾](https://unsplash.com/@laughayette)
# 数据
我们将使用两个数据集:
1。国际足球比赛
2。国际足联排名
国际足球比赛数据,我在 Kaggle by [Mart](https://www.kaggle.com/martj42) 上找到了[这个](https://www.kaggle.com/martj42/international-football-results-from-1872-to-2017)。它在 1872 年到 2017 年之间有超过 40,000 场比赛,太棒了!
对此,我在玩的时候加入了 FIFA 排名。我使用了与上一篇文章相同的数据集。再次感谢,[亚历克斯](https://www.kaggle.com/cashncarry)。
## 匆匆一瞥
我们总共和 313 个不同的国家进行了 42369 场比赛。
每个国家的比赛数量相差很大。前 20 名的出场次数总和为 17.521 次,而后 20 名只有区区 63 次。
另一个值得注意的事情是,它总是主队对客队,按照这个顺序,这意味着主场优势现象可能会扭曲对他们有利的结果。奖金部分还有更多关于主场优势的内容。

按比赛次数排序的前 20 个国家。[图片由作者提供]
## 国际足联排名
我们的国际足联排名数据集有 216 个不同的国家,从 1992 年 12 月 31 日开始。有点失望,因为我们的比赛有超过 100 个额外的国家和超过 100 年的额外比赛。

随着时间的推移,拥有国际足联排名的国家数量。[图片由作者提供]
在上面的图中,我们看到大多数国家都在 2000 年以后,所以让我们以此为分界点。
除去 2000 年以前的比赛或与没有国际足联排名的国家的比赛,我们剩下 17.701 场比赛。在我看来已经很多了。
# 第一个问题
> 国际足联的排名是判断谁将赢得比赛的好指标吗?
为了回答这个问题,我们将首先寻找一场比赛的净胜球和国家排名之间的关系。换句话说:如果一个国家的国际足联排名比他们的对手高,他们会进更多的球吗?下图表明答案是肯定的。

散点图,x 轴表示国际足联排名数字的差异,y 轴表示进球的差异。每个点代表一场比赛。红线穿过没有净胜球的比赛,意味着比赛以平局结束。请注意,国际足联的排名越高意味着排名越靠后,越好的球队排名越低。
为了可读性,上面的情节中排除了三场比赛:澳大利亚对汤加(2001 年 22-0),澳大利亚对美属萨摩亚(2001 年 31-0),关岛对朝鲜(2005 年 0-21)。澳大利亚在 2001 年一定是一支很无情的队伍,很高兴我不用和他们比赛。
从图中可以明显看出,国际足联排名有一定的预测价值。拥有一个较低的排名,意味着你在列表中的位置较高,与拥有一个较大的积极目标差异相关。
简而言之:如果你有一个更好的国际足联排名,你可能会得分更多。
> 但是...多了多少?
为了更深入地了解这一点,让我们来看看在比较具有相似 FIFA 级别差异的比赛组时的得分差异分布。一句话就这么多,我就分解一下。
1. 分数差异分布显示每个分数差异出现的频率
2. 上面提到的小组是由我决定的,尽管我确实问过一些更了解足球的人什么是合理的。这些组如下:
+-------------------------+--------------------------+
| Group name | FIFA rank difference |
+-------------------------+--------------------------+
| Very large advantage | over 50 ranks |
| Large advantage | between 20 and 50 ranks |
| Medium advantage | between 10 and 20 ranks |
| Small advantage | between 5 and 10 ranks |
| Equal | less than 5 ranks |
| Small disadvantage | between 5 and 10 ranks |
| Medium disadvantage | between 10 and 20 ranks |
| Large disadvantage | between 20 and 50 ranks |
| Very large disadvantage | over 50 ranks |
+-------------------------+--------------------------+
优势意味着排名较低,因此在列表中的位置较高。劣势意味着相反。

各组得分差异分布。小组是由国际足联等级差异在比赛时创建的。另外,请参见上表。[图片由作者提供]
从上面的分布中,我们可以看到,在国际足联排名方面对你的对手有非常大或非常大的优势,最有可能产生 1 分差,超过 20%的结果有这种差异。拥有非常大的劣势使得-1 成为最有可能的结果。
所有其他等级差异的最高值为零,这意味着没有分数差异仍然是最有可能的结果。
# 最大的问题
> 任何一场比赛最有可能的结果是什么?
这是一个终极问题,因为预测最有可能的结果应该会给我在足球友谊赛中带来很好的结果,这就是我做这些的原因。

记住主要目标是赢得足球友谊赛。[图片由[乔治](https://unsplash.com/@giorgiotrovato)提供]
我们现在知道了最有可能的比分差距,但是差距为 1 仍然可能是 1–0、2–1 或 3–2,我们应该预测什么?
为了从一个组中得到最可能的结果,我们做了与我们用来生成上面的分布几乎相同的计算,但是这次我们看的是整个分数,而不仅仅是差值。
例如,当我们查看“非常大的优势”组时,我们发现 397 场比赛以 2-0 结束,390 场比赛以 1-0 结束,346 场比赛以 3-0 结束,等等。这意味着我们预测 2-0,因为这是最有可能的结果。
Top 15 most common outcomes of a match with a very large advantage for the Home Team.+---------+--------------+
| Outcome | Occurrences |
+---------+--------------+
| 2 - 0 | 397 |
| 1 - 0 | 390 |
| 3 - 0 | 346 |
| 4 - 0 | 235 |
| 2 - 1 | 234 |
| 1 - 1 | 187 |
| 3 - 1 | 157 |
| 0 - 0 | 152 |
| 5 - 0 | 134 |
| 4 - 1 | 96 |
| 6 - 0 | 93 |
| 0 - 1 | 77 |
| 2 - 2 | 75 |
| 5 - 1 | 56 |
| 7 - 0 | 55 |
+---------+--------------+
请注意,在这 15 个最常见的结果中,11 个是赢,3 个是平,只有 1 个是输。
为了便于使用,我们将重复计算并将结果放在热图中。

最有可能的比赛结果热图由国际足联排名差异组。值是相对发生率,即 0.17 表示该组中所有匹配的 17%导致该结果。[图片由作者提供]
# 做预测
> 酷,我如何使用这个?
要使用此热图预测匹配:
1. 以 FIFA 排名确定主队优势。即荷兰(排名第 16)对捷克(排名第 40)将是:“巨大优势——20 到 50 名之间的排名差异”。
2. 找到最亮的地方。从热图中查找最有可能的结果,在“大优势”行中,我们发现结果 1–0 的值最高。
3. 就是这样。
如果你觉得特别勇敢,你可以考虑选择一个低价值的结果。
这里还有一个更大版本的热图。
# 实际的预测
+-------------+-------------+----+----+--------------+-------+
| Country 1 | Country 2 | r1 | r2 | group | PRED |
+-------------+-------------+----+----+--------------+-------+
| Belgium | Portugal | 1 | 5 | equal | 1-1 |
| Italy | Austria | 7 | 23 | med adv | 1-0 |
| France | Switzerland | 2 | 13 | med adv | 1-0 |
| Croatia | Spain | 14 | 6 | small disadv | 1-1 |
| Sweden | Ukraine | 18 | 24 | small adv | 1-1 |
| England | Germany | 4 | 12 | small adv | 1-1 |
| Netherlands | Czech | 16 | 40 | large adv | 1-0 |
| Wales | Denmark | 17 | 10 | small disadv | 1-1 |
+-------------+-------------+----+----+--------------+-------+
# 结束*
如果你读了整本书,我很感动,非常感谢你。如果你只是向下滚动来获得我的预测,那也没问题。
如果你想了解更多,你可以关注我[这里](https://sijmenvdw.medium.com/),在 [LinkedIn](https://www.linkedin.com/in/sijmen-van-der-willik/) 或 [GitHub](https://github.com/sijmenw?tab=repositories) 。
*但是有奖励
# 附加注释
如果你打算打赌,请理解我没有水晶球。你承担的任何风险都是你自己的。
# 奖金
一个额外的步骤:主场优势是一个真实的东西,我应该调整它。
以下是另外两张热图:
1. 仅使用具有主场优势的游戏(非中立比赛场地)[ [link](https://i.imgur.com/YH6wRiK.png) ]
2. 使用没有主场优势的游戏(中立比赛场地)[ [链接](https://i.imgur.com/O1EpTrN.png) ]
接下来的 8 场比赛只有一场是有主场优势的:英格兰在伦敦打。
使用相应的热图更新预测会得到以下最终预测表:
+-------------+-------------+----+----+--------------+-------+
| Country 1 | Country 2 | r1 | r2 | group | PRED |
+-------------+-------------+----+----+--------------+-------+
| Belgium | Portugal | 1 | 5 | equal | 1-0 |
| Italy | Austria | 7 | 23 | med adv | 1-1 |
| France | Switzerland | 2 | 13 | med adv | 1-1 |
| Croatia | Spain | 14 | 6 | small disadv | 1-1 |
| Sweden | Ukraine | 18 | 24 | small adv | 1-1 |
| England | Germany | 4 | 12 | small adv | 1-1 |
| Netherlands | Czech | 16 | 40 | large adv | 1-0 |
| Wales | Denmark | 17 | 10 | small disadv | 1-1 |
+-------------+-------------+----+----+--------------+-------+
# 太多领带
更新:我正在玩一个在 90 分钟后给你打分的球,不管是否会有加时赛。
如果你玩的是基于可能延长后的最终结果得分的球赛,1-1 就不太可能了。在这种情况下,选择 1-0 或 0-1,取决于哪个国家有优势。
# 结束了
如果你读了整本书,还有奖金,我会更加印象深刻。谢谢你。
以下是我的链接:
* [中等](https://sijmenvdw.medium.com/)
* [领英](https://www.linkedin.com/in/sijmen-van-der-willik/)
* [GitHub](https://github.com/sijmenw?tab=repositories)
# 阅读更多
这个故事有一个第 3 部分,用简单的统计数据预测欧洲杯比赛:我应该打赌吗?(第三部分)。
# 用简单的统计数据预测欧洲杯比赛:如何提高排名(pt。4)
> 原文:<https://towardsdatascience.com/predict-euro-cup-matches-with-simple-statistics-how-to-rise-the-ranks-pt-4-d076f3dbf763?source=collection_archive---------36----------------------->
## 足球不是单人游戏
我们已经讨论了[如何找到一场比赛最有可能的结果](/predict-euro-cup-matches-with-simple-statistics-but-better-pt-2-b48687a6d579)和[如何计算每个预测结果](https://sijmenvdw.medium.com/predict-euro-cup-matches-with-simple-statistics-should-i-take-that-bet-part-3-22df3d2ad882)的预期分数。
在锦标赛接近尾声的时候,我们还有一件事要弄清楚。
> 落后了怎么赢?
# 为什么期望值不再起作用
在真正的锦标赛中,比赛的次数是有限的。由于还有无限多的比赛要打,用最高期望值预测结果总是能得到最好的结果。没有例外,这是大数定律。
在匹配数量有限的情况下,这种方式会有所不同。请考虑以下情况:
在还剩 1 场比赛的情况下,你和你的对手打成平手,你们都有两个相同的选择:
—预测 2–2 作为比赛结果,如果正确,提供 10000 分
—预测非 2–2 作为比赛结果,如果正确,提供 1 分
第一个选项的期望值会更高。对于实力相当的球队,使用中性热图,2-2 的概率约为 0.04%或 4%。

中立竞争环境下不同结果可能性的热图。[图片由作者提供]
第一个选项的期望值:
`E = 0.04 * 10000 + 0.96 * 0 = 400`
第二个选项的期望值:
`E = 0.04 * 0 + 0.96 * 1 = 0.96`
显然,第一种选择更好,对吗?
如果你只是想打败你的对手就不会。如果我们的对手选择第一个选项,你有 96%的机会用第二个选项打败他。你只会领先他一分,但这可能是最重要的。
# 形势
有几个因素会影响超越对手的最佳策略:
1. 得分差距
2. 剩余的匹配数
3. 计分系统
4. 可能结果的可能性
5. 对手预测的结果
这是我们需要考虑的五个因素,以提高我们赢得奖金的机会。您可能还想考虑为 poule 中不同的结束位置授予的任何奖励。
# 真实的例子
假设,我的对手领先我 6 分,只剩下一场比赛,使用与[之前博客](https://sijmenvdw.medium.com/predict-euro-cup-matches-with-simple-statistics-should-i-take-that-bet-part-3-22df3d2ad882)相同的计分系统,一国优势较小,我们的对手使用最大期望值策略。我们做什么呢
首先,我们看看我们的对手在每种结果下会得到多少分。小优势最高 EV 的结果是 1–0,所以这将是我们对手的预测。下表列出了每项成果的得分:
+---------------+---------+
| Match outcome | points |
+---------------+---------+
| 1-0 | +10 |
| 2-0 | +7 |
| 3-0 | +7 |
| 4-0 | +7 |
| 2-1 | +5 |
| 3-1 | +5 |
| 3-2 | +5 |
| 1-1 | +2 |
| 0-0 | +2 |
| 1-2 | +2 |
| 1-3 | +2 |
| 0-1 | +0 |
| 0-2 | +0 |
| 2-2 | +0 |
| 0-3 | +0 |
+---------------+---------+
我们至少需要比对手多得 6 分。我们无法预测任何会给我们的对手 5 分或更多分的结果。毕竟,如果我们的对手得到 5 分或更多,我们至少需要得到 11 分,这是不可能的。
我们需要另一个专栏来添加我们自己的分数,并将其与我们对手的分数进行比较。让我们看看如果我们预测 1-1 会发生什么。
+---------------+-----------+-----+------+--------+
| Match outcome | opponent | me | net | beat? |
+---------------+-----------+-----+------+--------+
| 1-0 | +10 | +2 | -8 | |
| 2-0 | +7 | +0 | -7 | |
| 3-0 | +7 | +0 | -7 | |
| 4-0 | +7 | +0 | -7 | |
| 2-1 | +5 | +2 | -3 | |
| 3-1 | +5 | +2 | -3 | |
| 3-2 | +5 | +0 | -5 | |
| 1-1 | +2 | +10 | +8 | Yes |
| 0-0 | +2 | +7 | +5 | |
| 1-2 | +2 | +2 | +0 | |
| 1-3 | +2 | +2 | +0 | |
| 0-1 | +0 | +2 | +2 | |
| 0-2 | +0 | +0 | +0 | |
| 2-2 | +0 | +7 | +7 | Yes |
| 0-3 | +0 | +0 | +0 | |
+---------------+-----------+-----+------+--------+
从上表中,我们发现如果我们预测 1-1,只有两种可能的结果会导致接管我们的对手。
为了计算发生这种情况的可能性,我们在上面的热图中查找相应的值,并将它们相加。
1–1 和 2–2 的几率分别为 0.14 和 0.08。
这意味着我们有 22%的机会以 1:1 的预测击败我们的对手。顺便说一下,p 代表概率。
> 我们能做得更好吗?
# 更多计算
当然,只要计算所有合理的选项,然后选择最好的。
我们已经知道我们的对手在每个结果中得到的分数。接下来,我们计算每一个结果,每一个预测的分数。这导致了下表:

热图显示了每个预测结果的得分。[图片由作者提供]
现在,我们根据我们的对手将获得的分数来调整这些值,从而生成一个热图,其中包含与对手相比所获得的分数值。

热图显示了与我们的对手相比,在每个预测结果中的每个结果所获得的分数。[图片由作者提供]
我们仍然需要增加可能性来计算哪个预测最有可能为我们赢得胜利。但是,我们已经可以看到,一些预测从来没有为我们赢得足够的分数。例如,1–0 总是与我们的对手得分相同,3–0 最多比我们的对手多得 3 分,2–1 最多多得 5 分。
让我们把可能性加起来,决定我们的最佳预测。

热图显示了在每种预测结果下击败对手的几率。[图片由作者提供]
上面的热图显示了击败对手的几率,将一列中的所有值相加显示了在特定预测下击败对手的总几率。绿色表示我们击败了对手,红色表示我们不会击败对手。
例如,我们看到预测 0–0:
`P(win) = P(0-0) + P(2-2) = 0.13 + 0.08 = 0.21`
对于 0–1、0–2 和 0–3:
`P(win) = P(0-1) + P(0-2) + P(0-3)= 0.1 + 0.07 + 0.01 = 0.18`
对于 1–1:
`P(win) = P(1-1) + P(2-2) = 0.14 + 0.08 = 0.22`
其他选择击败我们对手的可能性要低得多。我们的第一次猜测是 1-1,实际上是正确的,尽管 0-0 是非常接近的第二次猜测。
# 如果不止有一场比赛要打呢?
要计算多个匹配的几率,您必须找到得分足够多的可能组合。这将变得太复杂,无法放入热图,你可能更适合模拟比赛,即使用[蒙特卡洛模拟](https://en.wikipedia.org/wiki/Monte_Carlo_method)。
那是以后的事了!
# 结束了
感谢您的阅读。
如果你喜欢这个,这里还有更多:
* [中](https://sijmenvdw.medium.com/) — sijmenvdw
* [LinkedIn](https://www.linkedin.com/in/sijmen-van-der-willik/)—Sijmen van der Willik
* [GitHub](https://github.com/sijmenw?tab=repositories) — sijmenw
# 使用 PyCaret 预测销售线索得分(正确的方式)
> 原文:<https://towardsdatascience.com/predict-lead-score-the-right-way-using-pycaret-332faa780cfc?source=collection_archive---------4----------------------->
## 关于如何使用 PyCaret 构建销售线索评分模型并提高营销活动投资回报率的分步指南。

使用 PyCaret 预测潜在客户转化(正确的方式)——作者图片
# **简介**
线索是当今许多企业的驱动力。随着基于订阅的商业模式的发展,尤其是在初创领域,将潜在客户转化为付费客户的能力是生存的关键。简单来说,“线索”代表有兴趣购买你的产品/服务的潜在客户。
通常,当您通过第三方服务或自己开展营销活动获得销售线索时,通常会包括以下信息:
* 潜在顾客的姓名和联系方式
* 销售线索属性(人口统计、社交、客户偏好)
* 来源(脸书广告、网站登陆页面、第三方等。)
* 花在网站上的时间,点击次数等。
* 推荐详情等。
# 销售线索管理流程一览

销售线索管理流程一览—作者图片
市场营销和销售部门在销售线索管理上花费了大量的时间、金钱和精力,我们将采用这一概念来涵盖销售线索生成、资格认定和货币化这三个关键阶段。
# 👉线索挖掘
潜在客户生成是客户对您企业的产品或服务产生兴趣或进行调查的开始。创建销售线索的目的是将兴趣或询问转化为销售。互联网上有无限数量的第三方公司承诺提供最佳线索。然而,你也可以通过营销活动自己来做。产生潜在客户的方法通常属于广告范畴,但也可能包括非付费来源,如有机搜索引擎结果或现有客户的推荐。
# 👉**领导资格**
销售线索资格是指确定哪些潜在客户最有可能进行实际购买的过程。这是销售漏斗中不可或缺的一部分,销售漏斗通常会吸纳许多线索,但只能转化其中的一小部分。简单来说,销售线索资格意味着**评估销售线索并对其进行优先排序,以得出转化的可能性**,这样您的市场营销和销售部门就可以追踪优先排序的销售线索,而不是所有的销售线索,因为这些线索通常可能有数千条。
# 👉**导联转换**
销售线索转化是您最终将合格的销售线索转化为付费客户的阶段。它包括所有刺激购买产品或服务的欲望并引导购买决策的营销实践。这是一个货币化或结束阶段,其结果通常决定了整个营销活动的成功。
# 👉**销售线索评分的真正含义是什么?**
想象一下,你的团队有很多潜在客户,但是没有足够的资源去追踪他们。无论你是一家拥有大量免费增值用户的产品导向型企业,还是一个巨大的潜在客户渠道,或者仅仅是一个令人惊叹的上门销售团队,在一天结束时,**你都需要优先安排销售团队的时间,给他们“最好的”潜在客户。**
> 问题是你怎么做才能**最大化你的胜率**?
一种简单的方法是分析历史数据,并查看销售线索转化为销售额的属性。例如,在某个特定的国家、城市或邮政编码中,历史上 90%的销售线索都转化为销售。同样,你的数据也可以告诉你,在你的网站上停留超过 20 分钟的客户,大部分时间都转化为销售额。使用这些业务规则,您可以创建一个**销售线索评分系统**,使用这些业务规则为每个销售线索打分(越高越好)。
这种方法的问题是,业务规则只能涵盖这么多。随着您业务的扩展,您可以收集的数据类型和种类将呈指数级增长。在某种程度上,基于规则的手动系统将不足以继续增加价值。
> **机器学习来了**
您可以从机器学习的角度接近**领先评分系统**,在这里您可以就客户属性、潜在客户来源、推荐和其他可用的详细信息训练 ML 模型,目标将是**潜在客户转换(是或否)**。
你如何得到目标变量?大多数 CRM 系统,如 Salesforce、Zoho 或 Microsoft Dynamics,都可以跟踪单个线索及其状态。销售线索的状态将帮助您创建目标变量。
> 需要注意的一点是,你必须确保不要泄露训练数据集中的任何信息。例如,您的 CRM 系统可以存储关于在销售线索转换时向第三方支付的推荐费的信息,想象一下,如果您在培训数据中使用该信息,从技术上讲,这是一种泄漏,因为您将只在转换时支付推荐费,这是您事后才知道的事情。

预测性销售线索评分工作流—作者的图像许可
# 让我们从实际例子开始👇
# PyCaret 是什么?
[PyCaret](https://www.pycaret.org/) 是一个开源、低代码的机器学习库和 Python 中的端到端模型管理工具,用于自动化机器学习工作流。使用 PyCaret,您可以高效地构建和部署端到端的机器学习管道。要了解更多关于 PyCaret 的信息,请查看他们的 GitHub。

PyCaret 的特点—作者图片
# 安装 PyCaret
# install pycaret pip install pycaret
# 👉资料组
对于本教程,我使用的是来自 Kaggle 的[销售线索转换](https://www.kaggle.com/ashydv/leads-dataset)数据集。该数据集包含超过 9000 个具有客户特征的销售线索,例如销售线索来源、销售线索来源、在网站上花费的总时间、在网站上的总访问量、人口统计信息和转换的目标列(*表示 1 表示转换,0 表示没有转换*)。
# import libraries
import pandas as pd
import numpy as np# read csv data data = pd.read_csv('Leads.csv')
data.head()

样本数据集-按作者分类的图像
# 👉探索性数据分析
# check data info data.info()

data . info()-按作者分类的图片
请注意,有几列缺少许多值。有几种方法可以处理缺失值。我将让 PyCaret 自动处理丢失的值。如果您想了解更多关于在 PyCaret 中输入缺失值的不同方法,请查看这个[文档链接](https://pycaret.org/missing-values/)。
从直觉上来说,在网站上花费的时间和活动分数以及线索来源是转化线索时非常重要的信息。让我们直观地探索一下这种关系:

按在网站上花费的总时间、活动分数和来源(按作者分类的图片)进行线索转化
请注意,来自“添加表单”的销售线索很可能会转化为销售额,而与在网站上花费的时间或分数无关。对于通过 API 或网站登陆页产生的线索来说,情况就不一样了。得分越高,在网站上花费的时间越长,就越有可能将销售线索转化为最终销售额。
# 👉数据准备
对于 PyCaret 中的所有模块来说,`setup`是在 PyCaret 中执行的任何机器学习实验中的第一个也是唯一一个强制步骤。该功能负责训练模型之前所需的所有数据准备。除了执行一些基本的默认处理任务,PyCaret 还提供了一系列预处理功能。要了解 PyCaret 中所有预处理功能的更多信息,可以查看这个[链接](https://pycaret.org/preprocessing/)。
# init setup
from pycaret.classification import *
s = setup(data, target = 'Converted', ignore_features = ['Prospect ID', 'Lead Number'])

pycaret.classification 中的设置函数—按作者分类的图像(图像被截断)
在 PyCaret 中初始化`setup`函数时,它会自动分析数据集并推断所有输入变量的数据类型。如果一切推断正确,您可以按 enter 键继续。您也可以使用设置中的`numeric_features`和`categorical_features`参数来强制/覆盖数据类型。
另外,请注意,我已经在`setup`函数中传递了`ignore_features = ['Prospect ID', 'Lead Number']`,因此在训练模型时不会考虑它。这样做的好处是 PyCaret 不会从数据集中删除该列,它只是在模型训练的幕后忽略它。因此,当您在最后生成预测时,您不需要担心自己将 IDs 连接回来。

设置输出—为显示而截断—按作者列出的图像(图像被截断)
# 👉模型训练和选择
现在数据准备工作已经完成,让我们使用`compare_models`功能开始培训过程。此函数训练模型库中可用的所有算法,并使用交叉验证评估多个性能指标。
# compare all models
best_model = compare_models(sort='AUC')

compare_models 的输出—按作者分类的图片
基于 **AUC** 的最佳模型是`**Catboost Classifier**`,具有`**0.9864**` **的平均 10 倍交叉验证 AUC。**
# print best_model parameters
print(best_model.get_all_params())# except for catboost you can do this:
print(best_model)

Catboost 超参数-作者图片
# 👉模型分析
# **AUC-ROC 图**
AUC-ROC 曲线是在各种阈值设置下对分类问题的性能测量。ROC 是概率曲线,AUC 代表可分性的程度或度量。它告诉我们这个模型在多大程度上能够区分不同的类。AUC 越高,模型在预测阳性和阴性类别方面就越好。虽然评估和比较不同模型的性能非常有帮助,但将这一指标转化为业务价值并不容易。
# AUC Plot
plot_model(best_model, plot = 'auc')

最佳模型的 AUC 图—作者提供的图像
# **SHAP 值**
与 AUC-ROC 不同,shap 值不会告诉您任何关于模型性能的信息,相反,它解释了给定特征具有某个值与我们在该特征采用某个基线值时所做的预测相比所产生的影响。在下图中,y 轴(左侧)包含模型的所有重要特征,x 轴是关联特征的 Shapley 值,色标(右侧)是该特征的实际值。图中每个特征处的每个点都是一个客户线索(来自测试集)——彼此重叠。
shap 值(x 轴)越高,正类的可能性越高(本例中为转换)。因此,从顶部开始阅读,我会将其解释为标记为“将在阅读电子邮件后回复”的线索与基础相比具有较高的 shap 值,这意味着转化的可能性较高。相反,如果您看到标签“Ringing ”,则 shap 值位于基本值的左侧,即 shap 值为负,表示此功能不利于转换。要更详细地了解 shap 值,请看这个[链接](https://github.com/slundberg/shap)。
# Shapley Values
interpret_model(best_model)

作者最佳模型图像的 Shapley 特征重要性图
# 特征重要性图
特征重要性图是解释模型结果的另一种方式。虽然 Shap 值仅适用于复杂基于树的模型,但特征重要性绘图更常见,可用于不同系列的模型。与 shap 值不同,要素重要性不会告诉我们该要素对特定类的影响,它只会告诉我们该要素是否重要。
# Feature Importance plot_model(best_model, plot = 'feature')

作者最佳模型图像的特征重要性图
# 混淆矩阵
混淆矩阵是观察模型性能的另一种方式。在所有可能的工具中,这可能是最简单的一个。它基本上将预测与实际标签进行比较,并将它们分成四个象限:
* 真正(**预测:**换算,**实际:**换算)
* 真负值(**预测:**无转换,**实际:**无转换)
* 假阳性(**预测:**转换,**实际:**无转换)
* 假阴性(**预测:**无转换,**实际:**转换)
如果将所有四个象限相加,它将等于测试集中的客户线索数(1667 + 70 + 84 + 952 = 2,773)。
* 952 个客户(右下象限)是真阳性,这些是模型预测将转化并且它们已经转化的潜在客户;
* 70 条线索是假阳性的(*这可能是你所付出的努力将会白费的地方*);
* 84 条线索为假阴性,即(*错失机会*);和
* 1,667 条线索为真阴性(*无影响*)。
# Confusion Matrix plot_model(best_model, plot = 'confusion_matrix')

最佳模型的混淆矩阵—作者图像
到目前为止,我们已经准备好了用于建模的数据(当您运行`setup`函数时,PyCaret 会自动完成),训练了多个模型以根据 AUC 选择最佳模型,通过不同的图(如 AUC-ROC、特征重要性、混淆矩阵和 Shapley 值)分析了性能。然而,我们还没有回答最重要的问题:
> **这种模式的商业价值是什么,我们为什么要使用这种模式?**
为了给这个模型赋予商业价值,让我们做几个假设:
* 转化为销售额的铅将在第一年产生 120 美元的收入
* 花费在追踪优先销售线索上的时间和精力(根据模型预测)是 15 美元
* 模型错过的机会(假阴性)产生负 120 美元的机会成本(*您可以添加也可以不添加,因为这不是真实成本,而是机会成本,这完全取决于用例*
如果你在这里做一点数学计算,你会得到 88,830 美元的利润。方法如下:

模型对 2,773 名客户的影响——图片由作者提供
这可能是一个好模型,但它不是一个商业智能模型,因为我们还没有加入成本/利润的假设。默认情况下,任何机器学习算法都会优化 AUC 等常规指标。为了实现业务目标,我们必须使用业务度量来训练、选择和优化模型。
# 👉在 PyCaret 中添加自定义指标
多亏了 PyCaret,使用`add_metric`函数可以非常容易地实现这一点。
# create a custom function def calculate_profit(y, y_pred):
tp = np.where((y_pred1) & (y1), (120-15), 0)
fp = np.where((y_pred1) & (y0), -15, 0)
fn = np.where((y_pred0) & (y1), -120, 0)
return np.sum([tp,fp,fn])# add metric to PyCaret add_metric('profit', 'Profit', calculate_profit)
现在让我们再次运行`compare_models`:
# compare all models
best_model = compare_models(sort='Profit')

compare_models 的输出—按作者分类的图像
请注意,这次添加了一个新列`**Profit**`,并且 **Catboost 分类器**不再是基于利润的最佳模型。是**光梯度助推器。**虽然在本例中差异并不显著,但根据您的数据和假设,这有时可能是数百万美元。
# confusion matrix
plot_model(best_model, plot = 'confusion_matrix')

LightGBM 的混淆矩阵—作者图片
客户总数仍然相同(测试集中有 2,773 个客户),现在发生变化的是模型如何在误报和漏报上出错。让我们用同样的假设(如上所述),用一些美元价值来反对它:

模型对 2,773 名客户的影响——图片由作者提供
与使用 Catboost 分类器时的 88,830 美元相比,现在的利润为 89,925 美元。这是 1.2%的提升,根据假阳性和假阴性的大小和成本,可能会转化为数百万美元。除此之外,您还可以做一些其他事情,例如通过显式优化`**Profit**`而不是 AUC、准确性、召回率、精确度或任何其他传统指标来调整最佳模型的超参数。
# 如何使用模型生成销售线索得分?
那么,您一定会问,既然我们已经选择了最佳模型,我该如何将该模型应用于新的销售线索以产生得分呢?嗯,这并不难。
# create copy of data data_new = data.copy()
data_new.drop('Converted', axis=1, inplace=True)# generate labels using predict_model predict_model(best_model, data=data_new, raw_score=True)

使用最佳模型生成的预测-作者提供的图像
请注意,最后三列被添加到数据集-Label(1 =转换,0 =无转换)、Score_0 和 Score_1 是每个类在 0 到 1 之间的概率。例如,第一个观察值 Score_0 是`0.9973`,表示没有转换的概率为 99.7%。
我是一个普通的博客作者,我主要写关于 PyCaret 及其在现实世界中的用例,如果你想自动得到通知,你可以在[媒体](https://medium.com/@moez-62905)、 [LinkedIn](https://www.linkedin.com/in/profile-moez/) 和 [Twitter](https://twitter.com/moezpycaretorg1) 上关注我。

PyCaret —作者图片

PyCaret —作者图片
使用 Python 中的这个轻量级工作流自动化库,您可以实现的目标是无限的。如果你觉得这很有用,请不要忘记给我们 GitHub 库上的⭐️。
要了解更多关于 PyCaret 的信息,请关注我们的 LinkedIn 和 Youtube。
加入我们的休闲频道。此处邀请链接[。](https://join.slack.com/t/pycaret/shared_invite/zt-p7aaexnl-EqdTfZ9U~mF0CwNcltffHg)
# 重要链接
[文档](https://pycaret.readthedocs.io/en/latest/installation.html)
[博客](https://medium.com/@moez_62905)
[GitHub](http://www.github.com/pycaret/pycaret)
[stack overflow](https://stackoverflow.com/questions/tagged/pycaret)
[安装 PyCaret](https://pycaret.readthedocs.io/en/latest/installation.html) [笔记本教程](https://pycaret.readthedocs.io/en/latest/tutorials.html) [投稿于 PyCaret](https://pycaret.readthedocs.io/en/latest/contribute.html)
# 更多 PyCaret 相关教程:
</predict-customer-churn-the-right-way-using-pycaret-8ba6541608ac> [## 使用 PyCaret 预测客户流失(正确的方式)
towardsdatascience.com](/predict-customer-churn-the-right-way-using-pycaret-8ba6541608ac) </build-with-pycaret-deploy-with-fastapi-333c710dc786> </time-series-anomaly-detection-with-pycaret-706a6e2b2427> </supercharge-your-machine-learning-experiments-with-pycaret-and-gradio-5932c61f80d9> </multiple-time-series-forecasting-with-pycaret-bc0a779a22fe>
# 预测下个月具有 RFM 功能的交易
> 原文:<https://towardsdatascience.com/predict-next-month-transactions-with-rfm-features-f6db106fe8d9?source=collection_archive---------5----------------------->
## 通过线性回归预测客户行为的近期、频率和货币指标
> *“没有人能打开另一个人,我们能做的只有等待。然后当它发生时,以开放的态度去工作。”
> ――彼得·霍格,安静的女孩*

照片由[凯丽·通盖](https://unsplash.com/@kellitungay?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)在 [Unsplash](https://unsplash.com/s/photos/minimalism-photo-art?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 上拍摄
# 介绍
在这篇博客文章中,我想重点谈谈预测下个月客户交易的特性工程。作为一名在市场营销领域工作的数据科学家,我发现理解我为什么要做一些工作以及它如何解决业务问题非常重要。我认为特征工程是分析的一部分,领域知识和硬技能同等重要,混合在一起可以找到更好的问题解决方案。
让我们创建 RFM(近期、频率和货币)特征来预测客户下个月的交易数量。为了简化分析,我将使用线性回归来预测下个月的交易。即使线性回归(也称为普通最小二乘法)是解决回归问题的一个很好的起点,你也可以尝试其他模型,如多项式、岭或套索模型。
# **近期、频率和货币(RFM)**

图片由 Aigerim Shopenova 提供
新近性、频率和货币是营销分析工具,通过使用以下衡量标准来识别公司的最佳客户:
* **最近度**:顾客购买的时间
* **频率**:顾客购物的频率
* **货币价值**:顾客在购物上花了多少钱[1]
通常,RFM 模型用于给每个用户分配分数,通常在 1 到 5 的范围内(数字越大,结果越好)。“最佳”客户将在每个类别中获得最高分。然后,营销人员/分析师可以创建用户细分,并开发量身定制的营销活动/行动。
但是,在此分析中,我们将仅使用 RFM 概念并创建新功能来预测下个月的交易。
# 数据
我使用的是来自 UCI 机器学习知识库的公共数据集,可以在这里找到。该数据集包含 2010 年 1 月 12 日和 2011 年 9 月 12 日之间英国注册的无店铺在线零售商的交易信息。该公司主要销售独特的适合各种场合的礼品。这家公司的许多客户都是批发商。
**属性信息:**
* *发票号*:发票号。名义上,分配给每笔交易的 6 位整数。
* *库存代码*:产品(物品)代码。名义上,一个 5 位数的整数,唯一分配给每个不同的产品。
* *描述*:产品(物品)名称。名义上的
* *数量*:每笔交易每种产品(物品)的数量。数字的
* InvoiceDate:发票日期和时间。数字,每笔交易生成的日期和时间
* *单价*:单价。数字,单位为英镑的产品价格
* *CustomerID* :客户编号。名义上,一个唯一分配给每个客户的 5 位整数
* *国家*:国家名称。名义上,每个客户居住的国家的名称。
# 数据争论
import necessary packages
import pandas as pd
import numpy as np
import datetime as dt
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
Read data
df = pd.read_excel('online_retail_dataset.xlsx', sheet_name='Online Retail')

*描述*和 *CustomerID* 列为空值。
# **数据清理和转换**
为了清理和转换数据,我执行了以下操作:
* *CustomerID* 有*空记录。让我们删除包含空值的行*
* *数量*栏有*负值*,可能是采购后退货造成的。让我们删除数量为负的行
* *单价*栏也有*负记录*。我也决定放弃负价格的唱片
* 购买记录的时间段是从 2010 年 1 月 12 日到 2011 年 9 月 12 日。上个月*的*数据*不完整*。让我们忽略不完整月份的记录
* 因为我们计划预测上个月的交易,所以让我们删除客户 id,它们出现在上个月,并且没有以前的购买记录
* 我通过将*数量*和*单价*相乘来计算总销售额
* 为进一步的特性工程定义的发票月份。
Let's drop rows containing NULL values
df.dropna(subset=['CustomerID'], inplace=True)
Keep positive records for quantity column
df = df.loc[df['Quantity'] > 0]
Remove negative price
df = df.loc[df['UnitPrice'] > 0]
Let's ignore the records for the incomplete month
df = df.loc[df['InvoiceDate'] < '2011-12-01']
# 特征工程
让我们使用清理后的数据创建 RFM 特征。此外,我决定添加定义在哪个国家购买的列。
Calculate total sales
df['TotalSum'] = df['Quantity'] * df['UnitPrice']
Define invoice month
df['InvoiceMonth'] = df['InvoiceDate'].dt.to_period('M')
df['InvoiceMonth'] = df['InvoiceMonth'].astype(str)
Split into 2 dataframes to check customer ids
df_nov = df[df['InvoiceMonth'] == '2011-11']
df_other_months = df[df['InvoiceMonth'] != '2011-11']
cust_ids_nov = df_nov['CustomerID'].unique().tolist()
cust_ids_others = df_other_months['CustomerID'].unique().tolist()
Let's exclude customer ids, which appeared for the first time in November 2011
new_cust_ids = list(set(cust_ids_nov) - set(cust_ids_others))
df = df[~df['CustomerID'].isin(new_cust_ids)]
Create a column to mark purchases from the UK, Germany, France and EIRE
df['IsUnitedKingdom'] = df['Country'].apply(lambda x: 1 if x == 'United Kingdom' else 0)
df['IsGermany'] = df['Country'].apply(lambda x: 1 if x == 'Germany' else 0)
df['IsFrance'] = df['Country'].apply(lambda x: 1 if x == 'France' else 0)
df['IsEIRE'] = df['Country'].apply(lambda x: 1 if x == 'EIRE' else 0)
Let's choose only records for previous months
df_X = df[df['InvoiceMonth'] != '2011-11']
now = dt.datetime(2011, 11, 1)
RFM features
df_features = df_X.groupby('CustomerID').agg({'InvoiceDate': lambda x: (now - x.max()).days,
'InvoiceNo': pd.Series.nunique,
'TotalSum': np.sum,
'Quantity': ['mean', 'sum'],
'IsUnitedKingdom': np.mean,
'IsGermany': np.mean,
'IsFrance': np.mean,
'IsEIRE': np.mean}).reset_index()
df_features.columns = ['CustomerID', 'Recency', 'Frequency',
'Monetary', 'QuantityAvg', 'QuantityTotal',
'IsUK', 'IsGermany', 'IsFrance', 'IsEIRE']
Get monthly transactiins for customers
cust_month_trans = pd.pivot_table(data=df,
index=['CustomerID'],
values='InvoiceNo',
columns=['InvoiceMonth'],
aggfunc=pd.Series.nunique,
fill_value=0).reset_index()
cust_month_trans = cust_month_trans.rename_axis('index',axis=1)
customer_id = ['CustomerID']
target = ['2011-11']
Define X and y
y = cust_month_trans[target]
cols = [col for col in df_features.columns if col not in customer_id]
X = df_features[cols]
# 线性回归
*线性回归*是回归问题最简单最经典的线性方法。线性回归找出参数 *w* 和 *b,*使得最小化预测值和实际值之间的*均方误差(MSE)* , *y* 。MSE 是预测值和实际值之差的平方和除以样本数。通常,线性回归这样简单而经典的模型是一个很好的起点,它易于应用,可以成为更复杂的机器学习模型的基准。线性回归没有参数,这是一个好处,但它也没有办法控制模型的复杂性[3,4]。
线性模型的一般预测公式:

其中, *x[0]* 到 *x[p]* 表示单个数据点的特征(在本例中,特征数为*p+1*),*w*和 *b* 是学习到的模型参数,*y’*是预测模型 makes。
让我们看看实例化和训练线性回归模型的代码。
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
X_train, X_test, y_train, y_test = train_test_split(X, y,
test_size=0.25,
random_state=23)
linreg = LinearRegression()
model = linreg.fit(X_train, y_train)
train_pred_y = model.predict(X_train)
test_pred_y = model.predict(X_test)
# **模型评估**
让我们看一下指标,我们将使用这些指标来评估线性回归模型。
## **均方误差**

其中 *Y* 为实际值,*Y’*为预测值。
MSE 测量平方误差的平均值,其中误差是预测值和实际值之间的差异。MSE 对异常值非常敏感,并受到异常值的严重影响[2]。
## **R 平方**

r 平方或决定系数衡量拟合优度。它表明回归模型对数据的拟合程度。r 平方的范围从 0 到 1,决定了模型中考虑的数据变化的比例[2,4]。
from sklearn.metrics import mean_absolute_error
from sklearn.metrics import mean_squared_error
import statsmodels.api as sm
Calculate MAE & MSE
mse_train = np.sqrt(mean_squared_error(y_train, train_pred_y))
mae_train = mean_absolute_error(y_train, train_pred_y)
mse_test = np.sqrt(mean_squared_error(y_test, test_pred_y))
mae_test = mean_absolute_error(y_test, test_pred_y)
OLS regression
y_train = np.array(y_train)
ols_reg = sm.OLS(y_train, X_train)
ols_reg = ols_reg.fit()
测试集的 set = 0.944,训练集的 set = 0.878,这没有太大的差异,表明我们没有模型的显著过拟合或欠拟合。
让我们看看 OLS(普通最小二乘法)回归结果的总结

如果我们将统计显著性确定为 95%,那么具有小于或等于 5%的 *p 值*的特征被认为具有统计显著性。查看 *p 值*,我们可以说*频率*和 *IsEIRE* 特征对于预测下个月的交易数量具有统计意义。
r 平方= 0.649,这意味着模型解释了约 64.9%的变异,这是相对较好的。
# 摘要
在这篇博文中,我想强调特征工程在营销数据科学中的重要性。我相信特性工程部分应该与我们试图解决的业务问题直接相关。
总的来说,这篇博文
* 我们使用线性回归,根据工程 RFM 特征预测了下个月的交易
* 查看 *p 值*,我们可以说*频率*和 *IsEIRE* 特征对于预测下个月的交易数量具有统计意义
* r 平方= 0.649,这意味着模型解释了约 64.9%的变异,这是相对较好的。但是,我们可以创建更多的功能和/或拟合其他模型,如多项式、山脊或套索模型,以改善度量或尝试其他模型。
感谢您的阅读,请在下面评论您对通过功能工程解决营销问题的想法。要查看我的更多帖子,请订阅 Medium 和 [LinkedIn](https://www.linkedin.com/in/aigerimshopenova/) 。
[Jupyter 笔记本](https://github.com/aig3rim/Predict_next_month_transactions)可以在我的 [GitHub](https://github.com/aig3rim) 上找到。
# 参考
1. t .西格尔(2021 年 2 月 15 日)。内部新近性、频率、货币价值(rfm)。2021 年 4 月 29 日检索,来自[https://www . investopedia . com/terms/r/RFM-recency-frequency-monetary-value . ASP](https://www.investopedia.com/terms/r/rfm-recency-frequency-monetary-value.asp)
2. 黄耀辉(2019)。市场营销数据科学实践:使用 Python 和 R. Birmingham 通过机器学习改进您的市场营销策略。
3. 米勒,A. C .,&圭多,S. (2018)。Python 机器学习导论:数据科学家指南。塞瓦斯托波尔,加利福尼亚州:奥赖利媒体。
4. 布鲁斯,P. C .,布鲁斯,a .,&格德克,P. (2020)。*数据科学家实用统计学:使用 R 和 Python 的 50 多个基本概念*。塞瓦斯托波尔,加利福尼亚州:奥赖利媒体。


浙公网安备 33010602011771号