Legend picking - Enable picking on the legend to toggle the original line on and off - matplotlib

I try to reproduce following example, but however I fail.
So what I would like to achieve is following: I have mutiple dataframes and these should be plotted individually - For this I already created a UI. I would like to click on the items on within the legend to switch on or off the line. To achieve this I used the following example
import numpy as np
import matplotlib.pyplot as plt
t = np.arange(0.0, 0.2, 0.1)
y1 = 2*np.sin(2*np.pi*t)
y2 = 4*np.sin(2*np.pi*2*t)
fig, ax = plt.subplots()
ax.set_title('Click on legend line to toggle line on/off')
line1, = ax.plot(t, y1, lw=2, label='1 HZ')
line2, = ax.plot(t, y2, lw=2, label='2 HZ')
leg = ax.legend(loc='upper left', fancybox=True, shadow=True)
leg.get_frame().set_alpha(0.4)
# we will set up a dict mapping legend line to orig line, and enable
# picking on the legend line
lines = [line1, line2]
lined = dict()
for legline, origline in zip(leg.get_lines(), lines):
legline.set_picker(5) # 5 pts tolerance
lined[legline] = origline
def onpick(event):
# on the pick event, find the orig line corresponding to the
# legend proxy line, and toggle the visibility
legline = event.artist
origline = lined[legline]
vis = not origline.get_visible()
origline.set_visible(vis)
# Change the alpha on the line in the legend so we can see what lines
# have been toggled
if vis:
legline.set_alpha(1.0)
else:
legline.set_alpha(0.2)
fig.canvas.draw()
fig.canvas.mpl_connect('pick_event', onpick)
plt.show()
If copy paste this to have two figures (I did this to check my error - since I had an error producing multiple plots) it gives me two figures, but I am not able to click on the legends of both figures, only on the last, here my code
import numpy as np
import matplotlib.pyplot as plt
t = np.arange(0.0, 0.2, 0.1)
y1 = 2*np.sin(2*np.pi*t)
y2 = 4*np.sin(2*np.pi*2*t)
fig, ax = plt.subplots()
ax.set_title('Click on legend line to toggle line on/off')
Line1, = ax.plot(t, y1, lw=2, label='1 HZ')
Line2, = ax.plot(t, y2, lw=2, label='2 HZ')
Leg = ax.legend(loc='upper left', fancybox=True, shadow=True)
Leg.get_frame().set_alpha(0.4)
# we will set up a dict mapping legend line to orig line, and enable
# picking on the legend line
Lines = [Line1, Line2]
Lined = dict()
for Legline, Origline in zip(Leg.get_lines(), Lines):
Legline.set_picker(5) # 5 pts tolerance
Lined[Legline] = Origline
def onpick(Event):
# on the pick event, find the orig line corresponding to the
# legend proxy line, and toggle the visibility
Legline = Event.artist
Origline = Lined[Legline]
vis = not Origline.get_visible()
Origline.set_visible(vis)
# Change the alpha on the line in the legend so we can see what lines
# have been toggled
if vis:
Legline.set_alpha(1.0)
else:
Legline.set_alpha(0.2)
fig.canvas.draw()
fig.canvas.mpl_connect('pick_event', onpick)
plt.show()
t = np.arange(0.0, 0.2, 0.1)
y1 = 2*np.sin(2*np.pi*t)
y2 = 4*np.sin(2*np.pi*2*t)
fig, ax = plt.subplots()
ax.set_title('Click on legend line to toggle line on/off')
line1, = ax.plot(t, y1, lw=2, label='1 HZ')
line2, = ax.plot(t, y2, lw=2, label='2 HZ')
leg = ax.legend(loc='upper left', fancybox=True, shadow=True)
leg.get_frame().set_alpha(0.4)
# we will set up a dict mapping legend line to orig line, and enable
# picking on the legend line
lines = [line1, line2]
lined = dict()
for legline, origline in zip(leg.get_lines(), lines):
legline.set_picker(5) # 5 pts tolerance
lined[legline] = origline
def onpick2(event):
# on the pick event, find the orig line corresponding to the
# legend proxy line, and toggle the visibility
legline = event.artist
origline = lined[legline]
vis = not origline.get_visible()
origline.set_visible(vis)
# Change the alpha on the line in the legend so we can see what lines
# have been toggled
if vis:
legline.set_alpha(1.0)
else:
legline.set_alpha(0.2)
fig.canvas.draw()
fig.canvas.mpl_connect('pick_event', onpick2)
plt.show()
So basically I just copy pasted it. But it's not working the way I expected it. Anyone any recommendation?

