Indexing and selecting data — xarray 0.8.0 documentation (2024)

Similarly to pandas objects, xarray objects support both integer and labelbased lookups along each dimension. However, xarray objects also have nameddimensions, so you can optionally use dimension names instead of relying on thepositional ordering of dimensions.

Thus in total, xarray supports four different kinds of indexing, as describedbelow and summarized in this table:

Dimension lookupIndex lookupDataArray syntaxDataset syntax
PositionalBy integerarr[:, 0]not available
PositionalBy labelarr.loc[:, 'IA']not available
By nameBy integerarr.isel(space=0) or
arr[dict(space=0)]
ds.isel(space=0) or
ds[dict(space=0)]
By nameBy labelarr.sel(space='IA') or
arr.loc[dict(space='IA')]
ds.sel(space='IA') or
ds.loc[dict(space='IA')]

Positional indexing

Indexing a DataArray directly works (mostly) just like itdoes for numpy arrays, except that the returned object is always anotherDataArray:

In [1]: arr = xr.DataArray(np.random.rand(4, 3), ...:  [('time', pd.date_range('2000-01-01', periods=4)), ...:  ('space', ['IA', 'IL', 'IN'])]) ...: In [2]: arr[:2]Out[2]: <xarray.DataArray (time: 2, space: 3)>array([[ 0.127, 0.967, 0.26 ], [ 0.897, 0.377, 0.336]])Coordinates: * time (time) datetime64[ns] 2000-01-01 2000-01-02 * space (space) |S2 'IA' 'IL' 'IN'In [3]: arr[0, 0]Out[3]: <xarray.DataArray ()>array(0.12696983303810094)Coordinates: time datetime64[ns] 2000-01-01 space |S2 'IA'In [4]: arr[:, [2, 1]]Out[4]: <xarray.DataArray (time: 4, space: 2)>array([[ 0.26 , 0.967], [ 0.336, 0.377], [ 0.123, 0.84 ], [ 0.448, 0.373]])Coordinates: * time (time) datetime64[ns] 2000-01-01 2000-01-02 2000-01-03 2000-01-04 * space (space) |S2 'IN' 'IL'

Attributes are persisted in all indexing operations.

Warning

Positional indexing deviates from the NumPy when indexing with multiplearrays like arr[[0, 1], [0, 1]], as described in Orthogonal (outer) vs. vectorized indexing.See Pointwise indexing for how to achieve this functionality inxarray.

xarray also supports label-based indexing, just like pandas. Becausewe use a pandas.Index under the hood, label based indexing is veryfast. To do label based indexing, use the loc attribute:

In [5]: arr.loc['2000-01-01':'2000-01-02', 'IA']Out[5]: <xarray.DataArray (time: 2)>array([ 0.127, 0.897])Coordinates: * time (time) datetime64[ns] 2000-01-01 2000-01-02 space |S2 'IA'

You can perform any of the label indexing operations supported by pandas,including indexing with individual, slices and arrays of labels, as well asindexing with boolean arrays. Like pandas, label based indexing in xarray isinclusive of both the start and stop bounds.

Setting values with label based indexing is also supported:

In [6]: arr.loc['2000-01-01', ['IL', 'IN']] = -10In [7]: arrOut[7]: <xarray.DataArray (time: 4, space: 3)>array([[ 0.127, -10. , -10. ], [ 0.897, 0.377, 0.336], [ 0.451, 0.84 , 0.123], [ 0.543, 0.373, 0.448]])Coordinates: * time (time) datetime64[ns] 2000-01-01 2000-01-02 2000-01-03 2000-01-04 * space (space) |S2 'IA' 'IL' 'IN'

Indexing with labeled dimensions

