Matplotlib:将两个图组合成一个 - 同时以两个不同的单位显示x轴

时间:2017-10-16 20:23:42

标签: python-3.x matplotlib

这是我的data_df

col_C     col_A     col_B   col_D
-----------------------------------
8/21/2017   4        1.0    736562.0
8/28/2017   5        2.0    736569.0
9/4/2017    6        4.0    736576.0
9/11/2017   7        5.0    736583.0
9/18/2017   8        6.0    736590.0
10/2/2017   10       7.0    736604.0
2/5/2018    28      125.0   736730.0

我有以下代码,它绘制了一些数据点及其拟合曲线。

xi = np.array(data_df[['col_A']])
yi = np.array(data_df[['col_B']])

plt.figure()
plt.scatter(xi, yi, color = 'r')

x = np.linspace(0, 30, 30)
y= np.exp(x*0.172)
plt.plot(x, y, color = 'g', label = 'exp(t)')
plt.legend()    
plt.show()

enter image description here

我还有另一段代码,用相应的日期绘制数据点作为x轴。

import matplotlib
data_df['col_D'] = data_df['col_C'] \
    .apply(lambda x: matplotlib.dates.datestr2num(x))
plt.plot_date(data_df[['col_D']], data_df[['col_B']])

plt.show()

enter image description here

现在我尝试将这两个图合并为一个。因此,x轴可以显示[0, 5, 10 ... 30][2017-09, 2017-10 ..., 2018-02]。以下是组合代码:

xi = np.array(data_df[['col_A']])
yi = np.array(data_df[['col_B']])  
plt.figure()
plt.scatter(xi, yi, color = 'r')

x = np.linspace(0, 30, 30)
y= np.exp(x*0.172)
plt.plot(x, y, color = 'g', label = 'exp(t)')
plt.legend()

data_df['col_C'] = data_df['col_D'] \
    .apply(lambda x: matplotlib.dates.datestr2num(x))
plt.plot_date(data_df[['col_C']], data_df[['col_B']])
plt.show()

但是,合并后的代码给了我以下错误:

ValueError                                Traceback (most recent call last)
/usr/local/lib/python3.4/dist-packages/IPython/core/formatters.py in __call__(self, obj)
    339                 pass
    340             else:
--> 341                 return printer(obj)
    342             # Finally look for special method names
    343             method = get_real_method(obj, self.print_method)

/usr/local/lib/python3.4/dist-packages/IPython/core/pylabtools.py in <lambda>(fig)
    236 
    237     if 'png' in formats:
--> 238         png_formatter.for_type(Figure, lambda fig: print_figure(fig, 'png', **kwargs))
    239     if 'retina' in formats or 'png2x' in formats:
    240         png_formatter.for_type(Figure, lambda fig: retina_figure(fig, **kwargs))

/usr/local/lib/python3.4/dist-packages/IPython/core/pylabtools.py in print_figure(fig, fmt, bbox_inches, **kwargs)
    120 
    121     bytes_io = BytesIO()
--> 122     fig.canvas.print_figure(bytes_io, **kw)
    123     data = bytes_io.getvalue()
    124     if fmt == 'svg':

/usr/local/lib/python3.4/dist-packages/matplotlib/backend_bases.py in print_figure(self, filename, dpi, facecolor, edgecolor, orientation, format, **kwargs)
   2190                     orientation=orientation,
   2191                     dryrun=True,
-> 2192                     **kwargs)
   2193                 renderer = self.figure._cachedRenderer
   2194                 bbox_inches = self.figure.get_tightbbox(renderer)

/usr/local/lib/python3.4/dist-packages/matplotlib/backends/backend_agg.py in print_png(self, filename_or_obj, *args, **kwargs)
    543 
    544     def print_png(self, filename_or_obj, *args, **kwargs):
--> 545         FigureCanvasAgg.draw(self)
    546         renderer = self.get_renderer()
    547         original_dpi = renderer.dpi

/usr/local/lib/python3.4/dist-packages/matplotlib/backends/backend_agg.py in draw(self)
    462 
    463         try:
--> 464             self.figure.draw(self.renderer)
    465         finally:
    466             RendererAgg.lock.release()

/usr/local/lib/python3.4/dist-packages/matplotlib/artist.py in draw_wrapper(artist, renderer, *args, **kwargs)
     61     def draw_wrapper(artist, renderer, *args, **kwargs):
     62         before(artist, renderer)
---> 63         draw(artist, renderer, *args, **kwargs)
     64         after(artist, renderer)
     65 

/usr/local/lib/python3.4/dist-packages/matplotlib/figure.py in draw(self, renderer)
   1141 
   1142             mimage._draw_list_compositing_images(
-> 1143                 renderer, self, dsu, self.suppressComposite)
   1144 
   1145             renderer.close_group('figure')

/usr/local/lib/python3.4/dist-packages/matplotlib/image.py in _draw_list_compositing_images(renderer, parent, dsu, suppress_composite)
    137     if not_composite or not has_images:
    138         for zorder, a in dsu:
--> 139             a.draw(renderer)
    140     else:
    141         # Composite any adjacent images together

/usr/local/lib/python3.4/dist-packages/matplotlib/artist.py in draw_wrapper(artist, renderer, *args, **kwargs)
     61     def draw_wrapper(artist, renderer, *args, **kwargs):
     62         before(artist, renderer)
---> 63         draw(artist, renderer, *args, **kwargs)
     64         after(artist, renderer)
     65 

/usr/local/lib/python3.4/dist-packages/matplotlib/axes/_base.py in draw(self, renderer, inframe)
   2407             renderer.stop_rasterizing()
   2408 
