1 R & Python: как их совместно использовать посредством библиотеки reticulate

С недавних пор особая версия Rstudio (версия 1.2.xxx) позволяет выполнять код на R и Python совместно использую оперативную память в программе Rstudio. При этом пакет rmarkdown должен быть не старше версии 1.10. Учитывайте, что “дневная” версия RStudio пока не стабильно выполняет код Python и ее нужно перегружать всякий раз перез запуском. Кроме того, в коде Python он не любит кириллические символы.

Мы применяем пакеты предустановленные в поставке Machine Learning Server 9.х, которая содержит наряду с R также и Python - конкретно сборку Anaconda 4.2 with Python 3.5.

## -- Attaching packages ------------------------------------------------------------------------------- tidyverse 1.2.1 --
## v ggplot2 3.1.0       v purrr   0.3.2  
## v tibble  2.1.1       v dplyr   0.8.0.1
## v tidyr   0.8.3       v stringr 1.4.0  
## v readr   1.3.1       v forcats 0.4.0
## -- Conflicts ---------------------------------------------------------------------------------- tidyverse_conflicts() --
## x dplyr::filter() masks stats::filter()
## x dplyr::lag()    masks stats::lag()
## 
## Attaching package: 'magrittr'
## The following object is masked from 'package:purrr':
## 
##     set_names
## The following object is masked from 'package:tidyr':
## 
##     extract
## python:         C:\Program Files\Microsoft\MLS\PYTHON_SERVER/python.exe
## libpython:      C:/Program Files/Microsoft/MLS/PYTHON_SERVER/python35.dll
## pythonhome:     C:\PROGRA~1\MIE74D~1\MLS\PYTHON~1
## version:        3.5.2 |Anaconda 4.2.0 (64-bit)| (default, Jul  5 2016, 11:41:13) [MSC v.1900 64 bit (AMD64)]
## Architecture:   64bit
## numpy:          C:\PROGRA~1\MIE74D~1\MLS\PYTHON~1\lib\site-packages\numpy
## numpy_version:  1.14.5
## 
## NOTE: Python version was forced by use_python function

Так же обратите внимание на то, что различные версии **Python* библиотек могут иметь различный синтаксис и из-за объектно-ориентированности этого языка программирования это будет вносить существеннуе изменения в код, что можеит приводить к сбоям уже отлажанных программ. Например, в версии 0.18 библиотеки sklearn, занимающейся машинным обучением у объекта GridSearchCV существовал атрибут grid_scores_. Однако в версии 0.20 он уже безвозвратно изчез и стало необходимым обращаться к атрибуту cv_results_. При этом, составные части последнего также будут изменяться от версии к версии в последующем.

Было бы хорошо, если такие изменения касались только прикладных библиотек. Однако это не так. Существенные изменения в синтаксисе проиходят даже в таких базовых библиотеках как pandas. Например, в версии 0.21 прекратил функционирование метод DataFrame.reindex_axis(). Так, что работая с такой “лапочкой” как Python будьте осторожны, не забывайте, что “сетчатый” - коварная рептилия.

2 Чтение и предобработка данных

Будем работать с данными за 2012-2019 гг., которые были получены из разнообразных баз данных в Интернете.

## The used python packages
## The numpy version is 1.14.5.
## The pandas version is 0.19.2.
## The scikit-learn version is 0.18.1.
## The scipy version is 0.19.0.
##       Period  Badrate  LN_Badrate         PoG_CP         PS_CP          NT_CP  \
## 0 2012-01-01      0.0   -6.907755  786052.700000  1.059344e+06  187179.833333   
## 1 2012-02-01      0.0   -6.907755  786052.700000  1.059344e+06  187179.833333   
## 2 2012-03-01      0.0   -6.907755  786052.700000  1.059344e+06  187179.833333   
## 3 2012-04-01      0.0   -6.907755  915496.433333  1.122793e+06  212134.500000   
## 4 2012-05-01      0.0   -6.907755  915496.433333  1.122793e+06  212134.500000   
## 
##          GDP_CP         PoG_05     PS_05         NT_05      ...       \
## 0  2.032577e+06  381646.600000  481124.1  43321.066667      ...        
## 1  2.032577e+06  381646.600000  481124.1  43321.066667      ...        
## 2  2.032577e+06  381646.600000  481124.1  43321.066667      ...        
## 3  2.250424e+06  384440.966667  522228.9  42671.666667      ...        
## 4  2.250424e+06  384440.966667  522228.9  42671.666667      ...        
## 
##    iFATS_OILS     iGRAINS  iOTHERFOOD  iRAW_MATERIAL     iTIMBER  \
## 0  113.562990  129.395382  106.955920     105.724914  111.329464   
## 1  117.047836  130.962494  110.966636     107.847977  111.058334   
## 2  122.580266  132.792130  113.217004     106.463610  109.771439   
## 3  129.173677  129.661085  109.323425     106.255568  110.410051   
## 4  125.242031  131.711678  104.370695     104.597615  111.078986   
## 
##    iOTHERRAWMAT  iFERTILIZERS     iMETMIN    iBASEMET  iPRECIOUSMET  
## 0     99.595886    138.557763  100.502475  101.512249    136.910716  
## 1    104.337191    139.326660  104.005008  105.806667    146.277188  
## 2    102.846232    136.905741  103.543268  104.558683    140.782530  
## 3    101.712306    154.337038  100.953867  100.892434    137.600443  
## 4     97.509712    161.103661   96.628640   97.319598    130.908813  
## 
## [5 rows x 68 columns]