You are using some variables that have the same name in the first and second figures. The event handling for the first figure is trying to access variables that have been overwritten in creating the second figure, and that's where things fail.
If you make sure you use unique variable names, everything works:
import numpy as np
import matplotlib.pyplot as plt
t = np.arange(0.0, 0.2, 0.1)
y1 = 2 * np.sin(2 * np.pi * t)
y2 = 4 * np.sin(2 * np.pi * 2 * t)
fig1, ax1 = plt.subplots()
ax1.set_title('Click on legend line to toggle line on/off')
Line11, = ax1.plot(t, y1, lw=2, label='1 HZ')
Line12, = ax1.plot(t, y2, lw=2, label='2 HZ')
Leg1 = ax1.legend(loc='upper left', fancybox=True, shadow=True)
Leg1.get_frame().set_alpha(0.4)
# we will set up a dict mapping legend line to orig line, and enable
# picking on the legend line
Lines1 = [Line11, Line12]
Lined1 = dict()
for Legline, Origline in zip(Leg1.get_lines(), Lines1):
Legline.set_picker(5) # 5 pts tolerance
Lined1[Legline] = Origline
def onpick(Event):
# on the pick event, find the orig line corresponding to the
# legend proxy line, and toggle the visibility
Legline = Event.artist
Origline = Lined1[Legline]
vis = not Origline.get_visible()
Origline.set_visible(vis)
# Change the alpha on the line in the legend so we can see what lines
# have been toggled
if vis:
Legline.set_alpha(1.0)
else:
Legline.set_alpha(0.2)
fig1.canvas.draw()
fig1.canvas.mpl_connect('pick_event', onpick)
t = np.arange(0.0, 0.2, 0.1)
y1 = 2 * np.sin(2 * np.pi * t)
y2 = 4 * np.sin(2 * np.pi * 2 * t)
fig2, ax2 = plt.subplots()
ax2.set_title('Click on legend line to toggle line on/off')
line21, = ax2.plot(t, y1, lw=2, label='1 HZ')
line22, = ax2.plot(t, y2, lw=2, label='2 HZ')
leg2 = ax2.legend(loc='upper left', fancybox=True, shadow=True)
leg2.get_frame().set_alpha(0.4)
# we will set up a dict mapping legend line to orig line, and enable
# picking on the legend line
lines2 = [line21, line22]
lined2 = dict()
for legline, origline in zip(leg2.get_lines(), lines2):
legline.set_picker(5) # 5 pts tolerance
lined2[legline] = origline
def onpick2(event):
# on the pick event, find the orig line corresponding to the
# legend proxy line, and toggle the visibility
legline = event.artist
origline = lined2[legline]
vis = not origline.get_visible()
origline.set_visible(vis)
# Change the alpha on the line in the legend so we can see what lines
# have been toggled
if vis:
legline.set_alpha(1.0)
else:
legline.set_alpha(0.2)
fig2.canvas.draw()
fig2.canvas.mpl_connect('pick_event', onpick2)
plt.show()

Related

animated multi-subplot with matplotlib hist

