matplotlib:创建两个(堆叠的)子图,共享X轴但具有单独的Y轴值

13
我正在使用 Ubuntu 10.0.4 上的 matplotlib 1.2.x 和 Python 2.6.5。我正在尝试创建一个包含顶部图表和底部图表的单一图表。
X 轴是时间序列的日期。顶部图表包含数据的蜡烛图,底部图表应该包含柱形图——带有自己的 Y 轴(也在左侧——与顶部图表相同)。这两个图表不应重叠。
这是我迄今为止所做的一小部分代码片段。
datafile = r'/var/tmp/trz12.csv'
r = mlab.csv2rec(datafile, delimiter=',', names=('dt', 'op', 'hi', 'lo', 'cl', 'vol', 'oi'))

mask = (r["dt"] >= datetime.date(startdate)) & (r["dt"] <= datetime.date(enddate))
selected = r[mask]
plotdata = zip(date2num(selected['dt']), selected['op'], selected['cl'], selected['hi'], selected['lo'], selected['vol'], selected['oi'])

# Setup charting 
mondays = WeekdayLocator(MONDAY)        # major ticks on the mondays
alldays    = DayLocator()               # minor ticks on the days
weekFormatter = DateFormatter('%b %d')  # Eg, Jan 12
dayFormatter = DateFormatter('%d')      # Eg, 12
monthFormatter = DateFormatter('%b %y')

# every Nth month
months = MonthLocator(range(1,13), bymonthday=1, interval=1)

fig = pylab.figure()
fig.subplots_adjust(bottom=0.1)
ax = fig.add_subplot(111)
ax.xaxis.set_major_locator(months)#mondays
ax.xaxis.set_major_formatter(monthFormatter) #weekFormatter
ax.format_xdata = mdates.DateFormatter('%Y-%m-%d')
ax.format_ydata = price
ax.grid(True)

candlestick(ax, plotdata, width=0.5, colorup='g', colordown='r', alpha=0.85)

ax.xaxis_date()
ax.autoscale_view()
pylab.setp( pylab.gca().get_xticklabels(), rotation=45, horizontalalignment='right')

# Add volume data 
# Note: the code below OVERWRITES the bottom part of the first plot
# it should be plotted UNDERNEATH the first plot - but somehow, that's not happening
fig.subplots_adjust(hspace=0.15)
ay = fig.add_subplot(212)
volumes = [ x[-2] for x in plotdata]
ay.bar(range(len(plotdata)), volumes, 0.05)

pylab.show()

使用上述代码,我已经成功地显示了两个图,但是有关于底部图的两个问题:

  1. 它完全覆盖了第一个(顶部)图的底部 - 好像第二个图是在同一'画布'上绘制的。我不知道为什么会发生这种情况。

  2. 它用自己的索引替换了现有的X轴,两个图之间应该共享X轴值(日期)。

我的代码哪里出了问题?有人能找出第二个(底部)图覆盖第一个(顶部)图的原因吗?如何解决呢?

以下是由上述代码创建的图的屏幕截图:

错误的图

[[编辑]]

按照hwlau的建议修改代码后,产生了新的图。与第一个图相比,它更好,因为两个图是分开的,然而以下问题仍然存在:

  1. 两个图之间应该共享X轴(即仅显示第二个[底部]图的X轴)

  2. 第二个图的Y值似乎格式不正确

部分正确的图

我认为这些问题应该很容易解决,但是我的matplotlib技能目前不太好,因为我最近才开始使用matplotlib进行编程。非常感谢任何帮助。

3个回答

12

你的代码存在一些问题:

  1. 如果你使用了 figure.add_subplot 并且使用了完整的签名 subplot(nrows, ncols, plotNum),那么你会发现第一个图请求1行 1 列,而第二个图请求2行 1列。因此,第一个图正在填充整个图形,而不是应有的1/3。请改用 fig.add_subplot(211) 加上 fig.add_subplot(212),而不是 fig.add_subplot(111)fig.add_subplot(212)

  2. add_subplot 命令中共享一个轴应该使用 sharex=first_axis_instance 来完成。

我已经准备好了一个示例供您参考:

import matplotlib.pyplot as plt
import matplotlib.ticker as mticker
import matplotlib.dates as mdates


import datetime as dt


n_pts = 10
dates = [dt.datetime.now() + dt.timedelta(days=i) for i in range(n_pts)]

ax1 = plt.subplot(2, 1, 1)
ax1.plot(dates, range(10))

ax2 = plt.subplot(2, 1, 2, sharex=ax1)
ax2.bar(dates, range(10, 20))

# Now format the x axis. This *MUST* be done after all sharex commands are run.

# put no more than 10 ticks on the date axis.  
ax1.xaxis.set_major_locator(mticker.MaxNLocator(10))
# format the date in our own way.
ax1.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d'))

# rotate the labels on both date axes
for label in ax1.xaxis.get_ticklabels():
    label.set_rotation(30)
for label in ax2.xaxis.get_ticklabels():
    label.set_rotation(30)

# tweak the subplot spacing to fit the rotated labels correctly
plt.subplots_adjust(hspace=0.35, bottom=0.125)

plt.show()

希望能对你有所帮助。


subplots 中的 sharexsharey 属性非常方便,感谢您的发布。这比从一个子图读取并在另一个子图中设置共同限制要好得多:当通过交互式缩放来检查结果时,如果共享,则所有子图都将一起缩放。 - Bonlenfum
sharex=first_axis_instance 这是一个 axes 实例,对吗? - Adam Wheeler

6
你需要更改这一行:
ax = fig.add_subplot(111)

to

ax = fig.add_subplot(211)

原始命令意味着只有一行和一列,因此它占据了整个图表。所以你的第二个图 fig.add_subplot(212) 覆盖了第一个图的下部分。

编辑

如果您不想在两个图之间保留空白,请使用 subplots_adjust() 来更改子图边距的大小。


谢谢你发现了这个问题。我几乎感到尴尬错过了那么明显的东西。也许是因为我盯着代码看太久了 ; ) 现在情节更接近我想要的了。不过还有一些未解决的问题。请有时间时看一下我编辑后的问题。 - Homunculus Reticulli

2
@Pelson 的简化示例。
import matplotlib.pyplot as plt
import datetime as dt

#Two subplots that share one x axis
fig,ax=plt.subplots(2,sharex=True)

#plot data
n_pts = 10
dates = [dt.datetime.now() + dt.timedelta(days=i) for i in range(n_pts)]
ax[0].bar(dates, range(10, 20))
ax[1].plot(dates, range(10))

#rotate and format the dates on the x axis
fig.autofmt_xdate()

共享x轴的子图可以在一行中创建,这在您需要超过两个子图时非常方便:
fig, ax = plt.subplots(number_of_subplots, sharex=True)

为了在x轴上正确格式化日期,我们可以简单地使用fig.autofmt_xdate()

shared x xaxis

额外信息请参见pylab示例中的共享轴演示日期演示。此示例在Python3,matplotlib 1.5.1上运行。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接