Статистический арбитраж

Корзина акций от MidJourney 12.2023
  • От теории к модели
  • Анализ статистического арбитража
  • Автоматизация анализа данных (EDA)

От теории к модели

Анализ главных компонент, собственные портфели и многофакторные регрессии

Кривая роста основного собственного портфеля по отношению к SPY ETF.

В первой части этого блога исследуются строительные блоки стандартной статистической арбитражной модели, основанной на главном компонентном анализе составляющих S&P 500. В ней исследуется теория, лежащая в основе процедуры анализа главных компонент, и заканчивается построением главного собственного портфеля и вычислением его доходности. Модель арбитража и бэктест построены во второй части блога.

PCA в двух словах

Анализ главных компонент (PCA) — это статистическая процедура, которая использует ортогональное преобразование для преобразования набора наблюдений потенциально коррелированных переменных в набор значений линейно некоррелированных переменных, известных как главные компоненты. Первый компонент (собственный вектор) объясняет наибольшую величину дисперсии в наборе данных, второй — вторую по величине величину дисперсии и так далее.

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

Основные составляющие фондового рынка

В контексте фондового рынка или индекса, такого как S&P 500, мы можем использовать цены составляющих для вычисления их ежедневной доходности, стандартизировать набор данных, а затем разложить его на основные компоненты с помощью PCA. Таким образом, PCA является полезным инструментом в выявлении базовых паттернов, которые двигают рынок; Первый компонент может представлять собой широкую рыночную тенденцию, затрагивающую все акции, а каждый последующий компонент может выявлять менее доминирующую, но отчетливую модель.

Собственные векторы

Каждый из этих трендов/компонент является собственным вектором, и в них мы находим коэффициенты, которые говорят нам, насколько данная акция вносит вклад в дисперсию, объясняемую собственным вектором. Знак коэффициента также дает нам представление о направленности акции на этот собственный вектор: если знак коэффициента соответствующей акции отрицателен, то это говорит о том, что акция движется против тренда, изображаемого собственным вектором, и наоборот.

Это также дает инвесторам представление о позиционировании — мы можем, скажем, шортить акции, движущиеся против тренда, и открывать длинные позиции, движущиеся вместе с трендом (это будет ставка на продолжение рыночных сил, как показано собственным вектором), или мы можем сделать наоборот, и в этом случае мы ставим на то, что рыночные силы, по сути, развернутся в режиме. Доминантный собственный вектор уникален тем, что его коэффициенты больше 0, и по мере продвижения по собственным векторам это может перестать быть верным, предполагая более ортогональные рыночные тенденции. Однако, чтобы получить размеры позиций инвестиций, нам нужно смотреть не только на коэффициенты.

Собственные портфели

Собственные портфели представляют собой полезное применение PCA на финансовых рынках, помогая нам более детально анализировать поведение рынка. Каждый портфель соответствует определенному основному компоненту, таким образом, отражая уникальный аспект поведения рынка. Например, «собственный портфель», соответствующий первому главному компоненту, будет представлять направление, в котором данные изменяются больше всего, часто отражая общее движение рынка. Таким образом, эти портфели являются производными от собственных векторов, которые мы получили из нашего PCA. Веса для каждой акции в собственном портфеле определяются коэффициентами в соответствующем собственном векторе, скорректированными на волатильность каждой акции:

Таким образом, вес акций в собственном портфеле обратно пропорционален их волатильности (т.е. размеры наших позиций меньше для более волатильных акций), метод, напоминающий индексы, взвешенные по капитализации, где компании с более высокой капитализацией часто имеют меньшую волатильность. Это, в сочетании со знаком коэффициентов, говорит нам о том, что мы можем менять наше позиционирование в зависимости от знака коэффициентов, и, в конечном счете, позволяет нам интерпретировать собственные портфели как по своей сути «парную торговлю» или, в более общем смысле, длинные и короткие позиции — обратите внимание, что доминирующий собственный вектор даст собственный портфель, основной собственный портфель, включающий все длинные/все короткие позиции. но в контексте рыночных индексов или ETF основной собственный портфель является портфелем на всю длину, учитывая его приближение к индексу в целом (Avellaneda & Lee, 2008, стр. 10–11).

Главный собственный портфель

Главный собственный портфель — это просто собственный портфель, связанный с первым главным компонентом. Значение основного собственного портфеля выходит за рамки его числового представления — его доминирование в объяснении дисперсии данных делает его сильным прокси для более широкого рынка. Например, основной собственный портфель, полученный из корреляционной матрицы составляющих S&P 500, в значительной степени приближается к индексу S&P 500, что делает его ценным инструментом для инвесторов для оценки направления и интенсивности рынка, выступая в качестве барометра общего настроения рынка. Этот «рыночный фактор» может стать основой для принятия стратегических решений о распределении активов, о чем мы поговорим в конце этой записной книжки.

Идиосинкразическая доходность и многофакторные модели

Естественно, возникает вопрос о необходимости PCA и о том, почему мы просто не можем использовать какой-то другой прокси, скажем, данные о ценах для SPY ETF, чтобы аппроксимировать индекс S&P 500 вместо собственных портфелей. Безусловно, можно, но при этом мы ограничимся одномерными регрессионными моделями. Наша арбитражная модель, которая стремится торговать поведением доходности акций, не коррелируя с рынком, чтобы оставаться рыночно-нейтральной, основана на линейной регрессии, и, таким образом, то, как мы настраиваем регрессию, становится инструментом для ее прибыльности.

Простая регрессионная модель говорит нам, что доходность любой из составляющих SPY может быть представлена следующим образом:

или, в более общем смысле,

где первый член представляет собой систематическую составляющую доходности акций, объясняемую доходностью SPY, а второй член представляет собой идиосинкразический компонент, не объясненный и не коррелирующий с доходностью SPY. Это однофакторная модель, в которой множителем является доходность SPY, сравнимая с однофакторной моделью, где множителем является доходность основного собственного портфеля.

Несмотря на то, что приведенная выше модель эффективна сама по себе, мы теряем информацию об уровне внутренней динамики рынка, поскольку сводим весь индекс S&P 500, состоящий из 500 уникальных, но коррелированных компонентов, к одной сущности — SPY. PCA в значительной степени решает это ограничение. Разложив все 500 составляющих на определенное число собственных векторов, мы получаем множество факторов, каждый из которых пытается объяснить систематическую составляющую доходности акции, что позволяет более точно оценить идиосинкразическую часть:

Таким образом, PCA помогает более точно оценить как систематическую составляющую доходности акций, так и индивидуальную часть. Последнее служит основой для того, как мы реализуем нашу арбитражную модель, которую я более подробно рассмотрю во второй части.

