matplotlib: Draw ontop of image - keep colorbar range - matplotlib

I want to draw into the same figure while keeping the colorbar and it's range. How to do that?
Sounds easy, but apparantly it's not:
cbar = plt.colorbar()
cbar.set_clim(vmin=-1.0, vmax=1.0)
plt.show(block=False)
for i in range(num_maps):
plt.imshow(img) # img to draw the data ontop of
data = all_data[:,:,i]
plt.imshow(data, alpha=0.5) # data to draw
plt.pause(0.5)
I assume because I first plot the image my colobar range gets destroyed as it has values between [0..255]. Any idea how to suppress that behaviour?

Looks like I have to call colobar() after setting the range...
plt.set_clim(vmin=-1.0, vmax=1.0) # <--- swaped
plt.colorbar() # <--- swaped
plt.show(block=False)
for i in range(num_maps):
plt.imshow(img) # img to draw the data ontop of
data = all_data[:,:,i]
# imshow resets the colorbar -> again specify vmin and vmax
plt.imshow(data, alpha=0.5, vmin=-1.0, vmax=1.0)
plt.pause(0.5)
now it works...

Related

How to make the plot's shape round?

I have created a plot, which is working just fine.
But I really want to change its shape to a circle.
This is my current plotting code:
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(5,5), dpi=300)
ax = fig.add_axes([0,0,1,1])
ax.plot(30, 80, marker="o", markersize=20, markeredgecolor="#ed6033", markerfacecolor="#ed6033")
ax.spines['left'].set_position('center')
ax.spines['bottom'].set_position('center')
ax.spines['right'].set_color('none')
ax.spines['top'].set_color('none')
ax.xaxis.set_ticks_position('bottom')
ax.yaxis.set_ticks_position('left')
ax.set_facecolor('#8cc9e2')
ax.margins(0.1)
plt.setp(ax.get_xticklabels()[4], visible=False)
plt.xlim(10, 90)
plt.ylim(10, 90)
plt.grid(color='white')
plt.show()
and this is the output I get:
eventually, this is my desired output:
You can clip the path of artists including the background patch using the path of another artist.
Add this snippet before the plt.show() call:
clip_path = plt.Circle(
(0.5, 0.5), 0.5, transform=ax.transAxes, # circle coordinates defined in axes fractions
fill=None, linewidth=0 # makes circle invisible
)
ax.add_patch(clip_path)
ax.patch.set_clip_path(clip_path)

No Plotting all values of X in xaxis and why there is the background grey?

I would like to get a plot with less data on xaxis. I have this very simple script. I put a 'range' for xaxis. Furthemore I would like that my background was white with contours black and not grey (see figure). How can I do?
import matplotlib.pyplot as plt
plt.figure()
# Increase the plot size and font size.
plt.rcParams["figure.figsize"] = (60,30)
plt.xticks(fontsize=40)
plt.yticks(fontsize=40)
plt.grid(True, color='gray', linestyle='dashed', linewidth=0.5, axis='y')
# Plot the learning curve.
plt.plot(df_stats['Training Loss'], color='b', marker='.', linestyle='solid', mec='b', markersize=24, markerfacecolor='white', label="Training", linewidth=7)
plt.plot(df_stats['Valid. Loss'], color='g', marker='.', linestyle='solid', mec='b', markersize=24, markerfacecolor='white',label="Validation", linewidth=7)
# Label the plot.
plt.title("Training & Validation Loss",fontsize=60)
plt.xlabel("Epoch", fontsize=52)
plt.ylabel("Loss", fontsize=52)
plt.legend(fontsize=50)
plt.xticks(list(range(1, 72)))
plt.show()
To set the X-axis ticks to a lower frequency, you will need to change the xticks to a lower frequency. One way to do this using numpy.arange().
Regarding the background color, the default is white. But, if for reason it is not, you can set it to white using the plot and axis facecolor() to white explicitly.
The code below is the updated version with these changes. Note that I used some dummy data to demonstrate the same.
Code
df_stats= pd.DataFrame(columns=['Training Loss', 'Valid. Loss'])
df_stats['Training Loss'] = list(range(1,72))
df_stats['Valid. Loss'] = df_stats['Training Loss'] * 2.1
import matplotlib.pyplot as plt
plt.figure()
# Background color of outer area
plt.figure(facecolor='white')
# Background color of the plot area
ax = plt.axes()
ax.set_facecolor("white")
# Increase the plot size and font size.
plt.rcParams["figure.figsize"] = (60,30)
plt.xticks(fontsize=40)
plt.yticks(fontsize=40)
plt.grid(True, color='gray', linestyle='dashed', linewidth=0.5, axis='y')
# Plot the learning curve.
plt.plot(df_stats['Training Loss'], color='b', marker='.', linestyle='solid', mec='b', markersize=24, markerfacecolor='white', label="Training", linewidth=7)
plt.plot(df_stats['Valid. Loss'], color='g', marker='.', linestyle='solid', mec='b', markersize=24, markerfacecolor='white',label="Validation", linewidth=7)
# Label the plot.
plt.title("Training & Validation Loss",fontsize=60)
plt.xlabel("Epoch", fontsize=52)
plt.ylabel("Loss", fontsize=52)
plt.legend(fontsize=50)
plt.xticks(list(np.arange(1, 72, 9)))
plt.show()
Output plot

