Reputation: 495
I want to plot the mean based on month/year
here is a sample of my data and date as index
mean count
date
1901-01-31 0.608475 3
1901-02-28 0.751826 6
1901-03-31 0.050980 6
1901-04-30 0.229060 2
1901-05-31 0.246976 4
1901-11-30 0.936585 8
1901-12-31 0.085918 1
1902-07-31 0.786897 0
1902-08-31 0.774024 9
1902-12-31 0.602361 6
1903-01-31 0.805497 4
1903-02-28 0.604498 7
1903-03-31 0.991389 2
1903-04-30 0.774783 1
1903-05-31 0.880222 9
1903-06-30 0.735544 1
so I wrote this code to groupby by month/year
data=df.groupby([df.index.year.rename('year'),df.index.month.rename('month')]).mean()
and here is a sample of data after group by
mean count
date date
1901 1 0.468082 7
2 0.378744 8
3 0.668791 3
4 0.563842 7
5 0.764414 2
6 0.530849 3
7 0.703855 7
8 0.604860 9
9 0.766420 0
10 0.752520 8
11 0.361177 3
12 0.502622 1
1902 1 0.694125 0
2 0.661784 0
3 0.491230 2
4 0.074626 7
5 0.291552 4
6 0.376639 6
7 0.681985 8
8 0.381116 6
9 0.895843 3
10 0.378515 8
11 0.803789 8
12 0.616002 7
1903 1 0.372718 4
2 0.992567 9
3 0.746151 9
4 0.970088 7
5 0.053416 1
6 0.959746 4
7 0.604006 0
8 0.894918 0
9 0.716278 3
10 0.420532 6
11 0.161469 7
then I have plot
import matplotlib.pyplot as plt
plt.figure()
ax = data['mean'].plot(figsize=(20,10),linewidth=0.8)
for d,row in data.iterrows():
ax.annotate('{:.0f}'.format(row['count']), xy=(d,row['mean']), ha='center',clip_on=True)
but I got this error
Error in callback <function install_repl_displayhook.<locals>.post_execute at 0x00000250AD5FDD08> (for post_execute):
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
~\Anaconda3\lib\site-packages\matplotlib\pyplot.py in post_execute()
107 def post_execute():
108 if matplotlib.is_interactive():
--> 109 draw_all()
110
111 # IPython >= 2
~\Anaconda3\lib\site-packages\matplotlib\_pylab_helpers.py in draw_all(cls, force)
126 for f_mgr in cls.get_all_fig_managers():
127 if force or f_mgr.canvas.figure.stale:
--> 128 f_mgr.canvas.draw_idle()
129
130 atexit.register(Gcf.destroy_all)
~\Anaconda3\lib\site-packages\matplotlib\backend_bases.py in draw_idle(self, *args, **kwargs)
1905 if not self._is_idle_drawing:
1906 with self._idle_draw_cntx():
-> 1907 self.draw(*args, **kwargs)
1908
1909 def draw_cursor(self, event):
~\Anaconda3\lib\site-packages\matplotlib\backends\backend_agg.py in draw(self)
386 self.renderer = self.get_renderer(cleared=True)
387 with RendererAgg.lock:
--> 388 self.figure.draw(self.renderer)
389 # A GUI class may be need to update a window using this draw, so
390 # don't forget to call the superclass.
~\Anaconda3\lib\site-packages\matplotlib\artist.py in draw_wrapper(artist, renderer, *args, **kwargs)
36 renderer.start_filter()
37
---> 38 return draw(artist, renderer, *args, **kwargs)
39 finally:
40 if artist.get_agg_filter() is not None:
~\Anaconda3\lib\site-packages\matplotlib\figure.py in draw(self, renderer)
1707 self.patch.draw(renderer)
1708 mimage._draw_list_compositing_images(
-> 1709 renderer, self, artists, self.suppressComposite)
1710
1711 renderer.close_group('figure')
~\Anaconda3\lib\site-packages\matplotlib\image.py in _draw_list_compositing_images(renderer, parent, artists, suppress_composite)
133 if not_composite or not has_images:
134 for a in artists:
--> 135 a.draw(renderer)
136 else:
137 # Composite any adjacent images together
~\Anaconda3\lib\site-packages\matplotlib\artist.py in draw_wrapper(artist, renderer, *args, **kwargs)
36 renderer.start_filter()
37
---> 38 return draw(artist, renderer, *args, **kwargs)
39 finally:
40 if artist.get_agg_filter() is not None:
~\Anaconda3\lib\site-packages\matplotlib\axes\_base.py in draw(self, renderer, inframe)
2643 renderer.stop_rasterizing()
2644
-> 2645 mimage._draw_list_compositing_images(renderer, self, artists)
2646
2647 renderer.close_group('axes')
~\Anaconda3\lib\site-packages\matplotlib\image.py in _draw_list_compositing_images(renderer, parent, artists, suppress_composite)
133 if not_composite or not has_images:
134 for a in artists:
--> 135 a.draw(renderer)
136 else:
137 # Composite any adjacent images together
~\Anaconda3\lib\site-packages\matplotlib\artist.py in draw_wrapper(artist, renderer, *args, **kwargs)
36 renderer.start_filter()
37
---> 38 return draw(artist, renderer, *args, **kwargs)
39 finally:
40 if artist.get_agg_filter() is not None:
~\Anaconda3\lib\site-packages\matplotlib\text.py in draw(self, renderer)
2353 return
2354
-> 2355 xy_pixel = self._get_position_xy(renderer)
2356 if not self._check_xy(renderer, xy_pixel):
2357 return
~\Anaconda3\lib\site-packages\matplotlib\text.py in _get_position_xy(self, renderer)
1903 "Return the pixel position of the annotated point."
1904 x, y = self.xy
-> 1905 return self._get_xy(renderer, x, y, self.xycoords)
1906
1907 def _check_xy(self, renderer, xy_pixel):
~\Anaconda3\lib\site-packages\matplotlib\text.py in _get_xy(self, renderer, x, y, s)
1757
1758 if s1 == 'data':
-> 1759 x = float(self.convert_xunits(x))
1760 if s2 == 'data':
1761 y = float(self.convert_yunits(y))
TypeError: float() argument must be a string or a number, not 'tuple'
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
~\Anaconda3\lib\site-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)
~\Anaconda3\lib\site-packages\IPython\core\pylabtools.py in <lambda>(fig)
242
243 if 'png' in formats:
--> 244 png_formatter.for_type(Figure, lambda fig: print_figure(fig, 'png', **kwargs))
245 if 'retina' in formats or 'png2x' in formats:
246 png_formatter.for_type(Figure, lambda fig: retina_figure(fig, **kwargs))
~\Anaconda3\lib\site-packages\IPython\core\pylabtools.py in print_figure(fig, fmt, bbox_inches, **kwargs)
126
127 bytes_io = BytesIO()
--> 128 fig.canvas.print_figure(bytes_io, **kw)
129 data = bytes_io.getvalue()
130 if fmt == 'svg':
~\Anaconda3\lib\site-packages\matplotlib\backend_bases.py in print_figure(self, filename, dpi, facecolor, edgecolor, orientation, format, bbox_inches, **kwargs)
2054 orientation=orientation,
2055 dryrun=True,
-> 2056 **kwargs)
2057 renderer = self.figure._cachedRenderer
2058 bbox_artists = kwargs.pop("bbox_extra_artists", None)
~\Anaconda3\lib\site-packages\matplotlib\backends\backend_agg.py in print_png(self, filename_or_obj, metadata, pil_kwargs, *args, **kwargs)
525
526 else:
--> 527 FigureCanvasAgg.draw(self)
528 renderer = self.get_renderer()
529 with cbook._setattr_cm(renderer, dpi=self.figure.dpi), \
~\Anaconda3\lib\site-packages\matplotlib\backends\backend_agg.py in draw(self)
386 self.renderer = self.get_renderer(cleared=True)
387 with RendererAgg.lock:
--> 388 self.figure.draw(self.renderer)
389 # A GUI class may be need to update a window using this draw, so
390 # don't forget to call the superclass.
~\Anaconda3\lib\site-packages\matplotlib\artist.py in draw_wrapper(artist, renderer, *args, **kwargs)
36 renderer.start_filter()
37
---> 38 return draw(artist, renderer, *args, **kwargs)
39 finally:
40 if artist.get_agg_filter() is not None:
~\Anaconda3\lib\site-packages\matplotlib\figure.py in draw(self, renderer)
1707 self.patch.draw(renderer)
1708 mimage._draw_list_compositing_images(
-> 1709 renderer, self, artists, self.suppressComposite)
1710
1711 renderer.close_group('figure')
~\Anaconda3\lib\site-packages\matplotlib\image.py in _draw_list_compositing_images(renderer, parent, artists, suppress_composite)
133 if not_composite or not has_images:
134 for a in artists:
--> 135 a.draw(renderer)
136 else:
137 # Composite any adjacent images together
~\Anaconda3\lib\site-packages\matplotlib\artist.py in draw_wrapper(artist, renderer, *args, **kwargs)
36 renderer.start_filter()
37
---> 38 return draw(artist, renderer, *args, **kwargs)
39 finally:
40 if artist.get_agg_filter() is not None:
~\Anaconda3\lib\site-packages\matplotlib\axes\_base.py in draw(self, renderer, inframe)
2643 renderer.stop_rasterizing()
2644
-> 2645 mimage._draw_list_compositing_images(renderer, self, artists)
2646
2647 renderer.close_group('axes')
~\Anaconda3\lib\site-packages\matplotlib\image.py in _draw_list_compositing_images(renderer, parent, artists, suppress_composite)
133 if not_composite or not has_images:
134 for a in artists:
--> 135 a.draw(renderer)
136 else:
137 # Composite any adjacent images together
~\Anaconda3\lib\site-packages\matplotlib\artist.py in draw_wrapper(artist, renderer, *args, **kwargs)
36 renderer.start_filter()
37
---> 38 return draw(artist, renderer, *args, **kwargs)
39 finally:
40 if artist.get_agg_filter() is not None:
~\Anaconda3\lib\site-packages\matplotlib\text.py in draw(self, renderer)
2353 return
2354
-> 2355 xy_pixel = self._get_position_xy(renderer)
2356 if not self._check_xy(renderer, xy_pixel):
2357 return
~\Anaconda3\lib\site-packages\matplotlib\text.py in _get_position_xy(self, renderer)
1903 "Return the pixel position of the annotated point."
1904 x, y = self.xy
-> 1905 return self._get_xy(renderer, x, y, self.xycoords)
1906
1907 def _check_xy(self, renderer, xy_pixel):
~\Anaconda3\lib\site-packages\matplotlib\text.py in _get_xy(self, renderer, x, y, s)
1757
1758 if s1 == 'data':
-> 1759 x = float(self.convert_xunits(x))
1760 if s2 == 'data':
1761 y = float(self.convert_yunits(y))
TypeError: float() argument must be a string or a number, not 'tuple'
<Figure size 1440x720 with 1 Axes>
and think maybe the error because of the (group by), if I don't (group by) the data by month and year then plot it as days like this format (1901-01-31) it's worked
so I had tried to ungroup by use .reset_index()
but the X label was the indexes of the data and what I want is month/year.
also, I had tried to merge the MultiIndex into one index by use
data.index = ['{}-{}'.format(i, j) for i, j in data.index]
but i got this erorr
ConversionError: Failed to convert value(s) to axis units: '1901-1'
the expected result is similar this but by months/years, not days
Upvotes: 0
Views: 890
Reputation: 23753
Your example DataFrame, ensuring the index is a datetime dtype
#imports excluded
d = {'mean':{'1901-01-31': 0.60847, '1901-02-28': 0.75183, '1901-03-31': 0.05098,
'1901-04-30': 0.22906, '1901-05-31': 0.24698, '1901-11-30': 0.93659,
'1901-12-31': 0.08592, '1902-07-31': 0.7869, '1902-08-31': 0.77402,
'1902-12-31': 0.60236, '1903-01-31': 0.8055, '1903-02-28': 0.6045,
'1903-03-31': 0.99139, '1903-04-30': 0.77478, '1903-05-31': 0.88022,
'1903-06-30': 0.73554},
'count': {'1901-01-31': 3, '1901-02-28': 6, '1901-03-31': 6, '1901-04-30': 2,
'1901-05-31': 4, '1901-11-30': 8, '1901-12-31': 1, '1902-07-31': 0,
'1902-08-31': 9, '1902-12-31': 6, '1903-01-31': 4, '1903-02-28': 7,
'1903-03-31': 2, '1903-04-30': 1, '1903-05-31': 9, '1903-06-30': 1}}
df = pd.DataFrame(d)
df.index = pd.to_datetime(df.index)
#df = df.set_index(pd.to_datetime(df.index))
Axes.annotate's xy
argument expects a tuple of two floats. After grouping by year and month; iterating over the rows produces a ((year,month),float)
tuple because of the multi-index - annotate doesn't like that.
I haven't figured out how to make your groupby solution work - I think it would involve translating/transforming the (year,month)
tuple into a coordinate the plot/axes understands or specifying an x coordinate system that can use that tuple.
Resampling by month, plotting and annotating works
data = df.resample('M').mean()
# or maybe it should be
#data = df.resample('M').agg({'mean':'mean','count':'sum'})
ax = data['mean'].plot()
for x,y,count in zip(data.index,data['mean'],data['count']):
ax.annotate(f'{count:.0f}',xy=(x,y), ha='center',clip_on=True)
# or
for (x,(y,count)) in data.iterrows():
ax.annotate(f'{count:.0f}',xy=(x,y), ha='center',clip_on=True)
plt.show()
plt.close()
There were missing months in the example DataFrame so this plot has discontinuities.
Upvotes: 1