как динамически добавлять временные интервалы в пандах - PullRequest
0 голосов
/ 30 сентября 2018

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

 code    start time      end time     quantity   time_diff(in mins) lpm
 123     12:37:00        13:35:00     6000       58                 103.44
 124     15:37:00        15:53:00     1000       16                 62.5

 time_diff = end_time - start_time
 lpm = quantity / time_diff

Теперь я хочу разделить это количество на полчаса, например, следующее

 code   half_hourly_bucket     quantity
 123    12:30:01-13:00:00      2379.35
 123    13:00:01-13:30:00      3103.50
 123    13:30:01-14:00:00      517.25
 124    15:30:01-16:00:00      1000

например, если мы рассмотрим первое наблюдение, его start_time и end_time делится на 3 ведра по полчаса, поэтому с 12:37:00 to 13:00:00 было распределено 2379,35 литров топлива, как и для других ведер.Когда start_time and end_time попадает в те же полчаса, нам не нужно ничего делать.

Как это можно реализовать в пандах?Я новичок в Python.

1 Ответ

0 голосов
/ 30 сентября 2018

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

import pandas as pd
from datetime import datetime, time, timedelta, date
import random

# --- make demo table ---

random.seed( 0 )

def makeRandomTable():
    data = []
    hour = 12
    code = 100

    for i in range(10):
        row = { 'code': code }
        code += 1
        if random.random() < 0.18:
            hour += 1
        minute = random.randint(0,59)
        row[ 'start_time' ] = datetime.combine( date.today(), time( hour=hour, minute=minute ) )
        row[ 'end_time' ] = row[ 'start_time' ] + timedelta( minutes=random.randint( 0, 80 ) )
        row[ 'quantity' ] = random.randint(1000,9000)
        data.append( row )

    df = pd.DataFrame( data )
    df[ 'time_diff' ] = df[ 'end_time' ] - df[ 'start_time' ]
    df[ 'time_diff' ] = df[ 'time_diff' ].apply( lambda x: x.total_seconds() / 60.0 )
    return df[ ['code', 'start_time', 'end_time', 'time_diff', 'quantity' ] ]


df = makeRandomTable()
print( df )

Так что мой вводданные выглядят примерно так:

   code          start_time            end_time  time_diff  quantity
0   100 2018-09-30 12:48:00 2018-09-30 13:41:00       53.0      1331
1   101 2018-09-30 12:32:00 2018-09-30 13:34:00       62.0      4317
2   102 2018-09-30 12:53:00 2018-09-30 13:31:00       38.0      8928
3   103 2018-09-30 12:37:00 2018-09-30 13:04:00       27.0      5134
4   104 2018-09-30 13:08:00 2018-09-30 13:20:00       12.0      6065
5   105 2018-09-30 13:58:00 2018-09-30 15:06:00       68.0      6776
6   106 2018-09-30 13:57:00 2018-09-30 14:15:00       18.0      3540
7   107 2018-09-30 14:04:00 2018-09-30 14:46:00       42.0      4867
8   108 2018-09-30 14:22:00 2018-09-30 15:17:00       55.0      3590
9   109 2018-09-30 14:58:00 2018-09-30 15:24:00       26.0      8918

Сначала вам нужно получить 30-минутное округленное время начала и окончания для всего периода:

# determine period start timestamp
min_start = df[ 'start_time' ].min()
if min_start.round('30min') != min_start:
    ts_start = (min_start - timedelta(minutes=15)).round('30min')
else:
    ts_start = min_start

# determine period end timestamp
max_end = df[ 'end_time' ].max()
if max_end.round('30min') != max_end:
    ts_end = (max_end + timedelta(minutes=15)).round('30min')
else:
    ts_end = max_end

А теперь для повторной выборки входных данных:

