Практическая работа №6

8. Дискриминантный анализ

Глушков Егор Александрович, гр. 20.М04-мм



Построение модели с помощью встроенной функции lda

library(readxl)
addicts <- read_excel("addicts.xlsx")
# View(addicts)

Исследуем переменные на наличие пропусков. Выделим нужные переменные. В качестве категориальной переменной возьмем curwor – занятость.

X <- data.frame(na.omit(addicts[ , c("sstati", "asi1_med", "asi2_emp", "asid3_dyr", "asi7_psy", "curwor")]))

summary(X)
     sstati         asi1_med         asi2_emp        asid3_dyr         asi7_psy        curwor      
 Min.   :23.00   Min.   :0.0000   Min.   :0.0000   Min.   : 0.500   Min.   :0.00   Min.   :0.0000  
 1st Qu.:43.00   1st Qu.:0.0000   1st Qu.:0.6900   1st Qu.: 2.000   1st Qu.:0.12   1st Qu.:0.0000  
 Median :48.00   Median :0.1900   Median :0.9100   Median : 3.000   Median :0.24   Median :0.0000  
 Mean   :48.52   Mean   :0.2447   Mean   :0.7834   Mean   : 3.605   Mean   :0.26   Mean   :0.2708  
 3rd Qu.:54.00   3rd Qu.:0.4200   3rd Qu.:1.0000   3rd Qu.: 4.000   3rd Qu.:0.36   3rd Qu.:1.0000  
 Max.   :72.00   Max.   :1.0000   Max.   :1.0000   Max.   :24.000   Max.   :0.87   Max.   :1.0000  
summary(as.factor(X$curwor))
  0   1 
202  75 

Применим функцию Линейного Дискриминантного анализа lda с curwor в качестве группирующей переменной

library(MASS)

LDA <- lda(X$curwor ~ ., subset(X, select=-c(curwor))); LDA
Call:
lda(X$curwor ~ ., data = subset(X, select = -c(curwor)))

Prior probabilities of groups:
        0         1 
0.7292419 0.2707581 

Group means:
    sstati  asi1_med  asi2_emp asid3_dyr  asi7_psy
0 48.79703 0.2438119 0.8666337  3.784653 0.2798317
1 47.77333 0.2470667 0.5590667  3.120000 0.2066667

Coefficients of linear discriminants:
                   LD1
sstati    -0.007783931
asi1_med   0.102334038
asi2_emp  -4.073131357
asid3_dyr -0.060416539
asi7_psy  -0.874984879

Веса дискриминантной функции:

alpha.LDA <- LDA$scaling; alpha.LDA
                   LD1
sstati    -0.007783931
asi1_med   0.102334038
asi2_emp  -4.073131357
asid3_dyr -0.060416539
asi7_psy  -0.874984879

Матрица классификации (confusion matrix):

pred <- predict(LDA, X)
tab <- table(real=X$curwor, prediction=pred$class); tab
    prediction
real   0   1
   0 176  26
   1  45  30

Точность классификации (accuracy):

acc <- (tab[1] + tab[4]) / sum(tab); acc
[1] 0.7436823

Заметим, что точность вообще говоря низка: baseline-классификатор с одним ответом о принадлежности к бОльшему классу (тут это 0) для абсолютно всех измерений дал бы 0.7292419.

Соотнесём значения дискриминантной функции, предсказанный класс (т.е. значение curwor – 0 или 1), реальный класс (значение curwor в действительности):

LDA.val <- cbind(pred$x, pred.class=as.numeric(pred$class) - 1, real.class=X$curwor); LDA.val[seq(20), ]
           LD1 pred.class real.class
1   0.33361953          0          1
2   2.64724414          1          1
3  -0.93850975          0          0
4  -0.03465237          0          1
5  -0.29307298          0          0
6   0.86599898          0          0
7  -1.05175811          0          0
8   2.14223046          1          1
9  -0.78318198          0          0
10 -1.21733317          0          0
11 -0.87299796          0          0
12  2.38028413          1          1
13  0.59466102          0          0
14 -0.93944647          0          0
15  0.27721632          0          0
16 -0.85217639          0          0
17 -1.70168728          0          0
18 -0.85045500          0          0
19  2.40385969          1          1
20 -1.05488260          0          0

Построение дискриминантной функции “своими руками”

