This document contains some common tools and tricks to explore survey data. For ease of generalisation, we will be simulating our own survey data rather importing a pre-prepared data set. If you are importing your own data set however, the following packages and functions will be useful for importing sav (SPSS), csv or Excel files respectively:

library(haven) #part of the tidyverse
haven::read_sav() #import SPSS files

library(readr) #part of the tidyverse
readr::read_csv() #import csv files

library(readxl)
readxl::read_xlsx() #import xlsx files

sav files usually contain variable labels, which may be useful for when you conduct survey analysis, e.g. remindng you what a particular Q2c_14 variable stands for.

Simulating the data

To start simulating some survey data, let’s first load the tidyverse package, set seed and create the variables:

library(tidyverse)
Loading tidyverse: ggplot2
Loading tidyverse: tibble
Loading tidyverse: tidyr
Loading tidyverse: readr
Loading tidyverse: purrr
Loading tidyverse: dplyr
Conflicts with tidy packages --------------------------------
filter(): dplyr, stats
lag():    dplyr, stats
set.seed(69)
resp_id <- 1:2000
gender <- round(runif(2000,min=0,max=1)) #0 is male, 1 is female
age <- sample(c("18-24","25-34","35-54","55-64","65+"),2000,replace=TRUE,prob=c(0.15,0.20,0.3,0.15,0.2))
visited_prev <- round(runif(2000,min=0,max=1)) #Whether respondent has visited before
csat <- round(runif(2000,min=0,max=10)) #Customer Satisfaction Score
csat[which(visited_prev==0)] <- NA #NA if respondent had not visited before
df <- data.frame(resp_id,gender,age,visited_prev,csat) #assign to a data frame

Let’s have a look at what the simulated data looks like:

glimpse(df)
Observations: 2,000
Variables: 5
$ resp_id      <int> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, ...
$ gender       <dbl> 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0,...
$ age          <fctr> 55-64, 35-54, 55-64, 55-64, 18-24,...
$ visited_prev <dbl> 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1,...
$ csat         <dbl> NA, 9, NA, NA, NA, NA, 9, NA, NA, 5...

Apart from the age variable which was set to factor, the rest of the variables will need to be set to the right type/class. As a general rule for survey analysis, respondent IDs should be set as character, and categorical variables as factor. Here’s what we’ll do:

df$resp_id <- as.character(df$resp_id)
library(stringr)
df$resp_id <- str_pad(df$resp_id,3,side="left",pad="0") #Make the ID into a "proper" 3-digit ID

This is what resp_id looks like after we run the code:

glimpse(df$resp_id)
 chr [1:2000] "001" "002" "003" "004" "005" ...

The other bits that we have to do is to coerce the other categorical variables in the data into factors. This is straightforward to do in R:

df$gender <- factor(df$gender)
levels(df$gender) <-c("Male","Female") #Rename levels of factor for easy reading
df$visited_prev <- factor(df$visited_prev)
glimpse(df)
Observations: 2,000
Variables: 5
$ resp_id      <chr> "001", "002", "003", "004", "005", ...
$ gender       <fctr> Female, Female, Female, Female, Ma...
$ age          <fctr> 55-64, 35-54, 55-64, 55-64, 18-24,...
$ visited_prev <fctr> 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1...
$ csat         <dbl> NA, 9, NA, NA, NA, NA, 9, NA, NA, 5...

The data looks ready. We can now begin exploring this data.

Exploring the data

Some prefer to go straight for plots (e.g. boxplots, histogram) when in the initial stage of exploring data. However, for survey data, I find that it’s generally useful to eye-ball some contingency tables. Let’s have a look at the gender variable first:

prop.table(table(df$gender))

  Male Female 
0.5125 0.4875 

So this table tells us that 51.3% and 48.8% of the sample are male and female respectively, which sounds pretty reasonable.

The next step is to see if any age groups are over or under-represented in either of the genders. I’m going to use the package gmodels to produce these cross-tables:

library(gmodels)
CrossTable(df$age,df$gender,prop.r=FALSE,prop.t=FALSE,prop.chisq=FALSE,format="SPSS")

   Cell Contents
|-------------------------|
|                   Count |
|          Column Percent |
|-------------------------|

Total Observations in Table:  2000 

             | df$gender 
      df$age |     Male  |   Female  | Row Total | 
