Pandas Timedelta 平均值返回错误“没有要聚合的数字类型”。为什么?

Fed*_*ato 5 python type-conversion timedelta pandas

我正在尝试执行以下操作:

pd.concat([A,B], axis = 1).groupby("status_reason")["closing_time"].mean()
Run Code Online (Sandbox Code Playgroud)

在哪里

  • A 是一个名为“status_reason”的系列(分类值)
  • B 是一个名为“close_time”的系列(TimeDelta 值)

例子:

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
Run Code Online (Sandbox Code Playgroud)

出现以下错误:

DataError: No numeric types to aggregate
Run Code Online (Sandbox Code Playgroud)

请注意:我尝试执行平均值,甚至隔离每个类别

现在,我在网上看到了一些与我类似的问题,所以我尝试了以下方法:

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

这只是将 Timedelta 转换为 int64,反之亦然。但结果很奇怪(数字太高)

为了排查情况,我编写了如下代码:

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

第二行工作正常,无需将 Timedelta 转换为 Int64。第三行不起作用,并再次返回 DataError。

我在这里很困惑!我错过了什么?

我想查看每个“状态原因”的“关闭时间”的平均值!

编辑

如果我尝试这样做:(隔离具有特定状态的行而不进行分组)

yyy = xxx[xxx["status_reason"] == "In Progress"]
yyy["closing_time"].mean()
Run Code Online (Sandbox Code Playgroud)

结果是:

Timedelta('310 days 21:18:05.454545')
Run Code Online (Sandbox Code Playgroud)

但如果我这样做:(隔离具有特定状态分组的行)

yyy = xxx[xxx["status_reason"] == "In Progress"]
yyy.groupby("status_reason")["closing_time"].mean()
Run Code Online (Sandbox Code Playgroud)

结果又是:

DataError: No numeric types to aggregate
Run Code Online (Sandbox Code Playgroud)

最后,如果我这样做:(转换并转换回来)(让我们称之为:特殊示例

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())
Run Code Online (Sandbox Code Playgroud)

我们回到我注意到的第一个问题:

status_reason
In Progress   -105558 days +10:08:05.605064
Name: closing_time, dtype: timedelta64[ns]
Run Code Online (Sandbox Code Playgroud)

编辑2

如果我这样做:(转换为秒并转换回来)

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" )
Run Code Online (Sandbox Code Playgroud)

结果是

status_reason
In Progress   08:12:38.181818
Name: closing_time, dtype: timedelta64[ns]
Run Code Online (Sandbox Code Playgroud)

如果我删除 NaN,或者用 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" )
Run Code Online (Sandbox Code Playgroud)

但这些数字与我们在第一次编辑中看到的非常不同!(特殊示例

-105558 days +10:08:05.605064
Run Code Online (Sandbox Code Playgroud)

另外,让我使用 dropna()运行相同的代码(特殊示例):

310 days 21:18:05.454545
Run Code Online (Sandbox Code Playgroud)

再次,让我们使用 fillna(0)运行相同的代码(特殊示例):

3 days 11:14:22.819472
Run Code Online (Sandbox Code Playgroud)

这无处可去。我可能应该准备这些数据的导出,并将它们发布到某个地方: 我们开始吧

Fed*_*ato 1

经过一些调查,我发现以下内容:

大多数混乱来自这样一个事实:在一种情况下我调用 SeriesGroupBy.mean() 而在另一种情况下调用 Series.mean()

这些函数实际上是不同的并且具有不同的行为。我没有意识到

第二个要点是,在处理 NaN 值时,转换为数字或秒会导致完全不同的行为。

为了克服这种情况,您要做的第一件事就是决定如何处理 NaN 值。最好的方法取决于我们想要实现的目标。就我而言,即使有一个简单的分类结果也很好,所以我可以这样做:

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)
Run Code Online (Sandbox Code Playgroud)

最终结果是这样的:

In : 
    time_results.value_counts()
Out : 
    >100    1452
    <100    1091
    Null    1000
    dtype: int64
Run Code Online (Sandbox Code Playgroud)