Tengo datos ruidosos en intervalos de aproximadamente 1 minuto a lo largo del día.
Aquí hay una versión simple:
¿Cómo puedo identificar los valores de índice inicial y final del período menos ruidoso y de menor valor marcado en amarillo?
Aquí están los datos de la prueba:
import numpy as np import pandas as pd import matplotlib.pyplot as plt arr = np.array([8,9,7,3,6,3,2,1,2,3,1,2,3,2,2,3,2,2,5,7,8,9,15,20,21]) plt.plot(arr) plt.show()Bueno, si solo quiere esa 'área', necesita alguna forma de encontrar puntos dentro de ciertos límites. ¿Cómo podemos hacer eso? Bueno, probablemente deberíamos comenzar por encontrar el mínimo de la matriz y luego encontrar otros valores en esa misma matriz que se encuentren dentro de la desviación especificada:
def lows(arr, dev=0): lim = min(arr) + dev pts = [] for i,e in enumerate(arr): if e <= lim: pts.append((i,e)) return ptsLa función anterior devuelve una lista de puntos que se encuentran dentro de los límites especificados. El límite inferior es el mínimo de la matriz de entrada y el límite superior es el valor mínimo más la desviación que proporcionará. Por ejemplo, si desea todos los puntos dentro de 1 del valor más bajo:
plt.plot(arr) for pt in lows(arr, 1): circle = plt.Circle(pt, 0.2, color='g') plt.gca().add_patch(circle) plt.show()Para un punto dado, podemos decidir mantenerlo/enmascararlo en función de ciertos criterios:
Nota: dado que etiquetó e importó pandas, usaré pandas por conveniencia, pero las mismas ideas se pueden implementar con numpy/matplotlib puro.
Luego, un enfoque simple es usar un delta vecino con un umbral mínimo (aunque tenga cuidado con los valores atípicos en los datos reales):
s = pd.Series(np.hstack([arr, arr])) delta = 2 threshold = s.std() # check if each point's neighbors are within `delta` mask_delta = s.diff().abs().le(delta) & s.diff(-1).abs().le(delta) # check if each point is within `threshold` of the minimum mask_threshold = s < s.min() + threshold s.plot(label='raw') s.where(mask_threshold & mask_delta).plot(marker='*', label='delta & threshold')Entonces, un umbral mínimo global no funcionará ya que algunos períodos serán demasiado altos. En este caso, pruebe con un delta vecino con bloques contiguos:
# shift the second period by 5 s = pd.Series(np.hstack([arr, arr + 5])) delta = 2 blocksize = 10 # check if each point's neighbors are within `delta` mask_delta = s.diff().abs().le(delta) & s.diff(-1).abs().le(delta) # check if each point is in a contiguous block of at least `blocksize` masked = s.where(mask_delta) groups = masked.isnull().cumsum() blocksizes = masked.groupby(groups).transform('count').mask(masked.isnull()) mask_contiguous = blocksizes >= blocksize s.plot(label='raw') s.where(mask_contiguous).plot(marker='*', label='delta & contiguous')Podría intentar detectar puntos menos ruidosos midiendo la varianza de los valores en su vecindad.
Por ejemplo, para cada punto, puede mirar los últimos N valores anteriores y calcular su desviación estándar, luego marcar el punto si el estándar es inferior a algún umbral.
El siguiente código aplica este procedimiento usando el método de rolling de una serie pandas.
std_thresh = 1 window_len = 5 s = pd.Series([8,9,7,3,6,3,2,1,2,3,1,2,3,2,2,3,2,2,5,7,8,9,15,20,21]) # Create a boolean mask which marks the less noisy points marked = s.rolling(window=window_len).std() < std_thresh # Whenever a new point is marked, mark also the other points of the window (see discussion below) for i in range(window_len + 1, len(marked)): if marked[i] and ~marked[i-1]: marked[i - (window_len-1) : i] = True plt.plot(s) plt.scatter(s[marked].index, s[marked], c='orange') Puede intentar cambiar los valores de window_len (la longitud de la ventana donde calcula el std) y std_thresh (puntos cuya ventana tiene std menos de lo que están marcados) y ajustarlos según sus necesidades.
Tenga en cuenta que rolling considera una ventana que termina en cada punto, por lo que, siempre que encuentre un segmento de puntos menos ruidosos, la primera window_len-1 de ellos no se marcará. Esta es la razón por la que incluí el ciclo for en el código después de definir el marked .