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
4 respuestas
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
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)
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]
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]
Preguntas relacionadas
Nuevas preguntas
python
Python es un lenguaje de programación multipropósito, de tipificación dinámica y de múltiples paradigmas. Está diseñado para ser rápido de aprender, comprender y usar, y hacer cumplir una sintaxis limpia y uniforme. Tenga en cuenta que Python 2 está oficialmente fuera de soporte a partir del 01-01-2020. Aún así, para preguntas de Python específicas de la versión, agregue la etiqueta [python-2.7] o [python-3.x]. Cuando utilice una variante de Python (por ejemplo, Jython, PyPy) o una biblioteca (por ejemplo, Pandas y NumPy), inclúyala en las etiquetas.