With labeled dimensions, we do not have to rely on dimension order and canuse them explicitly to slice data. There are two ways to do this:

  1. Use a dictionary as the argument for array positional or label based arrayindexing:

    # index by integer array indicesIn [8]: arr[dict(space=0, time=slice(None, 2))]Out[8]: <xarray.DataArray (time: 2)>array([ 0.127, 0.897])Coordinates: * time (time) datetime64[ns] 2000-01-01 2000-01-02 space |S2 'IA'# index by dimension coordinate labelsIn [9]: arr.loc[dict(time=slice('2000-01-01', '2000-01-02'))]Out[9]: <xarray.DataArray (time: 2, space: 3)>array([[ 0.127, -10. , -10. ], [ 0.897, 0.377, 0.336]])Coordinates: * time (time) datetime64[ns] 2000-01-01 2000-01-02 * space (space) |S2 'IA' 'IL' 'IN'
  2. Use the sel() and isel()convenience methods:

    # index by integer array indicesIn [10]: arr.isel(space=0, time=slice(None, 2))Out[10]: <xarray.DataArray (time: 2)>array([ 0.127, 0.897])Coordinates: * time (time) datetime64[ns] 2000-01-01 2000-01-02 space |S2 'IA'# index by dimension coordinate labelsIn [11]: arr.sel(time=slice('2000-01-01', '2000-01-02'))Out[11]: <xarray.DataArray (time: 2, space: 3)>array([[ 0.127, -10. , -10. ], [ 0.897, 0.377, 0.336]])Coordinates: * time (time) datetime64[ns] 2000-01-01 2000-01-02 * space (space) |S2 'IA' 'IL' 'IN'

The arguments to these methods can be any objects that could index the arrayalong the dimension given by the keyword, e.g., labels for an individual value,Python slice() objects or 1-dimensional arrays.

Note

We would love to be able to do indexing with labeled dimension names insidebrackets, but unfortunately, Python does yet not support indexing withkeyword arguments like arr[space=0]

Warning

Do not try to assign values when using any of the indexing methods isel,isel_points, sel or sel_points:

# DO NOT do thisarr.isel(space=0) = 0

Depending on whether the underlying numpy indexing returns a copy or aview, the method will fail, and when it fails, it will failsilently. Instead, you should use normal index assignment:

# this is safearr[dict(space=0)] = 0

Pointwise indexing

xarray pointwise indexing supports the indexing along multiple labeled dimensionsusing list-like objects. While isel() performsorthogonal indexing, the isel_points() methodprovides similar numpy indexing behavior as if you were using multiplelists to index an array (e.g. arr[[0, 1], [0, 1]] ):

# index by integer array indicesIn [12]: da = xr.DataArray(np.arange(56).reshape((7, 8)), dims=['x', 'y'])In [13]: daOut[13]: <xarray.DataArray (x: 7, y: 8)>array([[ 0, 1, 2, ..., 5, 6, 7], [ 8, 9, 10, ..., 13, 14, 15], [16, 17, 18, ..., 21, 22, 23], ...,  [32, 33, 34, ..., 37, 38, 39], [40, 41, 42, ..., 45, 46, 47], [48, 49, 50, ..., 53, 54, 55]])Coordinates: * x (x) int64 0 1 2 3 4 5 6 * y (y) int64 0 1 2 3 4 5 6 7In [14]: da.isel_points(x=[0, 1, 6], y=[0, 1, 0])Out[14]: <xarray.DataArray (points: 3)>array([ 0, 9, 48])Coordinates: y (points) int64 0 1 0 x (points) int64 0 1 6 * points (points) int64 0 1 2

There is also sel_points(), which analogouslyallows you to do point-wise indexing by label:

In [15]: times = pd.to_datetime(['2000-01-03', '2000-01-02', '2000-01-01'])In [16]: arr.sel_points(space=['IA', 'IL', 'IN'], time=times)Out[16]: <xarray.DataArray (points: 3)>array([ 0.451, 0.377, -10. ])Coordinates: time (points) datetime64[ns] 2000-01-03 2000-01-02 2000-01-01 space (points) |S2 'IA' 'IL' 'IN' * points (points) int64 0 1 2

The equivalent pandas method to sel_points islookup().

Dataset indexing

We can also use these methods to index all variables in a datasetsimultaneously, returning a new dataset:

In [17]: ds = arr.to_dataset(name='foo')In [18]: ds.isel(space=[0], time=[0])Out[18]: <xarray.Dataset>Dimensions: (space: 1, time: 1)Coordinates: * time (time) datetime64[ns] 2000-01-01 * space (space) |S2 'IA'Data variables: foo (time, space) float64 0.127In [19]: ds.sel(time='2000-01-01')Out[19]: <xarray.Dataset>Dimensions: (space: 3)Coordinates: time datetime64[ns] 2000-01-01 * space (space) |S2 'IA' 'IL' 'IN'Data variables: foo (space) float64 0.127 -10.0 -10.0In [20]: ds2 = da.to_dataset(name='bar')In [21]: ds2.isel_points(x=[0, 1, 6], y=[0, 1, 0], dim='points')Out[21]: <xarray.Dataset>Dimensions: (points: 3)Coordinates: y (points) int64 0 1 0 x (points) int64 0 1 6 * points (points) int64 0 1 2Data variables: bar (points) int64 0 9 48

