Files
notes_estom/Python/matplotlab/toolkits/axisartist.md
2020-09-26 22:03:11 +08:00

617 lines
16 KiB
Markdown

---
sidebarDepth: 3
sidebar: auto
---
# Overview of axisartist toolkit
The axisartist toolkit tutorial.
::: danger Warning
*axisartist* uses a custom Axes class
(derived from the mpl's original Axes class).
As a side effect, some commands (mostly tick-related) do not work.
:::
The *axisartist* contains a custom Axes class that is meant to support
curvilinear grids (e.g., the world coordinate system in astronomy).
Unlike mpl's original Axes class which uses Axes.xaxis and Axes.yaxis
to draw ticks, ticklines, etc., axisartist uses a special
artist (AxisArtist) that can handle ticks, ticklines, etc. for
curved coordinate systems.
Demo Floating Axis
Since it uses special artists, some Matplotlib commands that work on
Axes.xaxis and Axes.yaxis may not work.
## axisartist
The *axisartist* module provides a custom (and very experimental) Axes
class, where each axis (left, right, top, and bottom) have a separate
associated artist which is responsible for drawing the axis-line, ticks,
ticklabels, and labels. You can also create your own axis, which can pass
through a fixed position in the axes coordinate, or a fixed position
in the data coordinate (i.e., the axis floats around when viewlimit
changes).
The axes class, by default, has its xaxis and yaxis invisible, and
has 4 additional artists which are responsible for drawing the 4 axis spines in
"left", "right", "bottom", and "top". They are accessed as
ax.axis["left"], ax.axis["right"], and so on, i.e., ax.axis is a
dictionary that contains artists (note that ax.axis is still a
callable method and it behaves as an original Axes.axis method in
Matplotlib).
To create an axes,
``` python
import mpl_toolkits.axisartist as AA
fig = plt.figure()
ax = AA.Axes(fig, [0.1, 0.1, 0.8, 0.8])
fig.add_axes(ax)
```
or to create a subplot
``` python
ax = AA.Subplot(fig, 111)
fig.add_subplot(ax)
```
For example, you can hide the right and top spines using:
``` python
ax.axis["right"].set_visible(False)
ax.axis["top"].set_visible(False)
```
Simple Axisline3
It is also possible to add a horizontal axis. For example, you may have an
horizontal axis at y=0 (in data coordinate).
``` python
ax.axis["y=0"] = ax.new_floating_axis(nth_coord=0, value=0)
```
Simple Axisartist1
Or a fixed axis with some offset
``` python
# make new (right-side) yaxis, but with some offset
ax.axis["right2"] = ax.new_fixed_axis(loc="right",
offset=(20, 0))
```
### axisartist with ParasiteAxes
Most commands in the axes_grid1 toolkit can take an axes_class keyword
argument, and the commands create an axes of the given class. For example,
to create a host subplot with axisartist.Axes,
``` python
import mpl_toolkits.axisartist as AA
from mpl_toolkits.axes_grid1 import host_subplot
host = host_subplot(111, axes_class=AA.Axes)
```
Here is an example that uses ParasiteAxes.
Demo Parasite Axes2
### Curvilinear Grid
The motivation behind the AxisArtist module is to support a curvilinear grid
and ticks.
Demo Curvelinear Grid
### Floating Axes
AxisArtist also supports a Floating Axes whose outer axes are defined as
floating axis.
Demo Floating Axes
## axisartist namespace
The *axisartist* namespace includes a derived Axes implementation. The
biggest difference is that the artists responsible to draw axis line,
ticks, ticklabel and axis labels are separated out from the mpl's Axis
class, which are much more than artists in the original mpl. This
change was strongly motivated to support curvilinear grid. Here are a
few things that mpl_toolkits.axisartist.Axes is different from original
Axes from mpl.
- Axis elements (axis line(spine), ticks, ticklabel and axis labels)
are drawn by a AxisArtist instance. Unlike Axis, left, right, top
and bottom axis are drawn by separate artists. And each of them may
have different tick location and different tick labels.
- gridlines are drawn by a Gridlines instance. The change was
motivated that in curvilinear coordinate, a gridline may not cross
axis-lines (i.e., no associated ticks). In the original Axes class,
gridlines are tied to ticks.
- ticklines can be rotated if necessary (i.e, along the gridlines)
In summary, all these changes was to support
- a curvilinear grid.
- a floating axis
Demo Floating Axis
*mpl_toolkits.axisartist.Axes* class defines a *axis* attribute, which
is a dictionary of AxisArtist instances. By default, the dictionary
has 4 AxisArtist instances, responsible for drawing of left, right,
bottom and top axis.
xaxis and yaxis attributes are still available, however they are set
to not visible. As separate artists are used for rendering axis, some
axis-related method in mpl may have no effect.
In addition to AxisArtist instances, the mpl_toolkits.axisartist.Axes will
have *gridlines* attribute (Gridlines), which obviously draws grid
lines.
In both AxisArtist and Gridlines, the calculation of tick and grid
location is delegated to an instance of GridHelper class.
mpl_toolkits.axisartist.Axes class uses GridHelperRectlinear as a grid
helper. The GridHelperRectlinear class is a wrapper around the *xaxis*
and *yaxis* of mpl's original Axes, and it was meant to work as the
way how mpl's original axes works. For example, tick location changes
using set_ticks method and etc. should work as expected. But change in
artist properties (e.g., color) will not work in general, although
some effort has been made so that some often-change attributes (color,
etc.) are respected.
## AxisArtist
AxisArtist can be considered as a container artist with following
attributes which will draw ticks, labels, etc.
- line
- major_ticks, major_ticklabels
- minor_ticks, minor_ticklabels
- offsetText
- label
### line
Derived from Line2d class. Responsible for drawing a spinal(?) line.
### major_ticks, minor_ticks
Derived from Line2d class. Note that ticks are markers.
### major_ticklabels, minor_ticklabels
Derived from Text. Note that it is not a list of Text artist, but a
single artist (similar to a collection).
### axislabel
Derived from Text.
## Default AxisArtists
By default, following for axis artists are defined.:
``` python
ax.axis["left"], ax.axis["bottom"], ax.axis["right"], ax.axis["top"]
```
The ticklabels and axislabel of the top and the right axis are set to
not visible.
For example, if you want to change the color attributes of
major_ticklabels of the bottom x-axis
``` python
ax.axis["bottom"].major_ticklabels.set_color("b")
```
Similarly, to make ticklabels invisible
``` python
ax.axis["bottom"].major_ticklabels.set_visible(False)
```
AxisArtist provides a helper method to control the visibility of ticks,
ticklabels, and label. To make ticklabel invisible,
``` python
ax.axis["bottom"].toggle(ticklabels=False)
```
To make all of ticks, ticklabels, and (axis) label invisible
``` python
ax.axis["bottom"].toggle(all=False)
```
To turn all off but ticks on
``` python
ax.axis["bottom"].toggle(all=False, ticks=True)
```
To turn all on but (axis) label off
``` python
ax.axis["bottom"].toggle(all=True, label=False))
```
ax.axis's __getitem__ method can take multiple axis names. For
example, to turn ticklabels of "top" and "right" axis on,
``` python
ax.axis["top","right"].toggle(ticklabels=True))
```
Note that 'ax.axis["top","right"]' returns a simple proxy object that translate above code to something like below.
``` python
for n in ["top","right"]:
ax.axis[n].toggle(ticklabels=True))
```
So, any return values in the for loop are ignored. And you should not
use it anything more than a simple method.
Like the list indexing ":" means all items, i.e.,
``` python
ax.axis[:].major_ticks.set_color("r")
```
changes tick color in all axis.
## HowTo
1. Changing tick locations and label.
Same as the original mpl's axes.:
``` python
ax.set_xticks([1,2,3])
```
1. Changing axis properties like color, etc.
Change the properties of appropriate artists. For example, to change
the color of the ticklabels:
``` python
ax.axis["left"].major_ticklabels.set_color("r")
```
## Rotation and Alignment of TickLabels
This is also quite different from the original mpl and can be
confusing. When you want to rotate the ticklabels, first consider
using "set_axis_direction" method.
``` python
ax1.axis["left"].major_ticklabels.set_axis_direction("top")
ax1.axis["right"].label.set_axis_direction("left")
```
Simple Axis Direction01
The parameter for set_axis_direction is one of ["left", "right",
"bottom", "top"].
You must understand some underlying concept of directions.
On the other hand, there is a concept of "axis_direction". This is a
default setting of above properties for each, "bottom", "left", "top",
and "right" axis.
---
?
?
left
bottom
right
top
axislabel
direction
'-'
'+'
'+'
'-'
axislabel
rotation
180
0
0
180
axislabel
va
center
top
center
bottom
axislabel
ha
right
center
right
center
ticklabel
direction
'-'
'+'
'+'
'-'
ticklabels
rotation
90
0
-90
180
ticklabel
ha
right
center
right
center
ticklabel
va
center
baseline
center
baseline
And, 'set_axis_direction("top")' means to adjust the text rotation
etc, for settings suitable for "top" axis. The concept of axis
direction can be more clear with curved axis.
Demo Axis Direction
The axis_direction can be adjusted in the AxisArtist level, or in the
level of its child artists, i.e., ticks, ticklabels, and axis-label.
``` python
ax1.axis["left"].set_axis_direction("top")
```
changes axis_direction of all the associated artist with the "left"
axis, while
``` python
ax1.axis["left"].major_ticklabels.set_axis_direction("top")
```
changes the axis_direction of only the major_ticklabels. Note that
set_axis_direction in the AxisArtist level changes the
ticklabel_direction and label_direction, while changing the
axis_direction of ticks, ticklabels, and axis-label does not affect
them.
If you want to make ticks outward and ticklabels inside the axes,
use invert_ticklabel_direction method.
``` python
ax.axis[:].invert_ticklabel_direction()
```
A related method is "set_tick_out". It makes ticks outward (as a
matter of fact, it makes ticks toward the opposite direction of the
default direction).
``` python
ax.axis[:].major_ticks.set_tick_out(True)
```
Simple Axis Direction03
So, in summary,
- AxisArtist's methods
set_axis_direction : "left", "right", "bottom", or "top"
set_ticklabel_direction : "+" or "-"
set_axislabel_direction : "+" or "-"
invert_ticklabel_direction
- set_axis_direction : "left", "right", "bottom", or "top"
- set_ticklabel_direction : "+" or "-"
- set_axislabel_direction : "+" or "-"
- invert_ticklabel_direction
- Ticks' methods (major_ticks and minor_ticks)
set_tick_out : True or False
set_ticksize : size in points
- set_tick_out : True or False
- set_ticksize : size in points
- TickLabels' methods (major_ticklabels and minor_ticklabels)
set_axis_direction : "left", "right", "bottom", or "top"
set_rotation : angle with respect to the reference direction
set_ha and set_va : see below
- set_axis_direction : "left", "right", "bottom", or "top"
- set_rotation : angle with respect to the reference direction
- set_ha and set_va : see below
- AxisLabels' methods (label)
set_axis_direction : "left", "right", "bottom", or "top"
set_rotation : angle with respect to the reference direction
set_ha and set_va
- set_axis_direction : "left", "right", "bottom", or "top"
- set_rotation : angle with respect to the reference direction
- set_ha and set_va
### Adjusting ticklabels alignment
Alignment of TickLabels are treated specially. See below
Demo Ticklabel Alignment
### Adjusting pad
To change the pad between ticks and ticklabels
``` python
ax.axis["left"].major_ticklabels.set_pad(10)
```
Or ticklabels and axis-label
``` python
ax.axis["left"].label.set_pad(10)
```
Simple Axis Pad
## GridHelper
To actually define a curvilinear coordinate, you have to use your own
grid helper. A generalised version of grid helper class is supplied
and this class should suffice in most of cases. A user may provide
two functions which defines a transformation (and its inverse pair)
from the curved coordinate to (rectilinear) image coordinate. Note that
while ticks and grids are drawn for curved coordinate, the data
transform of the axes itself (ax.transData) is still rectilinear
(image) coordinate.
``` python
from mpl_toolkits.axisartist.grid_helper_curvelinear \
import GridHelperCurveLinear
from mpl_toolkits.axisartist import Subplot
# from curved coordinate to rectlinear coordinate.
def tr(x, y):
x, y = np.asarray(x), np.asarray(y)
return x, y-x
# from rectlinear coordinate to curved coordinate.
def inv_tr(x,y):
x, y = np.asarray(x), np.asarray(y)
return x, y+x
grid_helper = GridHelperCurveLinear((tr, inv_tr))
ax1 = Subplot(fig, 1, 1, 1, grid_helper=grid_helper)
fig.add_subplot(ax1)
```
You may use matplotlib's Transform instance instead (but a
inverse transformation must be defined). Often, coordinate range in a
curved coordinate system may have a limited range, or may have
cycles. In those cases, a more customized version of grid helper is
required.
``` python
import mpl_toolkits.axisartist.angle_helper as angle_helper
# PolarAxes.PolarTransform takes radian. However, we want our coordinate
# system in degree
tr = Affine2D().scale(np.pi/180., 1.) + PolarAxes.PolarTransform()
# extreme finder : find a range of coordinate.
# 20, 20 : number of sampling points along x, y direction
# The first coordinate (longitude, but theta in polar)
# has a cycle of 360 degree.
# The second coordinate (latitude, but radius in polar) has a minimum of 0
extreme_finder = angle_helper.ExtremeFinderCycle(20, 20,
lon_cycle = 360,
lat_cycle = None,
lon_minmax = None,
lat_minmax = (0, np.inf),
)
# Find a grid values appropriate for the coordinate (degree,
# minute, second). The argument is a approximate number of grids.
grid_locator1 = angle_helper.LocatorDMS(12)
# And also uses an appropriate formatter. Note that,the
# acceptable Locator and Formatter class is a bit different than
# that of mpl's, and you cannot directly use mpl's Locator and
# Formatter here (but may be possible in the future).
tick_formatter1 = angle_helper.FormatterDMS()
grid_helper = GridHelperCurveLinear(tr,
extreme_finder=extreme_finder,
grid_locator1=grid_locator1,
tick_formatter1=tick_formatter1
)
```
Again, the *transData* of the axes is still a rectilinear coordinate
(image coordinate). You may manually do conversion between two
coordinates, or you may use Parasite Axes for convenience.:
``` python
ax1 = SubplotHost(fig, 1, 2, 2, grid_helper=grid_helper)
# A parasite axes with given transform
ax2 = ParasiteAxesAuxTrans(ax1, tr, "equal")
# note that ax2.transData == tr + ax1.transData
# Anything you draw in ax2 will match the ticks and grids of ax1.
ax1.parasites.append(ax2)
```
Demo Curvelinear Grid
## FloatingAxis
A floating axis is an axis one of whose data coordinate is fixed, i.e,
its location is not fixed in Axes coordinate but changes as axes data
limits changes. A floating axis can be created using
*new_floating_axis* method. However, it is your responsibility that
the resulting AxisArtist is properly added to the axes. A recommended
way is to add it as an item of Axes's axis attribute.:
``` python
# floating axis whose first (index starts from 0) coordinate
# (theta) is fixed at 60
ax1.axis["lat"] = axis = ax1.new_floating_axis(0, 60)
axis.label.set_text(r"$\theta = 60^{\circ}$")
axis.label.set_visible(True)
```
See the first example of this page.
## Current Limitations and TODO's
The code need more refinement. Here is a incomplete list of issues and TODO's
- No easy way to support a user customized tick location (for
curvilinear grid). A new Locator class needs to be created.
- FloatingAxis may have coordinate limits, e.g., a floating axis of x
= 0, but y only spans from 0 to 1.
- The location of axislabel of FloatingAxis needs to be optionally
given as a coordinate value. ex, a floating axis of x=0 with label at y=1
## Download
- [Download Python source code: axisartist.py](https://matplotlib.org/_downloads/009aa8b612fe75c3b3046dbffcd0d1c7/axisartist.py)
- [Download Jupyter notebook: axisartist.ipynb](https://matplotlib.org/_downloads/47bc25cb4e9c18eccf1385f78c4ea405/axisartist.ipynb)