Найти начальный и конечный индексы последовательных в панде - PullRequest
2 голосов
/ 21 октября 2019

У меня есть следующий фрейм данных:

     A    B    C
0    1    1    1
1    0    1    0
2    1    1    1
3    1    0    1
4    1    1    0
5    1    1    0 
6    0    1    1
7    0    1    0

, из которого я хочу знать начальный и конечный индексы, когда значения 1 для 3 или более последовательных значений на столбец. Желаемый результат:

Column    From    To    
     A       2     5
     B       1     3         
     B       4     7

сначала я отфильтрую те, которые не являются последовательными для 3 или более значений

filtered_df = df.copy().apply(filter, threshold=3)

, где

def filter(col, threshold=3):  
    mask = col.groupby((col != col.shift()).cumsum()).transform('count').lt(threshold)
    mask &= col.eq(1)
    col.update(col.loc[mask].replace(1,0))
    return col

filtered_df сейчасвыглядят так:

     A    B    C
0    0    1    0
1    0    1    0
2    1    1    0
3    1    0    0
4    1    1    0
5    1    1    0 
6    0    1    0
7    0    1    0

Если в кадре данных будет только один столбец с нулями и единицами, результат может быть достигнут, как в Как использовать панды для поиска последовательных одних и тех же данных во временных рядах . Однако я пытаюсь сделать что-то похожее для нескольких столбцов одновременно.

Ответы [ 2 ]

2 голосов
/ 21 октября 2019

Используйте DataFrame.pipe для применения функции для всех DataFrame.

В первом решении получите первое и последнее значение подряд 1 для каждого столбца, добавьте вывод в списки иlast concat:

def f(df, threshold=3): 
    out = []
    for col in df.columns:
        m = df[col].eq(1)
        g = (df[col] != df[col].shift()).cumsum()[m]
        mask = g.groupby(g).transform('count').ge(threshold)
        filt = g[mask].reset_index()
        output = filt.groupby(col)['index'].agg(['first','last'])
        output.insert(0, 'col', col)
        out.append(output)

    return pd.concat(out, ignore_index=True)

Или сначала изменить форму на unstack, а затем применить решение:

def f(df, threshold=3):

    df1 = df.unstack().rename_axis(('col','idx')).reset_index(name='val')
    m = df1['val'].eq(1)
    g = (df1['val'] != df1.groupby('col')['val'].shift()).cumsum()
    mask = g.groupby(g).transform('count').ge(threshold) & m
    return (df1[mask].groupby([df1['col'], g])['idx']
                    .agg(['first','last'])
                    .reset_index(level=1, drop=True)
                    .reset_index())


filtered_df = df.pipe(f, threshold=3)
print (filtered_df)
  col  first  last
0   A      2     5
1   B      0     2
2   B      4     7
filtered_df = df.pipe(f, threshold=2)
print (filtered_df)
  col  first  last
0   A      2     5
1   B      0     2
2   B      4     7
3   C      2     3
1 голос
/ 21 октября 2019

Вы можете использовать rolling, чтобы создать окно над фреймом данных. Затем вы можете применить все ваши условия и shift вернуть окно в исходное положение:

length = 3
window = df.rolling(length)
mask = (window.min() == 1) & (window.max() == 1)
mask = mask.shift(1 - length)
print(mask)

, которое печатает:

       A      B      C
0  False   True  False
1  False  False  False
2   True  False  False
3   True  False  False
4  False   True  False
5  False   True  False
6    NaN    NaN    NaN
7    NaN    NaN    NaN
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...