Estoy tratando de realizar la siguiente operación:

pd.concat([A,B], axis = 1).groupby("status_reason")["closing_time"].mean()

Dónde

  • A es una serie denominada "status_reason" (valores categóricos)
  • B es una serie llamada "hora_cierre" (valores TimeDelta)

Ejemplo:

In : A.head(5)
Out: 
     0    -1 days +11:35:00
     1   -10 days +07:13:00
     2                  NaT
     3                  NaT
     4                  NaT
    Name: closing_time, dtype: timedelta64[ns]

In : B.head(5)
Out:
     0            Won
     1       Canceled
     2    In Progress
     3    In Progress
     4    In Progress
     Name: status_reason, dtype: object

Se produce el siguiente error:

DataError: No numeric types to aggregate

Tenga en cuenta: intenté realizar la media incluso aislando cada categoría

Ahora, vi algunas preguntas similares a las mías en línea, así que intenté esto:

pd.to_timedelta(pd.concat([pd.to_numeric(A),B], axis = 1).groupby("status_reason")["closing_time"].mean())

Lo cual es simplemente convertir el Timedelta a int64 y viceversa. Pero el resultado fue bastante extraño (números demasiado altos)

Para investigar la situación, escribí el siguiente código:

xxx = pd.concat([A,B], axis = 1)
xxx.closing_time.mean()
#xxx.groupby("status_reason")["closing_time"].mean()

La segunda fila FUNCIONA BIEN, sin convertir el Timedelta a Int64. La tercera fila NO funciona y devuelve nuevamente DataError.

Estoy tan confundido aquí! ¿Qué me estoy perdiendo?

¡Me gustaría ver la media de los "tiempos de cierre" para cada "razón de estado"!

EDITAR

Si trato de hacer esto: (aísle las filas con un estado específico sin agrupar)

yyy = xxx[xxx["status_reason"] == "In Progress"]
yyy["closing_time"].mean()

El resultado es:

Timedelta('310 days 21:18:05.454545')

Pero si hago esto: (aísle las filas con una agrupación de estado específica)

yyy = xxx[xxx["status_reason"] == "In Progress"]
yyy.groupby("status_reason")["closing_time"].mean()

El resultado es nuevamente:

DataError: No numeric types to aggregate

Por último, si hago esto: (convertir y volver a convertir) (LLAME A ESTO: Ejemplo especial )

yyy = xxx[xxx["status_reason"] == "In Progress"]
yyy.closing_time = pd.to_numeric (yyy.closing_time)
pd.to_timedelta(yyy.groupby("status_reason")["closing_time"].mean())

Volvemos al primer problema que noté:

status_reason
In Progress   -105558 days +10:08:05.605064
Name: closing_time, dtype: timedelta64[ns]

EDIT2

Si hago esto: (convertir a segundos y volver a convertir)

yyy = xxx[xxx["status_reason"] == "In Progress"]
yyy.closing_time = A.dt.seconds
pd.to_timedelta(yyy.groupby("status_reason")["closing_time"].mean(), unit="s" )

El resultado es

status_reason
In Progress   08:12:38.181818
Name: closing_time, dtype: timedelta64[ns]

El mismo resultado ocurre si elimino los NaN o si los relleno con 0:

yyy = xxx[xxx["status_reason"] == "In Progress"].dropna()
yyy.closing_time = A.dt.seconds
pd.to_timedelta(yyy.groupby("status_reason")["closing_time"].mean(), unit="s" )

¡PERO los números son muy diferentes de lo que vimos en la primera edición! ( Ejemplo especial )

-105558 days +10:08:05.605064

Además, déjame ejecutar el mismo código ( Ejemplo especial ) con dropna ():

310 days 21:18:05.454545

Y nuevamente, ejecutemos el mismo código ( Ejemplo especial ) con fillna (0):

3 days 11:14:22.819472

Esto no va a ninguna parte. Probablemente debería preparar una exportación de esos datos y publicarlos en algún lugar: Aquí vamos

