【问题标题】:Pandas: Multi-index apply function between column and indexPandas:列和索引之间的多索引应用功能
【发布时间】:2016-12-16 03:07:04
【问题描述】:

我有一个看起来像这样的多索引数据框:

In[13]: df
Out[13]:
              Last Trade
Date       Ticker           
1983-03-30 CLM83  1983-05-18
           CLN83  1983-06-17
           CLQ83  1983-07-18
           CLU83  1983-08-19
           CLV83  1983-09-16
           CLX83  1983-10-18
           CLZ83  1983-11-18
1983-04-04 CLM83  1983-05-18
           CLN83  1983-06-17
           CLQ83  1983-07-18
           CLU83  1983-08-19
           CLV83  1983-09-16
           CLX83  1983-10-18
           CLZ83  1983-11-18

索引有两个级别(即“日期”和“代码”)。我想对“最后交易”列应用一个函数,让我知道这个“最后交易”日期与索引“日期”相隔多少个月 我找到了一个计算函数:

from calendar import monthrange

def monthdelta(d1, d2):
    delta = 0
    while True:
        mdays = monthrange(d1.year, d1.month)[1]
        d1 += datetime.timedelta(days=mdays)
        if d1 <= d2:
            delta += 1
        else:
            break
    return delta

我尝试应用以下函数 h 但它返回一个 AttributeError: 'Timestamp' object has no attribute 'index':

In[14]: h = lambda x: monthdelta(x.index.get_level_values(0),x)

In[15]: df['Last Trade'] = df['Last Trade'].apply(h)

如何应用一个同时使用列和索引值的函数?

感谢您的提示,

【问题讨论】:

    标签: python pandas apply attributeerror multi-index


    【解决方案1】:

    使用df.index.to_series().str.get(0) 获取第一级索引。

    (df['Last Trade'].dt.month - df.index.to_series().str.get(0).dt.month) + \
    (df['Last Trade'].dt.year - df.index.to_series().str.get(0).dt.year) * 12
    
    Date        Ticker
    1983-03-30  CLM83     2
                CLN83     3
                CLQ83     4
                CLU83     5
                CLV83     6
                CLX83     7
                CLZ83     8
    1983-04-04  CLM83     1
                CLN83     2
                CLQ83     3
                CLU83     4
                CLV83     5
                CLX83     6
                CLZ83     7
    dtype: int64
    

    时间

    鉴于df

    pd.concat([df for _ in range(10000)])

    【讨论】:

    • .reset_index 会不会比转换为 Series,然后使用字符串类更快?
    • @Kartik 我正在检查。但我想要一种干净的方式来保存索引。
    • 非常有意义。 .reset_index 和提取月份相比转换为Series,然后使用str类获取第一级索引,然后提取月份所需的计算步骤更少。 .apply 只是一个循环,因此会随着大小的增加而缩放,这与直接减法不同......很棒的时间比较!
    • 非常感谢你们两位!由于我正在处理一个非常大的数据库,因此这次时间分析对我来说非常重要。我不知道 dt 访问器甚至 reset_index(),我不得不查找它们,这些都是非常强大的工具。
    • 我只剩下一个问题,你们两种方法都没有考虑年份,我们举个例子:Jan15 和 Dec14 之间的差是-11,我希望它是 1
    【解决方案2】:

    试试这个而不是你的函数:

    选项 1

    你得到一个整数

    def monthdelta(row):
        trade = row['Last Trade'].year*12 + row['Last Trade'].month
        date = row['Date'].year*12 + row['Date'].month
        return trade - date
    
    df.reset_index().apply(monthdelta, axis=1)
    

    灵感来自 PiRsquared:

    df = df.reset_index()
    (df['Last Trade'].dt.year*12 + df['Last Trade'].dt.month) -\
    (df['Date'].dt.year*12 + df['Date'].dt.month)
    

    选项 2

    你会得到一个numpy.timedelta64

    可以直接用于其他日期计算。但是,这将采用天而不是月的形式,因为days in a month are not constant 的数量。

    def monthdelta(row):
        return row['Last Trade'] - row['Date']
    
    df.reset_index().apply(monthdelta, axis=1)
    

    灵感来自 PiRsquared:

    df = df.reset_index()
    df['Last Trade'] - df['Date']
    

    选项 2 当然会更快,因为它涉及的计算量更少。选择你喜欢的!


    要找回您的索引:df.index = df[['Date', 'Ticker']]

    【讨论】:

    • 你的第一个解决方案给了我AttributeError: ("'Timestamp' object has no attribute 'dt'", u'occurred at index 0') 我怀疑是因为apply 是逐行提供的,因此row['Last Trade']row['Date'] 不是标量时间戳,不再是系列。因此不需要dt 访问器。
    • 但是dtypes 应该在.apply 之间保留。 df.reset_index().dtypes 表示什么?
    • 看看我在帖子中添加的时间。您将看到我用于您的解决方案的代码。我从函数中删除了.dt。否则它对我不起作用。
    • groupby apply 传递数据帧切片。您使用的典型apply 为每一行传递系列。访问系列中的日期时,您不再需要(它不起作用)dt 访问器。
    • 哦...这是我没想到的。我猜时间戳在 Series 中的存储方式与它们在 DataFrame 列中的存储方式不同。感谢您提供此信息... :)
    猜你喜欢
    • 1970-01-01
    • 2015-05-18
    • 1970-01-01
    • 2016-09-02
    • 2017-06-09
    • 1970-01-01
    • 2021-12-12
    • 2017-03-04
    • 2022-10-13
    相关资源
    最近更新 更多