I try to adapt this code animated plot line to similar situation but using hist in lieu of line plot and expend it to 4 subplot. It seem to be an easy task with this great exemple but I struggle an error on the adaptation.
line1, = ax1.hist([], HIST_BINS1, lw=1, density=True, color='orange', alpha=0.5,
label='1 an')
ValueError: too many values to unpack (expected 1)
I try to remove the "comma" but don't seem to be the solution, thank to all one who will take time to check my code
import pandas as pd
import numpy as np
from typing import Generator
import matplotlib.pyplot as plt
import matplotlib.animation as animation
path = "E:/data/HQ/meteo_app_moy/"
file = "Rouge (040204)_1062.csv"
data = pd.read_csv(path + file, usecols=[0, 1, 2, 3, 4], sep=";", header=None,
decimal=",", thousands=".")
data['date'] = pd.date_range(start='1/1/1950', end='31/12/2021')
data = data.set_index('date')
yrs = list(data.index.year.unique())
HIST_BINS1 = np.linspace(data[3].min(), data[3].max(), round(data[3].max() / 2))
HIST_BINS2 = np.linspace(data[4].min(), data[4].max(), round(data[4].max() / 2))
HIST_BINS3 = np.linspace(-40, 40, 40)
HIST_BINS4 = np.linspace(0, data[0].max(), 100)
def data_gen():
cnt = 0
while cnt <= len(yrs):
cnt += 1
y1 = data.iloc[data.index.year.isin([yrs[cnt]]), 3] # pluie
y1 = y1[y1 > 0]
y2 = data.iloc[data.index.year.isin([yrs[cnt]]), 4] # neige
y2 = y2[y2 > 1]
y3 = data[[1, 2]].mean(axis=1) # Tmoy
y3 = y3.iloc[y3.index.year.isin([yrs[cnt]])]
y4 = data.iloc[data.index.year.isin([yrs[cnt]]), 0] # App
yield y1, y2, y3, y4
# create a figure with two subplots
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2)
# add a fixed hist as ref
ax1.hist(data.loc[data[3]>1,3], HIST_BINS1, lw=1, density=True, color='steelblue',
alpha=0.5, label='histo')
# intialize hist objects (one in each axes)
line1, = ax1.hist([], HIST_BINS1, lw=1, density=True, color='orange', alpha=0.5,
label='1 an')
line2, = ax2.hist([], HIST_BINS2, lw=1, density=True)
line3, = ax3.hist([], HIST_BINS3, lw=1, density=True)
line4, = ax4.hist([], HIST_BINS4, lw=1, density=True)
line = [line1, line2, line3, line4]
# initialize the data arrays
y1data, y2data, y3data, y4data = [], [], [], []
def run(data):
# update the data
y1, y2, y3, y4 = data
# xdata.append(t)
y1data.append(y1)
y2data.append(y2)
y3data.append(y3)
y4data.append(y4)
# update the data of both line objects
line[0].set_data(y1data)
line[1].set_data(y2data)
line[2].set_data(y3data)
line[3].set_data(y4data)
return line
ani = animation.FuncAnimation(fig, run, data_gen, blit=True, interval=2,
repeat=False)
plt.show()
PS: sorry for my english Im working hard on it

Is there a way to label each wedge of pie chart in this grid?

