Pandas Datetime index: Количество текущих событий за время - PullRequest
2 голосов
/ 26 марта 2020

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

Рассмотрим набор данных ниже, перечисляя события N1-N4, каждое из которых имеет перекрывающиеся диапазоны:

>>> data = {
...    'name' : [ 'N1', 'N2', 'N3', 'N4', 'N1',  'N2', 'N7'],
...    'start_dt_str' : ['01-01-2020', '01-03-2020', '01-01-2020', '01-01-2020', '01-03-2020', '01-04-2020','01-10-2020'],
...    'end_dt_str' : ['01-03-2020', '01-05-2020', '01-05-2020', '01-02-2020', '01-04-2020', '01-05-2020', '01-11-2020']
... }
>>> df = pd.DataFrame(data)
>>> df['start_dt'] = pd.to_datetime(df['start_dt_str'])
>>> df['end_dt'] = pd.to_datetime(df['end_dt_str'])
>>> del df['start_dt_str']
>>> del df['end_dt_str']
>>> df 
  name   start_dt     end_dt
0   N1 2020-01-01 2020-01-03
1   N2 2020-01-03 2020-01-05
2   N3 2020-01-01 2020-01-05
3   N4 2020-01-01 2020-01-02
4   N1 2020-01-03 2020-01-04
5   N2 2020-01-04 2020-01-05
6   N7 2020-01-10 2020-01-11

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

               N1 N2 N3 N4 N7
2020-01-01     1  0  1  1  0
2020-01-02     1  0  1  1  0 
2020-01-03     2  1  1  0  0
2020-01-04     1  2  1  0  0
2020-01-05     1  2  0  0  0
2020-01-06     0  0  0  0  0
2020-01-07     0  0  0  0  0
2020-01-08     0  0  0  0  0
2020-01-09     0  0  0  0  0
2020-01-10     0  0  0  0  1
2020-01-11     0  0  0  0  1

Обратите внимание, что в столбцах start_dt и end_dt есть повторяющиеся даты.

Также обратите внимание, что решение должно обеспечивать возможность повторной выборки данных, чтобы заполнить отсутствующие даты строками, содержащими все нули. В этом примере дата 01-09 не отображается как дата начала или окончания, но должна присутствовать в выходных данных. В общем случае я sh смогу сделать повторную выборку для выбора любых произвольных интервалов.

Для простоты объяснения проблемы и отчетный период, и данные имеют дневную точность в наборе данных выше , В фактическом наборе данных start_dt и end_dt с точностью до миллисекунды (но все еще содержат дубликаты), и отчетный период может быть часами, днями, неделями и т. Д. c.

Также обратите внимание, что в данных, поэтому для создания ряда дата-время требуется повторная выборка. (IE, хотя данные с точностью до миллисекунды, целых дней не хватает).

Я пробовал несколько подходов, которые НЕ работают. Во-первых, мне показалось, что это будет просто, я попробовал:

df.set_index(['name','start_dt']).groupby('name').resample('D',level='start_dt').ffill()

ValueError: Upsampling from level= or on= selection is not supported, use .set_index(...) to explicitly set index to datetime-like

, что приводит к , эта pandas проблема в отношении повышения дискретизации открыта и предоставляет некоторые обходные пути. К сожалению, мы не можем использовать только start_dt (или end_dt) в качестве индекса, потому что он не уникален:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/dcowden/envs/analysis-env/lib/python3.6/site-packages/pandas/core/resample.py", line 453, in pad
    return self._upsample("pad", limit=limit)
  File "/home/dcowden/envs/analysis-env/lib/python3.6/site-packages/pandas/core/resample.py", line 1095, in _upsample
    res_index, method=method, limit=limit, fill_value=fill_value
  File "/home/dcowden/envs/analysis-env/lib/python3.6/site-packages/pandas/util/_decorators.py", line 227, in wrapper
    return func(*args, **kwargs)
  File "/home/dcowden/envs/analysis-env/lib/python3.6/site-packages/pandas/core/frame.py", line 3856, in reindex
    return super().reindex(**kwargs)
  File "/home/dcowden/envs/analysis-env/lib/python3.6/site-packages/pandas/core/generic.py", line 4544, in reindex
    axes, level, limit, tolerance, method, fill_value, copy
  File "/home/dcowden/envs/analysis-env/lib/python3.6/site-packages/pandas/core/frame.py", line 3744, in _reindex_axes
    index, method, copy, level, fill_value, limit, tolerance
  File "/home/dcowden/envs/analysis-env/lib/python3.6/site-packages/pandas/core/frame.py", line 3760, in _reindex_index
    new_index, method=method, level=level, limit=limit, tolerance=tolerance
  File "/home/dcowden/envs/analysis-env/lib/python3.6/site-packages/pandas/core/indexes/base.py", line 3149, in reindex
    "cannot reindex a non-unique index "
ValueError: cannot reindex a non-unique index with a method or limit

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

>>> df.set_index('start_dt').groupby('name').resample('D').asfreq()
                name     end_dt
name start_dt                  
N1   2020-01-01   N1 2020-01-03
     2020-01-02  NaN        NaT
     2020-01-03   N1 2020-01-04
N2   2020-01-03   N2 2020-01-05
     2020-01-04   N2 2020-01-05
N3   2020-01-01   N3 2020-01-05
N4   2020-01-01   N4 2020-01-02

Это решение показалось многообещающим, но и не совсем то, что мне нужно. По сути, он ищет одно событие в пределах диапазона, но не учитывает общее число в процессе. Хотя использование IntervalIndex кажется хорошим началом.

Я чувствую, что это должно быть довольно легко, но, очевидно, мой pandas foo крайне неадекватен.

Помощь очень ценится!

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

1 Ответ

1 голос
/ 26 марта 2020

Идея состоит в том, чтобы повторить значения на date_range для помощника DataFrame, а затем использовать SeriesGroupBy.value_counts с Series.unstack:

L = [pd.Series(r.name, pd.date_range(r.start_dt, r.end_dt)) for r in df.itertuples()]
s = pd.concat(L)

df1 = s.groupby(level=0).value_counts().unstack(fill_value=0)
print (df1)
            N1  N2  N3  N4
2020-01-01   1   0   1   1
2020-01-02   1   0   1   1
2020-01-03   2   1   1   0
2020-01-04   1   2   1   0
2020-01-05   0   2   1   0

Другое решение с изменением формы на DataFrame.melt, но сначала необходимо различить guish последовательных значений на Series.shift с помощью трюка Series.cumsum, затем используйте DataFrameGroupBy.resample и последний crosstab:

df['g'] = df['name'].ne(df['name'].shift()).cumsum()
df1 = (df.melt(['name','g'])
         .set_index('value')
         .groupby(['g','name'])['variable']
         .resample('d')
         .first()
         .reset_index())

df1 = pd.crosstab(df1['value'], df1['name'])
print (df1)
name        N1  N2  N3  N4
value                     
2020-01-01   1   0   1   1
2020-01-02   1   0   1   1
2020-01-03   2   1   1   0
2020-01-04   1   2   1   0
2020-01-05   0   2   1   0
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...