2015-08-25 4 views
0

У меня есть dataframe (серия) индексируется DatetimeIndexКак группировать метки времени с помощью ярлыков?

    tag 
2015-08-21 16:32:00 stationary 
2015-08-21 16:33:00 automotive 
2015-08-21 16:34:00 automotive 
2015-08-21 17:27:00 stationary 
2015-08-21 17:28:00 stationary 
2015-08-21 17:29:00 stationary 
2015-08-21 17:30:00 stationary 
2015-08-21 17:31:00 stationary 
2015-08-21 17:32:00 stationary 
2015-08-24 16:55:00 automotive 
2015-08-24 16:56:00 automotive 
2015-08-24 16:57:00 automotive 
2015-08-24 16:58:00 automotive 
2015-08-24 16:59:00 stationary 
2015-08-24 17:00:00 stationary 
2015-08-24 17:01:00 stationary 

хочет GroupBy тег и совокупный индекс времени, поэтому ожидаемого результат

Start      End     Tag 
-       2015-08-21 16:32:00 stationary 
2015-08-21 16:34:00   2015-08-21 16:34:00 automotive 
2015-08-21 17:27:00   2015-08-21 17:32:00 stationary 
2015-08-24 16:55:00   2015-08-24 16:58:00 automotive 
2015-08-24 16:59:00   2015-08-24 17:01:00 stationary 

ответ

3

Вы можете использовать схему groupby и apply.

def func(group): 
    return pd.Series({'Start': group.index[0], 'End': group.index[-1], 'Tag': group['tag'].values[0]}) 

df.groupby((df.shift(1) != df).cumsum()['tag'], as_index=False).apply(func) 

        End    Start   Tag 
0 2015-08-21 16:32:00 2015-08-21 16:32:00 stationary 
1 2015-08-21 16:34:00 2015-08-21 16:33:00 automotive 
2 2015-08-21 17:32:00 2015-08-21 17:27:00 stationary 
3 2015-08-24 16:58:00 2015-08-24 16:55:00 automotive 
4 2015-08-24 17:01:00 2015-08-24 16:59:00 stationary 
+1

Очень элегантный. Однако, протестированный на больших фреймах данных, он, кажется, работает очень медленнее: 9 секунд против 90 мс, используя мой ответ на 10000 строк. – Omar

+1

@ Омар согласился. Эта конкретная операция 'groupby' включает итерацию по слишком большому количеству подгрупп, а создание новой' pd.Series' в каждой группе приводит к большим накладным расходам, если функция вызывается так много раз (в случае, если тег изменяется так часто). Ваш подход полностью основан на векторизации 'numpy.array' и намного быстрее в этом сценарии. –

2

Не уверен, что ваш ожидаемый результат был правильным, вот что Я думаю, что правильный ожидаемый результат:

begin end  tag 
0 2015-08-21 16:32:00  2015-08-21 16:32:00  stationary 
1 2015-08-21 16:33:00  2015-08-21 16:34:00  automotive 
3 2015-08-21 17:27:00  2015-08-21 17:32:00  stationary 
9 2015-08-24 16:55:00  2015-08-24 16:58:00  automotive 
13 2015-08-24 16:59:00  2015-08-24 17:01:00  stationary 

А вот как его получить:

import pandas as pd 
import numpy as np 
from datetime import datetime 
# Prepare data from your example 
data = [ 
("2015-08-21 16:32:00", "stationary"), 
("2015-08-21 16:33:00", "automotive"), 
("2015-08-21 16:34:00", "automotive"), 
("2015-08-21 17:27:00", "stationary"), 
("2015-08-21 17:28:00", "stationary"), 
("2015-08-21 17:29:00", "stationary"), 
("2015-08-21 17:30:00", "stationary"), 
("2015-08-21 17:31:00", "stationary"), 
("2015-08-21 17:32:00", "stationary"), 
("2015-08-24 16:55:00", "automotive"), 
("2015-08-24 16:56:00", "automotive"), 
("2015-08-24 16:57:00", "automotive"), 
("2015-08-24 16:58:00", "automotive"), 
("2015-08-24 16:59:00", "stationary"), 
("2015-08-24 17:00:00", "stationary"), 
("2015-08-24 17:01:00", "stationary")] 
data = [(datetime.strptime(x[0], "%Y-%m-%d %H:%M:%S"), x[1]) for x in data] 
df = pd.DataFrame(data, columns=['ts', 'tag']).sort('ts') 
df['is_first'] = df.tag != df.tag.shift() 
df['is_last'] = df.tag != df.tag.shift(-1) 
# Fill begin timestamp, only on first occurences 
df['begin'] = df.ts 
df.loc[~df.is_first, 'begin'] = pd.NaT 
# Fill end timestamp, only on last occurences 
df['end'] = df.ts 
df.loc[~df.is_last, 'end'] = pd.NaT 
# Fill NaT with next end 
df['end'] = df['end'].bfill() 
# Restrict to changes 
df = df[df.is_first] 
# Remove useless columns 
df = df[['begin', 'end', 'tag']].sort('begin') 
+0

большое спасибо за ответ –

Смежные вопросы