Digamos que tengo una lista de listas donde cada lista anidada tiene dos valores: una fecha de inicio de un rango y una fecha de finalización de un rango. Entonces algo como esto:

ranges_list = [
    ['2020-03-12', '2020-06-12'],
    ['2020-03-13', '2020-06-13'],
    ['2020-03-14', '2020-06-14']
]

Esto representa 3 rangos:

  1. 12 de marzo de 2020-12 de junio de 2020
  2. 13 de marzo de 2020-13 de junio de 2020
  3. 14 de marzo de 2020-14 de junio de 2020

Digamos que también tengo un marco de datos d que tiene varias columnas, una de las cuales es una columna llamada 'occurence_date' que contiene fechas y horas.

Digamos que el marco de datos d se ve así:

ID      LinkID   PC    occurence_date
10R46   R*1005   8017  2020-03-12
10R46   R*10335  5019  2020-04-15
100R91  R*1005   8017  2020-04-15
10R91   R*243    8870  2020-06-14

Quiero agrupar el marco de datos d usando la columna occurence_date en los rangos especificados en ranges_list

Entonces algo como:

grouped = d.groupby('occurence_date', ranges=ranges_list)

Obviamente, este código groupby es incorrecto pero ayuda a cumplir lo que quiero hacer.

Al final de todo, el objeto agrupado debería tener 3 grupos distintos que se parezcan a:

group: ('2020-03-12', '2020-06-12')
ID      LinkID   PC    occurence_date
10R46   R*1005   8017  2020-03-12
10R46   R*10335  5019  2020-04-15
100R91  R*1005   8017  2020-04-15

group: ('2020-03-13', '2020-06-13')
ID      LinkID   PC    occurence_date
10R46   R*10335  5019  2020-04-15
100R91  R*1005   8017  2020-04-15

group: ('2020-03-14', '2020-06-14')
ID      LinkID   PC    occurence_date
10R46   R*10335  5019  2020-04-15
100R91  R*1005   8017  2020-04-15
10R91   R*243    8870  2020-06-14

¿Cómo puedo lograr esto?

3
sometimesiwritecode 14 mar. 2021 a las 04:01

2 respuestas

La mejor respuesta

Puede agrupar por pd.IntervalIndex:

ranges_list = [
    (pd.Timestamp('2020-03-12'), pd.Timestamp('2020-06-12')),
    (pd.Timestamp('2020-03-13'), pd.Timestamp('2020-06-13')),
    (pd.Timestamp('2020-03-14'), pd.Timestamp('2020-06-14'))
]

idx = pd.IntervalIndex.from_tuples(ranges_list, closed='both')

def in_ranges(x, bins):
    rv = []
    for b in bins:
        if x in b:
            rv.append(b)
    return rv

df['groups'] = df['occurence_date'].apply(lambda x: in_ranges(x, idx))

for g in df.explode('groups').groupby('groups'):
    print(g[0])
    print('-' * 80)
    print(g[1][['ID', 'LinkID', 'PC', 'occurence_date']])
    print()

Huellas:

[2020-03-12, 2020-06-12]
--------------------------------------------------------------------------------
       ID   LinkID    PC occurence_date
0   10R46   R*1005  8017     2020-03-12
1   10R46  R*10335  5019     2020-04-15
2  100R91   R*1005  8017     2020-04-15

[2020-03-13, 2020-06-13]
--------------------------------------------------------------------------------
       ID   LinkID    PC occurence_date
1   10R46  R*10335  5019     2020-04-15
2  100R91   R*1005  8017     2020-04-15

[2020-03-14, 2020-06-14]
--------------------------------------------------------------------------------
       ID   LinkID    PC occurence_date
1   10R46  R*10335  5019     2020-04-15
2  100R91   R*1005  8017     2020-04-15
3   10R91    R*243  8870     2020-06-14
1
Andrej Kesely 14 mar. 2021 a las 01:36

La siguiente sesión interactiva muestra cómo llegar a un punto en el que tiene los datos necesarios para agrupar registros como lo desee. Puede haber una forma más eficiente, ya que se repetirá sobre len(d) * len(dranges), pero si no tiene muchos datos, esta es una solución sencilla.

>>> d
       ID   LinkID    PC occurence_date
0   10R46   R*1005  8017     2020-03-12
1   10R46  R*10335  5019     2020-04-15
2  100R91   R*1005  8017     2020-04-15
3   10R91    R*243  8870     2020-06-14

>>> dranges
            0           1
0  2020-03-12  2020-06-12
1  2020-03-13  2020-06-13
2  2020-03-14  2020-06-14

>>> d['overlaps'] = d.apply(lambda row: [f'{dr[0]} to {dr[1]}' 
                                         for _, dr in dranges.iterrows() 
                                         if row['occurence_date'] >= dr[0] 
                                         and row['occurence_date'] <= dr[1]]
                           , axis=1)

>>> d.explode('overlaps').sort_values('overlaps')
       ID   LinkID    PC occurence_date                  overlaps
0   10R46   R*1005  8017     2020-03-12  2020-03-12 to 2020-06-12
1   10R46  R*10335  5019     2020-04-15  2020-03-12 to 2020-06-12
2  100R91   R*1005  8017     2020-04-15  2020-03-12 to 2020-06-12
1   10R46  R*10335  5019     2020-04-15  2020-03-13 to 2020-06-13
2  100R91   R*1005  8017     2020-04-15  2020-03-13 to 2020-06-13
1   10R46  R*10335  5019     2020-04-15  2020-03-14 to 2020-06-14
2  100R91   R*1005  8017     2020-04-15  2020-03-14 to 2020-06-14
3   10R91    R*243  8870     2020-06-14  2020-03-14 to 2020-06-14
2
ApplePie 14 mar. 2021 a las 01:37