baseindex = pd.DatetimeIndex( freq='30min', start=ts_start, end=ts_end )
rows = []
for start, end in zip( baseindex, baseindex[1:] ):
    # filter non-overlapping periods
    exclude_mask = (df[ 'end_time' ] < start) | (df[ 'start_time' ] > end)
    for code, dfg in df[ ~exclude_mask ].groupby( 'code' ):
        overlap = min( dfg.end_time.iat[0], end ) - max( dfg.start_time.iat[0], start )
        rows.append( { 'start': start,
                       'code': code,
                       'overlap': overlap.total_seconds() / 60.0, # in mins
                       'orig_quantity': dfg.quantity.iat[0],
                       'orig_start_time': dfg.start_time.iat[0],
                       'orig_end_time': dfg.end_time.iat[0],
                       'orig_time_diff' : dfg.time_diff.iat[0],
                     } )

col_order = ['orig_quantity','orig_start_time','orig_end_time','orig_time_diff','overlap']
df2 = pd.DataFrame( rows ).set_index( ['start','code'] ).sort_index()[ col_order ]

df2[ 'frac_overlap' ] = df2[ 'overlap' ] / df2[ 'orig_time_diff' ]
df2[ 'quantity' ] = df2[ 'orig_quantity' ] * df2[ 'frac_overlap' ]
df2[ 'lpm' ] = df2[ 'quantity' ] / df2[ 'overlap' ]

print( df2 )

Итак, df2 теперь выглядит следующим образом ...

                          orig_quantity     orig_start_time       orig_end_time  orig_time_diff  overlap  frac_overlap     quantity         lpm
start               code
2018-09-30 12:30:00 100            1331 2018-09-30 12:48:00 2018-09-30 13:41:00            53.0     12.0      0.226415   301.358491   25.113208
                    101            4317 2018-09-30 12:32:00 2018-09-30 13:34:00            62.0     28.0      0.451613  1949.612903   69.629032
                    102            8928 2018-09-30 12:53:00 2018-09-30 13:31:00            38.0      7.0      0.184211  1644.631579  234.947368
                    103            5134 2018-09-30 12:37:00 2018-09-30 13:04:00            27.0     23.0      0.851852  4373.407407  190.148148
2018-09-30 13:00:00 100            1331 2018-09-30 12:48:00 2018-09-30 13:41:00            53.0     30.0      0.566038   753.396226   25.113208
                    101            4317 2018-09-30 12:32:00 2018-09-30 13:34:00            62.0     30.0      0.483871  2088.870968   69.629032
                    102            8928 2018-09-30 12:53:00 2018-09-30 13:31:00            38.0     30.0      0.789474  7048.421053  234.947368
                    103            5134 2018-09-30 12:37:00 2018-09-30 13:04:00            27.0      4.0      0.148148   760.592593  190.148148
                    104            6065 2018-09-30 13:08:00 2018-09-30 13:20:00            12.0     12.0      1.000000  6065.000000  505.416667
2018-09-30 13:30:00 100            1331 2018-09-30 12:48:00 2018-09-30 13:41:00            53.0     11.0      0.207547   276.245283   25.113208
                    101            4317 2018-09-30 12:32:00 2018-09-30 13:34:00            62.0      4.0      0.064516   278.516129   69.629032
                    102            8928 2018-09-30 12:53:00 2018-09-30 13:31:00            38.0      1.0      0.026316   234.947368  234.947368
                    105            6776 2018-09-30 13:58:00 2018-09-30 15:06:00            68.0      2.0      0.029412   199.294118   99.647059
                    106            3540 2018-09-30 13:57:00 2018-09-30 14:15:00            18.0      3.0      0.166667   590.000000  196.666667
2018-09-30 14:00:00 105            6776 2018-09-30 13:58:00 2018-09-30 15:06:00            68.0     30.0      0.441176  2989.411765   99.647059
                    106            3540 2018-09-30 13:57:00 2018-09-30 14:15:00            18.0     15.0      0.833333  2950.000000  196.666667
                    107            4867 2018-09-30 14:04:00 2018-09-30 14:46:00            42.0     26.0      0.619048  3012.904762  115.880952
                    108            3590 2018-09-30 14:22:00 2018-09-30 15:17:00            55.0      8.0      0.145455   522.181818   65.272727
