熊猫数据可视化的终极备忘单
熊猫所有基本类型的可视化,以及一些非常有用和省时的高级可视化
我们使用 python 的 pandas 库主要是为了在数据分析中进行数据操作。但是我们也可以用熊猫来进行数据可视化。您甚至不需要为此导入 Matplotlib 库。Pandas 本身可以在后台使用 Matplotlib,为你渲染可视化。使用一个数据帧或一系列数据可以很容易地绘制图表。熊猫使用比 Matplotlib 更高级的 API。因此,它可以使用更少的代码行来绘制图表。
我将从使用随机数据的非常基本的图开始,然后转向使用真实数据集的更高级的图。
在本教程中,我将使用 Jupyter 笔记本环境。如果你没有安装,你可以简单地使用谷歌 Colab 笔记本电脑。你甚至不用在上面装熊猫。它已经为我们安装好了。
如果你想安装一台 Jupyter 笔记本,这也是一个好主意。请继续安装 anaconda 包。
对于数据科学家来说,这是一个非常棒的软件包,而且是免费的。
然后使用以下命令安装 pandas:
pip install pandas
或者在你的蟒蛇提示中
conda install pandas
你准备好摇滚了!
熊猫可视化
我们将从最基本的开始。
线条图
首次进口熊猫。那么,我们就在《熊猫》里做一个基础系列,做一个台词剧情吧。
import pandas as pd
a = pd.Series([40, 34, 30, 22, 28, 17, 19, 20, 13, 9, 15, 10, 7, 3])
a.plot()
最基本最简单的剧情都准备好了!看,多简单。我们可以稍微改进一下。
我要补充的是:
一个数字大小,使地块的大小更大,
要更改默认的蓝色,
上面的标题显示了这个情节的内容
和字体大小来更改坐标轴上这些数字的默认字体大小
a.plot(figsize=(8, 6), color='green', title = 'Line Plot', fontsize=12)
在本教程中,我们将学习更多的造型技巧。
区域地块
我将使用相同的系列“a ”,并在这里绘制一个面积图,
我可以用。plot()方法,并传递一个参数 kind 来指定我想要的绘图类型:
a.plot(kind='area')
或者我可以这样写
a.plot.area()
我上面提到的两种方法都会创建这个图:
当有几个变量时,面积图更有意义,看起来也更好。因此,我将制作几个系列,制作一个数据表格,并从中制作一个面积图。
b = pd.Series([45, 22, 12, 9, 20, 34, 28, 19, 26, 38, 41, 24, 14, 32])
c = pd.Series([25, 38, 33, 38, 23, 12, 30, 37, 34, 22, 16, 24, 12, 9])
d = pd.DataFrame({'a':a, 'b': b, 'c': c})
现在让我们把这个数据框画成面积图,
d.plot.area(figsize=(8, 6), title='Area Plot)
您不必接受这些默认颜色。让我们改变这些颜色,并添加一些更多的风格。
d.plot.area(alpha=0.4, color=['coral', 'purple', 'lightgreen'],figsize=(8, 6), title='Area Plot', fontsize=12)
可能参数α对你来说是新的。
“alpha”参数为绘图增加了一些半透明的外观。
当我们有重叠的面积图、直方图或密集散点图时,它显得非常有用。
这个。plot()函数可以制作十一种类型的图:
- 线条
- 区域
- 酒吧
- barh
- 馅饼
- 箱子
- 赫克宾
- 嘘
- kde
- 密度
- 分散
我想展示所有这些不同情节的用法。为此,我将使用美国疾病控制和预防中心的 NHANES 数据集。我下载了这个数据集,并把它保存在 Jupyter 笔记本所在的文件夹中。请随意下载数据集并跟随:
https://github.com/rashida048/Datasets/blob/master/nhanes_2015_2016.csv
在这里,我导入数据集:
df = pd.read_csv('nhanes_2015_2016.csv')
df.head()
这个数据集有 30 列和 5735 行。
在开始绘制图之前,检查数据集的列很重要:
df.columns
输出:
Index(['SEQN', 'ALQ101', 'ALQ110', 'ALQ130', 'SMQ020', 'RIAGENDR', 'RIDAGEYR', 'RIDRETH1', 'DMDCITZN', 'DMDEDUC2', 'DMDMARTL', 'DMDHHSIZ', 'WTINT2YR', 'SDMVPSU', 'SDMVSTRA', 'INDFMPIR', 'BPXSY1', 'BPXDI1', 'BPXSY2', 'BPXDI2', 'BMXWT', 'BMXHT', 'BMXBMI', 'BMXLEG', 'BMXARML', 'BMXARMC', 'BMXWAIST', 'HIQ210', 'DMDEDUC2x', 'DMDMARTLx'], dtype='object')
这些列的名称可能看起来很奇怪。但是不要担心这个。在我们进行的过程中,我会继续解释这些列的含义。我们不会使用所有的列。我们将使用其中一些来练习这些情节。
直方图
我会用人口的权重做一个基本的直方图。
df['BMXWT'].hist()
提醒一下,直方图提供了频率的分布。上图显示,约 1825 人体重 75。最大人数的权重范围为 49 到 99。
如果我想在一个图中放几个直方图呢?
我将使用体重、身高和身体质量指数(身体质量指数)在一张图中绘制三个直方图。
df[['BMXWT', 'BMXHT', 'BMXBMI']].plot.hist(stacked=True, bins=20, fontsize=12, figsize=(10, 8))
但是,如果您想要三个不同的直方图,也可以只使用一行代码,如下所示:
df[['BMXWT', 'BMXHT', 'BMXBMI']].hist(bins=20,figsize=(10, 8))
还可以更动感!
我们在“BPXSY1”列中有收缩压数据,在“DM deduct 2”列中有教育水平。如果我们想检查每个教育水平的人群中收缩压的分布,也只需一行代码即可完成。
但在此之前,我想用更有意义的字符串值替换“DM deduct 2”列的数值:
df["DMDEDUC2x"] = df.DMDEDUC2.replace({1: "less than 9", 2: "9-11", 3: "HS/GED", 4: "Some college/AA", 5: "College", 7: "Refused", 9: "Don't know"})
现在做直方图,
df[['DMDEDUC2x', 'BPXSY1']].hist(by='DMDEDUC2x', figsize=(18, 12))
看啊!我们仅用一行代码就获得了每个教育水平的收缩压水平分布!
柱状图
现在我们来看看收缩压是如何随婚姻状况变化的。这次我将制作一个条形图。像以前一样,我将用更有意义的字符串替换“DMDMARTL”列的数值。
df["DMDMARTLx"] = df.DMDMARTL.replace({1: "Married", 2: "Widowed", 3: "Divorced", 4: "Separated", 5: "Never married", 6: "Living w/partner", 77: "Refused"})
为了制作柱状图,我们需要对数据进行预处理。也就是把数据按不同的婚姻状况分组,取每组的平均值。在这里,我在同一行代码中处理数据和绘图。
df.groupby('DMDMARTLx')['BPXSY1'].mean().plot(kind='bar', rot=45, fontsize=10, figsize=(8, 6))
这里我们使用“rot”参数将 x 刻度旋转 45 度。否则,它们会太杂乱。
如果你愿意,你也可以把它做成水平的,
df.groupby('DMDEDUC2x')['BPXSY1'].mean().plot(kind='barh', rot=45, fontsize=10, figsize=(8, 6))
我想做一个多变量的柱状图。我们有一列包含人口的民族血统。看看人们的体重、身高和身体质量指数是否会随着种族的不同而变化,这将是一件有趣的事情。
为了绘制图表,我们需要将这三列(体重、身高和身体质量指数)按种族分组并取平均值。
df_bmx = df.groupby('RIDRETH1')['BMXWT', 'BMXHT', 'BMXBMI'].mean().reset_index()
这一次我没有更改民族血统数据。我保留了原来的数值。现在让我们制作柱状图,
df_bmx.plot(x = 'RIDRETH1',
y=['BMXWT', 'BMXHT', 'BMXBMI'],
kind = 'bar',
color = ['lightblue', 'red', 'yellow'],
fontsize=10)
看起来第四种族比其他种族高一点。但是都很接近。没有显著差异。
我们也可以将不同的参数(体重、身高和体重指数)相互叠加。
df_bmx.plot(x = 'RIDRETH1',
y=['BMXWT', 'BMXHT', 'BMXBMI'],
kind = 'bar', stacked=True,
color = ['lightblue', 'red', 'yellow'],
fontsize=10)
派剧情
这里我想检查一下婚姻状况和教育是否有关系。
我需要按教育程度对婚姻状况进行分组,并按教育程度统计每个婚姻状况组中的人口。听起来太罗嗦了吧?让我们看看:
df_edu_marit = df.groupby('DMDEDUC2x')['DMDMARTL'].count()
pd.Series(df_edu_marit)
使用这个系列很容易做出一个饼图:
ax = pd.Series(df_edu_marit).plot.pie(subplots=True, label='',
labels = ['College Education', 'high school',
'less than high school', 'Some college',
'HS/GED', 'Unknown'],
figsize = (8, 6),
colors = ['lightgreen', 'violet', 'coral', 'skyblue', 'yellow', 'purple'], autopct = '%.2f')
这里我添加了一些样式参数。请随意尝试更多的样式参数。
箱线图
例如,我将使用体重指数、腿和臂长数据绘制一个方框图。
color = {'boxes': 'DarkBlue', 'whiskers': 'coral',
'medians': 'Black', 'caps': 'Green'}
df[['BMXBMI', 'BMXLEG', 'BMXARML']].plot.box(figsize=(8, 6),color=color)
散点图
对于一个简单的散点图,我想看看身体质量指数(“BMXBMI”)和收缩压(“BPXSY1”)之间是否有任何关系。
df.head(300).plot(x='BMXBMI', y= 'BPXSY1', kind = 'scatter')
这太简单了!我只使用了 300 个数据,因为如果我使用所有的数据,散点图会变得过于密集,难以理解。尽管你可以使用 alpha 参数使其半透明。在本教程中,我倾向于保持轻松。
现在,让我们用同一行代码检查一个高级散点图。
这次我将添加一些颜色阴影。我将绘制一个散点图,x 轴表示重量,y 轴表示高度。有一点曲折!
我还会加上腿的长度。但是腿的长度会显示在阴影中。如果腿的长度更长,颜色会更深,否则颜色会更浅。
df.head(500).plot.scatter(x= 'BMXWT', y = 'BMXHT', c ='BMXLEG', s=50, figsize=(8, 6))
它显示了体重和身高的关系。你可以看看腿的长度与身高和体重之间是否有关系。
另一种增加第三个参数的方法是增加粒子的大小。在这里,我把身高放在 x 轴,体重放在 y 轴,体重指数作为气泡大小的指标。
df.head(200).plot.scatter(x= 'BMXHT', y = 'BMXWT',
s =df['BMXBMI'][:200] * 7,
alpha=0.5, color='purple',
figsize=(8, 6))
这里,小点表示身体质量指数较低,大点表示身体质量指数较高。
Hexbin
另一种美丽的可视化形式,点是六角形的。当数据太密集时,将它们放在箱中是有用的。正如你所看到的,在前面的两个图中,我只使用了 500 和 200 个数据,因为如果我把所有的数据都放在数据集中,图会变得太密集,无法理解或从中获得任何信息。
在这种情况下,使用空间分布会非常有用。我用的是 hexbin,数据用六边形表示。每个六边形是一个箱,代表该箱的密度。这是一个最基本的六邻体蛋白的例子。
df.plot.hexbin(x='BMXARMC', y='BMXLEG', gridsize= 20)
这里,较深的颜色代表较高的数据密度,较浅的颜色代表较低的数据密度。
听起来像直方图吗?是的,对吗?它不是用线条,而是用颜色来表示。
如果我们增加一个额外的参数 C,分布就会改变。它将不再像一个直方图。
参数“C”指定每个(x,y)坐标的位置,对每个六边形面元进行累加,然后使用 *reduce_C_function 进行缩减。*如果没有指定 reduce_C_ function,默认使用 np。卑鄙。你可以把它指定为 np。意思是,np。马克斯,np。sum,np。std 等。
查看文档了解更多信息 这里
这里有一个例子:
df.plot.hexbin(x='BMXARMC', y='BMXLEG', C = 'BMXHT',
reduce_C_function=np.max,
gridsize=15,
figsize=(8,6))
这里六边形的深色表示 np。max 有一个更高的人口高度值(’ BMXHT '),你可以看到我用了 np。max 作为 reduce_C_function。您可以使用色彩映射表来代替颜色阴影:
df.plot.hexbin(x='BMXARMC', y='BMXLEG', C = 'BMXHT',
reduce_C_function=np.max,
gridsize=15,
figsize=(8,6),
cmap = 'viridis')
看起来很漂亮,对吧?而且信息量也很大。
一些高级的可视化
我解释了一些人们在日常生活中处理数据时使用的基本绘图。但是数据科学家需要更多。熊猫图书馆也有一些更先进的可视化。这可以在一行代码中提供更多的信息。
散点 _ 矩阵
Scatter_matrix 非常有用。它在一个图中提供了大量的信息。它可以用于一般的数据分析或机器学习的特征工程。我们先来看一个例子。之后我会解释。
from pandas.plotting import scatter_matrixscatter_matrix(df[['BMXWT', 'BMXHT', 'BMXBMI', 'BMXLEG', 'BMXARML']], alpha = 0.2, figsize=(10, 8), diagonal = 'kde')
看那个!我在这里使用了五个特性。我得到了所有五个变量之间的关系。在对角线上,它给出了每个单独特征的密度图。我们将在下一个例子中讨论密度图。
KDE 或密度图
构建 KDE 图或核密度图是为了提供数据帧中一个系列或一列的概率分布。让我们来看看权重变量(’ BMXWT ')的概率分布。
df['BMXWT'].plot.kde()
你可以在一张图中看到几种概率分布。在这里,我在同一张图中绘制了身高、体重和身体质量指数的概率分布:
df[['BMXWT', 'BMXHT', 'BMXBMI']].plot.kde(figsize = (8, 6))
您也可以使用我们之前描述的其他样式参数。我喜欢保持简单。
平行 _ 坐标
这是显示多维数据的好方法。它清楚地显示了集群(如果有的话)。例如,我想看看男性和女性在身高、体重和身体质量指数方面是否有差异。让我们检查一下。
from pandas.plotting import parallel_coordinatesparallel_coordinates(df[['BMXWT', 'BMXHT', 'BMXBMI', 'RIAGENDR']].dropna().head(200), 'RIAGENDR', color=['blue', 'violet'])
你可以看到男性和女性在体重、身高和身体质量指数方面的明显差异。在这里,1 是男性,2 是女性。
自举 _ 剧情
这对于研究和统计分析是非常重要的。会节省很多统计分析的时间。自举图用于评估给定数据集的不确定性。
该函数随机抽取指定大小的样本。然后计算该样本的平均值、中间值和中间值。这个过程重复指定的次数。
在这里,我使用身体质量指数数据创建了一个自举图:
from pandas.plotting import bootstrap_plotbootstrap_plot(df['BMXBMI'], size=100, samples=1000, color='skyblue')
这里,样本量为 100,样本数为 1000。因此,我们随机抽取了 100 个数据样本来计算平均值、中间值和中间值。这个过程重复 1000 次。
这是一个极其重要的过程,也是统计学家和研究人员节省时间的方法。
结论
我想为熊猫的数据可视化做一个备忘单。虽然如果你使用 matplotlib 和 seaborn,有更多的可视化选项或类型。但是如果我们处理数据,我们在日常生活中会用到这些基本的可视化类型。使用 pandas 进行可视化将使您的代码更加简单,并节省大量代码。
欢迎在推特上关注我,喜欢我的脸书页面。
更多阅读:
用 Python 的 Seaborn 库实现数据可视化的终极指南
由 Unsplash 上的 CHUTTERSNAP 拍摄
一大堆情节
Seaborn 是一个基于 Matplotlib 的 python 数据可视化库。seaborn 有什么特别的?我们已经有了 Maplotlib,为什么还需要使用 seaborn?Matplotlib 可以满足您的需求。它具有执行数据故事项目所需的所有可视化功能。但是 seaborn 很特别,因为它有很多风格。样式已经内置了。与普通的 matplotlib 图相比,普通的 seaborn 图看起来要好得多!
此外,seaborn 库具有高级可视化功能,更具表现力,能够更有效地表达更多信息。
一点背景知识。如果您不熟悉 python 中的数据可视化,或者需要复习 Matplotlib,请看看这篇文章:
您也可以在 Pandas 中执行数据可视化。当你在 pandas 中调用 plot()函数时,它在后台使用 Matplotlib。
复习部分完成了。让我们现在潜入海底。
我将从基本的情节开始,然后慢慢转向一些更高级的。
我主要使用内置数据集。因此,安装了 seaborn 库的任何人都可以很容易地获得它们。
我将反复使用相同的变量,以节省寻找新数据集的时间。我的目标是为你展示可视化功能的选择。
首先导入必要的包和著名的 iris 数据集:
import matplotlib.pyplot as plt
import pandas as pd
import seaborn as snsiris = sns.load_dataset('iris')
iris
从 Matplotlib 中非常基本的散点图开始,然后是 Seaborn,以显示相同图中基本部分的差异。Matplotlib 中萼片长度与萼片宽度的基本散点图:
plt.scatter(iris.sepal_length, iris.sepal_width)
这是《海波恩》中相同的基本情节:
sns.set()
plt.scatter(iris.sepal_length, iris.sepal_width)
您可以看到,它甚至没有编写太多额外的代码就添加了一种样式!
我会尽量保持精确。大部分代码几乎是不言自明的。如果你是为了学习而阅读这篇文章的话,请把代码拿到你自己的笔记本上运行,改变不同的选项,然后玩一玩。这是唯一的学习方法。
在前面的情节中你已经看到。set()函数可以在绘图中引入默认的 seaborn 样式。下面是 set_style()函数的一个例子。
sns.set_style('whitegrid')
plt.scatter(iris.sepal_length, iris.sepal_width)
plt.show()
set_style()函数有一些其他的样式选项:深色网格、深色、白色和刻度。请随意试用它们。
下一个图也将是萼片长度对萼片宽度。但是花瓣长度参数也将被添加到它。圆点的大小会根据花瓣的长度而改变。
sns.set_style('darkgrid')
sns.set_context('talk', font_scale=1.1)
plt.figure(figsize=(8, 6))
sns.scatterplot(iris.sepal_length, iris.sepal_width, data=iris)
plt.xlabel("Sepal Length")
plt.ylabel("Sepal Width")
plt.title("Sepal Length vs Sepal Width")
plt.show()
圆点越大,花瓣越长。
此图中还引入了另一个新功能。那就是 set_context() 。它控制线条、标签和其他类似参数的大小。在该图中使用了“talk”选项。set _ context()函数中还有“纸张”、“笔记本”和“海报”选项。请检查一下。
这里可以轻松地添加一个变量。我将在这块土地上增加花的种类。不同物种的圆点颜色会有所不同。
sns.set_context('talk', font_scale=1.1)
plt.figure(figsize=(8, 6))
sns.scatterplot(iris.sepal_length, iris.sepal_width,
size="petal_length", data=iris,
sizes=(20, 500), hue="species",
alpha=0.6, palette="deep")
plt.xlabel("Sepal Length")
plt.ylabel("Sepal Width")
plt.title("Sepal Length vs Sepal Width")
plt.legend(bbox_to_anchor = (1.01, 1), borderaxespad=0)
plt.show()
重新绘图
relplot 函数既有趣又能提供信息。 **Relplots 可以是折线图或散点图。**这是一个折线图,每条线都显示置信区间。
如果不想要置信带,在 relplot 函数中添加“ci = None”。
我不会那么做的。因为我想要信心乐队。
sns.relplot(iris.sepal_length, iris.sepal_width,
data=iris, kind='line', hue='species')
plt.xlabel("Sepal Length")
plt.ylabel("Sepal Width")
plt.title("Sepal Length vs Sepal Width")
plt.show()
距离图
distplot 给出了直方图,一个连续变量的分布。这里有一个基本的。
plt.figure(figsize=(8, 6))
sns.distplot(iris.sepal_length)
plt.show()
如果您不想要密度曲线,在 distplot 函数中添加 kde = False 。下一个图是没有密度曲线的垂直直方图。
plt.figure(figsize=(8, 6))
sns.distplot(iris.sepal_length, vertical=True, kde=False, color='red')
plt.show()
直方图甚至可以提供更多信息。您可以用分类变量分隔连续变量的直方图。为了演示,我将使用不同的数据集。
tips = sns.load_dataset("tips")
tips.head()
该图将显示每个物种的鳍状肢长度,并按性别分类。
g = sns.displot(
tips, x="total_bill", col="day", row="sex",
binwidth=3, height=3, facet_kws=dict(margin_titles=True))
g.fig.set_size_inches(18, 10)
g.set_axis_labels("Total Bill", "Frequency")
因此,我们将账单总额的分布按照星期几和性别进行了划分。
也可以制作类似的重绘图。下面的 relplot 显示了按一周中的某一天和一天中的某个时间划分的账单总额与小费的散点图。
sns.set_context('paper', font_scale=1.8)
sns.relplot('total_bill', 'tip', data=tips, hue="time", col='day', col_wrap=2)
条形图
另一个广泛使用和流行的情节。在小费数据集中,我将在 x 轴上使用“大小”变量,总账单将在 y 轴上绘制。
总账单将按午餐和晚餐时间分开。
plt.figure(figsize=(8, 6))
sns.barplot(x='size', y= 'total_bill', hue='time',
palette = 'GnBu',
data=tips, ci='sd',
capsize=0.05,
saturation=5,
errcolor='lightblue',
errwidth=2)
plt.xlabel("Size")
plt.ylabel("Total Bill")
plt.title("Total Bill Per Day of Week")
plt.show()
注意,我在这里使用调色板作为“GnBu”。seaborn 库中有几个不同的调色板。在 页面 找到不同的调色板选项。
如果你对统计学感兴趣,你会喜欢这里的“ci”选项。否则,用’ ci=None’ 就可以避免。
计数图
计数图看起来也像条形图。但是它显示了每个类别的观察计数。
plt.figure(figsize=(8, 6))
sns.countplot(x='day', data=tips)
plt.xlabel("Day")
plt.title("Total Bill Per Day of Week")
plt.show()
该图显示了一周中每天有多少数据可用。“hue”参数在这里也可以用于通过另一个分类变量将其分离。我在考虑“时间”变量。
plt.figure(figsize=(8, 6))
sns.countplot(x = 'day', hue='time',
palette = 'GnBu',
data=tips)
plt.xlabel("Day")
plt.title("Tip Per Day of Week")
plt.show()
群集图
该图确保数据不会重叠。剧情之后再多解释。
plt.figure(figsize=(8, 6))
sns.set_style('whitegrid')
sns.swarmplot(x='size', y='total_bill', data=tips)
plt.xlabel("Size")
plt.ylabel("Total Bill")
plt.title("Total bill per size of the table")
plt.show()
当大小为 1 时,只有三个点,它们在同一条线上,自然不会重叠。但是当大小为 2 时,在同一个点上有很多数据,所以默认情况下 swarmplot 稍微调整了点的位置,使它们不相互重叠。
这看起来不错,而且当数据集不太大时,还可以更好地了解每个点有多少数据。如果数据集太大,群集图就不能很好地缩放。
在下一个图中,我将添加一个“色调”参数,它将显示不同性别的不同颜色。
plt.figure(figsize=(10, 6))
sns.set_style('whitegrid')
sns.set(font_scale=1.5)
sns.swarmplot(x='size', y='total_bill', data=tips, hue="sex")
plt.xlabel("Day")
plt.ylabel("Total Bill")
plt.legend(title="Time", fontsize=14)
plt.show()
性别隔离也可以被分开,
plt.figure(figsize=(10, 6))
sns.set_style('whitegrid')
sns.set(font_scale=1.5)
sns.swarmplot(x='size', y='total_bill', data=tips, hue="sex", split=True)
plt.xlabel("Size")
plt.ylabel("Total Bill")
plt.legend(title="Time", fontsize=14)
plt.show()
在这片土地上,雄性和雌性有不同的群体。
还有另一种称为因子图的图,它与群集图相同,但它是面网格图。您可以添加多个变量并提供更多信息。
g = sns.factorplot(x='size', y="tip",
data=tips, hue="time",
col="day", kind="swarm",
col_wrap=2, size=4)g.fig.set_size_inches(10, 10)
g.set_axis_labels("Size", "Tip")
plt.show()
该图显示了一周中每一天每份食物的小费金额,不同的颜色代表了用餐的不同时间。如此多的信息被压缩在一个情节中!
点图
点图可以提供很多信息,比条形图更有用。这是一个显示一周中每天小费金额的点图。情节之后我会再解释一些。
plt.figure(figsize=(8, 6))
sns.pointplot(x="day", y="tip", data=tips)
plt.xlabel("Day")
plt.ylabel("Tip")
plt.title("Tip Per Day of Week")
plt.show()
这里的点表示平均值,垂直线表示置信区间。有时候少即是多。简单而又丰富的情节。
可以在这里添加一个“色调”参数,通过另一个分类变量显示一周中每天的小费。我在这里用了性别。
plt.figure(figsize=(8, 6))
sns.pointplot(x="day", y="tip", hue="sex", data=tips, palette="Accent")
plt.xlabel("Day")
plt.ylabel("Tip")
plt.title("Tip Per Day of Week by Gender")
plt.show()
性别在小费金额上的差异如此明显!
Regplot
这实际上是一个散点图,增加了一条线性回归线和一个置信带。
plt.figure(figsize=(8, 6))
sns.set_style('whitegrid')
sns.regplot(x='total_bill', y='tip', data=tips)
plt.xlabel("Total Bill")
plt.ylabel("Tip")
plt.show()
接合图
联合绘图仅用一行代码在一个绘图中显示两种不同类型的绘图。默认情况下,散点图位于中心,x 和 y 变量的分布位于边缘。“hue”参数在这里是可选的。有需要就用。
sns.set_style('dark')
g = sns.jointplot(x='total_bill', y='tip', hue='time', data=tips)
g.fig.set_size_inches(8, 8)
g.set_axis_labels("Total Bill", "Tip")
plt.show()
该图是一个散点图,显示了账单总额与小费金额之间的关系,按“时间”进行划分。不同的颜色显示了用餐的不同时间。侧图显示了午餐和晚餐时间总账单和小费金额的分布。
如果您不喜欢默认选项,还有几个其他选项可用。这里我明确提到 regplot,它是一个散点图,带有一条线性回归线和置信带。
sns.set_style('darkgrid')
g = sns.jointplot(x='total_bill', y='tip', data=tips, kind='reg')
g.fig.set_size_inches(8, 8)
g.set_axis_labels("Total Bill", "Tip")
plt.show()
下一个图将不是散点图,而是 kde 图,
sns.set_style('darkgrid')
g = sns.jointplot(x='total_bill', y='tip', data=tips, kind='kde')
g.fig.set_size_inches(8, 8)
g.set_axis_labels("Total Bill", "Tip")
plt.show()
在这个 kde 图中可以随意使用“色调”参数,
sns.set_style('darkgrid')
g = sns.jointplot(x='total_bill', y='tip', hue='time', data=tips, kind='kde')
g.fig.set_size_inches(8, 8)
g.set_axis_labels("Total Bill", "Tip")
plt.show()
比起阴影线,kde 图总是更吸引我。下面有一个带阴影的 kde 图。
plt.figure(figsize=(8, 6))
sns.set_style('whitegrid')
g = sns.kdeplot(x='total_bill', y='tip', shade=True, data=tips)
plt.xlabel("Total Bill")
plt.ylabel("Tip")
plt.show()
阴影图显示了数据的密度。我觉得它更有表现力。
回到 jointplot,这里有一个 jointplot 中 hexplot 的例子。又一个美好的情节。
sns.set_style('dark')
g = sns.jointplot(x='total_bill', y='tip', data=tips, kind='hex')
g.fig.set_size_inches(8, 8)
g.set_axis_labels("Total Bill", "Tip")
plt.show()
当数据集太大时,Hexplot 特别有用。
抖动图
抖动图有点像之前展示的蜂群图。这一个也稍微调整了点的坐标以避免太多的混乱。但是有点不一样。在蜂群图中,没有一个点在另一个点的上面。但在抖动图中,它仅展开指定的量。下面是一个抖动图,指定抖动量为 0.2。此外,默认情况下,它添加了一个线性回归线和一个置信区间,这很好!
plt.figure(figsize=(8, 6))
sns.set_style('whitegrid')
sns.regplot(x='size', y='total_bill', data=tips, x_jitter=0.2)
plt.xlabel("Size")
plt.ylabel("Total Bill")
plt.show()
注意,这里的 x 轴包含一个分类变量。
lmplot
lmplot 是 regplot 和 facet grid 的组合。该图可以显示每个条件组的线性回归线和置信带。这听起来可能有点晦涩。请看这个情节。
sns.set(font_scale=1.5)
sns.lmplot(x='total_bill', y='tip', data = tips,
hue='time')
plt.gcf().set_size_inches(12, 8)
plt.ylabel("Total Bill")
plt.xlabel("Tip")
plt.show()
听着,午餐和晚餐时间都有回归线。
它甚至可以提供更多的信息。下面这张示意图显示的是每天的总账单和小费。
g = sns.lmplot(x='total_bill', y='tip', col="day", hue = "day",
data=tips, col_wrap=2, height=4)
g.fig.set_size_inches(11, 11)
g.set_axis_labels("Total Bill", "Tip")
plt.show()
箱线图
我不喜欢基本的箱线图。请看看我在开始提到的关于熊猫和 Matplotlib 的可视化教程,重温一下基本的情节。我喜欢箱线图,因为它能在同一个图中给出分布、中位数、IQR 和异常值的信息。下一个图将显示每个尺寸的总账单的箱线图。
sns.set(font_scale = 1.5)
sns.boxplot(x='size', y='total_bill', data=tips)
plt.gcf().set_size_inches(12, 8)
plt.xlabel("Size")
plt.ylabel("Total Bill")
如果您需要关于如何从箱线图中提取我之前提到的所有信息的提示,请阅读这篇文章:
紫罗兰花
这里是一个基本的小提琴情节。
ax = sns.violinplot(x=tips["total_bill"])
小提琴图显示了数据的分布。你可能会认为它就像一个直方图。可以,但是可以更高级。如下图所示,吸烟者和非吸烟者每天的总账单分配情况。
plt.figure(figsize=(10, 7))
sns.violinplot(x='day', y='total_bill', hue="smoker",
data=tips, palette="muted")
plt.xlabel("Day")
plt.ylabel("Total Bill")
plt.title("Total Bill per Day of the Week")
plt.show()
吸烟者和不吸烟者的部分可以显示在一把小提琴的不同侧面上,而不是由两把小提琴分开。看这个情节。
plt.figure(figsize=(10, 7))
sns.violinplot(x='day', y='total_bill', hue="smoker",
data=tips, palette="muted", split=True)
plt.xlabel("Day")
plt.ylabel("Total Bill")
plt.title("Total Bill per Day of the Week")
plt.show()
这里蓝色显示的是吸烟者总账单的分布,黄色代表不吸烟者。
小提琴情节可以与其他类型的情节结合。这是一个在小提琴图中显示蜂群图的例子。它看起来很好,并且给出了与这些分布相关的数据量的概念。
plt.figure(figsize=(10, 6))sns.violinplot(x='day', y='total_bill', inner=None,
data=tips, palette="muted")sns.swarmplot(x='day', y='total_bill',
data=tips, color="k", alpha=0.9)
plt.ylabel("Total Bill")
plt.xlabel("Day")
plt.title("Total Bill per Day")
plt.show()
热图
热图用于显示变量之间的相关性。热图在数据科学的许多领域非常有用。在数据故事项目中,这是一个受欢迎的元素,在机器学习中,它有助于选择特征。
这是一个基本的热图,显示了总账单和小费金额之间的关系。
sns.heatmap(tips[["total_bill", "tip"]].corr(), annot=True,
linewidths=0.9, linecolor="gray")
plt.show()
让我们回到虹膜数据集。看到萼片长度和宽度、花瓣长度和宽度之间的相互关系将会很有趣。
plt.figure(figsize=(8, 6))
sns.heatmap(iris.corr(), annot=True, linewidths=0.5, cmap='crest')
plt.show()
看色彩图。颜色越深,相关性越强。
facetgrid
我们以前处理过 facetgrid 样式的地块。但是没有直接使用函数 facet grid。以下是小平面网格函数的一个示例:
g = sns.FacetGrid(tips, col="time")
g.map(sns.scatterplot, "total_bill", "tip")
g.fig.set_size_inches(12, 8)
g.set_axis_labels("Total Bill", "Tip")
plt.show()
它还可以进一步被性别所分隔。
g = sns.FacetGrid(tips, col="time", row="sex")
g.map(sns.scatterplot, "total_bill", "tip")
g.fig.set_size_inches(12, 12)
g.set_axis_labels("Total Bill", "Tip")
plt.show()
配对图
又一个很有用的情节。你自己看个例子吧,之后我们会解释。
df = sns.load_dataset('iris')
sns.set_style('ticks')
sns.pairplot(df, hue="species", diag_kind='kde', kind='scatter', palette='husl')
plt.show()
该图显示了同一图中每对变量之间的关系。同时,给你每个连续变量的分布。我们在这里设置“色调=物种”来表示不同物种的不同颜色。这个图中包含了如此多的奇异图的信息。
本教程是不完整的,甚至没有显示一个时间序列热图。
我将在接下来的图中使用以下数据集:
https://github.com/rashida048/Datasets/blob/master/stock_data.csv
让我们导入数据集:
df = pd.read_csv("stock_data.csv", parse_dates=True, index_col = "Date")
df.head()
我们的目标是按月份和年份绘制“开放”数据的热图。为此,我们需要从“日期”中检索月份和年份,并分别列出“月份”和“年份”。
df['month'] = df.index.month
df['year'] = df.index.year
如果您重新检查数据集,您会发现其中有一个“月”和“年”列。使用 pandas pivot table 函数,创建月和年的数据集,其中月是索引,年是列,“开放”数据是值。
import calendar
all_month_year_df = pd.pivot_table(df, values="Open",
index=["month"],
columns=["year"],
fill_value=0,
margins=True)
named_index = [[calendar.month_abbr[i] if isinstance(i, int) else i for i in list(all_month_year_df.index)]] # name months
all_month_year_df = all_month_year_df.set_index(named_index)
all_month_year_df
数据集已准备好制作我们的热图!
plt.figure(figsize=(10,10))
ax = sns.heatmap(all_month_year_df, cmap='GnBu', robust=True, fmt='.2f',
annot=True, linewidths=.5, annot_kws={'size':11},
cbar_kws={'shrink':.8, 'label':'Open'})
ax.set_yticklabels(ax.get_yticklabels(), rotation=0, fontsize=10)
ax.set_xticklabels(ax.get_xticklabels(), rotation=0, fontsize=10)
plt.title('Average Opening', fontdict={'fontsize':18}, pad=14)
聚类图
聚类图也类似于热图。它不显示数字。仅按颜色进行分层聚类。让我们看一个例子:
sns.clustermap(all_month_year_df, linewidths=.5, cmap = "coolwarm")
查看该图中的 x 和 y 刻度标签。它们遵循集群的层次结构。2017 年最高,2016 年最低。如果您希望按顺序排列年份,请将“col_cluster”设置为 False。
sns.clustermap(all_month_year_df, linewidths=.5, cmap = "coolwarm", col_cluster=False)
现在,年是有序的。您也可以通过将“row_cluster”设置为 False 来按顺序排列月份。请你自己试试。
请随意查看这篇文章,以找到各种时间序列数据可视化选项:
结论
恭喜你。如果你今天真的在所有这些地块上工作了,你走了很长的路!Seaborn 是一个巨大的图书馆。当然,这些还不是全部。但是这篇文章涵盖了很多!关于这个库还有很多需要了解的。我希望在将来的某个时候能制作更多关于更多情节的教程。但在此之前,请随意阅读下面“更多阅读”部分的 python 文章中的高级可视化。他们在 Matplotlib 和 Seaborn 收集了更多的高级地块。
更多阅读:
Python 的终极(免费)数据可视化编译
在 Unsplash 上由 nine koepfer 拍摄的照片
学习使用更多的可视化功能和技术
如果您以任何方式处理数据,数据可视化是必不可少的。我非常关注这一点。我以前写过几篇关于 Python 中数据可视化的文章。我意识到如果我在一个页面上编译它们,它可能会成为一个巨大的数据绘图技术的集合。您可以从这里学到的数据可视化的数量可能会与任何付费的可视化课程相媲美。
Matplotlib
这可以说是 Python 中最流行、最常用的可视化库。还有其他基于 Matplotlib 构建的高质量 python 库。即使您使用 python 的其他一些库,学习 Matplotlib 也是一个好主意。下面这篇文章是我们在日常生活中使用的 Matplotlib 中所有基本图形的备忘单,以及一些指向更高级图形的链接:
这里是其中一些图的视频版本,包含一些更多的样式技术和一些更高级的选项,特别是柱状图、柱状图、箱线图和支线图:
熊猫
熊猫图书馆以数据操作和数据清理而闻名。但是它也有一些可视化的功能。当你使用它们时,它会在后台调用 Matplotlib 并为你渲染图形。问题是为什么我们需要用熊猫呢?答案是,它为您节省了几行代码,而且运行速度很快。在这里你可以找到熊猫图书馆中所有主要的可视化技术:
有时,在一个包含大量信息的图中使用多个图会很有帮助。此外,它还可以生成仪表板样式的绘图,这在报告或演示文稿中非常有用。本文解释了如何在绘图数组中进行完全控制和定制形状绘图:
高级可视化
Seaborn
Seaborn 库建立在 Matplotlib 之上。它受欢迎的原因是,它有一些内置的风格。最基本的情节也已经有了一些风格。因此,它节省了一些造型工作。同时,Seaborn 库有很多高级的可视化功能。只需几行代码,你就可以创造出惊人的情节。以下文章从基本的 Seaborn 情节开始,涵盖了许多高级情节:
时间序列可视化
它不是一个单独的图书馆。使用 Matplotlib 和 Seaborn,您可以实现出色的时间序列数据可视化。但是对于时间序列数据,你需要处理一些格式化的任务。所以,它需要一些关注。这就是为什么他们有自己的形象。在这里,您可以找到完整的时间序列可视化教程:
收集一些高级剧情
如果您经常处理数据,您可能希望让您的图看起来更有趣。尤其是当您必须制作大型报表时,最好选择不同的可视化样式。我整理了一些更有趣的可视化样式,不同的外观可能包含更多信息:
结论
Python 有几个库。甚至我主要关注的 Matplotlib 和 Seaborn 也是具有大量可视化选项的大型库。没有人能记住所有这些,除非你是一个超人或者多年来每天都在处理数据可视化。学习它们是一个好主意,这样你就知道你能做什么。然后准备一些资源,在需要的时候可以随时查看。所有这些可视化教程都可以用来学习,也可以用来做备忘单。希望有帮助。
更多阅读
匹配和倾向评分匹配终极指南
实践教程,使用观察数据进行因果推断
如何减少观察数据中混杂因素的影响
2021 年 8 月 15 日更新
介绍
随机对照试验(又名。A/B 测试)是确定干预和结果之间因果关系的黄金标准。RCT 的高效度源于其通过随机化过程对数据生成过程(DGP)的严密控制,使得实验组在很大程度上具有可比性。因此,我们可以将实验组之间最终指标的差异归因于干预。
它的缺点是,由于实际原因,RCT 在现实世界中并不总是可行的。公司没有实验基础设施来促进大规模测试。或者,高用户干扰会使个体水平随机化的任何结果无效。
在这种情况下,当 A/B 测试被搁置时,幸运的是我们有两种选择:准实验设计和观察设计。在几篇文章中,我介绍了几种准实验设计:差异中的差异、回归不连续设计、间断时间序列、和综合控制。
在今天的帖子中,我们转向观察性设计,重点关注两种观察方法——匹配和倾向得分匹配——并特别关注 PSM 的基本假设、限制和应用。
在未来的帖子中,我们将更深入地研究序列**“使用观察数据的因果推断”**,并讨论高级主题,如、倾向评分分层、治疗加权的逆概率和协变量调整。
对观测数据的一些抱怨
与具有清晰 DGP 的实验数据相反,研究人员不知道也不能控制治疗分配过程。我们只观察到一些受试者属于一个组(如治疗组),而其他受试者属于另一个组(如对照组),但不知道他们为什么会出现在那里。这两个群体之间的任何直接比较都是没有意义和误导性的。
为什么?
可能会有混杂变量影响分配过程和结果变量。
这是程序评估文献中的一个经典例子。比方说,我们正试图评估一个针对失业者的职业培训项目的有效性,这个项目是通过家庭年收入来衡量的。
由于道德约束,RCT 是不可能的。想想媒体的影响,如果公众意识到一部分人可以参加有益的社会项目,但不是所有人。
一个更好的选择是采用观察设计。
问题仍然是:我们能直接比较注册和未注册之间的目标度量来评估计划的有效性吗?
不要!
那些参与者可能比其他人更有动力和渴望找到工作。因此,结果指标的差异可能是由动机水平(一个混杂变量)而不是治疗状态(参与计划)引起的。换句话说,参与者即使不参加该计划也会做得更好(Gertler 等人,2011)。
我自己的截图
注:
- 虚线表示变量之间没有因果关系
- 实线表示因果关系
观察法的首要任务是找到一种方法来减少或消除选择偏差或混杂变量的影响。
选择可比反事实的三个条件
到一天结束时,因果推理是关于反事实的:如果没有干预,会发生什么?不幸的是,我们只能观察到两个潜在结果中的一个。从概念上来说,因果推理受到缺失数据问题的困扰,研究人员必须依靠标准程序来找到一个完美的克隆作为反事实。
由于随机化过程,这对于实验数据来说是一项简单的任务:我们可以在很大程度上相信治疗组和非治疗组之间的可比性。
然而,与实验方法相比,观察方法建立在更多的假设基础上,并做出仔细的推断。在选择控制案例时,我们必须牢记以下原则(Gertler 等人,2011 年):
宗旨 1:不要追求每个个体的相似性,而是群体水平的相似性。
原则 2:如果给予治疗,治疗组和对照组对治疗的反应应该相同。
原则 3:控制混杂因素。
宗旨 1 。无论你选择哪种估计量来估计,因果推断从来都不是关于每个个体单位的因果效应。相反,它是关于群体(总体)水平的治疗效果,平均*。*
宗旨二 。如果被分配到治疗组,两组对干预的反应是一样的。否则,就不可能获得因果效应。
宗旨 3 。如何控制混杂因素?
如何控制混杂因素?
如果混杂变量是可观察到的,我们可以通过将每个治疗个体与一个或多个对照相匹配来减少或消除协变量偏倚。假设倾向得分包含了关于选择过程的所有信息,那么倾向得分匹配获得了最佳的效率和一致性(Rosenbaum 和 Rubin,1983)。
不应有任何未观察到的变量;否则,估计量将是有偏差的和不一致的。
匹配
匹配是一个统计过程,试图根据关键的观察协变量将治疗受试者与对照受试者配对。
对于具有大量潜在对照的小治疗组,匹配是理想的。
基于匹配比例(一对一匹配、多对一匹配)、匹配对象的替换(有或无替换)、算法(贪婪、遗传或最优/完全匹配),有各种匹配策略(Kim 和 Steiner,2016)。
不可能在一篇博文中检查所有这些子类别。如果可能的话,我会在后续文章中研究一些最重要的方法。
倾向评分匹配
如果我们认为有多个混杂变量,由于缺乏数据,匹配所有变量可能是不可能的。作为一个解决方案,我们构建了一个比例条件概率接受治疗分配给定的协变量向量。
只有在少数罕见的情况下,已知分配治疗的概率,如在 RCT,真实的倾向评分仍然未知的观察设计。相反,我们必须使用 logit 或 probit 来估计它。
倾向得分是一个平衡得分:根据倾向得分,观察到的基线协变量的分布在治疗/对照受试者之间足够相似。换句话说,倾向得分为使用观察数据的有效因果推断提供了足够好的反事实。
倾向评分匹配模拟了 RCT,我们在倾向评分匹配样本中比较了治疗和未治疗受试者的结果(Austin,2011)。
然而,这是一个有争议的过程。正如 King 和 Nielsen (2019)所说,PSM 试图近似一个完全随机的实验,而不是一个分块随机的实验。因此,PSM 无法消除可以通过阻塞消除的协变量不平衡。我不会详细阐述这场辩论,把它留给以后的帖子。此外,金教授推荐其他类型的倾向得分分析,如倾向得分分层。
警告
PSM 有以下警告(Gertler 等人,2011 年):
警告 1:常见的支持。待治疗的倾向的范围在已治疗和未治疗的病例之间是相同或相似的。
警告 2:仅使用不受干预影响的基线特征进行匹配。
警告 3:潜在的混杂变量是可观察的,没有不可观察的变量;否则,估计是有偏差的。
警告 4:匹配最相关的特征,不应该将每个变量都放入等式中。
告诫 1 。在计算了治疗组和非治疗组的倾向后,我们得到了下图。在两个极端,缺乏共同的支持。在最右边,有更高概率接受治疗的人找不到可比较的对照。在最左边,接受治疗的概率较低的人找不到可比较的治疗案例。
在中档中有一个共同的支持,在未注册者和注册者之间有重叠。因此,我们不得不把我们的因果发现缩小到当地的平均治疗效果(晚期)。
格特勒等人,2011 年
警告 2 。在估计倾向得分时,我们不应该包括任何可能被干预改变的特征;否则,估计可能会有偏差。
告诫 3。没有正式的方法来测试它,我们必须依靠深入的领域知识来假设。在这种情况下,一些基于设计的想法就派上用场了。
首先,我们可以交叉检查不受干预影响的结果(Shadish 等人,2002)。
第二,我们可以添加第二个但概念上不同的对照组,允许对未受影响的结果进行类似的测试(Rosenbaum,2002)。
**告诫 4。仅包含决定注册状态的变量而非所有变量可提高估计值的精度。我们对登记过程了解得越多,倾向评分在构建可比群体时就越准确。
使用 PSM 的步骤 (Jalan 和 Ravallion,2003):
- 计算所有单位的倾向得分
- 按照某种匹配策略将治疗组与对照组进行匹配
- **检查协变量平衡&如果不平衡,使用替代规格重复步骤 1 和 2
- 计算治疗组和对照组之间结果的平均差异
应用程序
在这一部分,我将重复两项研究的结果(LaLonde,1986;德赫贾和瓦赫巴,1997 年)。请查看诺亚·格里弗( 链接 )的这篇帖子,获取完整的 R 代码和演练。读诺亚的 帖 ,我受益匪浅。
第 0 步:安装包并加载库
*#install.packages(“MatchIt”)
#install.packages(‘optmatch’)
library(“MatchIt”)
library(“optmatch”)
data(“lalonde”)
head(lalonde)*
我自己的截图
*Note: - The outcome variable is 1978 earnings ( ‘re78’)- The intervention: ‘treat’- 1974 earnings: re74- 1975 earnings: re75- and other control variables*
在这里,我们基于协变量(年龄、教育、种族、已婚、无学位、re74 和 re75)为每个观察构建了一个倾向得分,代表其加入该计划的倾向。
步骤 1:无匹配&比较协变量不平衡
*result_0 <- matchit(treat ~ age + educ + race + married + nodegree + re74 + re75, data = lalonde, method = NULL, distance = 'glm')result_0*
我自己的截图
*summary(result_0)*
我自己的截图
总共有 185 个经处理的观察值和 429 个未经处理的观察值。
标准差和 eCDF 统计接近 0 或方差比接近 1 表示良好的平衡;否则,不平衡。
在没有应用匹配方法的情况下,治疗组和非治疗组之间存在严重的不平衡。它们在标准均值差(Std)方面看起来非常不同。平均差异。),方差比(Var。比率),以及经验累积密度函数(eCDF)。
协变量失衡表明治疗前的选择偏差,因此我们不能将差异归因于干预。
步骤 2.1:最近邻居
*result_1 <- matchit(treat ~ age + educ + race + married + nodegree + re74 + re75, data = lalonde, method = “nearest”, distance = ‘glm’)result_1*
我自己的截图
我们应用最近的方法和 1:1 匹配最近的邻居。1:1 匹配意味着我们将一个治疗单位与一个具有最接近倾向得分的对照单位进行匹配。然后,这个控制单元将被从控制池中取出,并且不可用于其他情况(也称为。不更换)。对其余已处理的病例重复该过程。
*summary(result_1, un=FALSE)*
我自己的截图
*plot(result_1, type = “jitter”, interactive = FALSE)*
我自己的截图
*plot(result_1, type = “qq”, interactive = FALSE, which.xs = c(“age”, “married”, “re75”))*
我自己的截图
让我们检查三个变量的协变量的分布。远离实线对角线的点表示两组之间的协变量差异。两个变量,已婚和 re75,匹配后有更好的平衡,但对年龄没有改善。
在 1:1 匹配后,就 Std 而言,与不匹配相比,两组具有更好的平衡。平均差异。,Var。比率和 eCDF 统计。然而,仍然存在群体不平衡。让我们检查其他匹配方法并比较结果。
步骤 2.2:完全匹配和概率单位
*result_2 <- matchit(treat ~ age + educ + race + married + nodegree + re74 + re75, data = lalonde, method = “full”, distance = “glm”, link = “probit”)result_2*
我自己的截图
完全匹配将一个处理单元匹配到一个或多个控制单元(或一个控制单元匹配到一个或多个处理单元)()。此外,我们将链接函数从 logit 更改为 probit。
*summary(result_2, un = FALSE)*
我自己的截图
*plot(summary(result_2))*
我自己的截图
在应用完全匹配后,治疗组和非治疗组之间的协变量看起来更加平衡。
第三步:估计效果和标准误差
*m.data2 <- match.data(result_2)
m.data2*
我自己的截图
*library(‘lmtest’)
library(‘sandwich’) fit2 <- lm(re78 ~ treat + age + educ + race + married + nodegree +
re74 + re75, data = m.data2, weights = weights)coeftest(fit2, vcov. = vcovCL, cluster = ~subclass)*
最后一步,我们估计治疗效果及其标准误差。对于成对集合,我们使用聚类稳健的标准误差(查看 Noah 的帖子以获得解释)。治疗的效果是 1980 美元,标准误差为 756,统计显著性为 0.001。
结论
使用实验数据的因果推断依赖于温和的假设,但观察方法提出了更多的要求,需要更多的假设。对于观察数据,倾向评分匹配旨在通过匹配治疗组和未治疗组来减少干预前存在的偏差。
Medium 最近进化出了它的 作家伙伴计划 ,支持像我这样的普通作家。如果你还不是订户,通过下面的链接注册,我会收到一部分会员费。
*https://leihua-ye.medium.com/membership *
参考
德赫贾和瓦赫巴(1999 年)。非实验研究中的因果效应:重新评估训练计划的评估。美国统计协会杂志 94:1053–1062。
Gertler,P.J .,Martinez,s .,Premand,p .,Rawlings,L.B .和 Vermeersch,C.M .,2016 年。实际影响评估。世界银行。
Kim,y .和 Steiner,p .,2016 年。因果推理的准实验设计。教育心理学家,51(3–4),第 395–405 页。
拉隆德,R. (1986 年)。用实验数据评估训练计划的经济计量评估。美国经济评论 76:604–620。
格里弗,N .(2021 年)。MatchIt:入门
j .贾兰和 m .拉瓦里翁,2003 年。通过倾向得分匹配估计反贫困项目的受益发生率。商业杂志&经济统计, 21 (1),第 19–30 页。
相关阅读
*
喜欢读这本书吗?
还有,看看我其他关于人工智能和机器学习的帖子。*
熊猫时间序列分析终极指南
桑德拉·维尔廷格在 Unsplash 上的照片
在 Pandas 中执行时间序列分析所需的所有 Pandas 功能。您也可以将此用作备忘单。
什么是时间序列分析?
正是对数据集的分析才有了一系列时间戳。随着对机器学习的日益重视,它变得越来越重要。现在,许多不同类型的行业使用时间序列数据进行时间序列预测、季节性分析、发现趋势以及做出重要的商业和研究决策。所以作为数据科学家或数据分析师,清楚地理解时间序列数据是非常重要的。
时间序列数据用于:
银行和金融机构
股票市场
社会化媒体
电力、天然气和石油工业
机械或化学过程中的周期性措施
以及更多的领域
这是不可避免的。所以,我们来学学吧。
我将从一些通用函数开始,并使用脸书股票价格数据集展示更多主题。
让我们开始吧!
使用正确的格式
时间序列数据可以有多种不同的格式。但并非所有这些格式都对 python 的熊猫库友好。最方便的格式是熊猫的时间戳格式。但是大多数时候时间序列数据都是字符串格式的。这里我有一个不同格式的时间序列数据的例子。
import pandas as pd
import numpy as npdates = ['2020-11-25 2:30:00 PM', 'Jan 5, 2020 18:45:00', '01/11/2020', '2020.01.11', '2020/01/11', '20201105']
上面的“日期”变量显示了五种不同格式的日期时间设置,并且都是正确的。但是我们需要这种特定的格式来方便地工作。因此,将这些日期转换成正确的格式。
pd.to_datetime(dates)
输出:
DatetimeIndex(['2020-11-25 14:30:00', '2020-01-05 18:45:00',
'2020-01-11 00:00:00', '2020-01-11 00:00:00',
'2020-01-11 00:00:00', '2020-11-05 00:00:00'],
dtype='datetime64[ns]', freq=None)
世界上还有其他国家,他们首先使用天数。例如,在美式英语中,2002 年 6 月 1 日被写成“2020 年 6 月 1 日”。但是在英国,像印度、孟加拉国、巴基斯坦和世界其他一些地方的南亚国家把它写成“1/6/2020”。
如果你为来自世界其他地方的客户工作,以下是如何格式化日期。
pd.to_datetime(dates).strftime('%d-%m-%y')
输出:
Index(['25-11-20', '05-01-20', '11-01-20', '11-01-20', '11-01-20', '05-11-20'], dtype='object')
我只是在这里使用了“%d-%m-%y”作为格式。您可以根据需要更改顺序。
如果需要先放月或者先放年,只需要改变格式中的顺序即可。
现在,我将导入数据集,我们将使用该数据集来演示许多函数。我们的脸书股票数据。请随意在此下载数据集并跟随:
https://github.com/rashida048/Datasets/blob/master/FB_data.csv
你学习的唯一方法是通过实践。
如果你读这篇文章是为了学习,我强烈建议你边读边练。
df = pd.read_csv('FB_data.csv')
df.head()
这是一个原始数据集。看起来不坏!井井有条。但是我们需要像前面讨论的那样改变“日期”列的格式。我们将把它做成 DatetimeIndex 格式,并把它作为索引列。
因为当“日期”列是索引列时,我们将能够非常容易地对其进行重新采样。您将在后面的章节中看到这意味着什么。
这是导入数据的正确方法,我在导入时更改了日期的格式并将其设置为索引。
df = pd.read_csv('FB_data.csv', parse_dates=['Date'], index_col="Date")
df.head()
看,我们改变了“日期”栏的格式!
重采样
在这一节中,我将讨论如何对数据进行重采样。
为什么重采样很重要?
因为我们并不总是需要庞大数据集中的所有数据。例如,我们可能只需要 2019 年 6 月的数据。如果您的日期格式是 DatetimeIndex,那就非常简单了:
df["2019-06"]
我们只有八天的数据。求 2019 年 6 月开盘价均值。
df["2019-06"].Open.mean()
输出:
190.71000014285715
我们也可以得到单个日期的数据。
df.loc["2019-06-21"]
输出:
Open 1.887500e+02
High 1.920000e+02
Low 1.887500e+02
Close 1.911400e+02
Adj Close 1.911400e+02
Volume 2.275120e+07
Name: 2019-06-21 00:00:00, dtype: float64
比方说,我们需要 2019 年 6 月 27 日到 7 月 10 日两个星期的数据。
df.loc["2019-06-27": "2019-07-10"
你也可以按月重新取样。这给出了月平均值。例如,这里我将获得月平均收盘数据:
df.Close.resample('M').mean()
输出:
Date
2019-06-30 190.324286
2019-07-31 199.595454
2019-08-31 184.497726
2019-09-30 185.735000
2019-10-31 184.383912
2019-11-30 195.718500
2019-12-31 201.951904
2020-01-31 216.643333
2020-02-29 207.505263
2020-03-31 165.747727
2020-04-30 177.003335
2020-05-31 216.549001
2020-06-30 232.671332
Freq: M, Name: Close, dtype: float64
我们可以用一行代码计算月平均值并绘图:
df.Close.resample('M').mean().plot()
如果您想要周数据并绘制它,您可以通过以下代码获得它:
df.Close.resample('W').mean().plot()
使用 plot()函数中的“kind”参数,您可以获得总共 13 种类型的图,而不是简单的线形图。
我用这个柱状图命名了这 13 种类型的图。
我将绘制季度结算数据的柱状图。
df.Close.resample('Q').mean().plot(kind='bar')
上面的“种类”参数采用以下 13 种可视化类型:
- 线条
- 区域
- 酒吧
- barh
- 馅饼
- 箱子
- 赫克宾
- 嘘
- kde
- 密度
- 分散
变化
听起来,shift 函数将数据移动指定的次数。我们正在使用的 FB 数据集从 2019 年 6 月 20 日开始。现在,如果我们将数据移动 1,2019 年 6 月 20 日的数据将移动到 2019 年 6 月 21 日,2019 年 6 月 21 日的数据将移动到 2019 年 6 月 22 日,依此类推。
我稍后会解释为什么人们使用 shift
对于这个例子,我将只使用列。你会非常清楚地看到变化。我将创建一个名为“df1”的新数据帧,只包含开放数据。
df1 = pd.DataFrame(df['Open'])
df1.head()
将该数据移动 1。
df1.shift(1)
第一行的值为空。仅仅是因为第一行移到了第二行。但是第一行之前没有数据。
如果你在 shift 中使用了一个负值,结果正好相反。
df1.shift(-1)
我们为什么要使用 shift?
班次给你前一天的数据或者第二天的数据。这是一个使用案例。我将使用 shift 将今天的数据和前一天的数据并排放置。
df1['Prev Day Opening'] = df1['Open'].shift(1)
df1
有用吗!?
您还可以在另一列中获得 1 天数据的变化:
df1['1 day change'] = df1['Open'] - df1['Prev Day Opening']
以百分比计算 1 周的总数。为此,我们必须推迟 5 天。对吗?然后取今天和 5 天前数据的差值。乘以 100,然后除以今天的原始数据。让我们来看看,以便更好地理解它。
df1['One week total return'] = (df1['Open'] - df1['Open'].shift(5)) * 100/df1['Open'].shift(5)
df1.tail()
我要用 df.tail(),因为我们上了 5 天的班。所以前 5 行将为空。
时区
理解时区很重要。很可能,你在一个时区,而你的客户在另一个时区。熊猫有很强的适应不同时区的功能。
我们有两种类型的日期时间数据。不知道时区的简单日期时间和知道时区的时区感知日期时间。我们拥有的数据是朴素的日期时间。所以,我们需要使用 tz_localize 来转换这个 DateTime。
将脸书数据集的索引转换为“美国/东部”。
df.index = df.index.tz_localize(tz = 'US/Eastern')
df.index
输出:
DatetimeIndex(['2019-06-20 00:00:00-04:00', '2019-06-21 00:00:00-04:00', '2019-06-24 00:00:00-04:00', '2019-06-25 00:00:00-04:00', '2019-06-26 00:00:00-04:00', '2019-06-27 00:00:00-04:00', '2019-06-28 00:00:00-04:00', '2019-07-01 00:00:00-04:00', '2019-07-02 00:00:00-04:00', '2019-07-03 00:00:00-04:00',
...
'2020-06-08 00:00:00-04:00', '2020-06-09 00:00:00-04:00', '2020-06-10 00:00:00-04:00', '2020-06-11 00:00:00-04:00', '2020-06-12 00:00:00-04:00', '2020-06-15 00:00:00-04:00', '2020-06-16 00:00:00-04:00', '2020-06-17 00:00:00-04:00', '2020-06-18 00:00:00-04:00', '2020-06-19 00:00:00-04:00'], dtype='datetime64[ns, US/Eastern]', name='Date', length=253, freq=None)
在每个日期,它显示负 4 小时。同样,如果我们将其转换为“欧洲/柏林”,它将增加 6 个小时。
df = df.tz_convert('Europe/Berlin')
df.index
输出:
DatetimeIndex(['2019-06-20 06:00:00+02:00', '2019-06-21 06:00:00+02:00', '2019-06-24 06:00:00+02:00', '2019-06-25 06:00:00+02:00', '2019-06-26 06:00:00+02:00', '2019-06-27 06:00:00+02:00', '2019-06-28 06:00:00+02:00', '2019-07-01 06:00:00+02:00', '2019-07-02 06:00:00+02:00', '2019-07-03 06:00:00+02:00',
...
'2020-06-08 06:00:00+02:00', '2020-06-09 06:00:00+02:00', '2020-06-10 06:00:00+02:00', '2020-06-11 06:00:00+02:00', '2020-06-12 06:00:00+02:00', '2020-06-15 06:00:00+02:00', '2020-06-16 06:00:00+02:00', '2020-06-17 06:00:00+02:00', '2020-06-18 06:00:00+02:00', '2020-06-19 06:00:00+02:00'], dtype='datetime64[ns, Europe/Berlin]', name='Date', length=253, freq=None)
您可以找到世界上所有可用的时区,并以这种方式使用适合您的时区:
from pytz import all_timezones
print(all_timezones)
这是输出的一部分。满输出太大:
['Africa/Abidjan', 'Africa/Accra', 'Africa/Addis_Ababa', 'Africa/Algiers', 'Africa/Asmara', 'Africa/Asmera', 'Africa/Bamako', 'Africa/Bangui', 'Africa/Banjul', 'Africa/Bissau', 'Africa/Blantyre', 'Africa/Brazzaville', 'Africa/Bujumbura', 'Africa/Cairo',.....
如何生成缺失日期
如果您有数据并且知道时间段,但是时间没有记录在数据集中,该怎么办?或者你有去年第二季度的数据,但没有今年的数据。而你今年需要用去年的数据。
在许多情况下,您可能需要生成一系列日期。熊猫 date_range 功能就派上用场了。让我们生成一个 10 天的时间段:
rng = pd.date_range(start='11/1/2020', periods=10)
rng
输出:
DatetimeIndex(['2020-11-01', '2020-11-02', '2020-11-03', '2020-11-04', '2020-11-05', '2020-11-06', '2020-11-07', '2020-11-08', '2020-11-09', '2020-11-10'], dtype='datetime64[ns]', freq='D')
如果我只需要工作日怎么办?
我只需要添加一个称为频率的额外参数,如下所示:
rng = pd.date_range(start='11/1/2020', periods=10, freq='B')
rng
输出:
DatetimeIndex(['2020-11-02', '2020-11-03', '2020-11-04', '2020-11-05', '2020-11-06', '2020-11-09', '2020-11-10', '2020-11-11', '2020-11-12', '2020-11-13'], dtype='datetime64[ns]', freq='B')
还有几个类似的选项和频率。请查看这篇文章,我只详细解释了 date_range 函数:
因此,我们可以生成一系列日期,并将它们添加到我们的数据集中!
旋转
rolling 函数聚合指定日期时间数的数据。这里我将取每三天的平均值。在完成这个例子后,我将进一步解释:
df[["High"]].rolling(3).mean()[:10]
这里到底发生了什么?我在滚动函数中传递了 3 作为参数,而聚合函数是 mean。因此,它取 6 月 20 日、21 日和 24 日“高”数据的平均值,放在 24 日。对第 21、24 和 25 个数据进行同样的操作,并放在第 25 个数据上,以此类推。很多时候,我们使用周平均值或三天平均值来做决定。
您还可以选择放置滚动数据的位置。这里有一个例子:
data_rol = df[['High', 'Low']].rolling(window = 7, center = True).mean()
data_rol
在这个滚动函数中,我传递了 window = 7。这意味着需要 7 天的平均时间。center = True 表示将平均值放在第 4 行而不是第 7 行。这就是为什么它的底部也有一些空值。
让我们在同一个图中绘制原始“高”数据和 7 天累计“高”数据:
%matplotlib inline
import matplotlib.ticker as ticker
fig, ax = plt.subplots(figsize= (11, 4))
ax.plot(df['High'], marker = '.', markersize=4, color='0.4', linestyle='None', label='Daily')
ax.xaxis.set_major_locator(ticker.MultipleLocator(30))
ax.plot(data_rol['High'], linewidth=2, label='7-d rolling mean')
ax.set_xlabel('Month')
通常,这种类型的图用于观察数据中的任何趋势
在我们的数据中,有一个趋势是可以观察到的。2020 年 1 月后,价值开始下降,曲线变得陡峭。在时间序列分析中,我们有时会努力寻找趋势。但有时我们需要从数据中剔除趋势。尤其是当我们需要使用时间序列数据进行机器学习或预测时。
再次行军后,它有一个陡峭的上升。在下一节,我将告诉你如何摆脱这种趋势。
消除趋势的差异
如果数据中有任何趋势,这对于建模、预测或观察季节性都是不利的。为了提高模型性能,或者观察数据中的任何季节性或任何噪声,差分是一种常见的做法。它采用指定天数的数据差异。这里有一个例子:
df_first_order_diff = df[['High', 'Low']].diff()
df_first_order_diff
在这里,我没有指定。diff()函数。因此,默认情况下,只需要 1 天的差异。
您看到结果表中发生了什么吗?“高”和“低”数据是“20–06–19”是 21–06–19 和 20–06–19 的“高”和“低”数据的差值。故事发生在 2019 年 6 月 21 日。这就是为什么它在 20–06–19 中为空。因为在那之前没有数据可以减去。
这种差异过程被认为可以消除这种趋势。万一没有,尝试 3 天差异或 7 天差异。这是如何进行 3 天的差异:
df[['High', 'Low']].diff(3)
让我们绘制上面一阶差分的数据,看看我们在上一节观察到的趋势是否已经消除。
start = '20-06-19'fig, ax = plt.subplots(figsize = (11, 4))
ax.plot(df_first_order_diff.loc[start:, "High"], marker = 'o',
markersize = 4, linestyle = '-', label = 'First Order Differencing')
ax.xaxis.set_major_locator(ticker.MultipleLocator(30))
看那明显的趋势没了!如果你能在最后摆脱这种轻微的趋势,请随意检查我之前提到的 3 天差异。
重要时间特征提取
您可以从时间序列中提取非常有用的年、月、周或工作日。让我们从索引列“Date”中提取年份开始。
pd.DatetimeIndex(df.index).year
输出:
Int64Index([2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019,
...
2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020], dtype='int64', name='Date', length=253)
现在,选取数据集的一个子集使其变小,并在单独的列中添加年份。
df3 = df[['High','Low', 'Volume']]
df3['Year'] = pd.DatetimeIndex(df3.index).year
df3
看到我们在最后加上了年份。同样,您可以提取月份和工作日。下面是代码:
pd.DatetimeIndex(df3.index).month
输出:
Int64Index([6, 6, 6, 6, 6, 6, 6, 7, 7, 7,
...
6, 6, 6, 6, 6, 6, 6, 6, 6, 6], dtype='int64', name='Date', length=253)
以下是提取工作日的方法:
pd.DatetimeIndex(df3.index).weekday
输出:
Int64Index([3, 4, 0, 1, 2, 3, 4, 0, 1, 2,
...
0, 1, 2, 3, 4, 0, 1, 2, 3, 4], dtype='int64', name='Date', length=253)
工作日出来就是数字。如果您需要周日、周一等工作日格式,该怎么办?那会更有用!
df3['Weekday'] = pd.DatetimeIndex(df3.index).to_series().dt.day_name()
df3.head()
让我们检查工作日是否对“高”、“低”和“量”数据有任何影响。
import seaborn as sns
fig, axes = plt.subplots(3, 1,figsize=(11, 10), sharex=True)for name, ax in zip(['High', 'Low', 'Volume'], axes):
sns.boxplot(data=df3, x = 'Weekday', y = name, ax=ax)
ax.set_title(name)
工作日对这些数据有影响,对吗?周三的“高”、“低”和“量”都在上涨。周一情况正好相反。箱线图在一个包中给出了很多信息。如果您需要复习如何从箱线图中提取所有数据,这里有一篇详细的文章:
周期和周期指数
另一个重要的 python 函数。我们将通过实践来学习。使用周期函数的最基本方法是:
y = pd.Period('2020')
y
输出:
Period('2020', 'A-DEC')
此输出表明,这一时期’ 2020 年’将于 12 月结束。有道理,对吧?
以下是可从周期函数中提取的所有信息的目录:
dir(y)
这是输出的一部分。因为目录大!
.
.
.
.
.
'asfreq',
'day',
'dayofweek',
'dayofyear',
'days_in_month',
'daysinmonth',
'end_time',
'freq',
'freqstr',
'hour',
'is_leap_year',
'minute',
'month',
'now',
'ordinal',
'quarter',
'qyear',
'second',
'start_time',
'strftime',
'to_timestamp',
'week',
'weekday',
'weekofyear',
'year']
我将展示其中的一些。
y.start_time
输出:
Timestamp('2020-01-01 00:00:00')
输入:
y.end_time
输出:
Timestamp('2020-12-31 23:59:59.999999999')
如果我们使用频率作为月份:
month = pd.Period('2020-2', freq="M")
month
输出:
Period('2020-02', 'M')
输入:
month.start_time
输出:
Timestamp('2020-02-01 00:00:00')
输入:
month.end_time
输出:
Timestamp('2020-02-29 23:59:59.999999999')
我们可以用这种类型的月数据做什么?
必要的话可以加减。例如,如果您有学生的年龄数据,并且需要更新年份或月份,您可以这样做:
month + 2
输出:
Period('2020-04', 'M')
同样的,你可以增加或减少天数。如果我们输入一个日期,默认情况下它会将频率作为日期。
d = pd.Period('2020-02-28')
d
输出:
Period('2020-02-28', 'D')
如果你增加一两天,它就会增加一两天。但是我这里写的日期是 2 月 28 日。这是不同的,对不对?闰年的二月有 29 天,其他年份的二月有 28 天。
让我们在上面的日期 d 的基础上增加两天:
d + 2
输出:
Period('2020-03-01', 'D')
2 月 28 日加 2 天,就得到 3 月 1 日。这意味着周期函数知道闰年。
同样,你可以添加年、小时、分钟甚至季度。
让我们来看一个 25 美分硬币的例子:
q = pd.Period('2020Q1')
q
输出:
Period('2020Q1', 'Q-DEC')
检查 q 的开始和结束月份。
q.asfreq('M', how='start')
输出:
Period('2020-01', 'M')
2020Q1 的第一个月是一月。这意味着默认情况下第一季度从一月份开始。检查什么时候结束。虽然我们知道它应该在三月结束。
q.asfreq('M', how='end')
输出:
Period('2020-03', 'M')
q 期始于一月,止于三月。任何地方的商业年度都不是从一月开始到三月结束。一会儿我会再谈一谈。
在 q 上加 1:
q + 1
输出:
Period('2020Q2', 'Q-DEC')
在这里,“Q-DEC”表示该季度在 12 月结束。一年有四个季度,最后一个季度在 12 月结束。但是有几个行业将一月作为第四季度末,或者将六月作为第四季度末。
我们可以使用“频率”参数来指定季度末。在下一个例子中,我将使用第四季度末作为一月份。
q1 = pd.Period('2020Q2', freq = 'Q-Jan')
q1
输出:
Period('2020Q2', 'Q-JAN')
看,这里我们把第四季度末改成了一月份!请随意查看第一季度的开始和结束月份。你会看到开始的月份是三月而不是四月。因为第一季度是从二月到四月。
按照我们之前生成 date_range 的方式,我们也可以生成期间范围:
idx = pd.period_range('2017', '2020', freq = 'Q')
idx
输出:
PeriodIndex(['2017Q1', '2017Q2', '2017Q3', '2017Q4', '2018Q1', '2018Q2', '2018Q3', '2018Q4', '2019Q1', '2019Q2', '2019Q3', '2019Q4', '2020Q1'], dtype='period[Q-DEC]', freq='Q-DEC')
默认情况下,从“2017Q1”开始。因为默认情况下,季度从一月开始,到十二月结束。但与之前一样,如果我们在 1 月份指定季度末,它将从 2017 年第四季度开始。
idx = pd.period_range('2017', '2020', freq = 'Q-Jan')
idx
输出:
PeriodIndex(['2017Q4', '2018Q1', '2018Q2', '2018Q3', '2018Q4', '2019Q1', '2019Q2', '2019Q3', '2019Q4', '2020Q1', '2020Q2', '2020Q3', '2020Q4'], dtype='period[Q-JAN]', freq='Q-JAN')
您可以将这些季度转换为时间戳:
idx = idx.to_timestamp()
idx
输出:
DatetimeIndex(['2016-11-01', '2017-02-01', '2017-05-01', '2017-08-01', '2017-11-01', '2018-02-01', '2018-05-01', '2018-08-01', '2018-11-01', '2019-02-01', '2019-05-01', '2019-08-01', '2019-11-01'], dtype='datetime64[ns]', freq='QS-NOV')
同样,当我们有时间戳时,我们可以使用 to_period()将其转换为季度。
idx.to_period()
输出:
PeriodIndex(['2016Q4', '2017Q1', '2017Q2', '2017Q3', '2017Q4', '2018Q1', '2018Q2', '2018Q3', '2018Q4', '2019Q1', '2019Q2', '2019Q3', '2019Q4'], dtype='period[Q-DEC]', freq='Q-DEC')
恭喜你!您现在刚刚学会了对任何数据集执行时间序列分析!
结论
我试图记录和解释大部分大熊猫的功能,用于时间序列分析。学习完这一整页后,您应该有足够的知识对任何时间序列数据执行有效的时间序列分析。但是请记住,要熟练使用所有这些功能需要大量的练习!编码快乐!
欢迎在推特上关注我,喜欢我的 T2 脸书页面。
更多阅读:
一个非常规但方便的 Matplotlib Broken_Barh 函数,当它特别有用时
它是什么,如何使用和定制,何时使用
作者图片
尽管对于 Python 中的某些数据可视化情况非常方便,但[broken_barh](https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.broken_barh.html)
是 matplotlib 中不太为人所知和被低估的方法之一。它用于绘制不同 x 范围的水平矩形序列,并且位于 y 范围定义的相同垂直位置。换句话说,它产生了一个“断开的”水平条形图,即一个有间隙的图。
语法非常简单:该函数主要接受两个参数,xranges
和yrange
。xranges
参数表示 2 项元组的列表或元组,其中每个元组的第一项xmin
是相应矩形的最左侧 x 位置,第二项xwidth
是该矩形的宽度。yrange
参数是一个元组,表示所有矩形的 y 位置和高度。
一个基本的情节是这样的:
import matplotlib.pyplot as plt
import seaborn as sns
plt.figure(figsize=(8,2))
plt.broken_barh(xranges=[(1, 1), (3, 2)], yrange=(1, 1))
sns.despine()
plt.show()
作者图片
一些额外的不言自明的参数可以用来定制结果矩形的外观:color
、alpha
、edgecolor
、linewidth
、linestyle
(可以是'solid'
、'dashed'
、'dashdot'
或'dotted'
)。hatch
参数用于用图案填充矩形,可以取下列值之一或其组合作为字符串:/
、\\
、|
、-
、+
、x
、o
、O
、.
、*
。label
参数设置将在图例中显示的标签。
这些参数中的每一个都可以是应用于所有矩形的单个自变量(浮点或字符串)(例如color='red'
)或自变量的列表/元组(例如color=('red','blue')
),在该列表/元组上迭代以创建矩形的间歇序列:
plt.figure(figsize=(15,2))
x1 = (1, 1), (2.1, 2), (5, 2), (7.2, 1)
y1 = (1, 1)
plt.broken_barh(x1, y1,
color=['slateblue', 'orange'], alpha=0.7,
linewidth=4,
edgecolor=['black', 'brown', 'green', 'blue'],
linestyle='dashed', hatch='/')
sns.despine()
plt.show()
作者图片
我知道,上面的情节看起来有点让人不知所措🙂然而,它清楚地显示了附加参数是如何工作的。
broken_barh
函数什么时候能在现实世界中派上用场?
1.要显示范围
使用这种方法,我们可以在同一个图上显示不同类别的范围,例如:
- 不同种类动物的边界条件(比如温度),
- 不同地理位置在一段时间内的降雨量,
- 各种工作的工资范围。
让我们用虚拟数据来看看最后一种情况:
x_ranges = [[(3800, 600)], [(2000, 1000)], [(2700, 800)]]
y_start = 1
colors = ['deepskyblue', 'limegreen', 'magenta']
for i in range(len(x_ranges)):
plt.broken_barh(x_ranges[i], (y_start+i, 0.5), color=colors[i])
plt.title('Salary ranges by job ($)', fontsize=25)
plt.xticks(fontsize=14)
plt.yticks(ticks=[1.25, 2.25, 3.25],
labels=['Job1', 'Job2', 'Job3'], fontsize=16)
plt.tick_params(left=False)
sns.despine()
plt.show()
作者图片
在这种情况下,broken_barh
方法实际上为每个类别绘制了一个矩形。
**注意:**当我们必须为一个类别只绘制一个矩形时,我们必须将相应的 2 项元组放在列表中,而不是放在元组中。
2.强调另一个图形上的某些音程
在强调其他图表上的某些特定区间时,broken_barh
方法是一个不错的选择。例如:
- 我们有一个地理区域在一段时间内温度波动的线图,我们希望显示温度高于某个值的时间间隔,
- 货币汇率随着利息的变化而变化,
- 当交通比平均水平更拥挤时,交通流量波动
下图准确地代表了最后一种情况:工作日从 06:00 到 19:00, I-94 州际公路上每小时的交通量波动(数据取自 this repository ,经过修改和略微修改)。我们应用了broken_barh
方法来强调流量高于平均水平的时间范围:
import pandas as pd
cars_per_hr = pd.Series([4141, 4740, 4587, 4485, 4185, 4466, 4718, 4801, 4932, 5241, 5664, 5310, 4264, 3276])# Create a line plot
plt.figure(figsize=(10,5))
plt.plot(cars_per_hr, color="grey")# Create a broken horizontal bar plot
x = [(0.8, 0.95), (5.65, 6)]
y = (cars_per_hr.mean()-50, 100)
plt.broken_barh(x, y, color ='red')plt.title('Traffic volume from 06.00 till 19.00 on weekdays',
fontsize=25)
plt.xlabel('Time', fontsize=20)
plt.ylabel('Traffic volume, cars/hr', fontsize=20)
plt.xticks(ticks=list(range(14)),
labels=list(range(6,20)), fontsize=13)
plt.yticks(fontsize=13)
plt.xlim(0,13)
sns.despine()
plt.show()
作者图片
我们看到有两个时间段的交通比平均水平更拥挤:大约从 6.45 到 7.40,以及从 11.40 到 17.40。
3.要创建甘特图
或许,broken_barh
方法的最佳应用是创建简化的甘特图。甘特图是一种特殊类型的条形图,通常用于显示不同活动之间随时间变化的关系。这些活动可以包括:
- 项目管理进度及其当前状态,
- 要完成的任务及其截止日期,
- 正在进行的事件,
- 假期。
在 Python 中还有其他更复杂的创建甘特图的方法,比如使用 Plotly 或 python-gantt 库。然而,broken_barh
matplotlib 函数也足以创建一个基本但信息丰富的图形。此外,它只需要几行代码就可以完成。
让我们以今年夏天的假期计划为例来说明这种方法是如何工作的。我们将为一个 5 人的部门使用虚拟数据:
plt.figure(figsize=(20,7))# Create broken horizontal bar plots for vacation ranges
x_ranges = [[(0,3), (36,3), (69,12)], [(13,19), (55,5)], [(48,5), (76,12)], [(27,19)], [(0,4), (62,12), (86,2)]]
y_start = 0.75
colors = ['deepskyblue', 'indianred', 'limegreen', 'gold', 'violet']
for i in range(len(x_ranges)):
plt.broken_barh(x_ranges[i], (y_start+i, 0.5), color=colors[i])# Create broken horizontal bar plots for weekends
x_weekend = [(4,2), (11,2), (18,2), (25,2), (32,2), (39,2), (46,2), (53,2), (60,2), (67,2), (74,2), (81,2), (88,2)]
y_weekend = (0, 5.5)
plt.broken_barh(x_weekend, y_weekend, color='tomato', alpha=0.1)plt.title('Vacation schedule for June-August 2021', fontsize=35)
plt.xticks(ticks= [0, 4, 9, 14, 19, 24, 30, 34, 39, 44, 49, 54, 61, 65, 70, 75, 80, 85, 90],
labels=['June', 5, 10, 15, 20, 25, 'July', 5, 10, 15, 20, 25, 'August', 5, 10, 15, 20, 25, 30], fontsize=20)
plt.yticks(ticks= [1, 2, 3, 4, 5],
labels=['Vladimir', 'Natalia', 'Michael', 'Peter', 'Elena'], fontsize=20)
plt.xlim(0, 92)
plt.ylim(0, 5.5)
plt.axvline(30, color='black')
plt.axvline(61, color='black')
plt.grid(axis='x')
plt.tick_params(left=False, bottom=False)
plt.show()
作者图片
即使不使用任何专门的库,上面的图看起来也很清楚:我们看到了雇员的姓名、每个雇员的假期数和假期范围、主要的时间标志(开始、5 日、10 日等等。每个月、所有的周末)、重叠假期的时段或者没有人休假的时段。
结论
在本文中,我们探索了一个很少使用但很方便的broken_barh
matplotlib 方法的语法,定制结果图的方法,特别是该方法有特殊用途的情况,即在同一图上显示不同类别的范围(边界条件、工资范围),在另一个图上强调一些区间(汇率变化、交通量波动),以及创建甘特图(可视化计划,如项目步骤、任务或假期)。
感谢阅读!
相关主题:
https://medium.com/geekculture/creating-toyplots-in-python-49de0bb27ec1