# Plotting¶

## Introduction¶

The goal of xray’s plotting is to make exploratory plotting quick and easy by using metadata from xray.DataArray objects to add informative labels.

Xray plotting functionality is a thin wrapper around the popular matplotlib library. Matplotlib syntax and function names were copied as much as possible, which makes for an easy transition between the two. Matplotlib must be installed and working before plotting with xray.

For more extensive plotting applications consider the following projects:

• Seaborn: “provides a high-level interface for drawing attractive statistical graphics.” Integrates well with pandas.
• Holoviews: “Composable, declarative data structures for building even complex visualizations easily.” Works for 2d datasets.
• Cartopy: Provides cartographic tools.

### Imports¶

# Use defaults so we don't get gridlines in generated docs
In [1]: import matplotlib as mpl

In [2]: mpl.rcdefaults()


The following imports are necessary for all of the examples.

In [3]: import numpy as np

In [4]: import matplotlib.pyplot as plt

In [5]: import xray


## One Dimension¶

### Simple Example¶

Xray uses the coordinate name to label the x axis:

In [6]: t = np.linspace(0, np.pi, num=20)

In [7]: sinpts = xray.DataArray(np.sin(t), {'t': t}, name='sin(t)')

In [8]: sinpts
Out[8]:
<xray.DataArray 'sin(t)' (t: 20)>
array([  0.000e+00,   1.646e-01,   3.247e-01, ...,   3.247e-01,   1.646e-01,   1.225e-16])
Coordinates:
* t        (t) float64 0.0 0.1653 0.3307 0.496 0.6614 0.8267 0.9921 1.157 ...

In [9]: sinpts.plot()
Out[9]: [<matplotlib.lines.Line2D at 0x7fce229e4e90>]


Additional arguments are passed directly to the matplotlib function which does the work. For example, xray.DataArray.plot_line() calls matplotlib.pyplot.plot passing in the index and the array values as x and y, respectively. So to make a line plot with blue triangles a matplotlib format string can be used:

In [10]: sinpts.plot_line('b-^')
Out[10]: [<matplotlib.lines.Line2D at 0x7fce21f5d8d0>]


Warning

Not all xray plotting methods support passing positional arguments to the wrapped matplotlib functions, but they do all support keyword arguments.

Keyword arguments work the same way, and are more explicit.

In [11]: sinpts.plot_line(color='purple', marker='o')
Out[11]: [<matplotlib.lines.Line2D at 0x7fce225917d0>]


To add the plot to an existing axis pass in the axis as a keyword argument ax. This works for all xray plotting methods. In this example axes is a tuple consisting of the left and right axes created by plt.subplots.

In [12]: fig, axes = plt.subplots(ncols=2)

In [13]: axes
Out[13]:
array([<matplotlib.axes.AxesSubplot object at 0x7fce220e7690>,
<matplotlib.axes.AxesSubplot object at 0x7fce229fe910>], dtype=object)

In [14]: sinpts.plot(ax=axes[0])
Out[14]: [<matplotlib.lines.Line2D at 0x7fce22053910>]

In [15]: sinpts.plot_hist(ax=axes[1])
Out[15]:
(array([ 2.,  2.,  0.,  2.,  2.,  0.,  2.,  2.,  2.,  6.]),
array([ 0.   ,  0.1  ,  0.199, ...,  0.797,  0.897,  0.997]),
<a list of 10 Patch objects>)

In [16]: plt.show()


On the right is a histogram created by xray.DataArray.plot_hist().

### Time Series¶

The index may be a date.

In [17]: import pandas as pd

In [18]: npts = 20

In [19]: time = pd.date_range('2015-01-01', periods=npts)

In [20]: noise = xray.DataArray(np.random.randn(npts), {'time': time})

In [21]: noise.plot_line()
Out[21]: [<matplotlib.lines.Line2D at 0x7fce22c946d0>]


TODO- rotate dates printed on x axis.

