Índice pandas de fecha y hora: Número de eventos actuales a través del tiempo

user1680772:

Estoy analizando un conjunto de eventos, cada uno de los cuales tiene un tipo, inicio y final de marca de tiempo. Estoy tratando de resumir el número de concurrentes de cada hora del evento que está en progreso durante el intervalo de tiempo.

Considere el conjunto de datos más adelante, lista eventos N1-N4, cada una con intervalos que se solapan:

>>> 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

Mi objetivo es producir este resumen, el número de eventos simultáneos, por tipo, para cada fecha del intervalo. Esta sería la respuesta correcta:

               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

Tenga en cuenta que hay fechas duplicadas tanto en el start_dt y columnas end_dt.

También tenga en cuenta que la solución debe proporcionar capacidad de volver a muestrear los datos de modo de llenado fechas con filas que contienen todos los ceros que faltan. En este ejemplo, la fecha 01-09 no aparece como una fecha de inicio o al final, pero debe estar presente en la salida. En el caso general, me gustaría ser capaz de hacer un nuevo muestreo para seleccionar los intervalos arbitrarios.

Para simplificar la explicación del problema tanto en el período de referencia y los datos se encuentran en la precisión día en el conjunto de datos anterior. En el conjunto de datos reales, y start_dt end_dt están a una precisión de milisegundos (pero todavía contienen duplicados), y el período de referencia podría ser horas, días, semanas, etc.

También tenga en cuenta que existen lagunas en los datos, por lo que es necesario volver a muestrear para producir la serie de fecha y hora. (Es decir, a pesar de que los datos están en la precisión milliscond, hay días enteros que faltan).

He intentado varios métodos que no funcionan. Un principio, parecía que sería sencillo, he intentado:

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

Lo que conduce a este problema pandas respecto muestreo superior está abierta, y ofrece algunas soluciones. Desafortunadamente, no podemos usamos sólo start_dt (o end_dt) como el índice debido a que no es único:

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

Esta pregunta que parece similar a mi problema, pero tampoco llenar todas las fechas del rango para cada tipo de evento:

>>> 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

Esta solución parecía prometedor, pero no es exactamente lo que necesito tampoco. En esencia, se ve un solo evento dentro de un rango, pero no cuenta el número total en curso. Aunque el uso de un IntervalIndex que sí parece como un buen inicio.

Siento que esto debería ser bastante fácil, pero es evidente que mi pandas foo es lamentablemente inadecuada.

Ayuda es muy apreciada!

EDITAR:

Jezreel:

Idea es valores de repetición por date_rangeal ayudante DataFramey luego usar SeriesGroupBy.value_countscon 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

Otra solución con cambio de forma mediante DataFrame.melt, pero primero es necesario distinguir los valores consecutivos de Series.shiftla Series.cumsumtrampa, a continuación, utilizar DataFrameGroupBy.resampley última 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

Supongo que te gusta

Origin http://43.154.161.224:23101/article/api/json?id=373018&siteId=1
Recomendado
Clasificación