2

I folks,

Consider the following example

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

fig, (ax1,ax2) = plt.subplots(2,1)
dates = pd.date_range("2018-01-01","2019-01-01",freq = "1d")
x = pd.DataFrame(index = dates, data = np.linspace(0,1,len(dates)) )
x.plot(ax=ax1)
y = np.random.random([len(dates),100]) * x.values 
ax2.pcolormesh(range(len(x)), np.linspace(-1,1,100), y.T)
plt.show()

Plot

At this point, I would like the both axis (ax1,ax2) to share the x-axis, i.e. displaying proper pandas dates on the second axis. sharex=True does not seem to work. How can I achieve that? I tried different possibilities which did not work out.

Edit: Since the pandas date formatting is superior to the native matplotlib formatting, please provide me with a solution where pandas date formatting is used (for instance, zooming with an interactive environment works much better with pandas date formatting). Thanks You!

varantir
  • 6,624
  • 6
  • 36
  • 57

2 Answers2

3

One way to do it would be to do all the plotting with matplotlib, this way there are no problems with the different time formats being used:

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

fig, (ax1,ax2) = plt.subplots(2,1, sharex='col')
dates = pd.date_range("2018-01-01","2019-01-01",freq = "1d")
x = pd.DataFrame(index = dates, data = np.linspace(0,1,len(dates)) )

#x.plot(ax=ax1)
ax1.plot(x.index, x.values)


y = np.random.random([len(dates),100]) * x.values
ax2.pcolormesh(x.index, np.linspace(-1,1,100), y.T)

fig.tight_layout()
plt.show()

This gives the following plot:

result of the above code

Thomas Kühn
  • 9,412
  • 3
  • 47
  • 63
1

What seems to work fine is to first plot the same line into the axes that should host the image, then plot the image, then remove the line again. What this does is that it tells pandas to apply its locators and formatters to that axes; they will stay after removing the line.

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

fig, (ax1,ax2) = plt.subplots(2,1, sharex=True)
dates = pd.date_range("2018-01-01","2019-01-01",freq = "1d")
x = pd.DataFrame(index = dates, data = np.linspace(0,1,len(dates)) )
x.plot(ax=ax1)
y = np.random.random([len(dates),100]) * x.values 

x.plot(ax=ax2, legend=False)
ax2.pcolormesh(dates, np.linspace(-1,1,100), y.T)
ax2.lines[0].remove()

plt.show()

enter image description here

Note that there may be caveats of this solution when zooming or panning. Consider it more like a hack and use it as long as it works, but don't blame anyone once it doesn't.

ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712
  • Something else that came to mind would be to overlay the subplot with a second Axes instance as it is done in the first example of [this answer](https://stackoverflow.com/a/7769497/2454357). One could then make the spines of one axes invisible and plot the pcolormesh in it. The second axes could then be used in the way you do it here to get the desired xtick labels. – Thomas Kühn Feb 14 '19 at 06:44