Tengo un marco de datos con proyectos, fechas de inicio y fechas de finalización. Para cada fila me gustaría devolver el número de otros proyectos en proceso cuando comenzó el proyecto. ¿Cómo anidas bucles cuando usas df.apply()? Intenté usar un bucle for, pero mi marco de datos es grande y lleva demasiado tiempo.

import datetime as dt

data = {'project' :['A', 'B', 'C'],
        'pr_start_date':[dt.datetime(2018, 9, 1), dt.datetime(2019, 4, 1), dt.datetime(2019, 6, 8)],
        'pr_end_date': [dt.datetime(2019, 6, 15), dt.datetime(2019, 12, 1), dt.datetime(2019, 8, 1)]}

df = pd.DataFrame(data)

def cons_overlap(start):
    overlaps = 0
    for i in df.index:
        other_start = df.loc[i, 'pr_start_date']
        other_end = df.loc[i, 'pr_end_date']
        if (start > other_start) & (start < other_end):
            overlaps += 1

    return overlaps

df['overlap'] = df.apply(lambda row: cons_overlap(row['pr_start_date']), axis=1)

Esta es la salida que estoy buscando:

    pr  pr_start_date pr_end_date   overlap
0   A   2018-09-01    2019-06-15    0
1   B   2019-04-01    2019-12-01    1
2   C   2019-06-08    2019-08-01    2
4
jmon 8 oct. 2019 a las 22:54

3 respuestas

La mejor respuesta

Le sugiero que aproveche transmisión de numpy :

ends = df.pr_start_date.values < df.pr_end_date.values[:, None]
starts = df.pr_start_date.values > df.pr_start_date.values[:, None]
df['overlap'] = (ends & starts).sum(0)
print(df)

Salida

  project pr_start_date pr_end_date  overlap
0       A    2018-09-01  2019-06-15        0
1       B    2019-04-01  2019-12-01        1
2       C    2019-06-08  2019-08-01        2

Ambos extremos y comienzos son matrices de 3x3 que son verdaderas cuando se cumple la condición:

# ends   
[[ True  True  True]  
 [ True  True  True]
 [ True  True  True]]

# starts
[[False  True  True]
 [False False  True]
 [False False False]]

Luego encuentre la intersección con el & lógico y sume a través de las columnas (sum(0)).

3
Dani Mesejo 8 oct. 2019 a las 20:14

Debería ser más rápido que tu bucle for

enter image description here

2
Benoit de Menthière 8 oct. 2019 a las 20:13

Supongo que las filas están ordenadas por la fecha de inicio y verifico los proyectos iniciados anteriormente que aún no se han completado. Df.index.get_loc (r.name) produce el índice de la fila que se está procesando.

df["overlap"]=df.apply(lambda r: df.loc[:df.index.get_loc(r.name),"pr_end_date"].gt(r["pr_start_date"]).sum()-1, axis=1)
0
kantal 8 oct. 2019 a las 21:31
58293218