Зафиксируем целевую переменную y, представляющую отношение суммы активов банков, которые оказались в дефолте к общей сумме активов банков второго уровня, выраженное в процентных пунктах (Badrate), и удалим ее из данных - независимых переменных. Отдельно вынесем обозначения периодов Period. Удалим признаки Badrate, LN_Badrate и Period из исходных данных.

## count    84.000000
## mean      0.015251
## std       0.022695
## min       0.000000
## 25%       0.000000
## 50%       0.000000
## 75%       0.025810
## max       0.079678
## Name: Badrate, dtype: float64

Разделим набор данных на две части: обучающую train_df и тестовую выборки test.df. Последняя содержит 30% периодов наблюдений. Посмотрим размерность получившихся массивов.

## X_train.shape =  (58, 65)
## X_test.shape  = (26, 65)
## R Chunk

# Chart Function for `ggplot2` & `plotly` packages

ShowCaptionsofChart <- function(p, is_ggplot2) {
# Modify the layout of a plotly visualization & Add Mathjax 
  
  # Load required packages
  if (!requireNamespace('ggplot2', quietly = TRUE)) 
    stop('ggplot2 is needed for this function to work. Install it via install.packages("ggplot2")',
         call. = FALSE)
  if (!requireNamespace('plotly', quietly = TRUE)) 
    stop('plotly is needed for this function to work. Install it via install.packages("plotly")',
         call. = FALSE)

  p %>% 
    plotly::layout(annotations =
       list(x = 0, y = 1, text = paste('Graph is created by', ifelse(is_ggplot2 == TRUE, '`ggplot2` & `plotly` packages',
                                                                     '`plotly` package'), 'of R'),
            showarrow = FALSE, xref='paper', yref='paper',
            # xanchor='right', yanchor='auto', xshift=0, yshift=0,
            font=list(size=15, color='gray'))
       ) %>%
        plotly::layout(annotations =
           list(x = 1, y = 0, 
                text = 'Совместная работа языков программирования R и Python в программе RStudio 1.2.xxx',
                showarrow = FALSE, xref = 'paper', yref = 'paper',
                # xanchor='right', yanchor='auto', xshift=0, yshift=0,
                font=list(size=10, color='gray'))
           ) %>%
         plotly::config(mathjax = 'cdn')

}

ShowPlotOfModel <- function(NameOfModel, NameOfColor, MAE) {
# We cannot have R function with signature like function(a = 1, b) since Python function requires that arguments without default values appear before arguments with default values.
    
  # Load required packages
  if (!requireNamespace('reticulate', quietly = TRUE)) 
    stop('ggplot2 is needed for this function to work. Install it via install.packages("reticulate")',
         call. = FALSE)
  if (!requireNamespace('ggplot2', quietly = TRUE)) 
    stop('ggplot2 is needed for this function to work. Install it via install.packages("ggplot2")',
         call. = FALSE)
  if (!requireNamespace('plotly', quietly = TRUE)) 
    stop('plotly is needed for this function to work. Install it via install.packages("plotly")',
         call. = FALSE)

  my_shapes = c(20, 15)
  
  preds <- 
    py$y_test %>%
      data.frame %>%
        rownames_to_column %>% 
          setNames(c('index', 'Badrate')) %>%
            dplyr::mutate( Period = py$Period[index]
                         , Badrate = (exp(py$y_pred) - 0.001) * 100 %>% round(2) 
                         , Legend = 'Predicted' ) %>%
              dplyr::bind_rows(yt %>% dplyr::mutate( Legend = 'Observed' )) %>% 
                dplyr::select(-index)

  errorbars <-
    dplyr::left_join( dplyr::filter(preds, Legend == 'Predicted'), dplyr:: filter(preds, Legend == 'Observed'),
                      by = c('Period' = 'Period') ) %>% 
      dplyr::mutate( lower = if_else(Badrate.x < Badrate.y, Badrate.x, Badrate.y)
                   , upper = if_else(Badrate.x < Badrate.y, Badrate.y, Badrate.x)
                   , Legend = 'Predicted') %>% 
        dplyr::select(Period, lower, upper, Legend)
  
  preds %<>% left_join(errorbars, by = c('Period' = 'Period', 'Legend' = 'Legend'))
  
  MAE = sprintf('MAE = %.2f p.p.', MAE)
  
  R2 = sprintf('R2 = %.2f', py$r2_score(py$y_test, (exp(py$y_pred)-0.001) ))

  # R2 = plotly::TeX( paste('\\displaystyle \\large R^2 =',
  #                         sprintf('%.2f', py$r2_score(py$y_test, (exp(py$y_pred)-0.001) ))) )
  
  p <- 
    ggplot(data = preds) +
      geom_line(data = filter(preds, Legend == 'Observed'), aes(x = Period, y = Badrate), color = 'dodgerblue') +
      geom_point(aes(x = Period, y = Badrate, colour = Legend, shape = Legend
                             , text = paste0('Period:  ', Period,
                                         '<br>Badrate(', Legend, '): ', sprintf('%.2f', Badrate))
                             ), size = 2) +
      geom_linerange(data = filter(preds, Legend == 'Predicted'), aes(x = Period, ymin = lower, ymax = upper)
                     , linetype = 'dashed', color = NameOfColor) +
      scale_colour_manual(values =  c('dodgerblue3', NameOfColor),
                          guide = guide_legend(override.aes = list(linetype = c('solid', 'blank'),
                                                                   shape = my_shapes))) +
      scale_shape_manual(values = my_shapes) +
      scale_x_datetime(date_labels = '%b-%y', date_breaks = '4 months') +
      annotate(geom = 'text', x = as.POSIXct('2013-01-01'), y = max(yt$Badrate) * 0.95, label = MAE, color = 'red') +
      annotate(geom = 'text', x = as.POSIXct('2013-01-01'), y = max(yt$Badrate) * 0.9, label = R2, color = 'red') +
      theme(axis.text.x = element_text(angle = 90, hjust = 1)) +
      labs( title = paste('Предсказание значений Badrate по модели', NameOfModel)
          # , subtitle = 'Graph is created by `ggplot2` & `plotly` packages of R'
          , x = 'Периоды наблюдений', y = 'Badrate, %'
          # , caption = 'Совместная работа языков программирования R и Python в программе RStudio 1.2.xxx'
          )

  plotly::ggplotly(p, width = 900, height = 500, tooltip = c('text') ) %>% 
    ShowCaptionsofChart(is_ggplot2 = TRUE)
  
}