## Two Dimensions¶

### Simple Example¶

The default method xray.DataArray.plot() sees that the data is 2 dimensional. If the coordinates are uniformly spaced then it calls xray.DataArray.plot_imshow().

In [22]: a = xray.DataArray(np.zeros((4, 3)), dims=('y', 'x'))

In [23]: a[0, 0] = 1

In [24]: a
Out[24]:
<xray.DataArray (y: 4, x: 3)>
array([[ 1.,  0.,  0.],
[ 0.,  0.,  0.],
[ 0.,  0.,  0.],
[ 0.,  0.,  0.]])
Coordinates:
* x        (x) int64 0 1 2
* y        (y) int64 0 1 2 3


The plot will produce an image corresponding to the values of the array. Hence the top left pixel will be a different color than the others. Before reading on, you may want to look at the coordinates and think carefully about what the limits, labels, and orientation for each of the axes should be.

In [25]: a.plot()
Out[25]: <matplotlib.image.AxesImage at 0x7fce22092410>


It may seem strange that the values on the y axis are decreasing with -0.5 on the top. This is because the pixels are centered over their coordinates, and the axis labels and ranges correspond to the values of the coordinates.

All 2d plots in xray allow the use of the keyword arguments yincrease=True to produce a more conventional plot where the coordinates increase in the y axis. xincrease works similarly.

In [26]: a.plot(yincrease=True)
Out[26]: <matplotlib.image.AxesImage at 0x7fce21f7ba50>


### Missing Values¶

Xray plots data with Missing values.

# This data has holes in it!
In [27]: a[1, 1] = np.nan

In [28]: a.plot()
Out[28]: <matplotlib.image.AxesImage at 0x7fce21d4ff50>


### Simulated Data¶

For further examples we generate two dimensional data by computing the Euclidean distance from a 2d grid point to the origin.

In [29]: x = np.arange(start=0, stop=10, step=2)

In [30]: y = np.arange(start=9, stop=-7, step=-3)

In [31]: xy = np.dstack(np.meshgrid(x, y))

In [32]: distance = np.linalg.norm(xy, axis=2)

In [33]: distance = xray.DataArray(distance, zip(('y', 'x'), (y, x)))

In [34]: distance
Out[34]:
<xray.DataArray (y: 6, x: 5)>
array([[  9.   ,   9.22 ,   9.849,  10.817,  12.042],
[  6.   ,   6.325,   7.211,   8.485,  10.   ],
[  3.   ,   3.606,   5.   ,   6.708,   8.544],
[  0.   ,   2.   ,   4.   ,   6.   ,   8.   ],
[  3.   ,   3.606,   5.   ,   6.708,   8.544],
[  6.   ,   6.325,   7.211,   8.485,  10.   ]])
Coordinates:
* y        (y) int64 9 6 3 0 -3 -6
* x        (x) int64 0 2 4 6 8


Note the coordinate y here is decreasing. This makes the y axes appear in the conventional way.

In [35]: distance.plot()
Out[35]: <matplotlib.image.AxesImage at 0x7fce21e45a50>


### Changing Axes¶

To swap the variables plotted on vertical and horizontal axes one can transpose the array.

In [36]: distance.T.plot()
Out[36]: <matplotlib.image.AxesImage at 0x7fce21c2cbd0>


To make x and y increase:

In [37]: distance.T.plot(xincrease=True, yincrease=True)
Out[37]: <matplotlib.image.AxesImage at 0x7fce21af7e10>


### Nonuniform Coordinates¶

It’s not necessary for the coordinates to be evenly spaced. If not, then xray.DataArray.plot() produces a filled contour plot by calling xray.DataArray.plot_contourf(). This example demonstrates that by using one coordinate with logarithmic spacing.

In [38]: x = np.linspace(0, 500)

In [39]: y = np.logspace(0, 3)

In [40]: xy = np.dstack(np.meshgrid(x, y))

