-
Notifications
You must be signed in to change notification settings - Fork 0
Annotation
Sevans711 edited this page Jul 7, 2020
·
10 revisions
On this page are examples of how QOL.plots can help you annotate plots:
- Create a horizontal or vertical line and label it with text
- Put a textbox or legend at an empty spot on the plot
Before running any of the following examples, make sure to do:
import matplotlib.pyplot as plt
import numpy as np
import QOL.plots as pqol(or, go back to top)
In its simplest form, this is accomplished by:
pqol.hline(y0, "hello") #line at y=y0 [data coords] labeled "hello"
#or
pqol.vline(x0, "oh hi") #line at x=x0 [data coords] labeled "oh hi"For more complicated usage, see the images and description below.
## Labeled hline, with a black vline for reference ##
plt.plot(range(10)) #initial plot
pqol.hline(3, "hello, world!", textloc=7, textspec="data") #hline at y=3, text at x=7.
#textspec = coord system for textloc. it is "axes" by default.
pqol.vline(7, color="black") #vline at x=7.
plt.show()
## Labeled vline
plt.plot(range(10)) #initial plot
pqol.vline(3, ">LeftUp") #default textside and textdirection.
pqol.vline(3, ">LeftDown", textdirection='down') #default textside.
pqol.vline(3, ">RightUp", textside='right') #default textdirection.
pqol.vline(3, ">RightDown", textside='right', textdirection='down')
plt.show()There are many more ways to relatively easily customize text/line placement, text properties, line color/style, and more. For further information see the documentation for pqol.hline and pqol.vline, e.g. via help(pqol.hline) and help(pqol.vline).
(or, go back to top)
In its simplest form, this is accomplished by:
pqol.text("Hello, World!") #places textbox on plot, with text "Hello, World!"
#or
pqol.legend() #places legend on plotFor more complicated uses, check out the example image generated by the code below.
x = np.linspace(-4,4,20)
plt.plot(x, x, label='positive line')
annotate() #defined below
plt.show()
plt.plot(x, x, label='positive line')
plt.plot(x, -x, label='negative line')
annotate() #defined below
plt.show()
plt.plot(x, x**2, label='parabola')
annotate() #defined below
plt.show()
def annotate():
'''adds a legend and many textboxes.'''
pqol.legend()
pqol.text('look, a wild textbox!')
pqol.text('this box has\na whole new line!')
pqol.text('no border', bbox=None)
pqol.text('orange & transparent', bbox=dict(facecolor='orange', alpha=0.5))
pqol.text('small font', fontsize=10)
pqol.text('large font', fontsize=24)
pqol.text('this box is placed with extra care', iters=100)Notes (for all users of these commands):
- Each call of
pqol.textorpqol.legendautomatically searches for the best ('least cluttered') place to put the text or legend. This is especially useful when you have something you need on the plot, but don't care much where on the plot it ends up. - Any keyword arguments entered to
pqol.textorpqol.legendwill be passed toplt.textorplt.legend, respectively, so you can do any type of stylizations as you normally would, as demonstrated via the borderless, orange, small, and large text objects above. - When adding many text or legend objects to your plot, it is best practice (i.e. it will make overlapping objects least likely) to put them in the following order, since
pqol.textandpqol.legenddo not retroactively rearrange existing objects on the plot:- First, add anything with a location you want to specify.
- Next, add objects from biggest to smallest.
- If you have a textbox you need to place in a very specific location, you can use
pqol.text('text', ax_xy=axes_coordinates_for_box), orplt.text(x, y, 'text')wherexandyare in data coordinates.-
pqol.textandpqol.legendwill still take into account any text objects placed in specific locations. For example, if you place a textbox at the top right corner viaplt.text, then callpqol.text,pqol.textwill take this top-right-corner textbox into account while deciding where to add the new textbox.
-
More details (for more advanced use of these commands):
- Each call to
pqol.textorpqol.legenddoes a few iterations before finally placing the object in the best spot the code could find during those iterations. Each iteration involves placing the object at one grid point (on a 12x12 grid, by default), and deciding if that is a better spot for it than any of the previously tried spots. The code tries grid point anchors in order of least overlap with data/text/legends, to most overlap. By default, the code tries 20 iterations for a legend, and 40 iterations for a text object.- To improve selection, increase number of iterations via the
itersparameter. For example, see the code for the box "placed with extra care" in the example above. - To reduce plotting time, reduce number of iterations via the
itersparameter. - To iterate over all points in the grid, use
iters=144for the 12x12 grid, or useiters=-1for any grid size.
- To improve selection, increase number of iterations via the
- By default, the 'emptiness' of a spot is resolved on a 12x12 grid by counting the amount of data points in each grid box, blurring this image via a 5x5 gaussian kernel with
$\sigma$ =0.5 grid boxes, then determining the overlap with existing text and legend objects, weighted by the fraction of each grid box that they cover.- This produces a behavior which seems close to what would be desirable, and for most users this should be left alone.
- If you want to edit the parameters, you can do so by inputting optional keyword arguments to functions. Examine the documentation (docstrings) of the relevant functions to learn how to do this.
- In its current implementation,
pqol.textmay still lead to overlap between outlines of objects. This is mainly becauseplt.gca().transData.inverted().transform( obj.get_window_extent(renderer=plt.gcf().canvas.get_renderer() ), forobjbeing text or legend, gives an imprecise box which does not actually align with the textbox or legend outline, but is "close enough" to be worth using. (If you know a better way to learn the size of a text or legend object, please contact me and I will try to get it implemented!)