У меня ежедневные измерения уровня воды (гидравлика c напор) в течение нескольких лет (хранятся в серии с указателем даты и времени). Я пытаюсь вписать линию во все убывающие части данных. Эти прямые линии должны быть экстраполированы до следующего максимума данных. Если первая точка минимальна, я хочу установить прямую линию до следующего максимума. Это показано на рисунке ниже.
Мне удалось закодировать эту проблему в Python, но очень «безобразно», используя 150 строк кода (много операторов if).
Мой подход: сгладить данные путем подгонки сплайнов. Затем используйте find_peaks
из scipy.signal
, чтобы найти экстремумы (умножьте на -1, чтобы получить минимум). Поскольку эта функция не имеет дело с первым и последним пунктом, я использовал операторы if для решения этой проблемы. Затем я использую два цикла for для подгонки кривой и экстраполяции. Я использовал один для l oop в случае, если данные начинаются с минимума, а другой - в случае, если данные начинаются с максимума, поскольку границы моего «интервала подбора» и моего «интервала экстраполяции» различны для каждого случая. Если данные начинаются с минуты, я использовал прямую линию для первого интервала. Результат моего кода показан на изображении.
Изображение, показывающее результат моего кода
Есть идеи, как сделать это лучше? Без использования такого количества строк кода
Следующий фрагмент кода демонстрирует мой подход для случая, когда данные начинаются с максимума
#hydraulic_head is a series of interpolated (spline) hydraulic head measurements with a datetime index
from scipy.signal import find_peaks
import pandas as pd
import numpy as np
peak_max=hydraulic_head[find_peaks(hydraulic_head)[0]] #hydraulic head at max
peak_min=hydraulic_head[find_peaks(hydraulic_head*-1)[0]] #hydraulic head at min
for gr in range(1,len(peak_max.index),1):
interval_fit=hydraulic_head[peak_max.index[gr-1]:peak_min.index[gr-1]] #interval to fit curve from max to min
t_fit=(interval_fit.index-interval_fit.index[0]).total_seconds().values #time in seconds
parameters=np.polyfit(t_fit,interval_max_min.values,1) #fit a line
parameter_estimated[gr]=parameterss #store the paramters of the line in a dict
interval_extrapolate=hydraulic_head[peak_max.index[gr-1]:peak_max.index[gr]] #interval to extrapolate
t_extrapolate=(interval_extrapolate.index-interval_extrapolate.index[0]).total_seconds().values #transform to time
values_extrapolated=parameters[0]*t_extrapolate+parameters[1] #extrapolate the line
new_index=interval_extrapolate.index #get the index from the extrapolated interval
new_series=pd.DataFrame(data=values_extrapolated,index=new_index,columns=['extrapolated']) #new data frame with extrapolated values
interpolation_out=pd.concat([interpolation_out,new_series]) #growing frame where lines are stored
Возможный другой подход: использование масок для поиска интервалов, нумеруйте их и затем, возможно, используйте groupby, чтобы извлечь интервалы. Мне не удалось сделать это таким образом.
Это мой первый вопрос здесь. Открыты для любого улучшения формулировки вопроса