-------------|-----------|-----------|-----------|
       18-24 |      146  |      137  |      283  | 
             |   14.244% |   14.051% |           | 
-------------|-----------|-----------|-----------|
       25-34 |      206  |      219  |      425  | 
             |   20.098% |   22.462% |           | 
-------------|-----------|-----------|-----------|
       35-54 |      314  |      292  |      606  | 
             |   30.634% |   29.949% |           | 
-------------|-----------|-----------|-----------|
       55-64 |      150  |      137  |      287  | 
             |   14.634% |   14.051% |           | 
-------------|-----------|-----------|-----------|
         65+ |      209  |      190  |      399  | 
             |   20.390% |   19.487% |           | 
-------------|-----------|-----------|-----------|
Column Total |     1025  |      975  |     2000  | 
             |   51.250% |   48.750% |           | 
-------------|-----------|-----------|-----------|

 

What this table tells us is that there are slightly more 65+ males than females in this sample, but the age group with the highest incidence for both genders is 35-54. We can make this a little easier for us to visualise by creating some plots using the ggplot2 package.

library(ggplot2)
ggplot(df,aes(x=age))+
  geom_bar()+
  facet_grid(~gender)

Now that we’ve got a good sense of the demographic background which all seems to make sense, we can have a look at the key metric i.e. csat, or customer satisfaction.

Here are just a histogram and a boxplot to check the distribution of csat.

ggplot(df,aes(x=csat))+geom_histogram(binwidth=1)

ggplot(df,aes(x=1,y=csat))+geom_boxplot()

Since the csat variable was generated using a runif() (uniform distribution) function restricted to values between 0 and 10, we are unlikely to see a ‘realistic’ fall-out of customer satisfaction scores that we might expect from an actual survey.

Another way to get a good sense of the csat data is to use the summary() function, as below:

summary(df$csat) #get min, median, mean, max, etc. 
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
  0.000   3.000   5.000   5.024   7.000  10.000    1009 

One thing that we would be interested out of this survey data is whether customer satisfaction scores actually correlate with age and gender. First, however, we need to take note of the visted_prev variable, which represents whether the respondent has visited our business before. Again, we can use gmodel::CrossTable():

CrossTable(df$visited_prev,df$gender,prop.r=FALSE,prop.t=FALSE,prop.chisq=FALSE,format="SPSS")

   Cell Contents
|-------------------------|
|                   Count |
|          Column Percent |
|-------------------------|

Total Observations in Table:  2000 

                | df$gender 
df$visited_prev |     Male  |   Female  | Row Total | 
----------------|-----------|-----------|-----------|
              0 |      514  |      495  |     1009  | 
                |   50.146% |   50.769% |           | 
----------------|-----------|-----------|-----------|
              1 |      511  |      480  |      991  | 
                |   49.854% |   49.231% |           | 
----------------|-----------|-----------|-----------|
   Column Total |     1025  |      975  |     2000  | 
                |   51.250% |   48.750% |           | 
----------------|-----------|-----------|-----------|

 
CrossTable(df$visited_prev,df$age,prop.r=FALSE,prop.t=FALSE,prop.chisq=FALSE,format="SPSS")

   Cell Contents
|-------------------------|
|                   Count |
|          Column Percent |
|-------------------------|

Total Observations in Table:  2000 

                | df$age 
df$visited_prev |    18-24  |    25-34  |    35-54  |    55-64  |      65+  | Row Total | 
----------------|-----------|-----------|-----------|-----------|-----------|-----------|
              0 |      141  |      209  |      312  |      147  |      200  |     1009  | 
                |   49.823% |   49.176% |   51.485% |   51.220% |   50.125% |           | 
----------------|-----------|-----------|-----------|-----------|-----------|-----------|
              1 |      142  |      216  |      294  |      140  |      199  |      991  | 
                |   50.177% |   50.824% |   48.515% |   48.780% |   49.875% |           | 
----------------|-----------|-----------|-----------|-----------|-----------|-----------|
   Column Total |      283  |      425  |      606  |      287  |      399  |     2000  | 
                |   14.150% |   21.250% |   30.300% |   14.350% |   19.950% |           | 
----------------|-----------|-----------|-----------|-----------|-----------|-----------|

 

To be continued…

