提交 8cfb323e 编写于 作者: W wizardforcel

8.12-15

上级 97c8f3ee
# 8.12 文本和注解
> 原文:[Text and Annotation](http://nbviewer.jupyter.org/github/donnemartin/data-science-ipython-notebooks/blob/master/matplotlib/04.09-Text-and-Annotation.ipynb)
>
> 译者:[飞龙](https://github.com/wizardforcel)
>
> 协议:[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/)
>
> 本节是[《Python 数据科学手册》](https://github.com/jakevdp/PythonDataScienceHandbook)(Python Data Science Handbook)的摘录。
创建良好的可视化涉及引导读者并使图形讲述故事。在某些情况下,可以以完全可视的方式讲述这个故事,而不需要添加文本,但在其他情况下,需要小的文本提示和标签。也许你将使用的最基本的注释类型是轴标签和标题,但选项超出了这个范围。让我们看看一些数据,以及我们如何可视化和注释它,来有助于传达有趣的信息。 我们首先设置笔记本来绘图并导入我们将使用的函数:
```py
%matplotlib inline
import matplotlib.pyplot as plt
import matplotlib as mpl
plt.style.use('seaborn-whitegrid')
import numpy as np
import pandas as pd
```
## 示例: 美国新生儿的假期效应
让我们回到之前处理的一些数据,在“示例:出生率数据”中,我们在日历年上生成了平均出生率的图表;如前所述,这些数据可以在 <https://raw.githubusercontent.com/jakevdp/data-CDCbirths/master/births.csv> 下载。
我们将使用我们在那里使用的相同清理过程开始,并绘制结果:
```py
births = pd.read_csv('data/births.csv')
quartiles = np.percentile(births['births'], [25, 50, 75])
mu, sig = quartiles[1], 0.74 * (quartiles[2] - quartiles[0])
births = births.query('(births > @mu - 5 * @sig) & (births < @mu + 5 * @sig)')
births['day'] = births['day'].astype(int)
births.index = pd.to_datetime(10000 * births.year +
100 * births.month +
births.day, format='%Y%m%d')
births_by_date = births.pivot_table('births',
[births.index.month, births.index.day])
births_by_date.index = [pd.datetime(2012, month, day)
for (month, day) in births_by_date.index]
fig, ax = plt.subplots(figsize=(12, 4))
births_by_date.plot(ax=ax);
```
![png](../img/8-12-1.png)
When we're communicating data like this, it is often useful to annotate certain features of the plot to draw the reader's attention.
This can be done manually with the ``plt.text``/``ax.text`` command, which will place text at a particular x/y value:
```py
fig, ax = plt.subplots(figsize=(12, 4))
births_by_date.plot(ax=ax)
# 向绘图添加标签
style = dict(size=10, color='gray')
ax.text('2012-1-1', 3950, "New Year's Day", **style)
ax.text('2012-7-4', 4250, "Independence Day", ha='center', **style)
ax.text('2012-9-4', 4850, "Labor Day", ha='center', **style)
ax.text('2012-10-31', 4600, "Halloween", ha='right', **style)
ax.text('2012-11-25', 4450, "Thanksgiving", ha='center', **style)
ax.text('2012-12-25', 3850, "Christmas ", ha='right', **style)
# 标记轴域
ax.set(title='USA births by day of year (1969-1988)',
ylabel='average daily births')
# 使用中心化的月标签将 x 轴格式化
ax.xaxis.set_major_locator(mpl.dates.MonthLocator())
ax.xaxis.set_minor_locator(mpl.dates.MonthLocator(bymonthday=15))
ax.xaxis.set_major_formatter(plt.NullFormatter())
ax.xaxis.set_minor_formatter(mpl.dates.DateFormatter('%h'));
```
![png](../img/8-12-2.png)
``ax.text``方法接受`x`位置,`y`位置,字符串,然后是可选关键字,指定文本的颜色,大小,样式,对齐方式和其他属性。在这里,我们使用``ha='right'````ha='center'``,其中``ha``是 horizonal alignment 的缩写。可用选项的更多信息,请参阅``plt.text()````mpl.text.Text()``的文档字符串。
## 变换和文本位置
在前面的示例中,我们将文本注释锚定到数据位置。 有时最好将文本锚定到轴或图上的位置,与数据无关。在 Matplotlib 中,这是通过修改变换来完成的。
任何图形显示框架都需要一些在坐标系之间进行转换的方案。例如,`(x, y) = (1, 1)`处的数据点,需要以某种方式表示在图上的某个位置,而该位置又需要在屏幕上以像素表示。在数学上,这种坐标转换相对简单,Matplotlib 有一套完善的工具,它们在内部使用来执行(这些工具可以在``matplotlib.transforms``子模块中进行探索)。
普通用户很少需要关心这些变换的细节,但在考虑在图形上放置文本时,它是有用的知识。 在这种情况下,有三种预定义的转换可能很有用:
- ``ax.transData``:数据坐标相关的变换
- ``ax.transAxes``:轴域(以轴域维度为单位)相关的变换
- ``fig.transFigure``:图形(以图形维度为单位)相关的变换
这里让我们看一下,使用这些变换在不同位置绘制文本的示例:
```py
fig, ax = plt.subplots(facecolor='lightgray')
ax.axis([0, 10, 0, 10])
# transform=ax.transData 是默认值,但是我们无论如何也要指定它
ax.text(1, 5, ". Data: (1, 5)", transform=ax.transData)
ax.text(0.5, 0.1, ". Axes: (0.5, 0.1)", transform=ax.transAxes)
ax.text(0.2, 0.2, ". Figure: (0.2, 0.2)", transform=fig.transFigure);
```
![png](../img/8-12-3.png)
请注意,默认情况下,文本在指定坐标的上方和左侧对齐:这里,在每个字符串的开头的`'.'`将近似标记给定的坐标位置。
``transData``坐标给出了关联`x`轴和`y`轴标签的常用数据坐标。``transAxes``坐标给出了相对于轴域左下角(这里是白框)的位置,作为轴域大小的比例。``transFigure``坐标是相似的,但是指定相对于图左下角(这里是灰框)的位置,作为图形大小的比例。
现在请注意,如果我们更改轴限制,那么只有`transData`坐标会受到影响,而其他坐标则保持不变:
```py
ax.set_xlim(0, 2)
ax.set_ylim(-6, 6)
fig
```
![png](../img/8-12-4.png)
通过交互式更改轴限制可以更清楚地看到这种行为:如果你在笔记本中执行此代码,你可以通过将``%matplotlib inline``更改为``%matplotlib notebook``,并使用每个绘图的菜单与它互动来实现它。
## 箭头和标注
除了刻度线和文本,另一个有用的标注或标记是简单的箭头。
在 Matplotlib 中绘制箭头通常比砍价要困难得多。虽然``plt.arrow()``函数是可用的,我不建议使用它:它创建的箭头是 SVG 对象,它们会受到不同长宽比的影响,结果很少是用户所期望的。相反,我建议使用``plt.annotate()``函数。此函数可创建一些文本和箭头,并且箭头可以非常灵活地指定。
在这里,我们将使用``annotate``及其几个选项:
```py
%matplotlib inline
fig, ax = plt.subplots()
x = np.linspace(0, 20, 1000)
ax.plot(x, np.cos(x))
ax.axis('equal')
ax.annotate('local maximum', xy=(6.28, 1), xytext=(10, 4),
arrowprops=dict(facecolor='black', shrink=0.05))
ax.annotate('local minimum', xy=(5 * np.pi, -1), xytext=(2, -6),
arrowprops=dict(arrowstyle="->",
connectionstyle="angle3,angleA=0,angleB=-90"));
```
![png](../img/8-12-6.png)
箭头样式通过``arrowprops``字典控制,该字典有许多选项。这些选项在 Matplotlib 的在线文档中有相当详细的记录,因此,比起在此复述这些选项,快速展示一些选项可能更有用。让我们使用之前的出生率图表演示几种可用选项:
```py
fig, ax = plt.subplots(figsize=(12, 4))
births_by_date.plot(ax=ax)
# 向绘图添加标签
ax.annotate("New Year's Day", xy=('2012-1-1', 4100), xycoords='data',
xytext=(50, -30), textcoords='offset points',
arrowprops=dict(arrowstyle="->",
connectionstyle="arc3,rad=-0.2"))
ax.annotate("Independence Day", xy=('2012-7-4', 4250), xycoords='data',
bbox=dict(boxstyle="round", fc="none", ec="gray"),
xytext=(10, -40), textcoords='offset points', ha='center',
arrowprops=dict(arrowstyle="->"))
ax.annotate('Labor Day', xy=('2012-9-4', 4850), xycoords='data', ha='center',
xytext=(0, -20), textcoords='offset points')
ax.annotate('', xy=('2012-9-1', 4850), xytext=('2012-9-7', 4850),
xycoords='data', textcoords='data',
arrowprops={'arrowstyle': '|-|,widthA=0.2,widthB=0.2', })
ax.annotate('Halloween', xy=('2012-10-31', 4600), xycoords='data',
xytext=(-80, -40), textcoords='offset points',
arrowprops=dict(arrowstyle="fancy",
fc="0.6", ec="none",
connectionstyle="angle3,angleA=0,angleB=-90"))
ax.annotate('Thanksgiving', xy=('2012-11-25', 4500), xycoords='data',
xytext=(-120, -60), textcoords='offset points',
bbox=dict(boxstyle="round4,pad=.5", fc="0.9"),
arrowprops=dict(arrowstyle="->",
connectionstyle="angle,angleA=0,angleB=80,rad=20"))
ax.annotate('Christmas', xy=('2012-12-25', 3850), xycoords='data',
xytext=(-30, 0), textcoords='offset points',
size=13, ha='right', va="center",
bbox=dict(boxstyle="round", alpha=0.1),
arrowprops=dict(arrowstyle="wedge,tail_width=0.5", alpha=0.1));
# 标记轴域
ax.set(title='USA births by day of year (1969-1988)',
ylabel='average daily births')
# 使用中心化的月标签将 x 轴格式化
ax.xaxis.set_major_locator(mpl.dates.MonthLocator())
ax.xaxis.set_minor_locator(mpl.dates.MonthLocator(bymonthday=15))
ax.xaxis.set_major_formatter(plt.NullFormatter())
ax.xaxis.set_minor_formatter(mpl.dates.DateFormatter('%h'));
ax.set_ylim(3600, 5400);
```
![png](../img/8-12-7.png)
你会注意到箭头和文本框的规格非常详细:这使你能够创建几乎任何箭头样式。不幸的是,这也意味着这些功能通常必须手动调整,这个过程在制作出版品质的图形时非常耗时!最后我要提醒你,前面的样式混合绝不是展示数据的最佳实践,而是作为一些可用选项的演示。
可用箭头和注释样式的更多讨论和示例,可以在 Matplotlib 库中找到,特别是[标注的演示](http://matplotlib.org/examples/pylab_examples/annotation_demo2.html)
\ No newline at end of file
# 8.13 自定义刻度
> 原文:[Customizing Ticks](https://nbviewer.jupyter.org/github/donnemartin/data-science-ipython-notebooks/blob/master/matplotlib/04.10-Customizing-Ticks.ipynb)
>
> 译者:[飞龙](https://github.com/wizardforcel)
>
> 协议:[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/)
>
> 本节是[《Python 数据科学手册》](https://github.com/jakevdp/PythonDataScienceHandbook)(Python Data Science Handbook)的摘录。
Matplotlib 的默认刻度定位器和格式化程序,在许多常见情况下通常都足够了,但对于每个绘图都不是最佳选择。本节将提供几个刻度位置和格式的示例,它们调整你感兴趣的特定绘图类型。
在我们进入示例之前,我们最好进一步了解 Matplotlib 绘图的对象层次结构。Matplotlib 旨在用 Python 对象表示绘图中出现的所有内容:例如,回想一下``figure``是绘图元素所在的边框。每个 Matplotlib 对象也可以充当子对象的容器:例如,每个``figure``可以包含一个或多个``axes``对象,它们的每个又包含表示绘图内容的其他对象。
刻度线也不例外。 每个``axes``都有属性``xaxis````yaxis``,它们又具有一些属性,包括构成轴域的直线,刻度和标签。
## 主要和次要刻度
在每个轴内,有主要刻度标记和次要刻度标记的概念。 正如名称所暗示的那样,主要刻度通常更大或更明显,而次要刻度通常更小。 默认情况下,Matplotlib 很少使用次要刻度,但是你可以在对数绘图中看到它们:
```py
import matplotlib.pyplot as plt
plt.style.use('classic')
%matplotlib inline
import numpy as np
ax = plt.axes(xscale='log', yscale='log')
ax.grid();
```
![png](../img/8-13-1.png)
我们在这里看到每个主刻度线显示为一个大刻度线和一个标签,而每个次刻度线显示为一个没有标签的较小刻度线。
这些刻度属性 - 位置和标签 - 也就是说,可以通过设置每个轴的``formatter````locator``对象来定制。 让我们检查刚刚展示的绘图的`x`轴:
```py
print(ax.xaxis.get_major_locator())
print(ax.xaxis.get_minor_locator())
'''
<matplotlib.ticker.LogLocator object at 0x10dbaf630>
<matplotlib.ticker.LogLocator object at 0x10dba6e80>
'''
print(ax.xaxis.get_major_formatter())
print(ax.xaxis.get_minor_formatter())
'''
<matplotlib.ticker.LogFormatterMathtext object at 0x10db8dbe0>
<matplotlib.ticker.NullFormatter object at 0x10db9af60>
'''
```
我们看到主要和次要刻度标签的位置都由`LogLocator`指定(这对于对数图是有意义的)。 但是,次要刻度的标签格式为`NullFormatter`:这表示不会显示任何标签。我们现在将展示一些为各种图设置这些定位器和格式化器的示例。
## 隐藏刻度或标签
也许最常见的刻度/标签格式化操作是隐藏刻度或标签。这可以使用``plt.NullLocator()````plt.NullFormatter()``来完成,如下所示:
```py
ax = plt.axes()
ax.plot(np.random.rand(50))
ax.yaxis.set_major_locator(plt.NullLocator())
ax.xaxis.set_major_formatter(plt.NullFormatter())
```
![png](../img/8-13-2.png)
请注意,我们已经从`x`轴移除了标签(但保留了刻度线/网格线),并从`y`轴中删除了刻度线(以及标签)。
在许多情况下,不显示刻度可能很有用 - 例如,当你想要显示图像网格的时候。
例如,考虑下图,它包含不同的面部图像,这是监督机器学习问题中常用的一个例子(例如,参见“深入:支持向量机”):
```py
fig, ax = plt.subplots(5, 5, figsize=(5, 5))
fig.subplots_adjust(hspace=0, wspace=0)
# 从 sklearn 获取一些人脸数据
from sklearn.datasets import fetch_olivetti_faces
faces = fetch_olivetti_faces().images
for i in range(5):
for j in range(5):
ax[i, j].xaxis.set_major_locator(plt.NullLocator())
ax[i, j].yaxis.set_major_locator(plt.NullLocator())
ax[i, j].imshow(faces[10 * i + j], cmap="bone")
```
![png](../img/8-13-3.png)
请注意,每个图像都有自己的轴域,我们将定位器设置为`null`,因为刻度值(在这种情况下为像素数)不会传达这个特定可视化的相关信息。
## 减少或增加刻度数量
默认设置的一个常见问题是,较小的子图最终会拥有密集的标签。我们可以在这里显示的绘图网格中看到它:
```py
fig, ax = plt.subplots(4, 4, sharex=True, sharey=True)
```
![png](../img/8-13-4.png)
特别是对于`x`刻度,数字几乎重叠并使它们很难看清。我们可以用``plt.MaxNLocator()``解决这个问题,它允许我们指定要显示的最大刻度数。给定此最大数量,Matplotlib 将使用内部逻辑来选择特定的刻度位置:
```py
# 对于每个轴,设置 x 和 y 主要定位器
for axi in ax.flat:
axi.xaxis.set_major_locator(plt.MaxNLocator(3))
axi.yaxis.set_major_locator(plt.MaxNLocator(3))
fig
```
![png](../img/8-13-5.png)
这使事情变得更加干净。 如果你想要更多地控制等间隔的刻度位置,你也可以使用``plt.MultipleLocator``,我们将在下一节讨论。
## 花式刻度格式
Matplotlib 的默认刻度格式可能会有很多不足之处:它可以作为一个泛用的默认值,但有时你还想做更多的事情。考虑一下正弦和余弦的绘图:
```py
# 绘制正弦和余弦曲线
fig, ax = plt.subplots()
x = np.linspace(0, 3 * np.pi, 1000)
ax.plot(x, np.sin(x), lw=3, label='Sine')
ax.plot(x, np.cos(x), lw=3, label='Cosine')
# 配置网格,图例和限制
ax.grid(True)
ax.legend(frameon=False)
ax.axis('equal')
ax.set_xlim(0, 3 * np.pi);
```
![png](../img/8-13-6.png)
我们可能想做一些改变。 首先,以 π 的倍数的刻度线和网格线来区分这些数据更加自然。 我们可以通过设置``MultipleLocator``来实现,它可以在你提供的数字的倍数处,设置刻度线。 为了更好地衡量,我们将以`π/4`的倍数添加主要和次要刻度:
```py
ax.xaxis.set_major_locator(plt.MultipleLocator(np.pi / 2))
ax.xaxis.set_minor_locator(plt.MultipleLocator(np.pi / 4))
fig
```
![png](../img/8-13-7.png)
但现在这些刻度标签看起来有点傻:我们可以看到它们是 π 的倍数,但十进制表示并没有立即传达这一点。要解决这个问题,我们可以更改刻度格式化器。对于我们想要做的事情,没有内置格式化器,所以我们改为使用``plt.FuncFormatter``,它接受用户定义的函数,对刻度输出进行细粒度控制:
```py
def format_func(value, tick_number):
# 寻找 pi/2 倍数的数字
N = int(np.round(2 * value / np.pi))
if N == 0:
return "0"
elif N == 1:
return r"$\pi/2$"
elif N == 2:
return r"$\pi$"
elif N % 2 > 0:
return r"${0}\pi/2$".format(N)
else:
return r"${0}\pi$".format(N // 2)
ax.xaxis.set_major_formatter(plt.FuncFormatter(format_func))
fig
```
![png](../img/8-13-8.png)
这要好得多! 请注意,我们已经使用了 Matplotlib 的 LaTeX 支持,通过将字符串括在美元符号中来指定。 这对于显示数学符号和公式非常方便:在这种情况下,`$\pi$`显示为希腊字符`π`
``plt.FuncFormatter()``提供绘图刻度外观的极细粒度控制,并且在准备绘图用于演示或发布时非常方便。
## 格式化器和定位器的总结
我们已经提到了一些可用的格式化器和定位器。我们将简要列出所有内置定位器和格式化器的选项来结束本节。 对于其中任何内容的更多信息,请参阅文档字符串或 Matplotlib 在线文档。``plt``命名空间中提供以下东西:
| 定位器类 | 描述 |
| -------------------- | ------------ |
| ``NullLocator`` | 没有刻度 |
| ``FixedLocator`` | 刻度定位器是固定的 |
| ``IndexLocator`` | 索引绘图的定位器(也就是,其中`x = range(len(y))`) |
| ``LinearLocator`` | 等间隔的刻度,从最小值到最大值 |
| ``LogLocator`` | 对数刻度,从最小值到最大值 |
| ``MultipleLocator`` | 刻度和范围是基数的倍数 |
| ``MaxNLocator`` | 在不错的位置寻找小于等于最大值的刻度数 |
| ``AutoLocator`` | (默认)带有简单默认值的`MaxNLocator` |
| ``AutoMinorLocator`` | 用于次要刻度的定位器 |
| 格式化器类 | 描述 |
| --------------------- | -------------- |
| ``NullFormatter`` | 刻度上没有标签 |
| ``IndexFormatter`` | 从一列标签中设置字符串 |
| ``FixedFormatter`` | 手动为标签设置字符串 |
| ``FuncFormatter`` | 使用用户定义的函数设置标签 |
| ``FormatStrFormatter``| 对每个值使用格式化字符串 |
| ``ScalarFormatter`` | (默认)用于标量值的格式化器 |
| ``LogFormatter`` | 对数轴域的默认格式化器 |
我们将在本书的其余部分看到更多这些例子。
\ No newline at end of file
# 8.14 自定义 Matplotlib:配置和样式表
> 原文:[Customizing Matplotlib: Configurations and Stylesheets](https://nbviewer.jupyter.org/github/donnemartin/data-science-ipython-notebooks/blob/master/matplotlib/04.11-Settings-and-Stylesheets.ipynb)
>
> 译者:[飞龙](https://github.com/wizardforcel)
>
> 协议:[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/)
>
> 本节是[《Python 数据科学手册》](https://github.com/jakevdp/PythonDataScienceHandbook)(Python Data Science Handbook)的摘录。
Matplotlib 的默认绘图设置通常是其用户所抱怨的主题。虽然在 2016 年末的 Matplotlib 2.0 版本中有很多改进的内容,但自定义默认设置的能力,有助于使软件包符合你自己的审美偏好。
在这里,我们将介绍一些 Matplotlib 的运行时配置(`rc`)选项,并查看较新的样式表功能,其中包含一些不错的默认配置。
## 手动的绘图自定义
通过本章,我们已经看到了如何调整单个绘图设置,最终得到看起来比默认设置更好一些的东西。可以为每个单独的绘图执行这些自定义。例如,这是一个相当单调的默认直方图:
```py
import matplotlib.pyplot as plt
plt.style.use('classic')
import numpy as np
%matplotlib inline
x = np.random.randn(1000)
plt.hist(x);
```
![png](../img/8-14-1.png)
我们可以手动调整它,使其看上去好看一些:
```py
# 使用灰色背景
ax = plt.axes(axisbg='#E6E6E6')
ax.set_axisbelow(True)
# 绘制白色实网格线draw solid white grid lines
plt.grid(color='w', linestyle='solid')
# 隐藏轴的刻度
for spine in ax.spines.values():
spine.set_visible(False)
# 隐藏顶部和右侧刻度
ax.xaxis.tick_bottom()
ax.yaxis.tick_left()
# 将刻度和标签变亮
ax.tick_params(colors='gray', direction='out')
for tick in ax.get_xticklabels():
tick.set_color('gray')
for tick in ax.get_yticklabels():
tick.set_color('gray')
# 控制直方图的人脸和边界颜色
ax.hist(x, edgecolor='#E6E6E6', color='#EE6666');
```
![png](../img/8-14-2.png)
这看起来更好,你可能会认为外观受 R 语言的`ggplot`可视化包的启发。但这需要付出很多精力!我们绝对不希望每次创建绘图时都要做所有调整。幸运的是,有一种方法可以调整这些默认值,它将适用于所有绘图。
## 修改默认值:``rcParams``
每次加载 Matplotlib 时,它都会定义一个运行时配置(`rc`),其中包含你创建的每个绘图元素的默认样式。可以使用``plt.rc``便利例程随时调整此配置。让我们修改`rc`参数,使我们的默认绘图看起来与之前相似,并看看它的样子。
我们首先保存当前``rcParams``字典的副本,这样我们就可以在当前会话中轻松重置这些更改:
```py
IPython_default = plt.rcParams.copy()
```
现在我们可以使用``plt.rc``函数来改变其中的一些设置:
```py
from matplotlib import cycler
colors = cycler('color',
['#EE6666', '#3388BB', '#9988DD',
'#EECC55', '#88BB44', '#FFBBBB'])
plt.rc('axes', facecolor='#E6E6E6', edgecolor='none',
axisbelow=True, grid=True, prop_cycle=colors)
plt.rc('grid', color='w', linestyle='solid')
plt.rc('xtick', direction='out', color='gray')
plt.rc('ytick', direction='out', color='gray')
plt.rc('patch', edgecolor='#E6E6E6')
plt.rc('lines', linewidth=2)
```
通过定义这些设置,我们现在可以创建一个绘图并查看我们的设置:
```py
plt.hist(x);
```
![png](../img/8-14-3.png)
让我们看看使用`rc`参数,简单线条图是什么样:
```py
for i in range(4):
plt.plot(np.random.rand(10))
```
![png](../img/8-14-4.png)
我发现这比默认样式更美观。如果你不同意我的审美,好消息是你可以根据自己的喜好调整`rc`参数!这些设置可以保存在`.matplotlibrc`文件中,你可以阅读[ Matplotlib 文档](http://Matplotlib.org/users/customizing.html)来了解。也就是说,我更喜欢使用样式表来定制 Matplotlib。
## 样式表
2014 年 8 月发布的 Matplotlib 1.4 版增加了一个非常方便的``style``模块,其中包含许多新的默认样式表,以及创建和打包自己的样式的功能。这些样式表的格式与前面提到的`.matplotlibrc`文件类似,但必须使用`.mplstyle`扩展名来命名。
即使你不创建自己的样式,默认包含的样式表也非常有用。可用的样式在``plt.style.available``中列出 - 这里为了简洁我只列出前五个:
```py
plt.style.available[:5]
'''
['fivethirtyeight',
'seaborn-pastel',
'seaborn-whitegrid',
'ggplot',
'grayscale']
'''
```
切换样式表的基本方法是调用:
```py
plt.style.use('stylename')
```
但请记住,这将改变会话剩余部分的风格!或者,你可以使用样式上下文管理器,它可以临时设置样式:
```py
with plt.style.context('stylename'):
make_a_plot()
```
让我们创建一个函数,它将生成两种基本类型的绘图:
```py
def hist_and_lines():
np.random.seed(0)
fig, ax = plt.subplots(1, 2, figsize=(11, 4))
ax[0].hist(np.random.randn(1000))
for i in range(3):
ax[1].plot(np.random.rand(10))
ax[1].legend(['a', 'b', 'c'], loc='lower left')
```
我们将使用它,以及各种内置样式的样式来探索这些绘图。
### 默认样式
默认样式是我们到目前为止所看到的内容;我们将从它开始。首先,让我们将运行时配置重置为笔记本默认值:
```py
# 重置 rcParams
plt.rcParams.update(IPython_default);
```
现在让我们看看它的外观:
```py
hist_and_lines()
```
![png](../img/8-14-5.png)
### FiveThiryEight 样式
``fivethirtyeight``风格模仿流行的[ FiveThirtyEight 网站](https://fivethirtyeight.com)上的图形。正如你在这里看到的那样,它以深色,粗线条和透明的轴为代表:
```py
with plt.style.context('fivethirtyeight'):
hist_and_lines()
```
![png](../img/8-14-6.png)
### `ggplot`
R 语言中的``ggplot``包是非常流行的可视化工具。Matplotlib 的``ggplot``样式模仿该包的默认样式:
```py
with plt.style.context('ggplot'):
hist_and_lines()
```
![png](../img/8-14-7.png)
### “黑客的贝叶斯方法”风格
有一本非常好的在线简短书籍,叫做[黑客的概率编程和贝叶斯方法](http://camdavidsonpilon.github.io/Probabilistic-Programming-and-Bayesian-Methods-for-Hackers/);它具有使用 Matplotlib 创建的图形,并使用一组很好的`rc`参数,在整本书中创建一致且视觉上吸引人的风格。这种风格在``bmh``样式表中复现:
```py
with plt.style.context('bmh'):
hist_and_lines()
```
![png](../img/8-14-8.png)
### 暗黑背景
对于演示文稿中使用的图形,使用深色而非浅色背景通常很有用。``dark_background``样式提供了这个:
```py
with plt.style.context('dark_background'):
hist_and_lines()
```
![png](../img/8-14-9.png)
### 灰度
有时你可能会发现,自己正在为不接受彩色图形的印刷出版物准备图形。为此,此处展示的“灰度”样式非常有用:
```py
with plt.style.context('grayscale'):
hist_and_lines()
```
![png](../img/8-14-10.png)
### Seaborn 样式
Matplotlib 还有受 Seaborn 库启发的样式表(在“可视化和 Seaborn”中进行了更全面的讨论)。正如我们将看到的,将 Seaborn 导入笔记本时,这些样式会自动加载。我发现这些设置非常好,并且倾向于在我自己的数据探索中将它们用作默认设置。
```py
import seaborn
hist_and_lines()
```
![png](../img/8-14-11.png)
使用所有这些用于各种绘图样式的内置选项,对于交互式可视化和用于出版图形的创建,Matplotlib 变得更加有用。在本书中,我通常会在创建绘图时使用这些样式约定中的一个或多个。
\ No newline at end of file
# 8.15 Matplotlib 中的三维绘图
> 原文:[Three-Dimensional Plotting in Matplotlib](https://nbviewer.jupyter.org/github/donnemartin/data-science-ipython-notebooks/blob/master/matplotlib/04.12-Three-Dimensional-Plotting.ipynb)
>
> 译者:[飞龙](https://github.com/wizardforcel)
>
> 协议:[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/)
>
> 本节是[《Python 数据科学手册》](https://github.com/jakevdp/PythonDataScienceHandbook)(Python Data Science Handbook)的摘录。
Matplotlib 最初设计时只考虑了二维绘图。在 1.0 版本发布时,一些三维绘图工具构建在 Matplotlib 的二维显示之上,结果是一组方便(但是有限)的三维数据可视化工具。通过导入``mplot3d``工具包来启用三维绘图,它包含在主要的 Matplotlib 安装中:
```py
from mpl_toolkits import mplot3d
```
导入子模块后,可以通过将关键字``projection ='3d'``传递给任何普通轴域创建例程来创建三维轴域:
```py
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure()
ax = plt.axes(projection='3d')
```
![png](../img/8-15-1.png)
启用此三维轴后,我们现在可以绘制各种三维绘图。三维绘图通过交互式查看图形,而非静态地在笔记本中查看图形而获益;回想一下,要使用交互式图形,运行此代码时可以使用``%matplotlib notebook``而不是``%matplotlib inline``
## 三维的点和线
最基本的三维图是根据`(x, y, z)`三元组创建的散点图的线或集合。与前面讨论的更常见的二维图类比,这些可以使用``ax.plot3D````ax.scatter3D``函数创建。
这些调用签名几乎与它们的二维对应的签名相同,所以对于控制输出的更多信息,你可以参考“简单的折线图”和“简单的散点图”。在这里,我们将绘制一个三角螺旋线,并且在线条附近随机绘制一些点:
```py
ax = plt.axes(projection='3d')
# 三维线条的数据
zline = np.linspace(0, 15, 1000)
xline = np.sin(zline)
yline = np.cos(zline)
ax.plot3D(xline, yline, zline, 'gray')
# 三维散点的数据
zdata = 15 * np.random.random(100)
xdata = np.sin(zdata) + 0.1 * np.random.randn(100)
ydata = np.cos(zdata) + 0.1 * np.random.randn(100)
ax.scatter3D(xdata, ydata, zdata, c=zdata, cmap='Greens');
```
![png](../img/8-15-2.png)
请注意,默认情况下,散点会调整其透明度,以便在页面上给出深度感。虽然在静态图像中有时难以看到三维效果,但是交互式视图可以产生点的布局的一些很好的直觉。
## 三维等高线图
类似于我们在“密度和等高线图”中探索的等高线图,``mplot3d``包含使用相同输入创建三维浮雕图的工具。像二维`ax.contour`图一样,`ax.contour3D`要求所有输入数据都是二维规则网格的形式,带有每个点求得的`Z`数据。这里我们将展示三维正弦函数的三维等高线图:
```py
def f(x, y):
return np.sin(np.sqrt(x ** 2 + y ** 2))
x = np.linspace(-6, 6, 30)
y = np.linspace(-6, 6, 30)
X, Y = np.meshgrid(x, y)
Z = f(X, Y)
fig = plt.figure()
ax = plt.axes(projection='3d')
ax.contour3D(X, Y, Z, 50, cmap='binary')
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z');
```
![png](../img/8-15-3.png)
有时默认的视角不是最佳的,在这种情况下我们可以使用``view_init``方法来设置俯仰角和方位角。 在下面的示例中,我们将使用 60 度的俯仰角(即,在 x-y 平面上方 60 度)和 35 度的方位角(即绕 z 轴逆时针旋转 35 度):
```py
ax.view_init(60, 35)
fig
```
![png](../img/8-15-4.png)
再次注意,当使用 Matplotlib 的交互式后端之一时,通过单击和拖动可以交互式地完成这种类型的旋转。
## 线框和曲面图
处理网格化数据的另外两种类型的三维图是线框和曲面图。它们接受值的网格,并将其投影到指定的三维表面上,并且可以使得到的三维形式非常容易可视化。以下是使用线框图的示例:
```py
fig = plt.figure()
ax = plt.axes(projection='3d')
ax.plot_wireframe(X, Y, Z, color='black')
ax.set_title('wireframe');
```
![png](../img/8-15-5.png)
曲面图类似于线框图,但线框的每个面都是填充多边形。将颜色表添加到填充多边形,有助于感知可视化的表面拓扑:
```py
ax = plt.axes(projection='3d')
ax.plot_surface(X, Y, Z, rstride=1, cstride=1,
cmap='viridis', edgecolor='none')
ax.set_title('surface');
```
![png](../img/8-15-6.png)
请注意,虽然曲面图的值的网格需要是二维的,但它不必是直线的。下面是一个创建部分极坐标网格的示例,与`surface3D`图形一起使用时,可以为我们提供我们正在可视化的函数的切面:
```py
r = np.linspace(0, 6, 20)
theta = np.linspace(-0.9 * np.pi, 0.8 * np.pi, 40)
r, theta = np.meshgrid(r, theta)
X = r * np.sin(theta)
Y = r * np.cos(theta)
Z = f(X, Y)
ax = plt.axes(projection='3d')
ax.plot_surface(X, Y, Z, rstride=1, cstride=1,
cmap='viridis', edgecolor='none');
```
![png](../img/8-15-7.png)
## 表面的三角剖分
对于某些应用,上述例程所需的均匀采样网格过于严格且不方便。在这些情况下,基于三角剖分的图形可能非常有用。如果我们不从笛卡尔坐标或极坐标网格中均匀抽取,而是随机抽取一组的话,会如何呢?
```py
theta = 2 * np.pi * np.random.random(1000)
r = 6 * np.random.random(1000)
x = np.ravel(r * np.sin(theta))
y = np.ravel(r * np.cos(theta))
z = f(x, y)
```
我们可以创建点的散点图,来了解我们从中采样的表面:
```py
ax = plt.axes(projection='3d')
ax.scatter(x, y, z, c=z, cmap='viridis', linewidth=0.5);
```
![png](../img/8-15-8.png)
这留下了许多不足之处。在这种情况下帮助我们的函数是``ax.plot_trisurf``,它通过首先找到在相邻点之间形成的一组三角形来创建表面(请记住,这里`x``y``z`是一维数组):
```py
ax = plt.axes(projection='3d')
ax.plot_trisurf(x, y, z,
cmap='viridis', edgecolor='none');
```
![png](../img/8-15-9.png)
结果当然不像用网格绘制时那样干净,但这种三角剖分的灵活性,允许一些非常有趣的三维图。例如,实际上可以使用它绘制三维莫比乌斯条带,我们将在下面看到。
### 示例:可视化莫比乌斯带
莫比乌斯条带类似于旋转 90 度而拼接的纸条。在拓扑上,它非常有趣,因为外观只有一面!在这里,我们将使用 Matplotlib 的三维工具来可视化这样的对象。
创建莫比乌斯带的关键是考虑它的参数化:它是一个二维条带,所以我们需要两个内在维度。 让我们称它们为`θ`,其范围从`0``2π`,并且`w`的范围从`-1``1`,跨越条带的宽度:
```py
theta = np.linspace(0, 2 * np.pi, 30)
w = np.linspace(-0.25, 0.25, 8)
w, theta = np.meshgrid(w, theta)
```
现在根据这个参数化,我们必须确定嵌入条带的`(x, y, z)`位置。
考虑到这一点,我们可能会发现有两个发生的旋转:一个是环绕其中心的位置(我们称之为`θ`),而另一个是条带绕其轴的扭曲(我会称其为`φ`)。 对于莫比乌斯条带,我们必须让条带在完整循环期间产生半个扭曲,或者`Δφ = Δθ/2`
```py
phi = 0.5 * theta
```
现在我们使用三角函数的记忆来推导三维嵌入。我们将定义`r`,每个点距离中心的距离,并使用它来查找嵌入的`(x, y, z)`坐标:
```py
# x-y 平面中的半径
r = 1 + w * np.cos(phi)
x = np.ravel(r * np.cos(theta))
y = np.ravel(r * np.sin(theta))
z = np.ravel(w * np.sin(phi))
```
最后,为了绘制对象,我们必须确保三角剖分是正确的。 执行此操作的最佳方法是,在底层参数化中定义三角剖分,然后让 Matplotlib 将此三角剖分投影到莫比乌斯条带的三维空间中。这可以通过以下方式完成:
```py
# 在底层参数化中进行三角剖分
from matplotlib.tri import Triangulation
tri = Triangulation(np.ravel(w), np.ravel(theta))
ax = plt.axes(projection='3d')
ax.plot_trisurf(x, y, z, triangles=tri.triangles,
cmap='viridis', linewidths=0.2);
ax.set_xlim(-1, 1); ax.set_ylim(-1, 1); ax.set_zlim(-1, 1);
```
![png](../img/8-15-10.png)
结合所有这些技巧,可以在 Matplotlib 中创建和展示各种各样的三维对象和图案。
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册