python: группировка или разделение данных временных рядов на основе условий - PullRequest
2 голосов
/ 06 марта 2020

Я много работаю с данными временных рядов на своей работе, и я пытался использовать python - в частности pandas - чтобы сделать некоторые работы немного быстрее. У меня есть некоторый код, который считывает данные в DataFrame и идентифицирует сегменты, где выполняются указанные условия. Затем он разделяет эти сегменты на отдельные кадры данных.

У меня есть образец DataFrame здесь:

        Date      Time  Pressure   Temp  Flow  Valve Position
0   3/5/2020  12:00:01      5.32  22.12   199            1.00
1   3/5/2020  12:00:02      5.36  22.25   115            0.95
2   3/5/2020  12:00:03      5.33  22.18   109            0.92
3   3/5/2020  12:00:04      5.38  23.51   103            0.90
4   3/5/2020  12:00:05      5.42  24.27    99            0.89
5   3/5/2020  12:00:06      5.49  25.91    92            0.85
6   3/5/2020  12:00:07      5.55  26.78    85            0.82
7   3/5/2020  12:00:08      5.61  29.88    82            0.76
8   3/5/2020  12:00:09      5.69  31.16    87            0.79
9   3/5/2020  12:00:10      5.72  32.01    97            0.87
10  3/5/2020  12:00:11      5.59  29.68   104            0.90
11  3/5/2020  12:00:12      5.53  24.55   111            0.93
12  3/5/2020  12:00:13      5.48  23.54   116            0.96
13  3/5/2020  12:00:14      5.44  23.11   119            1.00
14  3/5/2020  12:00:15      5.41  23.08   121            1.00

Код, который я написал, делает то, что я хочу, но действительно трудно следовать, и я уверен, что это оскорбительно для опытных python users.

Вот что он делает: я более или менее создаю маску на основе набора условий и беру позиции индекса для всех значений True в маске. Затем он использует функцию NumPy .diff(), чтобы определить разрыв в индексах. Внутри for l oop он разделяет маску в месте каждого определенного разрыва. Как только это будет завершено, я могу использовать теперь отдельные наборы индексов, чтобы вырезать нужные сегменты данных из моего исходного DataFrame. См. Код ниже:

import pandas as pd
import numpy as np

df = pd.read_csv('sample_data.csv')

idx = np.where((df['Temp'] > 23) & (df['Temp'] < 30))[0]
discontinuity = np.where(np.diff(idx) > 1)[0]
intervals = {}

for i in range(len(discontinuity)+1):
    if i == 0:
        intervals[i] = df.iloc[idx[0]:idx[discontinuity[i]],1]
        if len(intervals[i].values) < 1:
            del intervals[i]
    elif i == len(discontinuity):
        intervals[i] = df.iloc[idx[discontinuity[i-1]+1]:idx[-1],1]
        if len(intervals[i].values) < 1:
            del intervals[i]
    else:
        intervals[i] = df.iloc[idx[discontinuity[i-1]+1]:idx[discontinuity[i]],1] 
        if len(intervals[i].values) < 1:
            del intervals[i]


df1 = df.loc[intervals[0].index, :]
df2 = df.loc[intervals[1].index, :]

df1 и df2 содержат все данные в оригинале DataFrame, соответствующие временам (строкам), в которых 'Temp' находится между 23 и 30.

df1:

       Date      Time  Pressure   Temp  Flow  Valve Position
3  3/5/2020  12:00:04      5.38  23.51   103            0.90
4  3/5/2020  12:00:05      5.42  24.27    99            0.89
5  3/5/2020  12:00:06      5.49  25.91    92            0.85
6  3/5/2020  12:00:07      5.55  26.78    85            0.82

df2:

        Date      Time  Pressure   Temp  Flow  Valve Position
10  3/5/2020  12:00:11      5.59  29.68   104            0.90
11  3/5/2020  12:00:12      5.53  24.55   111            0.93
12  3/5/2020  12:00:13      5.48  23.54   116            0.96
13  3/5/2020  12:00:14      5.44  23.11   119            1.00

Я рад, что смог заставить это работать для меня, и я могу жить с парой линии, которые теряются при использовании этого метода, но я знаю, что это действительно пешеходный подход, и я не могу не думать, что кто-то, кто не является python, может сделать то же самое намного более чисто и эффективно.

Может ли groupby из itertools или pandas работать на это? Я не смог найти способ заставить эту работу.

1 Ответ

0 голосов
/ 06 марта 2020

Добро пожаловать в переполнение стека.

Я думаю, ваш код можно упростить следующим образом:

# Get the subset that fulfills your conditions

df_conditioned = df.query('Temp > 23 and Temp < 30').copy()
# Check for discontinuities by looking at the indices
# I created a new column called 'Group' to keep track of the continuous indices

indices = df_conditioned.index.to_series()
df_conditioned['Group'] = ((indices - indices.shift(1)) != 1).cumsum()
# Store the groups (segments with same group number) as individual frames in a list

df_list = []
for group in df_conditioned['Group'].unique():
    df_list.append(df_conditioned.query('Group == @group').drop(columns='Group'))

Надеюсь, это поможет!

...