Positional indexing on a dataset is not supported because the ordering ofdimensions in a dataset is somewhat ambiguous (it can vary between differentarrays). However, you can do normal indexing with labeled dimensions:

In [22]: ds[dict(space=[0], time=[0])]Out[22]: <xarray.Dataset>Dimensions: (space: 1, time: 1)Coordinates: * time (time) datetime64[ns] 2000-01-01 * space (space) |S2 'IA'Data variables: foo (time, space) float64 0.127In [23]: ds.loc[dict(time='2000-01-01')]Out[23]: <xarray.Dataset>Dimensions: (space: 3)Coordinates: time datetime64[ns] 2000-01-01 * space (space) |S2 'IA' 'IL' 'IN'Data variables: foo (space) float64 0.127 -10.0 -10.0

Using indexing to assign values to a subset of dataset (e.g.,ds[dict(space=0)] = 1) is not yet supported.

Dropping labels

The drop() method returns a new object with the listedindex labels along a dimension dropped:

In [24]: ds.drop(['IN', 'IL'], dim='space')Out[24]: <xarray.Dataset>Dimensions: (space: 1, time: 4)Coordinates: * time (time) datetime64[ns] 2000-01-01 2000-01-02 2000-01-03 2000-01-04 * space (space) |S2 'IA'Data variables: foo (time, space) float64 0.127 0.8972 0.4514 0.543

drop is both a Dataset and DataArray method.

Nearest neighbor lookups

The label based selection methods sel(),reindex() and reindex_like() allsupport method and tolerance keyword argument. The method parameter allows forenabling nearest neighbor (inexact) lookups by use of the methods 'pad','backfill' or 'nearest':

In [25]: data = xr.DataArray([1, 2, 3], dims='x')In [26]: data.sel(x=[1.1, 1.9], method='nearest')Out[26]: <xarray.DataArray (x: 2)>array([2, 3])Coordinates: * x (x) int64 1 2In [27]: data.sel(x=0.1, method='backfill')Out[27]: <xarray.DataArray ()>array(2)Coordinates: x int64 1In [28]: data.reindex(x=[0.5, 1, 1.5, 2, 2.5], method='pad')Out[28]: <xarray.DataArray (x: 5)>array([1, 2, 2, 3, 3])Coordinates: * x (x) float64 0.5 1.0 1.5 2.0 2.5

Tolerance limits the maximum distance for valid matches with an inexact lookup:

In [29]: data.reindex(x=[1.1, 1.5], method='nearest', tolerance=0.2)Out[29]: <xarray.DataArray (x: 2)>array([ 2., nan])Coordinates: * x (x) float64 1.1 1.5

Using method='nearest' or a scalar argument with .sel() requires pandasversion 0.16 or newer. Using tolerance requries pandas version 0.17 or newer.

The method parameter is not yet supported if any of the argumentsto .sel() is a slice object:

In [30]: data.sel(x=slice(1, 3), method='nearest')NotImplementedError

However, you don’t need to use method to do inexact slicing. Slicingalready returns all values inside the range (inclusive), as long as the indexlabels are monotonic increasing:

In [31]: data.sel(x=slice(0.9, 3.1))Out[31]: <xarray.DataArray (x: 2)>array([2, 3])Coordinates: * x (x) int64 1 2

Indexing axes with monotonic decreasing labels also works, as long as theslice or .loc arguments are also decreasing:

In [32]: reversed_data = data[::-1]In [33]: reversed_data.loc[3.1:0.9]Out[33]: <xarray.DataArray (x: 2)>array([3, 2])Coordinates: * x (x) int64 2 1

Masking with where

Indexing methods on xarray objects generally return a subset of the original data.However, it is sometimes useful to select an object with the same shape as theoriginal data, but with some elements masked. To do this type of selection inxarray, use where():

