home <- [配布したフォルダをコピーしたパス]
setwd(home)

教師なし学習でクラスタリングと並んで重要なのが、次元縮約という方法論です。以下のように194サンプル(Tumor:97, Normal:97)の15121遺伝子の発現プロファイルを考えます。次元縮約の目的は、たくさんの変数(遺伝子)からなるデータを情報を可能な限り失わずに、少ない変数に要約し、解釈しやすくすることです。例えば、サンプル間の近さを可視化しようとすると、15121個の軸からなる座標空間を表示する必要がありますがそれは無理です。それを次元縮小で2,3個の軸に要約できれば、可視化が可能です。次元縮約の方法はたくさんありますが、今回は主成分分析(Principal component analysis;PCA)と多次元尺度構成法(Multidimensional scaling;MDS)を例に説明します。

input_file <- "data/data_expr_tumor_filtered.tsv"
data_tumor <- as.matrix(fread(input_file,header = TRUE,drop = 1))
colnames(data_tumor) <- paste0("Tumor",1:ncol(data_tumor))
input_file <- "data/data_expr_normal_filtered.tsv"
data_normal <- as.matrix(fread(input_file,header = TRUE,drop = 1))
colnames(data_normal) <- paste0("Normal",1:ncol(data_normal))
data_example <- cbind(data_tumor,data_normal)
rownames(data_example) <- paste0("Gene",1:nrow(data_example))
data_example <- t(data_example)
kable(data_example[1:10,1:10])
Gene1 Gene2 Gene3 Gene4 Gene5 Gene6 Gene7 Gene8 Gene9 Gene10
Tumor1 1.4159230 3.377278 0.7519159 98.35123 4.889642 15.20150 16.749664 0.3881095 1.4582108 139.74486
Tumor2 4.7858274 3.716744 0.2152176 100.90701 2.858270 16.31025 10.808428 0.3653588 0.3872724 24.27507
Tumor3 2.4368570 3.240557 0.3437818 40.82916 2.053996 16.05518 10.382871 0.0820921 0.2464288 35.18589
Tumor4 0.1958113 2.795901 0.8464482 46.42132 1.439745 13.99235 24.138840 0.1061655 0.5089498 37.40367
Tumor5 1.5079382 3.978028 0.2604621 234.46532 2.865083 11.50181 7.954990 0.7363401 0.5994274 30.36481
Tumor6 1.2348674 3.659223 0.4216911 327.46827 4.418752 13.27451 14.629319 0.3825851 0.9646250 19.29234
Tumor7 2.5003410 2.544354 0.8019765 180.69782 7.673741 10.38882 11.732746 0.2632887 0.5305637 19.26877
Tumor8 2.1449790 4.806979 0.2630553 246.70265 10.056010 14.63006 7.041516 0.2052525 2.3645559 24.56007
Tumor9 3.0588154 3.339891 6.4699673 89.54150 9.167577 10.79484 9.543649 0.2530093 2.4072224 17.29611
Tumor10 2.1071854 3.114608 0.5098996 109.82388 3.102324 23.12806 13.790673 0.5555666 0.6074557 23.08223

PCAから始めます。PCAでは、情報=データのばらつきと考えます。以下の図は、最初の20個の遺伝子のみについての対散布図です。遺伝子と遺伝子の間には、いくつかの相関関係が見られます。主成分分析では、このような構造も含めて、次元縮約を行います。さっそく15121個の遺伝子を2個の軸に要約してみましょう。今回はpcaMethodというパッケージ中のpca関数を用います。

pairs(data_example[,1:20])

library(pcaMethods)
(result_pca <- pca(data_example,nPcs = 2))
svd calculated PCA
Importance of component(s):
                PC1    PC2
R2            0.197 0.1297
Cumulative R2 0.197 0.3267
15121   Variables
194     Samples
0   NAs ( 0 %)
2   Calculated component(s)
Data was mean centered before running PCA 
Data was NOT scaled before running PCA 
Scores structure:
[1] 194   2
Loadings structure:
[1] 15121     2

要約した軸における値をスコアと呼びます。スコアを使って、2次元空間上でサンプルを表示して色分けしてみると、TumorとNormalの間で異なる傾向が見て取れます。

score <- result_pca@scores
score[1:10,]
                PC1         PC2
