Create an account


Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
[Tut] Matplotlib Cursor — How to Add a Cursor and Annotate Your Plot

#1
Matplotlib Cursor — How to Add a Cursor and Annotate Your Plot



This article explains how to insert a cursor to your plot, how to customize it and how to store the values that you selected on the plot window. In lots of situations we may want to select and store the coordinates of specific points in our graph; is it just for assessing their value or because we may want to use some specific values for successive processing of the data. As you will see, this is not a difficult task, but it will add a lot of value to your plots. We will also see how to pop in a small frame containing the coordinates of the selected point, every time we click on it.

Here’s our end-goal—an interactive plot that annotates the point you click on:

Figure 2: Final result of the script. Every time a point on the window is clicked with the cursor, an annotating box containing the values of the point coordinates is displayed.https://blog.finxter.com/wp-content/uplo...00x159.png 300w, https://blog.finxter.com/wp-content/uplo...40x340.png 640w, https://blog.finxter.com/wp-content/uplo...150x79.png 150w" sizes="(max-width: 642px) 100vw, 642px" />
Figure: Final result of the script. Every time a point on the window is clicked with the cursor, an annotating box containing the values of the point coordinates is displayed.

And here’s the code that we’ll discuss in this article that leads to this output:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Cursor #x and y arrays for definining an initial function
x = np.linspace(0, 10, 100)
y = np.exp(x**0.5) * np.sin(5*x) # Plotting
fig = plt.figure()
ax = fig.subplots()
ax.plot(x,y, color = 'b')
ax.grid() # Defining the cursor
cursor = Cursor(ax, horizOn=True, vertOn=True, useblit=True, color = 'r', linewidth = 1) # Creating an annotating box
annot = ax.annotate("", xy=(0,0), xytext=(-40,40),textcoords="offset points", bbox=dict(boxstyle='round4', fc='linen',ec='k',lw=1), arrowprops=dict(arrowstyle='-|>'))
annot.set_visible(False) # Function for storing and showing the clicked values
coord = []
def onclick(event): global coord coord.append((event.xdata, event.ydata)) x = event.xdata y = event.ydata # printing the values of the selected point print([x,y]) annot.xy = (x,y) text = "({:.2g}, {:.2g})".format(x,y) annot.set_text(text) annot.set_visible(True) fig.canvas.draw() #redraw the figure fig.canvas.mpl_connect('button_press_event', onclick)
plt.show() # Unzipping the coord list in two different arrays
x1, y1 = zip(*coord)
print(x1, y1)

Importing Libraries


As to begin, we import the libraries and the packages that will be used in this example. We will use NumPy for defining an initial function that will be then displayed using matplotlib.pyplot. Finally, from the matplotlib.widget package, we import the function Cursor, which will be used for the creation of the interactive cursor.

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Cursor

Defining an Initial Function to Plot


In order to use our cursor on a real plot, we introduce an initial function by defining two NumPy arrays, “x” and “y”. The “x” array is defined by exploiting the NumPy function .linspace(), which will generate an array of 100 equally spaced numbers  from 0 to 10. The “y” array is defined by the following function:

https://blog.finxter.com/wp-content/uplo...150x35.png 150w" sizes="(max-width: 279px) 100vw, 279px" />

Both the sin() and the exponential function are introduced using NumPy. Of course, this is only one possible example, any function is good for the final goal of this article. All these procedures are described in the following code-lines.

#x and y arrays
x = np.linspace(0, 10, 100)
y = np.exp(x**0.5) * np.sin(5*x)

Plotting the function


In the next step we define the plotting window and plot our function. To this purpose, we entirely rely on the matplotlib.pyplot package.

#Plotting
fig = plt.figure()
ax = fig.subplots()
ax.plot(x,y, color = 'red')
ax.grid()

Defining the Cursor



Cursor
Syntax: Cursor()
Parameters: ax (variable) Axes defining the space in which the button will be located
horizOn (bool) Drawing the horizontal line
vertOn (bool) Drawing the vertical line
useblit (bool) Use blitting for improving the performance
color (str or float) The color of the lines
linewidth (float) Width of the cursor lines
Return Value None
Table 1: The .Cursor()function and all the input parameters used in the present example.

To introduce a cursor in our plot, we first have to define all its properties; to do that, we exploit the function Cursor, from the matplotlib.widget package.

The function takes as input the axes in which we want to display the cursor (“ax” in this case) and other properties of the cursor itself; namely horizOn and vertOn, which generate an horizontal and a vertical line that univocally identify the cursor while it is hovering on the plot; their value can be set to True or False, depending on how we want to identify the cursor.

It is also possible to specify some properties of the line, like the color and the thickness (using linewidth).

