Ищем самый быстрый способ найти значения между порогами в xarray 3600x20x20 - PullRequest
0 голосов
/ 20 мая 2019

Я хочу посчитать количество дней, когда средняя температура находится между двумя значениями (скажем, 293K и 303K).Это необходимо рассчитать для массива размером примерно 10000x20x20 (время, широта, долгота).При таких размерах эффективность кода становится проблемой.Я знаю, что цикл довольно неэффективен, но я не смог придумать другой способ кодирования этого.

Короче говоря, Я ищу более эффективный фрагмент кода , чем тот, который я вставил ниже.Любые советы или рекомендации приветствуются!

(В дополнение к вышесказанному, я довольно новичок в python, поэтому любые ваши отзывы могут быть оценены!)

Сначала у меня было три вложенных цикла(for i .. for j ... for k ..), но это заняло в 100 раз больше времени.Использование 1*(boolean) оказывается гораздо более эффективным.Я сейчас пытаюсь избавиться от своего последнего цикла (for i ..).Скорость очень важна, потому что этот скрипт будет включен в интерактивное веб-приложение.

import xarray as xr
import numpy as np
import time

# Firstly construct a data array of temperatures with dimensions latitude, longitude, time
da_t1 = xr.DataArray([[290, 295, 300, 305, 295],
                       [295, 295, 305, 295, 290],
                       [300, 300, 300, 305, 295],
                       [290, 295, 300, 305, 295],
                       [290, 295, 300, 305, 295]],
                  dims=['lat', 'lon'],
                  coords={'lat': [-5, -2.5, 0, 2.5, 5], 'lon': [33, 35, 37, 39, 41]})

da_t2 = xr.DataArray([[295, 295, 305, 295, 295],
                       [295, 295, 305, 295, 290],
                       [300, 300, 300, 305, 295],
                       [290, 300, 300, 305, 305],
                       [290, 285, 285, 285, 295]],
                  dims=['lat', 'lon'],
                  coords={'lat': [-5, -2.5, 0, 2.5, 5], 'lon': [33, 35, 37, 39, 41]})

da = xr.concat([da_t1, da_t2], 'time')


# Create an array of zeros to keep track of number of days within certain temperature range for each cell 
zeros = da[0]
zeros.values = np.zeros((da.sizes['lat'], da.sizes['lon']))


# Loop through the timesteps and the cells to count for each cell the number of days in the temperature range

trange = (293,303)


# Here's the part that could use faster performance

start = time.time()

for i in range(0, (len(da.time))):
    int_array = 1*(da.values[i] >= trange[0]) * (da.values[i] <= trange[1])
    zeros = zeros + int_array

end = time.time()

print('time elapsed: ',end-start)
print(zeros.values)

Результатом является массив, который отображает количество дней в указанном диапазоне температур за выбранный период времени.В этом случае:

zeros = 
[[1. 2. 1. 1. 2.]
 [2. 2. 0. 2. 0.]
 [2. 2. 2. 0. 2.]
 [0. 2. 2. 0. 1.]
 [0. 1. 1. 0. 2.]]

Ответы [ 2 ]

0 голосов
/ 20 мая 2019

Мой подход будет

((da >= trange[0]) & (da <= trange[1])).sum(axis=0)

результат:

# <xarray.DataArray (lat: 5, lon: 5)>
# array([[1, 2, 1, 1, 2],
#        [2, 2, 0, 2, 0],
#        [2, 2, 2, 0, 2],
#        [0, 2, 2, 0, 1],
#        [0, 1, 1, 0, 2]])
# Coordinates:
#   * lat      (lat) float64 -5.0 -2.5 0.0 2.5 5.0
#   * lon      (lon) int32 33 35 37 39 41

РЕДАКТИРОВАТЬ: измерения времени с использованием %timeit в консоли IPython:

import xarray as xr
import numpy as np

da_big = xr.DataArray(np.random.randint(290, 305, (10000, 5, 5)),
              dims=['time', 'lat', 'lon'],
              coords={'lat': [-5, -2.5, 0, 2.5, 5], 'lon': [33, 35, 37, 39, 41]})

def OP(darr, trange = (293,303)):
    zeros = darr[0]
    zeros.values = np.zeros((darr.sizes['lat'], darr.sizes['lon']))

    for i in range(0, (len(darr.time))):
        int_array = 1*(darr.values[i] >= trange[0]) * (darr.values[i] <= trange[1])
        zeros = zeros + int_array

    return zeros.values
def SumAxis(darr, trange = (293,303)):
    return ((darr >= trange[0]) & (darr <= trange[1])).sum(axis=0)

%timeit -n10 OP(da_big)
%timeit -n10 SumAxis(da_big)

# 466 ms ± 13.9 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
# 1.89 ms ± 151 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
0 голосов
/ 20 мая 2019

Просто используйте поэлементное логическое / логическое индексирование, например

in_between = np.logical_and(da.values[i] >= trange[0], da.values[i] <= trange[1])

sum_in_between = np.count_nonzero(in_between) # True = 1, False = 0

https://docs.scipy.org/doc/numpy/reference/arrays.indexing.html#boolean-array-indexing

...