使用Pyplot在一个图中绘制两个数据集,跳过y轴的一部分
我正在用pylab.plot()把不同的数据集画到同一张图上,这个方法很好用。但是有一个数据集的值在0%到25%之间,另一个数据集的值在75%到100%之间。我想在y轴上跳过30%到70%这部分,以节省一些空间。你们有什么建议可以用pyplot来实现这个吗?
编辑:
为了更清楚,我添加了下面的图。我想在y轴上跳过30%到60%,这样红线和绿线就能靠得更近一些。
3 个回答
matplotlib的文档里其实有一个示例,教你怎么做这个。
基本的思路是把图表分成两个子图,在每个图上画同样的图,然后调整每个图的坐标轴,只显示特定的部分,最后让它看起来更美观。
那么,我们来试试这个方法。假设这是你的起始代码:
import matplotlib.pyplot as plt
import random, math
# Generates data
i = range(10)
x = [math.floor(random.random() * 5) + 67 for i in range(10)]
y = [math.floor(random.random() * 5) + 22 for i in range(10)]
z = [math.floor(random.random() * 5) + 13 for i in range(10)]
# Original plot
fig, ax = plt.subplots()
ax.plot(i, x, 'ro-')
ax.plot(i, y, 'go-')
ax.plot(i, z, 'bo-')
plt.show()
我们想让x
的部分和其他部分分开显示。
首先,我们需要把同样的图画两次,一个在上面,一个在下面。为了做到这一点,绘图的函数需要是通用的。现在它应该看起来像这样:
# Plotting function
def plot(ax):
ax.plot(i, x, 'ro-')
ax.plot(i, y, 'go-')
ax.plot(i, z, 'bo-')
# Draw the graph on two subplots
fig, (ax1, ax2) = plt.subplots(2, 1)
plot(ax1)
plot(ax2)
现在看起来可能有点糟糕,但我们可以调整每个坐标轴的范围,专注于我们想要的部分。目前我只是选择了一些简单的范围,确保能捕捉到所有数据,但稍后我会专注于让坐标轴一致。
# Changes graph axes
ax1.set_ylim(65, 75) # Top graph
ax2.set_ylim(5, 30) # Bottom graph
这已经接近我们想要的效果了。现在我们只需要让它看起来更好一些:
# Hides the spines between the axes
ax1.spines.bottom.set_visible(False)
ax2.spines.top.set_visible(False)
ax1.xaxis.tick_top()
ax1.tick_params(labeltop=False) # Don't put tick labels at the top
ax2.xaxis.tick_bottom()
# Adds slanted lines to axes
d = .5 # proportion of vertical to horizontal extent of the slanted line
kwargs = dict(
marker=[(-1, -d), (1, d)],
markersize=12,
linestyle='none',
color='k',
mec='k',
mew=1,
clip_on=False
)
ax1.plot([0, 1], [0, 0], transform=ax1.transAxes, **kwargs)
ax2.plot([0, 1], [1, 1], transform=ax2.transAxes, **kwargs)
最后,我们来调整坐标轴。这里需要做一点数学计算,决定布局。例如,我们可能想让上面的图更小,因为下面的图有两条线。为此,我们需要调整子图的高度比例,像这样:
# Draw the graph on two subplots
# Bottom graph is twice the size of the top one
fig, (ax1, ax2) = plt.subplots(2, 1, gridspec_kw={'height_ratios': [1, 2]})
最后,让坐标轴匹配是个好主意。在这种情况下,因为下面的图是上面图的两倍大,我们需要调整一个坐标轴来反映这一点。我这次选择修改上面的图。下面的图覆盖了25的范围,这意味着上面的图应该覆盖12.5的范围。
# Changes graph axes
ax1.set_ylim(60.5, 73) # Top graph
ax2.set_ylim(5, 30) # Bottom graph
我觉得这样看起来不错。如果你不想让刻度和断开的线重叠,可以继续调整坐标轴或刻度标签。
最终代码:
import matplotlib.pyplot as plt
import random, math
# Generates data
i = range(10)
x = [math.floor(random.random() * 5) + 67 for i in range(10)]
y = [math.floor(random.random() * 5) + 22 for i in range(10)]
z = [math.floor(random.random() * 5) + 13 for i in range(10)]
# Plotting function
def plot(ax):
ax.plot(i, x, 'ro-')
ax.plot(i, y, 'go-')
ax.plot(i, z, 'bo-')
# Draw the graph on two subplots
# Bottom graph is twice the size of the top one
fig, (ax1, ax2) = plt.subplots(2, 1, gridspec_kw={'height_ratios': [1, 2]})
plot(ax1)
plot(ax2)
# Changes graph axes
ax1.set_ylim(60.5, 73) # Top graph
ax2.set_ylim(5, 30) # Bottom graph
# Hides the spines between the axes
ax1.spines.bottom.set_visible(False)
ax2.spines.top.set_visible(False)
ax1.xaxis.tick_top()
ax1.tick_params(labeltop=False) # Don't put tick labels at the top
ax2.xaxis.tick_bottom()
# Adds slanted lines to axes
d = .5 # proportion of vertical to horizontal extent of the slanted line
kwargs = dict(
marker=[(-1, -d), (1, d)],
markersize=12,
linestyle='none',
color='k',
mec='k',
mew=1,
clip_on=False
)
ax1.plot([0, 1], [0, 0], transform=ax1.transAxes, **kwargs)
ax2.plot([0, 1], [1, 1], transform=ax2.transAxes, **kwargs)
plt.show()
你可以把第二个函数的x值减去40,这样x值的范围就会变得连续。这会让你的x值范围从0%到70%。然后你可以这样设置x轴的刻度和标签:
x_ticks = range(71, 0, 10)
a.set_xticks(x_ticks)
a.set_xticklabels([str(x) for x in [0, 10, 20, 30, 70, 80, 90, 100]])
这里的a
代表当前的坐标轴。简单来说,你是在0%到70%的范围内绘制你的函数,但在标记轴的时候留出一些空隙。
为了说明这一点,下面的脚本:
from numpy import arange
import matplotlib.pyplot as plt
x1 = arange(0, 26) # first function
y1 = x1**2
x2 = arange(75, 100) # second function
y2 = x2*4 + 10
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(x1, y1)
ax.plot(x2 - 40, y2) # shift second function 40 to left
ax.set_xticks(range(0, 61, 5)) # set custom x-ticks
# set labels for x-ticks - labels have the gap we want
ax.set_xticklabels([str(x) for x in range(0, 26, 5) + range(70, 101, 5)])
plt.show()
会生成如下的图表(注意x轴的标签):
这个解决方案是基于Space_C0wb0ys的帖子。
fig = pylab.figure()
ax = fig.add_subplot(111)
ax.plot( range(1,10), camean - 25, 'ro-' )
ax.plot( range(1,10), oemean , 'go-' )
ax.plot( range(1,10), hlmean , 'bo-' )
ax.set_yticks(range(5, 60, 5))
ax.set_yticklabels(["5","10","15","20","25","30","...","65","70","75"])
ax.legend(('ClassificationAccuracy','One-Error','HammingLoss'),loc='upper right')
pylab.show()
这段代码会生成下面这个图形。