matplotlib mouseclick event in pie chart - python

Is there a way in matplotlib and Python to return the value/label clicked in a pie chart. For example if user clicks on sliver A of pie chart, return value A. If user clicks on sliver B of B pie chart, return value B.

from matplotlib import pyplot as plt
# make a square figure and axes
plt.figure(figsize=(6,6))
ax = plt.axes([0.1, 0.1, 0.8, 0.8])
labels = 'Frogs', 'Hogs', 'Dogs', 'Logs'
fracs = [15,30,45, 10]
explode=(0, 0.05, 0, 0)
p = plt.pie(fracs, explode=explode, labels=labels, autopct='%1.1f%%', shadow=True)
plt.title('Raining Hogs and Dogs', bbox={'facecolor':'0.8', 'pad':5})
w = p[0][0]
plt.show()
class PieEventHandler:
def __init__(self,p):
self.p = p
self.fig = p[0].figure
self.ax = p[0].axes
self.fig.canvas.mpl_connect('button_press_event', self.onpress)
def onpress(self, event):
if event.inaxes!=self.ax:
return
for w in self.p:
(hit,_) = w.contains(event)
if hit:
print w.get_label()
handler = PieEventHandler(p[0])
references:
Color values in imshow for matplotlib?
http://matplotlib.org/examples/pylab_examples/pie_demo.html

:)
import matplotlib.pyplot as plt
def main():
# Make an example pie plot
fig = plt.figure()
ax = fig.add_subplot(111)
labels = ['Apple', 'Mango', 'Orange']
wedges, plt_labels = ax.pie([20, 40, 60], labels=labels)
ax.axis('equal')
make_picker(fig, wedges)
plt.show()
def make_picker(fig, wedges):
def onclick(event):
wedge = event.artist
label = wedge.get_label()
print label
# Make wedges selectable
for wedge in wedges:
wedge.set_picker(True)
fig.canvas.mpl_connect('pick_event', onclick)
if __name__ == '__main__':
main()

Related

matplotlib arrow color not work as expected

How to change the arrow color in below demo code?
import matplotlib.pyplot as plt
def save_fig(fig,pngname):
fig.savefig(pngname, dpi=fig.dpi, bbox_inches="tight")
print("[[%s]]"%pngname)
return
def add_arrow(fig,ax,lw,lc):
xmin,xmax = ax.get_xlim()
ax.arrow(xmin,0,xmax-xmin+.2,0,#fc='k', ec='k',
lw = lw,head_width=.1,head_length=0.4, overhang = 0,
length_includes_head=False, clip_on = False,edgecolor='k',facecolor=lc)
return
def main():
x = [
#"FLAGS",
"INTENDED_VSYNC",
"VSYNC",
"OLDEST_INPUT_EVENT",
"HANDLE_INPUT_START",
"ANIMATION_START",
"PERFORM_TRAVERSALS_START",
"DRAW_START",
"SYNC_QUEUED",
"SYNC_START",
"ISSUE_DRAW_COMMANDS_START",
"SWAP_BUFFERS",
"FRAME_COMPLETED",
]
lw = 2
lc = "grey"
fig, ax = plt.subplots(1, figsize=(8,.2))
y = [0]*len(x)
ax.plot(x,y,color=lc)
ax.set_ylim([0,0.1])
plt.xticks(rotation=45,ha='right')
ax.tick_params(direction = 'inout',color=lc)
ax.tick_params('both', length=20, width=lw, which='major')
ax.tick_params('both', length=10, width=lw, which='minor')
plt.yticks([], [])
for direction in ["left", "right", "bottom", "top"]:
ax.spines[direction].set_visible(False)
add_arrow(fig,ax,lw,lc)
#save_fig(fig,sdir + "/vsync.png")
plt.show()
return
sdir = "/home/tester"
main()
Output:
The color is defined in ax.arrow() by color, edgecolor and facecolor.
edgecolor sets the color of the edge of the arrow.
facecolor sets the color in the arrow body.
color sets the color of both, edgecolor and facecolor.
If you would like to have a uniformly colored arrow, you can either set color to the desired value or alternatively set both, edgecolor and facecolor to the same value. In this case it would mean removing edgecolor and facecolor and adding color=lc or alternatively replacing edgecolor='k' with edgecolor=lc.
import matplotlib.pyplot as plt
def save_fig(fig,pngname):
fig.savefig(pngname, dpi=fig.dpi, bbox_inches="tight")
print("[[%s]]"%pngname)
return
def add_arrow(fig,ax,lw,lc):
xmin,xmax = ax.get_xlim()
ax.arrow(xmin,0,xmax-xmin+.2,0,#fc='k', ec='k',
lw = lw,head_width=.1,head_length=0.4, overhang = 0,
length_includes_head=False, clip_on = False,color=lc)
return
def plot():
x = [
#"FLAGS",
"INTENDED_VSYNC",
"VSYNC",
"OLDEST_INPUT_EVENT",
"HANDLE_INPUT_START",
"ANIMATION_START",
"PERFORM_TRAVERSALS_START",
"DRAW_START",
"SYNC_QUEUED",
"SYNC_START",
"ISSUE_DRAW_COMMANDS_START",
"SWAP_BUFFERS",
"FRAME_COMPLETED",
]
lw = 2
lc = "grey"
fig, ax = plt.subplots(1, figsize=(8,.2))
y = [0]*len(x)
ax.plot(x,y,color=lc)
ax.set_ylim([0,0.1])
plt.xticks(rotation=45,ha='right')
ax.tick_params(direction = 'inout',color=lc)
ax.tick_params('both', length=20, width=lw, which='major')
ax.tick_params('both', length=10, width=lw, which='minor')
plt.yticks([], [])
for direction in ["left", "right", "bottom", "top"]:
ax.spines[direction].set_visible(False)
add_arrow(fig,ax,lw,lc)
#save_fig(fig,sdir + "/vsync.png")
plt.show()
return
plot()

