Domanda Panda: bandiera valori consecutivi


Ho una serie di panda del modulo [0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0 , 0 , 1].

0: indicates economic increase.
1: indicates economic decline.

Una recessione è segnalata da due diminuzioni consecutive (1).

La fine della recessione è segnalata da due aumenti consecutivi (0).

Nel set di dati di cui sopra ho due recessioni, inizio dall'indice 3, fine all'indice 5 e inizio dall'indice 8 alla fine dell'indice 11.

Sono perso per come affrontare questo con i panda. Vorrei identificare l'indice per l'inizio e la fine della recessione. Qualsiasi assistenza sarebbe apprezzata.

Ecco il mio tentativo di pitone in un soln.

np_decline =  np.array([0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0 , 0 , 1])
recession_start_flag = 0
recession_end_flag = 0
recession_start = []
recession_end = []

for i in range(len(np_decline) - 1):
    if recession_start_flag == 0 and np_decline[i] == 1 and np_decline[i + 1] == 1:
        recession_start.append(i)
        recession_start_flag = 1
    if recession_start_flag == 1 and np_decline[i] == 0 and np_decline[i + 1] == 0:
        recession_end.append(i - 1)
        recession_start_flag = 0

print(recession_start)
print(recession_end)

È un approccio più pandas centric? Leon


10
2017-11-11 19:38


origine


risposte:


L'inizio di una corsa di 1 soddisfa la condizione

x_prev = x.shift(1)
x_next = x.shift(-1)
((x_prev != 1) & (x == 1) & (x_next == 1))

Vale a dire, il valore all'inizio di una corsa è 1 e il valore precedente non è 1 e il valore successivo è 1. Analogamente, la fine di una corsa soddisfa la condizione

((x == 1) & (x_next == 0) & (x_next2 == 0))

poiché il valore alla fine di una corsa è 1 e il successivo valore di due valori è 0. Possiamo trovare indici in cui queste condizioni sono vere utilizzando np.flatnonzero:

import numpy as np
import pandas as pd

x = pd.Series([0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0 , 0 , 1])
x_prev = x.shift(1)
x_next = x.shift(-1)
x_next2 = x.shift(-2)
df = pd.DataFrame(
    dict(start = np.flatnonzero((x_prev != 1) & (x == 1) & (x_next == 1)),
         end = np.flatnonzero((x == 1) & (x_next == 0) & (x_next2 == 0))))
print(df[['start', 'end']])

i rendimenti

   start  end
0      3    5
1      8   11

3
2017-11-11 20:36



Puoi usare shift:

df = pd.DataFrame([0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0 , 0 , 1], columns=['signal'])
df_prev = df.shift(1)['signal']
df_next = df.shift(-1)['signal']
df_next2 = df.shift(-2)['signal']
df.loc[(df_prev != 1) & (df['signal'] == 1) & (df_next == 1), 'start'] = 1
df.loc[(df['signal'] != 0) & (df_next == 0) & (df_next2 == 0), 'end'] = 1
df.fillna(0, inplace=True)
df = df.astype(int)

    signal  start  end
0        0      0    0
1        1      0    0
2        0      0    0
3        1      1    0
4        1      0    0
5        1      0    1
6        0      0    0
7        0      0    0
8        1      1    0
9        1      0    0
10       0      0    0
11       1      0    1
12       0      0    0
13       0      0    0
14       1      0    0

4
2017-11-11 20:05



Idea simile usando shift, ma scrivendo il risultato come un'unica colonna booleana:

# Boolean indexers for recession start and stops.
rec_start = (df['signal'] == 1) & (df['signal'].shift(-1) == 1)
rec_end = (df['signal'] == 0) & (df['signal'].shift(-1) == 0)

# Mark the recession start/stops as True/False.
df.loc[rec_start, 'recession'] = True
df.loc[rec_end, 'recession'] = False

# Forward fill the recession column with the last known Boolean.
# Fill any NaN's as False (i.e. locations before the first start/stop).
df['recession'] = df['recession'].ffill().fillna(False)

L'output risultante:

    signal recession
0        0     False
1        1     False
2        0     False
3        1      True
4        1      True
5        1      True
6        0     False
7        0     False
8        1      True
9        1      True
10       0      True
11       1      True
12       0     False
13       0     False
14       1     False

4
2017-11-11 20:16



uso rolling(2)

s = pd.Series([0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0 , 0 , 1])

Sottrai .5 così il rolling la somma è 1 quando inizia una recessione e -1 quando si ferma.

s2 = s.sub(.5).rolling(2).sum()

da entrambi 1 e -1 valutare a True Posso mascherare il segnale di rotazione per avviare e arrestare e ffill. Ottieni valori di verità di quando sono positivi o negativi gt(0).

pd.concat([s, s2.mask(~s2.astype(bool)).ffill().gt(0)], axis=1, keys=['signal', 'isRec'])

enter image description here


4
2017-11-11 20:15