Проведем разведочный анализ зависимой переменной y. Поскольку применение Python библиотеки matplotlib на бета-версии Rstudio 1.2.xxx пока встречает определенные трудности, да и сами ее графические возможности пока не столь изощрены, то мы будем обращаться для каждого графического представления к одной из самых мощных графических библиотек R ggplot2, которая входит в комплект tidyverse. При помощи R библиотеки интерактивной графике plotly (пакет написан на Java) можно получить самую детальную информацию на схеме.

## 
## Attaching package: 'plotly'
## The following object is masked from 'package:ggplot2':
## 
##     last_plot
## The following object is masked from 'package:stats':
## 
##     filter
## The following object is masked from 'package:graphics':
## 
##     layout
yt = data.frame(Period = py$Period                             # Преобразование к R типу 'POSIXt' / 'POSIXct'
              , Badrate = round(py$y * 100, 2) %>% as.vector   # Преобразование к R типу 'vector', содержащих 'double'
              )

# Time Series Plot of `Badrate`
p <-
  ggplot(data = yt, aes(x = Period, y = Badrate, colour = Badrate)) +
    geom_line(colour = 'grey60') +
    geom_point(shape = 15, size = 4) +
    scale_color_gradient(low = 'blueviolet', high ='brown1') +
    scale_x_datetime(date_labels = '%b-%y', date_breaks = '4 months') +
    geom_hline(aes(yintercept = mean(yt$Badrate)), color = 'blue') +                                  # Mean
    annotate(geom = 'text', x = as.POSIXct('2013-01-01'), y = mean(yt$Badrate) %>% ceiling,
             label = plotly::TeX(paste0('\\displaystyle \\large \\mu = ', round(mean(yt$Badrate), 2))),
             color = 'red') +
    stat_smooth( color = 'lightcoral', fill = 'lightcoral', method = 'loess' ) +
    theme(axis.text.x = element_text(angle = 90, hjust = 1)) +
    labs( title = 'Отношение суммы активов банков, которые оказались в дефолте
         к общей сумме активов банков второго уровня'
        , subtitle = 'Graph is created by `ggplot` & `plotly` packages of R'
        , x = 'Периоды наблюдений', y = 'Badrate, %'
        , caption = 'Совместная работа языков программирования R и Python в программе RStudio 1.2.xxx')

plotly::ggplotly(p, width = 900, height = 500, tooltip = c('x', 'y', 'color') ) %>%
  ShowCaptionsofChart(is_ggplot2 = TRUE)

3 Корреляционный анализ признаков и отбор предикторов

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

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

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

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

Переменные Описание
NT_CP 1. Чистые налоги в ВВП в текущий ценах, млн. тенге
GDP_CP 2. Валовой внутренний продукт в текущих ценах, млн. тенге
PoG_05 3. Производство товаров в среднегодовых ценах 2005 года, млн. тенге
PS_05 4. Производство услуг в среднегодовых ценах 2005 года, млн. тенге
NT_05 5. Чистые налоги в ВВП в среднегодовых ценах 2005 года, млн. тенге
GDP_05 6. Валовой внутренний продукт в среднегодовых ценах 2005 года, млн. тенге
PoG_CP_1YI 7. Индекс Производство товаров в среднегодовых ценах 2005 года
LoansOI 8. Кредиты БВУ физическим лицам, млн тенге
LnLoans 9. Натуральный логарифм от суммы Кредитов БВУ, млн тенге
IoT 10. Импорт товаров по платежному балансу, млн долл.

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

## X_train_short
##  <class 'pandas.core.frame.DataFrame'>
## (58, 10)
##                NT_CP        GDP_CP         PoG_05          PS_05  \
## count      58.000000  5.800000e+01      58.000000      58.000000   
## mean   248835.996552  3.619994e+06  413142.893434  590007.948498   
## std     65530.987161  1.148603e+06   50148.063739   72051.666761   
## min    133800.600000  2.032577e+06  353718.600000  481124.100000   
## 25%    212134.500000  2.688893e+06  381597.400000  551808.858333   
## 50%    236454.566667  3.519345e+06  395557.416667  574488.166667   
## 75%    272897.633333  4.356984e+06  441336.200000  622482.058333   
## max    424002.533333  6.339544e+06  567931.284175  766652.300000   
## 
##                NT_05        GDP_05  PoG_CP_1YI       LoansOI    LnLoans  \
## count      58.000000  5.800000e+01   58.000000  5.800000e+01  58.000000   
## mean    67169.929421  1.082419e+06  109.147753  3.818230e+06  16.256230   
## std     37085.410995  1.502854e+05    7.310526  7.825052e+05   0.124005   
## min      9704.266667  9.060918e+05   96.602738  2.341709e+06  15.989490   
## 25%     43321.066667  1.001092e+06  103.923487  3.389576e+06  16.181804   
## 50%     52113.750000  1.042549e+06  110.765305  4.002058e+06  16.314717   
## 75%    108129.633333  1.134661e+06  112.716098  4.182302e+06  16.354485   
## max    133142.313248  1.593780e+06  129.617739  5.302620e+06  16.397433   
## 
##                IoT  
## count    58.000000  
## mean   3281.731994  
## std     759.848043  
## min    1932.366465  
## 25%    2737.798260  
## 50%    3001.441088  
## 75%    3842.402013  
## max    4907.970945
## 
## X_test_short
##  <class 'pandas.core.frame.DataFrame'>
## (26, 10)