In [34]: arr2 = xr.DataArray(np.arange(16).reshape(4, 4), dims=['x', 'y'])In [35]: arr2.where(arr2.x + arr2.y < 4)Out[35]: <xarray.DataArray (x: 4, y: 4)>array([[ 0., 1., 2., 3.], [ 4., 5., 6., nan], [ 8., 9., nan, nan], [ 12., nan, nan, nan]])Coordinates: * x (x) int64 0 1 2 3 * y (y) int64 0 1 2 3

This is particularly useful for ragged indexing of multi-dimensional data,e.g., to apply a 2D mask to an image. Note that where follows all theusual xarray broadcasting and alignment rules for binary operations (e.g.,+) between the object being indexed and the condition, as described inComputation:

In [36]: arr2.where(arr2.y < 2)Out[36]: <xarray.DataArray (x: 4, y: 4)>array([[ 0., 1., nan, nan], [ 4., 5., nan, nan], [ 8., 9., nan, nan], [ 12., 13., nan, nan]])Coordinates: * x (x) int64 0 1 2 3 * y (y) int64 0 1 2 3

By default where maintains the original size of the data. For caseswhere the selected data size is much smaller than the original data,use of the option drop=True clips coordinateelements that are fully masked:

In [37]: arr2.where(arr2.y < 2, drop=True)Out[37]: <xarray.DataArray (x: 4, y: 2)>array([[ 0., 1.], [ 4., 5.], [ 8., 9.], [ 12., 13.]])Coordinates: * x (x) int64 0 1 2 3 * y (y) int64 0 1

Multi-level indexing

Just like pandas, advanced indexing on multi-level indexes is possible withloc and sel. You can slice a multi-index by providing multiple indexers,i.e., a tuple of slices, labels, list of labels, or any selector allowed bypandas:

In [38]: midx = pd.MultiIndex.from_product([list('abc'), [0, 1]], ....:  names=('one', 'two')) ....: In [39]: mda = xr.DataArray(np.random.rand(6, 3), ....:  [('x', midx), ('y', range(3))]) ....: In [40]: mdaOut[40]: <xarray.DataArray (x: 6, y: 3)>array([[ 0.129, 0.86 , 0.82 ], [ 0.352, 0.229, 0.777], [ 0.595, 0.138, 0.853], [ 0.236, 0.146, 0.59 ], [ 0.574, 0.061, 0.59 ], [ 0.245, 0.34 , 0.985]])Coordinates: * x (x) object ('a', 0) ('a', 1) ('b', 0) ('b', 1) ('c', 0) ('c', 1) * y (y) int64 0 1 2In [41]: mda.sel(x=(list('ab'), [0]))Out[41]: <xarray.DataArray (x: 2, y: 3)>array([[ 0.129, 0.86 , 0.82 ], [ 0.595, 0.138, 0.853]])Coordinates: * x (x) object ('a', 0) ('b', 0) * y (y) int64 0 1 2

You can also select multiple elements by providing a list of labels or tuples ora slice of tuples:

In [42]: mda.sel(x=[('a', 0), ('b', 1)])Out[42]: <xarray.DataArray (x: 2, y: 3)>array([[ 0.129, 0.86 , 0.82 ], [ 0.236, 0.146, 0.59 ]])Coordinates: * x (x) object ('a', 0) ('b', 1) * y (y) int64 0 1 2

Additionally, xarray supports dictionaries:

In [43]: mda.sel(x={'one': 'a', 'two': 0})Out[43]: <xarray.DataArray (y: 3)>array([ 0.129, 0.86 , 0.82 ])Coordinates: x object ('a', 0) * y (y) int64 0 1 2In [44]: mda.loc[{'one': 'a'}, ...]Out[44]: <xarray.DataArray (two: 2, y: 3)>array([[ 0.129, 0.86 , 0.82 ], [ 0.352, 0.229, 0.777]])Coordinates: * two (two) int64 0 1 * y (y) int64 0 1 2

Like pandas, xarray handles partial selection on multi-index (level drop).As shown in the last example above, it also renames the dimension / coordinatewhen the multi-index is reduced to a single index.

