本文展示Python中最基本的可视化工具Matplotlib和Seaborn的用法
1.导入库和常规设置
import seaborn as sns
import numpy as np
import pandas as pd
import matplotlib as mpl
import matplotlib.pyplot as plt
%matplotlib inline
from datetime import datetime
import os
os.chdir(r'file_path')
plt.rcParams['font.sans-serif'] = ['SimHei'] # 中文字体设置-黑体
plt.rcParams['axes.unicode_minus'] = False # 解决保存图像是负号'-'显示为方块的问题
sns.set(font='SimHei') # 解决Seaborn中文显示问题
# 设置一下常用颜色
r_hex = '#dc2624' # red, RGB = 220,38,36
dt_hex = '#2b4750' # dark teal, RGB = 43,71,80
tl_hex = '#45a0a2' # teal, RGB = 69,160,162
r1_hex = '#e87a59' # red, RGB = 232,122,89
tl1_hex = '#7dcaa9' # teal, RGB = 125,202,169
g_hex = '#649E7D' # green, RGB = 100,158,125
o_hex = '#dc8018' # orange, RGB = 220,128,24
tn_hex = '#C89F91' # tan, RGB = 200,159,145
g50_hex = '#6c6d6c' # grey-50, RGB = 108,109,108
bg_hex = '#4f6268' # blue grey, RGB = 79,98,104
g25_hex = '#c7cccf' # grey-25, RGB = 199,204,207
2.基础元素概览
2.1四大容器
四大容器:
Figure → Axes → Axis → Ticks
图 → 坐标系 → 坐标轴 → 刻度
在坐标轴和刻度上添加标签
在坐标系中添加线、点、网格、图例和文字
在图中添加图例
要画出一幅有内容的图,需要在容器里添加基础元素比如线 (line), 点(marker), 文字 (text), 图例 (legend), 网格 (grid), 标题 (title), 图片 (image) 等,具体来说
画一条线,用 plt.plot() 或 ax.plot()
画个记号,用 plt.scatter() 或 ax.scatter()
添加文字,用 plt.text() 或 ax.text()
添加图例,用 plt.legend() 或 ax.legend()
添加图片,用 plt.imshow() 或 ax.imshow()
from PIL import Image
plt.figure()
plt.xticks([]), plt.yticks([])
im = np.array(Image.open('you_picture_path'))
plt.imshow(im)
plt.show()
2.2 两种推荐画法
两种生成坐标系的推荐代码
第一种:同时生成图和坐标系
fig, ax = plt.subplots()
第二种:先生成图,再添加坐标系
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
3.Matplotlib画图实操
3.1导入数据
data1 = pd.read_csv( 'S&P500.csv',
index_col=0,
parse_dates=True,
dayfirst=True )
data1.head(3).append(data1.tail(3))
Open | High | Low | Close | Adj Close | Volume | |
---|---|---|---|---|---|---|
Date | ||||||
1950-01-03 | 16.660000 | 16.660000 | 16.660000 | 16.660000 | 16.660000 | 1260000 |
1950-01-04 | 16.850000 | 16.850000 | 16.850000 | 16.850000 | 16.850000 | 1890000 |
1950-01-05 | 16.930000 | 16.930000 | 16.930000 | 16.930000 | 16.930000 | 2550000 |
2019-04-22 | 2898.780029 | 2909.510010 | 2896.350098 | 2907.969971 | 2907.969971 | 2997950000 |
2019-04-23 | 2909.989990 | 2936.310059 | 2908.530029 | 2933.679932 | 2933.679932 | 3635030000 |
2019-04-24 | 2934.000000 | 2936.830078 | 2926.050049 | 2927.250000 | 2927.250000 | 3448960000 |
spx = data1[['Adj Close']].loc['2007-01-01':'2010-01-01']
spx.head(3).append(spx.tail(3))
Adj Close | |
---|---|
Date | |
2007-01-03 | 1416.599976 |
2007-01-04 | 1418.339966 |
2007-01-05 | 1409.709961 |
2009-12-29 | 1126.199951 |
2009-12-30 | 1126.420044 |
2009-12-31 | 1115.099976 |
data2 = pd.read_csv( 'VIX.csv', index_col=0,
parse_dates=True,
dayfirst=True )
vix = data2[['Adj Close']].loc['2007-01-01':'2010-01-01']
vix.head(3).append(vix.tail(3))
Adj Close | |
---|---|
Date | |
2007-01-03 | 12.040000 |
2007-01-04 | 11.510000 |
2007-01-05 | 12.140000 |
2009-12-29 | 20.010000 |
2009-12-30 | 19.959999 |
2009-12-31 | 21.680000 |
用 plt.rcParams 可查看上图的所有默认属性 (非常多的属性值)
3.2 先画图再添加坐标系的方式
# 图的大小和像素 (w*dpi, h*dpi)即(1600, 600)
fig = plt.figure( figsize=(16,6), dpi=100 )
# 在图中 (fig) 添加了一个坐标系 (ax),然后所有操作都在 ax 里面完成
ax1 = fig.add_subplot(1, 1, 1)
x = spx.index
y1 = spx.values
y2 = vix.values
# 画图,并设置颜色,宽度 (2 像素) 和风格 (连续线)
ax1.plot(y1, color=dt_hex, linewidth=2, linestyle='-', label='S&P500')
# 设置横轴和纵轴的边界
ax1.set_xlim(-1, len(x)+1)
ax1.set_ylim(np.vstack([y1, y2]).min()*0.8, np.vstack([y1, y2]).max()*1.2)
# 先用 ax.set_ticks() 设置出数值刻度
# 再用 ax.set_xticklabels() 在对应的数值刻度上写标签
x_tick= range(0, len(x), 40)
x_label = [x[i].strftime('%Y-%m-%d') for i in x_tick]
ax1.set_xticks(x_tick)
ax1.set_xticklabels(x_label, rotation=90)
# 添加图例 (legend), loc = 0 表示 matplotlib 自动安排一个最好位置显示图例,而 frameon = True 给图例加了外框
ax1.legend(loc='upper left', frameon=True)
# Add a second axes
ax2 = ax1.twinx()
ax2.plot(y2, color=r_hex, linewidth=2, linestyle='-', label='VIX')
ax2.legend(loc='upper right', frameon=True)
plt.show()
添加第二幅图也很简单,用两次 plt.plot() 或者 ax.plot() 即可。
这里面用的是 plt 没用 ax,没有特殊原因,在本例中两者可以随意使用,
但两者在使用「.methods」时有个小细节:
plt.xlim
plt.ylim
plt.xticks
ax.set_xlim
ax.set_ylim
ax_set_xticks
3.3 同时生成图和坐标系的画法
# plt版本
plt.figure( figsize=(16,6), dpi=100 )
# subplot 1
plt.subplot(2, 1, 1)
x = spx.index
y1 = spx.values
plt.plot(y1, color=dt_hex, linewidth=2, linestyle='-', label='S&P500')
plt.xlim(-1, len(x)+1)
plt.ylim(y1.min()*0.8, y1.max()*1.2)
x_tick= range(0, len(x), 40)
x_label = [x[i].strftime('%Y-%m-%d') for i in x_tick]
plt.xticks(x_tick, x_label, rotation=90)
plt.legend(loc='upper left', frameon=True)
# subplot 2
plt.subplot(2, 1, 2)
y2 = vix.values
plt.plot(y2, color=r_hex, linewidth=2, linestyle='-', label='VIX')
plt.xlim(-1, len(x)+1)
plt.ylim(y2.min()*0.8, y2.max()*1.2)
plt.xticks(x_tick, x_label, rotation=45)
plt.legend(loc='upper left', frameon=True)
plt.show()
3.4 设置标注
# 设置标注版本
fig = plt.figure( figsize=(16,6), dpi=100 )
# 定义危机事件,以元组的列表存储
crisis_data = [(datetime(2007, 10, 11), 'Peak of bull market'),
(datetime(2008, 3, 12), 'Bear Stearns Fails'),
(datetime(2008, 9, 15), 'Lehman Bankruptcy'),
(datetime(2009, 1, 20), 'RBS Sell-off'),
(datetime(2009, 4, 2), 'G20 Summit')]
ax1 = fig.add_subplot(1, 1, 1)
x = spx.index
y1 = spx.values
y2 = vix.values
ax1.plot(y1, color=dt_hex, linewidth=2, linestyle='-', label='S&P500')
ax1.set_xlim(-1, len(x)+1)
ax1.set_ylim(np.vstack([y1, y2]).min()*0.8, np.vstack([y1, y2]).max()*1.2)
x_tick= range(0, len(x), 40)
x_label = [x[i].strftime('%Y-%m-%d') for i in x_tick]
ax1.set_xticks(x_tick)
ax1.set_xticklabels(x_label, rotation=90)
ax1.legend(loc='upper left', frameon=True)
init_tick = list(range(0, len(x), 40))
# 定义容器,常规日期标签 init_tick,五个事件日期标签 impt_tick
impt_tick = []
impt_date = []
# 用 for 循环读取 crisis_data 里面每个日期 date 和事件 label
for date, label in crisis_data:
date = date.strftime('%Y-%m-%d')
impt_date.append(date)
# 获取每一个 date 在整个日期数组中的索引 xi,以及对应的 spx 值 yi
xi = x.get_loc(date)
impt_tick.append(xi)
yi = spx.asof(date)
# 用 scatter() 函数画出一个圆点,标注事件在 spx 折现上的位置
ax1.scatter(xi, yi, 80, color=r_hex)
# 事件标注,在 annotate() 函数里设置了事件,箭头坐标,事件打印的坐标,箭头性质,以及对齐属性
ax1.annotate(label, xy=(xi, yi + 60),
xytext=(xi, yi + 300),
arrowprops=dict(facecolor='black', headwidth=4, width=1, headlength=6),
horizontalalignment='left', verticalalignment='top'
)
x_tick = init_tick + impt_tick
x_label = [x[i].strftime('%Y-%m-%d') for i in x_tick]
ax1.set_xticks(x_tick)
ax1.set_xticklabels(x_label, rotation=90)
for i, label in enumerate(ax1.get_xticklabels()):
if i >= len(init_tick):
label.set_color(r_hex)
label.set_fontweight('bold')
else:
label.set_fontsize(9)
# Add a second axes
ax2 = ax1.twinx()
ax2.plot(y2, color=r_hex, linewidth=2, linestyle='-', label='VIX', alpha=0.3) # 透明度
ax2.legend(loc='upper right', frameon=True)
plt.show()
4.图表的选择与画法
在做图表设计时候经常面临着怎么选用合适的图表,图表展示的关系分为四大类:
分布 (distribution)
联系 (relationship)
比较 (comparison)
构成 (composition)
直方图 (histogram chart),又称质量分布图,是一种统计报告图,由一系列高度不等的纵向条纹或线段表示数据分布的情况。 一般用横轴表示数据类型,纵轴表示分布情况。在 Matplotlib 里的语法是
plt.hist()
ax.hist()
散点图 (scatter chart) 用两组数据构成多个坐标点,考察坐标点的分布,判断两变量之间是否存在某种联系的分布模式。在 Matplotlib 里的语法是
plt.scatter()
ax.scatter()
折线图 (line chart) 显示随时间而变化的连续数据,因此非常适用于显示在相等时间间隔下数据的趋势。在 Matplotlib 里的语法是:
plt.plot()
ax.plot()
饼状图 (pie chart) 是一个划分为几个扇形的圆形统计图表,用于描述量、频率或百分比之间的相对关系。 在饼状图中,每个扇区面积大小为其所表示的数量的比例。在 Matplotlib 里的语法是:
plt.pie()
ax.pie()
# 查看所有的色彩风格
print(plt.style.available)
['bmh', 'classic', 'dark_background', 'fast', 'fivethirtyeight', 'ggplot', 'grayscale', 'seaborn-bright', 'seaborn-colorblind', 'seaborn-dark-palette', 'seaborn-dark', 'seaborn-darkgrid', 'seaborn-deep', 'seaborn-muted', 'seaborn-notebook', 'seaborn-paper', 'seaborn-pastel', 'seaborn-poster', 'seaborn-talk', 'seaborn-ticks', 'seaborn-white', 'seaborn-whitegrid', 'seaborn', 'Solarize_Light2', 'tableau-colorblind10', '_classic_test']
seaborn-colorblind 和 tableau-colorblind10 是专门为色盲考虑的色彩风格
可以设置:
plt.style.use('ggplot')
plt.style.use('seaborn-colorblind')
plt.style.use('tableau-colorblind10')
5.Seaborn画图
iris_data = pd.read_csv('iris.csv')
iris_data.head(3).append(iris_data.tail(3))
sepal_length | sepal_width | petal_length | petal_width | species | |
---|---|---|---|---|---|
0 | 5.1 | 3.5 | 1.4 | 0.2 | setosa |
1 | 4.9 | 3.0 | 1.4 | 0.2 | setosa |
2 | 4.7 | 3.2 | 1.3 | 0.2 | setosa |
147 | 6.5 | 3.0 | 5.2 | 2.0 | virginica |
148 | 6.2 | 3.4 | 5.4 | 2.3 | virginica |
149 | 5.9 | 3.0 | 5.1 | 1.8 | virginica |
用 set_style() 选五种风格:darkgrid, whitegrid, dark, white 和 ticks .
用 set_palette() 六种调色盘:deep, muted, pastel, bright, dark 和 colorblind
sns.set_style('darkgrid') # 将风格设置为 darkgrid (背景变成带网格的灰色)
sns.set_palette('colorblind') # 色板设置成 colorblind 为色盲用户着想
sns.pairplot( iris_data, hue='species',markers=['o','s','D'] ) # 将不同类用圆形 (o)、正方形 (s) 和方块 (D) 来标记
<seaborn.axisgrid.PairGrid at 0x1ff91688198>
# 选择子集图
sns.set_style('whitegrid')
# sns.pairplot( iris_data,
# vars=['sepal_width',
# 'sepal_length'] )
sns.pairplot( iris_data,
x_vars=['sepal_width',
'sepal_length'],
y_vars=['petal_width',
'petal_length'])
<seaborn.axisgrid.PairGrid at 0x1ff93aca198>
# 线性回归图,设置参数 kind = 'reg'
sns.set_style('ticks')
sns.set_palette('dark')
sns.pairplot( iris_data, kind='reg' )
<seaborn.axisgrid.PairGrid at 0x1ff940eb748>
# 核密度图(对角线),设置里面参数 diag_kind = 'kde'
sns.set_palette('bright')
sns.pairplot( iris_data, diag_kind='kde' )
<seaborn.axisgrid.PairGrid at 0x1ff946b9f98>
条形图 (barplot)
计数图 (countplot)
点图 (pointplot)
箱形图 (boxplot)
小提琴图 (violinplot)
titanic = sns.load_dataset(".seaborn-data-master/titanic")
titanic.head(3).append(titanic.tail(3))
survived | pclass | sex | age | sibsp | parch | fare | embarked | class | who | adult_male | deck | embark_town | alive | alone | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 3 | male | 22.0 | 1 | 0 | 7.2500 | S | Third | man | True | NaN | Southampton | no | False |
1 | 1 | 1 | female | 38.0 | 1 | 0 | 71.2833 | C | First | woman | False | C | Cherbourg | yes | False |
2 | 1 | 3 | female | 26.0 | 0 | 0 | 7.9250 | S | Third | woman | False | NaN | Southampton | yes | True |
508 | 0 | 3 | male | 28.0 | 0 | 0 | 22.5250 | S | Third | man | True | NaN | Southampton | no | True |
509 | 1 | 3 | male | 26.0 | 0 | 0 | 56.4958 | S | Third | man | True | NaN | Southampton | yes | True |
510 | 1 | 3 | male | 29.0 | 0 | 0 | 7.7500 | Q | Third | man | True | NaN | Q | NaN | NaN |
# 自定义的调色板
color = ['#dc2624', '#2b4750', '#45a0a2', '#e87a59',
'#7dcaa9', '#649E7D', '#dc8018', '#C89F91',
'#6c6d6c', '#4f6268', '#c7cccf']
sns.set_palette( color )
sns.palplot( sns.color_palette(color,11) )
plt.figure(figsize=(16,9))
# 条形图
plt.subplot(231)
sns.barplot( x='sex', y='survived', hue='class',hue_order= ['First', 'Second', 'Third'], data=titanic )
plt.title("条形图 barplot")
# 计数图
plt.subplot(232)
sns.countplot( x='deck',data=titanic )
plt.title("计数图 countplot")
# 点图
plt.subplot(233)
sns.pointplot( x='class',
y='survived',
hue='sex',
data=titanic,
markers=['^','o'],
linestyles=['-','--'] )
plt.title("点图 pointplot")
# 箱型图
plt.subplot(234)
sns.boxplot( x='alive',
y='age',
hue='adult_male',
data=titanic )
plt.title("箱型图 boxplot")
# 小提琴图
plt.subplot(235)
sns.violinplot( x='sex',
y='age',
hue='survived',
data=titanic )
plt.title("小提琴图 violinplot")
# plt.tight_layout() # 调整每隔子图之间的距离
plt.subplots_adjust(wspace =0.3, hspace =0.3)
plt.show()
# 箱形水平图
sns.boxplot( data=iris_data, orient='h' )
<matplotlib.axes._subplots.AxesSubplot at 0x1ff99852cf8>
# 双变量分布
sns.jointplot( 'sepal_length', 'sepal_width',
data=iris_data,
kind='kde')
<seaborn.axisgrid.JointGrid at 0x1ff9a02ee80>
sns.jointplot( 'sepal_length', 'sepal_width',
data=iris_data,
kind='reg')
<seaborn.axisgrid.JointGrid at 0x1ff97b3ee48>
6. 总结
了解 Matplotlib 的绘图逻辑,以及里面包含的画图元素以及它们之间的层级。
深度学 Matplotlib,只研究折线图,通过研究它的属性,一步步改进图的尺寸、像素、线条颜色宽度风格、坐标轴边界、刻度标签、图例、多图、多坐标系、标注、透明度等等,画出了一幅美图。
广度学 Matplotlib,通过数据的分布、联系、比较和构成研究了直方图、散点图、折线图和饼状图,最后还为用户着想 (习惯、色盲等等) 画出更能有效表达信息的图。
-
Seaborn 就是 Matplotlib 的升级版,底层绘图逻辑和元素层级相同
-
Seaborn 比 Matplotlib 强大的三个地方就是:
- 代码简单,基本都是一句话 (one-liner) 就可以画出变量之间统计关系图
- 能够处理分类 (categorical) 变量 (不仅仅只能处理连续变量)
- 颜色更加丰富好看 (不过这个看个人喜好)
-
本文综合整理自 传送门