Следует отметить, что интерпретация позиции и размера позиции на основе собственных портфелей не является неотъемлемым свойством декомпозиции PCA, а скорее приписыванием экономических и инвестиционных принципов ее результатам, и конкретные стратегии, полученные на основе этих интерпретаций, могут меняться в зависимости от аппетита инвестора и качества модели. Подробнее о PCA читайте здесь.

Имплитация

Импорт

Суть этого анализа заключается в разложении PCA; Для проведения PCA мы импортируем класс ‘PCA’ из модуля ‘decomposition’ ‘scikit-learn’. Я также использую Omega, пакет, который я написал, и который вы можете найти здесь.import Omega as o
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import project_functions as pf
import random

from sklearn.decomposition import PCA

Установка таймфрейма для анализа

Я произвольно выбираю таймфрейм, который отсчитывает год назад от сегодняшнего дня. Я также получаю данные о ценах для SPY ETF, чтобы позже сравнить его кривую роста с кривой роста основного собственного портфеля. Декомпозиция PCA проводится на срезе 100 акций из вселенной S&P 500 . Учитывая предположение о линейности данных, присущих PCA-разложению, нам нужно сначала вычислить доходность акций, чтобы преобразовать нестационарный ценовой ряд в стационарные, сопоставимые значения.# 1. Fetch data for S&P 500 tickers and SPY ETF for comparison.

start = ‘2022-07-16’
end = ‘2023-07-16’

random.seed(8)

snp_tickers = random.sample(o.get_snp(), 100)
snp_prices = o.Ticker(snp_tickers).bulk_prices(start, end)
snp_returns = snp_prices.pct_change()[1:] # Compute returns.

spy_prices = o.Ticker(‘SPY’).get_prices(start, end)# 1a. Plot SPY prices.