LS0tDQp0aXRsZTogIkFuYWx5c2luZyBTdXJ2ZXkgRGF0YSB3aXRoIFIgLSBQYXJ0IEkiDQphdXRob3I6ICJNYXJ0aW4gQ2hhbiINCmRhdGU6ICIyOSBNYXksIDIwMTciDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQNCiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0DQotLS0NCg0KVGhpcyBkb2N1bWVudCBjb250YWlucyBzb21lIGNvbW1vbiB0b29scyBhbmQgdHJpY2tzIHRvIGV4cGxvcmUgc3VydmV5IGRhdGEuIEZvciBlYXNlIG9mIGdlbmVyYWxpc2F0aW9uLCB3ZSB3aWxsIGJlIHNpbXVsYXRpbmcgb3VyIG93biBzdXJ2ZXkgZGF0YSByYXRoZXIgaW1wb3J0aW5nIGEgcHJlLXByZXBhcmVkIGRhdGEgc2V0LiBJZiB5b3UgYXJlIGltcG9ydGluZyB5b3VyIG93biBkYXRhIHNldCBob3dldmVyLCB0aGUgZm9sbG93aW5nIHBhY2thZ2VzIGFuZCBmdW5jdGlvbnMgd2lsbCBiZSB1c2VmdWwgZm9yIGltcG9ydGluZyBzYXYgKFNQU1MpLCBjc3Ygb3IgRXhjZWwgZmlsZXMgcmVzcGVjdGl2ZWx5Og0KDQoNCmBgYHtyLCBldmFsPUZBTFNFLGVjaG89VFJVRX0NCmxpYnJhcnkoaGF2ZW4pICNwYXJ0IG9mIHRoZSB0aWR5dmVyc2UNCmhhdmVuOjpyZWFkX3NhdigpICNpbXBvcnQgU1BTUyBmaWxlcw0KDQpsaWJyYXJ5KHJlYWRyKSAjcGFydCBvZiB0aGUgdGlkeXZlcnNlDQpyZWFkcjo6cmVhZF9jc3YoKSAjaW1wb3J0IGNzdiBmaWxlcw0KDQpsaWJyYXJ5KHJlYWR4bCkNCnJlYWR4bDo6cmVhZF94bHN4KCkgI2ltcG9ydCB4bHN4IGZpbGVzDQpgYGANCg0Kc2F2IGZpbGVzIHVzdWFsbHkgY29udGFpbiB2YXJpYWJsZSBsYWJlbHMsIHdoaWNoIG1heSBiZSB1c2VmdWwgZm9yIHdoZW4geW91IGNvbmR1Y3Qgc3VydmV5IGFuYWx5c2lzLCBlLmcuIHJlbWluZG5nIHlvdSB3aGF0IGEgcGFydGljdWxhciBRMmNfMTQgdmFyaWFibGUgc3RhbmRzIGZvci4gDQoNClNpbXVsYXRpbmcgdGhlIGRhdGENCi0tLQ0KDQpUbyBzdGFydCBzaW11bGF0aW5nIHNvbWUgc3VydmV5IGRhdGEsIGxldCdzIGZpcnN0IGxvYWQgdGhlICoqdGlkeXZlcnNlKiogcGFja2FnZSwgc2V0IHNlZWQgYW5kIGNyZWF0ZSB0aGUgdmFyaWFibGVzOg0KDQpgYGB7ciwgZXZhbD1UUlVFLCBlY2hvPVRSVUUsIGluY2x1ZGU9VFJVRX0NCmxpYnJhcnkodGlkeXZlcnNlKQ0Kc2V0LnNlZWQoNjkpDQoNCnJlc3BfaWQgPC0gMToyMDAwDQpnZW5kZXIgPC0gcm91bmQocnVuaWYoMjAwMCxtaW49MCxtYXg9MSkpICMwIGlzIG1hbGUsIDEgaXMgZmVtYWxlDQphZ2UgPC0gc2FtcGxlKGMoIjE4LTI0IiwiMjUtMzQiLCIzNS01NCIsIjU1LTY0IiwiNjUrIiksMjAwMCxyZXBsYWNlPVRSVUUscHJvYj1jKDAuMTUsMC4yMCwwLjMsMC4xNSwwLjIpKQ0KdmlzaXRlZF9wcmV2IDwtIHJvdW5kKHJ1bmlmKDIwMDAsbWluPTAsbWF4PTEpKSAjV2hldGhlciByZXNwb25kZW50IGhhcyB2aXNpdGVkIGJlZm9yZQ0KY3NhdCA8LSByb3VuZChydW5pZigyMDAwLG1pbj0wLG1heD0xMCkpICNDdXN0b21lciBTYXRpc2ZhY3Rpb24gU2NvcmUNCmNzYXRbd2hpY2godmlzaXRlZF9wcmV2PT0wKV0gPC0gTkEgI05BIGlmIHJlc3BvbmRlbnQgaGFkIG5vdCB2aXNpdGVkIGJlZm9yZQ0KDQpkZiA8LSBkYXRhLmZyYW1lKHJlc3BfaWQsZ2VuZGVyLGFnZSx2aXNpdGVkX3ByZXYsY3NhdCkgI2Fzc2lnbiB0byBhIGRhdGEgZnJhbWUNCmBgYA0KDQpMZXQncyBoYXZlIGEgbG9vayBhdCB3aGF0IHRoZSBzaW11bGF0ZWQgZGF0YSBsb29rcyBsaWtlOg0KDQpgYGB7ciwgZWNobz1UUlVFfQ0KZ2xpbXBzZShkZikNCmBgYA0KDQpBcGFydCBmcm9tIHRoZSAqYWdlKiB2YXJpYWJsZSB3aGljaCB3YXMgc2V0IHRvIGZhY3RvciwgdGhlIHJlc3Qgb2YgdGhlIHZhcmlhYmxlcyB3aWxsIG5lZWQgdG8gYmUgc2V0IHRvIHRoZSByaWdodCB0eXBlL2NsYXNzLiBBcyBhIGdlbmVyYWwgcnVsZSBmb3Igc3VydmV5IGFuYWx5c2lzLCByZXNwb25kZW50IElEcyBzaG91bGQgYmUgc2V0IGFzIGNoYXJhY3RlciwgYW5kIGNhdGVnb3JpY2FsIHZhcmlhYmxlcyBhcyBmYWN0b3IuIEhlcmUncyB3aGF0IHdlJ2xsIGRvOiANCg0KYGBge3IsIGVjaG89VFJVRSwgaW5jbHVkZT1UUlVFfQ0KZGYkcmVzcF9pZCA8LSBhcy5jaGFyYWN0ZXIoZGYkcmVzcF9pZCkNCg0KbGlicmFyeShzdHJpbmdyKQ0KZGYkcmVzcF9pZCA8LSBzdHJfcGFkKGRmJHJlc3BfaWQsMyxzaWRlPSJsZWZ0IixwYWQ9IjAiKSAjTWFrZSB0aGUgSUQgaW50byBhICJwcm9wZXIiIDMtZGlnaXQgSUQNCmBgYA0KVGhpcyBpcyB3aGF0IHJlc3BfaWQgbG9va3MgbGlrZSBhZnRlciB3ZSBydW4gdGhlIGNvZGU6IA0KDQoNCmBgYHtyLCBlY2hvPVRSVUUsIGluY2x1ZGU9VFJVRX0NCmdsaW1wc2UoZGYkcmVzcF9pZCkNCmBgYA0KVGhlIG90aGVyIGJpdHMgdGhhdCB3ZSBoYXZlIHRvIGRvIGlzIHRvIGNvZXJjZSB0aGUgb3RoZXIgY2F0ZWdvcmljYWwgdmFyaWFibGVzIGluIHRoZSBkYXRhIGludG8gZmFjdG9ycy4gVGhpcyBpcyBzdHJhaWdodGZvcndhcmQgdG8gZG8gaW4gUjogDQoNCmBgYHtyLCBlY2hvPVRSVUUsIGluY2x1ZGU9VFJVRX0NCg0KZGYkZ2VuZGVyIDwtIGZhY3RvcihkZiRnZW5kZXIpDQpsZXZlbHMoZGYkZ2VuZGVyKSA8LWMoIk1hbGUiLCJGZW1hbGUiKSAjUmVuYW1lIGxldmVscyBvZiBmYWN0b3IgZm9yIGVhc3kgcmVhZGluZw0KDQpkZiR2aXNpdGVkX3ByZXYgPC0gZmFjdG9yKGRmJHZpc2l0ZWRfcHJldikNCmdsaW1wc2UoZGYpDQpgYGANCg0KVGhlIGRhdGEgbG9va3MgcmVhZHkuIFdlIGNhbiBub3cgYmVnaW4gZXhwbG9yaW5nIHRoaXMgZGF0YS4gDQoNCkV4cGxvcmluZyB0aGUgZGF0YQ0KLS0tDQpTb21lIHByZWZlciB0byBnbyBzdHJhaWdodCBmb3IgcGxvdHMgKGUuZy4gYm94cGxvdHMsIGhpc3RvZ3JhbSkgd2hlbiBpbiB0aGUgaW5pdGlhbCBzdGFnZSBvZiBleHBsb3JpbmcgZGF0YS4gSG93ZXZlciwgZm9yIHN1cnZleSBkYXRhLCBJIGZpbmQgdGhhdCBpdCdzIGdlbmVyYWxseSB1c2VmdWwgdG8gZXllLWJhbGwgc29tZSBjb250aW5nZW5jeSB0YWJsZXMuIExldCdzIGhhdmUgYSBsb29rIGF0IHRoZSBnZW5kZXIgdmFyaWFibGUgZmlyc3Q6DQpgYGB7ciwgZWNobz1UUlVFLCBpbmNsdWRlPVRSVUV9DQoNCnByb3AudGFibGUodGFibGUoZGYkZ2VuZGVyKSkNCg0KYGBgDQoNClNvIHRoaXMgdGFibGUgdGVsbHMgdXMgdGhhdCA1MS4zJSBhbmQgNDguOCUgb2YgdGhlIHNhbXBsZSBhcmUgbWFsZSBhbmQgZmVtYWxlIHJlc3BlY3RpdmVseSwgd2hpY2ggc291bmRzIHByZXR0eSByZWFzb25hYmxlLiANCg0KVGhlIG5leHQgc3RlcCBpcyB0byBzZWUgaWYgYW55IGFnZSBncm91cHMgYXJlIG92ZXIgb3IgdW5kZXItcmVwcmVzZW50ZWQgaW4gZWl0aGVyIG9mIHRoZSBnZW5kZXJzLiBJJ20gZ29pbmcgdG8gdXNlIHRoZSBwYWNrYWdlICoqZ21vZGVscyoqIHRvIHByb2R1Y2UgdGhlc2UgY3Jvc3MtdGFibGVzOiANCg0KDQpgYGB7ciwgZWNobz1UUlVFLCBpbmNsdWRlPVRSVUV9DQoNCmxpYnJhcnkoZ21vZGVscykNCkNyb3NzVGFibGUoZGYkYWdlLGRmJGdlbmRlcixwcm9wLnI9RkFMU0UscHJvcC50PUZBTFNFLHByb3AuY2hpc3E9RkFMU0UsZm9ybWF0PSJTUFNTIikNCg0KYGBgDQoNCldoYXQgdGhpcyB0YWJsZSB0ZWxscyB1cyBpcyB0aGF0IHRoZXJlIGFyZSBzbGlnaHRseSBtb3JlIDY1KyBtYWxlcyB0aGFuIGZlbWFsZXMgaW4gdGhpcyBzYW1wbGUsIGJ1dCB0aGUgYWdlIGdyb3VwIHdpdGggdGhlIGhpZ2hlc3QgaW5jaWRlbmNlIGZvciBib3RoIGdlbmRlcnMgaXMgMzUtNTQuIFdlIGNhbiBtYWtlIHRoaXMgYSBsaXR0bGUgZWFzaWVyIGZvciB1cyB0byB2aXN1YWxpc2UgYnkgY3JlYXRpbmcgc29tZSBwbG90cyB1c2luZyB0aGUgKipnZ3Bsb3QyKiogcGFja2FnZS4NCg0KYGBge3IsIGVjaG89VFJVRSwgaW5jbHVkZT1UUlVFfQ0KbGlicmFyeShnZ3Bsb3QyKQ0KZ2dwbG90KGRmLGFlcyh4PWFnZSkpKw0KICBnZW9tX2JhcigpKw0KICBmYWNldF9ncmlkKH5nZW5kZXIpDQpgYGANCg0KTm93IHRoYXQgd2UndmUgZ290IGEgZ29vZCBzZW5zZSBvZiB0aGUgZGVtb2dyYXBoaWMgYmFja2dyb3VuZCAgd2hpY2ggYWxsIHNlZW1zIHRvIG1ha2Ugc2Vuc2UsIHdlIGNhbiBoYXZlIGEgbG9vayBhdCB0aGUga2V5IG1ldHJpYyBpLmUuICpjc2F0Kiwgb3IgKmN1c3RvbWVyIHNhdGlzZmFjdGlvbiouIA0KDQpIZXJlIGFyZSBqdXN0IGEgaGlzdG9ncmFtIGFuZCBhIGJveHBsb3QgdG8gY2hlY2sgdGhlIGRpc3RyaWJ1dGlvbiBvZiAqY3NhdCouIA0KDQpgYGB7ciwgZWNobz1UUlVFLCBpbmNsdWRlPVRSVUV9DQpnZ3Bsb3QoZGYsYWVzKHg9Y3NhdCkpK2dlb21faGlzdG9ncmFtKGJpbndpZHRoPTEpDQpnZ3Bsb3QoZGYsYWVzKHg9MSx5PWNzYXQpKStnZW9tX2JveHBsb3QoKQ0KYGBgDQoNClNpbmNlIHRoZSAqY3NhdCogdmFyaWFibGUgd2FzIGdlbmVyYXRlZCB1c2luZyBhICpydW5pZigpKiAodW5pZm9ybSBkaXN0cmlidXRpb24pIGZ1bmN0aW9uIHJlc3RyaWN0ZWQgdG8gdmFsdWVzIGJldHdlZW4gMCBhbmQgMTAsIHdlIGFyZSB1bmxpa2VseSB0byBzZWUgYSAncmVhbGlzdGljJyBmYWxsLW91dCBvZiBjdXN0b21lciBzYXRpc2ZhY3Rpb24gc2NvcmVzIHRoYXQgd2UgbWlnaHQgZXhwZWN0IGZyb20gYW4gYWN0dWFsIHN1cnZleS4gDQoNCkFub3RoZXIgd2F5IHRvIGdldCBhIGdvb2Qgc2Vuc2Ugb2YgdGhlICpjc2F0KiBkYXRhIGlzIHRvIHVzZSB0aGUgKnN1bW1hcnkoKSogZnVuY3Rpb24sIGFzIGJlbG93Og0KDQpgYGB7ciwgZWNobz1UUlVFLCBpbmNsdWRlPVRSVUV9DQoNCnN1bW1hcnkoZGYkY3NhdCkgI2dldCBtaW4sIG1lZGlhbiwgbWVhbiwgbWF4LCBldGMuIA0KDQpgYGANCg0KT25lIHRoaW5nIHRoYXQgd2Ugd291bGQgYmUgaW50ZXJlc3RlZCBvdXQgb2YgdGhpcyBzdXJ2ZXkgZGF0YSBpcyB3aGV0aGVyIGN1c3RvbWVyIHNhdGlzZmFjdGlvbiBzY29yZXMgYWN0dWFsbHkgY29ycmVsYXRlIHdpdGggYWdlIGFuZCBnZW5kZXIuIEZpcnN0LCBob3dldmVyLCB3ZSBuZWVkIHRvIHRha2Ugbm90ZSBvZiB0aGUgKnZpc3RlZF9wcmV2KiB2YXJpYWJsZSwgd2hpY2ggcmVwcmVzZW50cyB3aGV0aGVyIHRoZSByZXNwb25kZW50IGhhcyB2aXNpdGVkIG91ciBidXNpbmVzcyBiZWZvcmUuIEFnYWluLCB3ZSBjYW4gdXNlICoqZ21vZGVsOjpDcm9zc1RhYmxlKCkqKjoNCg0KYGBge3IsIGVjaG89VFJVRSwgaW5jbHVkZT1UUlVFfQ0KQ3Jvc3NUYWJsZShkZiR2aXNpdGVkX3ByZXYsZGYkZ2VuZGVyLHByb3Aucj1GQUxTRSxwcm9wLnQ9RkFMU0UscHJvcC5jaGlzcT1GQUxTRSxmb3JtYXQ9IlNQU1MiKQ0KYGBgDQpgYGB7ciwgZWNobz1UUlVFLCBpbmNsdWRlPVRSVUV9DQpDcm9zc1RhYmxlKGRmJHZpc2l0ZWRfcHJldixkZiRhZ2UscHJvcC5yPUZBTFNFLHByb3AudD1GQUxTRSxwcm9wLmNoaXNxPUZBTFNFLGZvcm1hdD0iU1BTUyIpDQpgYGANCg0KKipUbyBiZSBjb250aW51ZWQuLi4qKg0K