Matplotlib subplots size bigger than figure size

I discovered Matplotlib and I have a rendering problem. The 5 subplot I create step on each other, the xaxis labels and titles are behind the other plot.
Here is a part of the code I use :
fig, axs = plt.subplots(5,figsize=(8,25))
axs[0].plot(array_system_index, array_system_value, label="System", color="red", linewidth=1)
axs[0].set(xlabel="time", ylabel="Latency (µs)", yscale="log", title="System")
axs[0].axis([0, len(array_system_value), 1, 10000])
axs[1].plot(array_core0_index, array_core0_value, label="core 0", color="green", linewidth=1)
axs[1].set(xlabel="Time", ylabel="Latency (µs)", yscale="log", title="Core1")
axs[1].axis([0, len(array_core0_value), 1, 10000])
...
fig.tight_layout()
plt.show()
# fig.set_size_inches((15, 8), forward=False) # Break the png file
fig.savefig("my_graph.png", dpi=500)
Here is the result :
Graph
Do you know how can I increase the size of the figure itself ?
I tried to it after the subplot but it doesn't work and break the saved .png.

subplots_adjust moves axes unpredictably?

I'm working on a python module that creates a matplotlib figure with an on_resize listener. The listener forces the height of the lower axes to a specific number of pixels (rather than scaling relative to figure size). It works. However, if (in matplotlib interactive mode) after creating the plot the user calls fig.subplots_adjust() it messes up subplot sizes. Here's a radically simplified version of what the module does:
import matplotlib.pyplot as plt
plt.ion()
def make_plot():
fig = plt.figure()
gs = plt.GridSpec(10, 1, figure=fig)
ax_upper = fig.add_subplot(gs[:-1])
ax_lower = fig.add_subplot(gs[-1])
ax_upper.plot([0, 1])
ax_lower.plot([0, 1])
fig.canvas.mpl_connect('resize_event', on_resize)
return fig
def on_resize(event):
fig = event.canvas.figure
# get the current position
ax_lower_pos = list(fig.axes[1].get_position().bounds) # L,B,W,H
# compute desired height in figure-relative coords
desired_height_px = 40
xform = fig.transFigure.inverted()
desired_height_rel = xform.transform([0, desired_height_px])[1]
# set the new height
ax_lower_pos[-1] = desired_height_rel
fig.axes[1].set_position(ax_lower_pos)
# adjust ax_upper accordingly
ax_lower_top = fig.axes[1].get_position().extents[-1] # L,B,R,T
ax_upper_pos = list(fig.axes[0].get_position().bounds) # L,B,W,H
# new bottom
new_upper_bottom = ax_lower_top + desired_height_rel
ax_upper_pos[1] = new_upper_bottom
# new height
ax_upper_top = fig.axes[0].get_position().extents[-1] # L,B,R,T
new_upper_height = ax_upper_top - new_upper_bottom
ax_upper_pos[-1] = new_upper_height
# set the new position
fig.axes[0].set_position(ax_upper_pos)
fig.canvas.draw()
Here's the output if the user calls fig = make_plot():
Now if the user calls fig.subplots_adjust, the bottom axis is squished and the space between bottom and top axes is even more squished (the on_resize listener had set them both to 40px):
fig.subplots_adjust(top=0.7)
At this point, grabbing the corner of the window and dragging even a tiny bit is enough to trigger the on_resize listener and restore what I want (fixed pixel height for bottom axes and space between axes) while keeping the newly-added wide top margin intact:
How can I get that result without having to manually trigger a resize event? As far as I can tell, subplots_adjust does not fire off any events that I could listen for.
I think the problem lies in ax.update_params() updating the axes position with a figbox taken from the underlying subplotspec (which as far as I can tell doesn't get updated after initial figure creation?). (note: update_params is called from within subplots_adjust, see here).
The underlying problem seems to be to make an axes with a specific height in pixels. An easy solution to this is to use mpl_toolkits.axes_grid1's make_axes_locatable.
This allows to get rid of any callback and hence of the complete problem of the race condition in the events.
A note: The plot seems to be part of a bigger library. Since it is always nice not to patronize the users of such packages, one would usually allow them to specify the axes to plot to, such that they can put the plot into a bigger figure with other elements. The below solution makes this particularly easy.
Of course, also calling plt.subplots_adjust is still possible at any time.
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1.axes_divider import make_axes_locatable
desired_height_px = 40 #pixel
def make_plot(ax=None):
if not ax:
fig, ax = plt.subplots()
else:
fig = ax.figure
div = make_axes_locatable(ax)
cax = div.append_axes("bottom", desired_height_px/fig.dpi, pad=0.25)
sc1 = ax.scatter([2,1,3], [2,3,1], c=[1,2,3])
sc2 = cax.scatter([3,2,1],[2,3,1], c=[3,1,2])
return fig, ax, cax, (sc1, sc2)
fig, (ax1, ax2) = plt.subplots(1,2)
make_plot(ax=ax1)
#user plot on ax2
ax2.plot([1,3])
fig.subplots_adjust(top=0.7)
plt.show()

How do I add colour code to Matplotlib legend

I'm trying to add a legend to a matplotlib radar/polar graph. I am very new to matplotlib so please excuse the code. I also expect this is very simple but I've been at it an hour and got nowhere.
I have the following which produces a list of labels in the bottom left corner but whenever I try to add handles to give the color representing the label I lose the legend.
# Set color of axes
plt.rc('axes', linewidth=0.5, edgecolor="#888888")
# Create polar plot
ax = plt.subplot(111, polar=True)
# Set clockwise rotation. That is:
ax.set_theta_offset(pi / 2)
ax.set_theta_direction(-1)
# Set position of y-labels
ax.set_rlabel_position(0)
# Set color and linestyle of grid
ax.xaxis.grid(True, color="#888888", linestyle='solid', linewidth=0.5)
ax.yaxis.grid(True, color="#888888", linestyle='solid', linewidth=0.5)
# Plot data
ax.plot(x_as, values, linewidth=0, linestyle='solid', zorder=3)
# Fill area
ax.fill(x_as, values, 'r', alpha=0.3)
plt.legend(labels=[self.get_object().name], loc=(-.42,-.13))
if not self.get_object().subscription is None:
if self.get_object().subscription.benchmark:
bx = plt.subplot(111, polar=True)
bx.plot(x_as, baseline, linewidth=0, linestyle='solid', zorder=3)
bx.fill(x_as, baseline, 'b', alpha=0.3)
plt.legend(labels=[self.get_object().name, 'Benchmark'], loc=(-.42,-.13))
I believe I need
plt.lengend(handles=[some list], labels=[self.get_object().name, 'Benchmark'], loc=(-.42,-.13))
I do not understand what the list of handles should be and I've tried a number of things, including [ax, bx], [ax.plt(), bx.plt()], ['r', 'b']
From the documentation:
handles : sequence of Artist, optional
A list of Artists (lines, patches) to be added to the legend. Use this together with labels, if you need full control on what is shown
in the legend and the automatic mechanism described above is not
sufficient.
The length of handles and labels should be the same in this case. If they are not, they are truncated to the smaller length.
plt.plot returns a list a line2D objects which is what you need to pass to plt.legend(). Therefore a simplified example is as follows:
labels = ["Line 1", "Line 2"]
lines1, = plt.plot([1,2,3])
lines2, = plt.plot([3,2,1])
handles = [lines1, lines2]
plt.legend(handles, labels)
plt.show()