I want to have multiple pie charts in a grid.
Each pie chart will have a different number of wedges, values, and labels.
The code below shows multiple labels in one pie chart.
Is there a way to label each wedge of pie-charts in this grid?
import matplotlib.pyplot as plt
import numpy as np
def heatmap_with_circles(data_array,row_labels,column_labels,ax=None, cmap=None, norm=None, cbar_kw={}, cbarlabel="", **kwargs):
for row_index, row in enumerate(row_labels,0):
for column_index, column in enumerate(column_labels,0):
print('row_index: %d column_index: %d' %(row_index,column_index))
if row_index==0 and column_index==0:
colors=['indianred','orange','gray']
values=[10,20,30]
else:
values=[45,20,38]
colors=['pink','violet','green']
wedges, text = plt.pie(values,labels=['0', '2', '3'],labeldistance = 0.25,colors=colors)
print('len(wedges):%d wedges: %s, text: %s' %(len(wedges), wedges, text))
radius = 0.45
[w.set_center((column_index,row_index)) for w in wedges]
[w.set_radius(radius) for w in wedges]
# We want to show all ticks...
ax.set_xticks(np.arange(data_array.shape[1]))
ax.set_yticks(np.arange(data_array.shape[0]))
fontsize=10
ax.set_xticklabels(column_labels, fontsize=fontsize)
ax.set_yticklabels(row_labels, fontsize=fontsize)
#X axis labels at top
ax.tick_params(top=True, bottom=False,labeltop=True, labelbottom=False,pad=5)
plt.setp(ax.get_xticklabels(), rotation=55, ha="left", rotation_mode="anchor")
# We want to show all ticks...
ax.set_xticks(np.arange(data_array.shape[1]+1)-.5, minor=True)
ax.set_yticks(np.arange(data_array.shape[0]+1)-.5, minor=True)
ax.grid(which="minor", color="black", linestyle='-', linewidth=2)
ax.tick_params(which="minor", bottom=False, left=False)
data_array=np.random.rand(3,4)
row_labels=['Row1', 'Row2', 'Row3']
column_labels=['Column1', 'Column2', 'Column3','Column4']
fig, ax = plt.subplots(figsize=(1.9*len(column_labels),1.2*len(row_labels)))
ax.set_aspect(1.0)
ax.set_facecolor('white')
heatmap_with_circles(data_array,row_labels,column_labels, ax=ax)
plt.tight_layout()
plt.show()
After updating heatmap_with_circles
def heatmap_with_circles(data_array,row_labels,column_labels,ax=None, cmap=None, norm=None, cbar_kw={}, cbarlabel="", **kwargs):
labels = ['x', 'y', 'z']
for row_index, row in enumerate(row_labels,0):
for column_index, column in enumerate(column_labels,0):
print('row_index: %d column_index: %d' %(row_index,column_index))
if row_index==0 and column_index==0:
colors=['indianred','orange','gray']
values=[10,20,30]
else:
values=[45,20,38]
colors=['pink','violet','green']
# wedges, texts = plt.pie(values,labels=['0', '2', '3'],labeldistance = 0.45,colors=colors)
wedges, texts = plt.pie(values,labeldistance = 0.25,colors=colors)
print('text:%s len(wedges):%d wedges: %s' %(texts, len(wedges), wedges))
radius = 0.45
[w.set_center((column_index,row_index)) for w in wedges]
[w.set_radius(radius) for w in wedges]
[text.set_position((text.get_position()[0]+column_index,text.get_position()[1]+row_index)) for text in texts]
[text.set_text(labels[text_index]) for text_index, text in enumerate(texts,0)]
I got the following image :)
You could loop through the texts of each pie, get its xy position, add column_index and row_index, and set that as new position.
Some small changes to the existing code:
ax.grid(which="minor", ..., clip_on=False) to make sure the thick lines are shown completely, also near the border
ax.set_xlim(xmin=-0.5) to set the limits
import matplotlib.pyplot as plt
import numpy as np
def heatmap_with_circles(data_array, row_labels, column_labels, ax=None):
ax = ax or plt.gca()
for row_index, row in enumerate(row_labels, 0):
for column_index, column in enumerate(column_labels, 0):
colors = np.random.choice(['indianred', 'orange', 'gray', 'pink', 'violet', 'green'], 3, replace=False)
values = np.random.randint(10, 41, 3)
wedges, text = plt.pie(values, labels=['1', '2', '3'], labeldistance=0.25, colors=colors)
radius = 0.45
for w in wedges:
w.set_center((column_index, row_index))
w.set_radius(radius)
w.set_edgecolor('white')
# w.set_linewidth(1)
for t in text:
x, y = t.get_position()
t.set_position((x + column_index, y + row_index))
# We want to show all ticks...
ax.set_xticks(np.arange(data_array.shape[1]))
ax.set_yticks(np.arange(data_array.shape[0]))
fontsize = 10
ax.set_xticklabels(column_labels, fontsize=fontsize)
ax.set_yticklabels(row_labels, fontsize=fontsize)
# X axis labels at top
ax.tick_params(top=True, bottom=False, labeltop=True, labelbottom=False, pad=5)
plt.setp(ax.get_xticklabels(), rotation=55, ha="left", rotation_mode="anchor")
# We want to show all minor ticks...
ax.set_xticks(np.arange(data_array.shape[1] + 1) - .5, minor=True)
ax.set_yticks(np.arange(data_array.shape[0] + 1) - .5, minor=True)
ax.set_xlim(xmin=-.5)
ax.set_ylim(ymin=-.5)
ax.grid(which="minor", color="black", linestyle='-', linewidth=2, clip_on=False)
ax.tick_params(axis="both", which="both", length=0) # hide tick marks
data_array = np.random.rand(3, 4)
row_labels = ['Row1', 'Row2', 'Row3']
column_labels = ['Column1', 'Column2', 'Column3', 'Column4']
fig, ax = plt.subplots(figsize=(1.9 * len(column_labels), 1.2 * len(row_labels)))
ax.set_aspect(1.0)
ax.set_facecolor('white')
heatmap_with_circles(data_array, row_labels, column_labels, ax=ax)
plt.tight_layout()
plt.show()

interactive legend with twinx - matplotlib

