Python 进阶:Seaborn 绘图

1. 官方文档

seaborn: statistical data visualization — seaborn 0.13.2 documentation

API Reference — Matplotlib 3.9.1 documentation

2. 初探 seaborn:绘制柱状图

2.1 示例数据集 example data

seaborn 提供了一些内置数据集用于演示和练习( Available Example Datasets),列举如下,这些数据集可以通过 Seaborn 库的 load_dataset() 函数直接加载到 Python 环境中。

  • tips:一个关于餐厅顾客消费的数据集,包含了顾客的性别、抽烟习惯、就餐时间、用餐人数、总消费金额和小费金额等信息。
  • iris:鸢尾花数据集,包含了三个物种的花的萼片长度、萼片宽度、花瓣长度和花瓣宽度等信息。
  • titanic:泰坦尼克号乘客数据集,包含了乘客的个人信息、船票信息、生还情况等信息。
  • fmri:功能磁共振成像数据集,包含了被试在执行不同任务时的脑部活动数据。
  • diamonds:钻石数据集,包含了钻石的大小、颜色、净度、价格等信息。

使用 seaborn.load_dataset 读入示例数据

在这里插入图片描述

import matplotlib.pyplot as plt
import seaborn as sns
from IPython.core.interactiveshell import InteractiveShell
InteractiveShell.ast_node_interactivity = 'all'


tips = sns.load_dataset("tips")

tips 数据集内容概览:

读入数据
需要注意的一件事是,与 read_csv() 不同,在数据包含有限的固定数量的可能值时,load_dataset() 会自动将字符串列转换为 Categorical 类型列。在本例中,day 列将被视为表示星期的 Categorical 类型数据。
在这里插入图片描述
在这里插入图片描述

读入数据后,考虑先绘制一个条形图,显示服务员每天收到的平均小费金额。

2.2 使用 seaborn 绘制条形图

在这里插入图片描述

可以使用 seaborn 的 barplot() 函数创建条形图。调用函数时,将 data 指定为需要使用的 DataFrame,此处为 tips,并告诉函数基于 day 列和 tip 列绘制图形,它们分别表示收到小费的日期和小费金额。

需要强调的一点是,seaborn barplot() 函数(以及其他所有的 seaborn 绘图函数)能够接受并理解 pandas DataFrame,如果需要让 seaborn 使用某一列,将列名作为字符串传递即可,无需编写 pandas 代码来提取列数据 Series。

estimator 参数设置为 mean 表示想让 seaborn 绘制 x 的每个类别的 y 平均值,即显示每天的平均小费。estimator 可以使用常见的统计函数,如 sum、max、min 和 median, estimator=“mean” 是默认值。默认情况下,该图还将显示误差条 errorbar,设置 errorbar=None 将不显示误差条。

barplot() 函数将基于传递给它的参数生成一个图,使用数据的列名标记每个轴。barplot() 运行结束后,返回一个包含该图的 matplotlib Axes 对象,如果想给图像设置标题,调用 Axes 对象的 .set() 方法,传递想使用的标题内容即可。注意,这些操作都是直接在 seaborn 中完成的,而不是 Matplotlib。


(
    sns.barplot(
    data=tips, x="day", y="tip",
    estimator="mean", errorbar=None,
    )
    .set(title="Daily Tips ($)")
)

plt.show()

在这里插入图片描述

barplot() 函数被封装在一对括号 (…) 中,这是一种在 seaborn 代码中常见的编码风格,使用 seaborn 时,经常会在同一个对象上连续调用多个方法,使用括号,让每个方法都从点表示法的“点” 开始,从而使得这些方法对齐;其实也可以使用反斜杠() 来进行续写,但不推荐这样做。

在某些环境中,如 PyCharm,需要使用 Matplotlib 的 show() 函数来显示图形,这意味着你必须将Matplotlib 导入Python。如果使用的是 Jupyter Notebook,则不需要使用 plot. show(),但是使用它可以删除图像上方一些不需要的文本;在barplot() 的末尾放置分号 “;” 也可以达到一样的效果。

不加分号 vs 加分号(不显示其他文本),在 jupyter notebook 运行后显示的结果对比:
在这里插入图片描述在这里插入图片描述

接下来,使用 Matplotlib 创建相同的图,对比两个库代码的差异。

2.3 使用 matplotlib 绘制条形图

使用 Matplotlib 绘图时,首先使用 DataFrame 的 .groupby() 方法将数据分组,然后使用 .mean() 计算每天 tip 的平均值;接下来,使用 Matplotlib 的 bar() 函数绘图,该函数需要指定要绘制的两个数据系列,在本例中,传递 x=average_daily_tip.index 和 height=average_daily_tip.values;最后,设置轴标签和绘图标题。

import matplotlib.pyplot as plt
import pandas as pd


average_daily_tip = (
 tips
 .groupby("day", observed=False)["tip"]
 .mean()
)

fig, ax = plt.subplots()
plt.bar(x=average_daily_tip.index, height=average_daily_tip.values)
ax.set_xlabel("day")
ax.set_ylabel("tip")
ax.set_title("Daily Tips ($)")

plt.show()

正如上述 barplot() 的例子,seaborn 的 代码比 Matplotlib 的代码更简洁,seaborn 支持使用 pandas DataFrame,它能够理解 DataFrame 的结构,从而将 matploglib 代码中许多复杂的部分隐藏起来。

2.4 保存图像

