Principal component analysis, or PCA, is a common approach to dimensionality reduction. Learn exactly what PCA does, visualize the results of PCA with biplots and scree plots, and deal with practical issues such as centering and scaling the data before performing PCA.

library(readr)
library(dplyr)
library(ggplot2)
library(stringr)

3.1: PCA using prcomp()

In this exercise, you will create your first PCA model and observe the diagnostic results.

We have loaded the Pokemon data from earlier, which has four dimensions, and placed it in a variable called pokemon. Your task is to create a PCA model of the data, then to inspect the resulting model using the summary() function.

Instructions

100 XP

pokemon<-read.csv("Pokemon.csv")
pokemon_pr <- pokemon %>% select(HP, Attack, Defense, Speed)
glimpse(pokemon_pr)
Observations: 800
Variables: 4
$ HP      <int> 45, 60, 80, 80, 39, 58, 78, 78, 78, 44, 59, 79, 79, 45, 50, 60, 40, 45, 65, 65, 40...
$ Attack  <int> 49, 62, 82, 100, 52, 64, 84, 130, 104, 48, 63, 83, 103, 30, 20, 45, 35, 25, 90, 15...
$ Defense <int> 49, 63, 83, 123, 43, 58, 78, 111, 78, 65, 80, 100, 120, 35, 55, 50, 30, 50, 40, 40...
$ Speed   <int> 45, 60, 80, 80, 65, 80, 100, 100, 100, 43, 58, 78, 78, 45, 30, 70, 50, 35, 75, 145...
summary(pokemon_pr)
       HP             Attack       Defense           Speed       
 Min.   :  1.00   Min.   :  5   Min.   :  5.00   Min.   :  5.00  
 1st Qu.: 50.00   1st Qu.: 55   1st Qu.: 50.00   1st Qu.: 45.00  
 Median : 65.00   Median : 75   Median : 70.00   Median : 65.00  
 Mean   : 69.26   Mean   : 79   Mean   : 73.84   Mean   : 68.28  
 3rd Qu.: 80.00   3rd Qu.:100   3rd Qu.: 90.00   3rd Qu.: 90.00  
 Max.   :255.00   Max.   :190   Max.   :230.00   Max.   :180.00  
pr.out <- prcomp(x = pokemon_pr, scale = TRUE, center = TRUE)
summary(pr.out)
Importance of components:
                          PC1    PC2    PC3    PC4
Standard deviation     1.3721 0.9933 0.8526 0.6354
Proportion of Variance 0.4707 0.2467 0.1817 0.1009
Cumulative Proportion  0.4707 0.7173 0.8991 1.0000
biplot(pr.out)

PCbiplot(pr.pokemon)
Error in PCbiplot(pr.pokemon) : could not find function "PCbiplot"

Remark: Attack & HP variables have approximately the same loadings in the first two principal components (similar directions)

3.2: Variance explained

The second common plot type for understanding PCA models is a scree plot. A scree plot shows the variance explained as the number of principal components increases. Sometimes the cumulative variance explained is plotted as well.

In this and the next exercise, you will prepare data from the pr.out model you created at the beginning of the chapter for use in a scree plot. Preparing the data for plotting is required because there is not a built-in function in R to create this type of plot.

Instructions

100 XP

# Variability of each principal component: pr.var
pr.var <- pr.out$sdev^2
# Variance explained by each principal component: pve
pve <- pr.var / sum(pr.var)
pve
[1] 0.4706937 0.2466505 0.1817326 0.1009233

3.3: Visualize variance explained

Now you will create a scree plot showing the proportion of variance explained by each principal component, as well as the cumulative proportion of variance explained.

Recall from the video that these plots can help to determine the number of principal components to retain. One way to determine the number of principal components to retain is by looking for an elbow in the scree plot showing that as the number of principal components increases, the rate at which variance is explained decreases substantially. In the absence of a clear elbow, you can use the scree plot as a guide for setting a threshold.

Instructions

100 XP

The proportion of variance explained is still available in the pve object you created in the last exercise.

Use plot() to plot the proportion of variance explained by each principal component.

Use plot() and cumsum() (cumulative sum) to plot the cumulative proportion of variance explained as a function of the number principal components.