Hiding lines after plotting in matplotlib

I am trying to implement the solution presented by #Joe Kington in the following stack overflow thread:
Hiding lines after showing a pyplot figure
I have 16 lines I am trying to plot with this method, each of which have their own unique color and label. While #Joe Kington's solution works fine to get the plot to display, I cannot transfer his interactive method to my code with the interactive functionality of clicking on the legend entries to hide a line. That is, my code plots my data without error but there is no interactive functionality occurring.
I have tried:
setting the unique labels to be raw strings and regular strings
setting up every line to plot to be iterated over as in #Joe Kington's solution
using smaller legend labels
rearranging blocks of my code in the event that the functions defined in #Joe Kington's solution are out of scope
#Joe Kington's solution is below, which works fine:
import numpy as np
import matplotlib.pyplot as plt
def main():
x = np.arange(10)
fig, ax = plt.subplots()
for i in range(1, 31):
ax.plot(x, i * x, label=r'$y={}x$'.format(i))
ax.legend(loc='upper left', bbox_to_anchor=(1.05, 1),
ncol=2, borderaxespad=0)
fig.subplots_adjust(right=0.55)
fig.suptitle('Right-click to hide all\nMiddle-click to show all',
va='top', size='large')
leg = interactive_legend()
return fig, ax, leg
def interactive_legend(ax=None):
if ax is None:
ax = plt.gca()
if ax.legend_ is None:
ax.legend()
return InteractiveLegend(ax.get_legend())
class InteractiveLegend(object):
def __init__(self, legend):
self.legend = legend
self.fig = legend.axes.figure
self.lookup_artist, self.lookup_handle = self._build_lookups(legend)
self._setup_connections()
self.update()
def _setup_connections(self):
for artist in self.legend.texts + self.legend.legendHandles:
artist.set_picker(10) # 10 points tolerance
self.fig.canvas.mpl_connect('pick_event', self.on_pick)
self.fig.canvas.mpl_connect('button_press_event', self.on_click)
def _build_lookups(self, legend):
labels = [t.get_text() for t in legend.texts]
handles = legend.legendHandles
label2handle = dict(zip(labels, handles))
handle2text = dict(zip(handles, legend.texts))
lookup_artist = {}
lookup_handle = {}
for artist in legend.axes.get_children():
if artist.get_label() in labels:
handle = label2handle[artist.get_label()]
lookup_handle[artist] = handle
lookup_artist[handle] = artist
lookup_artist[handle2text[handle]] = artist
lookup_handle.update(zip(handles, handles))
lookup_handle.update(zip(legend.texts, handles))
return lookup_artist, lookup_handle
def on_pick(self, event):
handle = event.artist
if handle in self.lookup_artist:
artist = self.lookup_artist[handle]
artist.set_visible(not artist.get_visible())
self.update()
def on_click(self, event):
if event.button == 3:
visible = False
elif event.button == 2:
visible = True
else:
return
for artist in self.lookup_artist.values():
artist.set_visible(visible)
self.update()
def update(self):
for artist in self.lookup_artist.values():
handle = self.lookup_handle[artist]
if artist.get_visible():
handle.set_visible(True)
else:
handle.set_visible(False)
self.fig.canvas.draw()
def show(self):
plt.show()
if __name__ == '__main__':
fig, ax, leg = main()
plt.show()
Here is a snippet of my code that isn't working that is defined in a module plotting in a function called main() (I have other modifications I need to make such as changing axis labels, unique colors, etc.):
import numpy as np
import matplotlib.pyplot as plt
def main(t,n,timeSpan):
colors = ['blue','red','green','black','magenta','teal','orange','chartreuse','purple','sienna','goldenrod','lightgray','olive','cyan','maroon','pink']
labels = [r'foo1',r'bar2',r'foo3',r'bar4',r'foo5',r'bar6',r'foo7',r'bar8',r'foo9',r'bar10',r'foo11',r'bar12',r'foo13',r'bar14',r'foo15',r'bar16']
fig, ax = plt.subplots(figsize=(15,8))
for i in range(0, len(colors)):
ax.plot(t, n[:,i], color = colors[i], label = labels[i])
ax.set_xlabel('x label')
ax.set_ylabel('y label')
ax.set_xlim([0,timeSpan])
ax.set_ylim([0,110])
ax.legend(loc='upper left', bbox_to_anchor=(1.05, 1),
ncol=2, borderaxespad=0)
fig.subplots_adjust(right=0.55)
fig.suptitle('Title\n(Right-click to hide all\nMiddle-click to show all)',
va='top', size='large')
leg = interactive_legend()
plt.show()
def interactive_legend(ax=None):
if ax is None:
ax = plt.gca()
if ax.legend_ is None:
ax.legend()
return InteractiveLegend(ax.get_legend())
class InteractiveLegend(object):
def __init__(self, legend):
self.legend = legend
self.fig = legend.axes.figure
self.lookup_artist, self.lookup_handle = self._build_lookups(legend)
self._setup_connections()
self.update()
def _setup_connections(self):
for artist in self.legend.texts + self.legend.legendHandles:
artist.set_picker(10) # 10 points tolerance
self.fig.canvas.mpl_connect('pick_event', self.on_pick)
self.fig.canvas.mpl_connect('button_press_event', self.on_click)
def _build_lookups(self, legend):
labels = [t.get_text() for t in legend.texts]
handles = legend.legendHandles
label2handle = dict(zip(labels, handles))
handle2text = dict(zip(handles, legend.texts))
lookup_artist = {}
lookup_handle = {}
for artist in legend.axes.get_children():
if artist.get_label() in labels:
handle = label2handle[artist.get_label()]
lookup_handle[artist] = handle
lookup_artist[handle] = artist
lookup_artist[handle2text[handle]] = artist
lookup_handle.update(zip(handles, handles))
lookup_handle.update(zip(legend.texts, handles))
return lookup_artist, lookup_handle
def on_pick(self, event):
handle = event.artist
if handle in self.lookup_artist:
artist = self.lookup_artist[handle]
artist.set_visible(not artist.get_visible())
self.update()
def on_click(self, event):
if event.button == 3:
visible = False
elif event.button == 2:
visible = True
else:
return
for artist in self.lookup_artist.values():
artist.set_visible(visible)
self.update()
def update(self):
for artist in self.lookup_artist.values():
handle = self.lookup_handle[artist]
if artist.get_visible():
handle.set_visible(True)
else:
handle.set_visible(False)
self.fig.canvas.draw()
def show(self):
plt.show()
t = np.arange(1000)
n = np.zeros([1000,16])
for i in range(0, 1000):
for j in range(0, 16):
n[i][j] = t[i] * j
timeSpan = 1000
# if __name__ == "__main__":
# main(t,n,timeSpan)
colors = ['blue','red','green','black','magenta','teal','orange','chartreuse','purple','sienna','goldenrod','lightgray','olive','cyan','maroon','pink']
labels = [r'foo1',r'bar2',r'foo3',r'bar4',r'foo5',r'bar6',r'foo7',r'bar8',r'foo9',r'bar10',r'foo11',r'bar12',r'foo13',r'bar14',r'foo15',r'bar16']
fig, ax = plt.subplots(figsize=(15,8))
for i in range(0, len(colors)):
ax.plot(t, n[:,i], color = colors[i], label = labels[i])
ax.set_xlabel('x label')
ax.set_ylabel('y label')
ax.set_xlim([0,timeSpan])
ax.set_ylim([0,110])
ax.legend(loc='upper left', bbox_to_anchor=(1.05, 1),
ncol=2, borderaxespad=0)
fig.subplots_adjust(right=0.55)
fig.suptitle('Title\n(Right-click to hide all\nMiddle-click to show all)',
va='top', size='large')
leg = interactive_legend()
plt.show()
I have determined that when I leave the code as is it works, but when I comment out lines 113-131 and call main() from lines 111 and 112 insead, it no longer works. Is this a scope issue? Not sure why the way I am calling the interactive_legend() function determines whether it works or not.

