I was able to add a fill of a triangle onto my plot by following the format:
ax.fill([x1, x2, x3], [y1, y2, y3], 'r', alpha = 0.5)
But when I want to reuse the graph to show a different dataset, I cannot seem to be able to remove the fill object. My plot contains markers, lines and a single fill. What would be the best way to remove this fill?
Currently, when I want to reset my graph, I use:
EraseMarkers() # To remove markers
for i in ax.get_lines():
i.remove() # To remove lines
I tried use matplotlib's ax.cla() function, but this doesn't satisfy my needs as it clears the entirety of my plot in which I would like to keep the image that I set as a background and other settings. Here is the code for setting up my plot:
fig = Figure(figsize = (9, 3.8), dpi = 100)
img = plt.imread('Rink2.png')
ax = fig.add_subplot(111)
img = ax.imshow(img, extent=[-100, 100, -42.5, 42.5])
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
ax.axis('off')
I'd love to know an easier way to remove all plotted objects (Markers, lines & fill) without erasing my background and settings. Or just a way to remove the fill.
It is possible to remove an element arbitrary element A from a plot by using A.remove().
To do that, when you call ax.fill you can store the return of this function to a variable, as
filled = ax.fill([x1, x2, x3], [y1, y2, y3], 'r', alpha = 0.5)
to remove it you can than use:
for f in filled:
f.remove()
see the minimum example below:
fig = plt.Figure(figsize=(10,10))
ax = fig.add_subplot(111)
x1, x2, x3 = 1, 2, 3
y1, y2, y3 = 0, 2, 0
fill = ax.fill([x1, x2, x3], [y1, y2, y3], 'r', alpha = 0.5)
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
fig.savefig('test1.png')
for f in fill:
f.remove()
fig.savefig('test2.png')
test1.png:
test2.png:
PS: when adding the figure, is generally best to use the built-in constructors such as plt.subplots or plt.figure instead of accessing the class directly as in Figure
Related
I am using canvas.mpl_connect mouse click listener for my e.g. 100x100 contourf plot with xlim from 0 to 99. Doing so I get e.g [x,y]= 10,20 as desired. However I have to display a second x-axis with different coordinates (e.g. xlim from 0.01 to 1) but I dont want event.xdata to return the coordinates in the style of the second axis. Is there a possibility to do so?
You could use the transformations in matplotlib. You would want to convert from the data-coordinates in ax2 to display coordinates (which are universal between the two axes) and then into data coordinates for ax1. Helpfully, you can combine transformations.
For example:
import matplotlib.pyplot as plt
import numpy as np
fig, ax1 = plt.subplots(1)
# First axis, with x-values going from 0 to 100
x1 = np.linspace(0, 100, 101)
y1 = np.sin(2 * np.pi * x1 / max(x1))
ax1.plot(x1, y1, 'b.-')
# Second axis, x values going from 0 to 1
ax2 = ax1.twiny()
x2 = np.linspace(0, 1, 11)
y2 = np.cos(2 * np.pi * x2 / max(x2))
ax2.plot(x2, y2, 'r.-')
# Create a combined transform from ax2 data to ax1 data
combinedTransform = ax2.transData + ax1.transData.inverted()
def onclick(event):
# event has x and y in data coordinates for ax2:
pt_data2 = (event.xdata, event.ydata)
# Convert them into data coordinates for ax1:
pt_data1 = combinedTransform.transform(pt_data2)
# ...
cid = fig.canvas.mpl_connect('button_press_event', onclick)
It feels like there would be a nicer way (somehow tell the event listener which axis you want the xdata and ydata to be valid for, but I don't know it. Sorry)
thanks, I implemented something like this as well. The problem is that there is actually no direct linear transformation behind my data. I solved the issue by just calling the second axis in a function after I finished setting the marker or choosing points. It's not beautiful but should be fine for a Master's thesis!
I'm plotting multiple figures of the same variable on one plot using matplotlib library. I'm not looking for a colorbar for subplots, which is the dominant search material. I plot multiple scatters, but the colorbar is only set to the values of the last scatter I plot.
Here is the part of the code:
plt.scatter(x1, y1, c=z1,cmap='viridis_r',marker='s')
plt.scatter(x2, y2, c=z2,cmap='viridis_r',marker='o')
plt.scatter(x3, y3, c=z3,cmap='viridis_r',marker='^')
plt.colorbar().set_label('Wind speed',rotation=270)
It requires a bit of extra work:
You have to get the minimum and maximum of the cs (the colorbar values)
You have to clim each scatter plot
First the minimum and maximum:
zs = np.concatenate([z1, z2, z3], axis=0)
min_, max_ = zs.min(), zs.max()
Then the scatter plots with clim:
plt.scatter(x1, y1, c=z1,cmap='viridis_r',marker='s')
plt.clim(min_, max_)
plt.scatter(x2, y2, c=z2,cmap='viridis_r',marker='o')
plt.clim(min_, max_)
plt.scatter(x3, y3, c=z3,cmap='viridis_r',marker='^')
plt.clim(min_, max_)
plt.colorbar().set_label('Wind speed',rotation=270)
For a very simple dataset:
x1, x2, x3 = [1,2,3], [2,3,4], [3,4,5]
y1 = y2 = y3 = [1, 2, 3]
z1, z2, z3 = [1,2,3], [4,5,6], [7,8,9]
scatter has a norm argument. Using the same norm for all scatters ensures that the colorbar produced by any of the plots (hence also the last) is the same for all scatter plots.
The norm can be a Normalize instance, to which minimum and maximum value are set and which produces a linear scaling in between. Of course you can also use any other norm provided in matplotlib.colors like PowerNorm, LogNorm, etc.
mini, maxi = 0, 2 # or use different method to determine the minimum and maximum to use
norm = plt.Normalize(mini, maxi)
plt.scatter(x1, y1, c=z1,cmap='viridis_r',marker='s', norm=norm)
plt.scatter(x2, y2, c=z2,cmap='viridis_r',marker='o', norm=norm)
plt.scatter(x3, y3, c=z3,cmap='viridis_r',marker='^', norm=norm)
plt.colorbar().set_label('Wind speed',rotation=270)
This is related to this and this question.
I have a sequence of figures and subplots that have all very similar settings. However, I can't seem to find a way to set them all at the same time. Here's a simplified version (I generally work with more instances):
fspec = plt.figure(1)
spC = fspec.add_subplot(211)
spC.set_title('Surface concentrations')
spC.grid(True)
spC.set_ylim(1e-3, None)
spT = fspec.add_subplot(212, sharex=spC)
spT.set_title('Surface temperature')
spT.grid(True)
spT.set_ylim(1e-3, None)
fener = plt.figure(2)
enC = fener.add_subplot(211)
enC.set_title('Surface concentrations')
enC.grid(True)
enC.set_ylim(1e-3, None)
enT = fener.add_subplot(212, sharex=enC)
enT.set_title('Surface temperature')
enT.grid(True)
enT.set_ylim(1e-3, None)
I feel like there should be a way to apply something to every subplot open, or AT LEAST every subplot in a figure. Something like
fspec.set_global_grid(True)
fspec.set_global_ylim(1e-3, None)
But I can't find it.
I took a look at some of the previous but none of them seem to work for me, since I don't work with one figure or axis at a time, I work with all of them kind of at the same time.
Cheers.
Some settings concerning mostly the style of the figure can be set globally using the matplotlib rc parameters.
For example, setting the grid on throughout the script, put
plt.rcParams['axes.grid'] = True
at the beginning of your file (after the imports).
Other things like axis limits are really specific to the plot itself, and there is no global parameter for that. But you can still go the way, as outlined in the linked questions, i.e. write your own function that does most of the stuff you need.
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['axes.grid'] = True
def plotfunction(fig, x1, y1, x2, y2,
title1 = 'Surface concentrations',
title2 = 'Surface temperature', **kwargs ):
ax = fig.add_subplot(211)
ax2 = fig.add_subplot(212, sharex=ax)
ax.set_title(title1)
ax2.set_title(title2)
ax.set_ylim(1e-3, None)
ax2.set_ylim(1e-3, None)
ax.plot(x1, y1, **kwargs)
ax2.plot(x2, y2, **kwargs)
fspec = plt.figure(1)
fener = plt.figure(2)
x1, y1, x2, y2 = np.loadtxt("spectrum.txt", unpack=True)
plotfunction(fspec,x1, y1, x2, y2)
x1, y1, x2, y2 = np.loadtxt("energy.txt", unpack=True)
plotfunction(fener,x1, y1, x2, y2, linewidth=3)
I have this code:
pyplot.figure()
pyplot.suptitle('Finding the roots of the equation: z^4 - 1 = 0')
ticks = [-2.0, -1.0, 0.0, 1.0, 2.0]
pyplot.subplot(221)
pyplot.imshow(roots, extent=(x0, x1, y0, y1))
pyplot.xticks(ticks)
pyplot.yticks(ticks)
pyplot.subplot(222)
pyplot.imshow(iterations, extent=(x0, x1, y0, y1))
pyplot.colorbar(orientation='vertical')
pyplot.xticks(ticks)
pyplot.yticks(ticks)
pyplot.subplot(223)
pyplot.imshow(roots_zoom, extent=(x2, x3, y2, y3))
pyplot.subplot(224)
pyplot.imshow(iterations_zoom, extent=(x2, x3, y2, y3))
pyplot.colorbar(orientation='vertical')
pyplot.show()
I'm plotting four arrays of numbers as images in a 2x2 figure of subplots. I want the same x label and y label for each. How do I create one x label that is centered below the two bottom plots and one y label that is centered to the left of the left two plots?
Also, how do I change the specified tick marks to floats? I've created them as [-2.0 ... 2.0] but they are shown in the figure as -2, -1, 0 etc.
Probably the easiest way to do this is with Figure.text
from matplotlib import pyplot as plt
fig = plt.gcf()
fig.text(.5, .1, 'xlabel', ha='center')
fig.text(.1, .5, 'ylabel', ha='center', rotation='vertical')
You will need to tweak the location of the text to put it exactly where you want.
You will want to look into Formatters to control how your tick labels are displayed. You can get one decimal place floats with
ax = plt.gca()
ax.xaxis.set_major_formatter(matplotlib.ticker.FormatStrFormatter('%.1f'))
I have a bar plot consisting in 3 stacked series and 5 bars. I want to highlight one single bar (all 3 stacked elements) by changing the width of the line.
I'm drawing the bars with the following command:
mybar = ax.bar(x,Y[:,i],bottom=x,color=colors[i],edgecolor='none',width=wi,linewidth = 0)
bar_handles = np.append(bar_handles,mybar)
I have handle for the bar I want to change stored in the array bar_handles, is there a way to change a bar's edgecolor and linewidth property after it has been drawn?
ax.bar returns a Container of artists; each "artist" is a Rectangle with set_linewidth and set_edgecolor methods.
To change the settings of, say, the second bar in mybar, you could do this:
mybar[1].set_linewidth(4)
mybar[1].set_edgecolor('r')
Here's a script that shows how this could be used to change the linewidth of a stack:
import numpy as np
import matplotlib.pyplot as plt
x = np.array([1,2,3])
y1 = np.array([3,2.5,1])
y2 = np.array([4,3,2])
y3 = np.array([1,4,1])
width = 0.5
handles = []
b1 = plt.bar(x, y1, color='#2040D0', width=width, linewidth=0)
handles.append(b1)
b2 = plt.bar(x, y2, bottom=y1, color='#60A0D0', width=width, linewidth=0)
handles.append(b2)
b3 = plt.bar(x, y3, bottom=y1+y2, color='#A0D0D0', width=width, linewidth=0)
handles.append(b3)
# Highlight the middle stack.
for b in handles:
b[1].set_linewidth(3)
plt.xlim(x[0]-0.5*width, x[-1]+1.5*width)
plt.xticks(x+0.5*width, ['A', 'B', 'C'])
plt.show()
This script creates the following bar chart:
I ended up doing it like this:
ax.axvspan(X1,
X1+wi,
ymax=Y2,
facecolor='none',
edgecolor='black',
linewidth=2)
…where
X1 = bar_handles[startBlock].get_x()
wi = bar_handles[startBlock].get_width()
Y2 = ax.transLimits.transform((0,bar_handles[startBlock].get_height()))[1]
This produces an edge over my bar — including all the elements within — without the horizontal like between the elements.