как перегруппировать и не группировать интервалы в пандах - PullRequest
0 голосов
/ 02 марта 2019

У меня есть df с StartDate и конечными EndDate столбцами

df.loc[:,['StartDate','EndDate']].head()
Out[92]: 
                    StartDate                    EndDate
0 2016-05-19 14:19:14.820002 2016-05-19 14:19:17.899999
1 2016-05-19 14:19:32.119999 2016-05-19 14:19:37.020002

Я хотел бы получить df2 для произвольной частоты, такой как и для каждого бина количество временив той корзине, которая была включена между (StartDate, EndDate) интервалом, например,

df2 ('1s')
2016-05-19 14:19:14.000000              0.179998
2016-05-19 14:19:15.000000              1
2016-05-19 14:19:16.000000              1
2016-05-19 14:19:17.000000              0.89999
2016-05-19 14:19:18.000000              0

Конечно,

groupby(StartDate.date.dt)['Duration']

, где 'Duration' is 'EndDate'-'StartDate' не работает

1 Ответ

0 голосов
/ 02 марта 2019
import numpy as np
import pandas as pd
df = pd.DataFrame({'StartDate':['2016-05-19 14:19:14.820002','2016-05-19 14:19:32.119999', '2016-05-19 14:19:17.899999'],
                   'EndDate':['2016-05-19 14:19:17.899999', '2016-05-19 14:19:37.020002', '2016-05-19 14:19:18.5']})

df2 = pd.melt(df, var_name='type', value_name='date')
df2['date'] = pd.to_datetime(df2['date'])
df2['sign'] = np.where(df2['type']=='StartDate', 1, -1)
min_date = df2['date'].min().to_period('1s').to_timestamp()
max_date = (df2['date'].max() + pd.Timedelta('1s')).to_period('1s').to_timestamp()
index = pd.date_range(min_date, df2['date'].max(), freq='1s').union(df2['date'])
df2 = df2.groupby('date').sum()
df2 = df2.reindex(index)
df2['weight'] = df2['sign'].fillna(0).cumsum()
df2['duration'] = 0
df2.iloc[:-1, df2.columns.get_loc('duration')] = (df2.index[1:] - df2.index[:-1]).total_seconds()
df2['duration'] = df2['duration'] * df2['weight']
df2 = df2.resample('1s').sum()

print(df2)

урожайность

                     sign  weight  duration
2016-05-19 14:19:14   1.0     1.0  0.179998
2016-05-19 14:19:15   0.0     1.0  1.000000
2016-05-19 14:19:16   0.0     1.0  1.000000
2016-05-19 14:19:17   0.0     3.0  1.000000
2016-05-19 14:19:18  -1.0     1.0  0.500000
2016-05-19 14:19:19   0.0     0.0  0.000000
2016-05-19 14:19:20   0.0     0.0  0.000000
2016-05-19 14:19:21   0.0     0.0  0.000000
2016-05-19 14:19:22   0.0     0.0  0.000000
2016-05-19 14:19:23   0.0     0.0  0.000000
2016-05-19 14:19:24   0.0     0.0  0.000000
2016-05-19 14:19:25   0.0     0.0  0.000000
2016-05-19 14:19:26   0.0     0.0  0.000000
2016-05-19 14:19:27   0.0     0.0  0.000000
2016-05-19 14:19:28   0.0     0.0  0.000000
2016-05-19 14:19:29   0.0     0.0  0.000000
2016-05-19 14:19:30   0.0     0.0  0.000000
2016-05-19 14:19:31   0.0     0.0  0.000000
2016-05-19 14:19:32   1.0     1.0  0.880001
2016-05-19 14:19:33   0.0     1.0  1.000000
2016-05-19 14:19:34   0.0     1.0  1.000000
2016-05-19 14:19:35   0.0     1.0  1.000000
2016-05-19 14:19:36   0.0     1.0  1.000000
2016-05-19 14:19:37  -1.0     1.0  0.020002

Основная идея состоит в том, чтобы поместить StartDate и EndDate в один столбец и присвоить +1 каждому StartDate и-1 для каждого EndDate:

df2 = pd.melt(df, var_name='type', value_name='date')
df2['date'] = pd.to_datetime(df2['date'])
df2['sign'] = np.where(df2['type']=='StartDate', 1, -1)
#         type                       date  sign
# 0  StartDate 2016-05-19 14:19:14.820002     1
# 1  StartDate 2016-05-19 14:19:32.119999     1
# 2    EndDate 2016-05-19 14:19:17.899999    -1
# 3    EndDate 2016-05-19 14:19:37.020002    -1

Теперь создайте date индекс и затем переиндексируйте DataFrame, чтобы включить все метки времени с частотой 1 секунда:

min_date = df2['date'].min().to_period('1s').to_timestamp()
max_date = (df2['date'].max() + pd.Timedelta('1s')).to_period('1s').to_timestamp()
index = pd.date_range(min_date, df2['date'].max(), freq='1s').union(df2['date'])
df2 = df2.set_index('date')
df2 = df2.reindex(index)

#                                  type  sign
# 2016-05-19 14:19:14.000000        NaN   NaN
# 2016-05-19 14:19:14.820002  StartDate   1.0
# 2016-05-19 14:19:15.000000        NaN   NaN
# 2016-05-19 14:19:16.000000        NaN   NaN
# 2016-05-19 14:19:17.000000        NaN   NaN
# 2016-05-19 14:19:17.899999    EndDate  -1.0
# 2016-05-19 14:19:18.000000        NaN   NaN
# ...

InВ столбце sign заполните значения NaN 0 и вычислите кумулятивную сумму:

df2['weight'] = df2['sign'].fillna(0).cumsum()
#                                  type  sign  weight
# 2016-05-19 14:19:14.000000        NaN   NaN     0.0
# 2016-05-19 14:19:14.820002  StartDate   1.0     1.0
# 2016-05-19 14:19:15.000000        NaN   NaN     1.0
# 2016-05-19 14:19:16.000000        NaN   NaN     1.0
# 2016-05-19 14:19:17.000000        NaN   NaN     1.0
# 2016-05-19 14:19:17.899999    EndDate  -1.0     0.0
# 2016-05-19 14:19:18.000000        NaN   NaN     0.0
# ...

. Вычислите продолжительность времени между каждой строкой:

df2['duration'] = 0
df2.iloc[:-1, df2.columns.get_loc('duration')] = (df2.index[1:] - df2.index[:-1]).total_seconds()
df2['duration'] = df2['duration'] * df2['weight']

#                                  type  sign  weight  duration
# 2016-05-19 14:19:14.000000        NaN   NaN     0.0  0.000000
# 2016-05-19 14:19:14.820002  StartDate   1.0     1.0  0.179998
# 2016-05-19 14:19:15.000000        NaN   NaN     1.0  1.000000
# 2016-05-19 14:19:16.000000        NaN   NaN     1.0  1.000000
# 2016-05-19 14:19:17.000000        NaN   NaN     1.0  0.899999
# 2016-05-19 14:19:17.899999    EndDate  -1.0     0.0  0.000000
# 2016-05-19 14:19:18.000000        NaN   NaN     0.0  0.000000

Наконец, повторно сэмплируйте DataFrame в1-секундная частота

df2 = df2.resample('1s').sum()

Я узнал этот трюк от DSM, здесь .

...