DatetimeIndex останавливает возврат DataFrame из декорированной функции - PullRequest
4 голосов
/ 06 октября 2019

У меня есть декоратор, который добавляет возврат функции в предоставленный словарь или фрейм данных 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')]))

1 Ответ

1 голос
/ 20 октября 2019

Получил решение этой проблемы через коллегу (спасибо, Диллон). Проблема выглядит связанной с перезаписью всей переменной. Перезапись в функции выглядит как 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

Для использования в качестве такового (вероятно, хорошая идея для интеграции Test 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)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...