Изменить форму массива с минимальной скоростью - PullRequest
0 голосов
/ 15 ноября 2018

У меня есть массив, который не монотонно увеличивается. Я хотел бы сделать это монотонным увеличением, применяя постоянную скорость, когда массив уменьшается.

Я создал небольшой пример, где коэффициент равен 0,2:

# Rate
rate = 0.2

# Array to interpolate
arr1 = np.array([0,1,2,3,4,4,4,3,2,2.5,3.5,5.2,7,10,9.5,np.nan,np.nan,np.nan,11.2, 11.4, 12,10,9,9.5,10.2,10.5,10.8,12,12.5,15],dtype=float)

# Line with constant rate at first monotonic decrease (index 6)
xx1 = 6
xr1 = np.array(np.arange(0,arr1.shape[0]+1),dtype=float)
yr1 = rate*xr1 + (arr1[xx1]-rate*xx1)

# Line with constant rate at second monotonic decrease [index 14]
xx2 = 13
xr2 = np.array(np.arange(0,arr1.shape[0]+1),dtype=float)
yr2 = rate*xr2 + (arr1[xx2]-rate*xx2)

# Line with constant rate at second monotonic decrease [index 14]
xx3 = 20
xr3 = np.array(np.arange(0,arr1.shape[0]+1),dtype=float)
yr3 = rate*xr3 + (arr1[xx3]-rate*xx3)

plt.figure()
plt.plot(arr1,'.-',label='Original')
plt.plot(xr1,yr1,label='Const Rate line 1')
plt.plot(xr2,yr2,label='Const Rate line 2')
plt.plot(xr3,yr3,label='Const Rate line 2')
plt.legend()
plt.grid()

Массив "Original" - это мой набор данных. Конечные результаты, которые я хотел бы получить, - синяя + красная пунктирная линия. На рисунке я выделил также «кривые постоянной скорости».

enter image description here

Поскольку у меня очень большие массивы (миллионы записей), я бы хотел избежать циклов for для всего массива.

Большое спасибо всем за помощь!

Ответы [ 5 ]

0 голосов
/ 19 ноября 2018

Мне наконец удалось сделать то, что я хотел, с помощью цикла while.

# data['myvar'] is the original dataset I want to reshape
data['myvar_corrected'] = data['myvar'].values
temp_d = data['myvar'].fillna(0).values*1.0
dtc = np.maximum.accumulate(temp_d)
data.loc[temp_d < np.maximum.accumulate(dtc),'myvar_corrected'] = float('nan')
stay_in_while = True
min_rate = 5/200000/(24*60)
idx_next = 0
while stay_in_while:
    df_temp = data.iloc[idx_next:]
    if df_tem['myvar'].isnull().sum()>0:
        idx_first_nan = df_temp.reset_index().['myvar_corrected'].isnull().argmax()

        idx_nan_or = (data_new.index.values==df_temp.index.values[idx_first_nan]).argmax()

        x = np.arange(idx_first_nan-1,df_temp.shape[0])
        y0 = df_temp.iloc[idx_first_nan-1]['myvar_corrected']
        rate_curve = min_rate*x + (y0 - min_rate*(idx_first_nan-1))

        damage_m_rate = df_temp.iloc[idx_first_nan-1:]['myvar_corrected']-rate_curve

        try:
            idx_intercept = (data_new.index.values==damage_m_rate[damage_m_rate>0].index.values[0]).argmax()
            data_new.iloc[idx_nan_or:idx_intercept]['myvar'] = rate_curve[0:(damage_m_rate.index.values==damage_m_rate[damage_m_rate>0].index.values[0]).argmax()-1]
            idx_next = idx_intercept + 1
        except:
            stay_in_while = False
    else:
        stay_in_while = False
# Finally I have my result stored in data_new['myvar']

На следующем рисунке результат.

enter image description here

Спасибо всем за вклад!

0 голосов
/ 15 ноября 2018

Ваша проблема может быть выражена в одном простом рекурсивном разностном уравнении:

y[n] = max(y[n-1] + 0.2, x[n])

Таким образом, прямая форма Python будет

def func(a):
    out = np.zeros_like(a)
    out[0] = a[0]
    for i in range(1, len(a)):
        out[i] = max(out[i-1] + 0.2, a[i])

    return out

К сожалению, это уравнение является рекурсивным и нелинейнымпоэтому поиск векторизованного алгоритма может быть затруднен.

Однако, используя Numba, мы можем ускорить этот алгоритм на основе цикла в 300 раз:

fastfunc = numba.jit(func)

arr1 = np.random.rand(1000000)

%timeit func(arr1)
# 599 ms ± 13.1 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit fastfunc(arr1)
# 2.22 ms ± 107 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
0 голосов
/ 15 ноября 2018

Я бы хотел избежать циклов for для всего массива.

Честно говоря, трудно добиться отсутствия циклов for в numpy, потому что numpy, поскольку C-made-library использует циклы for, реализованные в C / C ++. И весь алгоритм сортировки (например, np.argwhere, np.all и т. Д.) Требует сравнений и, следовательно, также итераций.

Наоборот, я предлагаю использовать хотя бы один явный цикл, созданный в Python (итерация выполняется только один раз):

arr0 = np.zeros_like(arr1)
num = 1
rate = .2
while(num < len(arr1)):
    if arr1[num] < arr1[num-1] or np.isnan(arr1[num]):
        start = arr1[num-1]
        while(start > arr1[num] or np.isnan(arr1[num])):
            print(arr1[num])
            arr0[num] = arr0[num-1] + rate
            num+=1
        continue
    arr0[num] = arr1[num]
    num +=1
0 голосов
/ 15 ноября 2018

Вот другой вариант: если вы заинтересованы в построении монотонно возрастающей кривой из ваших данных, то вы можете просто пропустить нежелательные точки между двумя последовательными точками увеличения, например, между arr1[6] = 4 и arr1[11] = 5, соединяя их линией.

import numpy as np
import matplotlib.pyplot as plt

arr1 = np.array([0,1,2,3,4,4,4,3,2,2.5,3.5,5.2,7,10,9.5,np.nan,np.nan,np.nan,11.2, 11.4, 12,10,9,9.5,10.2,10.5,10.8,12,12.5,15],dtype=float)

mask = (arr1 == np.maximum.accumulate(np.nan_to_num(arr1)))

x = np.arange(len(arr1))

plt.figure()
plt.plot(x, arr1,'.-',label='Original')
plt.plot(x[mask], arr1[mask], 'r-', label='Interp.')    
plt.legend()
plt.grid()

enter image description here

0 голосов
/ 15 ноября 2018
arr2 = arr1[1:] - arr1[:-1]
ind = numpy.where(arr2 < 0)[0]
for i in ind:
    arr1[i] = arr1[i - 1] + rate

Вам может понадобиться сначала заменить любой numpy.nan значениями, такими как numpy.amin (arr1)

...