2017-04-18 2 views
2

Ich habe ein Skript mit Pandas implementiert, aber ich bin mir nicht sicher, ob es die eleganteste/schnellste Implementierung ist. (Ich bin ein for-Schleife ...)Pandas finden max stabiles Intervall

Angenommen, Sie haben eine y-Variable (float) vs Zeit

Was ich tun muß, ist eine neue Spalte (0 oder 1) auf dem Datenrahmen erstellen unter Angabe der Intervall, in dem Y-Wert innerhalb einer erlaubten Variation stabil ist/Rausch

abs(Y_start - Y_end) < noise_allowed 

Wenn es mehr als ein Intervall ist, werden nur das Intervall, in dem Y ist, sollte maximal

enter image description here

gekennzeichnet werden,

Was wäre die eleganteste Art das zu tun? Vielleicht mit Rolling Window?

Antwort

3

Hier ist ein vektorisiert NumPy Ansatz -

def max_stable_mask(a, thresh): # thresh controls noise 
    mask = np.r_[ False, np.abs(a - a.max()) < thresh, False] 
    idx = np.flatnonzero(mask[1:] != mask[:-1]) 
    s0 = (idx[1::2] - idx[::2]).argmax() 
    valid_mask = np.zeros(a.size, dtype=int) #Use dtype=bool for mask o/p 
    valid_mask[idx[2*s0]:idx[2*s0+1]] = 1 
    return valid_mask 

Beispielläufe -

In [193]: a = np.array([0, 2, 4, 9.2, 6, 6, 9, 9 , 9.2, 9.1, 9.2, 5, 0]) 

In [194]: max_stable_mask(a, thresh = 0.5) 
Out[194]: array([0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0]) 

In [195]: max_stable_mask(a, thresh = 0.1) 
Out[195]: array([0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0]) 

In [196]: max_stable_mask(a, thresh = 0.01) 
Out[196]: array([0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0]) 

In der letzten Probe Fall mit einer Schwelle von 0.01 gibt es zwei Intervalle mit je einem Element jeden und für eine solche tie-case, wählt es das erste dieser Intervalle aus.

1

Nicht gerade elegant, aber beinhaltet keine explizite für Schleifen:

import pandas as pd 

threshold = 0.015 

# construct a test DataFrame 
df = pd.DataFrame({'y': [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.5, 0.5, 0.5, 
         0.6, 0.7, 0.8, 0.9, 1, 1.01, 1.02, 1.03, 1.04, 
         0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0]}) 
# calculate a shifted series for differencing 
df['y_diff'] = df.y.shift(1) - df.y 
# find flat areas 
df['y_flat'] = abs(df['y_diff']) < threshold 
# index blocks 
df['y_flat_index'] = (df.y_flat.shift(1) != df.y_flat).astype(int).cumsum() 
# calculate mean y for flat areas 
df['y_flat_mean'] = df[['y', 'y_flat_index']].groupby('y_flat_index').transform(lambda x: x.mean()) 
# mark the flat area with the highest mean y 
df['y_marked'] = ((df['y_flat'] == True) & (df['y_flat_mean'] == max(df['y_flat_mean']))).astype(int) 
# take into account that we shifted the series, i.e. add one more True value at the beginning, 
# unless there's only one maximum value, in which case assign mark to that index 
if sum(df['y_marked']) == 0: 
    df['y_marked'].loc[df['y'] == max(df['y'])] = 1 
else: 
    df['y_marked'].iloc[df[df['y_marked'] == 1].index - 1] = 1 
print(df) 

Ergebnis:

 y y_diff y_flat y_flat_index y_flat_mean y_marked 
0 0.00  NaN False    1  0.250 0 
1 0.10 -0.10 False    1  0.250 0 
2 0.20 -0.10 False    1  0.250 0 
3 0.30 -0.10 False    1  0.250 0 
4 0.40 -0.10 False    1  0.250 0 
5 0.50 -0.10 False    1  0.250 0 
6 0.50 0.00 True    2  0.500 0 
7 0.50 0.00 True    2  0.500 0 
8 0.50 0.00 True    2  0.500 0 
9 0.60 -0.10 False    3  0.800 0 
10 0.70 -0.10 False    3  0.800 0 
11 0.80 -0.10 False    3  0.800 0 
12 0.90 -0.10 False    3  0.800 0 
13 1.00 -0.10 False    3  0.800 1 
14 1.01 -0.01 True    4  1.025 1 
15 1.02 -0.01 True    4  1.025 1 
16 1.03 -0.01 True    4  1.025 1 
17 1.04 -0.01 True    4  1.025 1 
18 0.90 0.14 False    5  0.450 0 
19 0.80 0.10 False    5  0.450 0 
20 0.70 0.10 False    5  0.450 0 
21 0.60 0.10 False    5  0.450 0 
22 0.50 0.10 False    5  0.450 0 
23 0.40 0.10 False    5  0.450 0 
24 0.30 0.10 False    5  0.450 0 
25 0.20 0.10 False    5  0.450 0 
26 0.10 0.10 False    5  0.450 0 
27 0.00 0.10 False    5  0.450 0 

Beachten Sie, dass diese alle ebenen Flächen mit maximal y markieren wird, wenn es mehrere, außer Fälle wie [0, 2, 0, 0, 2, 2].

Verwandte Themen