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

时间:2019-10-09 14:20:46

标签: python pandas type-conversion timedelta

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

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

哪里

  • A是一个名为“ status_reason”(分类值)的系列
  • B是一个名为“ closing_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

发生以下错误:

DataError: No numeric types to aggregate

请注意:我试图对均值进行平均,甚至隔离每个类别

现在,我在网上看到了一些类似于我的问题,所以我尝试了这个问题:

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

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

为了调查这种情况,我编写了以下代码:

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

第二行有效,但没有将Timedelta转换为Int64。第三行无效,并再次返回DataError。

我在这里很困惑!我是什么小姐?

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

编辑

如果我尝试执行此操作:(隔离具有特定状态的行而不进行分组)

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

结果是:

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

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

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

结果再次是:

DataError: No numeric types to aggregate

最后,如果我这样做:(转换并转换回)(请致电:特殊示例

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())

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

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

EDIT2

如果我这样做:(转换为秒然后转换回去)

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" )

结果是

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

如果删除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" )

但是数字与我们在第一次编辑中看到的数字有很大不同! (特殊示例

-105558 days +10:08:05.605064

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

310 days 21:18:05.454545

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

3 days 11:14:22.819472

这无济于事。我可能应该准备导出这些数据,并将其发布到某个地方:Here we go

4 个答案:

答案 0 :(得分:1)

问题可能是In Progress只有NaT的时间,而groupby().mean()可能不允许。这是测试:

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()

给出确切的错误。要克服这个问题,请执行以下操作:

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

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

给出:

status_reason
Canceled      07:13:00
In Progress        NaT
Won           11:35:00
Name: closing_time, dtype: timedelta64[ns]

答案 1 :(得分:1)

我不能说为什么groupby的mean()方法不起作用,但是对您的代码进行以下稍作修改应该起作用:首先,使用total_seconds()方法将timedelta列转换为秒,然后使用groupby和mean,然后将秒转换为timedelta再次:

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

例如下面的数据框代码-

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")

产生

status_reason
Canceled      0 days 07:13:00
In Progress               NaT
Won           2 days 11:35:00
Name: closing_time, dtype: timedelta64[ns]

答案 2 :(得分:1)

通过阅读有关Github here的有关此问题的讨论,可以通过如下方式为均值计算指定numeric_only = False来解决此问题

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

答案 3 :(得分:0)

经过一些调查,这是我发现的东西:

大多数困惑来自以下事实:在一个案例中,我调用了SeriesGroupBy.mean(),而在另一案例中,我调用了Series.mean()

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

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

要克服这种情况,您要做的第一件事就是确定如何处理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)

最后的结果是这样的:

In : 
    time_results.value_counts()
Out : 
    >100    1452
    <100    1091
    Null    1000
    dtype: int64