i want to create a plot with two y-axis and interactive legend. I made a minimal "working" example based on: https://matplotlib.org/3.1.1/gallery/event_handling/legend_picking.html
import matplotlib.pyplot as plt
import numpy as np
t = np.arange(0.0, 5, 0.01)
y1 = 2*np.sin(2*np.pi*t)
y2 = 4*np.sin(2*np.pi*2*t)+1
fig, ax = plt.subplots()
ax.set_title('Click on legend line to toggle line on/off')
line1, = ax.plot(t, y1, lw=2, label='1 HZ')
ax2 = ax.twinx()
line2, = ax2.plot(t, y2, lw=2, label='2 HZ')
lines_twinx=[line1,line2]
lbl = [l.get_label() for l in lines_twinx]
leg=ax.legend(lines_twinx, lbl, loc="upper left",fontsize='xx-small', shadow=True)
leg.get_frame().set_alpha(0.4)
lined = dict()
for legline, origline in zip(leg.get_lines(), lines_twinx):
legline.set_picker(5) # 5 pts tolerance
lined[legline] = origline
def onpick(event):
# on the pick event, find the orig line corresponding to the
# legend proxy line, and toggle the visibility
legline = event.artist
origline = lined[legline]
vis = not origline.get_visible()
origline.set_visible(vis)
# Change the alpha on the line in the legend so we can see what lines
# have been toggled
if vis:
legline.set_alpha(1.0)
else:
legline.set_alpha(0.2)
fig.canvas.draw()
fig.canvas.mpl_connect('pick_event', onpick)
plt.show()
plot-figure
Somehow the legend is not clickable. Anybody knows what to do?
Using fig.legend() instead of ax.legend() helped me
leg=fig.legend(lines_twinx, lbl, loc="upper left",fontsize='xx-small', shadow=True)
view here

AttributeError: 'Axes' object has no attribute 'get_proj' in matplotlib