2018-09-30 14:30:00 105            6776 2018-09-30 13:58:00 2018-09-30 15:06:00            68.0     30.0      0.441176  2989.411765   99.647059
                    107            4867 2018-09-30 14:04:00 2018-09-30 14:46:00            42.0     16.0      0.380952  1854.095238  115.880952
                    108            3590 2018-09-30 14:22:00 2018-09-30 15:17:00            55.0     30.0      0.545455  1958.181818   65.272727
                    109            8918 2018-09-30 14:58:00 2018-09-30 15:24:00            26.0      2.0      0.076923   686.000000  343.000000
2018-09-30 15:00:00 105            6776 2018-09-30 13:58:00 2018-09-30 15:06:00            68.0      6.0      0.088235   597.882353   99.647059
                    108            3590 2018-09-30 14:22:00 2018-09-30 15:17:00            55.0     17.0      0.309091  1109.636364   65.272727
                    109            8918 2018-09-30 14:58:00 2018-09-30 15:24:00            26.0     24.0      0.923077  8232.000000  343.000000

Что выглядит правильно

Итак, давайте рассмотрим важный бит:

for start, end in zip( baseindex, baseindex[1:] ):
    # filter non-overlapping periods
    exclude_mask = (df[ 'end_time' ] < start) | (df[ 'start_time' ] > end)
    for code, dfg in df[ ~exclude_mask ].groupby( 'code' ):
        overlap = min( dfg.end_time.iat[0], end ) - max( dfg.start_time.iat[0], start )

start и end принимают значения (2018-09-30 12:30:00, 2018-09-30 13:00:00) ... (2018-09-30 13:00: 00, 2018-09-30 13:30:00) ... и т. Д., Чтобы мы могли фильтровать df между этими временами.

exclude_mask равно True для каждой строки, где start_time и end_time не перекрываются с током (start, end).~exclude_mask flips True / False

for code, dfg in df[ ~exclude_mask ].groupby( 'code' ) перебирает строки с перекрывающимся временем, сгруппированным по code (т.е. отдельные строки), и генерирует code, что является просто значением кодаи dfg, который представляет собой фрейм данных, представляющий группу (в данном случае он всегда имеет 1 строку)

overlap = min( dfg.end_time.iat[0], end ) - max( dfg.start_time.iat[0], start ) - это просто перекрытие для текущего (start, end) периода.

Надеюсь, что имеет смысл


РЕДАКТИРОВАТЬ

Мы можем привести в порядок кадр данных, чтобы получить желаемый результат:

>>> df3 = df2.reset_index()
>>> df3[ 'half_hourly_bucket' ] = df3['start'].apply( lambda x: str(x.time()) + '-' + str((x+timedelta(minutes=30)).time()) )
>>>
>>> df3[ ['code','half_hourly_bucket','quantity'] ].set_index('code').sort_index()
     half_hourly_bucket     quantity
code
100   12:30:00-13:00:00   301.358491
100   13:00:00-13:30:00   753.396226
100   13:30:00-14:00:00   276.245283
101   12:30:00-13:00:00  1949.612903
101   13:00:00-13:30:00  2088.870968
101   13:30:00-14:00:00   278.516129
102   12:30:00-13:00:00  1644.631579
102   13:00:00-13:30:00  7048.421053
102   13:30:00-14:00:00   234.947368
103   12:30:00-13:00:00  4373.407407
103   13:00:00-13:30:00   760.592593
104   13:00:00-13:30:00  6065.000000
105   15:00:00-15:30:00   597.882353
105   14:30:00-15:00:00  2989.411765
105   13:30:00-14:00:00   199.294118
105   14:00:00-14:30:00  2989.411765
106   14:00:00-14:30:00  2950.000000
106   13:30:00-14:00:00   590.000000
107   14:00:00-14:30:00  3012.904762
107   14:30:00-15:00:00  1854.095238
108   14:00:00-14:30:00   522.181818
108   14:30:00-15:00:00  1958.181818
108   15:00:00-15:30:00  1109.636364
109   14:30:00-15:00:00   686.000000
109   15:00:00-15:30:00  8232.000000
>>>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...