Получите 95 процентилей переменных для данных SON, DJF, MAM за несколько лет - PullRequest
0 голосов
/ 01 марта 2019

У меня есть данные за 45 лет с именем ds в формате netCDF (.nc).Он содержит три координаты: time, latitude и longitude.

print(ds)

<xarray.Dataset>
Dimensions:    (latitude: 106, longitude: 193, time: 403248)
Coordinates:
  * latitude   (latitude) float32 -39.2 -39.149525 ... -33.950478 -33.9
  * longitude  (longitude) float32 140.8 140.84792 140.89584 ... 149.95209 150.0
  * time       (time) datetime64[ns] 1972-01-01 ... 2017-12-31T23:00:00
Data variables:
    FFDI       (time, latitude, longitude) float32 dask.array<shape=(403248, 106, 193), chunksize=(744, 106, 193)>
Attributes:
    creationTime:        1525925611
    creationTimeString:  Wed May  9 21:13:31 PDT 2018
    Conventions:         COARDS

Мне нужно рассчитать 95 процентиль FFDI по сезонам, а именно SON (сентябрь, октябрь, ноябрь), DJF (декабрь), Январь, февраль), MAM (март, апрель, май), JJA (июнь, июль, август).

da_ffdi_95th = ds['FFDI'].reduce(np.percentile, dim='time', q=95)

Это создало новый объект DataArray с переменными процентиля, но измерение времени было отброшено.

Как можно использовать groupby с функцией np.percentile?

1 Ответ

0 голосов
/ 01 марта 2019

Хотите верьте, хотите нет, но я думаю, что вы в основном там!Подробнее см. DataArrayGroupBy.reduce.

da_ffdi_95th = ds['FFDI'].groupby('time.season').reduce(
    np.percentile, dim='time', q=95)

Поскольку мы используем функцию NumPy, данные будут загружены с нетерпением.Чтобы сделать это совместимым с dask, функция, которую мы передаем reduce, должна иметь возможность работать с массивами NumPy или dask.В то время как dask реализует функцию, которая делает это, dask.array.percentile, он работает только с одномерными массивами, а не идеально подходит для функции NumPy .

К счастью, с dask.array.map_blocks достаточно легко написать нашу собственную.При этом используется реализация NumPy percentile и применяется к каждому фрагменту массива dask;единственное, к чему мы должны быть осторожны, - это убедиться, что массив, к которому мы его применяем, не разделен на части по измерению, по которому мы хотим вычислить процентиль.

import dask.array as dask_array

def dask_percentile(arr, axis=0, q=95):
    if len(arr.chunks[axis]) > 1:
        msg = ('Input array cannot be chunked along the percentile '
               'dimension.')
        raise ValueError(msg)
    return dask_array.map_blocks(np.percentile, arr, axis=axis, q=q,
                                 drop_axis=axis)

Затем мы можем написать функцию-обертку, котораявызывает соответствующую реализацию percentile в зависимости от типа входного массива (NumPy или dask):

def percentile(arr, axis=0, q=95):
    if isinstance(arr, dask_array.Array):
        return dask_percentile(arr, axis=axis, q=q)
    else:
        return np.percentile(arr, axis=axis, q=q)

Теперь, если мы вызываем reduce, убедитесь, что добавлен аргумент allow_lazy=True, этоОперация возвращает массив dask (если базовые данные хранятся в массиве dask и соответствующим образом разделены):

da_ffdi_95th = ds['FFDI'].groupby('time.season').reduce(
    percentile, dim='time', q=95, allow_lazy=True)
...