DatetimeIndex 阻止 DataFrame 从装饰函数返回
DatetimeIndex stops DataFrame from returning from a decorated function
我有一个装饰器,可以将函数的 return 添加到提供的字典或 pandas 数据框中。只要数据框在 return 上没有不同的 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)
但是使用 return 数据帧和 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
现在我的测试代码实际上运行了这个(想象一下函数 returning 一个带有 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')]))
通过同事找到了解决方案(感谢 Dillon)。问题看起来与覆盖整个变量有关。覆盖在函数内被视为 nonlocal
,但任何对变量的完全覆盖都不会保留在装饰器的局部范围之外。 global/outer 名称不能指向修饰装饰器中的不同内存地址,但它的任何可变成员都可以。这也解释了为什么以前的实现有效但索引的实现无效。所以问题与DatetimeIndex
.
没有直接关系
添加了一个额外的间接寻址以使其工作。如果有人能找到更好的实现,请 post:
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 可能是个好主意):
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)
我有一个装饰器,可以将函数的 return 添加到提供的字典或 pandas 数据框中。只要数据框在 return 上没有不同的 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)
但是使用 return 数据帧和 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
现在我的测试代码实际上运行了这个(想象一下函数 returning 一个带有 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')]))
通过同事找到了解决方案(感谢 Dillon)。问题看起来与覆盖整个变量有关。覆盖在函数内被视为 nonlocal
,但任何对变量的完全覆盖都不会保留在装饰器的局部范围之外。 global/outer 名称不能指向修饰装饰器中的不同内存地址,但它的任何可变成员都可以。这也解释了为什么以前的实现有效但索引的实现无效。所以问题与DatetimeIndex
.
添加了一个额外的间接寻址以使其工作。如果有人能找到更好的实现,请 post:
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 可能是个好主意):
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)