0
Federico Dorato 9 oct. 2019 a las 17:20

4 respuestas

La mejor respuesta

Después de algunas investigaciones, esto es lo que encontré:

La mayor parte de la confusión proviene del hecho de que en un caso estaba llamando a SeriesGroupBy.mean () y en el otro caso a Series.mean ()

Estas funciones son realmente diferentes y tienen comportamientos diferentes. No me estaba dando cuenta de eso

El segundo punto importante es que la conversión a numérico, o a segundos, conduce a un comportamiento totalmente diferente cuando se trata de manejar el valor de NaN.

Para superar esta situación, lo primero que debe hacer es decidir cómo manejar los valores de NaN. El mejor enfoque depende de lo que queremos lograr. En mi caso, está bien tener incluso un resultado categórico simple, por lo que puedo hacer algo como esto:

import datetime

def define_time(row):
    if pd.isnull(row["closing_time"]):
        return "Null"
    elif row["closing_time"] < datetime.timedelta(days=100):
        return "<100"
    elif row["closing_time"] > datetime.timedelta(days=100):
        return ">100"


time_results = pd.concat([A,B], axis = 1).apply(lambda row:define_time(row), axis = 1)

Al final el resultado es así:

In : 
    time_results.value_counts()
Out : 
    >100    1452
    <100    1091
    Null    1000
    dtype: int64
0
Federico Dorato 10 oct. 2019 a las 12:45

Al leer la discusión de este problema en Github aquí, puede resolver este problema especificando numeric_only = False para el cálculo medio de la siguiente manera

pd.concat([A,B], axis = 1).groupby("status_reason")["closing_time"] \
    .mean(numeric_only=False)
1
Anna K. 9 oct. 2019 a las 19:17

No puedo decir por qué el método mean () de groupby no funciona, pero la siguiente ligera modificación de su código debería funcionar: Primero, convierta la columna timedelta a segundos con el método total_seconds (), luego groupby y mean, luego convierta segundos a timedelta nuevamente:

pd.to_timedelta(pd.concat([ A.dt.total_seconds(), B], axis = 1).groupby("status_reason")["closing_time"].mean(), unit="s")

Por ejemplo, el siguiente marco de datos, el código:

df = pd.DataFrame({'closing_time':['2 days 11:35:00', '07:13:00', np.nan,np.nan, np.nan],'status_reason':['Won','Canceled','In Progress', 'In Progress', 'In Progress']})

df.loc[:,"closing_time"] = \
          pd.to_timedelta(df.closing_time).dt.days*24*3600 \
          + pd.to_timedelta(df.closing_time).dt.seconds

# or alternatively use total_seconds() to get total seconds in timedelta as follows
# df.loc[:,"closing_time"] = pd.to_timedelta(df.closing_time).dt.total_seconds()

pd.to_timedelta(df.groupby("status_reason")["closing_time"].mean(), unit="s")

Produce

status_reason
Canceled      0 days 07:13:00
In Progress               NaT
Won           2 days 11:35:00
Name: closing_time, dtype: timedelta64[ns]
1
Anna K. 9 oct. 2019 a las 18:50

El problema podría ser In Progress solo tiene NaT tiempo, lo que podría no estar permitido en groupby().mean(). Aquí está la prueba:

df = pd.DataFrame({'closing_time':['11:35:00', '07:13:00', np.nan,np.nan, np.nan],
                   'status_reason':['Won','Canceled','In Progress', 'In Progress', 'In Progress']})
df.closing_time = pd.to_timedelta(df.closing_time)
df.groupby('status_reason').closing_time.mean()

Da el error exacto. Para superar esto, haga:

def custom_mean(x):
    try:
        return x.mean()
    except:
        return pd.to_timedelta([np.nan])

df.groupby('status_reason').closing_time.apply(custom_mean)

Que da:

status_reason
Canceled      07:13:00
In Progress        NaT
Won           11:35:00
Name: closing_time, dtype: timedelta64[ns]
1
Quang Hoang 9 oct. 2019 a las 14:42
58306309