Unlike pandas, xarray does not guess whether you provide index levels ordimensions when using loc in some ambiguous cases. For example, formda.loc[{'one': 'a', 'two': 0}] and mda.loc['a', 0] xarrayalways interprets (‘one’, ‘two’) and (‘a’, 0) as the names andlabels of the 1st and 2nd dimension, respectively. You must specify alldimensions or use the ellipsis in the loc specifier, e.g. in the exampleabove, mda.loc[{'one': 'a', 'two': 0}, :] or mda.loc[('a', 0), ...].

Multi-dimensional indexing

xarray does not yet support efficient routines for generalized multi-dimensionalindexing or regridding. However, we are definitely interested in adding supportfor this in the future (see GH475 for the ongoing discussion).

Copies vs. views

Whether array indexing returns a view or a copy of the underlyingdata depends on the nature of the labels. For positional (integer)indexing, xarray follows the same rules as NumPy:

  • Positional indexing with only integers and slices returns a view.
  • Positional indexing with arrays or lists returns a copy.

The rules for label based indexing are more complex:

  • Label-based indexing with only slices returns a view.
  • Label-based indexing with arrays returns a copy.
  • Label-based indexing with scalars returns a view or a copy, dependingupon if the corresponding positional indexer can be represented as aninteger or a slice object. The exact rules are determined by pandas.

Whether data is a copy or a view is more predictable in xarray than in pandas, sounlike pandas, xarray does not produce SettingWithCopy warnings. However, youshould still avoid assignment with chained indexing.

Orthogonal (outer) vs. vectorized indexing

Indexing with xarray objects has one important difference from indexing numpyarrays: you can only use one-dimensional arrays to index xarray objects, andeach indexer is applied “orthogonally” along independent axes, instead ofusing numpy’s broadcasting rules to vectorize indexers. This means you can doindexing like this, which would require slightly more awkward syntax withnumpy arrays:

In [45]: arr[arr['time.day'] > 1, arr['space'] != 'IL']Out[45]: <xarray.DataArray (time: 3, space: 2)>array([[ 0.897, 0.336], [ 0.451, 0.123], [ 0.543, 0.448]])Coordinates: * time (time) datetime64[ns] 2000-01-02 2000-01-03 2000-01-04 * space (space) |S2 'IA' 'IN'

This is a much simpler model than numpy’s advanced indexing. If you wouldlike to do advanced-style array indexing in xarray, you have several options:

  • Pointwise indexing
  • Masking with where
  • Index the underlying NumPy array directly using .values, e.g.,
In [46]: arr.values[arr.values > 0.5]Out[46]: array([ 0.897, 0.84 , 0.543])

Align and reindex

xarray’s reindex, reindex_like and align impose a DataArray orDataset onto a new set of coordinates corresponding to dimensions. Theoriginal values are subset to the index labels still found in the new labels,and values corresponding to new labels not found in the original object arein-filled with NaN.

xarray operations that combine multiple objects generally automatically aligntheir arguments to share the same indexes. However, manual alignment can beuseful for greater control and for increased performance.

To reindex a particular dimension, use reindex():

In [47]: arr.reindex(space=['IA', 'CA'])Out[47]: <xarray.DataArray (time: 4, space: 2)>array([[ 0.127, nan], [ 0.897, nan], [ 0.451, nan], [ 0.543, nan]])Coordinates: * time (time) datetime64[ns] 2000-01-01 2000-01-02 2000-01-03 2000-01-04 * space (space) |S2 'IA' 'CA'

The reindex_like() method is a useful shortcut.To demonstrate, we will make a subset DataArray with new values:

In [48]: foo = arr.rename('foo')In [49]: baz = (10 * arr[:2, :2]).rename('baz')In [50]: bazOut[50]: <xarray.DataArray 'baz' (time: 2, space: 2)>array([[ 1.27 , -100. ], [ 8.972, 3.767]])Coordinates: * time (time) datetime64[ns] 2000-01-01 2000-01-02 * space (space) |S2 'IA' 'IL'

Reindexing foo with baz selects out the first two values along eachdimension:

In [51]: foo.reindex_like(baz)Out[51]: <xarray.DataArray 'foo' (time: 2, space: 2)>array([[ 0.127, -10. ], [ 0.897, 0.377]])Coordinates: * time (time) datetime64[ns] 2000-01-01 2000-01-02 * space (space) object 'IA' 'IL'