How to position matplotlib's Button without moving the title and labels as well

I'm a begginner at matplotlib/Python and I am building an animated scatter plot. I want to place a button below the plot so the user can start/pause/resume the animation. This is the plot (I know it's a mess, still working on it):
As a start I tried to add a button named Play to replay the animation after its first automatic execution, but I'm having trouble understanding how positioning it below the plot works, for it is "dragging" everything with it (the title, xlabel, ylabel and so on):
Here's the code:
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.cm as cm
import mplcursors
from matplotlib.animation import FuncAnimation
from matplotlib.widgets import Slider, Button, RadioButtons
df = pd.read_excel('nations2.xls')
df.head()
uniqueYears = df['Ano'].unique()
font = {'family': 'sans-serif',
'color': 'black',
'weight': 'normal',
'size': 16,
}
fig, ax = plt.subplots()
def animate(frames):
ax.clear()
data = df[df['Ano'] == uniqueYears[frames]]
ax.scatter(y = data['ExpecVida'],
x = data['PIBperCapita'],
s = data['PopX1000']/40000,
c = data['Regiao'].astype('category').cat.codes,
cmap = cm.viridis,
edgecolors = 'none',
alpha = 0.5)
ax.set_xlim([0,50000], auto=True)
ax.set_ylim([0,100], auto=True)
plt.title('Wealth and Health of Nations', fontsize=18)
plt.xlabel('GDP per Capita ($)', fontsize=14)
plt.ylabel('Life Expectancy (years)', fontsize=14)
plt.grid(color = '#A9A9A9')
plt.text(2, 0.65, uniqueYears[frames], fontdict=font)
for i, txt in enumerate(data['Pais']):
ax.annotate(txt, (data['PIBperCapita'].iat[i], data['ExpecVida'].iat[i]), fontsize = 6)
anim = FuncAnimation(fig, animate, frames=len(uniqueYears),interval = 200, repeat=False)
class Index(object):
ind = 0
def play(self, event):
FuncAnimation(fig, animate, frames=len(uniqueYears),interval = 200, repeat=False)
callback = Index()
axplay = plt.axes([0.81, 0.05, 0.1, 0.075])
bplay = Button(axplay, 'Play')
bplay.on_clicked(callback.play)