如果想将图表保存到外部文件中,有以下几种方法:

  1. 在许多环境中(如 Pycharm),调用 plt.show() 时,图表将出现在一个新的窗口中,这个窗口通常有一个保存选项。
  2. 如果使用的是 Jupyter Notebook,右键单击图像将其复制到剪贴板,然后粘贴到文档中。
  3. 使用代码保存图片:
(
    sns.barplot(
    data=tips, x="day", y="tip",
    estimator="mean",
    )
    .set_title("Daily Tips ($)")  
    .figure.savefig("daily_tips.png")
);

保存图像时,先获取 plot 的 figure 属性,访问底层的 Matplotlib 图形,然后调用它的 .savefig() 方法将其保存为 png 文件.savefig() 默认值是 "png",也允许传入其他图形格式,如 “jpeg”、“pdf” 和"ps"。

上述代码中,使用 .set_title(“Daily Tips($)”) 方法设置标题,而不是前面使用的 .set(title=“Daily Tips($)”) 。这是因为 .set_title(“Daily Tips($)”) 返回一个 matplotlib.text.Text 对象,可以使用 . figure 属性访问其底层关联的 figure 对象; 而 .set(title=“Daily Tips($)”) 返回 LIst,Text 对象是列表中的第一个元素,需要使用 .set(title=“Daily Tips ($)”[0].figure.savefig(“daily_tips.png”),代码可读性较差。

3. Functional Interface 和 Object Interface 简介

Seaborn 提供了两种不同的接口:functional interface 和 objects interface,上文中的 barplot 就是 functional interface 的一部分。总的来说,functional interface 适合快速绘制简单的图形,而 objects interface 更适合创建复杂的图形,并对其进行更细粒度的控制。

Functional Interface
是一种基于函数调用的接口,允许用户在单个函数调用中指定所有参数。在这种接口下,Seaborn 的函数将数据作为输入,返回一个 matplotlib 图形对象。这种接口非常适合快速绘制简单的图形。


Objects Interface
是一种基于对象的接口,它允许用户创建一个图形对象,并在该对象上调用方法来添加图形元素。这种接口更加灵活,可以用于创建复杂的图形,并允许用户对图形进行更细粒度的控制。

3.1 Functional Interface

3.1.1 Funtional Interface 概述

seaborn functional interface 包含一系列绘图函数,能够创建不同类型的图像,如前文使用过的 barplot() 函数。functional  interface 将其绘图函数分为如下三类(Overview of seaborn plotting functions — seaborn 0.13.2 documentation):

  1. 关系图 relational plots:显示数据集中的“变量对”如何相互关联,如散点图和线形图(scatter plots and line plots)。应用场景如,产品价格变化时,利润如何变化。
  2. 分布图 distribution plots:显示变量的分布情况,如直方图和地毯图(histogram plots and rug plots)。应用场景如,国家考试中不同分段的人数。
  3. 分类图 categorical plots:显示在变量对中一个变量包含离散类别时,两个变量的关系,如条形图和箱形图(bar plots and box plots)。前文中,服务员在一周的各天中平均收到的小费,就是分类图的一个例子。

你可能注意到了上图中的层级结构,seaborn 函数有两个级别:Axe-level 和 Figure-level。

  1. 图形级函数 figure-level functionrelplot()、displot() 和 catplot() 都是图形级函数。支持绘制多个子图,每个子图显示不同类别的数据。例如,想查看产品的利润如何随价格变化而变化,要求按照产品类型,每个产品单独绘制图像。可以在图形级函数中把指定的参数应用于所有子图,这使它们具有一致的外观。
  2. 轴级函数 axes-level functionscatterplot()、lineplot()、histplot() 和 boxplot() 都是轴级函数。轴级函数通常用于绘制单个图,将数据绘制到单个 matplotlib.pyplot.Axes 对象上,该对象是函数的返回值,提供给轴级函数的任何参数仅应用于该函数生成的单个图。

图形级函数示例:relplot

3.1.2 Axes-Level Function

如果你只需要一张图时,可以使用轴级函数。

(1)绘制一个散点图 scatterplot

还是使用上述 tips 数据集,假设你想查看账单总价与小费之间是否存在某种关系,考虑绘制散点图,使用 Seaborn 中的 scatterplot() 轴级函数。

scatterplot() 的使用方式与 barplot() 类似:需要提供一个 DataFrame 作为 data 参数,以及要绘制的列名,调用 Matplotlib.Axes.set() 方法设置标题,使用 xlabel 和 ylabel 标记轴名称(默认情况下,没有标题,轴名与数据列名一致,使用 Axes.set() 可以将设置字母大写)。

(
 sns.scatterplot(
     data=tips, x="total_bill", y="tip"
 )
 .set(
     title="Total Bill vs Tip",
     xlabel="Total Bill",
     ylabel="Tip",
 )
)

plt.show()

(2)区分颜色 hue

seaborn 中大多数轴级函数都有一个强大的参数:hue。此参数能够给图像上不同类别的数据添加不同的颜色,使用时,传入希望应用其进行着色的列名即可。

(
 sns.scatterplot(
     data=tips, x="total_bill", y="tip",
     hue="smoker"
 )
 .set(
     title="Total Bill vs Tip",
     xlabel="Total Bill",
     ylabel="Tip",
 )
);
plt.show()


(
 sns.scatterplot(
     data=tips, x="total_bill", y="tip",
     hue="size"
 )
 .set(
     title="Total Bill vs Tip",
     xlabel="Total Bill",
     ylabel="Tip",
 )
);
plt.show()

如果 hue 参数对应的数据不是类别,而是数值型的,浮点数,颜色将切换到顺序调色板 sequential palette。

你也可以自定义调色板,使用以字符 "ch:" 开头的字符串,创建一个 cubehelix_palette 对象。

(
 sns.scatterplot(
     data=tips, x="total_bill", y="tip",
     hue="size", palette="ch:r=-.5,l=.75"
 )
 .set(
     title="Total Bill vs Tip",
     xlabel="Total Bill",
     ylabel="Tip",
 )
);

(3)区分样式和大小 style 和 size

relational plots 还支持样式 style 和大小 size 参数,可以对每个点应用不同的样式和大小。

(
 sns.scatterplot(
     data=tips, x="total_bill", y="tip",
     hue="smoker", style="smoker", size="size"
 )
 .set(
     title="Total Bill vs Tip",
     xlabel="Total Bill",
     ylabel="Tip",
 )
);

在当前的示例中,同时使用三个参数可能有点冗余,在实际应用时可以根据需要酌情处理。

3.1.3 Figure-Level Function

有时需要在一张图上绘制多个子图,每个图对应一组类别的数据。可以手动创建多个图,不过,图形级函数能够自动完成此工作,更为方便。与轴级函数一样,图形级函数包含一些参数,常见的参数列举如下:

  • data:用于绘图的数据集。 
  • x, y:指定 x 轴和 y 轴的变量名称。
  • hue:用于分组的变量名称,会根据该变量的取值不同,对数据进行分组并绘制成不同的颜色。
  • col, row:用于分面绘图的变量名称,会根据该变量的取值不同,将数据分为多个子图进行绘制。col 参数将把每个子图放在单独的列中,row 参数把每个子图放在单独的行中。
  • kind:指定绘图的类型,如 scatter、line、bar、box、violin 等。
  • size:控制图形的大小。
  • palette:设置调色板,可以是预设的颜色名称,也可以是自定义的颜色列表。 
  • markers:设置散点图标记,可以是预设的标记名称,也可以是自定义的标记列表。 
  • estimator:设置聚合函数,如 mean、median、count、sum 等。
  • order, hue_order:控制分类变量的排序顺序。
  • dodge:控制分组条形图的位置偏移。

(1)一排子图 col

依然使用 tips 数据集,假设想查看,吸烟和不吸烟这两类顾客的消费情况:

type(
 sns.relplot(
     data=tips, x="total_bill", y="tip", kind="scatter",
     hue="smoker", col="smoker"
 )
 .set(
     title="Total Bill vs Tip",
     xlabel="Total Bill",
     ylabel="Tip",
 )
 .legend.set(loc="outside upper right", title="Smoker")
);

在本例中,使用 relplot(),并设置 kind="scatter",告诉函数创建多个散点图子图。

通过设置 col="month",每个子图单独一列中,表示一个单独的类别,这意味着最终会有“一排”图。hue 参数用于对数据应用不同的颜色,实际上,建议始终将 hug 参数与 col 或 row 参数一起使用,以强制 seaborn 创建图例,对每个子图的内容做出说明。

图形级绘图函数,将创建一个 FacetGrid 对象,每个子绘图都放置在该对象上。如果想把由图形级绘图创建的图例标题大写,可以先使用 FacetGrid.legend 访问.set_title(),为底层 FacetGrid 对象添加图例标题。

最终的绘图结果如下:针对吸烟和不吸烟两种情况,创建了两个独立的散点图,分别使用不同的颜色,同时提供了图例,以便更好地识别图像所显示的内容。

(2)一列子图 row

如果将 col 参数 改为 row 参数:

3.2 Objects Interface

下面将以 Plot 对象为例,展示 Object Interface 的使用:通过创建并添加所需的单个对象来分层构建图像。

3.2.1 创建 Plot 对象

同样使用 tips 数据集,首先需要创建一个 plot 对象:

在使用 seaborn object interface 时,导入时习惯上使用别名 so。创建 Plot 对象,需要调用其构造函数,并传入数据以及要用于绘图列名,这里是 x="total_bill", y"tip"。Plot 对象自己有 .show() 方法,用于显示它。

import seaborn.objects as so


(
 so.Plot(
     data=tips, x="total_bill", y="tip"
 )
 .show()
)

运行结果如下,不过只有背景底图,没有数据图。事实上,Plot 对象只包含用于可视化的数据,需要向 Plot 对象添加一个或多个 Mark 对象完成图像的构建,Mark 对象是一系列数据可视化子类的基类。

3.2.2 添加数据图 Plot.add()

如果想将 Plot 对象的数据显示为散点图,可以通过调用 Plot 对象的 .add() 方法,向 Plot 对象添加 Dot 对象类实现。Dot 类是 Mark 的子类,它将每个 x-y 对显示为一个点。每次调用.add(),都在为 Plot 添加一个新的图层。最后,可以调用 Plot 的 .label() 方法,设置 x 轴和 y 轴的名称以及图标题。

import seaborn.objects as so


(
 so.Plot(
     data=tips, x="total_bill", y="tip"
 )
 .add(so.Dot())
 .label(
     title="Total Bill vs Tip",
     x="Total Bill",
     y="Tip",
 )
 .show()
)

3.2.2 设置颜色和样式

下面考虑进一步完善绘图效果,比如,想使用不同颜色标记吸烟者和不吸烟者的数据,可以将用于区分数据颜色的列名称作为 color 参数的值传递给 Plot 对象,color 参数能实现与 Funtioanal Interface 的 hue 参数类似的功能。在本例中,使用 color="month" 为两类数据分配不同的颜色。

还可以将不同的标记样式应用到吸烟者和不吸烟者两类数据,只需将用于区分标记样式的列名称作为 marker 参数传递给与 dot 对象同一层的 add 方法。在本例中,设置 marker="smoker" 来表示依据 smoker 列区分标记样式。

图标题和轴名称同之前一样,通过 Plot 对象的 .label 方法设置;此外,还可以通过 . label() 方法的 color 参数设置图例。本例中,color=str.capitalize 表示将字符串的 .capitalize() 方法应用于 color 对应的标签 somker,使其显示为 Smoker

import seaborn.objects as so


(
 so.Plot(
     data=tips, x="total_bill", y="tip", color="smoker"
 )
 .add(so.Dot(), marker="smoker")
 .label(
     title="Total Bill vs Tip",
     x=str.capitalize,
     y=str.capitalize,
     color=str.capitalize
 )
 .show()
)

3.2.4 绘制子图 facet

最后,考虑创建两个子图,分别表示吸烟者的数据和不吸烟者的数据。使用 Plot 对象的 .facet() 方法,传入数据分组依据的列名称即可,在本例中,使用 col="smoker",将两组数据分隔为两列。最后可以使用 Plot.layout() 方法将输出的图像大小调整为15英寸乘5英寸的宽度,使图像更美观。

import seaborn.objects as so


(
 so.Plot(
     data=tips, x="total_bill", y="tip", color="smoker"
 )
 .add(so.Dot(), marker="smoker")
 .facet(col="smoker")
 .layout(size=(15, 5))
 .label(
     title="Total Bill vs Tip",
     x="Total Bill",
     y="Tip",
     color=str.capitalize
 )
 .show()
)

如上图所示,每个子图仍然保留了自己的颜色和标记样式。Object Interface 能够通过对现有代码进行微小的调整来创建多个子图,而不会使其变得更复杂。使用 Object Interface,无需头开始修改函数。

注:Plot 对象可以用于不同的绘图过程,如果将 Plot 对象赋值给一个变量,稍后可以重用相同的对象,通过向其添加不同的内容来创建不同的图。

seaborn object interface 能够以一种更直观和可扩展的方式实现数据可视化,object interface 以模块化(modularity)的方式运行,所有的绘图都是从相同的 Plot 对象开始的,无论想可视化什么内容,添加所需的 Mark 对象即可。object interface 的灵感来自于图形语法 Grammar of Graphics,与 Plotnine、R 语言的 ggplot2 这样的绘图库,具有相同的灵感。

随时添加对象的功能,意味着你可以逐渐构建一些非常复杂的图像,总的来说,Object Interface 能够创建复杂的图形而无需使用复杂的代码,并且可以使代码风格更加统一。

不过,functional interface 仍然很受欢迎,并被广泛使用。如果你对它所提供的东西感到满意,那么就没必要改变。seaborn 开发者仍然在维护和改进它,它绝不是过时的。

5. 实践:使用 Functional Interface 绘图

接下来将使用 functional interface 和 object interface 完成一系列绘图,虽不能涵盖 seaborn 的所有功能,但足以展示很多有用的技巧。

在继续探索 seaborn 提供的各种数据可视化函数前,需先下载附件中数据文件https://download.csdn.net/download/weixin_40134371/89567322(数据来源:纽约市政府推出的开放数据平台 NYC Open Data,该平台提供了大量的纽约市政府和机构的数据集,包括交通、教育、环境、公共安全、健康等各个领域的数据,可以被免费访问、下载和使用)。

读入数据:

crossings = pd.read_csv('cycle_crossings_apr_jun.csv')
crossing.head()

5.1 Categorical Plots

Seaborn’s categorical plots are a family of plots that show the relationship between a collection of numerical values and one or more different categories. This allows you to see how the value varies across the different categories.

5.1.1 数据预处理

crossings 数据记录四座桥每日过桥次数,假设你想分别查看每座桥、每日的过桥次数,现在的问题是,想按桥的类型对数据进行分类,需要有一列记录桥的类型,另一列记录对应的数据,当前数据是每个桥单独一列。因此,首先需要使用 DataFrame.melt()方法,将数据从当前的宽格式更改为所需的长格式。

说明:melt 方法用于将宽格式的数据转换为长格式的数据,它的作用是保留原始数据指定的列(id_vars)作为索引,然后将某几列(value_vars)转换为行。

df = pd.DataFrame({'Student': {0: 'A', 1: 'B', 2: 'C'},
                   'Math': {0: 65, 1: 93, 2: 93},
                   'English': {0: 75, 1: 85, 2: 95},
                   'Chinese': {0: 85, 1: 92, 2: 99},
                   'Physical': {0: 65, 1: 82, 2: 94}})
df.head()
df.melt(id_vars=['Student'], value_vars=['Math', 'English'])

在当前的例子中,将 day 、date 以及 month 列将用作标识变量,id_vars=["day", "date", "month"];value_vars 设置为桥数据对应的四列;为了使绘图标签有意义且整洁(大写),分别传入 Bridge 和 Crossings 作为 var_name 和 val_name 的参数值,表示创建两个新列,Bridge 列包含桥的名称,Crossings 列包含每个桥在每一天的数据。此外,使用 datafframe .rename() 方法分别将日期和日期列名更新为 Day、Date 以及 Month,这样可以不必像之前那样更改各种标签值。

bridge_crossings = crossings.melt(
    id_vars=["day", "date", "month"],
    value_vars=[
    "Brooklyn", "Manhattan",
    "Williamsburg", "Queensboro"
    ],
    var_name="Bridge",
    value_name="Crossings",
).rename(columns={"day": "Day", "date": "Date", "month":"Month"})

bridge_crossings.head()

5.1.2 barplot

绘制条形图的方式与之前类似,增加了 hue 参数为每个桥梁的数据涂上不同的颜色。下述代码了绘制两个图,分别对应 estimator="mean"  和 estimator="sum" 。

sns.barplot(
    data=bridge_crossings,
    x="Day", y="Crossings",
    hue="Bridge", errorbar=None,
)
plt.show()


sns.barplot(
    data=bridge_crossings,
    x="Day", y="Crossings",
    hue="Bridge", errorbar=None,
    estimator="sum",
)
plt.show()

进一步,考虑将周六和周日的数据,分别绘制成两个子图。


(
 sns.catplot(
     data=bridge_crossings.loc[bridge_crossings.Day.isin(["Saturday", "Sunday"])], x="Bridge", y="Crossings",
     hue="Bridge", col="Day", errorbar=None,
     estimator="max", kind="bar",
 ).set(xlabel=None)
)

plt.show()

5.1.3 boxplot

根据条形图的分析,显然 Williamsburg Bridge 是最繁忙的,此外,似乎周三是最繁忙的一天。为了进一步调查这个现象,考虑通过箱线图了解数据的分布情况,判断是否是个别日期造成的影响,箱形图以一种便于在变量之间或分类变量的不同水平之间进行比较的方式显示定量数据的分布。

绘制 Williamsburg Bridge 每个月周三数据的箱线图。 

wednesday_crossings = crossings.loc[
    crossings.day.isin(["Wednesday"])
].rename(columns={"month": "Month"})

(
 sns.boxplot(
     data=wednesday_crossings, x="day",
     y="Williamsburg", hue="Month",
 )
 .set(xlabel=None)
)

plt.show()

进一步,考虑绘制所有桥的箱线图。


(
 sns.catplot(
     data=bridge_crossings.loc[bridge_crossings.Day == "Wednesday"], 
     x="Day", y="Crossings", hue="Bridge",
     kind="box",
     col="Month"
 )
 .set(xlabel=None)
)

plt.show()

5.2 Distribution Plots

Seaborn’s distribution plots are a family of plots that allow you to view the distribution of data across a range of samples. This can reveal trends in the data or other insights, such as allowing you to see whether or not your data conforms to a common statistical distribution.

5.2.1 histplot

创建直方图使用 histplot,同其他轴级函数一样,data 参数接收 DataFrame,x 或 y 参数表示绘图使用的数据对应的列名(x 和 y 参数不能同时为None)。

  • data:绘图数据集。
  • x,y:指定绘图数据的变量名或变量位置索引。
  • hue:指定分类变量的变量名或索引,用于对数据进行分组并绘制不同颜色的直方图。
  • stat:指定直方图的统计方法,可以是 count(默认值)、density 或 probability。
  • bins:指定直方图的柱子数量。 
  • binwidth:指定直方图的柱子宽度。 
  • binrange:指定直方图的柱子范围。
  • cumulative:指定是否绘制累积分布函数曲线。
  • common_norm:hue 参数不为 None 时,该参数指定是否使用相同的 y 轴刻度范围。
  • multiple:当 hue 参数不为 None 时,该参数用于指定是否绘制多个直方图。 
  • element:指定绘图元素的类型,可以是 bars(默认值)、step 或 poly。 
  • fill:指定是否填充直方图的柱子。
  • kde:指定是否绘制核密度估计曲线。
  • palette:指定分类变量的调色板。
  • color:指定直方图的颜色。
  • hue_order:指定分类变量的顺序。
  • hue_norm:指定分类变量的归一化方法。
  • log_scale:指定是否对 y 轴进行对数变换。
  • legend:指定是否显示图例。
  • ax:指定绘图所在的坐标轴。
(
    sns.histplot(data=crossings, x="Brooklyn", bins=10)
    .set(title="Brooklyn Distribution")
)
plt.show()

(
    sns.histplot(data=crossings, y="Brooklyn", bins=10)
    .set(title="Brooklyn Distribution")
)
plt.show()

(
    sns.histplot(data=bridge_crossings, x="Crossings", bins=20, hue="Bridge", kde=True, multiple="dodge")
    .set(title="Crossings Distribution")
)
plt.show()

将 histplot() 函数的 hue 参数设置为 “manufacturer”,可以为四个类别分别绘制条形图;将 multiple 参数设置为 “dodge”,可确保图像不重叠;将 kde 参数设置为 True,可以在图像上增加概率密度曲线。

如果想把这四组数据分别绘制为四张子图,可以使用图形级函数 displot(),而不是轴级函数 histplot()。displot() 默认创建直方图,也可以使用 kind="hist" 显式指定为绘制直方图。

(
    sns.displot(data=bridge_crossings, x="Crossings", kind="hist", hue="Bridge", col="Bridge", bins=20)
)

5.2.2 kdeplot

另一种常见的分布图是核密度估计图(KDE),常用于分析连续数据,估计其概率密度曲线,即在其取值范围内任何值出现的概率。

(
    sns.kdeplot(data=bridge_crossings, x="Crossings", hue="Bridge")
    .set(title="Crossings Distribution")
)
plt.show()

kdeplot() 函数的参数含义与 histplot() 基本相同,生成的 KDE 曲线如下:

 

5.2.3 rugplot

地毯图是另一种用于可视化数据分布的图。它包含一组垂直线,就像地毯上的绒毛一样,但其间距随所代表的数据的分布密度而变化,更常见的数据对应更紧密的线,而不太常见的数据对应更稀疏的线。

地毯图本身是一个独立的图,但它通常作为其他图的补充来用,以一种不显眼的方式显示单个观测值的数据分布情况。

以之前使用过的 tips 数据集为例,可以为散点图增加 rugplot,更加直观的显示数据分布情况。

sns.scatterplot(data=tips, x="total_bill", y="tip")
(
    sns.rugplot(data=tips, x="total_bill", y="tip", height=0.05)
    .set(title="Total_Bill vs Tip", xlabel="Total_Bill", ylabel="Tip")
)

sns.kdeplot(data=crossings, x="Brooklyn")

(
 sns.rugplot(
     data=crossings, x="Brooklyn",
     height=0.2, color="black",
 )
 .set(title="Brooklyn-data Distribution")
)

plt.show()

5.3 Realtional Plots

5.3.1 scatterplot

参考 3.1 部分

5.3.2 lineplot

线形图将数据显示为一组用直线段相连的数据标记点,seaborn 中 使用 lineplot() 函数绘制线型图。lineplot 通常用于可视化时间序列数据,一般将 x 设置为日期列,将 y 设置为数据列。

下面,用线形图是显示从 4 月到 6 月,每天通过 Brooklyn 的人数,代码如下


(
    sns.lineplot(data=crossings, x="date", y="Brooklyn")
    .set(title="Lineplot of Brooklyn", xlabel="Date")
)

plt.xticks(ticks=["01/04/2017", "01/05/2017", "01/06/2017", "30/06/2017"], rotation=45)
plt.show()
  1. 在当前数据中,x 数据过多,有九十多个值,绘图时 x 轴的刻度值将被压缩在一起,不便阅读,为此使用 Matplotlib.xticks() 函数,仅显示三个月中的每个月的开始日期以及6月的最后一天。
  2. 为了美化图像外观,调用 seaborn 的 set_theme() 函数将背景主题设置为 darkgrid,图片背景将改为灰色并带有白色网格的风格。需要注意的是,此设置将适用于所有后续所有的图,除非将其重置为默认值 white。

按 Bridge 分组绘制,每个桥的数据显示为具有不同颜色和外观的线条(Data for each bridge appears as a separate line with a different color and appearance.):


(
    sns.lineplot(data=bridge_crossings, x="Date", y="Crossings", hue="Bridge", style="Bridge")
    .set(title="Lineplot of Brooklyn", xlabel="Date")
)
plt.legend(loc="upper right")
plt.xticks(ticks=["01/04/2017", "01/05/2017", "01/06/2017", "30/06/2017"], rotation=45)
plt.show()

 

绘制成 4 个子图的形式:

relp = (
    sns.relplot(data=bridge_crossings, x="Date", y="Crossings", 
                kind="line", 
                hue="Bridge", style="Bridge", col="Bridge")
)
for ax in relp.axes.flat:
    ax.set_xlabel(None)
    ax.set_xticks(["01/04/2017", "01/05/2017", "01/06/2017", "30/06/2017"])
    ax.set_xticklabels(ax.get_xticklabels(), rotation=45)
plt.show()

5.4 Regression Plots

回归图是一种用于可视化两个变量之间关系的图表,通常用于探索两个连续变量之间的线性关系。回归图通常会绘制一个散点图,其中每个数据点表示两个变量的一对观测值,然后,把回归线绘制在散点图上,以显示两个变量之间的线性关系。回归线的斜率和截距可以帮助我们了解两个变量之间的关系强度和方向。seaborn 中有两个轴级回归图函数: regplot() 和 residplot() 分别表示回归分析和回归分析的残差。

5.4.1 regplot

  • data:用于绘图的数据集。 
  • x, y:x, y 轴数据,可以是 numpy 数组,pandas 的 DataFrame 或 Series,也可以是变量名称的字符串。 
  • x_estimator:用于聚合 x 轴上相同值的 y 值的统计方法,默认为 numpy.mean。 
  • x_bins:将 x 轴上的数据分成多少个 bin,可用于控制 x 轴数据的离散化程度,默认为 None 不分组。 
  • scatter:是否绘制散点,默认为 True。
  • fit_reg:是否拟合回归模型,取值为 True 或 False,默认为 True。  
  • ci:误差线的置信区间,默认为 95%。 
  • n_boot:用于计算置信区间的 bootstrap 次数,默认为 1000。 
  • units:观测单位的标识符,如果数据集中有多个观测单位,则可以使用该参数来指定。 
  • seed:用于控制随机数生成器的种子。 
  • order:拟合回归模型的多项式次数,默认为1,即线性回归。
  • scatter_kws:用于控制散点图的样式和属性的字典。 
  • line_kws:用于控制回归线的样式和属性的字典。 
  • ax:绘制图形的坐标轴对象。 
  • color:回归线和误差带的颜色。 
  • label:回归线和误差带的标签。

(1)绘制回归图

使用 crossings 数据集,探究每日最高温度与最低温度的关系。


(
 sns.regplot(
     data=crossings, x="max_temp",
     y="min_temp", ci=95,
 )
 .set(
     title="Regression Analysis of Temperatures",
     xlabel="Minimum Temperature",
     ylabel="Maximum Temperature",
 )
)

plt.show()

得到的图像如下,线周围的阴影表示置信区间,默认情况下,该值为95%,可以通过设置 ci 参数进行调整,ci=None 表示删除置信区间。

(2)绘制残差图

ax = sns.residplot(
 data=crossings, x="min_temp", y="max_temp",
)

ax.set_title("Regression Analysis of Temperatures")
ax.set_xlabel("Minimum Temperature")
ax.set_ylabel("Maximum Temperature-Residual Error")


plt.show()

(3)回归方程和 R 方值

比较遗憾的是,regplot() 不能将回归方程公式和 R 方值插入到图像中,regplot() 内部是知道这些的,但不会把它们显示出来。如果想看到这个方程,需要手动计算并显示,可以使用 scikit-learn 库(pip install scikit-learn)的 LinearRegression 类,该类的实例可以根据两个变量的数据计算最小二乘线性回归的结果。

from sklearn.linear_model import LinearRegression

x = crossings.loc[:, ["min_temp"]]
y = crossings.loc[:, "max_temp"]

model = LinearRegression()
model.fit(x, y)

r_squared = f"R-Squared: {model.score(x, y):.2f}"
best_fit = (
 f"y = {model.coef_[0]:.4f}x"
 f"{model.intercept_:+.4f}"
)

ax = sns.regplot(
 data=crossings, x="min_temp", y="max_temp",
 line_kws={"label": f"{best_fit}\n{r_squared}"},
)

ax.set_xlabel("Minimum Temperature")
ax.set_title("Regression Analysis of Temperatures")
ax.set_ylabel("Maximum Temperature")
ax.legend()

plt.show()

在上述代码中,先从 sklearn.linear_model 导入 LinearRegression,需要用它执行线性回归计算(默认算法为普通最小二乘 OLS)。LinearRegression 的参数,x 为 min_temp 列数据 -- panda.DataFrame(df.loc[:,[]] 返回 DataFrame); y 为 max_temp 列数据 -- panda.Series,因为有可能需要针对多个变量做回归分析,x 被定义为 DataFrame。

LinearRegression 实例使用 .fit() 方法执行实际的回归计算;使用 .score() 方法计算 R 方值,R 方表示拟合线与实际值的接近程度;线性回归线的斜率和截距分别存储在LinearRegression 实例的 .coef_[0] 和 .intercept_ 属性中,coef_ 和 model.intercept_ 都带有下划线后缀,实际上这是一个scikit-learn 的书写惯例,用于指示这是有估计值的变量。

使用 regplot() 函数绘图,参数同前所述,不过这里额外设置了 line_kws 参数,用于定义回归线的标签内容。

regplot() 返回 Matplotlib Axes 对象,这里将其定义为名为 ax 的变量,并使用 axes.set_title 和 axes.set_ylabel 设置图像及轴的标题,使用 .legend() 方法显示标签内容。

最终得到如下图像:

(4)多组数据

regplot 的参数中没有 hug,使用如下代码把多组数据同时绘制在一张图上:

import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns
from sklearn.linear_model import LinearRegression

def calculate_regression(month, data):
    x = data.loc[:, ["min_temp"]]
    y = data.loc[:, "max_temp"]
    model = LinearRegression()
    model.fit(x, y)
    r_squared = (f"R-Squared: {model.score(x, y):.2f}")
    best_fit = (
     f"{month}\ny = {model.coef_[0]:.4f}x"
     f"{model.intercept_:+.4f}"
    )
    return r_squared, best_fit

def drawplot(month, crossings):
    monthly_crossings = crossings[crossings.month == month]
    r_squared, best_fit = calculate_regression(month, monthly_crossings)
    
    ax = sns.regplot(
        data=monthly_crossings, x="min_temp",
        y="max_temp", ci=None,
        line_kws={"label": f"{best_fit}\n{r_squared}"},
    )    

    
months = ["April", "May", "June"]
for month in months:
    drawplot(month, crossings)

plt.title("Regression Analysis of Temperatures")
plt.legend()
plt.show()

(5)多组数据 -- 子图

sns.lmplot(
        data=crossings, x="min_temp",
        y="max_temp", ci=None, hue="month", col="month"
    )

应用 FacetGrid.map_dataframe,为每个子图增加文本内容。

Seaborn's Object Interface : map() and map_dataframe() - GeeksforGeeks

https://www.cnblogs.com/xiqi2018/p/15775736.html

from sklearn.linear_model import LinearRegression


def regression_equation(data, **kws):
    x = data.loc[:, ["min_temp"]]
    y = data.loc[:, "max_temp"]
    model = LinearRegression()
    model.fit(x, y)
    r_squared = (f"R-Squared: {model.score(x, y):.2f}")
    best_fit = (
        f"y = {model.coef_[0]:.4f}x"
        f"{model.intercept_:+.4f}"
    )
    ax = plt.gca()  # Get current Axes.
    ax.text(0.1, 0.6, f"{best_fit}\n{r_squared}", 
            transform=ax.transAxes,
    )


sns.lmplot(
    data=crossings, x="min_temp",
    y="max_temp", col="month", order=1
    ).map_dataframe(regression_equation)

plt.show()

6. 实践:使用 Object Interface 绘图

官方教程:The seaborn.objects interface — seaborn 0.13.2 documentation

6.1 组合图

6.1.1 线型图+条形图

seaborn object interface 包括很多 Mark 对象,如 Line、Bar 和 Area,以及在 3.3 节介绍的 Dot,它们都可以单独生成图形,也可以将它们组合起来生成更复杂的图形。

crossings = pd.read_csv(
    "cycle_crossings_apr_jun.csv",
    parse_dates=["date"],
    dayfirst=True,  # DD/MM format dates, international and European format.
)


# 取四月第一周数据
first_week = (
 crossings
 .loc[crossings.month == "April"]
 .sort_values(by="date")
 .head(7)
)

(
 so.Plot(data=first_week, x="day", y="min_temp")
 .add(so.Line(color="black", linewidth=3, marker="o"))
 .add(so.Bar(color="green", fill=False, edgewidth=3))
 .add(so.Area(color="yellow"))
 .label(
     x="Day", y="Temperature",
     title="Minimum Temperature",
 )
 .show()
)

代码说明:首先,创建包含所有数据的 Plot 对象,指定 data、x 和 y 对应的内容,稍后添加到图像中的任何对象都可以使用这些数据。接下来,使用 plot .add() 方法将希望添加的对象加到图像中。每次调用 plot .add() 时,需将对象的参数添加到对象所属的单独的那一层中。

在本例中,调用了三次 .add(),添加了三个独立的层:第一层为 Line 对象,用于创建线型图,通过传递颜色、线宽和标记参数,自定义 Line 对象的外观;第二层为 Bar 对象,用于创建条形图;最后一层为 Area 对象,用于绘制数据下面的阴影,本例中,将其颜色指定为黄色。最后,调用 Plot 的 .label() 方法,为 Plot 设置标题和轴标签。

最终绘图结果如下:

6.1.2 条形图+条形图

建议阅读过 6.2 后再看此示例。

(
    so.Plot(
        data=bridge_crossings, x="Bridge", y="Crossings", color="Bridge"
    )
    .add(so.Bar(width=0.3), so.Agg(func="min"))
    .add(so.Bar(width=0.3), so.Agg(func="max"), so.Shift(x=0.35))
    .label(
        x="Bridge", y="Crossings",
        title="Bridge Crossings"
    )
)

6.2 应用 Stat 和 Move

还是使用 crossings 数据集,分析三个月中,每个月在一周七天中,各天最高温度的中值。


(
 so.Plot(
     data=crossings, x="month",
     y="max_temp", color="day",
 )
 .add(
     so.Bar(),
     so.Agg(func="median"),
     so.Dodge(gap=0.1),
 )
 .label(
     x=None, y="Temperature",
     title="Median Temperature", color="Day",
 )
)

plt.legend.set(loc="outside upper right")
plt.show()

代码说明:首先,定义 Plot 对象时,相比与 6.1, 此处多了一个 color 参数,此参数用于定义数据系列,功能类似于前文中的 hue 参数,只不过 Plot 中不存在 hue 参数。本例中,设置 color=“day” 表示所有添加的图层,都将把数据按照 day 列值分成不同的颜色。

接下来,使用 Bar 对象绘图,不过,仅有 bar 对象是不行的,所有的数据对应的 “柱” 会叠加显示,绘图结果很混乱,因此,需要在同一层使用 Stat 类型对象和 Move 类型对象调整细节:(1)把 Agg 对象(Stat 类型)添加到与 Bar 对象相同的层中,指示在把数据绘图之前如何转换或计算数据,即要求每个柱子显示这组数据的哪种统计值。在本例中,设置 func="median",要求每个 Bar 对象使用中值,默认值是 "mean"。(2)默认情况下,每个条将显示在彼此的上方,叠加显示,为了将它们分开,添加一个 Dodge 对象(Move 类型),调整不同条的位置。在本例中,用 gap=0.1 设置柱之间的间隙。最后使用 .label() 方法指定绘图的标签,color="Day" 用于设置图例标题。

最终绘图结果如下,每个月的数据由一个单独的柱状图簇表示,在每个柱状图簇中,每个柱表示一周中不同的日子,如果仔细观察还可以发现,柱与柱之间是稍微分开的。

如果仅使用 Bar 对象,所有数据的 “柱” 叠加在一起:

增加 Dodge 对象,不同 Day 的数据分开显示,但相同 Day 的数据,仍是叠加显示的:

应用 Agg 对象,相同 Day 的数据计算中位数,即只显示一个 “柱子” 对应这组数据的中位数:

Agg 对象参数为 "sum' 时:

6.3 子图

使用 .facet()


(
 so.Plot(
     data=crossings, x="month",
     y="max_temp", color="day",
 )
 .facet(col="month")
 .add(
     so.Bar(),
     so.Agg(func="median"),
     so.Dodge(gap=0.1),
 )
 .label(
     x=None, y="Temperature",
     title="Median Temperature", color="Day",
 )
)

plt.show()

绘图结果:

  • 9
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值