Tumor1   -2827.4559 -3197.35023
Tumor2     400.9473  8453.70884
Tumor3   -1571.8821  -655.33791
Tumor4   -4941.7396 -3255.54703
Tumor5     836.2170  2264.18164
Tumor6   -1399.2882   408.77359
Tumor7   -5014.9903    94.66862
Tumor8  -16852.6414 12812.95465
Tumor9   -3023.3209 -3246.54041
Tumor10  -3964.8193 -2034.51527
plot(score,cex=0.1)
text(score,labels=rownames(data_example),col=rep(c("black","blue"),each=ncol(data_tumor)))

得られた軸が何を表すかは必要に応じて、解析者が解釈を与えます。もともとの遺伝子の情報が軸に対して、どの程度、含まれているかは因子負荷量(Loadings)と呼ばれます。絶対値が大きい因子負荷量の遺伝子ほど、その軸に寄与しているという解釈ができます。例えば、Gene5はGene1よりもPC1に寄与しており、Gene4はGene5は符号が逆なので、何らかの逆の効果を持ってPC1に寄与しています。下図は各軸に対するloadingsを表示したものです。

loadings <- result_pca@loadings
loadings[1:10,]
                 PC1           PC2
Gene1  -4.510943e-05  9.483366e-05
Gene2   4.760426e-06 -1.528371e-05
Gene3  -1.171204e-04 -2.070240e-04
Gene4   5.027964e-03 -2.585365e-03
Gene5  -6.502928e-05 -3.937341e-05
Gene6  -4.661339e-05  2.453150e-04
Gene7   2.105485e-04 -8.920673e-06
Gene8   1.587441e-04 -6.743233e-05
Gene9  -2.211212e-05 -1.060542e-04
Gene10 -4.194461e-04  4.531030e-04
plot(loadings,cex=0.1)
text(loadings,labels=colnames(data_example),cex=0.5)

PCAで得られた軸が、もともとのデータのばらつきをどの程度説明できているかは、寄与率(それを累積した累積寄与率)を用います。今回は、PC1とPC2で32.6%のばらつきを説明できているということです。15121個の遺伝子をたった2個の軸にまとめていることを考えると、3割程度も説明できていれば、見方によっては十分な気がします。

(cumprop <- result_pca@R2cum)
    PC1     PC2 
0.19701 0.32669 

どれくらいの数の軸をとれば良いかは、累積寄与率をみて判断します。nPcs=10にすれば、10個の軸に要約できます。累積寄与率を見てみると、PC5までで、60%のばらつきを説明できていることになります。

result_pca2 <- pca(data_example,nPcs=10)
(cumprop2 <- result_pca2@R2cum)
    PC1     PC2     PC3     PC4     PC5     PC6     PC7     PC8     PC9    PC10 
0.19701 0.32669 0.41836 0.50605 0.57372 0.63551 0.67875 0.71249 0.74496 0.76931 

PCAの原理はとてもシンプルで、軸の回転です。一つ目の軸を選ぶには、まず一番ばらつきの大きい方向を15121個の軸からなる空間の中で見つけ、その方向を一つ目の軸(PC1)とします。二つ目の軸は、PC1に直角となるという制約のもとで、次にばらつきの大きい方向を見つけて、二つ目の軸(PC2)とします。これをすべての繰り返していけば最大で15121個の新しい軸を構成できます。このようにとった軸は、データのばらつきを説明できている順番に並べることができので、そのうちの上位を主成分として用いるわけです。

軸を回転させただけですから元々のデータ全体のばらつきと各軸のばらつきの和は同じです。

\[\sum_{i=1}^{15121}\mbox{var}(\mbox{gene}_i)=\sum_{i=1}^{15121}\mbox{var}(\mbox{PC}_i)\]

しかも、元々のデータの相関構造も壊していないことが理解できると思います。 ちなみに、先ほどの寄与率とは、全体のばらつきに対する各軸のばらつきの割合です。

\[\mbox{Proportion} = \frac{\mbox{var}(\mbox{PC}_i)}{\sum_{i=1}^{15121}\mbox{var}(\mbox{PC}_i)}\]

