Skip to content

Commit 87bb820

Browse files
author
dcherian
committed
Add x,y kwargs for plot.line().
Supports both 1D and 2D DataArrays as input. Change variable names to make code clearer: 1. set xplt, yplt to be values that are passed to ax.plot() 2. xlabel, ylabel are axes labels 3. xdim, ydim are dimension names
1 parent e544e0d commit 87bb820

File tree

4 files changed

+107
-19
lines changed

4 files changed

+107
-19
lines changed

doc/plotting.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,16 @@ It is required to explicitly specify either
197197
Thus, we could have made the previous plot by specifying ``hue='lat'`` instead of ``x='time'``.
198198
If required, the automatic legend can be turned off using ``add_legend=False``.
199199

200+
Data along x-axis
201+
~~~~~~~~~~~~~~~~~
202+
203+
It is also possible to make line plots such that the data are on the x-axis and a co-ordinate is on the y-axis. This can be done by specifying the ``x`` and ``y`` keyword arguments.
204+
205+
.. ipython:: python
206+
207+
@savefig plotting_example_xy_kwarg.png
208+
air.isel(time=10, lon=[10, 11]).plot.line(y='lat', hue='lon')
209+
200210
Two Dimensions
201211
--------------
202212

doc/whats-new.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ Enhancements
9797
encoding/decoding of datetimes with non-standard calendars without the
9898
netCDF4 dependency (:issue:`1084`).
9999
By `Joe Hamman <https://github.com/jhamman>`_.
100+
- :py:func:`~plot.line()` learned to make plots with data on x-axis if so specified.
101+
By `Deepak Cherian <https://github.com/dcherian>`_.
100102

101103
.. _Zarr: http://zarr.readthedocs.io/
102104

xarray/plot/plot.py

Lines changed: 57 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -175,9 +175,13 @@ def line(darray, *args, **kwargs):
175175
Axis on which to plot this figure. By default, use the current axis.
176176
Mutually exclusive with ``size`` and ``figsize``.
177177
hue : string, optional
178-
Coordinate for which you want multiple lines plotted (2D inputs only).
178+
Coordinate for which you want multiple lines plotted
179+
(2D DataArrays only).
179180
x : string, optional
180-
Coordinate for x axis.
181+
1D and 2D DataArrays: Coordinate for x axis.
182+
y : string, optional
183+
1D DataArray: Can be coordinate name or DataArray.name
184+
2D DataArray: Coordinate for y axis.
181185
add_legend : boolean, optional
182186
Add legend with y axis coordinates (2D inputs only).
183187
*args, **kwargs : optional
@@ -198,43 +202,78 @@ def line(darray, *args, **kwargs):
198202
ax = kwargs.pop('ax', None)
199203
hue = kwargs.pop('hue', None)
200204
x = kwargs.pop('x', None)
205+
y = kwargs.pop('y', None)
201206
add_legend = kwargs.pop('add_legend', True)
202207

203208
ax = get_axis(figsize, size, aspect, ax)
204209

205210
if ndims == 1:
206-
xlabel, = darray.dims
207-
if x is not None and xlabel != x:
208-
raise ValueError('Input does not have specified dimension'
209-
' {!r}'.format(x))
211+
dim, = darray.dims # get the only dimension name
212+
213+
error_msg = 'must be either None or %r' % dim
214+
if darray.name:
215+
error_msg += ' or %r.' % darray.name
216+
217+
if x not in [None, dim, darray.name]:
218+
raise ValueError('x ' + error_msg)
219+
220+
if y not in [None, dim, darray.name]:
221+
raise ValueError('y ' + error_msg)
210222

211-
x = darray.coords[xlabel]
223+
if x is not None and y is not None and x == y:
224+
raise ValueError('Cannot make a plot with x=%r and y=%r' % (x, y))
225+
226+
if (x is None and y is None) or x == dim or y is darray.name:
227+
xplt = darray.coords[dim]
228+
yplt = darray
229+
xlabel = dim
230+
ylabel = darray.name
231+
232+
elif y == dim or x is darray.name:
233+
yplt = darray.coords[dim]
234+
xplt = darray
235+
xlabel = darray.name
236+
ylabel = dim
212237

213238
else:
214-
if x is None and hue is None:
239+
if x is None and y is None and hue is None:
215240
raise ValueError('For 2D inputs, please specify either hue or x.')
216241

217-
xlabel, huelabel = _infer_xy_labels(darray=darray, x=x, y=hue)
218-
x = darray.coords[xlabel]
219-
darray = darray.transpose(xlabel, huelabel)
242+
if y is None:
243+
xlabel, huelabel = _infer_xy_labels(darray=darray, x=x, y=hue)
244+
ylabel = darray.name
245+
xplt = darray.coords[xlabel]
246+
yplt = darray.transpose(xlabel, huelabel)
220247

