Reputation: 155
I have data that I'd like to plot. I think the best way to do this is with a series of rectangles. I'd like each rectangle to span a width delta_t (each time interval is the same) and a height delta_f (the frequency intervals may differ) and each rectangle's color is given by the log(z). This example seems to have some hints, but I'm not able to put it all together. Here's what I've gotten so far
from matplotlib.collections import PatchCollection
from matplotlib.patches import Rectangle
def make_rectangle(t_min, f_min, delta_t, delta_f, z):
return Rectangle(xy = (t_min, f_min), width = delta_t, height = delta_f, edgecolor = 'k')
timeex=[0,1,2,3,4]
frequencyex=[0,.1,1,10,100]
zex=[[1,1.2,1.1,1.5,1.6], [.01,120,.11,1.6,1.5],
[.1,.12,1.1e-6,15,16], [1,1.2,1.1,1.5,1.6], [.01,120,.11,1.6,1.5]]
tiles = []
for i in range(len(timeex) - 1):
t_min = timeex[i]
f_min = frequencyex[i]
t_max = timeex[i + 1]
f_max = frequencyex[i + 1]
for j in range(len(zex[i])):
rect = make_rectangle(t_min, f_min, t_max - t_min, f_max - f_min, zex[i][j])
tiles.append(rect)
fig = plt.figure()
ax = fig.add_subplot(111)
p = PatchCollection(tiles, cmap=plt.cm.jet)
ax.add_collection(p)
fig.colorbar(p)
fig.show()
This produces the attached image. This plot is lacking in a few ways: the rectangle heights aren't what I would expect. Logscaleing the axis doesm't help. I don't know how to logarithmically color the rectangeles. I'd like to normalize the colorscale between something like 10e-6 and 10.
The end product I'm envisioning looks somewhat like the outputs shown here.
I'm using Python 3.6 and matplotlib 3.3.2.
Edit: the fantastic answer below solved most of my graphing problems. I turned myself around with the loops, but I sorted that out quickly. I appreciate your help.
Upvotes: 1
Views: 512
Reputation: 4629
There are a few things that I added or changed to obtain this:
changed the y-axis scale to log.
manually changed the x- and y-limits so that the rectangles would fit.
specified a norm
for your PatchCollection
so that it knows how to turn values into colors. Without this, you can only use the 0-1 range which is not what you want.
specified the array
of your PatchCollection
so that it knows which values to turn into colors. We store the list of provided zex[i][j]
values for this purpose. No need to provide those values to make_rectangle
(they were unused anyway).
In theory you could calculate automatically the min and max values of the norm as well as the limits of the ax from the data. Here I went with the norm you gave in the OP (1e-6, 10) and manual limits.
# Imports.
import matplotlib.pyplot as plt
from matplotlib.collections import PatchCollection
from matplotlib.patches import Rectangle
from matplotlib.colors import LogNorm
import numpy as np
def make_rectangle(t_min, f_min, delta_t, delta_f):
return Rectangle(xy = (t_min, f_min), width = delta_t, height = delta_f, edgecolor = 'k')
timeex = [0, 1, 2, 3, 4]
frequencyex = [0, 0.1, 1, 10, 100]
zex = [[1, 1.2, 1.1, 1.5, 1.6],
[0.01, 120, 0.11, 1.6, 1.5],
[0.1, 0.12, 1.1e-6, 15, 16],
[1, 1.2, 1.1, 1.5, 1.6],
[0.01, 120, 0.11, 1.6, 1.5]]
tiles = []
values = []
for i in range(len(timeex) - 1):
t_min = timeex[i]
f_min = frequencyex[i]
t_max = timeex[i + 1]
f_max = frequencyex[i + 1]
for j in range(len(zex[i])):
rect = make_rectangle(t_min, f_min, t_max - t_min, f_max - f_min)
tiles.append(rect)
values.append(zex[i][j])
# Create figure and ax.
fig = plt.figure()
ax = fig.add_subplot(111)
# Normalize entry values to 0-1 for the colormap, and add the colorbar.
norm = LogNorm(vmin=1e-6, vmax=10)
p = PatchCollection(tiles, cmap=plt.cm.jet, norm=norm, match_original=True) # You need `match_original=True` otherwise you lose the black edgecolor.
fig.colorbar(p)
ax.add_collection(p)
# Set the "array" of the patch collection which is in turn used to give the appropriate colors.
p.set_array(np.array(values))
# Scale the axis so that the rectangles show properly. This can be done automatically
# from the data of the patches but I leave this to you.
ax.set_yscale("log")
ax.set_xlim(0, 8)
ax.set_ylim(0, 100)
fig.show()
Upvotes: 2