Tobias M.
Tobias M.

Reputation: 146

matplotlib diagram background depending on data

I'm trying to plot some data and want to have a colored background depending on data.

In the following sample I want to have data1 and data2 on the left yaxis and data3 on right yaxis. This is working. But additionally I tried to colorize the background depending on data3.

How do I need to format the data to get it working?

import matplotlib.pyplot as plt
from datetime import datetime as dt
import matplotlib.dates as md

fig, ax1 = plt.subplots(constrained_layout=True)

data1 = [51.2, 51.2, 51.2, 50.7, 50.7, 50.5, 50.4, 50.7, 50.6]
data2 = [46.5, 46.1, 46.2, 46.3, 46.4, 46.3, 46.2, 46.1, 45.5]
data3 = [ 0.0,  1.0,  1.0,  1.0,  0.0,  0.0,  0.0,  0.0,  0.0]

timestamps = [1524614516, 1524615134, 1524615587, 1524615910, 1524616235, 1524616559, 1524616866, 1524617189, 1524617511]
timestamps_ = [dt.utcfromtimestamp(x) for x in timestamps]

for data in (data1,data2):
    ax1.plot(timestamps_, data, marker='.', linestyle='-')
ax1.set_ylabel("degC")

ax2 = ax1.twinx()
ax2.plot(timestamps_, data3, marker='x', linestyle='-')
ax2.pcolor(ax2.get_xlim(), ax2.get_ylim(), zip(timestamps_, data3), cmap='RdGn', alpha=0.3) 
ax2.set_ylabel("ON OFF")       

ax1.set_title("Mytitle")
for tick in ax1.xaxis.get_major_ticks():
    tick.label1.set_horizontalalignment('right')
    tick.label1.set_rotation(35)
xfmt = md.DateFormatter('%Y-%m-%d %H:%M:%S')
ax1.xaxis.set_major_formatter(xfmt)

plt.show()

Error message:

Traceback (most recent call last):
  File "/home/tobias/workspace/python_pyplot_test/main.py", line 25, in <module>
    ax2.pcolor(ax2.get_xlim(), ax2.get_ylim(), zip(timestamps_, data3), cmap='RdGn', alpha=0.3) 
  File "/usr/local/lib/python2.7/dist-packages/matplotlib/__init__.py", line 1855, in inner
    return func(ax, *args, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/matplotlib/axes/_axes.py", line 5732, in pcolor
    X, Y, C = self._pcolorargs('pcolor', *args, allmatch=False)
  File "/usr/local/lib/python2.7/dist-packages/matplotlib/axes/_axes.py", line 5576, in _pcolorargs
C.shape, Nx, Ny, funcname))
TypeError: Dimensions of C (9, 2) are incompatible with X (2) and/or Y (2); see help(pcolor)

Upvotes: 1

Views: 445

Answers (2)

unutbu
unutbu

Reputation: 879331

Another option is to use axvspans:

enter image description here

One difference between using axvspan and pcolor is that the vertical span (rectangles) drawn by axvspan are unbounded in the y-direction while the pcolor rectangles are not. So if you use the zoom button to resize the plot, the axvspan rectangles will stretch to infinity (roughly speaking) while zooming out the pcolor rectangles will expose white areas. It's not a big deal, just thought you'd like to know.


Also note that if the vertical spans start at the first data point and extend to the next data point, then the last value in data3 never gets used. (Nine data points make eight vertical spans). If, however, you center the vertical spans around the data points -- so each data point is in the center of a span, then all 9 values in data3 can be used.

Uncomment the commented code below (and comment-out the current definition of timestamps_left and timestamps_right) to see the difference.


import numpy as np
import matplotlib.pyplot as plt
from datetime import datetime as dt
import matplotlib.dates as md

def topydates(timestamps):
    return [dt.utcfromtimestamp(x) for x in timestamps]
fig, ax1 = plt.subplots(constrained_layout=True)

data1 = [51.2, 51.2, 51.2, 50.7, 50.7, 50.5, 50.4, 50.7, 50.6]
data2 = [46.5, 46.1, 46.2, 46.3, 46.4, 46.3, 46.2, 46.1, 45.5]
data3 = [ 0.0,  1.0,  1.0,  1.0,  0.0,  0.0,  0.0,  0.0,  1.0]

timestamps = np.array([1524614516, 1524615134, 1524615587, 1524615910,
                       1524616235, 1524616559, 1524616866, 1524617189, 1524617511])
timestamps_ = topydates(timestamps)

for data in (data1,data2):
    ax1.plot(timestamps_, data, marker='.', linestyle='-')
ax1.set_ylabel("degC")

ax2 = ax1.twinx()
ax2.plot(timestamps_, data3, marker='x', linestyle='-')

# if you want the axvspans to be centered around the data points
# widths = np.diff(timestamps)
# midpoints = timestamps[:-1] + widths/2.0
# timestamps_left = topydates(np.r_[timestamps[0]-widths[0]/2, midpoints])
# timestamps_right = topydates(np.r_[midpoints, timestamps[-1] + widths[-1]/2.0])
# if you uncomment the code above, then comment-out the line below:
timestamps_left, timestamps_right = timestamps_[:-1], timestamps_[1:]

cmap = plt.get_cmap('RdYlGn')
for left, right, val in zip(timestamps_left, timestamps_right, data3):
    print(left, right)
    color = cmap(val)
    ax2.axvspan(left, right, facecolor=color, alpha=0.3)

ax2.set_ylabel("ON OFF")       

ax1.set_title("Mytitle")
for tick in ax1.xaxis.get_major_ticks():
    tick.label1.set_horizontalalignment('right')
    tick.label1.set_rotation(35)
xfmt = md.DateFormatter('%Y-%m-%d %H:%M:%S')
ax1.xaxis.set_major_formatter(xfmt)

plt.show()

Upvotes: 0

anishtain4
anishtain4

Reputation: 2402

Here's a minimal solution to what you want:

import matplotlib.pyplot as plt
from datetime import datetime as dt
import matplotlib.dates as md
import numpy as np


data3 = np.array([ 0.0,  1.0,  1.0,  1.0,  0.0,  0.0,  0.0,  0.0,  0.0])

x=np.arange(9)
xp,yp=np.meshgrid(x,data3)
xp=xp.astype(float)-0.5
bgcolor=np.ones(xp.shape)*data3[None,:]

plt.pcolor(xp,yp,bgcolor) 
plt.plot(x, data3, marker='x', linestyle='-')

I took out the second axis and all the tick stuff as they were not related to the problem itself.

Upvotes: 1

Related Questions