I have the following script:
fig = plt.figure()
fig.set_size_inches(8,7)
ax1 = fig.add_subplot(1,1,1)
### PLOT
for k in my_dict:
x, y = my_dict[k][0], my_dict[k][1]
ax1.plot(x, y, linewidth = 0, marker='o', markersize = 4)
X = np.logspace(0.3, 6.6)
ax1.plot(X, 2*X**(-2), linewidth = 2, c='k')
X = np.logspace(0.3, 7.7)
ax1.plot(X, 3*X**(-1.5), linewidth = 2, c='b')
ax1.set_xscale('log')
ax1.set_yscale('log')
## LEGEND
labels = ['$10^{-1} \, \\Delta_1^*$', '$\\Delta_1^*$',\
'$10^{5/2} \, \\Delta_1^*$', '$10^3 \, \\Delta_1^*$',
'$x^{-2}$', '$x^{-3/2}$']
curves = ax1.get_lines()
legend1 = ax1.legend([curves[0], curves[1], curves[2]],\
[labels[0], labels[1], labels[2]],\
loc=1, ncol=1, fancybox=False, shadow=False,\
framealpha=0.0, markerscale=2, fontsize=25, handletextpad=0.0)
legend2 = ax1.legend([curves[3], curves[4], curves[5]],\
[labels[3], labels[4], labels[5]],\
loc=3, ncol=1, fancybox=False, shadow=False,\
framealpha=0.0, markerscale=2, fontsize=25, handletextpad=0.0)
vp = legend1._legend_box._children[-1]._children[0]
for c in vp._children:
c._children.reverse()
vp.align="right"
ax1.add_artist(legend1)
ax1.add_artist(legend2)
fig.tight_layout()
plt.show()
The result is
The issue: I use handletextpad in the legends, and that is because I need points and text to be very close. However the last two elements in the second legend are not points, but lines. They take more space then points and the text happens to be too close.
I need to keep this distance between text and points while increasing the distance between text and lines in the same legend.
I tried with handletextpad=[0.1, 0.5, 0.5] and similar strategies, but I haven't been able to set individual values of handletextpad.
Another possibility would be to make separate legends and specifically one with only lines. This, however, would force me to manually positioning any legend very carefully and I'd rather not doing it. Also (I don't know if it can help), but I'd rather not replace
plt.plot(x, y, linewidth = 0, markersize = 4)
with
plt.scatter(x, y).
Except for these two caveats, everything is welcome.
Related
plt.plot(x, y, label = name1)
plt.plot(x, y, label = name2)
plt.plot(x, y, label = name3)
plt.show()
How to get the label when I click the line or better if I can get this information directly in the graph window like I get the x and y axis values on bottom right.
The fastest way would be to add a legend to your graph with plt.legend() right before plt.show()
For more interactivity, maybe try bokeh instead of matplotlib.
Not sure exactly what you are asking for but if you want to represent each line with a name, or the series of the x-values, you could use legend() and input a string or series name as label name in the plot-line:
plt.plot(x1, y, label = "name1") # Show the string name1
plt.plot(x2, y, label = x2) # Shows the array x2
plt.legend() # Displays the legends
If you want to add title or labels for the axis you could use:
plt.xlabel('X-axis Label')
plt.ylabel('Y-axis label')
plt.title('Title')
I am not sure if this is what you are looking for? But you can easily name your graphs by using the Legend. the first graph will be the first in your Legendlist. The important code is between the slash :-)
import matplotlib.pyplot as plt
import numpy as np
# Select length of axes and the space between tick labels
xmin, xmax, ymin, ymax = -10, 10, -10, 10
ticks_frequency = 1
# Plot points
fig, ax = plt.subplots(figsize=(10, 10))
#//////////////////////////////////////////////////////////////////////////////
# x range
x = np.arange(-5, 5., 0.025)
# f1
y1 = 3*x+4
f1 = ax.plot(x, y1, lw = 3, alpha = 0.5, color="blue")
# f2
y2 = 1*x+1
f2 = ax.plot(x, y2, lw = 3, alpha = 0.5, color="orange")
# f3
y3 = -2*x+8
f3 = ax.plot(x, y3, lw = 3, alpha = 0.5, color="red")
# legend
ax.legend(["Gerade 1", "Gerade 2", "Gerade 3"])
#//////////////////////////////////////////////////////////////////////////////
# Set identical scales for both axes
ax.set(xlim=(xmin-1, xmax+1), ylim=(ymin-1, ymax+1), aspect='equal')
# Set bottom and left spines as x and y axes of coordinate system
ax.spines['bottom'].set_position('zero')
ax.spines['left'].set_position('zero')
# Remove top and right spines
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
# Create 'x' and 'y' labels placed at the end of the axes
ax.set_xlabel('x', size=14, labelpad=-24, x=1.03)
ax.set_ylabel('y', size=14, labelpad=-21, y=1.02, rotation=0)
# Create custom major ticks to determine position of tick labels
x_ticks = np.arange(xmin, xmax+1, ticks_frequency)
y_ticks = np.arange(ymin, ymax+1, ticks_frequency)
ax.set_xticks(x_ticks[x_ticks != 0])
ax.set_yticks(y_ticks[y_ticks != 0])
# Create minor ticks placed at each integer to enable drawing of minor grid
# lines: note that this has no effect in this example with ticks_frequency=1
ax.set_xticks(np.arange(xmin, xmax+1), minor=True)
ax.set_yticks(np.arange(ymin, ymax+1), minor=True)
# Draw major and minor grid lines
ax.grid(which='both', color='grey', linewidth=1, linestyle='-', alpha=0.2)
# Draw arrows
arrow_fmt = dict(markersize=4, color='black', clip_on=False)
ax.plot((1), (0), marker='>', transform=ax.get_yaxis_transform(), **arrow_fmt)
ax.plot((0), (1), marker='^', transform=ax.get_xaxis_transform(), **arrow_fmt)
plt.show()
I'm trying to plot 3d boxes iteratively using matplot and I want to add a legend for each box
def add_box(...):
# Draw 6 Faces of a box
...
...
xx, xy, xz = np.meshgrid(x_range, y_range, z_range) # X
yy, yx, yz = np.meshgrid(y_range, x_range, z_range) # Y
zx, zz, zy = np.meshgrid(x_range, z_range, y_range) # Z
for i in range(2):
self.ax.plot_wireframe(xx[i], xy[i], xz[i], color=color)
self.ax.plot_surface(xx[i], xy[i], xz[i], color=color, alpha=self.alpha)
self.ax.plot_wireframe(yx[i], yy[i], yz[i], color=color)
self.ax.plot_surface(yx[i], yy[i], yz[i], color=color, alpha=self.alpha)
self.ax.plot_wireframe(zx[i], zy[i], zz[i], color=color)
self.ax.plot_surface(zx[i], zy[i], zz[i], color=color, alpha=self.alpha)
# Creating dummy plot for the legend
fake2Dline = mpl.lines.Line2D([0],[0], linestyle="none", c=color, marker = 'o')
self.fake2Dline.append(fake2Dline)
label_box = 'C1'
self.legend_label.append(label_box)
Calling legend function after looping over boxes done
def draw_legend(self):
self.ax.legend([self.fake2Dline], [self.legend_label], numpoints = 1, bbox_to_anchor=(1.04, 0.5), loc="upper left", fontsize=9)
The plot is working fine but no legend appears with the following error:
A proxy artist may be used instead.
I tried also to modify the dummy plot line to be:
# assign label in lines.Line2D
fake2Dline = mpl.lines.Line2D([0],[0], linestyle="none", c=color, marker = 'o', label='test1')
And then the legend function:
def draw_legend(self):
plt.legend([self.fake2Dline], numpoints = 1, bbox_to_anchor=(1.04, 0.5), loc="upper left", fontsize=9)
but the legend looked like that..
legend takes as 1st and 2nd argument a list of artists and a list of strings, respectively. You were passing lists of lists.
So is must be:
def draw_legend(self):
self.ax.legend(self.fake2Dline, self.legend_label, numpoints = 1, bbox_to_anchor=(1.04, 0.5), loc="upper left", fontsize=9)
PS: to prevent such kind of mistakes it might be better to use the plural form for the variable names (self.fake2Dlines, self.legend_labels)
I would like to have an increasing spacing between legend items instead of a single value (labelspacing). The latter only accepts an int value type, but I want a variable spacing between legend items. Also, I want the markerfacecolor to follow the colormap used when creating the scatter plot.
N = 45
x, y = np.random.rand(2, N)
s = np.random.randint(10, 1000, size=N)
fig, ax = plt.subplots()
scatter = ax.scatter(x, y, c=s, s=s)
cbar = fig.colorbar(scatter,
ax=ax,
label='Size',
fraction=0.1,
pad=0.04)
# produce a legend with a cross section of sizes from the scatter
handles, labels = scatter.legend_elements(prop="sizes", alpha=0.6)
for hd in handles:
hd.set_markeredgewidth(2)
hd.set_markeredgecolor("red")
hd.set_markerfacecolor('blue')
legend2 = ax.legend(
handles[::2], labels[::2], loc="upper right", title="Sizes", labelspacing=1.2
)
plt.show()
I searched StackOverflow and tried some possible methods but without success. Could someone guide how I can achieve the desired output?
I managed to set markerfacecolor as the colormap. But I am still struggling with the variable labelspacing!.
Any help!
N = 45
x, y = np.random.rand(2, N)
s = np.random.randint(10, 1000, size=N)
fig, ax = plt.subplots()
scatter = ax.scatter(x, y, c=s, s=s)
cbar = fig.colorbar(scatter,
ax=ax,
label='Size',
fraction=0.1,
pad=0.04)
# produce a legend with a cross section of sizes from the scatter
handles, labels = scatter.legend_elements(prop="sizes", alpha=0.6)
leg_colrs = [color.get_markerfacecolor() for color in scatter.legend_elements()[0]]
for hd, color in zip(handles, leg_colrs):
hd.set_markeredgewidth(2)
hd.set_markeredgecolor("red")
hd.set_markerfacecolor(color)
legend2 = ax.legend(
handles[::2], labels[::2], loc="upper right", title="Sizes", labelspacing=1.2
)
plt.show()
plt.text(x, y, text) is working when I use single y-axis but it disappears when I apply twinx. Can it be fixed?
fig, ax = plt.figure(figsize=(8,6))
text = "E_rms(test) = {:7.3f}/nE_maxres = {:7.3f}".format(rmse,maxres)
plt.text(0,0,text)
plt.xlabel('data')
ax.set_ylable('PE(eV)')
ax.tick_params(axis='y', labelcolor='b')
if Ltwinx:
ax2 = ax.twinx()
ax2.set_ylabel("Difference(eV)")
ax2.set_tick_params(axis='y', labelcolor='g')
p1 = ax.scatter(range(y), y, c='r', label='true')
p2 = ax.scatter(range(y), h, c='b', label='hypothesis')
if Ltwinx:
p3, = ax2.plot(range(y), diff, c='g', label='difference')
plt.legend([p1,p2],['true value', 'hypothesis'],loc=(0.0, 0.1))
else:
p3, = plt.plot(range(y), diff, c='g', label='difference')
plt.legend([p1,p2,p3],['true value', 'hypothesis', 'difference'],loc=(0.0, 0.1))
plt.show()
This code is an extraction from full code and the belows are figure with two y-axes (1st figure) and single y-axis, where text disappeared in twinx (1st figure). Note y-axis scale is different due to the values of "diff" though p1, p2 figures are same in both figures.
I have misunderstood the manual for matplotlib.pyplot.text.
To use axis coordinate such as (0,0) to (1,1) regardless of y-values, I should add keyword of "transform=ax.transAxes". So
plt.text(0,0,text, transform=ax.transAxes)
is working.
I currently generate my legend with matplotlib this way:
if t==25:
l1,l2 = ax2.plot(x320,vTemp320,'or',x320,vAnaTemp320,'-r')
elif t==50:
l3,l4 = ax2.plot(x320,vTemp320,'ob',x320,vAnaTemp320,'-b')
else:
l5,l6 = ax2.plot(x320,vTemp320,'og',x320,vAnaTemp320,'-g')
plt.legend((l1,l2,l3,l4,l5,l6), ('t=25 Simulation', 't=25 Analytical','t=50 Simulation', 't=50 Analytical','t=500 Simulation', 't=500 Analytical'),
bbox_to_anchor=(-.25, 1), loc=2, borderaxespad=0.,prop={'size':12})
Which somehow works see 1. But I have duplicated information in my legend.
I would prefer to seperate the legend. So that I have different colored lines corresponding to the time t. And a normal line as my Analytical solution an dots for the results of my simulation.
Something like that
--(red line) t = 25
--(blue line) t = 50
--(green line) t = 500
o Simulaton
-- Analytical Solution
Does anyone now how I could achieve this with matplotlib?
You can chose the artists and labels to display in the legend as follows. You'll need to create custom artists for the elements in the legend that are not actually plotted.
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0,10,31)
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
#Plot analytic solution
ax.plot(x,1*x**2, color='r', label="t = 25")
ax.plot(x,2*x**2, color='b', label="t = 50")
ax.plot(x,3*x**2, color='g', label="t = 500")
#Plot simulation
ax.plot(x,1*x**2, color='r', linestyle='', marker='o')
ax.plot(x,2*x**2, color='b', linestyle='', marker='o')
ax.plot(x,3*x**2, color='g', linestyle='', marker='o')
#Get artists and labels for legend and chose which ones to display
handles, labels = ax.get_legend_handles_labels()
display = (0,1,2)
#Create custom artists
simArtist = plt.Line2D((0,1),(0,0), color='k', marker='o', linestyle='')
anyArtist = plt.Line2D((0,1),(0,0), color='k')
#Create legend from custom artist/label lists
ax.legend([handle for i,handle in enumerate(handles) if i in display]+[simArtist,anyArtist],
[label for i,label in enumerate(labels) if i in display]+['Simulation', 'Analytic'])
plt.show()