Затем отберем из контрольной выборки X_test набор применяемых предикторов в выборке X_test_short. В дальнейшем будем применять только сокращенное признаковое пространство этих предикторов.

4 Определение набора перспективных регрессионый моделей

Таковы основные характеристики оставшихся признаков на обучающей выборке. Оценку будем производить с помощью коэффициента детерминации (R2).

Отберем методы, на которых построим регрессионые модели. Они будут прелдставлять различные типы моделей:

При этом предсказывать будем не только напрямую значения Badrate - Y1, но также его натуральный логарифм ln(Badrate) - Y2, увеличенный на 0.001. Такой подход позволит,

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

Теперь попробуем настроить эти регрессионные модели с гипер-параметрами по умолчению оценивая их по R2 или средняя абсолютная ошибка - mae (от англ. Mean Absolute Error) на контрольной выборке.

## ElasticNet(alpha=1.0, copy_X=True, fit_intercept=True, l1_ratio=0.5,
##       max_iter=5000, normalize=False, positive=False, precompute=False,
##       random_state=17, selection='cyclic', tol=0.0001, warm_start=False)
## ElasticNet(alpha=1.0, copy_X=True, fit_intercept=True, l1_ratio=0.5,
##       max_iter=5000, normalize=False, positive=False, precompute=False,
##       random_state=17, selection='cyclic', tol=0.0001, warm_start=False)
## ARDRegression(alpha_1=1e-06, alpha_2=1e-06, compute_score=False, copy_X=True,
##        fit_intercept=True, lambda_1=1e-06, lambda_2=1e-06, n_iter=5000,
##        normalize=False, threshold_lambda=10000.0, tol=0.001, verbose=False)
## ARDRegression(alpha_1=1e-06, alpha_2=1e-06, compute_score=False, copy_X=True,
##        fit_intercept=True, lambda_1=1e-06, lambda_2=1e-06, n_iter=5000,
##        normalize=False, threshold_lambda=10000.0, tol=0.001, verbose=False)
## KNeighborsRegressor(algorithm='auto', leaf_size=30, metric='minkowski',
##           metric_params=None, n_jobs=1, n_neighbors=3, p=2,
##           weights='uniform')
## KNeighborsRegressor(algorithm='auto', leaf_size=30, metric='minkowski',
##           metric_params=None, n_jobs=1, n_neighbors=3, p=2,
##           weights='uniform')
## DecisionTreeRegressor(criterion='mse', max_depth=None, max_features=None,
##            max_leaf_nodes=None, min_impurity_split=1e-07,
##            min_samples_leaf=1, min_samples_split=2,
##            min_weight_fraction_leaf=0.0, presort=False, random_state=17,
##            splitter='best')
## DecisionTreeRegressor(criterion='mse', max_depth=None, max_features=None,
##            max_leaf_nodes=None, min_impurity_split=1e-07,
##            min_samples_leaf=1, min_samples_split=2,
##            min_weight_fraction_leaf=0.0, presort=False, random_state=17,
##            splitter='best')
## ExtraTreeRegressor(criterion='mae', max_depth=None, max_features='auto',
##           max_leaf_nodes=None, min_impurity_split=1e-07,
##           min_samples_leaf=1, min_samples_split=2,
##           min_weight_fraction_leaf=0.0, random_state=17, splitter='random')
## ExtraTreeRegressor(criterion='mae', max_depth=None, max_features='auto',
##           max_leaf_nodes=None, min_impurity_split=1e-07,
##           min_samples_leaf=1, min_samples_split=2,
##           min_weight_fraction_leaf=0.0, random_state=17, splitter='random')
## GradientBoostingRegressor(alpha=0.9, criterion='friedman_mse', init=None,
##              learning_rate=0.1, loss='ls', max_depth=3, max_features=None,
##              max_leaf_nodes=None, min_impurity_split=1e-07,
##              min_samples_leaf=1, min_samples_split=2,
##              min_weight_fraction_leaf=0.0, n_estimators=100,
##              presort='auto', random_state=17, subsample=1.0, verbose=0,
##              warm_start=False)
## GradientBoostingRegressor(alpha=0.9, criterion='friedman_mse', init=None,
##              learning_rate=0.1, loss='ls', max_depth=3, max_features=None,
##              max_leaf_nodes=None, min_impurity_split=1e-07,
##              min_samples_leaf=1, min_samples_split=2,
##              min_weight_fraction_leaf=0.0, n_estimators=100,
##              presort='auto', random_state=17, subsample=1.0, verbose=0,
##              warm_start=False)
## BaggingRegressor(base_estimator=DecisionTreeRegressor(criterion='mse', max_depth=None, max_features=None,
##            max_leaf_nodes=None, min_impurity_split=1e-07,
##            min_samples_leaf=1, min_samples_split=2,
##            min_weight_fraction_leaf=0.0, presort=False, random_state=None,
##            splitter='best'),
##          bootstrap=True, bootstrap_features=False, max_features=1.0,
##          max_samples=1.0, n_estimators=10, n_jobs=1, oob_score=False,
##          random_state=17, verbose=0, warm_start=False)
## BaggingRegressor(base_estimator=DecisionTreeRegressor(criterion='mse', max_depth=None, max_features=None,
##            max_leaf_nodes=None, min_impurity_split=1e-07,
##            min_samples_leaf=1, min_samples_split=2,
##            min_weight_fraction_leaf=0.0, presort=False, random_state=None,
##            splitter='best'),
##          bootstrap=True, bootstrap_features=False, max_features=1.0,
##          max_samples=1.0, n_estimators=10, n_jobs=1, oob_score=False,
##          random_state=17, verbose=0, warm_start=False)
## Validation on Testing Set 
##                                R2_Y1     R2_Y2
## Model                                        
## ElasticNet                 0.547305  0.656433
## ARDRegression              0.238050  0.370327
## KNeighborsRegressor        0.379940  0.353826
## DecisionTreeRegressor      0.484155  0.944444
## ExtraTreeRegressor         0.889875  0.869585
## GradientBoostingRegressor  0.821687  0.989186
## BaggingRegressor           0.714205  0.946951
## 
##  Mean R² Y1 = Badrate    : 0.5822 
##  Mean R² Y2 = ln(Badrate): 0.7330

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