Ввиду сложности “достать” граничное значение с из встроенной функции lda, самостоятельно реализуем модель линейного дискриминантного анализа для двух классов.
Выборочная ковариационная матрица: \[\Sigma = \frac{1}{n_1 + n_2 - 2} ((n_1 - 1)\Sigma_1 + (n_2 - 1)\Sigma_2)\] где \(\Sigma_1\) и \(\Sigma_2\) – ковариационные матрицы каждой популяции (значения curwor).

Веса дискриминантной функции находятся путём решения СЛАУ \(\Sigma \alpha = \mu_1 - \mu_2\) \[z = \alpha^TX, \quad z_i = \alpha^T \mu_i, \quad i=1,2.\]

W1 <- subset(X, curwor == 0, select=-c(curwor))
W2 <- subset(X, curwor == 1, select=-c(curwor))

n1 <- nrow(W1)
n2 <- nrow(W2)
n <- n1 + n2

mu1 <- colMeans(W1)
mu2 <- colMeans(W2)

S1 <- cov(W1)
S2 <- cov(W2)

# S1 <- 0
# for (i in seq(n1)) {
#   x <- as.matrix(W1[i, ] - mu1)
#   S1 <- S1 + t(x) %*% x
# }
# S1 <- S1 / (n1 - 1)

S <- ((n1 - 1) * S1 + (n2 - 1) * S2) / (n - 2)
# S %*% alpha = mu1 - mu2
alpha <- solve(S, mu1 - mu2); alpha
     sstati    asi1_med    asi2_emp   asid3_dyr    asi7_psy 
 0.01062691 -0.13971017  5.56078793  0.08248287  1.19456136 

Граничное значение \[c = \frac{z_1 + z_2}{2} + \ln {\frac{q_2}{q_1}}\] где \(q_1\) и \(q_2\) – приорные вероятности: \(q_i = \frac{n_i}{n}, \quad i = 1, 2\)

z1 <- t(alpha) %*% mu1
z2 <- t(alpha) %*% mu2

sigma2 <- t(alpha) %*% S %*% alpha

q1 <- n1 / n
q2 <- n2 / n

c <- as.numeric((z1 + z2) / 2 + log(q2 / q1)); c
[1] 4.027395

Проверим модель на исходных данных. Матрица классификации (confusion matrix):

X.red <- subset(X, select=-c(curwor))
mypred <- ifelse(as.matrix(X.red) %*% alpha >= c, 0, 1)
tb <- table(pred=mypred, real=X$curwor); tb
    real
pred   0   1
   0 176  45
   1  26  30

Точность классификации (accuracy)

acc <- (tb[1] + tb[4]) / sum(tb); acc
[1] 0.7436823

Заметим, что “самодельный” LDA сделал прогноз, идентичный прогнозу встроенной функции LDA, несмотря на разные веса alpha.

Проверим идентичность меток классов:

sum(mypred - (as.numeric(pred$class) - 1))
[1] 0

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

Проиллюстрируем это, выведя метки классов “самописного” lda, метки встроенного lda, а также настоящие метки [первые два столбца в точности совпадают].

cbind(handmade.pred=as.numeric(mypred), LDA.pred=as.numeric(pred$class) - 1, real.labels=X$curwor)[seq(30),]
      handmade.pred LDA.pred real.labels
 [1,]             0        0           1
 [2,]             1        1           1
 [3,]             0        0           0
 [4,]             0        0           1
 [5,]             0        0           0
 [6,]             0        0           0
 [7,]             0        0           0
 [8,]             1        1           1
 [9,]             0        0           0