221-
_ensure_plottable(x)
248+
else:
249+
if x is not None and x is not darray.name:
250+
raise ValueError('Cannot make a plot with x=%r and y=%r '
251+
% (x, y))
222252

223-
primitive = ax.plot(x, darray, *args, **kwargs)
253+
ylabel, huelabel = _infer_xy_labels(darray=darray, x=y, y=hue)
254+
xlabel = darray.name
255+
xplt = darray.transpose(ylabel, huelabel)
256+
yplt = darray.coords[ylabel]
224257

225-
ax.set_xlabel(xlabel)
226-
ax.set_title(darray._title_for_slice())
258+
_ensure_plottable(xplt)
227259

228-
if darray.name is not None:
229-
ax.set_ylabel(darray.name)
260+
primitive = ax.plot(xplt, yplt, *args, **kwargs)
261+
262+
if xlabel is not None:
263+
ax.set_xlabel(xlabel)
264+
265+
if ylabel is not None:
266+
ax.set_ylabel(ylabel)
267+
268+
ax.set_title(darray._title_for_slice())
230269

231270
if darray.ndim == 2 and add_legend:
232271
ax.legend(handles=primitive,
233272
labels=list(darray.coords[huelabel].values),
234273
title=huelabel)
235274

236275
# Rotate dates on xlabels
237-
if np.issubdtype(x.dtype, np.datetime64):
276+
if np.issubdtype(xplt.dtype, np.datetime64):
238277
ax.get_figure().autofmt_xdate()
239278

240279
return primitive

xarray/tests/test_plot.py

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,14 +92,42 @@ def setUp(self):
9292
def test1d(self):
9393
self.darray[:, 0, 0].plot()
9494

95-
with raises_regex(ValueError, 'dimension'):
95+
with raises_regex(ValueError, 'None'):
9696
self.darray[:, 0, 0].plot(x='dim_1')
9797

98+
def test_1d_x_y_kw(self):
99+
z = np.arange(10)
100+
da = DataArray(np.cos(z), dims=['z'], coords=[z], name='f')
101+
102+
xy = [[None, None],
103+
[None, 'f'],
104+
[None, 'z'],
105+
['f', None],
106+
['z', None],
107+
['z', 'f'],
108+
['f', 'z']]
109+
110+
f, ax = plt.subplots(2, 4)
111+
112+
for aa, (x, y) in enumerate(xy):
113+
da.plot(x=x, y=y, ax=ax.flat[aa])
114+
ax.flat[aa].set_title('x=' + str(x) + ' | '+'y='+str(y))
115+
116+
with raises_regex(ValueError, 'Cannot'):
117+
da.plot(x='z', y='z')
118+
98119
def test_2d_line(self):
99120
with raises_regex(ValueError, 'hue'):
100121
self.darray[:, :, 0].plot.line()
101122

102123
self.darray[:, :, 0].plot.line(hue='dim_1')
124+
self.darray[:, :, 0].plot.line(x='dim_1')
125+
self.darray[:, :, 0].plot.line(y='dim_1')
126+
self.darray[:, :, 0].plot.line(x='dim_0', hue='dim_1')
127+
self.darray[:, :, 0].plot.line(y='dim_0', hue='dim_1')
128+
129+
with raises_regex(ValueError, 'Cannot'):
130+
self.darray[:, :, 0].plot.line(x='dim_1', y='dim_0', hue='dim_1')
103131

104132
def test_2d_line_accepts_legend_kw(self):
105133
self.darray[:, :, 0].plot.line(x='dim_0', add_legend=False)
@@ -280,6 +308,10 @@ def test_xlabel_is_index_name(self):
280308
self.darray.plot()
281309
assert 'period' == plt.gca().get_xlabel()
282310

311+
def test_no_label_name_on_x_axis(self):
312+
self.darray.plot(y='period')
313+
self.assertEqual('', plt.gca().get_xlabel())
314+
283315
def test_no_label_name_on_y_axis(self):
284316
self.darray.plot()
285317
assert '' == plt.gca().get_ylabel()
@@ -289,6 +321,11 @@ def test_ylabel_is_data_name(self):
289321
self.darray.plot()
290322
assert self.darray.name == plt.gca().get_ylabel()
291323

324+
def test_xlabel_is_data_name(self):
325+
self.darray.name = 'temperature'
326+
self.darray.plot(x=self.darray.name)
327+
self.assertEqual(self.darray.name, plt.gca().get_xlabel())
328+
292329
def test_format_string(self):
293330
self.darray.plot.line('ro')
294331

0 commit comments

Comments
 (0)