## 
## Attaching package: 'latex2exp'
## The following object is masked from 'package:plotly':
## 
##     TeX

Наилучшие результаты по коэффициенту детерминации R2 на контрольной выборке оказались у среди линейных методов у ElasticNet, по “Деревьям решений” - ExtraTreeRegressor, а для ансамблей отличились оба - BaggingRegressor и GradientBoostingRegressor.

Поскольку значения R2 выше по Y2, то предсказывать будем не напрямую значения Badrate, а её превращенную форму - натуральный логарифм ln(Badrate) - Y2, увеличенный на 0.001.

Произведем настройки гипер-параметров у первой пары этих методов при помощи GridSearchCV.

5 Настройка гипер-параметров у модели ElasticNet

Построим наилучшую линейную модель - ElasticNet с регуляризацией L1 (LASSO) и L2 (Ridge или по акад. Тихонову). Для этого подберем сетку гипер-параметров α и ρ для этой разновидности регуляризации.

Рассмотрим значения R2 в пространстве гипер-параметров ρ - L1 и α - L2 при помощи R библиотеки интерактивной графике plotly.

На трехмерном графике видно, что максимальное значения R2, которое выступает в качестве score в этой модели едва достигает 0.7554, при этом значение α - 0.0001, а ρ - 0.7. Теперь настроим с полученными параметрами линейную регрессионную модель на зависимую переменную по обучающей выборке.

## Best score (R²) = 0.7554 
##  Best alpha      =  0.0001 
##  Best l1_ratio   =  0.7
## ElasticNet(alpha=0.0001, copy_X=True, fit_intercept=True, l1_ratio=0.7,
##       max_iter=5000, normalize=False, positive=False, precompute=False,
##       random_state=None, selection='cyclic', tol=0.0001, warm_start=False)

Спрогнозируем на контрольной выборке X_test_short по модели ElasticNet зависимую переменную Badrate.

## Parametric ElasticNet Model - 
##  Mean Absolute Error (MAE) on Testing Set (%): 1.2025 
##  R² on Testing Set Badrate through Ln(Badrate): 0.4373
## Coefficients for Y2 = ln(Badrate): 
##  [-3.49419814e-06  1.98213941e-07  9.90893136e-05  3.93057781e-05
##   8.28432558e-05 -4.20826802e-05 -8.81616015e-03 -3.79760332e-06
##   1.56679368e+01 -1.48363783e-03]

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

Посмотрим на интерактивном графике, созданном R библиотеками ggplot2 и plotly спрогнозированные значения по настроенной модели ElasticNet.

6 Настройка гипер-параметров у модели ExtraTreeRegressor

Следом обучим другую регрессионную модель - разновидность “деревьев решений” ExtraTreesRegressor. Используем параметр random_state=17 для воспроизводимости результатов.

## GridSearchCV(cv=KFold(n_splits=5, random_state=17, shuffle=True),
##        error_score='raise',
##        estimator=ExtraTreeRegressor(criterion='mae', max_depth=None, max_features='auto',
##           max_leaf_nodes=None, min_impurity_split=1e-07,
##           min_samples_leaf=1, min_samples_split=2,
##           min_weight_fraction_leaf=0.0, random_state=17, splitter='random'),
##        fit_params={}, iid=True, n_jobs=1,
##        param_grid={'max_depth': array([1, 2, 3, 4, 5, 6, 7, 8, 9]), 'max_features': array([1, 2, 3, 4, 5, 6, 7, 8, 9])},
##        pre_dispatch='2*n_jobs', refit=True, return_train_score=True,
##        scoring=None, verbose=0)
## {'max_depth': 9, 'max_features': 5}
## The best of score:  0.9325221116412763
## ExtraTreeRegressor(criterion='mae', max_depth=9, max_features=5,
##           max_leaf_nodes=None, min_impurity_split=1e-07,
##           min_samples_leaf=1, min_samples_split=2,
##           min_weight_fraction_leaf=0.0, random_state=17, splitter='random')

Рассмотрим детальнее значения R2 в пространстве двух настраиваемых гипер-параметров max_features и max_depth на трехмерном графике.

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

Теперь сделаем прогноз для контрольной выборки и оценим на ней качество модели ExtraTreeRegressor.

## Non-Parametric ExtraTree Model - 
##  Mean Absolute Error (MAE) on Testing Set (%): 0.4503 
##  R² on Testing Set Badrate through Ln(Badrate): 0.8694

