python - 修改pandas dataframe的日期索引

标签 python python-3.x pandas dataframe datetime

我正在尝试编写一个高效的函数,该函数将采用平均大小的数据帧(约 5000 行)并返回具有最近一年的列(和相同索引)的数据帧,这样对于原始数据帧的每个日期索引包含该日期的月份介于某个预先指定的开始日期 (st_d) 和结束日期 (end_d) 之间。我编写了一个代码,其中年份递减,直到特定日期索引的月份在所需的范围内。然而,它确实很慢。对于只有 366 个条目的数据帧,大约需要 0.2 秒。我需要使它至少快一个数量级,以便我可以重复地将其应用于数万个数据帧。我非常感谢对此的任何建议。

import pandas as pd
import numpy as np
import time
from pandas.tseries.offsets import MonthEnd

def year_replace(st_d, end_d, x):

    tmp = time.perf_counter()

    def prior_year(d):
        # 100 is number of the years back, more than enough.
        for i_t in range(100):

            #The month should have been fully seen in one of the data years.
            t_start = pd.to_datetime(str(d.month) + '/' + str(end_d.year - i_t), format="%m/%Y")
            t_end = t_start + MonthEnd(1)
            if t_start <= end_d and t_start >= st_d and t_end <= end_d and t_end >= st_d:
                break
        if i_t < 99:
            return t_start.year
        else:
            raise BadDataException("Not enough data for Gradient Boosted tree.")

    output = pd.Series(index = x.index, data = x.index.map(lambda tt: prior_year(tt)), name = 'year')

    print("time for single dataframe replacement = ", time.perf_counter() - tmp)    

    return output


i = pd.date_range('01-01-2019', '01-01-2020')
x = pd.DataFrame(index = i, data=np.full(len(i), 0))

st_d = pd.to_datetime('01/2016', format="%m/%Y")
end_d = pd.to_datetime('01/2018', format="%m/%Y")
year_replace(st_d, end_d, x)

最佳答案

我的建议是:尽可能避免循环,并检查是否有更简单的方法可用。

如果我明白你的目标是:

For given start and stop timestamps, find the latest (higher) timestamp t where month is given from index and start <= t <= stop

我相信这可以形式化如下(为了方便起见,我保留了您的函数签名):

def f(start, stop, x):
    assert start < stop
    tmp = time.perf_counter()
    def y(d):
        # Check current year:
        if start <= d.replace(day=1, year=stop.year) <= stop:
            return stop.year
        # Check previous year:
        if start <= d.replace(day=1, year=stop.year-1) <= stop:
            return stop.year-1
        # Otherwise fail:
        raise TypeError("Ooops")
    # Apply to index:
    df = pd.Series(index=x.index, data=x.index.map(lambda t: y(t)), name='year')
    print("Tick: ", time.perf_counter() - tmp) 
    return df

它似乎按照要求执行得更快(几乎二十年,我们应该进行基准测试以确保,例如:使用 timeit ):

Tick:  0.004744200000004639

无需迭代,只需查看当前年份和上一年即可。如果失败,则不存在满足您要求的时间戳。

如果必须保留这一天,则只需删除 day=1replace方法。如果您要求切割标准不相等,则相应地修改不等式。函数如下:

def y(d):
    if start < d.replace(year=stop.year) < stop:
        return stop.year
    if start < d.replace(year=stop.year-1) < stop:
        return stop.year-1
    raise TypeError("Ooops")

返回与您相同的数据帧。

关于python - 修改pandas dataframe的日期索引,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59557146/

相关文章:

python - 如何让 Python 的 subprocess() 与 input() 交互?

Python3 无法将字符串转换为 dict 或 json

python - 如何在数据框列中 append 值

pandas - 如何将包含值列表的数据框列转换为具有出现次数的单独列?

python - Django - 如何在登录时设置默认值 request.session ?

python - 为 numba 优化对 numpy 数组的访问

python字典函数,文本文件

python-3.x - Flask sqlalchemy 将列表保存为 json

python - Pandas /sqlalchemy/pyodbc : Result object does not return rows from stored proc when UPDATE statement appears before SELECT

python - Pandas :isin() 和 str.contains() 有什么区别?