Matplotlib真的不能绘制「交互图表」?
小数志
共 8115字,需浏览 17分钟
·
2021-08-16 02:59
👆星标关注pythonic生物人,前排阅读以下干货!
前景提要:本篇分享Matplotlib的两个扩展
mpl_interactions
和mpldatacursor
,弥补Matplotlib交互能力的缺陷。「文末有彩蛋」
mpl_interactions
支持窗口界面和jupyter notebook中渲染,以折线图mpl_interactions.ipyplot.plot为例
交互后端qt中渲染图形
魔法命令%matplotlib qt
调用qt后端,此时可点击界面按钮修改图形的线型、marker、颜色、坐标轴等等属性。
# 魔法命令调用qt后端
%matplotlib qt
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.widgets import Slider
import mpl_interactions.ipyplot as iplt
plt.style.use('fivethirtyeight')
fig, ax = plt.subplots(dpi=80)
plt.subplots_adjust(bottom=.25)
x = np.linspace(0, 2 * np.pi, 200)
def f(x, freq):
return np.sin(x * freq)
axfreq = plt.axes([0.25, 0.1, 0.65, 0.03])
slider = Slider(axfreq, label='freq', valmin=.05, valmax=10)
controls = iplt.plot(x, f, freq=slider, ax=ax)
交互后端ipympl中渲染图形
#魔法命令调用ipympl backend(后端)
%matplotlib ipympl
import numpy as np
import pandas as pd
import mpl_interactions.ipyplot as iplt
import matplotlib.pyplot as plt
plt.style.use('fivethirtyeight')
#准备数据
x = np.linspace(0, np.pi, 100)
tau = np.linspace(0.5, 10, 100)
def f1(x, tau, beta):
return np.sin(x * tau) * x * beta
def f2(x, tau, beta):
return np.sin(x * beta) * x * tau
fig, ax = plt.subplots(dpi=80)
#iplt.plot
controls = iplt.plot(x, f1, tau=tau, beta=(1, 10, 100), label="f1")
iplt.plot(x, f2, controls=controls, label="f2")#定义滑动条(sliders)
#图例(legend)
_ = plt.legend()
iplt.title("iplt.plot: tau is {tau:.2f}", controls=controls['tau'])
plt.show()
散点图mpl_interactions.ipyplot.scatter
%matplotlib ipympl
import matplotlib.pyplot as plt
import numpy as np
import mpl_interactions.ipyplot as iplt
import ipywidgets as widgets
import pandas as pd
from matplotlib.colors import to_rgba_array, TABLEAU_COLORS, XKCD_COLORS
# 数据准备
data = pd.read_json('nations.json')
def clean_data(data):
for column in ['income', 'lifeExpectancy', 'population']:
data = data.drop(data[data[column].apply(len) <= 4].index)
return data
def extrap_interp(data):
data = np.array(data)
x_range = np.arange(1800, 2009, 1.)
y_range = np.interp(x_range, data[:, 0], data[:, 1])
return y_range
def extrap_data(data):
for column in ['income', 'lifeExpectancy', 'population']:
data[column] = data[column].apply(extrap_interp)
return data
data = clean_data(data)
data = extrap_data(data)
income_min, income_max = np.min(data['income'].apply(np.min)), np.max(
data['income'].apply(np.max))
life_exp_min, life_exp_max = np.min(data['lifeExpectancy'].apply(
np.min)), np.max(data['lifeExpectancy'].apply(np.max))
pop_min, pop_max = np.min(data['population'].apply(np.min)), np.max(
data['population'].apply(np.max))
def x(year):
return data["income"].apply(lambda x: x[year - 1800])
def y(x, year):
return data["lifeExpectancy"].apply(lambda x: x[year - 1800])
def s(x, y, year):
pop = data["population"].apply(lambda x: x[year - 1800])
return 6000 * pop.values / pop_max
regions = data["region"].unique().tolist()
c = data["region"].apply(
lambda x: list(TABLEAU_COLORS)[regions.index(x)]).values
#画图
fig, ax = plt.subplots(dpi=80,figsize=(10, 4.8))
controls = iplt.scatter(
x,
y,
s=s,
year=np.arange(1800, 2009),
c=c,
edgecolors="k",
slider_formats="{:d}",
play_buttons=True,
play_button_pos="left",
)
fs = 15
iplt.title("iplt.scatter: year is {year} ", controls=controls['year'])
ax.set_xscale("log")
ax.set_ylim([0, 100])
ax.set_xlim([200, income_max * 1.05])
ax.set_xlabel("Income", fontsize=fs)
_ = ax.set_ylabel("Life Expectancy", fontsize=fs)
图像mpl_interactions.ipyplot.t.imshow
img = plt.imread("https://matplotlib.org/3.3.1/_images/stinkbug.png")
fig4, ax4 = plt.subplots(dpi=80)
controls4 = iplt.imshow(img, vmin_vmax=("r", img.min(), img.max()))
直方图mpl_interactions.interactive_hist
%matplotlib ipympl
import ipywidgets as widgets
import matplotlib.pyplot as plt
import numpy as np
from mpl_interactions import interactive_hist
def f(loc, scale):
return np.random.randn(10000) * scale + loc
fig, ax = plt.subplots(dpi=80)
controls = interactive_hist(f, loc=(0, 10, 100), scale=(0.5, 5))
iplt.title("iplt.interactive_hist: loc is {loc:.2f} ", controls=controls['loc'])
热图mpl_interactions.heatmap_slicer
%matplotlib widget
import matplotlib.pyplot as plt
import numpy as np
from mpl_interactions import heatmap_slicer
x = np.linspace(0, np.pi, 100)
y = np.linspace(0, 10, 200)
X, Y = np.meshgrid(x, y)
data1 = np.sin(X) + np.exp(np.cos(Y))
data2 = np.cos(X) + np.exp(np.sin(Y))
fig, axes = heatmap_slicer(
x,
y,
(data1, data2),
slices="both",
heatmap_names=("dataset 1", "dataset 2"),
labels=("Some wild X variable", "Y axis"),
interaction_type="move",
)
mpl_interactions缺陷
功能有限,仅仅支持以下几个方向,前面大部分已经列出。下面介绍另外一个扩展mpldatacursor。
mpldatacursor
主要使用data cursors方法,为matplotlib生成一个交互注视文本
,简单举几个例子.
import numpy as np
import matplotlib.pyplot as plt
from mpldatacursor import datacursor
x = np.linspace(0, 10, 100)
fig, ax = plt.subplots(dpi=80)
ax.set_title('Click to display its label')
for i in range(1, 20):
ax.plot(x, i * x, label='$y = {}x.format(i))
#formatter展示每个点详细数据、方程
datacursor(formatter="{label}\nx:{x:.2f}\ny:{y:.2f}".format)
plt.show()
mpldatacursor 缺陷
同第一个扩展,能力有限。
精进两工具
https://github.com/joferkington/mpldatacursor
https://github.com/ianhi/mpl-interactions
彩蛋彩蛋👇👇
mpl_interactions结合mpldatacursor能满足简单的交互需求,需要更多交互还是寻求更专业的交互软件
:
相关阅读:
评论