Move dot faster with Matplotlib

I am trying to move a dot to a particular location on the graph dynamically based on real-time data. I'm basically plotting the graph, emitting a signal from a worker thread which calls the 'move_dot' function. It works, however it is slow. I can only call one frame per second. I'm using the MPL widget in pythonxy. I am also using Windows. Is there a way to speed this up?
Here is the code:
from PyQt4 import QtGui
import ui_sof_test #Gui File
import sys
from matplotlib.ticker import AutoMinorLocator
class Gui(QtGui.QMainWindow, ui_sof_test.Ui_MainWindow):
def __init__(self):
super(self.__class__, self).__init__()
self.setupUi(self) # This is defined in ui_pumptest.py file automatically
self.mpl_plot(0)
self.move_dot()
cursorplot = 0
def move_dot(self, x = 100, y = 5, color = 'r'):
fig = self.mplwidget_3.figure
par = fig.add_subplot(111)
ax3 = par.twinx()
plty = fig.gca()
plty.yaxis.set_visible(False)
ax3.plot(x, y, color, marker = 'o', linewidth = 1)
fig.canvas.draw()
#ax3.cla()
def mpl_plot(self, plot_page, replot = 0): #Data stored in lists
fig = self.mplwidget_3.figure #Add a figure
#Clears Figure if data is replotted
if replot == 1:
fig.clf()
plty = fig.gca()
plty.yaxis.set_visible(False)
par0 = fig.add_subplot(111)
#Add Axes
plt = par0.twinx()
#Plot Chart
plt.hold(False)
plt.plot([0,100,200,300,400,500], [1,3,2,4,7,5], 'b', linestyle = "dashed", linewidth = 1)
#Plot Factory Power
minorLocatorx = AutoMinorLocator()
plt.xaxis.set_minor_locator(minorLocatorx)
plt.tick_params(which='both', width= 0.5)
plt.tick_params(which='major', length=7)
plt.tick_params(which='minor', length=4, color='k')
#Plot y axis minor tick marks
minorLocatory = AutoMinorLocator()
plt.yaxis.set_minor_locator(minorLocatory)
plt.tick_params(which='both', width= 0.5)
plt.tick_params(which='major', length=7)
plt.tick_params(which='minor', length=4, color='k')
plt.minorticks_on()
#Make Border of Chart White
fig.set_facecolor('white')
#Plot Grid
plt.grid(b=True, which='both', color='k', linestyle='-')
#Manually make vertical gridlines. Above line doesn't make vertical lines for some reason
for xmaj in plt.xaxis.get_majorticklocs():
plt.axvline(x=xmaj, color = 'k',ls='-')
for xmin in plt.xaxis.get_minorticklocs():
plt.axvline(x=xmin, color = 'k', ls='-')
#Set Scales
plt.yaxis.tick_left()
# Set Axes Colors
plt.tick_params(axis='y', colors='b')
# Set Chart Labels
plt.yaxis.set_label_position("left")
plt.set_xlabel(" ")
plt.set_ylabel(" " , color = 'b')
fig.canvas.draw()
self.move_dot()
def main():
app = QtGui.QApplication(sys.argv) # A new instance of QApplication
form = Gui() # We set the form to be our ExampleApp (design)
form.show() # Show the form
app.exec_() # and execute the. app
if __name__ == '__main__': # if we're running file directly and not importing it
main() # run the main function

