2

I want to display bars on a time series chart with proper bar widths.

fig,ax1=plt.subplots()

x_nums = list(range(50))
x_dates = pd.date_range(date_from,periods=50,freq='h')
y1 = list(range(50))
y2=y1.copy()
y2.reverse()

ax1.plot(x_nums, y1, color='r',zorder=10,linewidth=5)
# ax1.plot(x_dates, y1, color='r',zorder=10,linewidth=5)

ax2=ax1.twinx()    
ax2.bar(x_nums, y2, color='b',zorder=1,alpha=0.3)
# ax2.bar(x_dates, y2, color='b',zorder=1,alpha=0.3)

when I plot this with x_nums, left image will be shown, if with x_dates, right will be shown.

bar plot


  1. How can I make bars of proper width, ie the same as on image 1? I know I can set ax2.bar(width=.1) or something but I want to generate many charts with different number of ticks and sizes and I just want matplotlib to maintain proper widths as it does if x_nums is the X axis.
    I tried setting witdth = 1/len(x_dates) but that did not work for charts with lots of ticks as it makes the bars too thin.
    [EDIT] answer: upgrade matplotlib to version where width accepts np.timedelta, then
    ax2.bar(x_dates, y2, width=(x_dates[1]-x_dates[0])*0.8)
  1. How can I send ax1 to front? zorder is not working in this case. I still want y1 to be on left side and y2 to be on right side.
    [EDIT] answer: PyPlot move alternative y axis to background

Thank you!

storm_88
  • 92
  • 7
  • https://stackoverflow.com/questions/5902371/matplotlib-bar-chart-with-dates – agent_bean Nov 28 '19 at 13:26
  • 1
    And for the second question: https://stackoverflow.com/questions/46645280/pyplot-move-alternative-y-axis-to-background – TheIdealis Nov 28 '19 at 14:02
  • @rafaelgonzalez seen that one, does not help. The width is fixed in that answer, but I want the width to change based on how any rows of data I am plotting. (narrow for a lot of data, wide for few data) – storm_88 Nov 28 '19 at 14:21
  • Newer versions of matplotlib allow the width to be specified as a timedelta. – Jody Klymak Nov 29 '19 at 14:38
  • @JodyKlymak could you please post some link? Can't find it – storm_88 Dec 02 '19 at 20:12
  • https://github.com/matplotlib/matplotlib/pull/12903 Should be in 3.1, but maybe could use an example? See the changes to the tests. If you come up with an example that you think would help int he future, PRs welcome.... – Jody Klymak Dec 02 '19 at 20:40

1 Answers1

1

You can specify widths as np.timedelta64 objects. Or note that a width of 1.0 is 1 day, so to get 40-minute wide bars, you'd specify width=40/24/60

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

fig,ax1=plt.subplots()
date_from = '2019-01-01'

x_nums = list(range(50))
x_dates = pd.date_range(date_from,periods=50,freq='h')
y1 = np.arange(50)
y2=y1[::-1]

ax1.plot(x_dates, y1, color='r',zorder=10,linewidth=5)
ax2=ax1.twinx()
ax2.bar(x_dates, y2, color='b',zorder=1,alpha=0.3, width=np.timedelta64(40, 'm'))
plt.show()

enter image description here

Jody Klymak
  • 4,979
  • 2
  • 15
  • 31
  • Im on python 3.7 and it fails @ width=np.timedelta64(40, 'm'). width=40/24/60 works but it still is somehow fixed to the scale. I want the bars to be reasonably wide (as if the x axis were numbers) regardles on the scale (seconds, minutes, days, months..). Im looking for a solution that would work if I plot this chart with various frequencies, such as ['h','m','s'] – storm_88 Dec 04 '19 at 15:01
  • 1
    You need to upgrade your matplotlib if you want to use timedelta. WRT different resolutions you’ll have to figure that out yourself. Matplotlib bar widths are in data units and the unit of time is 1 day. The default is 0.8 days. There is no automated facility for making the widths a certain fraction of axes size. If it were me, I’d strongly consider using step instead of bar. – Jody Klymak Dec 04 '19 at 16:22
  • thanks for all the help. I sort of expected there would be some automated facility for making the bar widths look "nice" under all conditions (as there is with value based x axis) – storm_88 Dec 05 '19 at 08:21
  • and since your solution works after upgrading matplotlib, I can do something like width=(x_dates[1]-x_dates[0])*0.8 which works with any frequency – storm_88 Dec 05 '19 at 08:28