【问题标题】:Pandas Timedelta mean returns error "No numeric types to aggregate". Why?Pandas Timedelta 均值返回错误“没有要聚合的数字类型”。为什么?
【发布时间】:2020-02-06 22:03:04
【问题描述】:

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

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

在哪里

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

【问题讨论】:

    标签: python pandas type-conversion timedelta


    【解决方案1】:

    通过阅读 Github here 上有关此问题的讨论,您可以通过指定 numeric_only=False 进行均值计算来解决此问题,如下所示

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

    【讨论】:

    • 结果还是“一团糟”
    【解决方案2】:

    问题可能是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]
    

    【讨论】:

    • 正如我之前写的,遗憾的是这不是问题所在。但是你给了我一个提示,我现在发布一个新的编辑。完毕!这对我来说更有趣,也更令人困惑
    • 另一种呢? df.groupby('status_reason').count() 的输出是什么?它有零吗?
    • 我又编辑了帖子,又有新信息了。只需看一下帖子末尾的最后一个示例,您就会清楚地了解...我的困惑:P(PS在您要求的内容中没有零。您会找到的最小值是3)跨度>
    • 有趣的问题。 xxx.dropna().groupby('status_reason').closing_time.mean()呢?
    • 同样的错误:DataError: No numeric types to aggregate
    【解决方案3】:

    我不能说为什么 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")
    

    例如下面的dataframe,代码——

    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]
    

    【讨论】:

    • 有效!它没有!我决定发布一个新的 EDIT (EDIT2) 来向您展示您的代码发生了什么。非常感谢
    • 这里是 CSV:sharecsv.com/s/4374cef2e169fd7a79dd3aa793aeacad/save_me.csv
    • 在特殊示例中,当您执行 pd.to_numeric (yyy.closing_time) 时,默认情况下会以纳秒为单位获得结果,而当您使用 pd.to_timedelta(yyy.groupby("status_reason) 转换为 timedelta ")["closing_time"].mean()),您没有指定单位,这就是为什么您的数字在您的特殊示例中没有意义。尝试使用 pd.to_numeric(yyy.closing_time.dt.seconds) 和 pd.to_timedelta(yyy.groupby("status_reason")["closing_time"].mean(), unit="s) 的特殊示例,这将有助于避免单位混淆
    • 刚刚意识到 timedelta 的 .seconds() 方法不考虑天数,因此更正了我上面的答案以考虑到这一点
    【解决方案4】:

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

    大部分的困惑来自这样一个事实,在一种情况下我调用 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)
    

    最终结果是这样的:

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

    【讨论】:

      猜你喜欢
      • 2020-04-15
      • 2019-06-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-03
      • 2021-08-07
      • 1970-01-01
      • 2016-03-30
      相关资源
      最近更新 更多