MDSについて説明します。PCAは多数の変数を、データのばらつきを最も説明する方向へ軸を回転させることで新しい変数を作り、寄与率の高い上位の軸を選ぶ方法でした。MDSでは、元々のデータにおける個体(サンプル)間の距離をできるだけ保持するようなユークリッド空間を構成するアプローチで新しい軸を構成します。例えて言えば、都市間の距離のみわかっている状況から地図を構成するような方法です。データのばらつきは、言い換えれば個体間の距離なので、結果はPCAと似たものとなります。

MDSを実行するには、cmdscale関数を用います。

d <- dist(data_example)
result_mds <- cmdscale(d,k = 2)
plot(result_mds,xlab="x1",ylab="x2",cex=0.1)
text(result_mds,labels = rownames(data_example),col=rep(c("black","blue"),each = ncol(data_tumor)))

先ほどのPCAと上下が逆になっており、また座標値も異なりますが、配置はほぼ同じだということがわかります。 PCAもMDSもほぼ同じ結果を与えます。違う点は、MDSは個体間の距離さえわかっていれば、座標空間を構成できるという点です。単純な表形式のデータだといいのですが、より複雑な形式のデータである場合には、明示的に表形式にはならないが、個体間の非類似性(距離)は計算できる場合には有効です。

このような可視化は、色々な場面で役に立ちます。例えば、クラスター分析の結果を確かめる場合に便利です。実際、MDSの場合は、クラスター分析で用いた非類似度に基づいて、座標空間を構成するので、クラスター分析の結果と点の配置が対応する傾向があります。また、TumorとNormalのサンプルの近さを見たように、明らかにおかしなサンプルをはずれ値として視覚的に見つけることも可能でしょう。さらに別の使い方としては、縮約した数次元の軸は、データのばらつきを最もよく表す軸であると考えられます。それを新たな特徴量を持ったデータと考えて、クラスター分析をしたりするなどの使い方も考えられます。