# Plot variance explained for each principal component
plot(pve, xlab = "Principal Component",
     ylab = "Proportion of Variance Explained",
     ylim = c(0, 1), type = "b")

# Plot cumulative proportion of variance explained
plot(cumsum(pve), xlab = "Principal Component",
     ylab = "Cumulative Proportion of Variance Explained",
     ylim = c(0, 1), type = "b")

3.4: Practical issues: scaling

You saw in the video that scaling your data before doing PCA changes the results of the PCA modeling. Here, you will perform PCA with and without scaling, then visualize the results using biplots.

Sometimes scaling is appropriate when the variances of the variables are substantially different. This is commonly the case when variables have different units of measurement, for example, degrees Fahrenheit (temperature) and miles (distance). Making the decision to use scaling is an important step in performing a principal component analysis.

Instructions

100 XP

# Mean of each variable
pokemon_new<-read.csv("new_pokemon.csv")
colMeans(pokemon_new[,2:6])
    Total HitPoints    Attack   Defense     Speed 
   448.82     71.08     81.22     78.44     66.58 
# Standard deviation of each variable
apply(pokemon_new[,2:6], 2, sd)
    Total HitPoints    Attack   Defense     Speed 
119.32321  25.62193  33.03078  32.05809  27.51036 
# PCA model with scaling: pr.with.scaling
pr.with.scaling <- prcomp(x = pokemon_new[,2:6], scale = T, center =T)
# PCA model without scaling: pr.without.scaling
pr.without.scaling <- prcomp(x = pokemon_new[,2:6], scale = F, center = T)
# Create biplots of both for comparison
biplot(pr.with.scaling)

biplot(pr.without.scaling)

Remark: The new Total column contains much more variation, on average, than the other four columns, so it has a disproportionate effect on the PCA model when scaling is not performed. After scaling the data, there’s a much more even distribution of the loading vectors.

