<a id="time-series"></a>

# Time series data

A major use case for xarray is multi-dimensional time-series data.
Accordingly, we’ve copied many of features that make working with time-series
data in pandas such a joy to xarray. In most cases, we rely on pandas for the
core functionality.

## Creating datetime64 data

Xarray uses the numpy dtypes [`numpy.datetime64`](https://numpy.org/doc/stable/reference/arrays.scalars.html#numpy.datetime64) and [`numpy.timedelta64`](https://numpy.org/doc/stable/reference/arrays.scalars.html#numpy.timedelta64)
with specified units (one of `"s"`, `"ms"`, `"us"` and `"ns"`) to represent datetime
data, which offer vectorized operations with numpy and smooth integration with pandas.

To convert to or create regular arrays of [`numpy.datetime64`](https://numpy.org/doc/stable/reference/arrays.scalars.html#numpy.datetime64) data, we recommend
using [`pandas.to_datetime()`](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.to_datetime.html#pandas.to_datetime), [`pandas.DatetimeIndex`](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DatetimeIndex.html#pandas.DatetimeIndex), or [`xarray.date_range()`](../generated/xarray.date_range.html.md#xarray.date_range):

```ipython3
pd.to_datetime(["2000-01-01", "2000-02-02"])
```

```ipython3
pd.DatetimeIndex(
    ["2000-01-01 00:00:00", "2000-02-02 00:00:00"], dtype="datetime64[s]"
)
```

```ipython3
xr.date_range("2000-01-01", periods=365)
```

```ipython3
xr.date_range("2000-01-01", periods=365, unit="s")
```

#### NOTE
Care has to be taken to create the output with the wanted resolution.
For [`pandas.date_range()`](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.date_range.html#pandas.date_range) the `unit`-kwarg has to be specified
and for [`pandas.to_datetime()`](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.to_datetime.html#pandas.to_datetime) the selection of the resolution
isn’t possible at all. For that `pd.DatetimeIndex` can be used
directly. There is more in-depth information in section
[Time Coding](../internals/time-coding.html.md#internals-timecoding).

Alternatively, you can supply arrays of Python `datetime` objects. These get
converted automatically when used as arguments in xarray objects (with us-resolution):

```ipython3
import datetime

xr.Dataset({"time": datetime.datetime(2000, 1, 1)})
```

When reading or writing netCDF files, xarray automatically decodes datetime and
timedelta arrays using [CF conventions](https://cfconventions.org) (that is, by using a `units`
attribute like `'days since 2000-01-01'`).

#### NOTE
When decoding/encoding datetimes for non-standard calendars or for dates
before [1582-10-15](https://en.wikipedia.org/wiki/Gregorian_calendar), xarray uses the [cftime](https://unidata.github.io/cftime) library by default.
It was previously packaged with the `netcdf4-python` package under the
name `netcdftime` but is now distributed separately. `cftime` is an
[optional dependency](../getting-started-guide/installing.html.md#installing) of xarray.

You can manual decode arrays in this form by passing a dataset to
[`decode_cf()`](../generated/xarray.decode_cf.html.md#xarray.decode_cf):

```ipython3
attrs = {"units": "hours since 2000-01-01"}
ds = xr.Dataset({"time": ("time", [0, 1, 2, 3], attrs)})
# Default decoding to 'ns'-resolution
xr.decode_cf(ds)
```

```ipython3
# Decoding to 's'-resolution
coder = xr.coders.CFDatetimeCoder(time_unit="s")
xr.decode_cf(ds, decode_times=coder)
```

From xarray 2025.01.2 the resolution of the dates can be one of `"s"`, `"ms"`, `"us"` or `"ns"`. One limitation of using `datetime64[ns]` is that it limits the native representation of dates to those that fall between the years 1678 and 2262, which gets increased significantly with lower resolutions. When a store contains dates outside of these bounds (or dates < [1582-10-15](https://en.wikipedia.org/wiki/Gregorian_calendar) with a Gregorian, also known as standard, calendar), dates will be returned as arrays of [`cftime.datetime`](https://unidata.github.io/cftime/api.html#cftime.datetime) objects and a [`CFTimeIndex`](../generated/xarray.CFTimeIndex.html.md#xarray.CFTimeIndex) will be used for indexing.
[`CFTimeIndex`](../generated/xarray.CFTimeIndex.html.md#xarray.CFTimeIndex) enables most of the indexing functionality of a [`pandas.DatetimeIndex`](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DatetimeIndex.html#pandas.DatetimeIndex).
See [Non-standard calendars and dates outside the precision range](weather-climate.html.md#cftimeindex) for more information.

## Datetime indexing

Xarray borrows powerful indexing machinery from pandas (see [Indexing and selecting data](indexing.html.md#indexing)).

This allows for several useful and succinct forms of indexing, particularly for
`datetime64` data. For example, we support indexing with strings for single
items and with the `slice` object:

```ipython3
time = pd.date_range("2000-01-01", freq="h", periods=365 * 24)
ds = xr.Dataset({"foo": ("time", np.arange(365 * 24)), "time": time})
ds.sel(time="2000-01")
```

```ipython3
ds.sel(time=slice("2000-06-01", "2000-06-10"))
```

You can also select a particular time by indexing with a
[`datetime.time`](https://docs.python.org/3/library/datetime.html#datetime.time) object:

```ipython3
ds.sel(time=datetime.time(12))
```

For more details, read the pandas documentation and the section on [Indexing Using Datetime Components](#datetime-component-indexing) (i.e. using the `.dt` accessor).

<a id="dt-accessor"></a>

## Datetime components

Similar to [pandas accessors](https://pandas.pydata.org/pandas-docs/stable/basics.html#basics-dt-accessors), the components of datetime objects contained in a
given `DataArray` can be quickly computed using a special `.dt` accessor.

```ipython3
time = pd.date_range("2000-01-01", freq="6h", periods=365 * 4)
ds = xr.Dataset({"foo": ("time", np.arange(365 * 4)), "time": time})
ds.time.dt.hour
```

```ipython3
ds.time.dt.day_of_week
```

The `.dt` accessor works on both coordinate dimensions as well as
multi-dimensional data.

Xarray also supports a notion of “virtual” or “derived” coordinates for
[datetime components](https://pandas.pydata.org/pandas-docs/stable/api.html#time-date-components) implemented by pandas, including “year”, “month”,
“day”, “hour”, “minute”, “second”, “day_of_year”, “week”, “day_of_week”, and
“quarter”:

```ipython3
ds["time.month"]
```

```ipython3
ds["time.day_of_year"]
```

For use as a derived coordinate, xarray adds `'season'` to the list of
datetime components supported by pandas:

```ipython3
ds["time.season"]
```

```ipython3
ds["time"].dt.season
```

The set of valid seasons consists of ‘DJF’, ‘MAM’, ‘JJA’ and ‘SON’, labeled by
the first letters of the corresponding months.

You can use these shortcuts with both Datasets and DataArray coordinates.

In addition, xarray supports rounding operations `floor`, `ceil`, and `round`. These operations require that you supply a [rounding frequency as a string argument.](https://pandas.pydata.org/pandas-docs/stable/timeseries.html#offset-aliases)

```ipython3
ds["time"].dt.floor("D")
```

The `.dt` accessor can also be used to generate formatted datetime strings
for arrays utilising the same formatting as the standard [datetime.strftime](https://docs.python.org/3/library/datetime.html#strftime-strptime-behavior).

```ipython3
ds["time"].dt.strftime("%a, %b %d %H:%M")
```

<a id="datetime-component-indexing"></a>

### Indexing Using Datetime Components

You can use use the `.dt` accessor when subsetting your data as well. For example, we can subset for the month of January using the following:

```ipython3
ds.isel(time=(ds.time.dt.month == 1))
```

You can also search for multiple months (in this case January through March), using `isin`:

```ipython3
ds.isel(time=ds.time.dt.month.isin([1, 2, 3]))
```

<a id="resampling"></a>

## Resampling and grouped operations

#### SEE ALSO
For more generic documentation on grouping, see [GroupBy: Group and Bin Data](groupby.html.md#groupby).

Datetime components couple particularly well with grouped operations for analyzing features that repeat over time.
Here’s how to calculate the mean by time of day:

```ipython3
ds.groupby("time.hour").mean()
```

For upsampling or downsampling temporal resolutions, xarray offers a
[`Dataset.resample()`](../generated/xarray.Dataset.resample.html.md#xarray.Dataset.resample) method building on the core functionality
offered by the pandas method of the same name. Resample uses essentially the
same api as [`pandas.DataFrame.resample()`](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.resample.html#pandas.DataFrame.resample) [in pandas](https://pandas.pydata.org/pandas-docs/stable/timeseries.html#up-and-downsampling).

For example, we can downsample our dataset from hourly to 6-hourly:

```ipython3
ds.resample(time="6h")
```

This will create a specialized [`DatasetResample`](../generated/xarray.core.resample.DatasetResample.html.md#xarray.core.resample.DatasetResample) or [`DataArrayResample`](../generated/xarray.core.resample.DataArrayResample.html.md#xarray.core.resample.DataArrayResample)
object which saves information necessary for resampling. All of the reduction methods which work with
[`Dataset`](../generated/xarray.Dataset.html.md#xarray.Dataset) or [`DataArray`](../generated/xarray.DataArray.html.md#xarray.DataArray) objects can also be used for resampling:

```ipython3
ds.resample(time="6h").mean()
```

You can also supply an arbitrary reduction function to aggregate over each
resampling group:

```ipython3
ds.resample(time="6h").reduce(np.mean)
```

You can also resample on the time dimension while applying reducing along other dimensions at the same time
by specifying the `dim` keyword argument

```python
ds.resample(time="6h").mean(dim=["time", "latitude", "longitude"])
```

For upsampling, xarray provides six methods: `asfreq`, `ffill`, `bfill`, `pad`,
`nearest` and `interpolate`. `interpolate` extends `scipy.interpolate.interp1d()`
and supports all of its schemes. All of these resampling operations work on both
Dataset and DataArray objects with an arbitrary number of dimensions.

In order to limit the scope of the methods `ffill`, `bfill`, `pad` and
`nearest` the `tolerance` argument can be set in coordinate units.
Data that has indices outside of the given `tolerance` are set to `NaN`.

```ipython3
ds.resample(time="1h").nearest(tolerance="1h")
```

It is often desirable to center the time values after a resampling operation.
That can be accomplished by updating the resampled dataset time coordinate values
using time offset arithmetic via the [`pandas.tseries.frequencies.to_offset()`](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.tseries.frequencies.to_offset.html#pandas.tseries.frequencies.to_offset) function.

```ipython3
resampled_ds = ds.resample(time="6h").mean()
offset = pd.tseries.frequencies.to_offset("6h") / 2
resampled_ds["time"] = resampled_ds.get_index("time") + offset
resampled_ds
```

#### SEE ALSO
For more examples of using grouped operations on a time dimension, see [Toy weather data](../examples/weather-data.html.md).

<a id="seasonal-grouping"></a>

### Handling Seasons

Two extremely common time series operations are to group by seasons, and resample to a seasonal frequency.
Xarray has historically supported some simple versions of these computations.
For example, `.groupby("time.season")` (where the seasons are DJF, MAM, JJA, SON)
and resampling to a seasonal frequency using Pandas syntax: `.resample(time="QS-DEC")`.

Quite commonly one wants more flexibility in defining seasons. For these use-cases, Xarray provides
[`groupers.SeasonGrouper`](../generated/xarray.groupers.SeasonGrouper.html.md#xarray.groupers.SeasonGrouper) and [`groupers.SeasonResampler`](../generated/xarray.groupers.SeasonResampler.html.md#xarray.groupers.SeasonResampler).

```ipython3
from xarray.groupers import SeasonGrouper

ds.groupby(time=SeasonGrouper(["DJF", "MAM", "JJA", "SON"])).mean()
```

Note how the seasons are in the specified order, unlike `.groupby("time.season")` where the
seasons are sorted alphabetically.

```ipython3
ds.groupby("time.season").mean()
```

[`SeasonGrouper`](../generated/xarray.groupers.SeasonGrouper.html.md#xarray.groupers.SeasonGrouper) supports overlapping seasons:

```ipython3
ds.groupby(time=SeasonGrouper(["DJFM", "MAMJ", "JJAS", "SOND"])).mean()
```

Skipping months is allowed:

```ipython3
ds.groupby(time=SeasonGrouper(["JJAS"])).mean()
```

Use [`SeasonResampler`](../generated/xarray.groupers.SeasonResampler.html.md#xarray.groupers.SeasonResampler) to specify custom seasons.

```ipython3
from xarray.groupers import SeasonResampler

ds.resample(time=SeasonResampler(["DJF", "MAM", "JJA", "SON"])).mean()
```

[`SeasonResampler`](../generated/xarray.groupers.SeasonResampler.html.md#xarray.groupers.SeasonResampler) is smart enough to correctly handle years for seasons that
span the end of the year (e.g. DJF). By default [`SeasonResampler`](../generated/xarray.groupers.SeasonResampler.html.md#xarray.groupers.SeasonResampler) will skip any
season that is incomplete (e.g. the first DJF season for a time series that starts in Jan).
Pass the `drop_incomplete=False` kwarg to [`SeasonResampler`](../generated/xarray.groupers.SeasonResampler.html.md#xarray.groupers.SeasonResampler) to disable this behaviour.

```ipython3
from xarray.groupers import SeasonResampler

ds.resample(
    time=SeasonResampler(["DJF", "MAM", "JJA", "SON"], drop_incomplete=False)
).mean()
```

Seasons need not be of the same length:

```ipython3
ds.resample(time=SeasonResampler(["JF", "MAM", "JJAS", "OND"])).mean()
```