The last input parameter is useblit, we set it to True since it generally improves the performance of interactive figures by “not re-doing work we do not have to” (if you are interested in the process of Blitting, please visit: https://matplotlib.org/3.3.1/tutorials/advanced/blitting.html ).

All the input parameters of the function Cursor are summarized in Table 1 and additional documentation can be found at: https://matplotlib.org/3.3.3/api/widgets_api.html.

All the properties defined within the function Cursor, are assigned to the variable “cursor”.

#defining the cursor
cursor = Cursor(ax, horizOn = True, vertOn=True, color='red', linewidth=1, useblit=True)

At this point, we completed the definition of our cursor, if we were to show the plot, we would get the result displayed in Figure 1.

Figure 1: Matplotlib window displaying the initial plot and the cursor (red lines).https://blog.finxter.com/wp-content/uplo...00x222.png 300w, https://blog.finxter.com/wp-content/uplo...50x111.png 150w" sizes="(max-width: 396px) 100vw, 396px" />
Figure 1: Matplotlib window displaying the initial plot and the cursor (red lines).

In the next steps, we will see how to define the framework, containing the coordinates of the selected point, that will pop in at each mouse click. If you are not interested in this feature, you can skip to the next section in which we will see how to store and print the values selected by the cursor.

Creating the Annotating Frameworks



Annotate
Syntax: annotate()
Parameters: text (str) The text of the annotation
xy (float, float) The point to annotate
xytext (float, float) The position to place the text at
textcoords The coordinate system that xytext is given in
bbox Instancing a frame
arrowprops Instancing an arrow
Return Value None

Table 2: The .annotate() function and all the input parameters used in the present example.

As anticipated in the introduction, we want to improve the graphical outcome and the efficiency of our cursor by popping in a small framework, containing the coordinates of the selected point, at each mouse click.

To this purpose, we exploit the matplotlib function .annotate(), which provides lots of different features for customizing the annotations within a plot (additional documentation can be found here: https://matplotlib.org/3.2.1/api/_as_gen/matplotlib.axes.Axes.annotate.html).

The first input parameter of the .annotate() function is the text that will appear in the annotation; we enter a blank string, since we will add the text later on (it will change at each mouse click).

We then specify the properties “xy”, “xytext” and “textcoords” with which we define a reference point, the distance of the text from this point and how the distance gets calculated (in our case counting the numerical values in points, pixel is also available), respectively.

To better highlight the annotation in the plot, we also add an external framework, using bbox and passing all the properties of the framework (like filling color, edgecolor and linewidth) as keys and values of a dictionary.

Finally, we generate an arrow, going from “xy” to “xytext” in a similar way (all the properties for the arrows can be found in the .annotate documentation). The annotation properties just defined are then assigned to the variable “annot”; by exploiting the method .set_visible(), the visibility of the annotation framework is initially set to False (it will appear only after the mouse click).

All the parameters used in the definition of the .annotate() function are summarized in Table 2.

#Creating the annotation framework
annot = ax.annotate("", xy=(0,0), xytext=(-40,40),textcoords="offset points", bbox=dict(boxstyle="round4", fc="grey", ec="k", lw=2), arrowprops=dict(arrowstyle="-|>"))
annot.set_visible(False)

Storing and Displaying the Coordinates of the Selected Point


The cursor is now working but nothing still happens when we click on the plot. In this section, we define a function that will print and store the coordinates of the point clicked on the plot; it will also display the previously defined annotating box.

Storing the values outside the function


We define an empty list, called “coord”, in which will be stored the coordinates of all the clicked points.

After that, we start defining the new function, it will be called “onclick”. The input of the function is set to event, in order to access to the indicator position on the plot.

Within the function, a global variable called “coord” is defined, this is done in order to store the values generated within the function and to have them available also in the “coord” variable outside the function. To store the coordinates of the selected point, we append the variables event.xdata and event.ydata, as a tuple, to the list coord; in this way, the values will be accessible even outside the function. For the sake of simplicity, we then assign them to two different local variables “x” and “y”.

Printing the coordinates values


At this point we can also print their value by just typing the print() command.

Displaying the point coordinates in the annotating box


The next feature that we can add to the function is to display the annotating box, containing the “x” and “y” values. For this task, the “x” and “y” values are firstly used to define the position of the annotating box, changing the xy property of the “annot” variable and then to define the variable “text”, a string that contains the annotation text. To change the text of the “annot” variable, we use the method .set_text(), entering, as the only input parameter, the variable “text”.

We conclude by changing the visibility of the “annot” function to True and by redrawing the figure. The following code lines display the entire function definition, following the same order used in the above description.

#Function for storing and showing the clicked values
coord = []
def onclick(event): global coord coord.append((event.xdata, event.ydata)) x = event.xdata y = event.ydata print([x,y]) annot.xy = (x,y) text = "({:.2g},{:.2g})".format( x,y ) annot.set_text(text) annot.set_visible(True) fig.canvas.draw() #redraw the figure

In order to connect the clicking event with the execution of the “onclick” function, we exploit the matplotlib method .mpl_connect(), linking it with the event “button_press_event”. We finally plot the figure. Figure 2 displays the ending result.

Figure 2: Final result of the script. Every time a point on the window is clicked with the cursor, an annotating box containing the values of the point coordinates is displayed.https://blog.finxter.com/wp-content/uplo...00x159.png 300w, https://blog.finxter.com/wp-content/uplo...40x340.png 640w, https://blog.finxter.com/wp-content/uplo...150x79.png 150w" sizes="(max-width: 642px) 100vw, 642px" />
Figure 2: Final result of the script. Every time a point on the window is clicked with the cursor, an annotating box containing the values of the point coordinates is displayed.

Accessing the Stored Values Outside the Function


Since the coordinates of the selected points have all been stored in the list “coord”, it is now possible to have access to their values by just processing the list with standard functions. One example is to use the function .zip(*), in which we enter the name of the list after the asterisk, in order to unzip all the tuples into two different arrays “x1” and “y1”.

#unzipping the x and y values of the selected points
x1, y1 = zip(*coord)

Conclusion


In this article, we have seen how to introduce a cursor into a matplotlib window, how to customize its properties and appearance. We also explored the possibility of creating an annotating box and how to display it at every mouse click.

All these features will provide additional value to your plots from both an aesthetic and functional point of view, making them more enjoyable and understandable, two fundamental aspects that every data science report should always possess.

The post Matplotlib Cursor — How to Add a Cursor and Annotate Your Plot first appeared on Finxter.



https://www.sickgaming.net/blog/2021/01/...your-plot/
Reply



Possibly Related Threads…
Thread Author Replies Views Last Post
  [Tut] How to Customize Multiple Subplots in Matplotlib xSicKxBot 0 11 03-02-2021, 07:29 AM
Last Post: xSicKxBot
  [Tut] How to Plot the Confidence Interval in Python? xSicKxBot 0 26 02-23-2021, 02:00 PM
Last Post: xSicKxBot
  [Tut] Best Matplotlib Cheat Sheet xSicKxBot 0 33 01-30-2021, 09:08 AM
Last Post: xSicKxBot
  [Tut] Matplotlib Scatter Plot – Simple Illustrated Guide xSicKxBot 0 52 11-25-2020, 01:23 AM
Last Post: xSicKxBot
  [Tut] Matplotlib Animation – A Helpful Illustrated Guide xSicKxBot 0 179 05-14-2020, 12:25 AM
Last Post: xSicKxBot
  [Tut] Matplotlib 3D Plot Advanced xSicKxBot 0 243 04-20-2020, 11:42 PM
Last Post: xSicKxBot
  [Tut] Matplotlib 3D Plot – A Helpful Illustrated Guide xSicKxBot 0 230 04-03-2020, 07:47 PM
Last Post: xSicKxBot
  [Tut] Matplotlib Boxplot – A Helpful Illustrated Guide xSicKxBot 0 225 03-25-2020, 10:22 PM
Last Post: xSicKxBot
  [Tut] Matplotlib Legend – A Helpful Illustrated Guide xSicKxBot 0 310 03-17-2020, 04:33 PM
Last Post: xSicKxBot
  [Tut] Matplotlib Imshow — A Helpful Illustrated Guide xSicKxBot 0 610 03-03-2020, 06:30 PM
Last Post: xSicKxBot

Forum Jump:

[-]
Active Threads
Microsoft - New Group Transcribe app tra...
Last Post: xSicKxBot
Today 01:52 AM
» Replies: 0
» Views: 0
News - Soapbox: The Nintendo Switch Was ...
Last Post: xSicKxBot
Today 01:51 AM
» Replies: 0
» Views: 0
[Tut] Best LLC Services for Your Freelan...
Last Post: xSicKxBot
Yesterday 07:38 PM
» Replies: 0
» Views: 1
(Indie Deal) PAW Patrol, Ben10, Dead in ...
Last Post: xSicKxBot
Yesterday 07:38 PM
» Replies: 0
» Views: 3
(Free Game Key) Wargame: Red Dragon - Fr...
Last Post: xSicKxBot
Yesterday 07:38 PM
» Replies: 0
» Views: 3
Flutter 2 Release Adds Desktop and Web S...
Last Post: xSicKxBot
Yesterday 07:38 PM
» Replies: 0
» Views: 4
Microsoft - Meet the 2021 Imagine Cup Wo...
Last Post: xSicKxBot
Yesterday 07:38 PM
» Replies: 0
» Views: 2
News - Review: G-Darius HD – A True Shmu...
Last Post: xSicKxBot
Yesterday 07:37 PM
» Replies: 0
» Views: 2
News - Minute Of Islands, The Adventure ...
Last Post: xSicKxBot
Yesterday 07:37 PM
» Replies: 0
» Views: 1
News - Tales From the (Deep Stone) Crypt
Last Post: xSicKxBot
Yesterday 10:30 AM
» Replies: 0
» Views: 6

[-]
Twitter

[-]
Sponsored
Get the Deal of the Week at RefurBees.com

Copyright © SickGaming.net 2012-2020