LS0tDQp0aXRsZTogIkRhdGFjYW1wIFIgLSBVbnN1cGVydmlzZWQgTGVhcm5pbmcgaW4gUiA6IENoYXB0ZXIgMyAoRGltZW5zaW9uYWxpdHkgcmVkdWN0aW9uIHdpdGggUENBKSINCmF1dGhvcjogIkNoZW4gV2VpcWlhbmciDQpkYXRlOiAiTm92ZW1iZXIgMjgsIDIwMTgiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpQcmluY2lwYWwgY29tcG9uZW50IGFuYWx5c2lzLCBvciBQQ0EsIGlzIGEgY29tbW9uIGFwcHJvYWNoIHRvIGRpbWVuc2lvbmFsaXR5IHJlZHVjdGlvbi4gTGVhcm4gZXhhY3RseSB3aGF0IFBDQSBkb2VzLCB2aXN1YWxpemUgdGhlIHJlc3VsdHMgb2YgUENBIHdpdGggYmlwbG90cyBhbmQgc2NyZWUgcGxvdHMsIGFuZCBkZWFsIHdpdGggcHJhY3RpY2FsIGlzc3VlcyBzdWNoIGFzIGNlbnRlcmluZyBhbmQgc2NhbGluZyB0aGUgZGF0YSBiZWZvcmUgcGVyZm9ybWluZyBQQ0EuDQoNCmBgYHtyfQ0KbGlicmFyeShyZWFkcikNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KHN0cmluZ3IpDQpgYGANCg0KIyAzLjE6IFBDQSB1c2luZyBwcmNvbXAoKQ0KDQpJbiB0aGlzIGV4ZXJjaXNlLCB5b3Ugd2lsbCBjcmVhdGUgeW91ciBmaXJzdCBQQ0EgbW9kZWwgYW5kIG9ic2VydmUgdGhlIGRpYWdub3N0aWMgcmVzdWx0cy4NCg0KV2UgaGF2ZSBsb2FkZWQgdGhlIFBva2Vtb24gZGF0YSBmcm9tIGVhcmxpZXIsIHdoaWNoIGhhcyBmb3VyIGRpbWVuc2lvbnMsIGFuZCBwbGFjZWQgaXQgaW4gYSB2YXJpYWJsZSBjYWxsZWQgcG9rZW1vbi4gWW91ciB0YXNrIGlzIHRvIGNyZWF0ZSBhIFBDQSBtb2RlbCBvZiB0aGUgZGF0YSwgdGhlbiB0byBpbnNwZWN0IHRoZSByZXN1bHRpbmcgbW9kZWwgdXNpbmcgdGhlIHN1bW1hcnkoKSBmdW5jdGlvbi4NCg0KSW5zdHJ1Y3Rpb25zDQoNCjEwMCBYUA0KDQotIENyZWF0ZSBhIFBDQSBtb2RlbCBvZiB0aGUgZGF0YSBpbiBwb2tlbW9uLCBzZXR0aW5nIHNjYWxlIHRvIFRSVUUuIFN0b3JlIHRoZSByZXN1bHQgaW4gcHIub3V0Lg0KDQotIEluc3BlY3QgdGhlIHJlc3VsdCB3aXRoIHRoZSBzdW1tYXJ5KCkgZnVuY3Rpb24uDQpgYGB7cn0NCnBva2Vtb248LXJlYWQuY3N2KCJQb2tlbW9uLmNzdiIpDQpwb2tlbW9uX3ByIDwtIHBva2Vtb24gJT4lIHNlbGVjdChIUCwgQXR0YWNrLCBEZWZlbnNlLCBTcGVlZCkNCmdsaW1wc2UocG9rZW1vbl9wcikNCmBgYA0KDQpgYGB7cn0NCnN1bW1hcnkocG9rZW1vbl9wcikNCmBgYA0KDQpgYGB7cn0NCnByLm91dCA8LSBwcmNvbXAoeCA9IHBva2Vtb25fcHIsIHNjYWxlID0gVFJVRSwgY2VudGVyID0gVFJVRSkNCnN1bW1hcnkocHIub3V0KQ0KYmlwbG90KHByLm91dCkNCmBgYA0KUmVtYXJrOiAgQXR0YWNrICYgSFAgdmFyaWFibGVzIGhhdmUgYXBwcm94aW1hdGVseSB0aGUgc2FtZSBsb2FkaW5ncyBpbiB0aGUgZmlyc3QgdHdvIHByaW5jaXBhbCBjb21wb25lbnRzIChzaW1pbGFyIGRpcmVjdGlvbnMpDQoNCg0KIyAzLjI6IFZhcmlhbmNlIGV4cGxhaW5lZA0KDQpUaGUgc2Vjb25kIGNvbW1vbiBwbG90IHR5cGUgZm9yIHVuZGVyc3RhbmRpbmcgUENBIG1vZGVscyBpcyBhIHNjcmVlIHBsb3QuIEEgc2NyZWUgcGxvdCBzaG93cyB0aGUgdmFyaWFuY2UgZXhwbGFpbmVkIGFzIHRoZSBudW1iZXIgb2YgcHJpbmNpcGFsIGNvbXBvbmVudHMgaW5jcmVhc2VzLiBTb21ldGltZXMgdGhlIGN1bXVsYXRpdmUgdmFyaWFuY2UgZXhwbGFpbmVkIGlzIHBsb3R0ZWQgYXMgd2VsbC4NCg0KSW4gdGhpcyBhbmQgdGhlIG5leHQgZXhlcmNpc2UsIHlvdSB3aWxsIHByZXBhcmUgZGF0YSBmcm9tIHRoZSBwci5vdXQgbW9kZWwgeW91IGNyZWF0ZWQgYXQgdGhlIGJlZ2lubmluZyBvZiB0aGUgY2hhcHRlciBmb3IgdXNlIGluIGEgc2NyZWUgcGxvdC4gUHJlcGFyaW5nIHRoZSBkYXRhIGZvciBwbG90dGluZyBpcyByZXF1aXJlZCBiZWNhdXNlIHRoZXJlIGlzIG5vdCBhIGJ1aWx0LWluIGZ1bmN0aW9uIGluIFIgdG8gY3JlYXRlIHRoaXMgdHlwZSBvZiBwbG90Lg0KDQpJbnN0cnVjdGlvbnMNCg0KMTAwIFhQDQoNCi0gcHIub3V0IGFuZCB0aGUgcG9rZW1vbiBkYXRhIGFyZSBzdGlsbCBhdmFpbGFibGUgaW4geW91ciB3b3Jrc3BhY2UuDQoNCg0KLSBBc3NpZ24gdG8gdGhlIHZhcmlhYmxlIHByLnZhciB0aGUgc3F1YXJlIG9mIHRoZSBzdGFuZGFyZCBkZXZpYXRpb25zIG9mIHRoZSBwcmluY2lwYWwgY29tcG9uZW50cyAoaS5lLiB0aGUgdmFyaWFuY2UpLiBUaGUgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIHRoZSBwcmluY2lwYWwgY29tcG9uZW50cyBpcyBhdmFpbGFibGUgaW4gdGhlIHNkZXYgY29tcG9uZW50IG9mIHRoZSBQQ0EgbW9kZWwgb2JqZWN0Lg0KDQotIEFzc2lnbiB0byB0aGUgdmFyaWFibGUgcHZlIHRoZSBwcm9wb3J0aW9uIG9mIHRoZSB2YXJpYW5jZSBleHBsYWluZWQsIGNhbGN1bGF0ZWQgYnkgZGl2aWRpbmcgcHIudmFyIGJ5IHRoZSB0b3RhbCB2YXJpYW5jZSBleHBsYWluZWQgYnkgYWxsIHByaW5jaXBhbCBjb21wb25lbnRzLg0KDQpgYGB7cn0NCiMgVmFyaWFiaWxpdHkgb2YgZWFjaCBwcmluY2lwYWwgY29tcG9uZW50OiBwci52YXINCnByLnZhciA8LSBwci5vdXQkc2Rldl4yDQoNCiMgVmFyaWFuY2UgZXhwbGFpbmVkIGJ5IGVhY2ggcHJpbmNpcGFsIGNvbXBvbmVudDogcHZlDQpwdmUgPC0gcHIudmFyIC8gc3VtKHByLnZhcikNCnB2ZQ0KYGBgDQoNCiMgMy4zOiBWaXN1YWxpemUgdmFyaWFuY2UgZXhwbGFpbmVkDQoNCk5vdyB5b3Ugd2lsbCBjcmVhdGUgYSBzY3JlZSBwbG90IHNob3dpbmcgdGhlIHByb3BvcnRpb24gb2YgdmFyaWFuY2UgZXhwbGFpbmVkIGJ5IGVhY2ggcHJpbmNpcGFsIGNvbXBvbmVudCwgYXMgd2VsbCBhcyB0aGUgY3VtdWxhdGl2ZSBwcm9wb3J0aW9uIG9mIHZhcmlhbmNlIGV4cGxhaW5lZC4NCg0KUmVjYWxsIGZyb20gdGhlIHZpZGVvIHRoYXQgdGhlc2UgcGxvdHMgY2FuIGhlbHAgdG8gZGV0ZXJtaW5lIHRoZSBudW1iZXIgb2YgcHJpbmNpcGFsIGNvbXBvbmVudHMgdG8gcmV0YWluLiBPbmUgd2F5IHRvIGRldGVybWluZSB0aGUgbnVtYmVyIG9mIHByaW5jaXBhbCBjb21wb25lbnRzIHRvIHJldGFpbiBpcyBieSBsb29raW5nIGZvciBhbiBlbGJvdyBpbiB0aGUgc2NyZWUgcGxvdCBzaG93aW5nIHRoYXQgYXMgdGhlIG51bWJlciBvZiBwcmluY2lwYWwgY29tcG9uZW50cyBpbmNyZWFzZXMsIHRoZSByYXRlIGF0IHdoaWNoIHZhcmlhbmNlIGlzIGV4cGxhaW5lZCBkZWNyZWFzZXMgc3Vic3RhbnRpYWxseS4gSW4gdGhlIGFic2VuY2Ugb2YgYSBjbGVhciBlbGJvdywgeW91IGNhbiB1c2UgdGhlIHNjcmVlIHBsb3QgYXMgYSBndWlkZSBmb3Igc2V0dGluZyBhIHRocmVzaG9sZC4NCg0KSW5zdHJ1Y3Rpb25zDQoNCjEwMCBYUA0KDQpUaGUgcHJvcG9ydGlvbiBvZiB2YXJpYW5jZSBleHBsYWluZWQgaXMgc3RpbGwgYXZhaWxhYmxlIGluIHRoZSBwdmUgb2JqZWN0IHlvdSBjcmVhdGVkIGluIHRoZSBsYXN0IGV4ZXJjaXNlLg0KDQpVc2UgcGxvdCgpIHRvIHBsb3QgdGhlIHByb3BvcnRpb24gb2YgdmFyaWFuY2UgZXhwbGFpbmVkIGJ5IGVhY2ggcHJpbmNpcGFsIGNvbXBvbmVudC4NCg0KVXNlIHBsb3QoKSBhbmQgY3Vtc3VtKCkgKGN1bXVsYXRpdmUgc3VtKSB0byBwbG90IHRoZSBjdW11bGF0aXZlIHByb3BvcnRpb24gb2YgdmFyaWFuY2UgZXhwbGFpbmVkIGFzIGEgZnVuY3Rpb24gb2YgdGhlIG51bWJlciBwcmluY2lwYWwgY29tcG9uZW50cy4NCg0KYGBge3J9DQojIFBsb3QgdmFyaWFuY2UgZXhwbGFpbmVkIGZvciBlYWNoIHByaW5jaXBhbCBjb21wb25lbnQNCnBsb3QocHZlLCB4bGFiID0gIlByaW5jaXBhbCBDb21wb25lbnQiLA0KICAgICB5bGFiID0gIlByb3BvcnRpb24gb2YgVmFyaWFuY2UgRXhwbGFpbmVkIiwNCiAgICAgeWxpbSA9IGMoMCwgMSksIHR5cGUgPSAiYiIpDQoNCiMgUGxvdCBjdW11bGF0aXZlIHByb3BvcnRpb24gb2YgdmFyaWFuY2UgZXhwbGFpbmVkDQpwbG90KGN1bXN1bShwdmUpLCB4bGFiID0gIlByaW5jaXBhbCBDb21wb25lbnQiLA0KICAgICB5bGFiID0gIkN1bXVsYXRpdmUgUHJvcG9ydGlvbiBvZiBWYXJpYW5jZSBFeHBsYWluZWQiLA0KICAgICB5bGltID0gYygwLCAxKSwgdHlwZSA9ICJiIikNCmBgYA0KDQojIDMuNDogUHJhY3RpY2FsIGlzc3Vlczogc2NhbGluZw0KDQpZb3Ugc2F3IGluIHRoZSB2aWRlbyB0aGF0IHNjYWxpbmcgeW91ciBkYXRhIGJlZm9yZSBkb2luZyBQQ0EgY2hhbmdlcyB0aGUgcmVzdWx0cyBvZiB0aGUgUENBIG1vZGVsaW5nLiBIZXJlLCB5b3Ugd2lsbCBwZXJmb3JtIFBDQSB3aXRoIGFuZCB3aXRob3V0IHNjYWxpbmcsIHRoZW4gdmlzdWFsaXplIHRoZSByZXN1bHRzIHVzaW5nIGJpcGxvdHMuDQoNClNvbWV0aW1lcyBzY2FsaW5nIGlzIGFwcHJvcHJpYXRlIHdoZW4gdGhlIHZhcmlhbmNlcyBvZiB0aGUgdmFyaWFibGVzIGFyZSBzdWJzdGFudGlhbGx5IGRpZmZlcmVudC4gVGhpcyBpcyBjb21tb25seSB0aGUgY2FzZSB3aGVuIHZhcmlhYmxlcyBoYXZlIGRpZmZlcmVudCB1bml0cyBvZiBtZWFzdXJlbWVudCwgZm9yIGV4YW1wbGUsIGRlZ3JlZXMgRmFocmVuaGVpdCAodGVtcGVyYXR1cmUpIGFuZCBtaWxlcyAoZGlzdGFuY2UpLiBNYWtpbmcgdGhlIGRlY2lzaW9uIHRvIHVzZSBzY2FsaW5nIGlzIGFuIGltcG9ydGFudCBzdGVwIGluIHBlcmZvcm1pbmcgYSBwcmluY2lwYWwgY29tcG9uZW50IGFuYWx5c2lzLg0KDQpJbnN0cnVjdGlvbnMNCg0KMTAwIFhQDQoNCi0gVGhlIHNhbWUgUG9rZW1vbiBkYXRhc2V0IGlzIGF2YWlsYWJsZSBpbiB5b3VyIHdvcmtzcGFjZSBhcyBwb2tlbW9uLCBidXQgb25lIG5ldyB2YXJpYWJsZSBoYXMgYmVlbiBhZGRlZDogVG90YWwuDQoNCi0gVGhlcmUgaXMgc29tZSBjb2RlIGF0IHRoZSB0b3Agb2YgdGhlIGVkaXRvciB0byBjYWxjdWxhdGUgdGhlIG1lYW4gYW5kIHN0YW5kYXJkIGRldmlhdGlvbiBvZiBlYWNoIHZhcmlhYmxlIGluIHRoZSBtb2RlbC4gUnVuIHRoaXMgY29kZSB0byBzZWUgaG93IHRoZSBzY2FsZSBvZiB0aGUgdmFyaWFibGVzIGRpZmZlcnMgaW4gdGhlIG9yaWdpbmFsIGRhdGEuDQoNCi0gQ3JlYXRlIGEgUENBIG1vZGVsIG9mIHBva2Vtb24gd2l0aCBzY2FsaW5nLCBhc3NpZ25pbmcgdGhlIHJlc3VsdCB0byBwci53aXRoLnNjYWxpbmcuDQoNCi0gQ3JlYXRlIGEgUENBIG1vZGVsIG9mIHBva2Vtb24gd2l0aG91dCBzY2FsaW5nLCBhc3NpZ25pbmcgdGhlIHJlc3VsdCB0byBwci53aXRob3V0LnNjYWxpbmcuDQoNCi0gVXNlIGJpcGxvdCgpIHRvIHBsb3QgYm90aCBtb2RlbHMgKG9uZSBhdCBhIHRpbWUpIGFuZCBjb21wYXJlIHRoZWlyIG91dHB1dHMuDQpgYGB7cn0NCiMgTWVhbiBvZiBlYWNoIHZhcmlhYmxlDQpwb2tlbW9uX25ldzwtcmVhZC5jc3YoIm5ld19wb2tlbW9uLmNzdiIpDQpjb2xNZWFucyhwb2tlbW9uX25ld1ssMjo2XSkNCg0KIyBTdGFuZGFyZCBkZXZpYXRpb24gb2YgZWFjaCB2YXJpYWJsZQ0KYXBwbHkocG9rZW1vbl9uZXdbLDI6Nl0sIDIsIHNkKQ0KDQojIFBDQSBtb2RlbCB3aXRoIHNjYWxpbmc6IHByLndpdGguc2NhbGluZw0KcHIud2l0aC5zY2FsaW5nIDwtIHByY29tcCh4ID0gcG9rZW1vbl9uZXdbLDI6Nl0sIHNjYWxlID0gVCwgY2VudGVyID1UKQ0KDQoNCiMgUENBIG1vZGVsIHdpdGhvdXQgc2NhbGluZzogcHIud2l0aG91dC5zY2FsaW5nDQpwci53aXRob3V0LnNjYWxpbmcgPC0gcHJjb21wKHggPSBwb2tlbW9uX25ld1ssMjo2XSwgc2NhbGUgPSBGLCBjZW50ZXIgPSBUKQ0KDQojIENyZWF0ZSBiaXBsb3RzIG9mIGJvdGggZm9yIGNvbXBhcmlzb24NCmJpcGxvdChwci53aXRoLnNjYWxpbmcpDQpiaXBsb3QocHIud2l0aG91dC5zY2FsaW5nKQ0KYGBgDQoNClJlbWFyazogVGhlIG5ldyBUb3RhbCBjb2x1bW4gY29udGFpbnMgbXVjaCBtb3JlIHZhcmlhdGlvbiwgb24gYXZlcmFnZSwgdGhhbiB0aGUgb3RoZXIgZm91ciBjb2x1bW5zLCBzbyBpdCBoYXMgYSBkaXNwcm9wb3J0aW9uYXRlIGVmZmVjdCBvbiB0aGUgUENBIG1vZGVsIHdoZW4gc2NhbGluZyBpcyBub3QgcGVyZm9ybWVkLiBBZnRlciBzY2FsaW5nIHRoZSBkYXRhLCB0aGVyZSdzIGEgbXVjaCBtb3JlIGV2ZW4gZGlzdHJpYnV0aW9uIG9mIHRoZSBsb2FkaW5nIHZlY3RvcnMu