[10,]             0        0           0
[11,]             0        0           0
[12,]             1        1           1
[13,]             0        0           0
[14,]             0        0           0
[15,]             0        0           0
[16,]             0        0           0
[17,]             0        0           0
[18,]             0        0           0
[19,]             1        1           1
[20,]             0        0           0
[21,]             0        0           0
[22,]             0        0           0
[23,]             0        0           0
[24,]             0        0           1
[25,]             0        0           0
[26,]             0        0           0
[27,]             1        1           0
[28,]             0        0           0
[29,]             1        1           0
[30,]             0        0           0
LS0tDQp0aXRsZTogIiINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCiMjIyMg0J/RgNCw0LrRgtC40YfQtdGB0LrQsNGPINGA0LDQsdC+0YLQsCDihJY2DQojIyMgOC4g0JTQuNGB0LrRgNC40LzQuNC90LDQvdGC0L3Ri9C5INCw0L3QsNC70LjQtw0KDQo+INCT0LvRg9GI0LrQvtCyINCV0LPQvtGAINCQ0LvQtdC60YHQsNC90LTRgNC+0LLQuNGHLCDQs9GALiAyMC7QnDA0LdC80LwgIA0KDQotLS0NCg0KLSDQn9C+0YHRgtGA0L7QuNGC0Ywg0LTQuNGB0LrRgNC40LzQuNC90LDQvdGC0L3Rg9GOINGE0YPQvdC60YbQuNGOLg0KDQotINCe0YbQtdC90LjRgtGMINCz0YDQsNC90LjRh9C90L7QtSDQt9C90LDRh9C10L3QuNC1Lg0KDQotINCf0L7RgdGC0YDQvtC40YLRjCDQvNCw0YLRgNC40YbRgyDQutC70LDRgdGB0LjRhNC40LrQsNGG0LjQuA0KDQotINCS0YvRh9C40YHQu9C40YLRjCDRgtC+0YfQvdC+0YHRgtGMINC60LvQsNGB0YHQuNGE0LjQutCw0YbQuNC4Lg0KDQotLS0NCg0KPiDQn9C+0YHRgtGA0L7QtdC90LjQtSDQvNC+0LTQtdC70Lgg0YEg0L/QvtC80L7RidGM0Y4g0LLRgdGC0YDQvtC10L3QvdC+0Lkg0YTRg9C90LrRhtC40LggKmxkYSoNCg0KYGBge3J9DQpsaWJyYXJ5KHJlYWR4bCkNCmFkZGljdHMgPC0gcmVhZF9leGNlbCgiYWRkaWN0cy54bHN4IikNCiMgVmlldyhhZGRpY3RzKQ0KYGBgDQoNCtCY0YHRgdC70LXQtNGD0LXQvCDQv9C10YDQtdC80LXQvdC90YvQtSDQvdCwINC90LDQu9C40YfQuNC1INC/0YDQvtC/0YPRgdC60L7Qsi4g0JLRi9C00LXQu9C40Lwg0L3Rg9C20L3Ri9C1INC/0LXRgNC10LzQtdC90L3Ri9C1LiDQkiDQutCw0YfQtdGB0YLQstC1INC60LDRgtC10LPQvtGA0LjQsNC70YzQvdC+0Lkg0L/QtdGA0LXQvNC10L3QvdC+0Lkg0LLQvtC30YzQvNC10LwgKmN1cndvciogLS0g0LfQsNC90Y/RgtC+0YHRgtGMLg0KYGBge3J9DQpYIDwtIGRhdGEuZnJhbWUobmEub21pdChhZGRpY3RzWyAsIGMoInNzdGF0aSIsICJhc2kxX21lZCIsICJhc2kyX2VtcCIsICJhc2lkM19keXIiLCAiYXNpN19wc3kiLCAiY3Vyd29yIildKSkNCg0Kc3VtbWFyeShYKQ0Kc3VtbWFyeShhcy5mYWN0b3IoWCRjdXJ3b3IpKQ0KYGBgDQrQn9GA0LjQvNC10L3QuNC8INGE0YPQvdC60YbQuNGOINCb0LjQvdC10LnQvdC+0LPQviDQlNC40YHQutGA0LjQvNC40L3QsNC90YLQvdC+0LPQviDQsNC90LDQu9C40LfQsCAqbGRhKiDRgSBjdXJ3b3Ig0LIg0LrQsNGH0LXRgdGC0LLQtSDQs9GA0YPQv9C/0LjRgNGD0Y7RidC10Lkg0L/QtdGA0LXQvNC10L3QvdC+0LkNCmBgYHtyfQ0KbGlicmFyeShNQVNTKQ0KDQpMREEgPC0gbGRhKFgkY3Vyd29yIH4gLiwgc3Vic2V0KFgsIHNlbGVjdD0tYyhjdXJ3b3IpKSk7IExEQQ0KYGBgDQoNCtCS0LXRgdCwINC00LjRgdC60YDQuNC80LjQvdCw0L3RgtC90L7QuSDRhNGD0L3QutGG0LjQuDoNCmBgYHtyfQ0KYWxwaGEuTERBIDwtIExEQSRzY2FsaW5nOyBhbHBoYS5MREENCmBgYA0KDQrQnNCw0YLRgNC40YbQsCDQutC70LDRgdGB0LjRhNC40LrQsNGG0LjQuCAoKmNvbmZ1c2lvbiBtYXRyaXgqKToNCmBgYHtyfQ0KcHJlZCA8LSBwcmVkaWN0KExEQSwgWCkNCnRhYiA8LSB0YWJsZShyZWFsPVgkY3Vyd29yLCBwcmVkaWN0aW9uPXByZWQkY2xhc3MpOyB0YWINCmBgYA0K0KLQvtGH0L3QvtGB0YLRjCDQutC70LDRgdGB0LjRhNC40LrQsNGG0LjQuCAoKmFjY3VyYWN5Kik6DQpgYGB7cn0NCmFjYyA8LSAodGFiWzFdICsgdGFiWzRdKSAvIHN1bSh0YWIpOyBhY2MNCmBgYA0K0JfQsNC80LXRgtC40LwsINGH0YLQviDRgtC+0YfQvdC+0YHRgtGMINCy0L7QvtCx0YnQtSDQs9C+0LLQvtGA0Y8g0L3QuNC30LrQsDogYmFzZWxpbmUt0LrQu9Cw0YHRgdC40YTQuNC60LDRgtC+0YAg0YEg0L7QtNC90LjQvCDQvtGC0LLQtdGC0L7QvCDQviDQv9GA0LjQvdCw0LTQu9C10LbQvdC+0YHRgtC4INC6INCx0J7Qu9GM0YjQtdC80YMg0LrQu9Cw0YHRgdGDICjRgtGD0YIg0Y3RgtC+IDApINC00LvRjyDQsNCx0YHQvtC70Y7RgtC90L4g0LLRgdC10YUg0LjQt9C80LXRgNC10L3QuNC5INC00LDQuyDQsdGLIDAuNzI5MjQxOS4NCg0KDQrQodC+0L7RgtC90LXRgdGR0Lwg0LfQvdCw0YfQtdC90LjRjyDQtNC40YHQutGA0LjQvNC40L3QsNC90YLQvdC+0Lkg0YTRg9C90LrRhtC40LgsINC/0YDQtdC00YHQutCw0LfQsNC90L3Ri9C5INC60LvQsNGB0YEgKNGCLtC1LiDQt9C90LDRh9C10L3QuNC1IGN1cndvciAtLSAwINC40LvQuCAxKSwg0YDQtdCw0LvRjNC90YvQuSDQutC70LDRgdGBICjQt9C90LDRh9C10L3QuNC1IGN1cndvciDQsiDQtNC10LnRgdGC0LLQuNGC0LXQu9GM0L3QvtGB0YLQuCk6DQpgYGB7cn0NCkxEQS52YWwgPC0gY2JpbmQocHJlZCR4LCBwcmVkLmNsYXNzPWFzLm51bWVyaWMocHJlZCRjbGFzcykgLSAxLCByZWFsLmNsYXNzPVgkY3Vyd29yKTsgTERBLnZhbFtzZXEoMjApLCBdDQpgYGANCg0KPiDQn9C+0YHRgtGA0L7QtdC90LjQtSDQtNC40YHQutGA0LjQvNC40L3QsNC90YLQvdC+0Lkg0YTRg9C90LrRhtC40LggItGB0LLQvtC40LzQuCDRgNGD0LrQsNC80LgiDQoNCtCS0LLQuNC00YMg0YHQu9C+0LbQvdC+0YHRgtC4ICLQtNC+0YHRgtCw0YLRjCIg0LPRgNCw0L3QuNGH0L3QvtC1INC30L3QsNGH0LXQvdC40LUgKtGBKiDQuNC3INCy0YHRgtGA0L7QtdC90L3QvtC5INGE0YPQvdC60YbQuNC4ICpsZGEqLCDRgdCw0LzQvtGB0YLQvtGP0YLQtdC70YzQvdC+INGA0LXQsNC70LjQt9GD0LXQvCDQvNC+0LTQtdC70Ywg0LvQuNC90LXQudC90L7Qs9C+INC00LjRgdC60YDQuNC80LjQvdCw0L3RgtC90L7Qs9C+INCw0L3QsNC70LjQt9CwINC00LvRjyDQtNCy0YPRhSDQutC70LDRgdGB0L7Qsi4gIA0K0JLRi9Cx0L7RgNC+0YfQvdCw0Y8g0LrQvtCy0LDRgNC40LDRhtC40L7QvdC90LDRjyDQvNCw0YLRgNC40YbQsDoNCiQkXFNpZ21hID0gXGZyYWN7MX17bl8xICsgbl8yIC0gMn0gKChuXzEgLSAxKVxTaWdtYV8xICsgKG5fMiAtIDEpXFNpZ21hXzIpJCQNCtCz0LTQtSAkXFNpZ21hXzEkINC4ICRcU2lnbWFfMiQgLS0g0LrQvtCy0LDRgNC40LDRhtC40L7QvdC90YvQtSDQvNCw0YLRgNC40YbRiyDQutCw0LbQtNC+0Lkg0L/QvtC/0YPQu9GP0YbQuNC4ICjQt9C90LDRh9C10L3QuNGPIGN1cndvcikuDQoNCtCS0LXRgdCwINC00LjRgdC60YDQuNC80LjQvdCw0L3RgtC90L7QuSDRhNGD0L3QutGG0LjQuCDQvdCw0YXQvtC00Y/RgtGB0Y8g0L/Rg9GC0ZHQvCDRgNC10YjQtdC90LjRjyDQodCb0JDQoyAkXFNpZ21hIFxhbHBoYSA9IFxtdV8xIC0gXG11XzIkDQokJHogPSBcYWxwaGFeVFgsIFxxdWFkIHpfaSA9IFxhbHBoYV5UIFxtdV9pLCBccXVhZCBpPTEsMi4kJA0KDQpgYGB7cn0NClcxIDwtIHN1YnNldChYLCBjdXJ3b3IgPT0gMCwgc2VsZWN0PS1jKGN1cndvcikpDQpXMiA8LSBzdWJzZXQoWCwgY3Vyd29yID09IDEsIHNlbGVjdD0tYyhjdXJ3b3IpKQ0KDQpuMSA8LSBucm93KFcxKQ0KbjIgPC0gbnJvdyhXMikNCm4gPC0gbjEgKyBuMg0KDQptdTEgPC0gY29sTWVhbnMoVzEpDQptdTIgPC0gY29sTWVhbnMoVzIpDQoNClMxIDwtIGNvdihXMSkNClMyIDwtIGNvdihXMikNCg0KIyBTMSA8LSAwDQojIGZvciAoaSBpbiBzZXEobjEpKSB7DQojICAgeCA8LSBhcy5tYXRyaXgoVzFbaSwgXSAtIG11MSkNCiMgICBTMSA8LSBTMSArIHQoeCkgJSolIHgNCiMgfQ0KIyBTMSA8LSBTMSAvIChuMSAtIDEpDQoNClMgPC0gKChuMSAtIDEpICogUzEgKyAobjIgLSAxKSAqIFMyKSAvIChuIC0gMikNCiMgUyAlKiUgYWxwaGEgPSBtdTEgLSBtdTINCmFscGhhIDwtIHNvbHZlKFMsIG11MSAtIG11Mik7IGFscGhhDQpgYGANCg0KKirQk9GA0LDQvdC40YfQvdC+0LUg0LfQvdCw0YfQtdC90LjQtSoqICQkYyA9IFxmcmFje3pfMSArIHpfMn17Mn0gKyBcbG4ge1xmcmFje3FfMn17cV8xfX0kJA0K0LPQtNC1ICRxXzEkINC4ICRxXzIkIC0tINC/0YDQuNC+0YDQvdGL0LUg0LLQtdGA0L7Rj9GC0L3QvtGB0YLQuDogJHFfaSA9IFxmcmFje25faX17bn0sIFxxdWFkIGkgPSAxLCAyJA0KYGBge3J9DQp6MSA8LSB0KGFscGhhKSAlKiUgbXUxDQp6MiA8LSB0KGFscGhhKSAlKiUgbXUyDQoNCnNpZ21hMiA8LSB0KGFscGhhKSAlKiUgUyAlKiUgYWxwaGENCg0KcTEgPC0gbjEgLyBuDQpxMiA8LSBuMiAvIG4NCg0KYyA8LSBhcy5udW1lcmljKCh6MSArIHoyKSAvIDIgKyBsb2cocTIgLyBxMSkpOyBjDQpgYGANCg0K0J/RgNC+0LLQtdGA0LjQvCDQvNC+0LTQtdC70Ywg0L3QsCDQuNGB0YXQvtC00L3Ri9GFINC00LDQvdC90YvRhS4g0JzQsNGC0YDQuNGG0LAg0LrQu9Cw0YHRgdC40YTQuNC60LDRhtC40LggKCpjb25mdXNpb24gbWF0cml4Kik6DQpgYGB7cn0NClgucmVkIDwtIHN1YnNldChYLCBzZWxlY3Q9LWMoY3Vyd29yKSkNCm15cHJlZCA8LSBpZmVsc2UoYXMubWF0cml4KFgucmVkKSAlKiUgYWxwaGEgPj0gYywgMCwgMSkNCnRiIDwtIHRhYmxlKHByZWQ9bXlwcmVkLCByZWFsPVgkY3Vyd29yKTsgdGINCmBgYA0KDQrQotC+0YfQvdC+0YHRgtGMINC60LvQsNGB0YHQuNGE0LjQutCw0YbQuNC4ICgqYWNjdXJhY3kqKQ0KYGBge3J9DQphY2MgPC0gKHRiWzFdICsgdGJbNF0pIC8gc3VtKHRiKTsgYWNjDQpgYGANCtCX0LDQvNC10YLQuNC8LCDRh9GC0L4gItGB0LDQvNC+0LTQtdC70YzQvdGL0LkiICpMREEqINGB0LTQtdC70LDQuyDQv9GA0L7Qs9C90L7Qtywg0LjQtNC10L3RgtC40YfQvdGL0Lkg0L/RgNC+0LPQvdC+0LfRgyDQstGB0YLRgNC+0LXQvdC90L7QuSDRhNGD0L3QutGG0LjQuCAqTERBKiwg0L3QtdGB0LzQvtGC0YDRjyDQvdCwINGA0LDQt9C90YvQtSDQstC10YHQsCAqYWxwaGEqLg0KDQrQn9GA0L7QstC10YDQuNC8INC40LTQtdC90YLQuNGH0L3QvtGB0YLRjCDQvNC10YLQvtC6INC60LvQsNGB0YHQvtCyOg0KYGBge3J9DQpzdW0obXlwcmVkIC0gKGFzLm51bWVyaWMocHJlZCRjbGFzcykgLSAxKSkNCmBgYA0K0KHRg9C80LzQsCDQv9C+0Y3Qu9C10LzQtdC90YLQvdC+0LPQviDQstGL0YfQuNGC0LDQvdC40Y8g0LzQtdGC0L7QuiDRgNCw0LLQvdCwIDAsINGC0L4g0LXRgdGC0Ywg0LzQtdGC0LrQuCDQuNC00LXQvdGC0LjRh9C90YsuDQoNCtCf0YDQvtC40LvQu9GO0YHRgtGA0LjRgNGD0LXQvCDRjdGC0L4sINCy0YvQstC10LTRjyDQvNC10YLQutC4INC60LvQsNGB0YHQvtCyICLRgdCw0LzQvtC/0LjRgdC90L7Qs9C+IiAqbGRhKiwg0LzQtdGC0LrQuCDQstGB0YLRgNC+0LXQvdC90L7Qs9C+ICpsZGEqLCDQsCDRgtCw0LrQttC1INC90LDRgdGC0L7Rj9GJ0LjQtSDQvNC10YLQutC4IFvQv9C10YDQstGL0LUg0LTQstCwINGB0YLQvtC70LHRhtCwINCyINGC0L7Rh9C90L7RgdGC0Lgg0YHQvtCy0L/QsNC00LDRjtGCXS4NCmBgYHtyfQ0KY2JpbmQoaGFuZG1hZGUucHJlZD1hcy5udW1lcmljKG15cHJlZCksIExEQS5wcmVkPWFzLm51bWVyaWMocHJlZCRjbGFzcykgLSAxLCByZWFsLmxhYmVscz1YJGN1cndvcilbc2VxKDMwKSxdDQpgYGANCg==