Подтвердить что ты не робот

Pandas регрессия качения: альтернативы циклу

Мне удалось использовать класс pandas 'MovingOLS (источник здесь) в устаревшем модуле stats/ols. К сожалению, он был полностью испорчен pandas 0.20.

Вопрос о том, как эффективно запускать регрессию OLS регенерации, задавался несколько раз (здесь), но сформулирован немного широко и оставлен без отличный ответ, на мой взгляд.

Вот мои вопросы:

  • Как лучше всего имитировать базовую структуру pandas 'MovingOLS? Самой привлекательной особенностью этого класса была возможность просмотра нескольких методов/атрибутов в виде отдельных временных рядов - т.е. коэффициенты, r-квадрат, t-статистика и т.д., не требуя повторной регрессии. Например, вы можете создать что-то вроде model = pd.MovingOLS(y, x), а затем вызвать .t_stat, .rmse, .std_err и тому подобное. В приведенном ниже примере, наоборот, я не вижу возможности заставить вас вычислять каждую статистику отдельно. Есть ли метод, который не предусматривает создание скользящих/катящихся "блоков" (шагов) и выполнение регрессий/использование линейной алгебры для получения параметров модели для каждого?

  • В более широком смысле, что происходит под капотом в pandas, что делает rolling.apply неспособным выполнять более сложные функции? * Когда вы создаете объект .rolling, в условиях неспециалиста, что происходит внутри - Это принципиально отличается от цикла по каждому окну и создания более массивного массива, как я делаю ниже?

* А именно, func передано .apply:

Необходимо создать одно значение из входных данных ndarray * args и ** kwargs передаются функции

Здесь, где я сейчас нахожусь с некоторыми образцами данных, регрессирует процентные изменения в торговом взвешенном долларе по спредам по процентной ставке и цене на медь. (Это не имеет смысла, просто выбрал их случайным образом.) Я взял это из реализации на основе классов и попытался разбить его на более простой script.

from datetime import date
from pandas_datareader.data import DataReader
import statsmodels.formula.api as smf

syms = {'TWEXBMTH' : 'usd', 
        'T10Y2YM' : 'term_spread', 
        'PCOPPUSDM' : 'copper'
       }

start = date(2000, 1, 1)
data = (DataReader(syms.keys(), 'fred', start)
        .pct_change()
        .dropna())
data = data.rename(columns = syms)
data = data.assign(intercept = 1.) # required by statsmodels OLS

def sliding_windows(x, window):
    """Create rolling/sliding windows of length ~window~.

    Given an array of shape (y, z), it will return "blocks" of shape
    (x - window + 1, window, z)."""

    return np.array([x[i:i + window] for i 
                    in range(0, x.shape[0] - window + 1)])

data.head(3)
Out[33]: 
                 usd  term_spread    copper  intercept
DATE                                                  
2000-02-01  0.012573    -1.409091 -0.019972        1.0
2000-03-01 -0.000079     2.000000 -0.037202        1.0
2000-04-01  0.005642     0.518519 -0.033275        1.0

window = 36
wins = sliding_windows(data.values, window=window)
y, x = wins[:, :, 0], wins[:, :, 1:]

coefs = []

for endog, exog in zip(y, x):
    model = smf.OLS(endog, exog).fit()
        # The full set of model attributes gets lost with each loop
    coefs.append(model.params)

df = pd.DataFrame(coefs, columns=data.iloc[:, 1:].columns,
                  index=data.index[window - 1:])

df.head(3) # rolling 36m coefficients
Out[70]: 
            term_spread    copper  intercept
DATE                                        
2003-01-01    -0.000122 -0.018426   0.001937
2003-02-01     0.000391 -0.015740   0.001597
2003-03-01     0.000655 -0.016811   0.001546
4b9b3361

Ответ 1

Я создал модуль ols, предназначенный для имитации pandas 'устаревшего MovingOLS; это здесь.

Он имеет три основных класса:

  • ols: статическая (однооконная) обычная регрессия наименьших квадратов. На выходе представлены массивы NumPy
  • RollingOLS: циклическая (многооконная) обычная регрессия наименьших квадратов. Результатом являются массивы NumPy более высокого порядка.
  • PandasRollingOLS: завершает результаты RollingOLS в pandas рядах и в DataFrames. Предназначен для имитации внешнего вида модуля pandas.

Обратите внимание, что модуль является частью пакета (который теперь находится в PyPI) и требует одного межпакетного импорта.

Первые два класса выше полностью реализованы в NumPy и в первую очередь используют матричную алгебру. RollingOLS также широко используется широковещание. Атрибуты в значительной степени имитируют OLS RegressionResultsWrapper statsmodels.

Пример:

# Pull some data from fred.stlouisfed.org
from pandas_datareader.data import DataReader

syms = {'TWEXBMTH' : 'usd', 
        'T10Y2YM' : 'term_spread', 
        'PCOPPUSDM' : 'copper'
       }
data = (DataReader(syms.keys(), 'fred', start='2000-01-01')
        .pct_change()
        .dropna())
data = data.rename(columns=syms)
print(data.head())
                # usd  term_spread   copper
# DATE                                     
# 2000-02-01  0.01260     -1.40909 -0.01997
# 2000-03-01 -0.00012      2.00000 -0.03720
# 2000-04-01  0.00564      0.51852 -0.03328
# 2000-05-01  0.02204     -0.09756  0.06135
# 2000-06-01 -0.01012      0.02703 -0.01850

# Rolling regressions

from pyfinance.ols import OLS, RollingOLS, PandasRollingOLS

y = data.usd
x = data.drop('usd', axis=1)

window = 12  # months
model = PandasRollingOLS(y=y, x=x, window=window)

print(model.beta.head())  # Coefficients excluding the intercept
            # term_spread   copper
# DATE                            
# 2001-01-01      0.00010  0.05568
# 2001-02-01      0.00047  0.06271
# 2001-03-01      0.00147  0.03576
# 2001-04-01      0.00161  0.02956
# 2001-05-01      0.00158 -0.04497

print(model.fstat.head())
# DATE
# 2001-01-01    0.28121
# 2001-02-01    0.42602
# 2001-03-01    0.38802
# 2001-04-01    0.39230
# 2001-05-01    0.41706
# Freq: MS, Name: fstat, dtype: float64

print(model.rsq.head())  # R-squared
# DATE
# 2001-01-01    0.05882
# 2001-02-01    0.08648
# 2001-03-01    0.07938
# 2001-04-01    0.08019
# 2001-05-01    0.08482
# Freq: MS, Name: rsq, dtype: float64