修改熊猫数据框的日期索引

时间:2020-01-02 00:14:26

标签: python python-3.x pandas dataframe datetime

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

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)

1 个答案:

答案 0 :(得分:1)

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

如果我确实了解您的目标是:

对于给定的namestart时间戳,找到最新(较高)时间戳stop,其中从索引和t给出月份。

我相信可以将其形式化如下(为方便起见,我保留了您的功能签名):

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=1。如果您要求的切割标准不相等,请相应地修改不等式。以下功能:

replace

返回与您相同的数据框。