Legend transparent to horizontal grid in matplotlib

I'm working with the following class:
import numpy as np
import matplotlib
matplotlib.use('Qt4Agg')
import matplotlib.pyplot as plt
import matplotlib.ticker as plticker
class matplotLIV():
def __init__(self, BaseFilename, temperatures, length=None, width=None, area=None, title = '', ylim=None):
self.BaseFilename = BaseFilename
self.temperatures = temperatures
if length and width:
self.length = length
self.width = width
self.area = length*width*1e-5
else:
self.area = area
self.title = title
self.ylim = ylim
filenames = [("%s_%sK.txt" % (self.BaseFilename, str(temp)), temp) for temp in self.temperatures]
self.rawData = [(np.loadtxt(fname), temp) for fname, temp in filenames]
self.colors = colors = ['#1b9e77', '#d95f02', '#7570b3', '#e7298a', '#e6ab02', '#a6761d', '#666666']
self.maxValueRow = (0,0,0)
def plot(self):
self.fig = plt.figure()
self.ax1 = self.fig.add_subplot(111)
ax1 = self.ax1
ax1.tick_params(bottom='off')
ax1.xaxis.tick_top()
self.ax2 = ax1.twinx()
ax2 = self.ax2
self.ax3 = ax2.twiny()
ax3 = self.ax3
ax3.xaxis.tick_bottom()
ax1.set_xlabel("current / A")
ax1.xaxis.set_label_position('top')
ax1.set_ylabel("voltage / V")
ax2.set_ylabel("light intensity / arb. u.")
ax3.set_xlabel(r'current density / $\mathregular{Acm^{-2}}$')
ax3.xaxis.set_label_position('bottom')
for i, (datafile, label) in enumerate(self.rawData):
self.checkMaxValues(datafile)
ax1.plot( datafile[:,0], datafile[:,1], color=self.colors[i], label='%sK' % str(label))
ax2.plot( datafile[:,0], datafile[:,2], color=self.colors[i], label='%sK' % str(label), linewidth=2)
ax1.margins(x=0)
ax1.grid(True, axis='y')
ax3.grid(True)
start, end = ax1.get_xlim()
self.setAxesScale(ax1, ax2)
if self.ylim:
ax2.set_ylim(top=self.ylim)
ax3.set_xlim(start/self.area, end/self.area)
leg = ax2.legend(loc='upper left')
self.fig.suptitle(self.title, y=0.98, weight='bold')
self.fig.subplots_adjust(top=0.86)
loc = plticker.MultipleLocator(base=20.0) # this locator puts ticks at regular intervals
ax3.xaxis.set_major_locator(loc)
def checkMaxValues(self, data):
maxInd = data.argmax(axis=0)[2]
if data[maxInd][2] > self.maxValueRow[2]:
self.maxValueRow = data[maxInd]
def setAxesScale(self, ax1, ax2):
yrange = ax1.get_ylim()
y1Fraction = self.maxValueRow[1]/yrange[1]
y2Fraction = y1Fraction - 0.02
ax2.set_ylim(top=self.maxValueRow[2]/y2Fraction)
def show(self):
plt.savefig(self.BaseFilename + '.pdf')
plt.show()
which you can run with this sample code:
import matplotLIV as mpliv
######## configuration
BaseFilename = "testdata"
temperatures = (5,)
area = 1e-8
######## end of configuration
liv = mpliv.matplotLIV(BaseFilename, temperatures, area=area)
liv.plot()
liv.show()
on this file: http://pastebin.com/GMAC3mUu
The problem that I'm experiencing is that the legend is transparent to the grid. Oddly enough, it is only the vertical grid that you can see through the legend box:
Is this a bug? If not, how do I set the legend so it is NOT transparent?
The problem is the vertical grid is on ax3, and the legend is on ax2, so the grid is plotted after the legend.
One way around this is pasted below (just the section you need to modify). You need to plot the legend on ax3, and explicitly tell it which lines and labels you want.
# make a list for the lines that you are plotting
l1 = []
l2 = []
for i, (datafile, label) in enumerate(self.rawData):
self.checkMaxValues(datafile)
# Give your lines some names (l1,l2)
l1+=ax1.plot( datafile[:,0], datafile[:,1], color=self.colors[i], label='%sK' % str(label))
l2+=ax2.plot( datafile[:,0], datafile[:,2], color=self.colors[i], label='%sK' % str(label), linewidth=2)
# Define which lines to put in the legend. If you want l1 too, then use lns = l1+l2
lns = l2
labs = [l.get_label() for l in lns]
ax1.margins(x=0)
ax1.grid(True, axis='y')
ax3.grid(True)
start, end = ax1.get_xlim()
self.setAxesScale(ax1, ax2)
if self.ylim:
ax2.set_ylim(top=self.ylim)
ax3.set_xlim(start/self.area, end/self.area)
# Set the legend on ax3, not ax2
leg = ax3.legend(lns,labs,loc='upper left')

Categories

Resources