spy_prices.plot(legend=False, figsize=(12, 4.5), linewidth=1.4, color=’#C44E52′)
plt.title(‘SPY ETF Price Series’)
plt.ylabel(‘Price’); plt.xlabel(»);

Анализ главных компонент

Теперь нам нужно стандартизировать наш набор данных о доходах. PCA, будучи упражнением по максимизации дисперсии, в противном случае может быть смещено в сторону переменных с более высокой дисперсией, поэтому стандартизация данных предотвращает такие результаты, зависящие от масштаба:

Здесь мы также устанавливаем количество компонентов, до которых мы хотим, чтобы PCA сократил наш набор данных. В нашем случае мы выбираем 15 компонентов, которые Авельянеда и Ли сочли оптимальными для возможной арбитражной модели. На практике, однако, лучше всего проверить это с помощью тщательного тестирования, как это сделали авторы.

Затем метод fit применяет PCA, вычисляя собственные векторы.

Авторы ранжировали каждый собственный вектор в порядке убывания по отношению к доле объяснимой дисперсии, отраженной в каждом из них, но класс PCA делает это за нас автоматически.# 2. Conduct Principal Component Analysis on the standardized data.

n_components = 15 # As per Avellaneda and Lee’s findings.
stdized_snp_returns = (snp_returns — snp_returns.mean()) / snp_returns.std()
pca = PCA(n_components=n_components)
pca.fit(stdized_snp_returns);# 2a. Plot each principal component’s proportion of explained variance.

evr = pca.explained_variance_ratio_
print(f’Proportion of variance explained by the principal eigenvector: {evr[0]:,.2f}’)

plt.figure(figsize=(12, 4.5))
sns.barplot(x=np.arange(1, len(evr)+1), y=evr, color=’#4273FF’, edgecolor=’k’)
plt.title(‘Explained Variance Ratio’)
plt.xlabel(‘Principal Component’); plt.ylabel(‘Proportion of Explained Variance’);

На приведенном выше графике мы видим, что первый главный компонент объясняет около 39% общей дисперсии в нашем наборе данных о доходности. Второй собственный вектор объясняет значительно меньшую долю дисперсии. Каждый собственный вектор ортогонален друг другу, так как PCA пытается свести к минимуму корреляцию между каждым собственным вектором.# 2b. Plot density of explained variance ratios.

plt.figure(figsize=(12,4.5))
sns.histplot(evr, alpha=1, color=»#C44E52″, stat=’percent’)
plt.title(‘Density Plot of Explained Variance Ratio’)
plt.xlabel(‘Explained Variance Ratio’); plt.ylabel(‘Percentage of Principal Components’);

Доминирующий компонент можно увидеть изолированно в крайнем правом углу, что объясняет наибольшую долю дисперсии в наборе данных returns.

Построение собственных портфелей

Мы можем получить собственные векторы (предварительно отсортированные) из подогнанного экземпляра PCA с помощью его атрибута components_. Затем мы можем преобразовать эти собственные векторы в собственные портфели следующим образом:

# Compute eigenportfolios.

def get_eigenportfolios(pca, snp_returns):
eigenvectors = pca.components_ # Retrieve eigenvectors from PCA object
returns_std = snp_returns.std().values
weights_vol_adj = eigenvectors / returns_std
return eigenvectors, weights_vol_adj

eigenvectors, eigenportfolios = get_eigenportfolios(pca, snp_returns)
eigenvectors_df, eigenportfolios_df = pf.to_df(eigenvectors, eigenportfolios, snp_returns)
principal_eigenportfolio = eigenportfolios_df.iloc[0]; mth_eigenportfolio = eigenportfolios_df.iloc[-1]# 3b. Plot weights of principal eigenportfolio and mth eigenportfolio.

fig, axes = plt.subplots(nrows=2, ncols=1, figsize=(20, 10))
principal_eigenportfolio.plot.bar(ax=axes[0], color=’#4273FF’, edgecolor=’k’, linewidth=.25)
axes[0].grid(False); axes[0].legend([‘Principal Eigenportfolio’])
mth_eigenportfolio.plot.bar(ax=axes[1], color=’#C44E52′, edgecolor=’k’, linewidth=.25)
axes[1].grid(False); axes[1].legend([‘mth Eigenportfolio’])
plt.tight_layout();

Приведенный выше график показывает нам всестороннюю природу главного собственного портфеля. Собственный портфель mочень далек от главного собственного портфеля — поскольку каждый собственный вектор ортогонален друг другу, это имеет смысл.

Вычисление доходности собственного портфеля

Используя весовые коэффициенты, полученные выше, мы можем вычислить доходность каждого собственного портфеля с помощью следующего уравнения:

# 4. Compute the returns of each eigenportfolio.

def get_eigenportfolio_returns(eigenportfolios, snp_prices):
ep_returns = ((eigenportfolios @ snp_prices.T).T).pct_change()[1:]
col_map = {col: f»EP{col+1}» for col in ep_returns.columns}
return ep_returns.rename(columns=col_map).rename(columns={‘EP1’: ‘PrEP’})

eigenportfolio_returns = get_eigenportfolio_returns(eigenportfolios, snp_prices)
eigenportfolio_returns.head()

# 5. Plot the growth of the first eigenportfolio in tandem with that of the SPY ETF.
# =========================================================================================================

def get_growth(eigenportfolio_returns, spy_prices):
principal_eigenportfolio_returns = eigenportfolio_returns[‘PrEP’]
principal_eigenportfolio_growth = (1 + principal_eigenportfolio_returns).cumprod() — 1
spy_growth = (1 + spy_prices.pct_change()[1:]).cumprod() — 1
return principal_eigenportfolio_growth, spy_growth

def plot_comparative_growth(principal_eigenportfolio_growth, spy_growth, color=’#4273FF’):
plot = principal_eigenportfolio_growth.plot(color=color, linewidth=1.4)
spy_growth.plot(ax=plot, linestyle=’dashed’, linewidth=1.4)
plt.axhline(y=0, color=’k’, linewidth=.5, alpha=.75)
plt.title(‘Growth Curve’); plt.legend([‘Principal Eigenportfolio’, ‘SPY ETF’])
plt.xlabel(»); plt.ylabel(‘Growth’)

principal_eigenportfolio_growth, \
spy_growth = get_growth(eigenportfolio_returns, spy_prices)

corr_spy_pr_ep = spy_growth.squeeze().corr(principal_eigenportfolio_growth.squeeze())

plt.figure(figsize=(12, 4.5))
plot_comparative_growth(principal_eigenportfolio_growth, spy_growth)
print(f’Correlation between SPY ETF returns and Principal Eigen Portfolio returns: {corr_spy_pr_ep:,.2f}’)

Приведенный выше график показывает, что поведение основного собственного портфеля и SPY ETF очень похоже, что свидетельствует о большой дисперсии основного собственного портфеля по отношению к более широкому индексу S&P 500. Мы также можем видеть их сходство в сильной, положительной корреляции между ними, где ρ ≈ 0,92.

Готовимся к арбитражу: доходность собственного портфеля как фактор

Доходность собственного портфеля рисует многогранную картину экономического ландшафта. Ранние собственные портфели, как правило, отражают широкие экономические тенденции, влияющие на подавляющее большинство акций, в то время как более поздние часто освещают отраслевые закономерности. Возвращение первичного собственного портфеля играет ключевую роль, выявляя первичные тенденции, влияющие на динамику акций. В то же время эти факторы подчеркивают индивидуальную доходность, уникальную для каждой акции и не подверженную влиянию систематических факторов.

В контексте инвестирования эти факторы дают ценную информацию, которая может направлять формирование портфеля в соответствии с общими рыночными тенденциями. Это позволяет инвесторам настраивать свою подверженность каждому фактору в соответствии со своей толерантностью к риску, рыночными прогнозами и инвестиционными целями. Кроме того, эти факторы помогают инвесторам выявлять потенциальные несоответствия в ценообразовании, чтобы выявить возможности для арбитража, когда цена акции отклоняется от ее внутренней стоимости (как предполагает регрессия). Таким образом, эти факторы служат двойной цели: они помогают интерпретировать рыночные тенденции и закладывают основу для потенциальных арбитражных сделок. Они выходят за рамки своих теоретических построений, становясь практическими инструментами для прогнозирования рыночных сдвигов и выявления арбитражных возможностей. В нашем предстоящем обсуждении мы углубимся в арбитражную стратегию, построенную на основе этих принципов.

Анализ статистического арбитража

Статистический арбитраж (StatArb) — это любой метод в количественных финансах, который использует статистические и математические модели для использования краткосрочной неэффективности рынка. Известным StatArb является парная торговля, которую мы обсуждали в предыдущей статье и использующая коинтеграцию для торговли чистым рыночно-нейтральным портфелем из двух активов.

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

Подготовка среды

Подготовьте среду jupyter и pip установите следующие библиотеки:

  • numpy
  • pandas
  • yfinance

Вам понадобится доступ к analysis_utils библиотеке для общих функций.

Настройте Вселенную

Мы загрузим корзину акций с наибольшей капитализацией из Nasdaq 100.

Так как Stat Arb является тактической стратегией, мы сосредоточимся только на корреляции и ковариации, и отфильтруем вселенную акций по этим критериям.START_DATE = «2020-01-01»
END_DATE = «2023-11-22»
tickers = [«AAPL», «MSFT», «AMZN», «TSLA», «GOOG», «GOOGL», «META», «NVDA», «PEP», «AVGO», «ADBE», «COST», «PYPL», «AMD», «QCOM», «INTC», «TXN», «CHTR», «TMUS», «ISRG», «SBUX», «AMGN», «INTU», «ADP», «CSX», «ADI», «MU», «ZM», «MAR», «GILD», «MELI», «WDAY», «CTSH», «PANW», «REGN», «LRCX», «BKNG», «EXC», «ADSK», «KLAC», «TEAM»]

tickers_df = load_ticker_prices_ts_df(tickers, START_DATE, END_DATE)
tickers_rets_df = tickers_df.dropna(axis=1).pct_change().dropna() # first % is NaN
# 1+ to allow the cumulative product of returns over time, and -1 to remove it at the end.
tickers_rets_df = (1 + tickers_rets_df).cumprod() — 1
plt.figure(figsize=(16, 16))
for ticker in tickers_rets_df.columns:
plt.plot(tickers_rets_df.index, tickers_rets_df[ticker] * 100.0, label=ticker)
plt.xlabel(«Date (Year-Month)»)
plt.ylabel(«Cummulative Returns(%»)
plt.legend()
plt.show()

TARGET = «AAPL»
MA_WINDOW = 24
ARB_WINDOW = MA_WINDOW * 10
plt.figure(figsize=(16, 8))

corr_ticks = []
LEADS = []
for ticker in tickers:
if ticker == TARGET:
continue
correlation = tickers_df[ticker].corr(tickers_df[TARGET])
if abs(correlation) < 0.75:
continue
LEADS.append(ticker)
corr_ts = tickers_df[ticker].rolling(ARB_WINDOW).corr(tickers_df[TARGET])
plt.plot(
tickers_df.index,
corr_ts.rolling(MA_WINDOW).mean(),
label=f»{ticker} (Corr: {correlation:.2f})»,
alpha=correlation,
linewidth=correlation,
)
corr_ticks.append(ticker)
plt.axhline(y=0, color=»k», linestyle=»—«, linewidth=1)
plt.xlabel(«Date (Year-Month)»)
plt.ylabel(«Correlation with AAPL»)
plt.legend()
plt.show()

На приведенном выше графике мы видим корреляции с течением времени, и мы видим, что они колеблются между некоррелированными и коррелированными.

В реальных реализациях корзина акций периодически перестраивается по определенным факторам — корреляция может быть одним из них. К счастью для нас, большинство выбранных тикеров, таких как MSFT и GOOGL, остаются сильно коррелированными во время этой симуляции.

Сигнальное действие

Давайте пройдемся по псевдокоду для этого наивного алгоритма StatArb, который мы создаем:

  1. Сначала мы вычисляем экспоненциальную скользящую среднюю (EMA) с помощью заданного окна (тестируем 1 месяц). Это делается для всех акций, как лидов, так и таргета.
  2. Вычисляем дельту текущей цены от EMA по всем акциям.
  3. Используя скользящее окно (мы тестируем 1/2 в год), мы получаем попарную корреляцию между разницей EMA лидов и целевых акций. Это фиксирует краткосрочные тренды пары.
  4. То же самое мы проделываем для ковариационных коэффициентов между каждой парой. Это позволяет количественно оценить, насколько сильные изменения цены лидера могут спроецировать изменения на целевую цель.
  5. Мы используем ковариацию для создания прогнозов. Собственная разница EMA каждого лидера умножается на его ковариационный коэффициент с целью, чтобы спрогнозировать, какой «должна» быть разница цели, если она движется в ногу с лидером. Это похоже на алгоритм коинтеграции в парном трейдинге.
  6. Вычисляется погрешность между прогнозируемой и фактической целевой разницей EMA. Эта невязка показывает, насколько цель работает ниже или выше по сравнению с прогнозом на основе лидера. Сумма этих данных и будет нашим сигналом.
  7. Наконец, мы используем коэффициенты корреляции, чтобы взвесить входные данные каждого провода для сигнала.

Напоминаем, что ковариация — это:

  • Xbar и X — среднее арифметическое X и отдельного наблюдения.
  • Ybar и Y – это среднее значение Y и индивидуальное наблюдение.
  • N — количество наблюдений

Мы используем ковариацию, чтобы получить величину движения ведущей акции, а не коэффициенты корреляции (хотя это нормализованное значение имеет некоторую величину, эта информация была утеряна). Сама по себе ковариация не имеет внутренней интерпретации.

Вот код:arb_df = tickers_rets_df.copy()
arb_df[«price»] = tickers_df[TARGET]
arb_df[f»ema_{TARGET}»] = arb_df[TARGET].ewm(MA_WINDOW).mean()
arb_df[f»ema_d_{TARGET}»] = arb_df[TARGET] — arb_df[f»ema_{TARGET}»]
arb_df[f»ema_d_{TARGET}»].fillna(0, inplace=True)

for ticker in LEADS:
arb_df[f»ema_{ticker}»] = arb_df[ticker].ewm(MA_WINDOW).mean()
arb_df[f»ema_d_{ticker}»] = arb_df[ticker] — arb_df[f»ema_{ticker}»]
arb_df[f»ema_d_{ticker}»].fillna(0, inplace=True)
arb_df[f»{ticker}_corr»] = (
arb_df[f»ema_d_{ticker}»].rolling(ARB_WINDOW).corr(arb_df[f»ema_d_{TARGET}»])
)
arb_df[f»{ticker}_covr»] = (
arb_df[[f»ema_d_{ticker}», f»ema_d_{TARGET}»]]
.rolling(ARB_WINDOW)
.cov(numeric_only=True)
.groupby(level=0, axis=0, dropna=True) # Cov returns pairwise!
.apply(lambda x: x.iloc[0, 1] / x.iloc[0, 0])
)
arb_df[f»{ticker}_emas_d_prj»] = (
arb_df[f»ema_d_{ticker}»] * arb_df[f»{ticker}_covr»]
)
arb_df[f»{ticker}_emas_act»] = (
arb_df[f»{ticker}_emas_d_prj»] — arb_df[f»ema_d_{TARGET}»]
)
arb_df.filter(regex=f»(_emas_d_prj|_corr|_covr)$»).dropna().iloc[ARB_WINDOW:]

Теперь взвесим сигналы:ts = 0
delta_projected = 0
for ticker in LEADS:
corr_abs = abs(arb_df[f»{ticker}_corr»].fillna(0))
weights += corr_abs
arb_df[f»{ticker}_emas_act_w»] = arb_df[f»{ticker}_emas_act»].fillna(0) * corr_abs
delta_projected += arb_df[f»{ticker}_emas_act_w»]

weights = weights.replace(0, 1)
arb_df[f»{TARGET}_signal»] = delta_projected / weights
arb_df[f»{TARGET}_signal»].iloc[ARB_WINDOW:]

Чтобы визуализировать качество нашего сигнала, рассчитаем погрешности и доверительные интервалы сигналов на уровне 95%.

Где:

  • Xhat — выборочное среднее,
  • Z – Z-оценка, соответствующая желаемому уровню достоверности (1,96 для 95% доверительного интервала),
  • Сигма – это стандартное отклонение генеральной совокупности или, в нашем случае, невязки.
  • n — размер выборки.

Приведенный ниже код рассчитает эти критерии и будет использован в дальнейшем при работе по сигналу:errors = (
arb_df[f»ema_{TARGET}»] + arb_df[f»ema_d_{TARGET}»] + arb_df[f»{TARGET}_signal»]
) — (arb_df[f»ema_{TARGET}»].shift(-1))
arb_df[«rmse»] = np.sqrt((errors**2).rolling(ARB_WINDOW).mean()).fillna(0)

me = errors.rolling(ARB_WINDOW).mean().fillna(0)
e_std = errors.rolling(ARB_WINDOW).std().fillna(0)
ci = (me — 1.96 * e_std, me + 1.96 * e_std)
arb_df[«ci_lower»] = arb_df[f»{TARGET}_signal»].fillna(0) + ci[0]
arb_df[«ci_upper»] = arb_df[f»{TARGET}_signal»].fillna(0) + ci[1]
arb_df[«ci_spread»] = arb_df[«ci_upper»] — arb_df[«ci_lower»]
arb_df.fillna(0, inplace=True)
arb_df[[«ci_lower», «ci_upper»]].iloc[ARB_WINDOW:].tail(10)

Визуализация сигнала и ошибок

Теперь, когда метрики сигналов и ошибок готовы, давайте визуализируем все это:fig, axes = plt.subplots(2, 1, gridspec_kw={«height_ratios»: (3, 1)}, figsize=(22, 8))

axes[0].set_title(f»Visualizing {TARGET}’s Signal and Errors»)
axes[0].plot(
arb_df.iloc[ARB_WINDOW:].index,
arb_df[TARGET].iloc[ARB_WINDOW:],
label=f»{TARGET} $»,
alpha=1,
color=»b»,
)
axes[0].plot(
arb_df.iloc[ARB_WINDOW:].index,
arb_df[f»ema_{TARGET}»].iloc[ARB_WINDOW:],
label=f»{TARGET} EMA $»,
alpha=1,
)
axes[0].plot(
arb_df.iloc[ARB_WINDOW:].index,
arb_df[f»{TARGET}_signal»].iloc[ARB_WINDOW:].fillna(0)
+ arb_df[TARGET].iloc[ARB_WINDOW:],
label=f»{TARGET} + Signal $»,
alpha=0.75,
linestyle=»—«,
color=»r»,
)
axes[0].legend()
axes[1].plot(
arb_df.iloc[ARB_WINDOW:].index,
arb_df[f»{TARGET}_signal»].iloc[ARB_WINDOW:],
label=f»Wieghted {TARGET} Signal $»,
alpha=0.75,
color=»g»,
)
axes[1].plot(
arb_df.iloc[ARB_WINDOW:].index,
arb_df[«rmse»].iloc[ARB_WINDOW:],
label=»RMSE»,
alpha=0.75,
linestyle=»—«,
color=»r»,
)
axes[1].fill_between(
arb_df.iloc[ARB_WINDOW:].index,
arb_df[«ci_lower»].iloc[ARB_WINDOW:],
arb_df[«ci_upper»].iloc[ARB_WINDOW:],
color=»gray»,
alpha=0.3,
)
axes[1].axhline(0, color=»black», linestyle=»—«, linewidth=1)
axes[1].legend()
plt.tight_layout()
plt.show()

По мере того, как окно StatArb охватывает все больше данных, его прогнозы становятся более «точными». Чем плотнее доверительные коридоры, тем больше вероятность того, что мы будем моделировать сделку.

Должен сказать, что в постковидном режиме рынка (2020–2023 гг.) наблюдались некоторые долгосрочные тенденции, которые способствовали точности наших прогнозов.

Симуляция торговли

Теперь давайте действовать по нашим сигналам. Это симуляция, поэтому мы игнорируем различные рыночные нюансы, в том числе стоп-лоссы и транзакционные издержки.

Приведенный ниже код будет открывать длинную или короткую позицию по портфелю в зависимости от положения сигнала до выбранных пороговых значений:LONG_THRESHOLD = 0.0025
SHORT_THRESHOLD = -0.0025
CONF_SPREAD_THRESHOLD = 0.15
MAX_SHARES = 1

arb_df[«orders»] = 0
signals = arb_df[f»{TARGET}_signal»]
prev_signals = signals.shift(-1)
add_long_cond = (signals > LONG_THRESHOLD) & (prev_signals <= LONG_THRESHOLD) & (signals < arb_df[«ci_upper»]) & (signals > arb_df[«ci_lower»]) & (arb_df[«ci_spread»] < CONF_SPREAD_THRESHOLD)
add_short_cond = (signals < SHORT_THRESHOLD) & (prev_signals >= SHORT_THRESHOLD) & (signals < arb_df[«ci_upper»]) & (signals > arb_df[«ci_lower»]) & (arb_df[«ci_spread»] < CONF_SPREAD_THRESHOLD)

arb_df.loc[add_long_cond, «orders»] += MAX_SHARES
arb_df.loc[add_short_cond, «orders»] -= MAX_SHARES
arb_df[«orders»].fillna(0, inplace=True)
arb_df.loc[arb_df[«orders»] != 0, «orders»].tail(10)

В этом разделе кода Pandas используется для получения PnL. Реальная система исполнения сделок будет сильно отличаться от этой:signal_changes_df = arb_df.loc[(add_long_cond | add_short_cond), [«price», «orders», f»{TARGET}_signal»]]

signal_changes_df[«holdings»] = signal_changes_df[«orders»].cumsum()
signal_changes_df[«stat_chng»] = np.sign(signal_changes_df[«orders»].shift(1)) != np.sign(signal_changes_df[«orders»])
prev_holdings = signal_changes_df[«holdings»].shift(1)
signal_changes_df[«price_open»] = signal_changes_df[«price»].shift(1)
signal_changes_df[«cost_open_avg»] = (signal_changes_df.loc[signal_changes_df[«stat_chng»] == False, «price_open»].shift(1) + signal_changes_df.loc[signal_changes_df[«stat_chng»]== False, «price_open»]) / 2
signal_changes_df[«cost_open_avg»].fillna(signal_changes_df[«price_open»], inplace=True)
signal_changes_df[«price_close»] = signal_changes_df[«price»]
signal_changes_df[«price_close»].iloc[0] = np.nan # First signal shouldn’t have a closing price
signal_changes_df[«pnl»] = (signal_changes_df.loc[signal_changes_df[«stat_chng»], «price_close»] — signal_changes_df.loc[signal_changes_df[«stat_chng»], «cost_open_avg»]) * np.sign(signal_changes_df[«holdings»].shift(1))
signal_changes_df[«pnl_rets»] = signal_changes_df[«pnl»] / signal_changes_df[«cost_open_avg»].abs()
signal_changes_df.fillna(0, inplace=True)
arb_df = pd.concat([arb_df, signal_changes_df[[«pnl», «pnl_rets», «holdings»]]], axis=1).drop_duplicates(keep=’last’)
signal_changes_df.tail(10)

Наконец, мы визуализируем сделку, совершенную с сигналом и допустимыми мерами ошибки:plt.figure(figsize=(26, 18))

fig, (ax1, ax2, ax3) = plt.subplots(
3, 1, gridspec_kw={«height_ratios»: (3, 1, 1)}, figsize=(24, 12)
)
ax1.plot(
arb_df.iloc[ARB_WINDOW:].index,
arb_df[«price»].iloc[ARB_WINDOW:],
color=»g»,
lw=1.25,
label=f»{TARGET}»,
)
ax1.plot(
arb_df.loc[add_long_cond].index,
arb_df.loc[add_long_cond, «price»],
«^»,
markersize=12,
color=»blue»,
label=»Buy»,
)
ax1.plot(
arb_df.loc[add_short_cond].index,
arb_df.loc[add_short_cond, «price»],
«v»,
markersize=12,
color=»red»,
label=»Sell»,
)
ax1.set_ylabel(«Price in $»)
ax1.legend(loc=»upper left», fontsize=10)
ax1.set_title(f»Stat-Arb {TARGET}», fontsize=18)
ax2.plot(
arb_df[«pnl»].iloc[ARB_WINDOW:].index, arb_df[«pnl_rets»].iloc[ARB_WINDOW:].fillna(0).cumsum(), color=»b», label=»Returns»
)
ax2.set_ylabel(«Cumulative Profit (%)»)
ax2.legend(loc=»upper left», fontsize=10)
ax2.set_title(f»Cummulative Returns», fontsize=18)
ax3.plot(
arb_df.iloc[ARB_WINDOW:].index,
arb_df[f»{TARGET}_signal»].iloc[ARB_WINDOW:],
label=f»Signal»,
alpha=0.75,
color=»g»,
)
ax3.plot(
arb_df.loc[add_long_cond].index,
arb_df.loc[add_long_cond, f»{TARGET}_signal»],
«^»,
markersize=12,
color=»blue»,
label=»Buy»,
)
ax3.plot(
arb_df.loc[add_short_cond].index,
arb_df.loc[add_short_cond, f»{TARGET}_signal»],
«v»,
markersize=12,
color=»red»,
label=»Sell»,
)
ax3.axhline(0, color=»black», linestyle=»—«, linewidth=1)
ax3.axhline(SHORT_THRESHOLD, color=»r», linestyle=»—«, label=f»Short ({SHORT_THRESHOLD})»,linewidth=1)
ax3.axhline(LONG_THRESHOLD, color=»b», linestyle=»—«, label=f»Long ({LONG_THRESHOLD})»,linewidth=1)
ax3.fill_between(arb_df.index, arb_df[f»{TARGET}_signal»], SHORT_THRESHOLD, where=(arb_df[f»{TARGET}_signal»] < SHORT_THRESHOLD), interpolate=True, color=’red’, alpha=0.3)
ax3.fill_between(arb_df.index, arb_df[f»{TARGET}_signal»], LONG_THRESHOLD, where=(arb_df[f»{TARGET}_signal»] > LONG_THRESHOLD), interpolate=True, color=’blue’, alpha=0.3)
ax3.fill_between(
arb_df.iloc[ARB_WINDOW:].index,
arb_df[«ci_lower»].iloc[ARB_WINDOW:],
arb_df[«ci_upper»].iloc[ARB_WINDOW:],
color=»gray»,
alpha=0.3,
label=f»Confidence»
)
ax3.legend(loc=»lower left», fontsize=10)
ax3.set_title(f»Leads’ Signal», fontsize=18)
plt.tight_layout()
plt.show()

Поскольку это симуляция, мы упростили рынок, как и сам алгоритм StatArb. Вариант в реальном мире будет использовать сложные статистические процессы в среде высокочастотного исполнения, торгуя внутри дня или близко к этому, поскольку альфа от рыночной неэффективности берется мгновенно.

Мы также пропустили бэк-тестирование, форвард-тестирование и настройку параметров таких параметров, как длины скользящих окон и пороговые значения.

Заключение

В этой статье мы смоделировали наивную стратегию статистического арбитража, построенную на доверительных интервалах и дисперсионно-взвешенных сигналах.

Как и большинство финансовых факторов, это игрушечная стратегия, и ее не следует использовать за пределами этого анализа. Берегите себя.

Шаг в ногу с рынком от MidJourney 12.2023

Ссылки

Github

Статья здесь также доступна на Github

Блокнот Kaggle доступен здесь

Автоматизация анализа данных (EDA)

Что такое EDA?

САПР — это одна из самых важных вещей, которые мы должны сделать в качестве подхода, чтобы лучше понять набор данных. Почти все специалисты по анализу данных или науке о данных выполняют этот процесс перед получением аналитических сведений или моделированием данных. В реальной жизни этот процесс занимал много времени, в зависимости от сложности и полноты имеющегося у нас датасета. Конечно, большее количество переменных заставляет нас больше исследовать, чтобы получить необходимую сводку, прежде чем делать следующие шаги.

Вот почему использование R или Python, наиболее распространенного языка программирования для анализа данных, некоторые пакеты помогают сделать этот процесс быстрее и проще, но не лучше. Почему не лучше? Потому что он показывает нам только сводку, прежде чем мы сосредоточимся на более глубоком изучении любых переменных, которые мы считаем «интересными».

Действует «правило 80/20»: 80 процентов драгоценного времени аналитика данных или ученого тратится на простой поиск, очистку и организацию данных, оставляя только 20 процентов для выполнения анализа.

Какие библиотеки мы можем использовать?

В R мы можем использовать следующие библиотеки:

  1. dataMaid
  2. DataExplorer
  3. SmartEDA

В Python мы можем использовать следующие библиотеки:

  1. ydata-profiling
  2. dtale
  3. sweetviz
  4. autoviz

Давайте попробуем каждую из перечисленных выше библиотек, чтобы узнать, как они выглядят и как они могут помочь нам в исследовательском анализе данных! В этом посте я буду использовать набор данных iris, который обычно используется для изучения программирования на R или Python.

В R можно использовать следующий код для загрузки набора iris:# iris is part of R’s default, no need to load any packages
df = iris
# use «head()» to show the first 6 rows
head(df)

Рисунок 1. Загрузка набора данных iris в R

В Python вы можете использовать этот код для загрузки набора данных iris:# need to import these things first
from sklearn.datasets import load_iris
import numpy as np
import pandas as pd
# use load_iris
iris = load_iris()
# convert into a pandas data frame
df = pd.DataFrame(
data= np.c_[iris[‘data’], iris[‘target’]],
columns= iris[‘feature_names’] + [‘species’]
)
# set manually the species column as a categorical variable
df[‘species’] = pd.Categorical.from_codes(iris.target, iris.target_names)
# use «.head()» to show the first 5 rows
df.head()

Рисунок 2. Загрузка набора данных ‘iris’ в Python

Р: dataMaid

Во-первых, нам нужно выполнить простой код, приведенный ниже:# install the dataMaid library
install.packages(«dataMaid»)
# load the dataMaid library
library(dataMaid)
# use makeDataReport with HTML as output
makeDataReport(df, output = «html», replace = TRUE)

Из первого снимка (рис. 3) мы уже получаем много информации о наборе данных iris:

  1. Количество наблюдений – 150.
  2. Количество переменных равно 5.
  3. В зависимости от типа данных каждой переменной выполнялись проверки переменных, такие как выявление неправильно закодированных отсутствующих значений, уровней с < 6 ob и выбросов.
Рисунок 3. Первый снимок созданного отчета с использованием dataMaid набора данных iris

Из второго снимка (рис. 4):

  1. Сводная таблица переменных включает класс переменных, уникальные значения, отсутствующие наблюдения и любые обнаруженные проблемы. Мы видим, что в переменных Sepal.Width и Petal.Length обнаружены проблемы.
  2. Sepal.Length, включая гистограмму, дали нам одномерное распределение.
  3. Sepal.Width имеет возможные значения выбросов, перечисленные ниже. Вот почему в сводной таблице указано, что обнаружены проблемы.
Рисунок 4. Второй снимок созданного отчета с использованием dataMaid набора данных iris

Из третьего снимка (рис. 5):

  1. Petal.Length имеет возможные значения выбросов, перечисленные ниже.
  2. Были предоставлены центральные измерения Petal.Width, включая гистограмму, чтобы получить одномерное распределение.
  3. Species в качестве целевой переменной определяется как factor, и количество данных одинаково для каждого типа, то есть 50.
Рисунок 5. Третий снимок созданного отчета с использованием dataMaid набора данных iris

На основе приведенного выше отчета о данных, созданного с помощью dataMaid в R, мы уже получаем много информации о наборе данных iris, только выполнив однострочный код. 😃

Р: DataExplorer

Во-первых, нам нужно выполнить простой код, приведенный ниже:# install the DataExplorer library
install.packages(«DataExplorer»)
# load the DataExplorer library
library(DataExplorer)
# use create_report
create_report(df)

С первого по шестой снимок (изображения 6, 7, 8, 9, 10, 11) информация, которую мы получили, мало чем отличалась от предыдущего пакета.

Рисунок 6. Первый снимок созданного отчета с помощью DataExplorer набора данных радужной оболочки глаза
Рисунок 7. Второй снимок созданного отчета с помощью DataExplorer набора данных радужной оболочки глаза
Рисунок 8. Третий снимок созданного отчета с помощью DataExplorer набора данных радужной оболочки глаза
Рисунок 9. Четвертый снимок созданного отчета с помощью DataExplorer набора данных радужной оболочки глаза
Рисунок 10. Пятый снимок созданного отчета с помощью DataExplorer набора данных радужной оболочки глаза
Рисунок 11. Шестой снимок созданного отчета с помощью DataExplorer набора данных радужной оболочки глаза

Из седьмого снимка (рис. 12) мы получили график КК для каждой числовой переменной в наборе данных iris.

Рисунок 12. Седьмой снимок созданного отчета с помощью DataExplorer набора данных радужной оболочки глаза

Из восьмого снимка (рис. 13) мы получили корреляционную матрицу для каждой переменной в наборе данных iris. Мы можем увидеть некоторую информацию, такую как:

  1. Petal.Width и Petal.Length имеют сильную положительную корреляцию 0,96, что означает, что в наборе данных iris чем шире ширина лепестка, тем больше длина лепесткаPetal.Width
  2. Species_setosa и Petal.Length имеет сильную отрицательную корреляцию -0,92, что означает, что в наборе данных iris чем короче длина лепестка, тем выше вероятность того, что вид является setosa.
  3. Используя приведенные выше примеры, пожалуйста, предоставьте свои выводы с помощью этой корреляционной матрицы.
Рисунок 13. Восьмой снимок созданного отчета с помощью DataExplorer набора данных радужной оболочки глаза

На девятом снимке (рис. 14) с использованием метода главных компонент (PCA) показан процент дисперсии, объясняемой главными компонентами, с примечанием о том, что метки указывают на кумулятивный процент объяснимой дисперсии, он показывает 62%, чем выше, тем лучше. Для объяснения PCA, я думаю, мне нужен еще один пост для этого. 😆

Рисунок 14. Девятый снимок созданного отчета с помощью DataExplorer набора данных радужной оболочки глаза

Десятый снимок (рис. 15) показывает относительную важность каждой переменной, он показывает, что Petal.Length имеет наибольшую важность, которая составляет почти 0,5, за ней следует Petal.Width и так далееPetal.Length

Рисунок 15. Десятый снимок созданного отчета с помощью DataExplorer набора данных iris

Р: SmartEDA

Во-первых, нам нужно выполнить простой код, приведенный ниже:# install the SmartEDA library
install.packages(«SmartEDA»)
# load the SmartEDA library
library(SmartEDA)
# use ExpReport
ExpReport(df, op_file = ‘SmartEDA_df.html’)

Из изображений 16, 17, 18, 23 и 24 информация, которую мы получили, не сильно отличалась от предыдущего пакета.

Рисунок 16. Первый снимок созданного отчета с помощью ‘SmartEDA’ набора данных радужной оболочки глаза
Рисунок 17. Второй снимок созданного отчета с помощью ‘SmartEDA’ набора данных радужной оболочки глаза
Рисунок 18. Третий снимок созданного отчета с помощью ‘SmartEDA’ набора данных радужной оболочки глаза

На рисунке 19 показан график плотности каждой переменной, включая измерение асимметрии и эксцесса, которое используется для определения того, нормально ли распределены данные или нет. Объяснение асимметрии и эксцесса тоже нуждается в другом посте, я думаю 😅

Рисунок 19. Четвертый снимок созданного отчета с помощью SmartEDA набора данных радужной оболочки глаза

На рисунках 20, 21 и 22 показана точечная диаграмма между числовыми переменными, доступными в наборе данных iris, которая визуально показывает нам корреляцию. Он дает нам информацию, аналогичную корреляционной матрице, которая представлена в числовом формате.

Рисунок 20. Пятый снимок созданного отчета с использованием ‘SmartEDA’ датасета радужной оболочки глаза
Рисунок 21. Шестой снимок созданного отчета с использованием ‘SmartEDA’ набора данных радужной оболочки глаза
Рисунок 22. Седьмой снимок созданного отчета с использованием ‘SmartEDA’ датасета радужной оболочки глаза
Рисунок 23. Девятый снимок созданного отчета с помощью SmartEDA набора данных радужной оболочки глаза
Рисунок 24. Десятый снимок созданного отчета с помощью SmartEDA датасета радужной оболочки глаза

Р: Заключение

Используя три пакета, описанных выше, мы получили много информации о наборе iris, гораздо быстрее, чем мы пытались создать его вручную, но этого недостаточно, поэтому я сказал в заголовке «… быстрее и проще…», потому что это дает нам только представление о наборе данных iris, но, по крайней мере, дает нам то, над чем мы можем начать работать, а не искать отправную точку, например:

  1. Нет пропущенных переменных / нет неправильно закодированных переменных, мы можем пропустить эти шаги.
  2. Выбросы, обнаруженные в некоторых переменных, мы можем начать очистку данных, используя любые подходящие методы для обработки значений выбросов, вместо того, чтобы вручную искать, какие переменные имеют значения выбросов, одно за другим.
  3. При необходимости мы можем начать работать с переменными, которые обычно не распределяются.
  4. На основе корреляционной матрицы и диаграмм рассеяния мы получили представление о том, какие переменные сильно или слабо коррелируют.
  5. При использовании анализа главных компонент (PCA) процент дисперсии, объясняемой главными компонентами, указывается, что метки указывают на кумулятивный процент объясненной дисперсии
  6. Относительная важность каждого объекта набора данных iris также показана в этом автоматизированном EDA.

Python: ydata-профилирование

Во-первых, нам нужно выполнить простой код, приведенный ниже:# install the ydata-profiling package
pip install ydata-profiling
# load the ydata_profiling package
from ydata_profiling import ProfileReport
# use ProfileReport
pr_df = ProfileReport(df)
# show pr_df
pr_df

В основном, он показывает аналогичную информацию. Я постараюсь упомянуть некоторую информацию, которая довольно сильно отличается от предыдущих пакетов:

  1. На рисунке 26 мы получили сводку в предложениях о том, какие переменные имеют высокую корреляцию.
  2. В целом, вывод стал более интерактивным по сравнению с предыдущими пакетами, потому что мы можем щелкнуть мышью, чтобы перейти к другим вкладкам, и выбрать определенные столбцы для отображения.
Рисунок 25. Первый снимок созданного отчета с использованием ydata_profiling набора данных радужной оболочки глаза
Рисунок 26. Второй снимок созданного отчета с использованием ydata_profiling набора данных радужной оболочки глаза
Рисунок 27. Третий снимок созданного отчета с использованием ydata_profiling набора данных радужной оболочки глаза
Рисунок 28. Четвертый снимок созданного отчета с использованием ydata_profiling набора данных радужной оболочки глаза
Рисунок 29. Пятый снимок созданного отчета с использованием ydata_profiling набора данных радужной оболочки глаза
Рисунок 30. Шестой снимок созданного отчета с использованием ydata_profiling набора данных радужной оболочки глаза
Рисунок 31. Седьмой снимок созданного отчета с использованием ydata_profiling набора данных радужной оболочки глаза
Рисунок 32. Восьмой снимок созданного отчета с использованием ydata_profiling набора данных радужной оболочки глаза
Рисунок 33. Девятый снимок созданного отчета с использованием ydata_profiling набора данных радужной оболочки глаза

Python: dtale

Во-первых, нам нужно выполнить простой код, приведенный ниже:# install the dtale package
pip install dtale
# load the dtale
import dtale
# use show
dtale.show(df)

Вывод этого пакета сильно отличается от предыдущих пакетов, с точки зрения того, как его использовать, содержимое довольно похоже, но это заставляет нас лучше исследовать.

Рисунок 34. Первый снимок созданного отчета с использованием ‘dtale’ набора данных iris
Рисунок 35. Второй снимок созданного отчета с использованием ‘dtale’ набора данных радужной оболочки глаза
Рисунок 36. Третий снимок созданного отчета с использованием ‘dtale’ набора данных радужной оболочки глаза
Рисунок 37. Четвертый снимок созданного отчета с использованием ‘dtale’ набора данных радужной оболочки глаза

Python: sweetviz

Во-первых, нам нужно выполнить простой код, приведенный ниже:# install the sweetviz package
pip install sweetviz
# load the sweetviz
import sweetviz
# use analyze
analyze_df = sweetviz.analyze([df, «df»], target_feat = ‘species’)
# then show
analyze_df.show_html(‘analyze.html’)

Используя этот пакет, пользовательский интерфейс и UX очень отличаются, пожалуйста, наслаждайтесь шоу!

Рисунок 38. Первый снимок созданного отчета с помощью sweetviz набора данных iris
Рисунок 39. Второй снимок созданного отчета с помощью sweetviz набора данных iris

HЭто означает, что человеческий мозг обрабатывает изображения в 60 000 раз быстрее, чем текст, а 90 процентов информации, передаваемой в мозг, является визуальной. Визуальная информация упрощает совместную работу и генерацию новых идей, влияющих на эффективность организации. Это единственная причина, по которой аналитик данных тратит максимум времени на визуализацию данных.

Python: autoviz

Во-первых, нам нужно выполнить простой код, приведенный ниже:# install the dtale package
pip install autoviz
# load the autoviz
from autoviz import AutoViz_Class
# set AutoViz_Class()
av = AutoViz_Class()
# produce AutoVize_Class of df
avt = av.AutoViz(
«»,
sep = «,»,
depVar = «»,
dfte = df,
header = 0,
verbose = 1,
lowess = False,
chart_format = «server»,
max_rows_analyzed = 10000,
max_cols_analyzed = 10,
save_plot_dir=None
)

С помощью приведенного выше кода в браузере генерируются некоторые вкладки. Новое, что мы можем увидеть с помощью этого пакета:

  1. Выходные данные генерируются в нескольких вкладках браузера, предыдущие пакеты отображают все выходные данные в одной вкладке.
  2. Скрипичный график каждой переменной. Это гибридная версия блочной диаграммы и диаграммы плотности ядер. По-прежнему показывает аналогичную информацию по сравнению с предыдущими пакетами.
Рисунок 40. Первый снимок созданного отчета с помощью autoviz датасета радужной оболочки глаза
Рисунок 41. Второй снимок созданного отчета с помощью autoviz датасета радужной оболочки глаза
Рисунок 41. Третий снимок созданного отчета с помощью autoviz датасета радужной оболочки глаза
Рисунок 42. Четвертый снимок созданного отчета с помощью autoviz датасета радужной оболочки глаза
Рисунок 43. Пятый снимок созданного отчета с помощью autoviz датасета iris

Заключение

Используя четыре пакета, описанные выше, мы получили много информации о наборе данных iris, не слишком большой разницы по сравнению с пакетами R, но все же, когда иметь большую перспективу обычно лучше, чем меньшую. Несколько замечаний:

  1. Выходные данные пакетов Python в основном более интерактивны по сравнению с пакетами R.
  2. При установке пакетов могут возникать некоторые ошибки. Для dtale распространенной ошибкой является jinja и escape. Вы можете получить решение, обратившись к этому посту.
  3. В некоторых пакетах код не такой простой, как в пакетах R, но я думаю, что это не большая проблема, пока мы не поленимся прочитать инструкцию по эксплуатации, думаю все ок.

Источник

Источник

Источник