The opposite operation asks us to reindex to a larger shape, so we fill inthe missing values with NaN:

In [52]: baz.reindex_like(foo)Out[52]: <xarray.DataArray 'baz' (time: 4, space: 3)>array([[ 1.27 , -100. , nan], [ 8.972, 3.767, nan], [ nan, nan, nan], [ nan, nan, nan]])Coordinates: * time (time) datetime64[ns] 2000-01-01 2000-01-02 2000-01-03 2000-01-04 * space (space) object 'IA' 'IL' 'IN'

The align() function lets us perform more flexible database-like'inner', 'outer', 'left' and 'right' joins:

In [53]: xr.align(foo, baz, join='inner')Out[53]: (<xarray.DataArray 'foo' (time: 2, space: 2)> array([[ 0.127, -10. ], [ 0.897, 0.377]]) Coordinates: * time (time) datetime64[ns] 2000-01-01 2000-01-02 * space (space) object 'IA' 'IL', <xarray.DataArray 'baz' (time: 2, space: 2)> array([[ 1.27 , -100. ], [ 8.972, 3.767]]) Coordinates: * time (time) datetime64[ns] 2000-01-01 2000-01-02 * space (space) object 'IA' 'IL')In [54]: xr.align(foo, baz, join='outer')Out[54]: (<xarray.DataArray 'foo' (time: 4, space: 3)> array([[ 0.127, -10. , -10. ], [ 0.897, 0.377, 0.336], [ 0.451, 0.84 , 0.123], [ 0.543, 0.373, 0.448]]) Coordinates: * time (time) datetime64[ns] 2000-01-01 2000-01-02 2000-01-03 2000-01-04 * space (space) object 'IA' 'IL' 'IN', <xarray.DataArray 'baz' (time: 4, space: 3)> array([[ 1.27 , -100. , nan], [ 8.972, 3.767, nan], [ nan, nan, nan], [ nan, nan, nan]]) Coordinates: * time (time) datetime64[ns] 2000-01-01 2000-01-02 2000-01-03 2000-01-04 * space (space) object 'IA' 'IL' 'IN')

Both reindex_like and align work interchangeably betweenDataArray and Dataset objects, and with any number of matching dimension names:

In [55]: dsOut[55]: <xarray.Dataset>Dimensions: (space: 3, time: 4)Coordinates: * time (time) datetime64[ns] 2000-01-01 2000-01-02 2000-01-03 2000-01-04 * space (space) |S2 'IA' 'IL' 'IN'Data variables: foo (time, space) float64 0.127 -10.0 -10.0 0.8972 0.3767 0.3362 ...In [56]: ds.reindex_like(baz)Out[56]: <xarray.Dataset>Dimensions: (space: 2, time: 2)Coordinates: * time (time) datetime64[ns] 2000-01-01 2000-01-02 * space (space) object 'IA' 'IL'Data variables: foo (time, space) float64 0.127 -10.0 0.8972 0.3767In [57]: other = xr.DataArray(['a', 'b', 'c'], dims='other')# this is a no-op, because there are no shared dimension namesIn [58]: ds.reindex_like(other)Out[58]: <xarray.Dataset>Dimensions: (space: 3, time: 4)Coordinates: * time (time) datetime64[ns] 2000-01-01 2000-01-02 2000-01-03 2000-01-04 * space (space) |S2 'IA' 'IL' 'IN'Data variables: foo (time, space) float64 0.127 -10.0 -10.0 0.8972 0.3767 0.3362 ...
Indexing and selecting data — xarray 0.8.0 documentation (2024)

References

Top Articles
Latest Posts
Article information

Author: The Hon. Margery Christiansen

Last Updated:

Views: 6259

Rating: 5 / 5 (70 voted)

Reviews: 85% of readers found this page helpful

Author information

Name: The Hon. Margery Christiansen

Birthday: 2000-07-07

Address: 5050 Breitenberg Knoll, New Robert, MI 45409

Phone: +2556892639372

Job: Investor Mining Engineer

Hobby: Sketching, Cosplaying, Glassblowing, Genealogy, Crocheting, Archery, Skateboarding

Introduction: My name is The Hon. Margery Christiansen, I am a bright, adorable, precious, inexpensive, gorgeous, comfortable, happy person who loves writing and wants to share my knowledge and understanding with you.