-> 2409         mimage._draw_list_compositing_images(renderer, self, dsu)
   2410 
   2411         renderer.close_group('axes')

/usr/local/lib/python3.4/dist-packages/matplotlib/image.py in _draw_list_compositing_images(renderer, parent, dsu, suppress_composite)
    137     if not_composite or not has_images:
    138         for zorder, a in dsu:
--> 139             a.draw(renderer)
    140     else:
    141         # Composite any adjacent images together

/usr/local/lib/python3.4/dist-packages/matplotlib/artist.py in draw_wrapper(artist, renderer, *args, **kwargs)
     61     def draw_wrapper(artist, renderer, *args, **kwargs):
     62         before(artist, renderer)
---> 63         draw(artist, renderer, *args, **kwargs)
     64         after(artist, renderer)
     65 

/usr/local/lib/python3.4/dist-packages/matplotlib/axis.py in draw(self, renderer, *args, **kwargs)
   1134         renderer.open_group(__name__)
   1135 
-> 1136         ticks_to_draw = self._update_ticks(renderer)
   1137         ticklabelBoxes, ticklabelBoxes2 = self._get_tick_bboxes(ticks_to_draw,
   1138                                                                 renderer)

/usr/local/lib/python3.4/dist-packages/matplotlib/axis.py in _update_ticks(self, renderer)
    967 
    968         interval = self.get_view_interval()
--> 969         tick_tups = [t for t in self.iter_ticks()]
    970         if self._smart_bounds:
    971             # handle inverted limits

/usr/local/lib/python3.4/dist-packages/matplotlib/axis.py in <listcomp>(.0)
    967 
    968         interval = self.get_view_interval()
--> 969         tick_tups = [t for t in self.iter_ticks()]
    970         if self._smart_bounds:
    971             # handle inverted limits

/usr/local/lib/python3.4/dist-packages/matplotlib/axis.py in iter_ticks(self)
    910         Iterate through all of the major and minor ticks.
    911         """
--> 912         majorLocs = self.major.locator()
    913         majorTicks = self.get_major_ticks(len(majorLocs))
    914         self.major.formatter.set_locs(majorLocs)

/usr/local/lib/python3.4/dist-packages/matplotlib/dates.py in __call__(self)
    981     def __call__(self):
    982         'Return the locations of the ticks'
--> 983         self.refresh()
    984         return self._locator()
    985 

/usr/local/lib/python3.4/dist-packages/matplotlib/dates.py in refresh(self)
   1001     def refresh(self):
   1002         'Refresh internal information based on current limits.'
-> 1003         dmin, dmax = self.viewlim_to_dt()
   1004         self._locator = self.get_locator(dmin, dmax)
   1005 

/usr/local/lib/python3.4/dist-packages/matplotlib/dates.py in viewlim_to_dt(self)
    758             vmin, vmax = vmax, vmin
    759 
--> 760         return num2date(vmin, self.tz), num2date(vmax, self.tz)
    761 
    762     def _get_unit(self):

/usr/local/lib/python3.4/dist-packages/matplotlib/dates.py in num2date(x, tz)
    399         tz = _get_rc_timezone()
    400     if not cbook.iterable(x):
--> 401         return _from_ordinalf(x, tz)
    402     else:
    403         x = np.asarray(x)

/usr/local/lib/python3.4/dist-packages/matplotlib/dates.py in _from_ordinalf(x, tz)
    252 
    253     ix = int(x)
--> 254     dt = datetime.datetime.fromordinal(ix).replace(tzinfo=UTC)
    255 
    256     remainder = float(x) - ix

ValueError: ordinal must be >= 1

<matplotlib.figure.Figure at 0x7f65c6f98f28>

知道出了什么问题吗?是否可以在x轴上显示[0, 5, 10 ... 30][2017-09, 2017-10 ..., 2018-02]?谢谢!

1 个答案:

答案 0 :(得分:0)

问题是一个图的轴范围大致为0到30,而另一个图的轴范围大致为736562到736730(这是日期的数字表示。

您可以选择使用双轴并将一个范围映射到另一个范围。然后根据映射设置双轴的极限。

u = u"""col_C     col_A     col_B   col_D
8/21/2017   4        1.0    736562.0
8/28/2017   5        2.0    736569.0
9/4/2017    6        4.0    736576.0
9/11/2017   7        5.0    736583.0
9/18/2017   8        6.0    736590.0
10/2/2017   10       7.0    736604.0
2/5/2018    28      125.0   736730.0"""

import io
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.dates 

df = pd.read_csv(io.StringIO(u), delim_whitespace=True)

xi = np.array(df[['col_A']])
yi = np.array(df[['col_B']])

fig, ax = plt.subplots()
ax.scatter(xi, yi, color = 'r')

x = np.linspace(0, 30, 30)
y= np.exp(x*0.172)
ax.plot(x, y, color = 'g', label = 'exp(t)')
ax.legend()    

ax2 = ax.twiny()

df['col_D'] = df['col_C'].apply(lambda x: matplotlib.dates.datestr2num(x))
ax2.plot_date(df[['col_D']], df[['col_B']])

f = lambda x: (df['col_D'].iloc[-1]- df['col_D'].iloc[0])/ \
                (xi[-1]-xi[0])*(x-xi[0])+df['col_D'].iloc[0] 

ax2.set_xlim(f(np.array(ax.get_xlim())))


plt.show()

enter image description here

如果您愿意,可以将alpha=0添加到第二个图中以隐藏它。