Как посчитать перекрывающиеся интервалы даты и времени в Pandas? - PullRequest
3 голосов
/ 14 февраля 2020

У меня есть следующий DataFrame с двумя столбцами даты и времени:

    start               end
0   01.01.2018 00:47    01.01.2018 00:54
1   01.01.2018 00:52    01.01.2018 01:03
2   01.01.2018 00:55    01.01.2018 00:59
3   01.01.2018 00:57    01.01.2018 01:16
4   01.01.2018 01:00    01.01.2018 01:12
5   01.01.2018 01:07    01.01.2018 01:24
6   01.01.2018 01:33    01.01.2018 01:38
7   01.01.2018 01:34    01.01.2018 01:47
8   01.01.2018 01:37    01.01.2018 01:41
9   01.01.2018 01:38    01.01.2018 01:41
10  01.01.2018 01:39    01.01.2018 01:55

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

Например, с 00:47 до 00:52 только один активен, с 00:52 до 00:54 два, с 00:54 до 00:55 только один снова и т. Д.

Я попытался сложить столбцы друг на друга, отсортировать по дате и, перемещаясь по всему фрейму данных, дать каждому «началу» +1 к счетчику и -1 к каждому «концу». Это работает, но в моем исходном фрейме данных, где у меня несколько миллионов строк, итерация занимает вечность - мне нужно найти более быстрый путь.

Мой оригинальный basi c -и-не-очень хорошо код:

import pandas as pd
import numpy as np

df = pd.read_csv('something.csv', sep=';')

df = df.stack().to_frame()
df = df.reset_index(level=1)
df.columns = ['status', 'time']
df = df.sort_values('time')
df['counter'] = np.nan
df = df.reset_index().drop('index', axis=1)

print(df.head(10))

дает:

    status  time                counter
0   start   01.01.2018 00:47    NaN
1   start   01.01.2018 00:52    NaN
2   stop    01.01.2018 00:54    NaN
3   start   01.01.2018 00:55    NaN
4   start   01.01.2018 00:57    NaN
5   stop    01.01.2018 00:59    NaN
6   start   01.01.2018 01:00    NaN
7   stop    01.01.2018 01:03    NaN
8   start   01.01.2018 01:07    NaN
9   stop    01.01.2018 01:12    NaN

и:

counter = 0

for index, row in df.iterrows():

    if row['status'] == 'start':
        counter += 1
    else:
        counter -= 1
    df.loc[index, 'counter'] = counter

конечный вывод:

        status  time                counter
    0   start   01.01.2018 00:47    1.0
    1   start   01.01.2018 00:52    2.0
    2   stop    01.01.2018 00:54    1.0
    3   start   01.01.2018 00:55    2.0
    4   start   01.01.2018 00:57    3.0
    5   stop    01.01.2018 00:59    2.0
    6   start   01.01.2018 01:00    3.0
    7   stop    01.01.2018 01:03    2.0
    8   start   01.01.2018 01:07    3.0
    9   stop    01.01.2018 01:12    2.0

Есть ли способ, которым я могу сделать это с помощью НЕ с помощью iterrows ()?

Заранее спасибо!

1 Ответ

3 голосов
/ 14 февраля 2020

Использование Series.cumsum с Series.map (или Series.replace):

new_df = df.melt(var_name = 'status',value_name = 'time').sort_values('time')
new_df['counter'] = new_df['status'].map({'start':1,'end':-1}).cumsum()
print(new_df)
   status                time  counter
0   start 2018-01-01 00:47:00        1
1   start 2018-01-01 00:52:00        2
11    end 2018-01-01 00:54:00        1
2   start 2018-01-01 00:55:00        2
3   start 2018-01-01 00:57:00        3
13    end 2018-01-01 00:59:00        2
4   start 2018-01-01 01:00:00        3
12    end 2018-01-01 01:03:00        2
5   start 2018-01-01 01:07:00        3
15    end 2018-01-01 01:12:00        2
14    end 2018-01-01 01:16:00        1
16    end 2018-01-01 01:24:00        0
6   start 2018-01-01 01:33:00        1
7   start 2018-01-01 01:34:00        2
8   start 2018-01-01 01:37:00        3
9   start 2018-01-01 01:38:00        4
17    end 2018-01-01 01:38:00        3
10  start 2018-01-01 01:39:00        4
19    end 2018-01-01 01:41:00        3
20    end 2018-01-01 01:41:00        2
18    end 2018-01-01 01:47:00        1
21    end 2018-01-01 01:55:00        0

Мы могли бы также используйте numpy.cumsum:

new_df['counter'] = np.where(new_df['status'].eq('start'),1,-1).cumsum()
...