LS0tDQp0aXRsZTogIouzjnSCyIK1inePS4Fpjp+Ms49rlvGBaiINCmF1dGhvcjogIll1c3VrZSBNYXRzdWkiDQphZmZpbGlhdGlvbjogIpa8jMOJrpHlineDVoNYg2WDgJC2laiKd5WqluwiDQpkYXRlOiAijcWPSY1YkFaT+oFGMjAxN5RONYyOMTeT+iINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQpgYGB7cixldmFsPUZ9DQpob21lIDwtIFuUepV6grWCvYN0g0iDi4NfgvCDUoNzgVuCtYK9g3CDWF0NCnNldHdkKGhvbWUpDQpgYGANCmBgYHtyLGVjaG89Rn0NCmxpYnJhcnkoa25pdHIpDQpgYGANCg0Ki7OOdILIgrWKd49LgsWDToOJg1iDXoOKg5ODT4LGlcCC8YLFj2SXdoLIgsyCqoFBjp+Ms49rlvGCxoKigqSV+5ZAmF+CxYK3gUKIyIm6gsyC5oKkgskxOTSDVIOTg3aDiyhUdW1vcjo5NywgTm9ybWFsOjk3KYLMMTUxMjGI4pNgjnGCzJStjLuDdoONg3SDQINDg4uC8I1sgqaC3IK3gUKOn4yzj2uW8YLMltqTSYLNgUGCvYKtgrOC8YLMlc+QlIFpiOKTYI5xgWqCqYLngsiC6YNmgVuDXoLwj+6V8YLwicKUXILIjMCC6I64gu2CuILJgUGPrYLIgqKVz5CUgsmXdpbxgrWBQYnwjt+CtYLigreCrYK3gumCsYLGgsWCt4FCl+GCpoLOgUGDVIOTg3aDi4rUgsyL34KzgvCJwo6LibuCtYLmgqSCxoK3gumCxoFBMTUxMjGMwoLMjrKCqYLngsiC6Y3AlVeL84rUgvCVXI6mgreC6ZVLl3aCqoKgguiC3IK3gqqCu4Lqgs2Ws5edgsWCt4FCgruC6oLwjp+Ms49rj6yCxTIsM4zCgsyOsoLJl3aW8YLFgquC6oLOgUGJwo6LibuCqonClFyCxYK3gUKOn4yzj2uW8YLMlfuWQILNgr2CrYKzgvGCoILogtyCt4KqgUGNoYnxgs2O5ZCslaqVqpDNgWlQcmluY2lwYWwgY29tcG9uZW50IGFuYWx5c2lzO1BDQYFqgsaRvY6fjLOO2pN4jVyQrJZAKE11bHRpZGltZW5zaW9uYWwgc2NhbGluZztNRFMpgvCX4YLJkOCWvoK1gtyCt4FCDQpgYGB7cn0NCmlucHV0X2ZpbGUgPC0gImRhdGEvZGF0YV9leHByX3R1bW9yX2ZpbHRlcmVkLnRzdiINCmRhdGFfdHVtb3IgPC0gYXMubWF0cml4KGZyZWFkKGlucHV0X2ZpbGUsaGVhZGVyID0gVFJVRSxkcm9wID0gMSkpDQpjb2xuYW1lcyhkYXRhX3R1bW9yKSA8LSBwYXN0ZTAoIlR1bW9yIiwxOm5jb2woZGF0YV90dW1vcikpDQoNCg0KaW5wdXRfZmlsZSA8LSAiZGF0YS9kYXRhX2V4cHJfbm9ybWFsX2ZpbHRlcmVkLnRzdiINCmRhdGFfbm9ybWFsIDwtIGFzLm1hdHJpeChmcmVhZChpbnB1dF9maWxlLGhlYWRlciA9IFRSVUUsZHJvcCA9IDEpKQ0KY29sbmFtZXMoZGF0YV9ub3JtYWwpIDwtIHBhc3RlMCgiTm9ybWFsIiwxOm5jb2woZGF0YV9ub3JtYWwpKQ0KDQpkYXRhX2V4YW1wbGUgPC0gY2JpbmQoZGF0YV90dW1vcixkYXRhX25vcm1hbCkNCnJvd25hbWVzKGRhdGFfZXhhbXBsZSkgPC0gcGFzdGUwKCJHZW5lIiwxOm5yb3coZGF0YV9leGFtcGxlKSkNCg0KZGF0YV9leGFtcGxlIDwtIHQoZGF0YV9leGFtcGxlKQ0KYGBgDQoNCmBgYHtyfQ0Ka2FibGUoZGF0YV9leGFtcGxlWzE6MTAsMToxMF0pDQpgYGANCg0KUENBgqmC545ugt+C3IK3gUJQQ0GCxYLNgUFgj+6V8T2DZoFbg16CzILOgueCwoKrYILGjWyCpoLcgreBQojIibqCzJB9gs2BQY3Fj4mCzDIwjMKCzIjik2COcYLMgt2CyYLCgqKCxILMkc6OVZV6kH2CxYK3gUKI4pNgjnGCxojik2COcYLMitSCyYLNgUGCooKtgsKCqYLMkYqK1orWjFeCqoypgueC6oLcgreBQo7lkKyVqpWqkM2CxYLNgUGCsYLMguaCpILIjVyRooLgityC34LEgUGOn4yzj2uW8YLwjXOCooLcgreBQoKzgsGCu4KtMTUxMjGMwoLMiOKTYI5xgvAyjMKCzI6ygsmXdpbxgrWCxILdgtyCtYLlgqSBQo2hifGCzWBwY2FNZXRob2RggsaCooKkg3CDYoNQgVuDV5KGgsxgcGNhYIrWkJSC8JdwgqKC3IK3gUINCmBgYHtyLGZpZy53aWR0aD0yMCxmaWcuaGVpZ2h0PTIwfQ0KcGFpcnMoZGF0YV9leGFtcGxlWywxOjIwXSkNCmBgYA0KDQoNCmBgYHtyfQ0KbGlicmFyeShwY2FNZXRob2RzKQ0KKHJlc3VsdF9wY2EgPC0gcGNhKGRhdGFfZXhhbXBsZSxuUGNzID0gMikpDQpgYGANCg0Kl3aW8YK1gr2OsoLJgqiCr4LpkmyC8INYg1KDQYLGjMSC0YLcgreBQoNYg1KDQYLwjmeCwYLEgUEyjp+Ms4vzitSP44LFg1SDk4N2g4uC8JVcjqaCtYLEkEaVqoKvgrWCxILdgumCxoFBVHVtb3KCxk5vcm1hbILMitSCxYjZgsiC6YxYjPyCqoypgsSO5oLqgtyCt4FCDQpgYGB7cn0NCnNjb3JlIDwtIHJlc3VsdF9wY2FAc2NvcmVzDQpzY29yZVsxOjEwLF0NCnBsb3Qoc2NvcmUsY2V4PTAuMSkNCnRleHQoc2NvcmUsbGFiZWxzPXJvd25hbWVzKGRhdGFfZXhhbXBsZSksY29sPXJlcChjKCJibGFjayIsImJsdWUiKSxlYWNoPW5jb2woZGF0YV90dW1vcikpKQ0KYGBgDQoNCpO+gueC6oK9jrKCqom9gvCVXIK3gqmCzZVLl3aCyYmegraCxIFBifCQzY7SgqqJ8I7fgvCXXoKmgtyCt4FCguCCxoLggsaCzIjik2COcYLMj+6V8YKqjrKCyZHOgrWCxIFBgseCzJL2k3iBQYrcgtyC6oLEgqKC6YKpgs2I9o5xlYmJ15fKKExvYWRpbmdzKYLGjMSCzoLqgtyCt4FCkOKRzpJsgqqR5YKrgqKI9o5xlYmJ15fKgsyI4pNgjnGC2YLHgUGCu4LMjrKCyYrxl16CtYLEgqKC6YLGgqKCpInwjt+CqoLFgquC3IK3gUKX4YKmgs6BQUdlbmU1gs1HZW5lMYLmguiC4FBDMYLJivGXXoK1gsSCqILogUFHZW5lNILNR2VuZTWCzZWEjYaCqot0gsiCzILFgUGJvYLngqmCzIt0gsyM+InKgvCOnYLBgsRQQzGCyYrxl16CtYLEgqKC3IK3gUKJupB9gs2KZY6ygsmRzoK3gulsb2FkaW5nc4LwlVyOpoK1gr2C4ILMgsWCt4FCDQoNCmBgYHtyfQ0KbG9hZGluZ3MgPC0gcmVzdWx0X3BjYUBsb2FkaW5ncw0KbG9hZGluZ3NbMToxMCxdDQpwbG90KGxvYWRpbmdzLGNleD0wLjEpDQp0ZXh0KGxvYWRpbmdzLGxhYmVscz1jb2xuYW1lcyhkYXRhX2V4YW1wbGUpLGNleD0wLjUpDQpgYGANClBDQYLFk76C54Lqgr2OsoKqgUGC4ILGguCCxoLMg2aBW4NegsyCzoLngsKCq4LwgseCzJL2k3iQ4Ja+gsWCq4LEgqKC6YKpgs2BQYrxl16XpoFpgruC6oLwl92Qz4K1gr2X3ZDPivGXXpemgWqC8JdwgqKC3IK3gUKNoYnxgs2BQVBDMYLGUEMygsUzMi42JYLMgs6C54LCgquC8JDglr6CxYKrgsSCooLpgsaCooKkgrGCxoLFgreBQjE1MTIxjMKCzIjik2COcYLwgr2CwYK9MozCgsyOsoLJgtyCxoLfgsSCooLpgrGCxoLwjWyCpoLpgsaBQYJSioSS9pN4guCQ4Ja+gsWCq4LEgqKC6oLOgUGMqZX7gsmC5oLBgsSCzY9claqCyItDgqqCtYLcgreBQg0KDQpgYGB7cn0NCihjdW1wcm9wIDwtIHJlc3VsdF9wY2FAUjJjdW0pDQpgYGANCoLHguqCrYLngqKCzJCUgsyOsoLwgsaC6oLOl8eCooKpgs2BQZfdkM+K8Zdel6aC8ILdgsSUu5JmgrWC3IK3gUJgblBjcz0xMGCCyYK3guqCzoFBMTCMwoLMjrKCyZd2lvGCxYKrgtyCt4FCl92Qz4rxl16XpoLwjKmCxILdgumCxoFBUEM1gtyCxYLFgUE2MCWCzILOgueCwoKrgvCQ4Ja+gsWCq4LEgqKC6YKxgsaCyYLIguiC3IK3gUINCmBgYHtyfQ0KcmVzdWx0X3BjYTIgPC0gcGNhKGRhdGFfZXhhbXBsZSxuUGNzPTEwKQ0KKGN1bXByb3AyIDwtIHJlc3VsdF9wY2EyQFIyY3VtKQ0KYGBgDQoNClBDQYLMjLSXnYLNgsaCxILgg1aDk4N2g4uCxYFBYI6ygsyJ8ZNdYILFgreBQojqgsKW2oLMjrKC8JFJgtSCyYLNgUGC3IK4iOqU1ILOgueCwoKrgsyR5YKrgqKV+4z8gvAxNTEyMYzCgsyOsoKpgueCyILpi/OK1ILMkoaCxYypgsKCr4FBgruCzJX7jPyC8IjqgsKW2oLMjrIoUEMxKYLGgrWC3IK3gUKT8YLCltqCzI6ygs2BQVBDMYLJkryKcILGgsiC6YLGgqKCpJCnlvGCzILggsaCxYFBjp+CyYLOgueCwoKrgsyR5YKrgqKV+4z8gvCMqYLCgq+CxIFBk/GCwpbagsyOsihQQzIpgsaCtYLcgreBQoKxguqC8IK3gteCxILMjEqC6JXUgrWCxIKigq+Czo3FkeWCxTE1MTIxjMKCzJBWgrWCoo6ygvCNXJCsgsWCq4LcgreBQoKxgsyC5oKkgsmCxoLBgr2OsoLNgUGDZoFbg16CzILOgueCwoKrgvCQ4Ja+gsWCq4LEgqKC6Y+HlNSCyZXAgteC6YKxgsaCqoLFgquCzILFgUGCu4LMgqSCv4LMj+OIyoLwjuWQrJWqgsaCtYLEl3CCooLpgu2Cr4LFgreBQg0KDQqOsoLwifGTXYKzgrmCvYK+gq+CxYK3gqmC54yzgViCzINmgVuDXpFTkcyCzILOgueCwoKrgsaKZY6ygsyCzoLngsKCq4LMmGGCzZOvgraCxYK3gUINCg0KXFtcc3VtX3tpPTF9XnsxNTEyMX1cbWJveHt2YXJ9KFxtYm94e2dlbmV9X2kpPVxzdW1fe2k9MX1eezE1MTIxfVxtYm94e3Zhcn0oXG1ib3h7UEN9X2kpXF0NCg0KgrWCqYLggUGMs4FYgsyDZoFbg16CzJGKitaNXJGiguCJ84K1gsSCooLIgqKCsYLGgqqXnYnwgsWCq4LpgsaOdoKigtyCt4FCDQqCv4LIgt2CyYFBkOaC2YLHgsyK8Zdel6aCxoLNgUGRU5HMgsyCzoLngsKCq4LJkc6Ct4LpimWOsoLMgs6C54LCgquCzIqEjYeCxYK3gUINCg0KXFtcbWJveHtQcm9wb3J0aW9ufSA9IFxmcmFje1xtYm94e3Zhcn0oXG1ib3h7UEN9X2kpfXtcc3VtX3tpPTF9XnsxNTEyMX1cbWJveHt2YXJ9KFxtYm94e1BDfV9pKX1cXQ0KDQoNCk1EU4LJgsKCooLEkOCWvoK1gtyCt4FCUENBgs2RvZCUgsyVz5CUgvCBQYNmgVuDXoLMgs6C54LCgquC8I3FguCQ4Ja+greC6ZX7jPyC1o6ygvCJ8ZNdgrOCuYLpgrGCxoLFkFaCtYKilc+QlILwjeyC6IFBivGXXpemgsyNgoKij+OIyoLMjrKC8JFJgtSV+5ZAgsWCtYK9gUJNRFOCxYLNgUGMs4FYgsyDZoFbg16CyYKogq+C6YzCkcwog1SDk4N2g4spitSCzIuXl6OC8ILFgquC6YK+gq+V246dgreC6YLmgqSCyIOGgVuDToOKg2KDaIvzitSC8I1ckKyCt4Lpg0GDdoONgVuDYILFkFaCtYKijrKC8I1ckKyCtYLcgreBQpfhgqaCxIy+gqaCzoFBk3OOc4rUgsyLl5ejgsyC3YLtgqmCwYLEgqKC6Y/zi7WCqYLnkm6QfYLwjVyQrIK3gumC5oKkgsiV+5ZAgsWCt4FCg2aBW4NegsyCzoLngsKCq4LNgUGMvoKiireCpoLqgs6MwpHMitSCzIuXl6OCyILMgsWBQYyLicqCzVBDQYLGjpeCvYLggsyCxoLIguiC3IK3gUINCg0KDQpNRFOC8I7AjXOCt4LpgsmCzYFBYGNtZHNjYWxlYIrWkJSC8JdwgqKC3IK3gUINCg0KDQpgYGB7cn0NCmQgPC0gZGlzdChkYXRhX2V4YW1wbGUpDQpyZXN1bHRfbWRzIDwtIGNtZHNjYWxlKGQsayA9IDIpDQpgYGANCg0KYGBge3J9DQpwbG90KHJlc3VsdF9tZHMseGxhYj0ieDEiLHlsYWI9IngyIixjZXg9MC4xKQ0KdGV4dChyZXN1bHRfbWRzLGxhYmVscyA9IHJvd25hbWVzKGRhdGFfZXhhbXBsZSksY29sPXJlcChjKCJibGFjayIsImJsdWUiKSxlYWNoID0gbmNvbChkYXRhX3R1bW9yKSkpDQpgYGANCpDmgtmCx4LMUENBgsaP44m6gqqLdILJgsiCwYLEgqiC6IFBgtyCvY3AlVeSbILgiNmCyILogtyCt4KqgUGUepJ1gs2C2YLak6+CtoK+gsaCooKkgrGCxoKqgu2CqYLogtyCt4FCIFBDQYLgTURTguCC2YLak6+CtoyLicqC8JdegqaC3IK3gUKI4YKkk1+CzYFBTURTgs2MwpHMitSCzIuXl6OCs4Kmgu2CqYLBgsSCooLqgs6BQY3AlVeL84rUgvCNXJCsgsWCq4LpgsaCooKkk1+CxYK3gUKSUI+DgsiVXIxgjq6CzINmgVuDXoK+gsaCooKigsyCxYK3gqqBQYLmguiVoY5HgsiMYI6ugsyDZoFbg16CxYKggumP6o2HgsmCzYFBlr6OppNJgsmVXIxgjq6CyYLNgsiC54LIgqKCqoFBjMKRzIrUgsyU8ZfejpeQq4Fpi5eXo4Fqgs2Mdo5agsWCq4Lpj+qNh4LJgs2XTIz4gsWCt4FCDQoNCoKxgsyC5oKkgsiJwo6LibuCzYFBkEaBWILIj+qWyoLFlvCCyZengr+C3IK3gUKX4YKmgs6BQYNOg4mDWINegVuVqpDNgsyMi4nKgvCKbYKpgt+C6Y/qjYeCyZXWl5iCxYK3gUKOwI3bgUFNRFOCzI/qjYeCzYFBg06DiYNYg16BW5WqkM2CxZdwgqKCvZTxl96Ol5N4gsmK7oLDgqKCxIFBjcCVV4vzitSC8I1ckKyCt4LpgsyCxYFBg06DiYNYg16BW5WqkM2CzIyLicqCxpNfgsyUepJ1gqqRzomegreC6YxYjPyCqoKgguiC3IK3gUKC3IK9gUFUdW1vcoLGTm9ybWFsgsyDVIOTg3aDi4LMi9+Cs4LwjKmCvYLmgqSCyYFBlr6C54KpgsmCqIKpgrWCyINUg5ODdoOLgvCCzYK4guqSbILGgrWCxI6Lim+TSYLJjKmCwoKvgumCsYLGguCJwpRcgsWCtYLlgqSBQoKzgueCyZXKgsyOZ4KilfuCxoK1gsSCzYFBj2uW8YK1gr2QlI6fjLOCzI6ygs2BQYNmgVuDXoLMgs6C54LCgquC8I3FguCC5oKtlVyCt46ygsWCoILpgsaNbIKmgueC6oLcgreBQoK7guqC8JBWgr2CyJPBkqWXyoLwjp2CwYK9g2aBW4NegsaNbIKmgsSBQYNOg4mDWINegVuVqpDNgvCCtYK9guiCt4LpgsiCx4LMjmeCopX7guCNbIKmgueC6oLcgreBQg==