Приведем визуальное отображение прогнозных значени, полученных методом ExtraTreeRegressor. Красными точками на графике отмечены прогнозы. К сожалению, не возможно провести границы доверительных интервалов, поскольку предсказания были получены непараметрическим методами.

Полученные в результате прогнозы лежат ближе к наблюдаемым точкам. Качество предсказания по модели “Дерева решений” ExtraTree заметно лучше, чем по линейной модели ElasticNet: MAE = 0.45 p.p. и
R2 = 0.87.

7 Обучение регрессионой модели BaggingRegressor

Теперь натренируем ансамблевый метод Баггинга “Деревья решений” на обучающей выборке с параметром random_state=17 для воспроизводимости результатов.

## BaggingRegressor(base_estimator=DecisionTreeRegressor(criterion='mse', max_depth=None, max_features=None,
##            max_leaf_nodes=None, min_impurity_split=1e-07,
##            min_samples_leaf=1, min_samples_split=2,
##            min_weight_fraction_leaf=0.0, presort=False, random_state=None,
##            splitter='best'),
##          bootstrap=True, bootstrap_features=False, max_features=5,
##          max_samples=1.0, n_estimators=10, n_jobs=1, oob_score=False,
##          random_state=17, verbose=0, warm_start=False)
## Non-Parametric BaggingRegressor Model - 
##  Mean Absolute Error (MAE) on Testing Set (%): 0.6453 
##  R² on Testing Set Badrate through Ln(Badrate): 0.7466

Мы получили методом BaggingRegressor на выбранных гипер-параметрах результат лучше, чем по по обобщенной линейной модели с регуляризация L1 и L2, но заметно хуже, чем по методу “деревьев решений” ExtraTreeRegressor. Посмотрим на интерактивном графике спрогнозированные значения по настроенной модели BaggingRegressor.

8 Обучение ансамбля GradientBoostingRegressor

Теперь обучим ансамблевый метод “Градиентного бустинга” на обучающей выборке с параметром random_state=17 для воспроизводимости результатов.

## GradientBoostingRegressor(alpha=0.9, criterion='friedman_mse', init=None,
##              learning_rate=0.1, loss='ls', max_depth=3, max_features=5,
##              max_leaf_nodes=None, min_impurity_split=1e-07,
##              min_samples_leaf=1, min_samples_split=3,
##              min_weight_fraction_leaf=0.0, n_estimators=100,
##              presort='auto', random_state=17, subsample=1.0, verbose=0,
##              warm_start=False)
## Non-Parametric GradientBoostingRegressor Model - 
##  Mean Absolute Error (MAE) on Testing Set (%): 0.4122 
##  R² on Testing Set Badrate through Ln(Badrate): 0.9024

Мы получили методом “Градиентного бустинга” на установленных гипер-параметрах результат выше, чем по по обобщенной линейной модели с регуляризация L1 и L2. Посмотрим на интерактивном графике спрогнозированные значения по настроенной модели GradientBoostingRegressor.

Полученный результат по модели GradientBoostingRegressor следует счесть наилучшим среди полученных в рамках Частотного подхода, так как у нее имеются наименьшая средняя абсолютная ошибка (англ. Mean Absolute Error), равная 0.41 p.p..

Из графика видно, что значительные по абсолютной величине ошибки (около 2 процентных пункта) приходятся на вторую половину 2016 г. Однако уже в начале 2017 г. таких крупных расхождений не наблюдается. В остальном прогнозные точки располагаются на графике вблизи фактических значений.

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

9 Заключение

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

## 
## INSTALLED VERSIONS
## ------------------
## commit: None
## python: 3.5.2.final.0
## python-bits: 64
## OS: Windows
## OS-release: 8.1
## machine: AMD64
## processor: Intel64 Family 6 Model 58 Stepping 9, GenuineIntel
## byteorder: little
## LC_ALL: None
## LANG: None
## LOCALE: None.None
## 
## pandas: 0.19.2
## nose: 1.3.7
## pip: 19.0.3
## setuptools: 27.2.0
## Cython: 0.25.2
## numpy: 1.14.5
## scipy: 0.19.0
## statsmodels: 0.8.0
## xarray: None
## IPython: 5.3.0
## sphinx: 1.5.4
## patsy: 0.4.1
## dateutil: 2.6.0
## pytz: 2017.2
## blosc: None
## bottleneck: 1.2.0
## tables: 3.2.2
## numexpr: 2.6.2
## matplotlib: 2.0.0
## openpyxl: 2.4.1
## xlrd: 1.0.0
## xlwt: 1.2.0
## xlsxwriter: 0.9.6
## lxml: 3.7.3
## bs4: 4.5.1
## html5lib: None
## httplib2: None
## apiclient: None
## sqlalchemy: 1.1.9
## pymysql: None
## psycopg2: None
## jinja2: 2.8
## boto: 2.42.0
## pandas_datareader: 0.2.1
## None

Наконец, посмотрим какие библиотеки применял R.