In [41]: d_ylog = np.linalg.norm(xy, axis=2)

In [42]: d_ylog = xray.DataArray(d_ylog, zip(('y', 'x'), (y, x)))

In [43]: d_ylog.plot()


### Calling Matplotlib¶

Since this is a thin wrapper around matplotlib, all the functionality of matplotlib is available.

In [44]: d_ylog.plot(cmap=plt.cm.Blues)

In [45]: plt.title('Euclidean distance from point to origin')
Out[45]: <matplotlib.text.Text at 0x7fce2199fc10>

In [46]: plt.xlabel('temperature (C)')
Out[46]: <matplotlib.text.Text at 0x7fce21b19310>

In [47]: plt.show()


Warning

Xray methods update label information and generally play around with the axes. So any kind of updates to the plot should be done after the call to the xray’s plot. In the example below, plt.xlabel effectively does nothing, since d_ylog.plot() updates the xlabel.

In [48]: plt.xlabel('temperature (C)')
Out[48]: <matplotlib.text.Text at 0x7fce218c56d0>

In [49]: d_ylog.plot()

In [50]: plt.show()


Contour plots can have missing values also.

In [51]: d_ylog[30:48, 10:30] = np.nan

In [52]: d_ylog.plot()

In [53]: plt.text(100, 600, 'So common...')
Out[53]: <matplotlib.text.Text at 0x7fce216fd990>

In [54]: plt.show()


### Colormaps¶

Suppose we want two plots to share the same color scale. This can be achieved by passing in the appropriate arguments and adding the color bar later.

In [55]: fig, axes = plt.subplots(ncols=2)

In [56]: kwargs = {'cmap': plt.cm.Blues, 'vmin': distance.min(), 'vmax': distance.max(), 'add_colorbar': False}

In [57]: im = distance.plot(ax=axes[0], **kwargs)

In [58]: halfd = distance / 2

In [59]: halfd.plot(ax=axes[1], **kwargs)
Out[59]: <matplotlib.image.AxesImage at 0x7fce21508e90>

In [60]: plt.colorbar(im, ax=axes.tolist())
Out[60]: <matplotlib.colorbar.Colorbar instance at 0x7fce215222d8>

In [61]: plt.show()


Here we’ve used the object returned by xray.DataArray.plot() to pass in as an argument to plt.colorbar. Take a closer look:

In [62]: im
Out[62]: <matplotlib.image.AxesImage at 0x7fce21508290>


In general xray’s plotting functions modify the axes and return the same objects that the wrapped matplotlib functions return.

## Maps¶

To follow this section you’ll need to have Cartopy installed and working.

This script will plot an image over the Atlantic ocean.

import xray
import numpy as np
import matplotlib.pyplot as plt
import cartopy.crs as ccrs

nlat = 15
nlon = 5
atlantic = xray.DataArray(np.random.randn(nlat, nlon),
coords = (np.linspace(50, 20, nlat), np.linspace(-60, -20, nlon)),
dims = ('latitude', 'longitude'))

ax = plt.axes(projection=ccrs.PlateCarree())

atlantic.plot(ax=ax)

ax.set_ylim(0, 90)
ax.set_xlim(-180, 30)
ax.coastlines()

plt.savefig('atlantic_noise.png')


Here is the resulting image:

## Details¶

There are two ways to use the xray plotting functionality:

1. Use the plot convenience methods of xray.DataArray

2. Directly from the xray plotting submodule:

import xray.plotting as xplt


The convenience method xray.DataArray.plot() dispatches to an appropriate plotting function based on the dimensions of the DataArray and whether the coordinates are sorted and uniformly spaced. This table describes what gets plotted:

 Dimensions Coordinates Plotting function 1 xray.DataArray.plot_line() 2 Uniform xray.DataArray.plot_imshow() 2 Irregular xray.DataArray.plot_contourf() Anything else xray.DataArray.plot_hist()