I am making the representation of a polynom function.
I have an error in a matplotlib code and cannot understand where it is coming from. any advice is welcome.
I tried already Gtk3agg but nothing changed.
Below is the failure code.
For any reason 'get_proj' dont work here for creating labels.
And: when I use ax.get_proj() instead,
a) all labels appear bottom left
b) not all labels appear at bottom left (all points are identified by the cursor bot the labels are not written at the bottom left).
The final project will be (few things still to be done):
- on button -> labelling with coordinate appear at each cursor movement (temporary)
- click right button, the labels will be persistent till button clear is clicked
- off button -> no labelling appear
My feeling: the 3x button creation is messing anything up.
# -*- coding: utf-8 -*-
import matplotlib as mpl
from mpl_toolkits.mplot3d.proj3d import proj_transform
import matplotlib.pyplot as plt
from matplotlib.widgets import Button
import numpy as np
mpl.use('tkagg')
def distance(point, event):
plt.sca(ax) # <------------------ introduce this one !!!!!!!!!!!!!!!!!!!!!!!!!!!
x2, y2, _ = proj_transform(point[0], point[1], point[2], plt.gca().get_proj())
x3, y3 = ax.transData.transform((x2, y2))
return np.sqrt ((x3 - event.x)**2 + (y3 - event.y)**2)
def calcClosestDatapoint(X, event):
distances = [distance(X[i, 0:3], event) for i in range(Sol)]
return np.argmin(distances)
#
def annotatePlot(X, index):
global last_mark, generated_labels
if activated_labelling:
x2, y2, _ = proj_transform(X[index, 0], X[index, 1], X[index, 2], ax.get_proj())
last_mark = plt.annotate(generated_labels[index],
xy = (x2, y2), xytext = (-20, 20), textcoords = 'offset points', ha = 'right', va = 'bottom',
bbox = dict(boxstyle = 'round,pad=0.5', fc = 'yellow', alpha = 0.5),
arrowprops = dict(arrowstyle = '->', connectionstyle = 'arc3,rad=0'))
fig.canvas.draw()
#
def onMouseMotion(event):
global Coord
if activated_labelling:
closestIndex = calcClosestDatapoint(Coord, event)
last_mark.remove()
annotatePlot(Coord, closestIndex)
def show_on(event):
global activated_labelling, last_mark,pid,mid
if activated_labelling == False:
activated_labelling = True
x2, y2, _ = proj_transform(Coord[0,0], Coord[0,1], Coord[0,2], ax.get_proj())
last_mark = plt.annotate("3D measurement on " + generated_labels[0],
xy = (x2, y2), xytext = (-20, 20), textcoords = 'offset points', ha = 'right', va = 'bottom',
bbox = dict(boxstyle = 'round,pad=0.5', fc = 'yellow', alpha = 0.5),
arrowprops = dict(arrowstyle = '->', connectionstyle = 'arc3,rad=0'))
mid = fig.canvas.mpl_connect('motion_notify_event', onMouseMotion)
#
def show_off(event):
global activated_labelling
'''
deactivate the persistent XYZ position labels at the grafic
'''
if activated_labelling:
activated_labelling = False
last_mark.remove()
fig.canvas.draw()
fig.canvas.mpl_disconnect(mid)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
#ax = fig.gca(projection='3d')
activated_labelling = False
Wide = 100
Minimum = -50
ScanLimit = 3 # searching between o and 3; 4 and 5 are no solutions
Search = 45
Coord=[]
values=[]
generated_labels = []
#
XMin = 0
XMax = 0
YMin = 0
YMax = 0
ZMin = 0
ZMax = 0
# count the solutions found in the scan area defined above
Sol=0
for i in range(Wide+1):
for j in range(Wide+1):
for k in range(Wide+1):
########################################################################
########################################################################
####
#### THIS IS THE POLYNOM TO BE REPRESENTED
####
param_dens = ((i+Minimum)**3)+((j+Minimum)**3)+((k+Minimum)**3) -Search
if abs(param_dens) <= abs(ScanLimit):
Coord.append([i+Minimum,j+Minimum,k+Minimum])
if ScanLimit !=0:
values.append([abs(param_dens)])
labelling = "value {}\nin X:{} Y:{} Z:{}".format(Search+param_dens,i+Minimum,j+Minimum,k+Minimum)
generated_labels.append(labelling)
print(labelling+"\n")
# increase the number indicating the solutions found
Sol +=1
# for centering the window
if XMin > i+Minimum:
XMin = i+Minimum
if YMin > j+Minimum:
YMin = j+Minimum
if ZMin > k+Minimum:
ZMin = k+Minimum
if XMax < i+Minimum:
XMax = i+Minimum
if YMax < j+Minimum:
YMax = j+Minimum
if ZMax < k+Minimum:
ZMax = k+Minimum
print('######################################################')
print('## statistics / move this to a parallel search engine?')
print('## search ')
print("## total solution %d for searching center %d" % (Sol,Search))
print("## from %d to %d" % (Search-ScanLimit,Search+ScanLimit))
print("## from %d to %d" % (Minimum,Wide+Minimum))
print('##')
print('#######################################################')
#
values = np.array(values, dtype='int64')
Coord = np.array(Coord, dtype='int64')
#
if ScanLimit !=0:
cmap = plt.cm.jet # define the colormap
# extract all colors from the .jet map
cmaplist = [cmap(i) for i in range(cmap.N)]
# force the first color entry to be black
cmaplist[0] = (0, 0, 0, 1.0)
# create the new map
cmap = mpl.colors.LinearSegmentedColormap.from_list('Custom cmap', cmaplist, cmap.N)
# define the bins and normalize
bounds = np.linspace(0, ScanLimit, ScanLimit+1)
norm = mpl.colors.BoundaryNorm(bounds, cmap.N)
# create a second axes for the colorbar
ax2 = fig.add_axes([0.95, 0.1, 0.03, 0.8])
cb = mpl.colorbar.ColorbarBase(ax2, cmap=cmap, norm=norm,
spacing='proportional', ticks=bounds, boundaries=bounds, format='%1i')
#
ax.set_xlim3d(XMin-5, XMax+5)
ax.set_ylim3d(YMin-5, YMax+5)
ax.set_zlim3d(ZMin-5, ZMax+5)
#
ax.set_xlabel('X X')
ax.set_ylabel('Y Y')
ax.set_zlabel('Z Z')
ax.set_aspect(aspect=1)
# extract the scatterplot drawing in a separate function so we ca re-use the code
def draw_scatterplot():
if ScanLimit !=0:
ax.scatter3D(Coord[:,0], Coord[:,1], Coord[:,2], s=20, c=values[:,0], cmap=cmap, norm=norm)
else:
ax.scatter3D(Coord[:,0], Coord[:,1], Coord[:,2], s=20, c='green')
# draw the initial scatterplot
draw_scatterplot()
# create the "on" button, and place it somewhere on the screen
ax_on = plt.axes([0.0, 0.0, 0.1, 0.05])
button_on = Button(ax_on, 'on')
#
ax_off = plt.axes([0.12, 0.0, 0.1, 0.05])
button_off = Button(ax_off, 'off')
#
#ax_off = plt.axes([0.24, 0.0, 0.1, 0.05])
#button_off = Button(ax_off, 'off')
# link the event handler function to the click event on the button
button_on.on_clicked(show_on)
button_off.on_clicked(show_off)
#fig.colorbar(img)
plt.show()
Traceback (most recent call last):
File "C:\Program Files\Anaconda3\lib\site-packages\matplotlib\cbook\__init__.py", line 388, in process
proxy(*args, **kwargs)
File "C:\Program Files\Anaconda3\lib\site-packages\matplotlib\cbook\__init__.py", line 228, in __call__
return mtd(*args, **kwargs)
File "C:/Users/../Desktop/heat.py", line 137, in onClick
closestIndex,LowestDistance = calcClosestDatapoint(Coord, event)
File "C:/Users/../Desktop/heat.py", line 50, in calcClosestDatapoint
distances = [distance(X[i, 0:3], event) for i in range(Sol)]
File "C:/Users/../Desktop/heat.py", line 50, in <listcomp>
distances = [distance(X[i, 0:3], event) for i in range(Sol)]
File "C:/Users/../Desktop/heat.py", line 35, in distance
x2, y2, _ = proj_transform(point[0], point[1], point[2], plt.gca().get_proj())
AttributeError: 'Axes' object has no attribute 'get_proj'

