【问题标题】:Pandas Timedelta in monthsPandas Timedelta 以月为单位
【发布时间】:2017-04-16 20:51:30
【问题描述】:

如何使用 pandas 计算经过的月份?我已经写了以下内容,但是这段代码并不优雅。你能告诉我一个更好的方法吗?

import pandas as pd

df = pd.DataFrame([pd.Timestamp('20161011'),
                   pd.Timestamp('20161101') ], columns=['date'])
df['today'] = pd.Timestamp('20161202')

df = df.assign(
    elapsed_months=(12 *
                    (df["today"].map(lambda x: x.year) -
                     df["date"].map(lambda x: x.year)) +
                    (df["today"].map(lambda x: x.month) -
                     df["date"].map(lambda x: x.month))))
# Out[34]: 
#         date      today  elapsed_months
# 0 2016-10-11 2016-12-02               2
# 1 2016-11-01 2016-12-02               1

【问题讨论】:

    标签: python pandas


    【解决方案1】:

    pandas 0.24.0 更新

    由于 0.24.0 更改了 api 以从周期减法中返回 MonthEnd 对象,您可以进行如下手动计算以获得整个月份的差异:

    12 * (df.today.dt.year - df.date.dt.year) + (df.today.dt.month - df.date.dt.month)
    
    # 0    2
    # 1    1
    # dtype: int64
    

    包装在一个函数中:

    def month_diff(a, b):
        return 12 * (a.dt.year - b.dt.year) + (a.dt.month - b.dt.month)
    
    month_diff(df.today, df.date)
    # 0    2
    # 1    1
    # dtype: int64
    

    在熊猫 0.24.0 之前。您可以使用to_period() 将日期四舍五入到月份,然后减去结果:

    df['elapased_months'] = df.today.dt.to_period('M') - df.date.dt.to_period('M')
    
    df
    #         date       today  elapased_months
    #0  2016-10-11  2016-12-02                2
    #1  2016-11-01  2016-12-02                1
    

    【讨论】:

    • 这样,epased_months dtype 是“object”,所以我使用 pd.to_numeric() 将“object”转换为“int64”。谢谢你的回答。
    • 这不再直接在 Pandas 0.24 中工作,其中减去句点 now gives you a DateOffset() object
    • 那么,有没有办法直接得到月份呢?这是非常不切实际的
    【解决方案2】:

    你也可以试试:

    df['months'] = (df['today'] - df['date']) / np.timedelta64(1, 'M')
    df
    #      date      today    months
    #0 2016-10-11 2016-12-02  1.708454
    #1 2016-11-01 2016-12-02  1.018501
    

    【讨论】:

    • 2018-03-012018-02-01 提供0.919937
    • 我不推荐使用:“有两个 Timedelta 单位('Y', 年和'M', 月)被特殊处理,因为它们代表的时间取决于它们的时间已使用。虽然 timedelta 日单位相当于 24 小时,但无法将月份单位转换为天数,因为不同的月份有不同的天数。"
    【解决方案3】:

    pandas 1.3 更新

    如果你想要整数而不是 MonthEnd 对象:

    df['elapsed_months'] = df.today.dt.to_period('M').view(dtype='int64') - df.date.dt.to_period('M').view(dtype='int64')
    df
    
    # Out[11]: 
    #         date      today  elapsed_months
    # 0 2016-10-11 2016-12-02               2
    # 1 2016-11-01 2016-12-02               1
    

    这适用于 pandas 1.1.1:

    df['elapsed_months'] = df.today.dt.to_period('M').astype(int) - df.date.dt.to_period('M').astype(int)
    df
    
    # Out[11]: 
    #         date      today  elapsed_months
    # 0 2016-10-11 2016-12-02               2
    # 1 2016-11-01 2016-12-02               1
    

    【讨论】:

    • Casting period[M] values to int with .astype 现已弃用,会引发错误,建议改用.view
    • @Yulian - 感谢您的提醒。我已经更新了答案。
    【解决方案4】:

    更简单的方法,也可以使用pandas中的to_period函数来计算。

    pd.to_datetime('today').to_period('M') - pd.to_datetime('2020-01-01').to_period('M')
    # [Out]:
    # <7 * MonthEnds>
    

    如果您只想要整数值,请使用(&lt;above_code&gt;).n

    【讨论】:

    • 在数据框中使用它时遇到'Series' object has no attribute 'n' 错误。见map答案。
    • @citynorman 它适用于我的 pandas 1.2.4 (pd.to_datetime('today').to_period('M') - pd.to_datetime('2020-01-01').to_period('M')).n 提供输出 20
    • 它适用于单个值,但不适用于数据帧
    【解决方案5】:

    以下将完成此操作:

    df["elapsed_months"] = ((df["today"] - df["date"]).
                            map(lambda x: round(x.days/30)))
    
    
    # Out[34]: 
    #         date      today  elapsed_months
    # 0 2016-10-11 2016-12-02               2
    # 1 2016-11-01 2016-12-02               1
    

    【讨论】:

    • 抱歉没有解释。这样,2016-11-30 和 2016-12-02 之间的月份为 0。在这种情况下我想得到 1。但这很有帮助。谢谢你的回答。
    • 如果年数累积超过1个月,每年累积4天,超过8年,你会开始有错误的结果
    【解决方案6】:

    这会在处理舍入问题的同时为您提供数据框中的整数月数:

    df["n_months"] = (df["date1"].dt.to_period("M") - df["date2"].dt.to_period("M")).map(lambda x: x.n)
    

    还照顾 pandas 1.3.2 int conversion issue

    【讨论】:

      【解决方案7】:

      如果你不介意忽略这些日子,你可以使用 numpy 功能:

      import numpy as np
      
      df['elapsed month'] = (df.date.values.astype('datetime64[M]')-    
                          df.today.values.astype('datetime64[M]')) 
                                / np.timedelta64(1,'M')
      

      【讨论】:

        猜你喜欢
        • 2013-04-12
        • 1970-01-01
        • 1970-01-01
        • 2016-12-06
        • 2011-07-27
        • 1970-01-01
        相关资源
        最近更新 更多