## - Session info ----------------------------------------------------------
##  setting  value                       
##  version  R version 3.5.3 (2019-03-11)
##  os       Windows 8.1 x64             
##  system   x86_64, mingw32             
##  ui       RTerm                       
##  language (EN)                        
##  collate  Russian_Russia.1251         
##  ctype    Russian_Russia.1251         
##  tz       Asia/Dhaka                  
##  date     2019-04-01                  
## 
## - Packages --------------------------------------------------------------
##  package     * version date       lib
##  assertthat    0.2.1   2019-03-21 [1]
##  backports     1.1.3   2018-12-14 [1]
##  broom         0.5.1   2018-12-05 [1]
##  callr         3.2.0   2019-03-15 [1]
##  cellranger    1.1.0   2016-07-27 [1]
##  cli           1.1.0   2019-03-19 [1]
##  colorspace    1.4-1   2019-03-18 [1]
##  crayon        1.3.4   2017-09-16 [1]
##  crosstalk     1.0.0   2016-12-21 [1]
##  data.table    1.12.3  2019-03-29 [1]
##  desc          1.2.0   2018-05-01 [1]
##  devtools      2.0.1   2018-10-26 [1]
##  digest        0.6.18  2018-10-10 [1]
##  dplyr       * 0.8.0.1 2019-02-15 [1]
##  evaluate      0.13    2019-02-12 [1]
##  forcats     * 0.4.0   2019-02-17 [1]
##  fs            1.2.7   2019-03-19 [1]
##  generics      0.0.2   2018-11-29 [1]
##  ggplot2     * 3.1.0   2018-10-25 [1]
##  glue          1.3.1   2019-03-12 [1]
##  gtable        0.3.0   2019-03-25 [1]
##  haven         2.1.0   2019-02-19 [1]
##  hms           0.4.2   2018-03-10 [1]
##  htmltools     0.3.6   2017-04-28 [1]
##  htmlwidgets   1.3     2018-09-30 [1]
##  httpuv        1.5.0   2019-03-15 [1]
##  httr          1.4.0   2018-12-11 [1]
##  jsonlite      1.6     2018-12-07 [1]
##  knitr         1.22    2019-03-08 [1]
##  labeling      0.3     2014-08-23 [1]
##  later         0.8.0   2019-02-11 [1]
##  latex2exp   * 0.4.0   2015-11-30 [1]
##  lattice       0.20-38 2018-11-04 [2]
##  lazyeval      0.2.2   2019-03-15 [1]
##  lubridate     1.7.4   2018-04-11 [1]
##  magrittr    * 1.5     2014-11-22 [1]
##  Matrix        1.2-15  2018-11-01 [2]
##  memoise       1.1.0   2017-04-21 [1]
##  mime          0.6     2018-10-05 [1]
##  modelr        0.1.4   2019-02-18 [1]
##  munsell       0.5.0   2018-06-12 [1]
##  nlme          3.1-137 2018-04-07 [2]
##  pillar        1.3.1   2018-12-15 [1]
##  pkgbuild      1.0.3   2019-03-20 [1]
##  pkgconfig     2.0.2   2018-08-16 [1]
##  pkgload       1.0.2   2018-10-29 [1]
##  plotly      * 4.8.0   2018-07-20 [1]
##  plyr          1.8.4   2016-06-08 [1]
##  prettyunits   1.0.2   2015-07-13 [1]
##  processx      3.3.0   2019-03-10 [1]
##  promises      1.0.1   2018-04-13 [1]
##  ps            1.3.0   2018-12-21 [1]
##  purrr       * 0.3.2   2019-03-15 [1]
##  R6            2.4.0   2019-02-14 [1]
##  Rcpp          1.0.1   2019-03-17 [1]
##  readr       * 1.3.1   2018-12-21 [1]
##  readxl        1.3.1   2019-03-13 [1]
##  remotes       2.0.2   2018-10-30 [1]
##  reshape2      1.4.3   2017-12-11 [1]
##  reticulate  * 1.11.1  2019-03-06 [1]
##  rlang         0.3.2   2019-03-21 [1]
##  rmarkdown     1.12    2019-03-14 [1]
##  rprojroot     1.3-2   2018-01-03 [1]
##  rstudioapi    0.10    2019-03-19 [1]
##  rvest         0.3.2   2016-06-17 [1]
##  scales        1.0.0   2018-08-09 [1]
##  sessioninfo   1.1.1   2018-11-05 [1]
##  shiny         1.2.0   2018-11-02 [1]
##  stringi       1.4.3   2019-03-12 [1]
##  stringr     * 1.4.0   2019-02-10 [1]
##  tibble      * 2.1.1   2019-03-16 [1]
##  tidyr       * 0.8.3   2019-03-01 [1]
##  tidyselect    0.2.5   2018-10-11 [1]
##  tidyverse   * 1.2.1   2017-11-14 [1]
##  usethis       1.4.0   2018-08-14 [1]
##  viridisLite   0.3.0   2018-02-01 [1]
##  withr         2.1.2   2018-03-15 [1]
##  xfun          0.5     2019-02-20 [1]
##  xml2          1.2.0   2018-01-24 [1]
##  xtable        1.8-3   2018-08-29 [1]
##  yaml          2.2.0   2018-07-25 [1]
##  source                                
##  CRAN (R 3.5.3)                        
##  CRAN (R 3.5.2)                        
##  CRAN (R 3.5.2)                        
##  CRAN (R 3.5.3)                        
##  CRAN (R 3.5.1)                        
##  CRAN (R 3.5.3)                        
##  CRAN (R 3.5.3)                        
##  CRAN (R 3.5.1)                        
##  CRAN (R 3.5.1)                        
##  Github (Rdatatable/data.table@8603485)
##  CRAN (R 3.5.1)                        
##  CRAN (R 3.5.2)                        
##  CRAN (R 3.5.1)                        
##  CRAN (R 3.5.2)                        
##  CRAN (R 3.5.2)                        
##  CRAN (R 3.5.2)                        
##  CRAN (R 3.5.3)                        
##  CRAN (R 3.5.1)                        
##  CRAN (R 3.5.1)                        
##  CRAN (R 3.5.3)                        
##  CRAN (R 3.5.3)                        
##  CRAN (R 3.5.2)                        
##  CRAN (R 3.5.1)                        
##  CRAN (R 3.5.1)                        
##  CRAN (R 3.5.1)                        
##  CRAN (R 3.5.3)                        
##  CRAN (R 3.5.2)                        
##  CRAN (R 3.5.2)                        
##  CRAN (R 3.5.3)                        
##  CRAN (R 3.5.0)                        
##  CRAN (R 3.5.2)                        
##  CRAN (R 3.5.2)                        
##  CRAN (R 3.5.3)                        
##  CRAN (R 3.5.3)                        
##  CRAN (R 3.5.1)                        
##  CRAN (R 3.5.1)                        
##  CRAN (R 3.5.3)                        
##  CRAN (R 3.5.1)                        
##  CRAN (R 3.5.1)                        
##  CRAN (R 3.5.2)                        
##  CRAN (R 3.5.1)                        
##  CRAN (R 3.5.3)                        
##  CRAN (R 3.5.2)                        
##  CRAN (R 3.5.3)                        
##  CRAN (R 3.5.1)                        
##  CRAN (R 3.5.1)                        
##  CRAN (R 3.5.2)                        
##  CRAN (R 3.5.1)                        
##  CRAN (R 3.5.1)                        
##  CRAN (R 3.5.3)                        
##  CRAN (R 3.5.1)                        
##  CRAN (R 3.5.2)                        
##  CRAN (R 3.5.3)                        
##  CRAN (R 3.5.2)                        
##  CRAN (R 3.5.3)                        
##  CRAN (R 3.5.2)                        
##  CRAN (R 3.5.3)                        
##  CRAN (R 3.5.1)                        
##  CRAN (R 3.5.1)                        
##  CRAN (R 3.5.3)                        
##  CRAN (R 3.5.3)                        
##  CRAN (R 3.5.3)                        
##  CRAN (R 3.5.1)                        
##  CRAN (R 3.5.3)                        
##  CRAN (R 3.5.1)                        
##  CRAN (R 3.5.1)                        
##  CRAN (R 3.5.1)                        
##  CRAN (R 3.5.1)                        
##  CRAN (R 3.5.3)                        
##  CRAN (R 3.5.2)                        
##  CRAN (R 3.5.3)                        
##  CRAN (R 3.5.3)                        
##  CRAN (R 3.5.1)                        
##  CRAN (R 3.5.3)                        
##  CRAN (R 3.5.1)                        
##  CRAN (R 3.5.1)                        
##  CRAN (R 3.5.1)                        
##  CRAN (R 3.5.2)                        
##  CRAN (R 3.5.1)                        
##  CRAN (R 3.5.1)                        
##  CRAN (R 3.5.1)                        
## 
## [1] C:/Users/ARodionov/Documents/R/win-library/3.5
## [2] C:/Program Files/R/R-3.5.3/library
## R version 3.5.3 (2019-03-11)
## Platform: x86_64-w64-mingw32/x64 (64-bit)
## Running under: Windows 8.1 x64 (build 9600)
## 
## Matrix products: default
## 
## locale:
## [1] LC_COLLATE=Russian_Russia.1251  LC_CTYPE=Russian_Russia.1251   
## [3] LC_MONETARY=Russian_Russia.1251 LC_NUMERIC=C                   
## [5] LC_TIME=Russian_Russia.1251    
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
##  [1] latex2exp_0.4.0   plotly_4.8.0      reticulate_1.11.1
##  [4] magrittr_1.5      forcats_0.4.0     stringr_1.4.0    
##  [7] dplyr_0.8.0.1     purrr_0.3.2       readr_1.3.1      
## [10] tidyr_0.8.3       tibble_2.1.1      ggplot2_3.1.0    
## [13] tidyverse_1.2.1  
## 
## loaded via a namespace (and not attached):
##  [1] httr_1.4.0        pkgload_1.0.2     jsonlite_1.6     
##  [4] viridisLite_0.3.0 modelr_0.1.4      shiny_1.2.0      
##  [7] assertthat_0.2.1  cellranger_1.1.0  remotes_2.0.2    
## [10] yaml_2.2.0        sessioninfo_1.1.1 pillar_1.3.1     
## [13] backports_1.1.3   lattice_0.20-38   glue_1.3.1       
## [16] digest_0.6.18     promises_1.0.1    rvest_0.3.2      
## [19] colorspace_1.4-1  htmltools_0.3.6   httpuv_1.5.0     
## [22] Matrix_1.2-15     plyr_1.8.4        pkgconfig_2.0.2  
## [25] devtools_2.0.1    broom_0.5.1       haven_2.1.0      
## [28] xtable_1.8-3      scales_1.0.0      processx_3.3.0   
## [31] later_0.8.0       generics_0.0.2    usethis_1.4.0    
## [34] withr_2.1.2       lazyeval_0.2.2    cli_1.1.0        
## [37] crayon_1.3.4      readxl_1.3.1      mime_0.6         
## [40] memoise_1.1.0     evaluate_0.13     ps_1.3.0         
## [43] fs_1.2.7          nlme_3.1-137      xml2_1.2.0       
## [46] pkgbuild_1.0.3    tools_3.5.3       data.table_1.12.3
## [49] prettyunits_1.0.2 hms_0.4.2         munsell_0.5.0    
## [52] callr_3.2.0       compiler_3.5.3    rlang_0.3.2      
## [55] grid_3.5.3        rstudioapi_0.10   htmlwidgets_1.3  
## [58] crosstalk_1.0.0   labeling_0.3      rmarkdown_1.12   
## [61] gtable_0.3.0      reshape2_1.4.3    R6_2.4.0         
## [64] lubridate_1.7.4   knitr_1.22        rprojroot_1.3-2  
## [67] desc_1.2.0        stringi_1.4.3     Rcpp_1.0.1       
## [70] tidyselect_0.2.5  xfun_0.5