Python数据科学分享——3.数据可视化(1)
数据可视化是描述性统计不可或缺的亮点,在matplotlib、JavaScript(D3.js)、OpenGL的基础上百花齐放,百家争鸣
</img>
目标与原则
以最小的复杂度展示足够多的信息
- 目标:如果不能为用户提供有用的信息,那么就没啥用;如果信息展现形式太复杂,那么就会被噪声干扰
- 原则:
- 对比(Contrast):让页面引人注目,避免页面上的元素太过相似。如果元素(字体、颜色、大小、线宽、形状、空间等)不相同,那就干脆让它们截然不同。
- 重复(Repetition):让设计中的视觉要素在整个作品中重复出现。既能增加条理性,还可以加强统一性。
- 对齐(Alignment):任何东西都不能在页面上随意安放。每个元素都应当与页面上的另一个元素有某种视觉联系,建立清晰、精巧而且清爽的外观。
- 亲密性(Proximity):彼此相关的项应当靠近,归组在一起。如果多个项相互之间存在很近的亲密性,它们就会成为一个视觉单元,而不是多个孤立的元素。这有助于组织信息,减少混乱,为读者提供清晰的结构。
美国教育家、设计师Robin Williams《The Non-Designer's Design Book(写给大家看的设计书)》
随着HTML5、SVG/Canvas普及,尤其是Mike Bostock于2010开源D3.js,python数据可视化受到冲击,Facebook于2013发布react.js后,Python数据可视化开始向web组件化发展,重点方向是机器学习与Web交互
首发年份 | 名称 | 简介 |
---|---|---|
2003 | matplotlib | 基础绘图工具 |
2010 | networkx | 复杂网络与图算法工具 |
2012 | seaborn | 快速统计图 |
2012 | bokeh | 交互式 |
2012 | plotly | 交互式 |
2015 | altair | 声明式语义 |
2015 | dash | 基于plotly的web app |
2015 | tensorboard | tensorflow and keras |
2017 | ipyvolume | 3D交互 |
2018 | Vaex | 高性能渲染 |
2018 | streamlit | 机器学习web app |
2018 | volia | notebook web app |
pyviz网站整理了Python数据可视化工具
matplotlib基础图库
Matplotlib的设计哲学是让Python程序员完全控制可视化应用。Matplotlib中文字体显示问题,请参考中文设置方法
- 模仿MatLab,上手简单,理工科同学上手成本低
- 许多渲染接口
- 功能齐全、文档完整
- 测试容易、源代码质量高
</img>
%load_ext autoreload
%autoreload 2
%matplotlib inline
from matplotlib.font_manager import _rebuild
_rebuild()
import matplotlib.pyplot as plt
import seaborn as sns
sns.set_style("whitegrid", {"font.sans-serif": ["SimHei", "Arial"]})
import pandas_alive
import pandas as pd
import numpy as np
df_covid = pd.read_json("3.data-viz/timeseries.json")
df_covid.index = pd.DatetimeIndex(df_covid.iloc[:, 0].apply(lambda _: _["date"]))
df_covid.index.name = "日期"
df_covid = df_covid.applymap(lambda _: int(_["confirmed"]))
df_covid.replace(0, np.nan, inplace=True)
top20 = df_covid.iloc[-1].sort_values().tail(20).index
df_covid = df_covid[top20]
%matplotlib notebook
x = np.linspace(0, 10, 100)
fig = plt.figure()
plt.plot(x, np.sin(x))
plt.plot(x, np.cos(x))
plt.title('测试')
# plt.show()`会启动一个事件循环(event loop)
plt.show()
%matplotlib inline
x = np.linspace(0, 10, 100)
fig = plt.figure()
plt.plot(x, np.sin(x))
plt.plot(x, np.cos(x))
plt.title('测试')
# plt.show()`会启动一个事件循环(event loop)
plt.show()
# 保存图形
fig.savefig('sin_cos.png')
ls -lh sin_cos.png
# 用IPython的`Image`对象显示图形
from IPython.display import Image
Image('sin_cos.png')
用markdown语法显示图形
plt.figure() # 创建图形
# 创建两个子图中的第一个,设置坐标轴
plt.subplot(2, 1, 1) # (行、列、子图编号)
plt.plot(x, np.sin(x))
# 创建两个子图中的第二个,设置坐标轴
plt.subplot(2, 1, 2)
plt.plot(x, np.cos(x));
# 先创建图形网格
# ax是一个包含两个Axes对象的数组
fig, ax = plt.subplots(2)
# 在每个对象上调用`plot()`方法
ax[0].plot(x, np.sin(x))
ax[1].plot(x, np.cos(x));
plt.figure(figsize=(10, 5))
plt.plot(x, np.sin(x - 0), color='blue') # 标准颜色名称
plt.plot(x, np.sin(x - 1), color='g') # 缩写颜色代码(rgbcmyk)
plt.plot(x, np.sin(x - 2), color='0.75') # 范围在0~1之间的灰度值
plt.plot(x, np.sin(x - 3), color='#FFDD44') # 十六进制(RRGGBB,00~FF)
plt.show()
plt.figure(figsize=(10, 5))
plt.plot(x, np.sin(x - 0), linestyle='-') # 实线
plt.plot(x, np.sin(x - 1), linestyle='--') # 虚线
plt.plot(x, np.sin(x - 2), linestyle='-.') # 点划线
plt.plot(x, np.sin(x - 3), linestyle=':'); # 实点线
plt.show()
可以将linestyle
和color
编码组合起来,作为plt.plot()
函数的一个参数使用:
plt.figure(figsize=(10, 5))
plt.plot(x, np.sin(x - 0), '-g') # 绿色实线
plt.plot(x, np.sin(x - 1), '--c') # 青色虚线
plt.plot(x, np.sin(x - 2), '-.k') # 黑色点划线
plt.plot(x, np.sin(x - 3), ':r'); # 红色实点线
plt.figure(figsize=(10, 5))
plt.plot(x, np.sin(x), '-g', label='sin(x)')
plt.plot(x, np.cos(x), ':b', label='cos(x)')
plt.title("正弦余弦曲线")
plt.xlabel("x值")
plt.ylabel("三角函数值");
plt.legend();
rng = np.random.RandomState(0)
x = rng.randn(100)
y = rng.randn(100)
colors = rng.rand(100)
sizes = 1000 * rng.rand(100)
plt.figure(figsize=(10, 10))
plt.scatter(x, y, c=colors, s=sizes, alpha=0.3, cmap="viridis")
# 显示颜色条
plt.colorbar();
from sklearn.datasets import load_digits
digits = load_digits()
fig, ax = plt.subplots(10, 10, figsize=(8, 8))
for i, axi in enumerate(ax.flat):
axi.imshow(digits.images[i], cmap='binary')
axi.set(xticks=[], yticks=[])
通过scikit-learn流形学习(manifold learning)最早的算法Isomap(Isometric Mapping)将64维空间降成2维平面实现可视化,对比PCA(主成分分析),Isomap可以学习到非线性特征
"流形学习"——中国拓扑学家江泽涵院士取自文天祥《正气歌》的“天地有正气,杂然赋流形”,表示“多样体”
from sklearn.manifold import Isomap
iso = Isomap(n_components=2).fit_transform(digits.data)
sns.set_style("dark", {"font.sans-serif": ["SimHei", "Arial"]})
plt.figure(figsize=(10, 10))
plt.scatter(
iso[:, 0], iso[:, 1], lw=0.1, c=digits.target, cmap=plt.cm.get_cmap("cubehelix", 10),
)
plt.colorbar(ticks=range(10), label="数字值")
plt.clim(-0.5, 9.5);
sns.set_style("white", {"font.sans-serif": ["SimHei", "Arial"]})
plt.figure(figsize=(10, 10))
ax1 = plt.axes() # 默认坐标轴
ax2 = plt.axes([0.65, 0.65, 0.2, 0.2])
面向对象画图接口中类似的命令有fig.add_axes()
。用这个命令创建两个竖直排列的坐标轴:
fig = plt.figure(figsize=(10, 10))
ax1 = fig.add_axes([0.1, 0.5, 0.8, 0.4], xticklabels=[], ylim=(-1.2, 1.2))
ax2 = fig.add_axes([0.1, 0.1, 0.8, 0.4], ylim=(-1.2, 1.2))
x = np.linspace(0, 10)
ax1.plot(np.sin(x))
ax2.plot(np.cos(x));
plt.figure(figsize=(10, 10))
for i in range(1, 7):
plt.subplot(2, 3, i)
plt.text(0.5, 0.5, str((2, 3, i)), fontsize=18, ha="center")
plt.subplots_adjust
命令可以调整子图之间的间隔。用面向对象接口的命令fig.add_subplot()
可以取得同样的效果:
通过
plt.subplots_adjust
的hspace
与wspace
参数设置与图形高度与宽度一致的子图间距
fig = plt.figure(figsize=(10, 10))
fig.subplots_adjust(hspace=0.4, wspace=0.4)
for i in range(1, 7):
ax = fig.add_subplot(2, 3, i)
ax.text(0.5, 0.5, str((2, 3, i)), fontsize=18, ha="center")
fig, ax = plt.subplots(2, 3, sharex="col", sharey="row", figsize=(10, 10))
for i in range(2):
for j in range(3):
ax[i, j].text(0.5, 0.5, str((i, j)), fontsize=18, ha="center")
plt.figure(figsize=(10, 10))
grid = plt.GridSpec(2, 3, wspace=0.4, hspace=0.3)
plt.subplot(grid[0, 0])
plt.subplot(grid[0, 1:])
plt.subplot(grid[1, :2])
plt.subplot(grid[1, 2]);
用Matplotlib画三维图
可以用ax.plot3D
与ax.scatter3D
函数来创建三维坐标点构成的线图与散点图,需要用%matplotlib notebook
实现交互
ipyvolume:通过WebGL在Jupyter notebook实现3D交互,支持百万散点的页面渲染和交互
%matplotlib notebook
from mpl_toolkits import mplot3d
fig = plt.figure(figsize=(10, 10))
ax = plt.axes(projection="3d")
y = 15
# 三维曲线
zline = np.linspace(0, y, 1000)
xline = np.sin(zline)
yline = np.cos(zline)
ax.plot3D(xline, yline, zline, "gray")
# 三维散点
zdata = y * 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");
用ax.plot_surface
演示一个三维正弦函数画的三维等高曲面图,要求X,Y,Z都是二维网格数据的形式:
x = np.linspace(-6, 6, 30)
y = np.linspace(-6, 6, 30)
X, Y = np.meshgrid(x, y)
Z = np.sin(np.sqrt(X ** 2 + Y ** 2))
fig = plt.figure(figsize=(10, 10))
ax = plt.axes(projection="3d")
ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap="viridis", edgecolor="none")
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.set_zlabel("z")
pandas plot与pandas-alive
Pandas以Matplotlib实现了plot接口(Matlab风格),可以快速实现Serise与Datafram的可视化,pandas-alive增加了时间序列的动态图效果
%matplotlib inline
!head -n 20 timeseries.json
df_covid.diff().hist(figsize=(20,15), sharey=True);
spain = df_covid['Spain'].diff()
spain[spain<0]
df_covid.loc["2020-04-20":"2020-04-30", "Spain"]
def current_total(values):
total = values.sum()
s = f"总数 : {int(total)}"
return {"x": 0.85, "y": 0.2, "s": s, "ha": "right", "size": 11}
animated_html = df_covid.tail(60).plot_animated(period_summary_func=current_total)
from IPython.display import display, Video
display(Video('3.data-viz/covid19.mp4'))