interpolate - Python regulariza series temporales irregulares con interpolación lineal.
pandas interpolate (4)
El mismo resultado que obtiene @mstringer puede lograrse únicamente en pandas. El truco consiste en volver a muestrear por segundo, utilizando la interpolación para completar los valores intermedios ( .resample(''s'').interpolate()
), y luego muestrear en períodos de 15 minutos ( .resample(''15T'').asfreq()
).
import io
import pandas as pd
data = io.StringIO(''''''/
Values
1992-08-27 07:46:48,28.0
1992-08-27 08:00:48,28.2
1992-08-27 08:33:48,28.4
1992-08-27 08:43:48,28.8
1992-08-27 08:48:48,29.0
1992-08-27 08:51:48,29.2
1992-08-27 08:53:48,29.6
1992-08-27 08:56:48,29.8
1992-08-27 09:03:48,30.0
'''''')
s = pd.read_csv(data, squeeze=True)
s.index = pd.to_datetime(s.index)
res = s.resample(''s'').interpolate().resample(''15T'').asfreq().dropna()
print(res)
Salida:
1992-08-27 08:00:00 28.188571
1992-08-27 08:15:00 28.286061
1992-08-27 08:30:00 28.376970
1992-08-27 08:45:00 28.848000
1992-08-27 09:00:00 29.891429
Freq: 15T, Name: Values, dtype: float64
Tengo una serie de tiempo en pandas que se ve así:
Values
1992-08-27 07:46:48 28.0
1992-08-27 08:00:48 28.2
1992-08-27 08:33:48 28.4
1992-08-27 08:43:48 28.8
1992-08-27 08:48:48 29.0
1992-08-27 08:51:48 29.2
1992-08-27 08:53:48 29.6
1992-08-27 08:56:48 29.8
1992-08-27 09:03:48 30.0
Me gustaría volver a muestrear una serie de tiempo regular con pasos de 15 minutos donde los valores se interpolan linealmente. Básicamente me gustaría conseguir:
Values
1992-08-27 08:00:00 28.2
1992-08-27 08:15:00 28.3
1992-08-27 08:30:00 28.4
1992-08-27 08:45:00 28.8
1992-08-27 09:00:00 29.9
Sin embargo, utilizando el método de remuestreo (df.resample (''15Min'')) de Pandas, obtengo:
Values
1992-08-27 08:00:00 28.20
1992-08-27 08:15:00 NaN
1992-08-27 08:30:00 28.60
1992-08-27 08:45:00 29.40
1992-08-27 09:00:00 30.00
He probado el método de remuestreo con diferentes parámetros de ''cómo'' y ''método de relleno'', pero nunca obtuve exactamente los resultados que quería. ¿Estoy usando el método equivocado?
Me imagino que esta es una consulta bastante simple, pero he buscado en la web por un tiempo y no pude encontrar una respuesta.
Gracias de antemano por cualquier ayuda que pueda obtener.
Hace poco tuve que volver a muestrear datos de aceleración que no fueron muestreados uniformemente. En general, se tomaron muestras en la frecuencia correcta, pero tuvieron retrasos intermitentes que se acumularon.
Encontré esta pregunta y combiné las respuestas de mstringer y Alberto Garcia-Rabosco usando pandas puros y adormecidos. Este método crea un nuevo índice a la frecuencia deseada y luego se interpola sin el paso intermitente de interpolar a una frecuencia más alta.
# from Alberto Garcia-Rabosco above
import io
import pandas as pd
data = io.StringIO(''''''/
Values
1992-08-27 07:46:48,28.0
1992-08-27 08:00:48,28.2
1992-08-27 08:33:48,28.4
1992-08-27 08:43:48,28.8
1992-08-27 08:48:48,29.0
1992-08-27 08:51:48,29.2
1992-08-27 08:53:48,29.6
1992-08-27 08:56:48,29.8
1992-08-27 09:03:48,30.0
'''''')
s = pd.read_csv(data, squeeze=True)
s.index = pd.to_datetime(s.index)
Código para hacer la interpolación:
import numpy as np
# create the new index and a new series full of NaNs
new_index = pd.DatetimeIndex(start=''1992-08-27 08:00:00'',
freq=''15 min'', periods=5, yearfirst=True)
new_series = pd.Series(np.nan, index=new_index)
# concat the old and new series and remove duplicates (if any)
comb_series = pd.concat([s, new_series])
comb_series = comb_series[~comb_series.index.duplicated(keep=''first'')]
# interpolate to fill the NaNs
comb_series.interpolate(method=''time'', inplace=True)
Salida:
>>> print(comb_series[new_index])
1992-08-27 08:00:00 28.188571
1992-08-27 08:15:00 28.286061
1992-08-27 08:30:00 28.376970
1992-08-27 08:45:00 28.848000
1992-08-27 09:00:00 29.891429
Freq: 15T, dtype: float64
Como antes, puede usar cualquier método de interpolación que admita Scipy y esta técnica también funciona con DataFrames (para eso lo usé originalmente). Finalmente, tenga en cuenta que los valores predeterminados de interpolación al método ''lineal'' ignoran la información de tiempo en el índice y no funcionarán con datos espaciados de manera no uniforme.
Puedes hacer esto con traces . Primero, cree un TimeSeries
con sus medidas irregulares como lo haría con un diccionario:
ts = traces.TimeSeries([
(datetime(1992, 8, 27, 7, 46, 48), 28.0),
(datetime(1992, 8, 27, 8, 0, 48), 28.2),
...
(datetime(1992, 8, 27, 9, 3, 48), 30.0),
])
Luego regularizar usando el método de sample
:
ts.sample(
sampling_period=timedelta(minutes=15),
start=datetime(1992, 8, 27, 8),
end=datetime(1992, 8, 27, 9),
interpolate=''linear'',
)
Esto da como resultado la siguiente versión regularizada, donde los puntos grises son los datos originales y la naranja es la versión regularizada con interpolación lineal.
Los valores interpolados son:
1992-08-27 08:00:00 28.189
1992-08-27 08:15:00 28.286
1992-08-27 08:30:00 28.377
1992-08-27 08:45:00 28.848
1992-08-27 09:00:00 29.891
Se necesita un poco de trabajo, pero prueba esto. La idea básica es encontrar las dos marcas de tiempo más cercanas a cada punto de remuestreo e interpolar. np.searchsorted
se utiliza para buscar fechas más cercanas al punto de remuestreo.
# empty frame with desired index
rs = pd.DataFrame(index=df.resample(''15min'').iloc[1:].index)
# array of indexes corresponding with closest timestamp after resample
idx_after = np.searchsorted(df.index.values, rs.index.values)
# values and timestamp before/after resample
rs[''after''] = df.loc[df.index[idx_after], ''Values''].values
rs[''before''] = df.loc[df.index[idx_after - 1], ''Values''].values
rs[''after_time''] = df.index[idx_after]
rs[''before_time''] = df.index[idx_after - 1]
#calculate new weighted value
rs[''span''] = (rs[''after_time''] - rs[''before_time''])
rs[''after_weight''] = (rs[''after_time''] - rs.index) / rs[''span'']
# I got errors here unless I turn the index to a series
rs[''before_weight''] = (pd.Series(data=rs.index, index=rs.index) - rs[''before_time'']) / rs[''span'']
rs[''Values''] = rs.eval(''before * before_weight + after * after_weight'')
Después de todo eso, espero que la respuesta correcta:
In [161]: rs[''Values'']
Out[161]:
1992-08-27 08:00:00 28.011429
1992-08-27 08:15:00 28.313939
1992-08-27 08:30:00 28.223030
1992-08-27 08:45:00 28.952000
1992-08-27 09:00:00 29.908571
Freq: 15T, Name: Values, dtype: float64