Legend location in matplotlib subplot

I am trying to create subplots on (6X3) grid. I am having a problem with the location of legend. The legend is common to all subplot. The lgend is now overlapping with the y-axis label
I tried with removing the constrained_layout=True option. But this keeps a lot of white space between legend and subplots.
import numpy as np
import matplotlib.pyplot as plt
#plt.rcParams["font.family"] = "Times New Roman"
#plt.rcParams.update({'font.size': 12})
font = {'family' : 'Times New Roman',
'size' : 14}
plt.rc('font', **font)
t = np.linspace(0,10, num=200)
fig, axs = plt.subplots(6, 3, figsize=(12,16))#, constrained_layout=True)
i = 0 # i = 0 for x = 0.25; i = 3 for x = -0.25
j = 6 # j = 6 for x = 0.25; j = 9 for x = -0.25
#%%
solution = np.loadtxt(open("sequential_legs=01.csv", "rb"), delimiter=",", skiprows=0)
axs[0, 0].plot(t, solution[:,i],'r-')
axs[0, 0].plot(t, solution[:,i+1],'g-')
axs[0, 0].plot(t, solution[:,i+2],'b-')
axs[0, 0].plot(t, solution[:,j],'r--')
axs[0, 0].plot(t, solution[:,j+1],'g--')
axs[0, 0].plot(t, solution[:,j+2],'b--')
axs[0, 0].set_title('DNN-S (p = 1)', fontsize=14)
axs[0, 0].set_xlim([0, 10])
(repeated for each grid)
line_labels = ["$y_1$ (True)","$y_2$ (True)", "$y_3$ (True)", "$y_1$ (ML-Pred)","$y_2$ (ML-Pred)", "$y_3$ (ML-Pred)"]
plt.figlegend( line_labels, loc = 'lower center', borderaxespad=0.1, ncol=6, labelspacing=0., prop={'size': 13} ) #bbox_to_anchor=(0.5, 0.0), borderaxespad=0.1,
for ax in axs.flat:
ax.set(xlabel='Time', ylabel='Response')
for ax in axs.flat:
ax.label_outer()
fig.savefig('LSE_X=025.pdf', bbox_inches = 'tight')
constrained_layout does not take figure legends into account. The easiest option I currently see here is to use tight_layout to have the subplots distribute nicely in the figure, and after that, add some space for the legend manually.
import matplotlib.pyplot as plt
fig, axs = plt.subplots(6, 3, figsize=(12,8), sharex=True, sharey=True,
constrained_layout=False)
labels = [f"Label {i}" for i in range(1,5)]
for ax in axs.flat:
for i, lb in enumerate(labels):
ax.plot([1,2], [0,i+1], label=lb)
ax.set_xlabel("x label")
ax.label_outer()
fig.tight_layout()
fig.subplots_adjust(bottom=0.1) ## Need to play with this number.
fig.legend(labels=labels, loc="lower center", ncol=4)
plt.show()