【问题标题】:DatetimeIndex stops DataFrame from returning from a decorated functionDatetimeIndex 阻止 DataFrame 从修饰函数返回
【发布时间】:2019-10-06 11:40:54
【问题描述】:

我有一个装饰器,可以将函数的返回值添加到提供的字典或 pandas 数据框中。只要数据框在返回时没有不同的 DateTimeIndex ,这就可以正常工作。我尝试简单地合并数据框并考虑索引,但由于某种原因,这意味着收集框最终为空。

所以这段代码可以正常工作:

    def add_return_to_dict_or_pandas_col_decorator(return_dict):
        def actual_decorator(func):
            @functools.wraps(func)
            def wrapper(*args, **kwargs):
                nonlocal return_dict
                return_dict[args[0]] = func(*args, **kwargs)        
            return wrapper    
        return actual_decorator

如果适用于:

accumulate_dict = dict()    
@add_return_to_dict_or_pandas_col_decorator(accumulate_dict)
def f2(identifier, x):
    return x * x    
f2('thrity', 30)
f2('three', 3)
print(accumulate_dict)

accumulate_df = pd.DataFrame()
@add_return_to_dict_or_pandas_col_decorator(accumulate_df)
def f3(identifier, x):
    return [x, x * x, x + x]
f3('thrity', 30)
f3('three', 3)
print(accumulate_df)

但是使用返回带有 DateTimeIndex 的数据帧的函数会失败(因为它们并不真正匹配)。以下是修复该问题的尝试:

def add_return_to_pandas_indexed_col_decorator(return_data_frame):
def actual_decorator(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        nonlocal return_data_frame
        if return_data_frame.shape[0] > 0:
            return_data_frame = pd.merge(return_data_frame, func(*args, **kwargs),
                                         how='outer', left_index=True, right_index=True)
        else:
            return_data_frame = func(*args, **kwargs)
    return wrapper

return actual_decorator

现在我的测试代码实际上运行了这个(想象一下函数返回一个带有 DateTimeIndex 的数据帧),但最终结果是一个空数据帧。

return_df = pd.DataFrame()
tckrs = ['GLD', 'GDX']  
@add_return_to_pandas_indexed_col_decorator(return_df)
def set_df_get_return_series(*args, **kwargs):
    return get_return_series(*args, **kwargs)

for ticker in tckrs:
    set_df_get_return_series(ticker)
print(return_df)

get_return_series 在哪里:

def get_return_series(ticker):
    from faker import Faker
    fake = Faker()
    return pd.DataFrame(np.random.randn(2).tolist(),
                    columns=[ticker],
                    index=pd.DatetimeIndex([fake.date_between(start_date='-30y', end_date='-1d'),
                                            fake.date_between(start_date='today', end_date='+30y')]))

【问题讨论】:

    标签: python pandas dataframe python-decorators datetimeindex


    【解决方案1】:

    通过一位同事找到了解决方案(感谢 Dillon)。问题看起来与整个变量的覆盖有关。覆盖在函数内被视为nonlocal,但变量的任何完全覆盖都不会保留在装饰器的本地范围之外。全局/外部名称不能指向装饰装饰器中的不同内存地址,但它的任何可变成员都可以。这也解释了为什么以前的实现有效,但索引的无效。所以问题与DatetimeIndex没有直接关系。

    添加了一个额外的间接来使其工作。如果有人能找到更好的实现,请发帖:

    def add_return_to_pandas_indexed_col_decorator(return_object):
    def actual_decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            nonlocal return_object
            if return_object.frame is not None:
                return_object.frame = pd.merge(return_object.frame, func(*args, **kwargs), how='outer', left_index=True,
                                               right_index=True)
            else:
                return_object.frame = func(*args, **kwargs)
        return wrapper
    return actual_decorator
    

    这样使用(在装饰器中集成测试类可能是个好主意):

    class Test(object):
        def __init__(self):
            self.frame = pd.DataFrame()
    
    tckrs = ['GLD', 'GDX']
    accumulate_object = Test()
    @add_return_to_pandas_indexed_col_decorator(accumulate_object)
    def set_df_get_return_series(*args, **kwargs):
        return get_return_series(*args, **kwargs)
    for ticker in tckrs:
        set_df_get_return_series(ticker)
    print(accumulate_object.frame)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-02-16
      • 2018-04-08
      • 2018-02-23
      • 1970-01-01
      • 2016-05-31
      • 1970-01-01
      • 2021-03-16
      • 2016-10-20
      相关资源
      最近更新 更多