Salah satu keterampilan yang paling mendasar bagi seorang Data Scientist adalah Persiapan Data (Memanipulasi Data). Untuk menjadi benar - benar efektif, Anda harus menguasai dalam memanipulasi data - data yang penting. Karena sebagian besar pekerjaan Anda hanya akan melibatkan pengambilan dan pembersihan data.

Data scientist menghabiskan 60% waktunya untuk membersihkan dan merapikan data. Mengumpulkan dataset yang berada di urutan kedua dengan 19% waktunya, yang berarti Data Scientist menghabiskan sekitar 80% waktunya untuk menyiapkan dan mengelola data untuk menganalisis.

Di bagian ini, Anda akan mempelajari bagaimana cara melakukan persiapan data dengan mudah menggunakan R. Kami akan membahas beberapa kata kerja data manipulasi mendasar yang akan sering Anda gunakan dalam pekerjaan.

  • read_csv() Mengimpor data (anda bisa mengikuti yang lain)

  • str() Struktur Data

  • apply() Untuk memeriksa dan mengganti data yang hilang

  • select() Memilih kolum mana yang akan dimasukkan

  • filter() Memilih subset dari data yang ditentukan

  • arrange() Menyusun data, dari ukuran variabel kontinu, berdasarkan tanggal, atau secara alfabet

  • rename() Mengganti nama kolom

  • mutate() Membuat kolom baru pada data, atau mengganti kolom yang sudah ada

  • bind_rows() Menggabungkan dua data frame menjadi satu, mengkombinasikan data dari kolom yang namanya sama

  • group_by() Mengelompokkan data berdasarkan kategori variabelnya

  • summarize() Menyimpulkan, atau mengumpulan data (untuk masing-masing group jika menggunakan group_by).

    Biasa digunakan penghubung dengan beberapa fungsi termasuk :

    • mean() Mencari rata - rata sebuah data
    • median() Mencari nilai tengah sebuah data
    • max() Mencari nilai maksimal atau tertinggi sebuah data
    • min() Mencari nilai minimal atau terkecil sebuah data
    • sum() Menjumlahkan atau total semua nilai secara bersamaan
    • n() Menghitung angka pada data

Saya merekomendasikan Anda untuk menginstall package tidyverse. Karena inti dari tidyverse termasuk package yang sering Anda gunakan di setiap menganalisis data.

install.packages("tidyverse")

Kita terutama akan bekerja dengan dua paket yang sangat berguna yang dikembangkan oleh Hadley Wickham, kepala scientist di RStudio :

  • readr untuk membaca dan menulis CSV dan teks file lainnya.
  • dplyr untuk memproses dan memanipulasi data.

1. Import Data

Data yang akan kita gunakan untuk bagian ini adalah pfizer.csv dan fda.csv, silakan download dan simpan di desktop Anda. Sebagai opsional, Anda dapat memuat data ke sesi R saat ini dengan memilih Impor Set Data> Dari File Teks ... di tab Environment. Tapi, dalam kasus ini, kita akan menggunakan fungsi read_csv () dari paket readr. Salin kode berikut ke dalam skrip Anda dan Jalankan :

suppressPackageStartupMessages(library(tidyverse))              # Memuat package tidyverse
## Warning: package 'tidyverse' was built under R version 4.0.3
#setwd("C:/Users/Siana/Downloads")                              # Ingat untuk mengatur working directory
Pfizer <- read_csv("C:/Users/Siana/Downloads/pfizer.csv")       # Memuat data `pfizer` 
## Parsed with column specification:
## cols(
##   org_indiv = col_character(),
##   first_plus = col_character(),
##   first_name = col_character(),
##   last_name = col_character(),
##   city = col_character(),
##   state = col_character(),
##   category = col_character(),
##   cash = col_double(),
##   other = col_double(),
##   total = col_double()
## )
Fda <- read_csv("C:/Users/Siana/Downloads/fda.csv")               # Memuat data `fda`
## Parsed with column specification:
## cols(
##   name_last = col_character(),
##   name_first = col_character(),
##   name_middle = col_character(),
##   issued = col_date(format = ""),
##   office = col_character()
## )

2. Data Structures

Perhatikan bahwa Anda memerlukan pemahaman yang mendalam tentang jenis data dasar dan struktur data serta cara mengoperasikannya. Fungsi str () akan memberi tahu lebih banyak tentang kolom dalam data, termasuk tipe datanya. Salin kode ini ke skrip dan Jalankan perintahnya :

str(Pfizer)                                                     # Melihat struktur data dari `pfizer`
## tibble [10,087 x 10] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
##  $ org_indiv : chr [1:10087] "3-D MEDICAL SERVICES LLC" "AA DOCTORS, INC." "ABBO, LILIAN MARGARITA" "ABBO, LILIAN MARGARITA" ...
##  $ first_plus: chr [1:10087] "STEVEN BRUCE" "AAKASH MOHAN" "LILIAN MARGARITA" "LILIAN MARGARITA" ...
##  $ first_name: chr [1:10087] "STEVEN" "AAKASH" "LILIAN" "LILIAN" ...
##  $ last_name : chr [1:10087] "DEITELZWEIG" "AHUJA" "ABBO" "ABBO" ...
##  $ city      : chr [1:10087] "NEW ORLEANS" "PASO ROBLES" "MIAMI" "MIAMI" ...
##  $ state     : chr [1:10087] "LA" "CA" "FL" "FL" ...
##  $ category  : chr [1:10087] "Professional Advising" "Expert-Led Forums" "Business Related Travel" "Meals" ...
##  $ cash      : num [1:10087] 2625 1000 0 0 1800 ...
##  $ other     : num [1:10087] 0 0 448 119 0 0 47 0 0 396 ...
##  $ total     : num [1:10087] 2625 1000 448 119 1800 ...
##  - attr(*, "spec")=
##   .. cols(
##   ..   org_indiv = col_character(),
##   ..   first_plus = col_character(),
##   ..   first_name = col_character(),
##   ..   last_name = col_character(),
##   ..   city = col_character(),
##   ..   state = col_character(),
##   ..   category = col_character(),
##   ..   cash = col_double(),
##   ..   other = col_double(),
##   ..   total = col_double()
##   .. )
str(Fda)                                                          # Melihat struktur data dari `fda`
## tibble [272 x 5] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
##  $ name_last  : chr [1:272] "ADELGLASS" "ADKINSON" "ALLEN" "AMSTERDAM" ...
##  $ name_first : chr [1:272] "JEFFREY" "N." "MARK" "DANIEL" ...
##  $ name_middle: chr [1:272] "M." "FRANKLIN" "S." NA ...
##  $ issued     : Date[1:272], format: "1999-05-25" "2000-04-19" ...
##  $ office     : chr [1:272] "Center for Drug Evaluation and Research" "Center for Biologics Evaluation and Research" "Center for Devices and Radiological Health" "Center for Biologics Evaluation and Research" ...
##  - attr(*, "spec")=
##   .. cols(
##   ..   name_last = col_character(),
##   ..   name_first = col_character(),
##   ..   name_middle = col_character(),
##   ..   issued = col_date(format = ""),
##   ..   office = col_character()
##   .. )

Hal ini sangat penting untuk di mengerti karena ini adalah objek yang akan anda manipulasikan dari hari ke hari dalam basis R. Jika anda perlu untuk mengubah tipe data dari kolom tertentu, maka gunakan fungsi berikut :

  • as.character() - Mengubah menjadi teks string.
  • as.numeric() - Mengubah menjadi numerik.
  • as.factor() - Mengubah menjadi variabel kategorikal.
  • as.integer() - Mengubah menjadi integer.
  • as.Date() - Mengubah menjadi tanggal.
  • as.POSIXct() - Mengubah menjadi tanggal dan waktu secara lengkap.

Contohnya, tambahkan kode berikut ke R skrip untuk mengonversi total yang dikonversi dalam data pfizer menjadi variabel numerik (yang akan memungkinkannya menyimpan nilai desimal jika ada).

Pfizer$total <- as.numeric(Pfizer$total)                        # Mengkonversi Total ke variabel numerik
str(Pfizer$total)                                               # Maengecek kembali struktur datanya
##  num [1:10087] 2625 1000 448 119 1800 ...

3. Missing Value

Tidak seperti pemograman biasa, saat bekerja dengan data di kehidupan nyata, Anda mungkin memiliki nilai yang hilang : Pengukuran data yang tidak direkam/disimpan dll. R memiliki mekanisme yang cukup canggih untuk mengatasi missing value. Ini dibedakan antara berikut:

  • NA : Entri yang tidak tersedia (nilai NA juga memiliki kelas, jadi ada bilangan bulat NA, karakter NA, dll.)
  • NaN : Bukan Angka (nilai NaN juga NA tetapi konversinya tidak benar).

Mencari nmissing value dari kolom pada data frame pfizer.

is.na(Pfizer)                                                   # Cara klasik untuk memeriksa `NA`
sum(is.na(Pfizer))                                              # Menghitung total `NA`
apply(is.na(Pfizer),2, which)                                   # Yang mana indeks `NA` (hanya `df`)
which(complete.cases(Pfizer))                                   # Mengidentifikasi nilai lengkap yang diamati

Mekanisme yang lebih umum adalah menghapusnya secara manual :

clean.vector <- na.omit(Pfizer$first_name)                      # Menghapus vektor `NA`
clean.df <- na.omit(Pfizer)                                     # Menghapus dataframe `NA`
apply(is.na(clean.df),2, which)                                 # Memastikan jika ada `Missing Value`
## integer(0)

4. Replace Missing Value

Kita juga bisa mengganti nilai yang hilang dengan Mean (median). Praktik yang baik adalah membuat dua variabel terpisah untuk Mean. Setelah dibuat, kita dapat mengganti Missing Value dengan variabel yang baru dibentuk. Selanjutnya upload data dan verifikasi data yang hilang.

PATH <- "https://raw.githubusercontent.com/Bakti-Siregar/dataset/master/Bookdown-Data-Science-for-Beginners/Missing_Values.csv"
Titanic <- read.csv(PATH, sep = ",")
List_NA <- colnames(Titanic)[ apply(Titanic, 2, anyNA) ]
List_NA
## [1] "Age"  "Fare"

Dalam kasus ini, kita tidak menghapus semua Missing Value, tetapi kita menggunakan metode apply() untuk menghitung rata-rata kolom dengan NA. Pertama, kita perlu menghitung Mean dengan argumen na.rm = TRUE. Argumen ini wajib karena kolom memiliki data yang hilang, dan ini memberitahu R untuk mengabaikannya.

Average_Missing <- apply(Titanic[,colnames(Titanic) %in% List_NA],
                         2,
                         mean,
                         na.rm = TRUE)
Average_Missing
##      Age     Fare 
## 30.27259 35.62719

Penjelasan Code : Kita memiliki 4 argumen dalam metode Apply.

  • df titanic[,colnames(Titanic) %in% List_Na]. Code ini akan mengembalikan nama kolom dari objek List_Na (yaitu, “Age” dan “Fare”).
  • 2 Menghitung fungsi pada kolom.
  • Mean Menghitung rata - rata data.
  • na.rm = TRUE berarti mengabaikan nilai yang hilang.

Selanjutnya, kita dapat mengganti nilai NA. Kata kerja ‘mutate’ dari pustaka dplyr berguna dalam membuat variabel baru. Kita tidak perlu mengubah kolom asli sehingga kita dapat membuat variabel baru tanpa NA. ‘mutate’ mudah digunakan, kita hanya perlu memilih nama variabel dan menentukan cara membuat variabelnya. Berikut kode lengkapnya :

Titanic_Replace <- Titanic %>%
mutate(age  = ifelse(is.na(Age), Average_Missing[1], Age),
fare = ifelse(is.na(Fare), Average_Missing[2], Fare))
sum(is.na(Titanic_Replace$Age))
## [1] 86
sum(is.na(Titanic_Replace$Fare))
## [1] 1
sum(is.na(Titanic_Replace$age))
## [1] 0
sum(is.na(Titanic_Replace$fare))
## [1] 0

Pada kolom umur yang original, terdapat 86 nilai yang hilang dimana pada variabel yang baru dibuat menggantikannya dengan Mean dan variabel age. Anda dapat mencoba sendiri untuk mengganti observasi yang hilang dengan Median juga.

5. Select Data

Pada bagian ini, Anda akan mempelajari bagaimana cara memilih atau mensubset kolom data frame berdasarkan Nama dan Posisi menggunakan fungsi R select() pada package dplyr. Anda akan mempelajari cara menggunakan fungsi fungsi berikut :

  • pull() : Mengekstrak nilai kolom sebagai vektor. Kolom Interest dapat ditentukan berdasarkan nama atau indeks.
  • select() : Mengekstrak satu atau beberapa kolom sebagai Data Tabel. Ini juga bisa digunakan untuk menghapus kolom dari Data Frame.
  • select_if() : Memilih kolom berdasarkan kondisi tertentu. Fungsi ini dapat digunakan untuk misalnya memilih kolom jika bentuknya numerik.
  • Helper functions : starts_with(), ends_with(), contains(), matches() : Memilih kolom atau variabel berdasarkan namanya.
library(tidyverse)                                # Menjalankan `tidyverse`, yang termasuk dalam `dplyr`
Pfizer %>% pull(state) %>% head()                 # Mengekstrak nilai kolom 'state' sebagai vektor
## [1] "LA" "CA" "FL" "FL" "FL" "MI"
Pfizer %>% select(1:3)                            # Memilih kolom 1 sampai 3
## # A tibble: 10,087 x 3
##    org_indiv                  first_plus       first_name
##    <chr>                      <chr>            <chr>     
##  1 3-D MEDICAL SERVICES LLC   STEVEN BRUCE     STEVEN    
##  2 AA DOCTORS, INC.           AAKASH MOHAN     AAKASH    
##  3 ABBO, LILIAN MARGARITA     LILIAN MARGARITA LILIAN    
##  4 ABBO, LILIAN MARGARITA     LILIAN MARGARITA LILIAN    
##  5 ABBO, LILIAN MARGARITA     LILIAN MARGARITA LILIAN    
##  6 ABDULLAH RAFFEE MD PC      ABDULLAH         ABDULLAH  
##  7 ABEBE, SHEILA Y            SHEILA Y         SHEILA    
##  8 ABEBE, SHEILA Y            SHEILA Y         SHEILA    
##  9 ABILENE FAMILY FOOT CENTER GALEN CHRIS      GALEN     
## 10 ABOLNIK, IGOR Z            IGOR Z           IGOR      
## # ... with 10,077 more rows
Pfizer %>% select(1,3)                            # Memilih kolom 1 dan 3
## # A tibble: 10,087 x 2
##    org_indiv                  first_name
##    <chr>                      <chr>     
##  1 3-D MEDICAL SERVICES LLC   STEVEN    
##  2 AA DOCTORS, INC.           AAKASH    
##  3 ABBO, LILIAN MARGARITA     LILIAN    
##  4 ABBO, LILIAN MARGARITA     LILIAN    
##  5 ABBO, LILIAN MARGARITA     LILIAN    
##  6 ABDULLAH RAFFEE MD PC      ABDULLAH  
##  7 ABEBE, SHEILA Y            SHEILA    
##  8 ABEBE, SHEILA Y            SHEILA    
##  9 ABILENE FAMILY FOOT CENTER GALEN     
## 10 ABOLNIK, IGOR Z            IGOR      
## # ... with 10,077 more rows
Pfizer %>% select(state:total)                    # Memilih semua kolom dari `state` sampai `total`
## # A tibble: 10,087 x 5
##    state category                 cash other total
##    <chr> <chr>                   <dbl> <dbl> <dbl>
##  1 LA    Professional Advising    2625     0  2625
##  2 CA    Expert-Led Forums        1000     0  1000
##  3 FL    Business Related Travel     0   448   448
##  4 FL    Meals                       0   119   119
##  5 FL    Professional Advising    1800     0  1800
##  6 MI    Expert-Led Forums         750     0   750
##  7 IN    Educational Items           0    47    47
##  8 IN    Expert-Led Forums         825     0   825
##  9 TX    Professional Advising    3000     0  3000
## 10 UT    Business Related Travel     0   396   396
## # ... with 10,077 more rows
Pfizer %>% select(state,total)                    # Memilih kolom bersadarkan nama variabelnya
## # A tibble: 10,087 x 2
##    state total
##    <chr> <dbl>
##  1 LA     2625
##  2 CA     1000
##  3 FL      448
##  4 FL      119
##  5 FL     1800
##  6 MI      750
##  7 IN       47
##  8 IN      825
##  9 TX     3000
## 10 UT      396
## # ... with 10,077 more rows
Pfizer %>% select_if(is.numeric)                  # Hanya memilih kolom numerik
## # A tibble: 10,087 x 3
##     cash other total
##    <dbl> <dbl> <dbl>
##  1  2625     0  2625
##  2  1000     0  1000
##  3     0   448   448
##  4     0   119   119
##  5  1800     0  1800
##  6   750     0   750
##  7     0    47    47
##  8   825     0   825
##  9  3000     0  3000
## 10     0   396   396
## # ... with 10,077 more rows
Pfizer %>% select_if(is.character)                # Hanya memilih kolom character 
## # A tibble: 10,087 x 7
##    org_indiv       first_plus   first_name last_name  city    state category    
##    <chr>           <chr>        <chr>      <chr>      <chr>   <chr> <chr>       
##  1 3-D MEDICAL SE~ STEVEN BRUCE STEVEN     DEITELZWE~ NEW OR~ LA    Professiona~
##  2 AA DOCTORS, IN~ AAKASH MOHAN AAKASH     AHUJA      PASO R~ CA    Expert-Led ~
##  3 ABBO, LILIAN M~ LILIAN MARG~ LILIAN     ABBO       MIAMI   FL    Business Re~
##  4 ABBO, LILIAN M~ LILIAN MARG~ LILIAN     ABBO       MIAMI   FL    Meals       
##  5 ABBO, LILIAN M~ LILIAN MARG~ LILIAN     ABBO       MIAMI   FL    Professiona~
##  6 ABDULLAH RAFFE~ ABDULLAH     ABDULLAH   RAFFEE     FLINT   MI    Expert-Led ~
##  7 ABEBE, SHEILA Y SHEILA Y     SHEILA     ABEBE      INDIAN~ IN    Educational~
##  8 ABEBE, SHEILA Y SHEILA Y     SHEILA     ABEBE      INDIAN~ IN    Expert-Led ~
##  9 ABILENE FAMILY~ GALEN CHRIS  GALEN      ALBRITTON  ABILENE TX    Professiona~
## 10 ABOLNIK, IGOR Z IGOR Z       IGOR       ABOLNIK    PROVO   UT    Business Re~
## # ... with 10,077 more rows
Pfizer %>% select(starts_with("first"))           # Memilih kolom yang dimulai dengan `first`
## # A tibble: 10,087 x 2
##    first_plus       first_name
##    <chr>            <chr>     
##  1 STEVEN BRUCE     STEVEN    
##  2 AAKASH MOHAN     AAKASH    
##  3 LILIAN MARGARITA LILIAN    
##  4 LILIAN MARGARITA LILIAN    
##  5 LILIAN MARGARITA LILIAN    
##  6 ABDULLAH         ABDULLAH  
##  7 SHEILA Y         SHEILA    
##  8 SHEILA Y         SHEILA    
##  9 GALEN CHRIS      GALEN     
## 10 IGOR Z           IGOR      
## # ... with 10,077 more rows
Pfizer %>% select(ends_with("name"))              # Memilih kolom yang diakhiri dengan `name`
## # A tibble: 10,087 x 2
##    first_name last_name  
##    <chr>      <chr>      
##  1 STEVEN     DEITELZWEIG
##  2 AAKASH     AHUJA      
##  3 LILIAN     ABBO       
##  4 LILIAN     ABBO       
##  5 LILIAN     ABBO       
##  6 ABDULLAH   RAFFEE     
##  7 SHEILA     ABEBE      
##  8 SHEILA     ABEBE      
##  9 GALEN      ALBRITTON  
## 10 IGOR       ABOLNIK    
## # ... with 10,077 more rows
Pfizer %>% select(contains("rst"))                # Memilih kolom yang namanya mencakup `rst`
## # A tibble: 10,087 x 2
##    first_plus       first_name
##    <chr>            <chr>     
##  1 STEVEN BRUCE     STEVEN    
##  2 AAKASH MOHAN     AAKASH    
##  3 LILIAN MARGARITA LILIAN    
##  4 LILIAN MARGARITA LILIAN    
##  5 LILIAN MARGARITA LILIAN    
##  6 ABDULLAH         ABDULLAH  
##  7 SHEILA Y         SHEILA    
##  8 SHEILA Y         SHEILA    
##  9 GALEN CHRIS      GALEN     
## 10 IGOR Z           IGOR      
## # ... with 10,077 more rows
Pfizer %>% select(matches("_"))                   # Memilih kolom yang namanya cocok dengan yang biasa
## # A tibble: 10,087 x 4
##    org_indiv                  first_plus       first_name last_name  
##    <chr>                      <chr>            <chr>      <chr>      
##  1 3-D MEDICAL SERVICES LLC   STEVEN BRUCE     STEVEN     DEITELZWEIG
##  2 AA DOCTORS, INC.           AAKASH MOHAN     AAKASH     AHUJA      
##  3 ABBO, LILIAN MARGARITA     LILIAN MARGARITA LILIAN     ABBO       
##  4 ABBO, LILIAN MARGARITA     LILIAN MARGARITA LILIAN     ABBO       
##  5 ABBO, LILIAN MARGARITA     LILIAN MARGARITA LILIAN     ABBO       
##  6 ABDULLAH RAFFEE MD PC      ABDULLAH         ABDULLAH   RAFFEE     
##  7 ABEBE, SHEILA Y            SHEILA Y         SHEILA     ABEBE      
##  8 ABEBE, SHEILA Y            SHEILA Y         SHEILA     ABEBE      
##  9 ABILENE FAMILY FOOT CENTER GALEN CHRIS      GALEN      ALBRITTON  
## 10 ABOLNIK, IGOR Z            IGOR Z           IGOR       ABOLNIK    
## # ... with 10,077 more rows
Pfizer %>% select(-(state:total))                 # Menghapus semua kolom mulai dari `state` hingga `total`
## # A tibble: 10,087 x 5
##    org_indiv                 first_plus       first_name last_name   city       
##    <chr>                     <chr>            <chr>      <chr>       <chr>      
##  1 3-D MEDICAL SERVICES LLC  STEVEN BRUCE     STEVEN     DEITELZWEIG NEW ORLEANS
##  2 AA DOCTORS, INC.          AAKASH MOHAN     AAKASH     AHUJA       PASO ROBLES
##  3 ABBO, LILIAN MARGARITA    LILIAN MARGARITA LILIAN     ABBO        MIAMI      
##  4 ABBO, LILIAN MARGARITA    LILIAN MARGARITA LILIAN     ABBO        MIAMI      
##  5 ABBO, LILIAN MARGARITA    LILIAN MARGARITA LILIAN     ABBO        MIAMI      
##  6 ABDULLAH RAFFEE MD PC     ABDULLAH         ABDULLAH   RAFFEE      FLINT      
##  7 ABEBE, SHEILA Y           SHEILA Y         SHEILA     ABEBE       INDIANAPOL~
##  8 ABEBE, SHEILA Y           SHEILA Y         SHEILA     ABEBE       INDIANAPOL~
##  9 ABILENE FAMILY FOOT CENT~ GALEN CHRIS      GALEN      ALBRITTON   ABILENE    
## 10 ABOLNIK, IGOR Z           IGOR Z           IGOR       ABOLNIK     PROVO      
## # ... with 10,077 more rows
Pfizer %>% select(-state, -total)                 # Menghapus kolom `state` dan `total`
## # A tibble: 10,087 x 8
##    org_indiv     first_plus   first_name last_name city   category    cash other
##    <chr>         <chr>        <chr>      <chr>     <chr>  <chr>      <dbl> <dbl>
##  1 3-D MEDICAL ~ STEVEN BRUCE STEVEN     DEITELZW~ NEW O~ Professio~  2625     0
##  2 AA DOCTORS, ~ AAKASH MOHAN AAKASH     AHUJA     PASO ~ Expert-Le~  1000     0
##  3 ABBO, LILIAN~ LILIAN MARG~ LILIAN     ABBO      MIAMI  Business ~     0   448
##  4 ABBO, LILIAN~ LILIAN MARG~ LILIAN     ABBO      MIAMI  Meals          0   119
##  5 ABBO, LILIAN~ LILIAN MARG~ LILIAN     ABBO      MIAMI  Professio~  1800     0
##  6 ABDULLAH RAF~ ABDULLAH     ABDULLAH   RAFFEE    FLINT  Expert-Le~   750     0
##  7 ABEBE, SHEIL~ SHEILA Y     SHEILA     ABEBE     INDIA~ Education~     0    47
##  8 ABEBE, SHEIL~ SHEILA Y     SHEILA     ABEBE     INDIA~ Expert-Le~   825     0
##  9 ABILENE FAMI~ GALEN CHRIS  GALEN      ALBRITTON ABILE~ Professio~  3000     0
## 10 ABOLNIK, IGO~ IGOR Z       IGOR       ABOLNIK   PROVO  Business ~     0   396
## # ... with 10,077 more rows

6. Filter and Sort Data

Sekarang, kita akan filter() dan arrange() data dengan cara yang spesifik. Untuk setiap contoh berikut, salin code berikut ke R script dan lihat hasilnya. Perhatikan bagaimana kita membuat objek baru untuk menampung data yang diproses.

6.1 Example 1

Temukan dokter di California yang dibayar $10,000 atau lebih oleh Pfizer untuk menjalankan “Professional Advising”!

CA_Expert_10000 <- Pfizer %>%                     # Menjalankan semua `pfizer`    
  filter(state == "CA" &                          # Menjalankan semua `pfizer` yang di filter berdasarkan `state`
         total >= 10000 &                         # Di filter berdasarkan `total` >= 10000
         category == "Professional Advising")     # Lalu filter bersadarkan 'category'
CA_Expert_10000                                   # Menprint hasilnya
## # A tibble: 35 x 10
##    org_indiv first_plus first_name last_name city  state category  cash other
##    <chr>     <chr>      <chr>      <chr>     <chr> <chr> <chr>    <dbl> <dbl>
##  1 CUMMINGS~ JEFFREY L~ JEFFREY    CUMMINGS  LOS ~ CA    Profess~ 13500     0
##  2 FONAROW,~ GREGG CUR~ GREGG      FONAROW   LOS ~ CA    Profess~ 15750     0
##  3 GANZ, PE~ PETER      PETER      GANZ      SAN ~ CA    Profess~ 22000     0
##  4 GT AND A~ GEORGE     GEORGE     TRIADAFI~ SAN ~ CA    Profess~ 33705     0
##  5 HANAHAN,~ DOUGLAS    DOUGLAS    HANAHAN   SAN ~ CA    Profess~ 37500     0
##  6 HERBERT ~ HERBERT    HERBERT    CRUZ      FRES~ CA    Profess~ 13250     0
##  7 HU, RONA~ RONA JANE  RONA       HU        PALO~ CA    Profess~ 22500     0
##  8 KAPLOWIT~ NEIL       NEIL       KAPLOWITZ LOS ~ CA    Profess~ 12000     0
##  9 KIZER, K~ KENNETH W~ KENNETH    KIZER     ALIS~ CA    Profess~ 10000     0
## 10 KOO, EDW~ EDWARD HA~ EDWARD     MANG      LA J~ CA    Profess~ 12500     0
## # ... with 25 more rows, and 1 more variable: total <dbl>

6.2 Example 2

Sekarang tambahkan sort di akhir code untuk mengurutkan daftar dokter secara menurun berdasarkan pembayaran yang diterima!

CA_Expert_10000 <- Pfizer %>%                     # Menjalankan semua `pfizer`    
  filter(state == "CA" &                          # Memfilter berdasarkan `state` di California
         total >= 10000 &                         # Difilter berdasarkan `total` >= 10000
         category == "Professional Advising")%>%  # Difilter lagi berdasarkan `category`
  arrange(desc(total))                            # Mengurutkan order berdasarkan pembayaran yang diterima secara menurun
CA_Expert_10000                                   # Memprint hasilnya
## # A tibble: 35 x 10
##    org_indiv first_plus first_name last_name city  state category  cash other
##    <chr>     <chr>      <chr>      <chr>     <chr> <chr> <chr>    <dbl> <dbl>
##  1 MALENKA,~ ROBERT CH~ ROBERT     MALENKA   STAN~ CA    Profess~ 75566     0
##  2 REGENTS ~ DAVID RAY~ DAVID      GANDARA   IRVI~ CA    Profess~ 38500     0
##  3 PTACEK, ~ LOUIS JOHN LOUIS      PTACEK    SAN ~ CA    Profess~ 37588     0
##  4 HANAHAN,~ DOUGLAS    DOUGLAS    HANAHAN   SAN ~ CA    Profess~ 37500     0
##  5 REGENTS ~ JOHN POWER JOHN       KANE      IRVI~ CA    Profess~ 37500     0
##  6 RISCH, S~ SAMUEL CR~ SAMUEL     RISCH     SAN ~ CA    Profess~ 36000     0
##  7 GT AND A~ GEORGE     GEORGE     TRIADAFI~ SAN ~ CA    Profess~ 33705     0
##  8 SANDROCK~ CHRISTIAN~ CHRISTIAN  SANDROCK  SACR~ CA    Profess~ 33000     0
##  9 MATTHEW ~ MATTHEW J~ MATTHEW    BUDOFF    MANH~ CA    Profess~ 31500     0
## 10 OLEFSKY,~ JERROLD M~ JERROLD    OLEFSKY   SOLA~ CA    Profess~ 27746     0
## # ... with 25 more rows, and 1 more variable: total <dbl>

6.3 Example 3

Temukan dokter di California atau Newyork yang dibayar $10.000 atau lebih menggunakan Pfizer untuk menjalankan “Professional Advising”!

Perhatikan, dalam hal ini kita menggunakan | Operator Boolean, dan tanda kurung di sekitar bagian querynya. Ini memastikan bahwa bagian Query ini dijalankan pertama kali. Lihat apa yang terjadi jika Anda mengecualikan mereka.

CA_NY_Expert_10000 <- Pfizer %>%                    # Menjalankan semua `pfizer`    
  filter((state == "CA" | state == "NY")  &         # Difilter berdasarkan `state` California atau New York
         total >= 10000 &                           # Difilter berdasarkan `total` sama dengan/ lebih dari 10000
         category == "Professional Advising") %>%   # Difilter lagi berdasarkan `category`
  arrange(desc(total))                              # Mengurutkan order berdasarkan pembayaran yang diterima secara menurun
CA_NY_Expert_10000                                  # Memprint hasilnya
## # A tibble: 46 x 10
##    org_indiv first_plus first_name last_name city  state category   cash other
##    <chr>     <chr>      <chr>      <chr>     <chr> <chr> <chr>     <dbl> <dbl>
##  1 SAWYERS,~ CHARLES L~ CHARLES    SAWYERS   NEW ~ NY    Profess~ 100000     0
##  2 MALENKA,~ ROBERT CH~ ROBERT     MALENKA   STAN~ CA    Profess~  75566     0
##  3 REGENTS ~ DAVID RAY~ DAVID      GANDARA   IRVI~ CA    Profess~  38500     0
##  4 PTACEK, ~ LOUIS JOHN LOUIS      PTACEK    SAN ~ CA    Profess~  37588     0
##  5 HANAHAN,~ DOUGLAS    DOUGLAS    HANAHAN   SAN ~ CA    Profess~  37500     0
##  6 JAVITT, ~ DANIEL CO~ DANIEL     JAVITT    ORAN~ NY    Profess~  37500     0
##  7 REGENTS ~ JOHN POWER JOHN       KANE      IRVI~ CA    Profess~  37500     0
##  8 RISCH, S~ SAMUEL CR~ SAMUEL     RISCH     SAN ~ CA    Profess~  36000     0
##  9 GT AND A~ GEORGE     GEORGE     TRIADAFI~ SAN ~ CA    Profess~  33705     0
## 10 SANDROCK~ CHRISTIAN~ CHRISTIAN  SANDROCK  SACR~ CA    Profess~  33000     0
## # ... with 36 more rows, and 1 more variable: total <dbl>

6.4 Example 4

Temukan dokter di negara bagian selain California yang dibayar $10,000 atau lebih berdasarkan Pfizer untuk menjalankan “Professional Advising”!

Not_CA_Expert_10000 <- Pfizer %>%                   # Menjalankan semua `pfizer`   
  filter(state != "CA" &                            # Filter berdasarkan `state` selain California
         total >= 10000 &                           # Difilter berdasarkan `total` >= dari 10000
         category == "Professional Advising") %>%   # Difilter lagi berdasarkan `category`
  arrange(desc(total))                              # Mengurutkan order berdasarkan pembayaran yang diterima secara menurun
Not_CA_Expert_10000                                 # Memprint hasilnya
## # A tibble: 135 x 10
##    org_indiv first_plus first_name last_name city  state category   cash other
##    <chr>     <chr>      <chr>      <chr>     <chr> <chr> <chr>     <dbl> <dbl>
##  1 BAILES, ~ JOSEPH SW~ JOSEPH     BAILES    AUST~ TX    Profess~ 105000     0
##  2 SAWYERS,~ CHARLES L~ CHARLES    SAWYERS   NEW ~ NY    Profess~ 100000     0
##  3 MARDER, ~ HAROLD KAY HAROLD     MARDER    PHIL~ PA    Profess~  87610     0
##  4 WILLIAMS~ GRANT      GRANT      WILLIAMS  WAYNE PA    Profess~  85621     0
##  5 THE SCHE~ PHILIP SA~ PHILIP     SCHEIN    BRYN~ PA    Profess~  75609     0
##  6 GORDON, ~ JEFFREY I~ JEFFREY    GORDON    SAIN~ MO    Profess~  75108     0
##  7 KOLLEF, ~ MARIN HRI~ MARIN      KOLLEF    SAIN~ MO    Profess~  72750     0
##  8 JOSEPH, ~ WARREN S   WARREN     JOSEPH    HUNT~ PA    Profess~  71875     0
##  9 SHORR, A~ ANDREW FR~ ANDREW     SHORR     WASH~ DC    Profess~  62000     0
## 10 MAYO FOU~ PETER JAM~ PETER      DYCK      ROCH~ MN    Profess~  61175     0
## # ... with 125 more rows, and 1 more variable: total <dbl>

6.5 Example 5

Temukan 20 dokter yang ada di 4 negara bagian terbesar (CA, TX, FL, NY) yang dibayar terbanyak untuk “Expert-Led Forums”!

FourCountry_Prof_Top20 <- Pfizer %>%
  filter((state == "CA" | 
          state == "NY" |
          state == "TX" | 
          state == "FL") &
          category == "Expert-Led Forums") %>%
  arrange(desc(total)) %>%
  head(20)
FourCountry_Prof_Top20
## # A tibble: 20 x 10
##    org_indiv first_plus first_name last_name city  state category   cash other
##    <chr>     <chr>      <chr>      <chr>     <chr> <chr> <chr>     <dbl> <dbl>
##  1 SACKS, G~ GERALD MI~ GERALD     SACKS     SANT~ CA    Expert-~ 146500     0
##  2 NIDES, M~ MITCHELL   MITCHELL   NIDES     LOS ~ CA    Expert-~  70500     0
##  3 ROBERT B~ ROBERT BU~ ROBERT     NETT      SAN ~ TX    Expert-~  60750     0
##  4 SOLERA C~ STEVEN AB~ STEVEN     KAPLAN    CHAP~ NY    Expert-~  56500     0
##  5 GRIFFIN,~ JAMES DALE JAMES      GRIFFIN   DALL~ TX    Expert-~  54250     0
##  6 STUBBLEF~ MICHAEL D  MICHAEL    STUBBLEF~ NEW ~ NY    Expert-~  50500     0
##  7 POTKIN, ~ STEVEN GA~ STEVEN     POTKIN    ORAN~ CA    Expert-~  48350     0
##  8 GINSBERG~ DAVID ALAN DAVID      GINSBERG  LOS ~ CA    Expert-~  45750     0
##  9 LOUIE, S~ SAMUEL     SAMUEL     LOUIE     SACR~ CA    Expert-~  41250     0
## 10 INSTITUT~ GURKIPAL   GURKIPAL   SINGH     WOOD~ CA    Expert-~  40000     0
## 11 NEURONLI~ JEFFREY B~ JEFFREY    GELBLUM   MIAMI FL    Expert-~  39200     0
## 12 HENNEKEN~ CHARLES H  CHARLES    HENNEKENS BOCA~ FL    Expert-~  33250     0
## 13 SOBHY EL~ SAAD A     SAAD       SOBHY     SYRA~ NY    Expert-~  31500     0
## 14 COHEN, L~ LEE STEVEN LEE        COHEN     HAST~ NY    Expert-~  30500     0
## 15 BAROYA, ~ IVAN STEP~ IVAN       BAROYA    BONI~ CA    Expert-~  26400     0
## 16 JONES, S~ STEPHEN E~ STEPHEN    JONES     DALL~ TX    Expert-~  26250     0
## 17 CRANE, D~ DANIEL LI~ DANIEL     CRANE     NEW ~ NY    Expert-~  25500     0
## 18 AXONZ INC STANLEY J  STANLEY    KROLCZYK  WESL~ FL    Expert-~  24750     0
## 19 MATTHEW ~ MATTHEW J~ MATTHEW    BUDOFF    MANH~ CA    Expert-~  24000     0
## 20 QUANG H ~ QUANG H    QUANG      NGUYEN    LA J~ CA    Expert-~  22500     0
## # ... with 1 more variable: total <dbl>

6.6 Example 6

Filter data Pfizer untuk semua pembayaran untuk menjalankan “Expert-Led Forums” atau “Professional Advising”, dan susun berdasarkan abjad dokter (nama belakang, lalu nama depan)

Expert_Professional_Advice <- Pfizer %>%
  filter(category == "Expert-Led Forums" | 
         category == "Professional Advising") %>%
  arrange(last_name, first_name)
Expert_Professional_Advice
## # A tibble: 4,382 x 10
##    org_indiv first_plus first_name last_name city  state category  cash other
##    <chr>     <chr>      <chr>      <chr>     <chr> <chr> <chr>    <dbl> <dbl>
##  1 ABBO, LI~ LILIAN MA~ LILIAN     ABBO      MIAMI FL    Profess~  1800     0
##  2 ABEBE, S~ SHEILA Y   SHEILA     ABEBE     INDI~ IN    Expert-~   825     0
##  3 NEW YORK~ JUDITH ANN JUDITH     ABERG     NEW ~ NY    Profess~  1750     0
##  4 ABOLNIK,~ IGOR Z     IGOR       ABOLNIK   PROVO UT    Expert-~  1750     0
##  5 ABRAKSIA~ SAMIR      SAMIR      ABRAKSIA  BEAC~ OH    Expert-~  2000     0
##  6 ABRAKSIA~ SAMIR      SAMIR      ABRAKSIA  BEAC~ OH    Profess~  2500     0
##  7 ABRAMSON~ STEVEN BA~ STEVEN     ABRAMSON  NEW ~ NY    Profess~  4400     0
##  8 ABUZZAHA~ FARUK S    FARUK      ABUZZAHAB MINN~ MN    Profess~  1750     0
##  9 ABUZZAHA~ MARY JENN~ MARY       ABUZZAHAB SAIN~ MN    Expert-~  1000     0
## 10 ACCACHA,~ SIHAM DON~ SIHAM      ACCACHA   MINE~ NY    Expert-~  1250     0
## # ... with 4,372 more rows, and 1 more variable: total <dbl>

7. Rename and Mutate

Pada bagian ini, Anda akan mempelajari bagaimana cara mengganti nama kolom pada sebuah Data Frame di R. Selanjutnya, Anda akan belajar cara menghitung dan menambahkan variabel baru dalam Data Frame di R. Anda akan belajar fungsi R dari paket dplyr R berikut :

  • rename() Kode ini digunakan untuk mengganti nama kolom dari sebuah Data Frame di R.
  • mutate() Menghitung dan menambah variabel baru kedalam Data Table. Ini mempertahankan variabel yang ada.
  • transmute() Menghitung kolom baru tetapi menghilangkan variabel yang ada.
# Mengubah nama kolom data `pfizer` dengan fungsi dasar R:
names(Pfizer)[names(Pfizer) == "org_indiv"] <- "rename1"
names(Pfizer)[1]                            <- "rename2"
names(Pfizer)[names(Pfizer) == names(Pfizer)] <- c("rename3",
                                                 "rename4",
                                                 "first_name",
                                                 "last_name",
                                                 "city",
                                                 "state",
                                                 "category",
                                                 "cash",
                                                 "other",
                                                 "total")
# Mengubah nama kolom data `pfizer` dengan packages `dplyr::rename()`:
Pfizer %>% 
  rename(
    org_indiv = rename3 ,
    first_plus = rename4  
    )
## # A tibble: 10,087 x 10
##    org_indiv first_plus first_name last_name city  state category  cash other
##    <chr>     <chr>      <chr>      <chr>     <chr> <chr> <chr>    <dbl> <dbl>
##  1 3-D MEDI~ STEVEN BR~ STEVEN     DEITELZW~ NEW ~ LA    Profess~  2625     0
##  2 AA DOCTO~ AAKASH MO~ AAKASH     AHUJA     PASO~ CA    Expert-~  1000     0
##  3 ABBO, LI~ LILIAN MA~ LILIAN     ABBO      MIAMI FL    Busines~     0   448
##  4 ABBO, LI~ LILIAN MA~ LILIAN     ABBO      MIAMI FL    Meals        0   119
##  5 ABBO, LI~ LILIAN MA~ LILIAN     ABBO      MIAMI FL    Profess~  1800     0
##  6 ABDULLAH~ ABDULLAH   ABDULLAH   RAFFEE    FLINT MI    Expert-~   750     0
##  7 ABEBE, S~ SHEILA Y   SHEILA     ABEBE     INDI~ IN    Educati~     0    47
##  8 ABEBE, S~ SHEILA Y   SHEILA     ABEBE     INDI~ IN    Expert-~   825     0
##  9 ABILENE ~ GALEN CHR~ GALEN      ALBRITTON ABIL~ TX    Profess~  3000     0
## 10 ABOLNIK,~ IGOR Z     IGOR       ABOLNIK   PROVO UT    Busines~     0   396
## # ... with 10,077 more rows, and 1 more variable: total <dbl>
# Menambahkan kolom baru (year*) dengan mempertahankan data `fda` yang ada :
Letters_Year <- Fda %>%
  mutate(year = format(issued, "%Y")) %>%
  group_by(year)
Letters_Year
## # A tibble: 272 x 6
## # Groups:   year [15]
##    name_last  name_first name_middle issued     office                     year 
##    <chr>      <chr>      <chr>       <date>     <chr>                      <chr>
##  1 ADELGLASS  JEFFREY    M.          1999-05-25 Center for Drug Evaluatio~ 1999 
##  2 ADKINSON   N.         FRANKLIN    2000-04-19 Center for Biologics Eval~ 2000 
##  3 ALLEN      MARK       S.          2002-01-28 Center for Devices and Ra~ 2002 
##  4 AMSTERDAM  DANIEL     <NA>        2004-11-17 Center for Biologics Eval~ 2004 
##  5 AMSTUTZ    HARLAN     C.          2004-07-19 Center for Devices and Ra~ 2004 
##  6 ANDERSON   C.         JOSEPH      2000-02-25 Center for Devices and Ra~ 2000 
##  7 ANDREWS    DAVID      W.          2000-07-19 Center for Biologics Eval~ 2000 
##  8 AQEL       RAED       <NA>        2002-10-30 Center for Devices and Ra~ 2002 
##  9 ARROWSMITH PETER      N.          2004-01-21 Center for Devices and Ra~ 2004 
## 10 BARR       JOHN       D.          2000-01-14 Center for Devices and Ra~ 2000 
## # ... with 262 more rows
# Menambahkan kolom baru (year*) dan (last_name*) dengan menghilangkan data yang ada `fda`:
Fda %>%
  transmute(
    year = format(issued, "%Y"),
    last_name = name_last  
    )
## # A tibble: 272 x 2
##    year  last_name 
##    <chr> <chr>     
##  1 1999  ADELGLASS 
##  2 2000  ADKINSON  
##  3 2002  ALLEN     
##  4 2004  AMSTERDAM 
##  5 2004  AMSTUTZ   
##  6 2000  ANDERSON  
##  7 2000  ANDREWS   
##  8 2002  AQEL      
##  9 2004  ARROWSMITH
## 10 2000  BARR      
## # ... with 262 more rows

8. Join Data

Terdapat juga sejumlah fungsi join atau gabungan dalam dplyr untuk menggabungkan data dari dua data frame. Berikut ini fungsi join yang sering digunakan:

  • inner_join() Mengembalikan nilai dari kedua tabel hanya jika ada yang sesuai.
  • left_join() Mengembalikan semua nilai yang disebutkan dari awal tabel, ditambah dari tabel kedua yang sesuai.
  • semi_join() Melakukan filter nilai yang disebutkan dari awal tabel hanya jika terdapat nilai yang sesuai dengan tabel kedua.
  • anti_join() Melakukan filter nilai yang disebutkan dari awal tabel hanya jika terdapat nilai yang tidak sesuai dengan tabel kedua.

Untuk ilustrasi, fungsi join ini akan menemukan dokter-dokter yang dibayar oleh pfizer untuk menjalankan forum yang dipimpin ahli (Expert-led forums) dimana juga telah menerima surat peringatan dari fda :

Expert_Warned_Inner <- inner_join(Pfizer, Fda, 
                                  by=c("first_name" = "name_first", 
                                       "last_name" = "name_last")) %>%
                                  filter(category == "Expert-Led Forums")

Expert_Warned_Semi <- semi_join(Pfizer, Fda, 
                                by=c("first_name" = "name_first", 
                                     "last_name" = "name_last")) %>%
                                filter(category == "Expert-Led Forums")

Kode pada by=c() mengartikan bagaimana join harus dibuat. Jika intruksi bagaimana menggabungkan tabel tidak disediakan, dplyr akan mencari kolom dengan nama yang cocok, dan menampilkan gabungannya berdasarkan itu. Perbedaan antara kedua join diatas yaitu pertama, inner_join mengandung semua kolom yang ada pada kedua data frame, sedangkan yang kedua, semi_join hanya memberi kolom dari data frame pfizer.

Pada praktiknya, Anda mungkin bisa menggunakan inner_join kemudian gunakan fungsi select() yang ada pada dplyr, untuk memilih kolom yang Anda ingin pertahankan, sebagai contoh :

Expert_Warned <- inner_join(Pfizer, Fda, 
                            by=c("first_name" = "name_first", 
                                 "last_name" = "name_last")) %>%
                            filter(category=="Expert-Led Forums") %>%
                            select(last_name, 
                                   city, 
                                   state, 
                                   total, 
                                   issued)

Expert_Warned <- inner_join(Pfizer, Fda, 
                            by=c("first_name" = "name_first",
                                 "last_name" = "name_last")) %>%
                            filter(category=="Expert-Led Forums") %>%
                            select(2:5,10,12)
Expert_Warned
## # A tibble: 4 x 6
##   rename4        first_name last_name city             total issued    
##   <chr>          <chr>      <chr>     <chr>            <dbl> <date>    
## 1 RONALD MATHEW  RONALD     BUKOWSKI  CLEVELAND        22500 2009-03-30
## 2 JEFFREY RONALD JEFFREY    LEVENSON  SAINT PETERSBURG  1500 2000-09-27
## 3 THOMAS DAVID   THOMAS     GAZDA     SCOTTSDALE        1000 2009-11-24
## 4 DOUGLAS JAMES  DOUGLAS    WARD      WASHINGTON        1500 1997-11-20

Klik disini untuk lebih banyak referensi yang berguna untuk mengelola fungsi join dengan dplyr.

9. Group and Summarize

Bagian ini menjelaskan bagaimana menghitung perhitungan statistik dengan mudah di R menggunakan package dplyr. Anda akan mempelajari :

  • Menghitung ringkasan statistik untuk data yang tidak dikelompokkan serta data yang dikelommpokkan dengan satu variabel atau lebih. Fungsi R nya: summarise() dan group_by().
  • Meringkaskan beberapa kolom variabel. Fungsi R nya:
  • summarise_all() Menerapkan fungsi summary ke setiap kolom pada Data Frame.
  • summarise_at() Menerapkan fungsi summary ke kolom yang lebih spesifik dengan karakter vektor.
  • summarise_if() Menerapkan fungsi summary ke kolom yang dipilih dengan fungsi predikat mengembalikan TRUE (BENAR).

9.1 Example 7

Hitung total pembayaran dari data pfizer, bagian negara dengan urutan menurun!

State_Sum <- Pfizer %>%
  group_by(state) %>%
  summarize(sum = sum(total)) %>%
  arrange(desc(sum))
## `summarise()` ungrouping output (override with `.groups` argument)
State_Sum
## # A tibble: 52 x 2
##    state     sum
##    <chr>   <dbl>
##  1 CA    4737807
##  2 TX    2802196
##  3 FL    2564047
##  4 PA    2484505
##  5 NC    2328435
##  6 NY    2065042
##  7 MA    1764771
##  8 IL    1256825
##  9 MI    1146285
## 10 OH    1019450
## # ... with 42 more rows

9.2 Example 8

Hitung beberapa tambahan ringkasan statistik dari pfizer data, menurut status secara menurun!

State_Summary <- Pfizer %>%
  group_by(state) %>%
  summarize(sum = sum(total), 
        average = mean(total), 
         median = median(total),
            min = min(total),
            max = max(total),
            count = n()) %>%
  arrange(desc(sum))
## `summarise()` ungrouping output (override with `.groups` argument)
State_Summary
## # A tibble: 52 x 7
##    state     sum average median   min     max count
##    <chr>   <dbl>   <dbl>  <dbl> <dbl>   <dbl> <int>
##  1 CA    4737807   4042.   886.    25  323122  1172
##  2 TX    2802196   4491.   802     25  479314   624
##  3 FL    2564047   3474.   669     25 1185466   738
##  4 PA    2484505   4661.   669     25  672263   533
##  5 NC    2328435   6079.   777      0  857698   383
##  6 NY    2065042   2479.   750     25  100000   833
##  7 MA    1764771   6814.  1250     29  617548   259
##  8 IL    1256825   2805.   661     25  160984   448
##  9 MI    1146285   3352.   750     26  152466   342
## 10 OH    1019450   2207.   685     25  107292   462
## # ... with 42 more rows

9.3 Example 9

Kelompokkan dan ringkaslah data pfizer untuk beberapa kategori secara naik!

State_Summary <- Pfizer %>%
  group_by(state, category) %>%
  summarize(sum = sum(total), 
        average = mean(total), 
         median = median(total),
            min = min(total),
            max = max(total),
            count = n()) %>%
  arrange(state, category)
## `summarise()` regrouping output by 'state' (override with `.groups` argument)
State_Summary
## # A tibble: 319 x 8
## # Groups:   state [52]
##    state category                          sum average median   min    max count
##    <chr> <chr>                           <dbl>   <dbl>  <dbl> <dbl>  <dbl> <int>
##  1 AK    Expert-Led Forums                1750  1.75e3 1.75e3  1750   1750     1
##  2 AL    Business Related Travel         29123  9.39e2 3.31e2    39   8041    31
##  3 AL    Educational Items                 431  6.16e1 6.70e1    47     78     7
##  4 AL    Expert-Led Forums              129850  4.06e3 2.00e3   550  26250    32
##  5 AL    Investigator-Initiated Resear~  55937  5.59e4 5.59e4 55937  55937     1
##  6 AL    Meals                           10217  2.69e2 1.86e2    26   1385    38
##  7 AL    Pfizer Sponsored Research ini~ 279485  1.40e5 1.40e5 46494 232991     2
##  8 AL    Pfizer Sponsored Research ini~  66096  6.61e4 6.61e4 66096  66096     1
##  9 AL    Professional Advising          110560  5.53e3 2.00e3   702  35000    20
## 10 AR    Business Related Travel          3783  2.70e2 1.41e2    27   1017    14
## # ... with 309 more rows

9.4 Example 10

Filter data fda untuk surat-surat yang dikirim dari awal tahun 2006 dan seterusnya dan ringkaslah!

Year_Summary <- Fda %>%
  filter(issued >= "2005-01-01") %>%
  arrange(issued) %>%
  mutate(year = format(issued, "%Y")) %>%
  group_by(year) %>%
  summarize(letters=n())
## `summarise()` ungrouping output (override with `.groups` argument)
Year_Summary
## # A tibble: 6 x 2
##   year  letters
##   <chr>   <int>
## 1 2005       31
## 2 2006       25
## 3 2007       11
## 4 2008       23
## 5 2009       25
## 6 2010        5

Peringatan : Menggunakan kembali variabel dapat menyebabkan hasil yang tidak diharapkan, tetapi jangan khawatir. R akan memberi peringatan seperti summarise() ungrouping output (override with .groups argument).

10. Split Data

Seperti yang telah disebutkan di bab Pemograman R tentang fungsi penulisan untuk membagi data pelatihan dan pengujian. Di sini, kita akan belajar lebih banyak tentang bagaimana menggunakan beberapa packages untuk membagi data. Karena bagian ini sangat penting bagi Data Scientist terutama saat Anda menggunakan Machine Learning untuk menganalisis data. Pada bagian ini biasanya kita perlu membagi Data Set antara Train Set dan Test Set. Train set memungkinkan algoritma untuk belajar dari data. Untuk menguji kinerja model kita, kita dapat menggunakan set pengujian untuk mengembalikan ukuran kinerja. Jadi, lihat beberapa package yang dapat Anda gunakan untuk pembagian data :

10.1 Dplyr

Anda bisa menggunakan dplyr untuk membuatnya menjadi sangat sederhana. Hal ini memang membutuhkan variabel id pada Data Set anda, yang merupakan ide yang bagus, tidak hanya untuk membuat set tetapi juga untuk keterlacakan selama proyek. Tambahkan jika belum ada didalam data.

library(dplyr)                                              # Memuat package `dplyr`
data(mtcars)                                                # Gunakan data dari R environment
set.seed(123)                                               # untuk memastikan kita menghasilkan data yang sama
mtcars$id <- 1:nrow(mtcars)                                 # Tambahkan `id` kedalam data jika belum ada
train     <- mtcars %>% dplyr::sample_frac(.75)             # Tetapkan train set
test      <- dplyr::anti_join(mtcars, train, by = 'id')     # Tetapkan test set
dim(train)                                                  # Cek dimensi dari Train Set
## [1] 24 12
dim(test)                                                   # Mengecek dimensi dari Test Set
## [1]  8 12

Catatan : Pada Data Set ini, misalnya, tidak cocok untuk tugas Machine Learning karena datanya terlalu kecil.

10.2 caTools

Ada banyak pendekatan untuk melakukan partisi data. Untuk pendekatan yang lebih lengkap, lihat fungsi createDataPartition dalam package caTools.

library(caTools)                                            # Memuat package `caTools`
## Warning: package 'caTools' was built under R version 4.0.3
data(mtcars)                                                # Gunakan data dari R environment
set.seed(123)                                               # untuk memastikan kita menghasilkan data yang sama
smp_size  <- floor(0.75 * nrow(mtcars))                     # 75% dari ukuran sampel
train_ind <- sample(seq_len(nrow(mtcars)), 
                    size = smp_size)
train     <- mtcars[train_ind,]                             # Tetapkan Train Set
test      <- mtcars[-train_ind,]                            # Tetapkan Test Set
dim(train)                                                  # Cek dimensi dari Train Set
## [1] 24 11
dim(test)                                                   # Cek dimensi dari Test Set
## [1]  8 11

10.3 Caret

Package keren lainnya yang dapat kita gunakan untuk Split atau pembagian data yaitu caret.

library(caret)                                    # Menjalankan package `caret`
## Warning: package 'caret' was built under R version 4.0.3
## Loading required package: lattice
## 
## Attaching package: 'caret'
## The following object is masked from 'package:purrr':
## 
##     lift
data(mtcars)                                      # Menggunakan data dari R
set.seed(123)                                     # Untuk memastikan kita menjalankan data yang sama
intrain<-createDataPartition(mtcars$mpg,
                             p=0.75,list=FALSE)
train<-mtcars[intrain,]
test<-mtcars[-intrain,]
dim(train)                                        # Mengecek dimensi dari Train Set
## [1] 25 11
dim(test)                                         # Mengecek dimensi dari Test Set
## [1]  7 11

More

LS0tDQp0aXRsZTogJ0xhYjEwIH4gRGF0YSBQcmVwYXJhdGlvbiB3aXRoIFInDQphdXRob3I6ICJTaWFuYSBEYXJtYSBQdXRyaSwgMjAxOTQ5MjAwMTIiDQpkYXRlOiAiYHIgZm9ybWF0KFN5cy5EYXRlKCksICclQiAlZCwgJVknKWAiDQpvdXRwdXQ6DQogIG9wZW5pbnRybzo6bGFiX3JlcG9ydDogZGVmYXVsdA0KICBodG1sX2RvY3VtZW50Og0KICAgIGhpZ2hsaWdodDogbW9ub2Nocm9tZQ0KICAgIHRoZW1lOiBzcGFjZWxhYg0KICAgIG51bWJlcl9zZWN0aW9uczogeWVzDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZmxvYXQ6IHllcw0KICAgIGNvZGVfZG93bmxvYWQgOiBOTw0KICAgIGNvZGVfZm9sZGluZzogaGlkZQ0KLS0tDQoNCmBgYHtyIExvZ28sIGVjaG89RkFMU0UsZmlnLmFsaWduPSdjZW50ZXInLCBvdXQud2lkdGggPSAnNDAlJ30NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJodHRwczovL2dpdGh1Yi5jb20vQmFrdGktU2lyZWdhci9pbWFnZXMvYmxvYi9tYXN0ZXIvbG9nby5wbmc/cmF3PXRydWUiKQ0KYGBgDQoNCioqKg0KDQpTYWxhaCBzYXR1IGtldGVyYW1waWxhbiB5YW5nIHBhbGluZyBtZW5kYXNhciBiYWdpIHNlb3JhbmcgRGF0YSBTY2llbnRpc3QgYWRhbGFoIFBlcnNpYXBhbiBEYXRhIChNZW1hbmlwdWxhc2kgRGF0YSkuIFVudHVrIG1lbmphZGkgYmVuYXIgLSBiZW5hciBlZmVrdGlmLCBBbmRhIGhhcnVzIG1lbmd1YXNhaSBkYWxhbSBtZW1hbmlwdWxhc2kgZGF0YSAtIGRhdGEgeWFuZyBwZW50aW5nLiBLYXJlbmEgc2ViYWdpYW4gYmVzYXIgcGVrZXJqYWFuIEFuZGEgaGFueWEgYWthbiBtZWxpYmF0a2FuIHBlbmdhbWJpbGFuIGRhbiBwZW1iZXJzaWhhbiBkYXRhLg0KDQpgYGB7ciBEYXRhLVByZXBhcmF0aW9uLCBlY2hvPUZBTFNFLCBmaWcuYWxpZ249J2NlbnRlcicsIG91dC53aWR0aD0nMTAwJSd9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiQzovVXNlcnMvU2lhbmEvRG93bmxvYWRzL0RhdGEtUHJlcGFyYXRpb24uanBlZyIpDQpgYGANCg0KRGF0YSBzY2llbnRpc3QgbWVuZ2hhYmlza2FuIDYwJSB3YWt0dW55YSB1bnR1ayBtZW1iZXJzaWhrYW4gZGFuIG1lcmFwaWthbiBkYXRhLiBNZW5ndW1wdWxrYW4gZGF0YXNldCB5YW5nIGJlcmFkYSBkaSB1cnV0YW4ga2VkdWEgZGVuZ2FuIDE5JSB3YWt0dW55YSwgeWFuZyBiZXJhcnRpIERhdGEgU2NpZW50aXN0IG1lbmdoYWJpc2thbiBzZWtpdGFyIDgwJSB3YWt0dW55YSB1bnR1ayBtZW55aWFwa2FuIGRhbiBtZW5nZWxvbGEgZGF0YSB1bnR1ayBtZW5nYW5hbGlzaXMuDQoNCkRpIGJhZ2lhbiBpbmksIEFuZGEgYWthbiBtZW1wZWxhamFyaSBiYWdhaW1hbmEgY2FyYSBtZWxha3VrYW4gcGVyc2lhcGFuIGRhdGEgZGVuZ2FuIG11ZGFoIG1lbmdndW5ha2FuIFIuIEthbWkgYWthbiBtZW1iYWhhcyBiZWJlcmFwYSBrYXRhIGtlcmphIGRhdGEgbWFuaXB1bGFzaSBtZW5kYXNhciB5YW5nIGFrYW4gc2VyaW5nIEFuZGEgZ3VuYWthbiBkYWxhbSBwZWtlcmphYW4uDQoNCiogYHJlYWRfY3N2KClgIE1lbmdpbXBvciBkYXRhIChhbmRhIGJpc2EgbWVuZ2lrdXRpIHlhbmcgbGFpbikNCiogYHN0cigpYCBTdHJ1a3R1ciBEYXRhDQoqIGBhcHBseSgpYCBVbnR1ayBtZW1lcmlrc2EgZGFuIG1lbmdnYW50aSBkYXRhIHlhbmcgaGlsYW5nDQoqIGBzZWxlY3QoKWAgTWVtaWxpaCBrb2x1bSBtYW5hIHlhbmcgYWthbiBkaW1hc3Vra2FuDQoqIGBmaWx0ZXIoKWAgTWVtaWxpaCBzdWJzZXQgZGFyaSBkYXRhIHlhbmcgZGl0ZW50dWthbg0KKiBgYXJyYW5nZSgpYCBNZW55dXN1biBkYXRhLCBkYXJpIHVrdXJhbiB2YXJpYWJlbCBrb250aW51LCBiZXJkYXNhcmthbiB0YW5nZ2FsLCBhdGF1IHNlY2FyYSBhbGZhYmV0DQoqIGByZW5hbWUoKWAgTWVuZ2dhbnRpIG5hbWEga29sb20NCiogYG11dGF0ZSgpYCBNZW1idWF0IGtvbG9tIGJhcnUgcGFkYSBkYXRhLCBhdGF1IG1lbmdnYW50aSBrb2xvbSB5YW5nIHN1ZGFoIGFkYQ0KKiBgYmluZF9yb3dzKClgIE1lbmdnYWJ1bmdrYW4gZHVhIGRhdGEgZnJhbWUgbWVuamFkaSBzYXR1LCBtZW5na29tYmluYXNpa2FuIGRhdGEgZGFyaSBrb2xvbSB5YW5nIG5hbWFueWEgc2FtYQ0KKiBgZ3JvdXBfYnkoKWAgTWVuZ2Vsb21wb2trYW4gZGF0YSBiZXJkYXNhcmthbiBrYXRlZ29yaSB2YXJpYWJlbG55YQ0KKiBgc3VtbWFyaXplKClgIE1lbnlpbXB1bGthbiwgYXRhdSBtZW5ndW1wdWxhbiBkYXRhICh1bnR1ayBtYXNpbmctbWFzaW5nIGdyb3VwIGppa2EgbWVuZ2d1bmFrYW4gZ3JvdXBfYnkpLiANCg0KICBCaWFzYSBkaWd1bmFrYW4gcGVuZ2h1YnVuZyBkZW5nYW4gYmViZXJhcGEgZnVuZ3NpIHRlcm1hc3VrIDoNCiAgDQogICogYG1lYW4oKWAgTWVuY2FyaSByYXRhIC0gcmF0YSBzZWJ1YWggZGF0YQ0KICAqIGBtZWRpYW4oKWAgTWVuY2FyaSBuaWxhaSB0ZW5nYWggc2VidWFoIGRhdGENCiAgKiBgbWF4KClgIE1lbmNhcmkgbmlsYWkgbWFrc2ltYWwgYXRhdSB0ZXJ0aW5nZ2kgc2VidWFoIGRhdGENCiAgKiBgbWluKClgIE1lbmNhcmkgbmlsYWkgbWluaW1hbCBhdGF1IHRlcmtlY2lsIHNlYnVhaCBkYXRhDQogICogYHN1bSgpYCBNZW5qdW1sYWhrYW4gYXRhdSB0b3RhbCBzZW11YSBuaWxhaSBzZWNhcmEgYmVyc2FtYWFuDQogICogYG4oKWAgTWVuZ2hpdHVuZyBhbmdrYSBwYWRhIGRhdGENCg0KU2F5YSBtZXJla29tZW5kYXNpa2FuIEFuZGEgdW50dWsgbWVuZ2luc3RhbGwgcGFja2FnZSBbYHRpZHl2ZXJzZWBdKGh0dHBzOi8vd3d3LnRpZHl2ZXJzZS5vcmcvcGFja2FnZXMvKS4gS2FyZW5hIGludGkgZGFyaSBgdGlkeXZlcnNlYCB0ZXJtYXN1ayBwYWNrYWdlIHlhbmcgc2VyaW5nIEFuZGEgZ3VuYWthbiBkaSBzZXRpYXAgbWVuZ2FuYWxpc2lzIGRhdGEuDQoNCmBgYA0KaW5zdGFsbC5wYWNrYWdlcygidGlkeXZlcnNlIikNCmBgYA0KDQpLaXRhIHRlcnV0YW1hIGFrYW4gYmVrZXJqYSBkZW5nYW4gZHVhIHBha2V0IHlhbmcgc2FuZ2F0IGJlcmd1bmEgeWFuZyBkaWtlbWJhbmdrYW4gb2xlaCBIYWRsZXkgV2lja2hhbSwga2VwYWxhIHNjaWVudGlzdCBkaSBSU3R1ZGlvIDoNCg0KKiBbYHJlYWRyYF0oaHR0cHM6Ly9yZWFkci50aWR5dmVyc2Uub3JnLykgdW50dWsgbWVtYmFjYSBkYW4gbWVudWxpcyBDU1YgZGFuIHRla3MgZmlsZSBsYWlubnlhLg0KKiBbYGRwbHlyYF0oaHR0cHM6Ly9kcGx5ci50aWR5dmVyc2Uub3JnLykgdW50dWsgbWVtcHJvc2VzIGRhbiBtZW1hbmlwdWxhc2kgZGF0YS4NCg0KIyMgMS4gSW1wb3J0IERhdGENCg0KRGF0YSB5YW5nIGFrYW4ga2l0YSBndW5ha2FuIHVudHVrIGJhZ2lhbiBpbmkgYWRhbGFoIGBwZml6ZXIuY3N2YCBkYW4gYGZkYS5jc3ZgLCBzaWxha2FuIGRvd25sb2FkIGRhbiBzaW1wYW4gZGkgZGVza3RvcCBBbmRhLiBTZWJhZ2FpIG9wc2lvbmFsLCBBbmRhIGRhcGF0IG1lbXVhdCBkYXRhIGtlIHNlc2kgUiBzYWF0IGluaSBkZW5nYW4gbWVtaWxpaCBgSW1wb3IgU2V0IERhdGE+IERhcmkgRmlsZSBUZWtzIC4uLmAgZGkgdGFiIEVudmlyb25tZW50LiBUYXBpLCBkYWxhbSBrYXN1cyBpbmksIGtpdGEgYWthbiBtZW5nZ3VuYWthbiBmdW5nc2kgYHJlYWRfY3N2ICgpYCBkYXJpIHBha2V0IGByZWFkcmAuIFNhbGluIGtvZGUgYmVyaWt1dCBrZSBkYWxhbSBza3JpcCBBbmRhIGRhbiBKYWxhbmthbiA6DQoNCmBgYHtyfQ0Kc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKGxpYnJhcnkodGlkeXZlcnNlKSkgICAgICAgICAgICAgICMgTWVtdWF0IHBhY2thZ2UgdGlkeXZlcnNlDQojc2V0d2QoIkM6L1VzZXJzL1NpYW5hL0Rvd25sb2FkcyIpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBJbmdhdCB1bnR1ayBtZW5nYXR1ciB3b3JraW5nIGRpcmVjdG9yeQ0KUGZpemVyIDwtIHJlYWRfY3N2KCJDOi9Vc2Vycy9TaWFuYS9Eb3dubG9hZHMvcGZpemVyLmNzdiIpICAgICAgICMgTWVtdWF0IGRhdGEgYHBmaXplcmAgDQpgYGANCg0KYGBge3J9DQpGZGEgPC0gcmVhZF9jc3YoIkM6L1VzZXJzL1NpYW5hL0Rvd25sb2Fkcy9mZGEuY3N2IikgICAgICAgICAgICAgICAjIE1lbXVhdCBkYXRhIGBmZGFgDQpgYGANCg0KIyMgMi4gRGF0YSBTdHJ1Y3R1cmVzDQoNClBlcmhhdGlrYW4gYmFod2EgQW5kYSBtZW1lcmx1a2FuIHBlbWFoYW1hbiB5YW5nIG1lbmRhbGFtIHRlbnRhbmcgamVuaXMgZGF0YSBkYXNhciBkYW4gc3RydWt0dXIgZGF0YSBzZXJ0YSBjYXJhIG1lbmdvcGVyYXNpa2FubnlhLiBGdW5nc2kgYHN0ciAoKWAgYWthbiBtZW1iZXJpIHRhaHUgbGViaWggYmFueWFrIHRlbnRhbmcga29sb20gZGFsYW0gZGF0YSwgdGVybWFzdWsgdGlwZSBkYXRhbnlhLiBTYWxpbiBrb2RlIGluaSBrZSBza3JpcCBkYW4gSmFsYW5rYW4gcGVyaW50YWhueWEgOg0KDQpgYGB7cn0NCnN0cihQZml6ZXIpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIE1lbGloYXQgc3RydWt0dXIgZGF0YSBkYXJpIGBwZml6ZXJgDQpgYGANCg0KYGBge3J9DQpzdHIoRmRhKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIE1lbGloYXQgc3RydWt0dXIgZGF0YSBkYXJpIGBmZGFgDQpgYGANCg0KSGFsIGluaSBzYW5nYXQgcGVudGluZyB1bnR1ayBkaSBtZW5nZXJ0aSBrYXJlbmEgaW5pIGFkYWxhaCBvYmplayB5YW5nIGFrYW4gYW5kYSBtYW5pcHVsYXNpa2FuIGRhcmkgaGFyaSBrZSBoYXJpIGRhbGFtIGJhc2lzIFIuIEppa2EgYW5kYSBwZXJsdSB1bnR1ayBtZW5ndWJhaCB0aXBlIGRhdGEgZGFyaSBrb2xvbSB0ZXJ0ZW50dSwgbWFrYSBndW5ha2FuIGZ1bmdzaSBiZXJpa3V0IDoNCg0KKiBgYXMuY2hhcmFjdGVyKClgIC0gTWVuZ3ViYWggbWVuamFkaSB0ZWtzIHN0cmluZy4NCiogYGFzLm51bWVyaWMoKWAgLSBNZW5ndWJhaCBtZW5qYWRpIG51bWVyaWsuDQoqIGBhcy5mYWN0b3IoKWAgLSBNZW5ndWJhaCBtZW5qYWRpIHZhcmlhYmVsIGthdGVnb3Jpa2FsLg0KKiBgYXMuaW50ZWdlcigpYCAtIE1lbmd1YmFoIG1lbmphZGkgaW50ZWdlci4NCiogYGFzLkRhdGUoKWAgLSBNZW5ndWJhaCBtZW5qYWRpIHRhbmdnYWwuDQoqIGBhcy5QT1NJWGN0KClgIC0gTWVuZ3ViYWggbWVuamFkaSB0YW5nZ2FsIGRhbiB3YWt0dSBzZWNhcmEgbGVuZ2thcC4NCg0KQ29udG9obnlhLCB0YW1iYWhrYW4ga29kZSBiZXJpa3V0IGtlIFIgc2tyaXAgdW50dWsgbWVuZ29udmVyc2kgdG90YWwgeWFuZyBkaWtvbnZlcnNpIGRhbGFtIGRhdGEgYHBmaXplcmAgbWVuamFkaSB2YXJpYWJlbCBudW1lcmlrICh5YW5nIGFrYW4gbWVtdW5na2lua2FubnlhIG1lbnlpbXBhbiBuaWxhaSBkZXNpbWFsIGppa2EgYWRhKS4NCg0KYGBge3J9DQpQZml6ZXIkdG90YWwgPC0gYXMubnVtZXJpYyhQZml6ZXIkdG90YWwpICAgICAgICAgICAgICAgICAgICAgICAgIyBNZW5na29udmVyc2kgVG90YWwga2UgdmFyaWFiZWwgbnVtZXJpaw0Kc3RyKFBmaXplciR0b3RhbCkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgTWFlbmdlY2VrIGtlbWJhbGkgc3RydWt0dXIgZGF0YW55YQ0KYGBgDQoNCiMjIDMuIE1pc3NpbmcgVmFsdWUNCg0KVGlkYWsgc2VwZXJ0aSBwZW1vZ3JhbWFuIGJpYXNhLCBzYWF0IGJla2VyamEgZGVuZ2FuIGRhdGEgZGkga2VoaWR1cGFuIG55YXRhLCBBbmRhIG11bmdraW4gbWVtaWxpa2kgbmlsYWkgeWFuZyBoaWxhbmcgOiBQZW5ndWt1cmFuIGRhdGEgeWFuZyB0aWRhayBkaXJla2FtL2Rpc2ltcGFuIGRsbC4gUiBtZW1pbGlraSBtZWthbmlzbWUgeWFuZyBjdWt1cCBjYW5nZ2loIHVudHVrIG1lbmdhdGFzaSBtaXNzaW5nIHZhbHVlLiBJbmkgZGliZWRha2FuIGFudGFyYSBiZXJpa3V0Og0KDQoqIGBOQWAgIDogRW50cmkgeWFuZyB0aWRhayB0ZXJzZWRpYSAobmlsYWkgTkEganVnYSBtZW1pbGlraSBrZWxhcywgamFkaSBhZGEgYmlsYW5nYW4gYnVsYXQgTkEsIGthcmFrdGVyIE5BLCBkbGwuKQ0KKiBgTmFOYCA6IEJ1a2FuIEFuZ2thIChuaWxhaSBOYU4ganVnYSBOQSB0ZXRhcGkga29udmVyc2lueWEgdGlkYWsgYmVuYXIpLg0KDQpNZW5jYXJpIG5taXNzaW5nIHZhbHVlIGRhcmkga29sb20gcGFkYSBkYXRhIGZyYW1lIGBwZml6ZXJgLg0KDQpgYGB7ciwgY2FjaGU9VFJVRSwgcmVzdWx0cz0naGlkZSd9DQppcy5uYShQZml6ZXIpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBDYXJhIGtsYXNpayB1bnR1ayBtZW1lcmlrc2EgYE5BYA0Kc3VtKGlzLm5hKFBmaXplcikpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgTWVuZ2hpdHVuZyB0b3RhbCBgTkFgDQphcHBseShpcy5uYShQZml6ZXIpLDIsIHdoaWNoKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBZYW5nIG1hbmEgaW5kZWtzIGBOQWAgKGhhbnlhIGBkZmApDQp3aGljaChjb21wbGV0ZS5jYXNlcyhQZml6ZXIpKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBNZW5naWRlbnRpZmlrYXNpIG5pbGFpIGxlbmdrYXAgeWFuZyBkaWFtYXRpDQpgYGANCg0KTWVrYW5pc21lIHlhbmcgbGViaWggdW11bSBhZGFsYWggbWVuZ2hhcHVzbnlhIHNlY2FyYSBtYW51YWwgOg0KDQpgYGB7cn0NCmNsZWFuLnZlY3RvciA8LSBuYS5vbWl0KFBmaXplciRmaXJzdF9uYW1lKSAgICAgICAgICAgICAgICAgICAgICAjIE1lbmdoYXB1cyB2ZWt0b3IgYE5BYA0KY2xlYW4uZGYgPC0gbmEub21pdChQZml6ZXIpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgTWVuZ2hhcHVzIGRhdGFmcmFtZSBgTkFgDQphcHBseShpcy5uYShjbGVhbi5kZiksMiwgd2hpY2gpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBNZW1hc3Rpa2FuIGppa2EgYWRhIGBNaXNzaW5nIFZhbHVlYA0KYGBgDQoNCiMjIDQuIFJlcGxhY2UgTWlzc2luZyBWYWx1ZQ0KDQpLaXRhIGp1Z2EgYmlzYSBtZW5nZ2FudGkgbmlsYWkgeWFuZyBoaWxhbmcgZGVuZ2FuIE1lYW4gKG1lZGlhbikuIFByYWt0aWsgeWFuZyBiYWlrIGFkYWxhaCBtZW1idWF0IGR1YSB2YXJpYWJlbCB0ZXJwaXNhaCB1bnR1ayBNZWFuLiBTZXRlbGFoIGRpYnVhdCwga2l0YSBkYXBhdCBtZW5nZ2FudGkgTWlzc2luZyBWYWx1ZSBkZW5nYW4gdmFyaWFiZWwgeWFuZyBiYXJ1IGRpYmVudHVrLiBTZWxhbmp1dG55YSB1cGxvYWQgZGF0YSBkYW4gdmVyaWZpa2FzaSBkYXRhIHlhbmcgaGlsYW5nLg0KDQpgYGB7cn0NClBBVEggPC0gImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9CYWt0aS1TaXJlZ2FyL2RhdGFzZXQvbWFzdGVyL0Jvb2tkb3duLURhdGEtU2NpZW5jZS1mb3ItQmVnaW5uZXJzL01pc3NpbmdfVmFsdWVzLmNzdiINClRpdGFuaWMgPC0gcmVhZC5jc3YoUEFUSCwgc2VwID0gIiwiKQ0KTGlzdF9OQSA8LSBjb2xuYW1lcyhUaXRhbmljKVsgYXBwbHkoVGl0YW5pYywgMiwgYW55TkEpIF0NCkxpc3RfTkENCmBgYA0KDQpEYWxhbSBrYXN1cyBpbmksIGtpdGEgdGlkYWsgbWVuZ2hhcHVzIHNlbXVhIE1pc3NpbmcgVmFsdWUsIHRldGFwaSBraXRhIG1lbmdndW5ha2FuIG1ldG9kZSBgYXBwbHkoKWAgdW50dWsgbWVuZ2hpdHVuZyByYXRhLXJhdGEga29sb20gZGVuZ2FuIGBOQWAuIFBlcnRhbWEsIGtpdGEgcGVybHUgbWVuZ2hpdHVuZyBNZWFuIGRlbmdhbiBhcmd1bWVuIGBuYS5ybSA9IFRSVUVgLiBBcmd1bWVuIGluaSB3YWppYiBrYXJlbmEga29sb20gbWVtaWxpa2kgZGF0YSB5YW5nIGhpbGFuZywgZGFuIGluaSBtZW1iZXJpdGFodSBSIHVudHVrIG1lbmdhYmFpa2FubnlhLg0KDQpgYGB7cn0NCkF2ZXJhZ2VfTWlzc2luZyA8LSBhcHBseShUaXRhbmljWyxjb2xuYW1lcyhUaXRhbmljKSAlaW4lIExpc3RfTkFdLA0KICAgICAgICAgICAgICAgICAgICAgICAgIDIsDQogICAgICAgICAgICAgICAgICAgICAgICAgbWVhbiwNCiAgICAgICAgICAgICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUpDQpBdmVyYWdlX01pc3NpbmcNCmBgYA0KDQoqKlBlbmplbGFzYW4gQ29kZSoqIDogS2l0YSBtZW1pbGlraSA0IGFyZ3VtZW4gZGFsYW0gbWV0b2RlIEFwcGx5Lg0KDQoqIGBkZmAgdGl0YW5pY1ssY29sbmFtZXMoVGl0YW5pYykgJWluJSBMaXN0X05hXS4gQ29kZSBpbmkgYWthbiBtZW5nZW1iYWxpa2FuIG5hbWEga29sb20gZGFyaSBvYmplayBMaXN0X05hICh5YWl0dSwg4oCcQWdl4oCdIGRhbiDigJxGYXJl4oCdKS4NCiogYDJgIE1lbmdoaXR1bmcgZnVuZ3NpIHBhZGEga29sb20uDQoqIGBNZWFuYCBNZW5naGl0dW5nIHJhdGEgLSByYXRhIGRhdGEuDQoqIGBuYS5ybWAgPSBUUlVFIGJlcmFydGkgbWVuZ2FiYWlrYW4gbmlsYWkgeWFuZyBoaWxhbmcuDQoNClNlbGFuanV0bnlhLCBraXRhIGRhcGF0IG1lbmdnYW50aSBuaWxhaSBgTkFgLiBLYXRhIGtlcmphIOKAmG11dGF0ZeKAmSBkYXJpIHB1c3Rha2EgYGRwbHlyYCBiZXJndW5hIGRhbGFtIG1lbWJ1YXQgdmFyaWFiZWwgYmFydS4gS2l0YSB0aWRhayBwZXJsdSBtZW5ndWJhaCBrb2xvbSBhc2xpIHNlaGluZ2dhIGtpdGEgZGFwYXQgbWVtYnVhdCB2YXJpYWJlbCBiYXJ1IHRhbnBhIGBOQWAuIOKAmG11dGF0ZeKAmSBtdWRhaCBkaWd1bmFrYW4sIGtpdGEgaGFueWEgcGVybHUgbWVtaWxpaCBuYW1hIHZhcmlhYmVsIGRhbiBtZW5lbnR1a2FuIGNhcmEgbWVtYnVhdCB2YXJpYWJlbG55YS4gQmVyaWt1dCBrb2RlIGxlbmdrYXBueWEgOg0KDQpgYGB7cn0NClRpdGFuaWNfUmVwbGFjZSA8LSBUaXRhbmljICU+JQ0KbXV0YXRlKGFnZSAgPSBpZmVsc2UoaXMubmEoQWdlKSwgQXZlcmFnZV9NaXNzaW5nWzFdLCBBZ2UpLA0KZmFyZSA9IGlmZWxzZShpcy5uYShGYXJlKSwgQXZlcmFnZV9NaXNzaW5nWzJdLCBGYXJlKSkNCnN1bShpcy5uYShUaXRhbmljX1JlcGxhY2UkQWdlKSkNCnN1bShpcy5uYShUaXRhbmljX1JlcGxhY2UkRmFyZSkpDQpzdW0oaXMubmEoVGl0YW5pY19SZXBsYWNlJGFnZSkpDQpzdW0oaXMubmEoVGl0YW5pY19SZXBsYWNlJGZhcmUpKQ0KYGBgDQoNClBhZGEga29sb20gdW11ciB5YW5nIG9yaWdpbmFsLCB0ZXJkYXBhdCA4NiBuaWxhaSB5YW5nIGhpbGFuZyBkaW1hbmEgcGFkYSB2YXJpYWJlbCB5YW5nIGJhcnUgZGlidWF0IG1lbmdnYW50aWthbm55YSBkZW5nYW4gTWVhbiBkYW4gdmFyaWFiZWwgYWdlLiBBbmRhIGRhcGF0IG1lbmNvYmEgc2VuZGlyaSB1bnR1ayBtZW5nZ2FudGkgb2JzZXJ2YXNpIHlhbmcgaGlsYW5nIGRlbmdhbiBNZWRpYW4ganVnYS4NCg0KIyMgNS4gU2VsZWN0IERhdGENCg0KUGFkYSBiYWdpYW4gaW5pLCBBbmRhIGFrYW4gbWVtcGVsYWphcmkgYmFnYWltYW5hIGNhcmEgbWVtaWxpaCBhdGF1IG1lbnN1YnNldCBrb2xvbSBkYXRhIGZyYW1lIGJlcmRhc2Fya2FuIE5hbWEgZGFuIFBvc2lzaSBtZW5nZ3VuYWthbiBmdW5nc2kgUiBgc2VsZWN0KClgIHBhZGEgcGFja2FnZSBbYGRwbHlyYF0oaHR0cHM6Ly9kcGx5ci50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS9zdW1tYXJpc2UuaHRtbCkuIEFuZGEgYWthbiBtZW1wZWxhamFyaSBjYXJhIG1lbmdndW5ha2FuIGZ1bmdzaSBmdW5nc2kgYmVyaWt1dCA6DQoNCiogYHB1bGwoKWAgICAgICAgICA6IE1lbmdla3N0cmFrIG5pbGFpIGtvbG9tIHNlYmFnYWkgdmVrdG9yLiBLb2xvbSBJbnRlcmVzdCBkYXBhdCBkaXRlbnR1a2FuIGJlcmRhc2Fya2FuIG5hbWEgYXRhdSBpbmRla3MuDQoqIGBzZWxlY3QoKWAgICAgICAgOiBNZW5nZWtzdHJhayBzYXR1IGF0YXUgYmViZXJhcGEga29sb20gc2ViYWdhaSBEYXRhIFRhYmVsLiBJbmkganVnYSBiaXNhIGRpZ3VuYWthbiB1bnR1ayBtZW5naGFwdXMga29sb20gZGFyaSBEYXRhIEZyYW1lLg0KKiBgc2VsZWN0X2lmKClgICAgIDogTWVtaWxpaCBrb2xvbSBiZXJkYXNhcmthbiBrb25kaXNpIHRlcnRlbnR1LiBGdW5nc2kgaW5pIGRhcGF0IGRpZ3VuYWthbiB1bnR1ayBtaXNhbG55YSBtZW1pbGloIGtvbG9tIGppa2EgYmVudHVrbnlhIG51bWVyaWsuDQoqICoqSGVscGVyIGZ1bmN0aW9ucyoqIDogYHN0YXJ0c193aXRoKClgLCBgZW5kc193aXRoKClgLCBgY29udGFpbnMoKWAsIGBtYXRjaGVzKClgIDogTWVtaWxpaCBrb2xvbSBhdGF1IHZhcmlhYmVsIGJlcmRhc2Fya2FuIG5hbWFueWEuDQoNCmBgYHtyfQ0KbGlicmFyeSh0aWR5dmVyc2UpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIE1lbmphbGFua2FuIGB0aWR5dmVyc2VgLCB5YW5nIHRlcm1hc3VrIGRhbGFtIGBkcGx5cmANClBmaXplciAlPiUgcHVsbChzdGF0ZSkgJT4lIGhlYWQoKSAgICAgICAgICAgICAgICAgIyBNZW5nZWtzdHJhayBuaWxhaSBrb2xvbSAnc3RhdGUnIHNlYmFnYWkgdmVrdG9yDQpQZml6ZXIgJT4lIHNlbGVjdCgxOjMpICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgTWVtaWxpaCBrb2xvbSAxIHNhbXBhaSAzDQpQZml6ZXIgJT4lIHNlbGVjdCgxLDMpICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgTWVtaWxpaCBrb2xvbSAxIGRhbiAzDQpQZml6ZXIgJT4lIHNlbGVjdChzdGF0ZTp0b3RhbCkgICAgICAgICAgICAgICAgICAgICMgTWVtaWxpaCBzZW11YSBrb2xvbSBkYXJpIGBzdGF0ZWAgc2FtcGFpIGB0b3RhbGANClBmaXplciAlPiUgc2VsZWN0KHN0YXRlLHRvdGFsKSAgICAgICAgICAgICAgICAgICAgIyBNZW1pbGloIGtvbG9tIGJlcnNhZGFya2FuIG5hbWEgdmFyaWFiZWxueWENClBmaXplciAlPiUgc2VsZWN0X2lmKGlzLm51bWVyaWMpICAgICAgICAgICAgICAgICAgIyBIYW55YSBtZW1pbGloIGtvbG9tIG51bWVyaWsNClBmaXplciAlPiUgc2VsZWN0X2lmKGlzLmNoYXJhY3RlcikgICAgICAgICAgICAgICAgIyBIYW55YSBtZW1pbGloIGtvbG9tIGNoYXJhY3RlciANClBmaXplciAlPiUgc2VsZWN0KHN0YXJ0c193aXRoKCJmaXJzdCIpKSAgICAgICAgICAgIyBNZW1pbGloIGtvbG9tIHlhbmcgZGltdWxhaSBkZW5nYW4gYGZpcnN0YA0KUGZpemVyICU+JSBzZWxlY3QoZW5kc193aXRoKCJuYW1lIikpICAgICAgICAgICAgICAjIE1lbWlsaWgga29sb20geWFuZyBkaWFraGlyaSBkZW5nYW4gYG5hbWVgDQpQZml6ZXIgJT4lIHNlbGVjdChjb250YWlucygicnN0IikpICAgICAgICAgICAgICAgICMgTWVtaWxpaCBrb2xvbSB5YW5nIG5hbWFueWEgbWVuY2FrdXAgYHJzdGANClBmaXplciAlPiUgc2VsZWN0KG1hdGNoZXMoIl8iKSkgICAgICAgICAgICAgICAgICAgIyBNZW1pbGloIGtvbG9tIHlhbmcgbmFtYW55YSBjb2NvayBkZW5nYW4geWFuZyBiaWFzYQ0KUGZpemVyICU+JSBzZWxlY3QoLShzdGF0ZTp0b3RhbCkpICAgICAgICAgICAgICAgICAjIE1lbmdoYXB1cyBzZW11YSBrb2xvbSBtdWxhaSBkYXJpIGBzdGF0ZWAgaGluZ2dhIGB0b3RhbGANClBmaXplciAlPiUgc2VsZWN0KC1zdGF0ZSwgLXRvdGFsKSAgICAgICAgICAgICAgICAgIyBNZW5naGFwdXMga29sb20gYHN0YXRlYCBkYW4gYHRvdGFsYA0KYGBgDQoNCiMjIDYuIEZpbHRlciBhbmQgU29ydCBEYXRhDQoNClNla2FyYW5nLCBraXRhIGFrYW4gYGZpbHRlcigpYCBkYW4gYGFycmFuZ2UoKWAgZGF0YSBkZW5nYW4gY2FyYSB5YW5nIHNwZXNpZmlrLiBVbnR1ayBzZXRpYXAgY29udG9oIGJlcmlrdXQsIHNhbGluIGNvZGUgYmVyaWt1dCBrZSBSIHNjcmlwdCBkYW4gbGloYXQgaGFzaWxueWEuIFBlcmhhdGlrYW4gYmFnYWltYW5hIGtpdGEgbWVtYnVhdCBvYmplayBiYXJ1IHVudHVrIG1lbmFtcHVuZyBkYXRhIHlhbmcgZGlwcm9zZXMuDQoNCiMjIyA2LjEgRXhhbXBsZSAxDQoNClRlbXVrYW4gZG9rdGVyIGRpIENhbGlmb3JuaWEgeWFuZyBkaWJheWFyICQxMCwwMDAgYXRhdSBsZWJpaCBvbGVoIFBmaXplciB1bnR1ayBtZW5qYWxhbmthbiDigJxQcm9mZXNzaW9uYWwgQWR2aXNpbmfigJ0hDQoNCmBgYHtyfQ0KQ0FfRXhwZXJ0XzEwMDAwIDwtIFBmaXplciAlPiUgICAgICAgICAgICAgICAgICAgICAjIE1lbmphbGFua2FuIHNlbXVhIGBwZml6ZXJgICAgIA0KICBmaWx0ZXIoc3RhdGUgPT0gIkNBIiAmICAgICAgICAgICAgICAgICAgICAgICAgICAjIE1lbmphbGFua2FuIHNlbXVhIGBwZml6ZXJgIHlhbmcgZGkgZmlsdGVyIGJlcmRhc2Fya2FuIGBzdGF0ZWANCiAgICAgICAgIHRvdGFsID49IDEwMDAwICYgICAgICAgICAgICAgICAgICAgICAgICAgIyBEaSBmaWx0ZXIgYmVyZGFzYXJrYW4gYHRvdGFsYCA+PSAxMDAwMA0KICAgICAgICAgY2F0ZWdvcnkgPT0gIlByb2Zlc3Npb25hbCBBZHZpc2luZyIpICAgICAjIExhbHUgZmlsdGVyIGJlcnNhZGFya2FuICdjYXRlZ29yeScNCkNBX0V4cGVydF8xMDAwMCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBNZW5wcmludCBoYXNpbG55YQ0KYGBgDQoNCiMjIyA2LjIgRXhhbXBsZSAyDQoNClNla2FyYW5nIHRhbWJhaGthbiBzb3J0IGRpIGFraGlyIGNvZGUgdW50dWsgbWVuZ3VydXRrYW4gZGFmdGFyIGRva3RlciBzZWNhcmEgbWVudXJ1biBiZXJkYXNhcmthbiBwZW1iYXlhcmFuIHlhbmcgZGl0ZXJpbWEhDQoNCmBgYHtyfQ0KQ0FfRXhwZXJ0XzEwMDAwIDwtIFBmaXplciAlPiUgICAgICAgICAgICAgICAgICAgICAjIE1lbmphbGFua2FuIHNlbXVhIGBwZml6ZXJgICAgIA0KICBmaWx0ZXIoc3RhdGUgPT0gIkNBIiAmICAgICAgICAgICAgICAgICAgICAgICAgICAjIE1lbWZpbHRlciBiZXJkYXNhcmthbiBgc3RhdGVgIGRpIENhbGlmb3JuaWENCiAgICAgICAgIHRvdGFsID49IDEwMDAwICYgICAgICAgICAgICAgICAgICAgICAgICAgIyBEaWZpbHRlciBiZXJkYXNhcmthbiBgdG90YWxgID49IDEwMDAwDQogICAgICAgICBjYXRlZ29yeSA9PSAiUHJvZmVzc2lvbmFsIEFkdmlzaW5nIiklPiUgICMgRGlmaWx0ZXIgbGFnaSBiZXJkYXNhcmthbiBgY2F0ZWdvcnlgDQogIGFycmFuZ2UoZGVzYyh0b3RhbCkpICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgTWVuZ3VydXRrYW4gb3JkZXIgYmVyZGFzYXJrYW4gcGVtYmF5YXJhbiB5YW5nIGRpdGVyaW1hIHNlY2FyYSBtZW51cnVuDQpDQV9FeHBlcnRfMTAwMDAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgTWVtcHJpbnQgaGFzaWxueWENCmBgYA0KDQojIyMgNi4zIEV4YW1wbGUgMw0KDQpUZW11a2FuIGRva3RlciBkaSBDYWxpZm9ybmlhIGF0YXUgTmV3eW9yayB5YW5nIGRpYmF5YXIgJDEwLjAwMCBhdGF1IGxlYmloIG1lbmdndW5ha2FuIFBmaXplciB1bnR1ayBtZW5qYWxhbmthbiDigJxQcm9mZXNzaW9uYWwgQWR2aXNpbmfigJ0hDQoNClBlcmhhdGlrYW4sIGRhbGFtIGhhbCBpbmkga2l0YSBtZW5nZ3VuYWthbiBgfGAgT3BlcmF0b3IgQm9vbGVhbiwgZGFuIHRhbmRhIGt1cnVuZyBkaSBzZWtpdGFyIGJhZ2lhbiBxdWVyeW55YS4gSW5pIG1lbWFzdGlrYW4gYmFod2EgYmFnaWFuIFF1ZXJ5IGluaSBkaWphbGFua2FuIHBlcnRhbWEga2FsaS4gTGloYXQgYXBhIHlhbmcgdGVyamFkaSBqaWthIEFuZGEgbWVuZ2VjdWFsaWthbiBtZXJla2EuDQoNCmBgYHtyfQ0KQ0FfTllfRXhwZXJ0XzEwMDAwIDwtIFBmaXplciAlPiUgICAgICAgICAgICAgICAgICAgICMgTWVuamFsYW5rYW4gc2VtdWEgYHBmaXplcmAgICAgDQogIGZpbHRlcigoc3RhdGUgPT0gIkNBIiB8IHN0YXRlID09ICJOWSIpICAmICAgICAgICAgIyBEaWZpbHRlciBiZXJkYXNhcmthbiBgc3RhdGVgIENhbGlmb3JuaWEgYXRhdSBOZXcgWW9yaw0KICAgICAgICAgdG90YWwgPj0gMTAwMDAgJiAgICAgICAgICAgICAgICAgICAgICAgICAgICMgRGlmaWx0ZXIgYmVyZGFzYXJrYW4gYHRvdGFsYCBzYW1hIGRlbmdhbi8gbGViaWggZGFyaSAxMDAwMA0KICAgICAgICAgY2F0ZWdvcnkgPT0gIlByb2Zlc3Npb25hbCBBZHZpc2luZyIpICU+JSAgICMgRGlmaWx0ZXIgbGFnaSBiZXJkYXNhcmthbiBgY2F0ZWdvcnlgDQogIGFycmFuZ2UoZGVzYyh0b3RhbCkpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBNZW5ndXJ1dGthbiBvcmRlciBiZXJkYXNhcmthbiBwZW1iYXlhcmFuIHlhbmcgZGl0ZXJpbWEgc2VjYXJhIG1lbnVydW4NCkNBX05ZX0V4cGVydF8xMDAwMCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIE1lbXByaW50IGhhc2lsbnlhDQpgYGANCg0KIyMjIDYuNCBFeGFtcGxlIDQNCg0KVGVtdWthbiBkb2t0ZXIgZGkgbmVnYXJhIGJhZ2lhbiAqKnNlbGFpbioqIENhbGlmb3JuaWEgeWFuZyBkaWJheWFyICQxMCwwMDAgYXRhdSBsZWJpaCBiZXJkYXNhcmthbiBQZml6ZXIgdW50dWsgbWVuamFsYW5rYW4g4oCcUHJvZmVzc2lvbmFsIEFkdmlzaW5n4oCdIQ0KDQpgYGB7cn0NCk5vdF9DQV9FeHBlcnRfMTAwMDAgPC0gUGZpemVyICU+JSAgICAgICAgICAgICAgICAgICAjIE1lbmphbGFua2FuIHNlbXVhIGBwZml6ZXJgICAgDQogIGZpbHRlcihzdGF0ZSAhPSAiQ0EiICYgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBGaWx0ZXIgYmVyZGFzYXJrYW4gYHN0YXRlYCBzZWxhaW4gQ2FsaWZvcm5pYQ0KICAgICAgICAgdG90YWwgPj0gMTAwMDAgJiAgICAgICAgICAgICAgICAgICAgICAgICAgICMgRGlmaWx0ZXIgYmVyZGFzYXJrYW4gYHRvdGFsYCA+PSBkYXJpIDEwMDAwDQogICAgICAgICBjYXRlZ29yeSA9PSAiUHJvZmVzc2lvbmFsIEFkdmlzaW5nIikgJT4lICAgIyBEaWZpbHRlciBsYWdpIGJlcmRhc2Fya2FuIGBjYXRlZ29yeWANCiAgYXJyYW5nZShkZXNjKHRvdGFsKSkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIE1lbmd1cnV0a2FuIG9yZGVyIGJlcmRhc2Fya2FuIHBlbWJheWFyYW4geWFuZyBkaXRlcmltYSBzZWNhcmEgbWVudXJ1bg0KTm90X0NBX0V4cGVydF8xMDAwMCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgTWVtcHJpbnQgaGFzaWxueWENCmBgYA0KDQojIyMgNi41IEV4YW1wbGUgNQ0KDQpUZW11a2FuIDIwIGRva3RlciB5YW5nIGFkYSBkaSA0IG5lZ2FyYSBiYWdpYW4gdGVyYmVzYXIgKiooQ0EsIFRYLCBGTCwgTlkpKiogeWFuZyBkaWJheWFyIHRlcmJhbnlhayB1bnR1ayDigJxFeHBlcnQtTGVkIEZvcnVtc+KAnSENCg0KYGBge3J9DQpGb3VyQ291bnRyeV9Qcm9mX1RvcDIwIDwtIFBmaXplciAlPiUNCiAgZmlsdGVyKChzdGF0ZSA9PSAiQ0EiIHwgDQogICAgICAgICAgc3RhdGUgPT0gIk5ZIiB8DQogICAgICAgICAgc3RhdGUgPT0gIlRYIiB8IA0KICAgICAgICAgIHN0YXRlID09ICJGTCIpICYNCiAgICAgICAgICBjYXRlZ29yeSA9PSAiRXhwZXJ0LUxlZCBGb3J1bXMiKSAlPiUNCiAgYXJyYW5nZShkZXNjKHRvdGFsKSkgJT4lDQogIGhlYWQoMjApDQpGb3VyQ291bnRyeV9Qcm9mX1RvcDIwDQpgYGANCg0KIyMjIDYuNiBFeGFtcGxlIDYNCg0KRmlsdGVyIGRhdGEgYFBmaXplcmAgdW50dWsgc2VtdWEgcGVtYmF5YXJhbiB1bnR1ayBtZW5qYWxhbmthbiAqKuKAnEV4cGVydC1MZWQgRm9ydW1z4oCdKiogYXRhdSAqKuKAnFByb2Zlc3Npb25hbCBBZHZpc2luZ+KAnSoqLCBkYW4gc3VzdW4gYmVyZGFzYXJrYW4gYWJqYWQgZG9rdGVyIChuYW1hIGJlbGFrYW5nLCBsYWx1IG5hbWEgZGVwYW4pDQoNCmBgYHtyfQ0KRXhwZXJ0X1Byb2Zlc3Npb25hbF9BZHZpY2UgPC0gUGZpemVyICU+JQ0KICBmaWx0ZXIoY2F0ZWdvcnkgPT0gIkV4cGVydC1MZWQgRm9ydW1zIiB8IA0KICAgICAgICAgY2F0ZWdvcnkgPT0gIlByb2Zlc3Npb25hbCBBZHZpc2luZyIpICU+JQ0KICBhcnJhbmdlKGxhc3RfbmFtZSwgZmlyc3RfbmFtZSkNCkV4cGVydF9Qcm9mZXNzaW9uYWxfQWR2aWNlDQpgYGANCg0KIyMgNy4gUmVuYW1lIGFuZCBNdXRhdGUNCg0KUGFkYSBiYWdpYW4gaW5pLCBBbmRhIGFrYW4gbWVtcGVsYWphcmkgYmFnYWltYW5hIGNhcmEgbWVuZ2dhbnRpIG5hbWEga29sb20gcGFkYSBzZWJ1YWggRGF0YSBGcmFtZSBkaSBSLiBTZWxhbmp1dG55YSwgQW5kYSBha2FuIGJlbGFqYXIgY2FyYSBtZW5naGl0dW5nIGRhbiBtZW5hbWJhaGthbiB2YXJpYWJlbCBiYXJ1IGRhbGFtIERhdGEgRnJhbWUgZGkgUi4gQW5kYSBha2FuIGJlbGFqYXIgZnVuZ3NpIFIgZGFyaSBwYWtldCBkcGx5ciBSIGJlcmlrdXQgOg0KDQoqIGByZW5hbWUoKWAgS29kZSBpbmkgZGlndW5ha2FuIHVudHVrIG1lbmdnYW50aSBuYW1hIGtvbG9tIGRhcmkgc2VidWFoIERhdGEgRnJhbWUgZGkgUi4NCiogYG11dGF0ZSgpYCBNZW5naGl0dW5nIGRhbiBtZW5hbWJhaCB2YXJpYWJlbCBiYXJ1IGtlZGFsYW0gRGF0YSBUYWJsZS4gSW5pIG1lbXBlcnRhaGFua2FuIHZhcmlhYmVsIHlhbmcgYWRhLg0KKiBgdHJhbnNtdXRlKClgIE1lbmdoaXR1bmcga29sb20gYmFydSB0ZXRhcGkgbWVuZ2hpbGFuZ2thbiB2YXJpYWJlbCB5YW5nIGFkYS4NCg0KYGBge3J9DQojIE1lbmd1YmFoIG5hbWEga29sb20gZGF0YSBgcGZpemVyYCBkZW5nYW4gZnVuZ3NpIGRhc2FyIFI6DQpuYW1lcyhQZml6ZXIpW25hbWVzKFBmaXplcikgPT0gIm9yZ19pbmRpdiJdIDwtICJyZW5hbWUxIg0KbmFtZXMoUGZpemVyKVsxXSAgICAgICAgICAgICAgICAgICAgICAgICAgICA8LSAicmVuYW1lMiINCm5hbWVzKFBmaXplcilbbmFtZXMoUGZpemVyKSA9PSBuYW1lcyhQZml6ZXIpXSA8LSBjKCJyZW5hbWUzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicmVuYW1lNCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImZpcnN0X25hbWUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsYXN0X25hbWUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJjaXR5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic3RhdGUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJjYXRlZ29yeSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImNhc2giLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvdGhlciIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInRvdGFsIikNCmBgYA0KDQpgYGB7cn0NCiMgTWVuZ3ViYWggbmFtYSBrb2xvbSBkYXRhIGBwZml6ZXJgIGRlbmdhbiBwYWNrYWdlcyBgZHBseXI6OnJlbmFtZSgpYDoNClBmaXplciAlPiUgDQogIHJlbmFtZSgNCiAgICBvcmdfaW5kaXYgPSByZW5hbWUzICwNCiAgICBmaXJzdF9wbHVzID0gcmVuYW1lNCAgDQogICAgKQ0KYGBgDQoNCmBgYHtyfQ0KIyBNZW5hbWJhaGthbiBrb2xvbSBiYXJ1ICh5ZWFyKikgZGVuZ2FuIG1lbXBlcnRhaGFua2FuIGRhdGEgYGZkYWAgeWFuZyBhZGEgOg0KTGV0dGVyc19ZZWFyIDwtIEZkYSAlPiUNCiAgbXV0YXRlKHllYXIgPSBmb3JtYXQoaXNzdWVkLCAiJVkiKSkgJT4lDQogIGdyb3VwX2J5KHllYXIpDQpMZXR0ZXJzX1llYXINCmBgYA0KDQpgYGB7cn0NCiMgTWVuYW1iYWhrYW4ga29sb20gYmFydSAoeWVhciopIGRhbiAobGFzdF9uYW1lKikgZGVuZ2FuIG1lbmdoaWxhbmdrYW4gZGF0YSB5YW5nIGFkYSBgZmRhYDoNCkZkYSAlPiUNCiAgdHJhbnNtdXRlKA0KICAgIHllYXIgPSBmb3JtYXQoaXNzdWVkLCAiJVkiKSwNCiAgICBsYXN0X25hbWUgPSBuYW1lX2xhc3QgIA0KICAgICkNCmBgYA0KDQojIyA4LiBKb2luIERhdGENCg0KVGVyZGFwYXQganVnYSBzZWp1bWxhaCBmdW5nc2kgam9pbiBhdGF1IGdhYnVuZ2FuIGRhbGFtIGBkcGx5cmAgdW50dWsgbWVuZ2dhYnVuZ2thbiBkYXRhIGRhcmkgZHVhIGRhdGEgZnJhbWUuIEJlcmlrdXQgaW5pIGZ1bmdzaSBqb2luIHlhbmcgc2VyaW5nIGRpZ3VuYWthbjoNCg0KKiBgaW5uZXJfam9pbigpYCBNZW5nZW1iYWxpa2FuIG5pbGFpIGRhcmkga2VkdWEgdGFiZWwgaGFueWEgamlrYSBhZGEgeWFuZyBzZXN1YWkuDQoqIGBsZWZ0X2pvaW4oKWAgTWVuZ2VtYmFsaWthbiBzZW11YSBuaWxhaSB5YW5nIGRpc2VidXRrYW4gZGFyaSBhd2FsIHRhYmVsLCBkaXRhbWJhaCBkYXJpIHRhYmVsIGtlZHVhIHlhbmcgc2VzdWFpLg0KKiBgc2VtaV9qb2luKClgIE1lbGFrdWthbiBmaWx0ZXIgbmlsYWkgeWFuZyBkaXNlYnV0a2FuIGRhcmkgYXdhbCB0YWJlbCBoYW55YSBqaWthIHRlcmRhcGF0IG5pbGFpIHlhbmcgc2VzdWFpIGRlbmdhbiB0YWJlbCBrZWR1YS4NCiogYGFudGlfam9pbigpYCBNZWxha3VrYW4gZmlsdGVyIG5pbGFpIHlhbmcgZGlzZWJ1dGthbiBkYXJpIGF3YWwgdGFiZWwgaGFueWEgamlrYSB0ZXJkYXBhdCBuaWxhaSB5YW5nIHRpZGFrIHNlc3VhaSBkZW5nYW4gdGFiZWwga2VkdWEuDQoNClVudHVrIGlsdXN0cmFzaSwgZnVuZ3NpIGpvaW4gaW5pIGFrYW4gbWVuZW11a2FuIGRva3Rlci1kb2t0ZXIgeWFuZyBkaWJheWFyIG9sZWggYHBmaXplcmAgdW50dWsgbWVuamFsYW5rYW4gZm9ydW0geWFuZyBkaXBpbXBpbiBhaGxpIChFeHBlcnQtbGVkIGZvcnVtcykgZGltYW5hIGp1Z2EgdGVsYWggbWVuZXJpbWEgc3VyYXQgcGVyaW5nYXRhbiBkYXJpIGBmZGFgIDoNCg0KYGBge3J9DQpFeHBlcnRfV2FybmVkX0lubmVyIDwtIGlubmVyX2pvaW4oUGZpemVyLCBGZGEsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5PWMoImZpcnN0X25hbWUiID0gIm5hbWVfZmlyc3QiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsYXN0X25hbWUiID0gIm5hbWVfbGFzdCIpKSAlPiUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIoY2F0ZWdvcnkgPT0gIkV4cGVydC1MZWQgRm9ydW1zIikNCg0KRXhwZXJ0X1dhcm5lZF9TZW1pIDwtIHNlbWlfam9pbihQZml6ZXIsIEZkYSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5PWMoImZpcnN0X25hbWUiID0gIm5hbWVfZmlyc3QiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibGFzdF9uYW1lIiA9ICJuYW1lX2xhc3QiKSkgJT4lDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRlcihjYXRlZ29yeSA9PSAiRXhwZXJ0LUxlZCBGb3J1bXMiKQ0KYGBgDQoNCktvZGUgcGFkYSBgYnk9YygpYCBtZW5nYXJ0aWthbiBiYWdhaW1hbmEgam9pbiBoYXJ1cyBkaWJ1YXQuIEppa2EgaW50cnVrc2kgYmFnYWltYW5hIG1lbmdnYWJ1bmdrYW4gdGFiZWwgdGlkYWsgZGlzZWRpYWthbiwgYGRwbHlyYCBha2FuIG1lbmNhcmkga29sb20gZGVuZ2FuIG5hbWEgeWFuZyBjb2NvaywgZGFuIG1lbmFtcGlsa2FuIGdhYnVuZ2FubnlhIGJlcmRhc2Fya2FuIGl0dS4gUGVyYmVkYWFuIGFudGFyYSBrZWR1YSBqb2luIGRpYXRhcyB5YWl0dSBwZXJ0YW1hLCBgaW5uZXJfam9pbmAgbWVuZ2FuZHVuZyBzZW11YSBrb2xvbSB5YW5nIGFkYSBwYWRhIGtlZHVhIGRhdGEgZnJhbWUsIHNlZGFuZ2thbiB5YW5nIGtlZHVhLCBgc2VtaV9qb2luYCBoYW55YSBtZW1iZXJpIGtvbG9tIGRhcmkgZGF0YSBmcmFtZSBwZml6ZXIuDQoNClBhZGEgcHJha3Rpa255YSwgQW5kYSBtdW5na2luIGJpc2EgbWVuZ2d1bmFrYW4gaW5uZXJfam9pbiBrZW11ZGlhbiBndW5ha2FuIGZ1bmdzaSBzZWxlY3QoKSB5YW5nIGFkYSBwYWRhIGRwbHlyLCB1bnR1ayBtZW1pbGloIGtvbG9tIHlhbmcgQW5kYSBpbmdpbiBwZXJ0YWhhbmthbiwgc2ViYWdhaSBjb250b2ggOg0KDQpgYGB7cn0NCkV4cGVydF9XYXJuZWQgPC0gaW5uZXJfam9pbihQZml6ZXIsIEZkYSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgYnk9YygiZmlyc3RfbmFtZSIgPSAibmFtZV9maXJzdCIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxhc3RfbmFtZSIgPSAibmFtZV9sYXN0IikpICU+JQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRlcihjYXRlZ29yeT09IkV4cGVydC1MZWQgRm9ydW1zIikgJT4lDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KGxhc3RfbmFtZSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNpdHksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdGF0ZSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvdGFsLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaXNzdWVkKQ0KDQpFeHBlcnRfV2FybmVkIDwtIGlubmVyX2pvaW4oUGZpemVyLCBGZGEsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5PWMoImZpcnN0X25hbWUiID0gIm5hbWVfZmlyc3QiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxhc3RfbmFtZSIgPSAibmFtZV9sYXN0IikpICU+JQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRlcihjYXRlZ29yeT09IkV4cGVydC1MZWQgRm9ydW1zIikgJT4lDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KDI6NSwxMCwxMikNCkV4cGVydF9XYXJuZWQNCmBgYA0KDQpbS2xpayBkaXNpbmldKGh0dHBzOi8vd3d3Lmd1cnU5OS5jb20vci1kcGx5ci10dXRvcmlhbC5odG1sKSB1bnR1ayBsZWJpaCBiYW55YWsgcmVmZXJlbnNpIHlhbmcgYmVyZ3VuYSB1bnR1ayBtZW5nZWxvbGEgZnVuZ3NpIGpvaW4gZGVuZ2FuIGRwbHlyLg0KDQojIyA5LiBHcm91cCBhbmQgU3VtbWFyaXplDQoNCkJhZ2lhbiBpbmkgbWVuamVsYXNrYW4gYmFnYWltYW5hIG1lbmdoaXR1bmcgcGVyaGl0dW5nYW4gc3RhdGlzdGlrIGRlbmdhbiBtdWRhaCBkaSBSIG1lbmdndW5ha2FuIHBhY2thZ2UgZHBseXIuIEFuZGEgYWthbiBtZW1wZWxhamFyaSA6DQoNCiogTWVuZ2hpdHVuZyByaW5na2FzYW4gc3RhdGlzdGlrIHVudHVrIGRhdGEgeWFuZyB0aWRhayBkaWtlbG9tcG9ra2FuIHNlcnRhIGRhdGEgeWFuZyBkaWtlbG9tbXBva2thbiBkZW5nYW4gc2F0dSB2YXJpYWJlbCBhdGF1IGxlYmloLiBGdW5nc2kgUiBueWE6IGBzdW1tYXJpc2UoKWAgZGFuIGBncm91cF9ieSgpYC4NCiogTWVyaW5na2Fza2FuIGJlYmVyYXBhIGtvbG9tIHZhcmlhYmVsLiBGdW5nc2kgUiBueWE6DQogKiBgc3VtbWFyaXNlX2FsbCgpYCBNZW5lcmFwa2FuIGZ1bmdzaSBzdW1tYXJ5IGtlIHNldGlhcCBrb2xvbSBwYWRhIERhdGEgRnJhbWUuDQogKiBgc3VtbWFyaXNlX2F0KClgIE1lbmVyYXBrYW4gZnVuZ3NpIHN1bW1hcnkga2Uga29sb20geWFuZyBsZWJpaCBzcGVzaWZpayBkZW5nYW4ga2FyYWt0ZXIgdmVrdG9yLg0KICogYHN1bW1hcmlzZV9pZigpYCBNZW5lcmFwa2FuIGZ1bmdzaSBzdW1tYXJ5IGtlIGtvbG9tIHlhbmcgZGlwaWxpaCBkZW5nYW4gZnVuZ3NpIHByZWRpa2F0IG1lbmdlbWJhbGlrYW4gKipUUlVFIChCRU5BUikqKi4NCiANCiMjIyA5LjEgRXhhbXBsZSA3DQoNCkhpdHVuZyB0b3RhbCBwZW1iYXlhcmFuIGRhcmkgZGF0YSBgcGZpemVyYCwgYmFnaWFuIG5lZ2FyYSBkZW5nYW4gdXJ1dGFuIG1lbnVydW4hDQoNCmBgYHtyfQ0KU3RhdGVfU3VtIDwtIFBmaXplciAlPiUNCiAgZ3JvdXBfYnkoc3RhdGUpICU+JQ0KICBzdW1tYXJpemUoc3VtID0gc3VtKHRvdGFsKSkgJT4lDQogIGFycmFuZ2UoZGVzYyhzdW0pKQ0KU3RhdGVfU3VtDQpgYGANCg0KIyMjIDkuMiBFeGFtcGxlIDgNCg0KSGl0dW5nIGJlYmVyYXBhIHRhbWJhaGFuIHJpbmdrYXNhbiBzdGF0aXN0aWsgZGFyaSBgcGZpemVyYCBkYXRhLCBtZW51cnV0IHN0YXR1cyBzZWNhcmEgbWVudXJ1biENCg0KYGBge3J9DQpTdGF0ZV9TdW1tYXJ5IDwtIFBmaXplciAlPiUNCiAgZ3JvdXBfYnkoc3RhdGUpICU+JQ0KICBzdW1tYXJpemUoc3VtID0gc3VtKHRvdGFsKSwgDQogICAgICAgIGF2ZXJhZ2UgPSBtZWFuKHRvdGFsKSwgDQogICAgICAgICBtZWRpYW4gPSBtZWRpYW4odG90YWwpLA0KICAgICAgICAgICAgbWluID0gbWluKHRvdGFsKSwNCiAgICAgICAgICAgIG1heCA9IG1heCh0b3RhbCksDQogICAgICAgICAgICBjb3VudCA9IG4oKSkgJT4lDQogIGFycmFuZ2UoZGVzYyhzdW0pKQ0KU3RhdGVfU3VtbWFyeQ0KYGBgDQoNCiMjIyA5LjMgRXhhbXBsZSA5DQoNCktlbG9tcG9ra2FuIGRhbiByaW5na2FzbGFoIGRhdGEgYHBmaXplcmAgdW50dWsgYmViZXJhcGEga2F0ZWdvcmkgc2VjYXJhIG5haWshDQoNCmBgYHtyfQ0KU3RhdGVfU3VtbWFyeSA8LSBQZml6ZXIgJT4lDQogIGdyb3VwX2J5KHN0YXRlLCBjYXRlZ29yeSkgJT4lDQogIHN1bW1hcml6ZShzdW0gPSBzdW0odG90YWwpLCANCiAgICAgICAgYXZlcmFnZSA9IG1lYW4odG90YWwpLCANCiAgICAgICAgIG1lZGlhbiA9IG1lZGlhbih0b3RhbCksDQogICAgICAgICAgICBtaW4gPSBtaW4odG90YWwpLA0KICAgICAgICAgICAgbWF4ID0gbWF4KHRvdGFsKSwNCiAgICAgICAgICAgIGNvdW50ID0gbigpKSAlPiUNCiAgYXJyYW5nZShzdGF0ZSwgY2F0ZWdvcnkpDQpTdGF0ZV9TdW1tYXJ5DQpgYGANCg0KIyMjIDkuNCBFeGFtcGxlIDEwDQoNCkZpbHRlciBkYXRhIGBmZGFgIHVudHVrIHN1cmF0LXN1cmF0IHlhbmcgZGlraXJpbSBkYXJpIGF3YWwgdGFodW4gMjAwNiBkYW4gc2V0ZXJ1c255YSBkYW4gcmluZ2thc2xhaCENCg0KYGBge3J9DQpZZWFyX1N1bW1hcnkgPC0gRmRhICU+JQ0KICBmaWx0ZXIoaXNzdWVkID49ICIyMDA1LTAxLTAxIikgJT4lDQogIGFycmFuZ2UoaXNzdWVkKSAlPiUNCiAgbXV0YXRlKHllYXIgPSBmb3JtYXQoaXNzdWVkLCAiJVkiKSkgJT4lDQogIGdyb3VwX2J5KHllYXIpICU+JQ0KICBzdW1tYXJpemUobGV0dGVycz1uKCkpDQpZZWFyX1N1bW1hcnkNCmBgYA0KDQoqKlBlcmluZ2F0YW4qKiA6IE1lbmdndW5ha2FuIGtlbWJhbGkgdmFyaWFiZWwgZGFwYXQgbWVueWViYWJrYW4gaGFzaWwgeWFuZyB0aWRhayBkaWhhcmFwa2FuLCB0ZXRhcGkgamFuZ2FuIGtoYXdhdGlyLiBSIGFrYW4gbWVtYmVyaSBwZXJpbmdhdGFuIHNlcGVydGkgYHN1bW1hcmlzZSgpIHVuZ3JvdXBpbmcgb3V0cHV0IChvdmVycmlkZSB3aXRoIC5ncm91cHMgYXJndW1lbnQpLmANCg0KIyMgMTAuIFNwbGl0IERhdGENCg0KU2VwZXJ0aSB5YW5nIHRlbGFoIGRpc2VidXRrYW4gZGkgYmFiIGBQZW1vZ3JhbWFuIFJgIHRlbnRhbmcgZnVuZ3NpIHBlbnVsaXNhbiB1bnR1ayBtZW1iYWdpIGRhdGEgcGVsYXRpaGFuIGRhbiBwZW5ndWppYW4uIERpIHNpbmksIGtpdGEgYWthbiBiZWxhamFyIGxlYmloIGJhbnlhayB0ZW50YW5nIGJhZ2FpbWFuYSBtZW5nZ3VuYWthbiBiZWJlcmFwYSBwYWNrYWdlcyB1bnR1ayBtZW1iYWdpIGRhdGEuIEthcmVuYSBiYWdpYW4gaW5pIHNhbmdhdCBwZW50aW5nIGJhZ2kgRGF0YSBTY2llbnRpc3QgdGVydXRhbWEgc2FhdCBBbmRhIG1lbmdndW5ha2FuIFtNYWNoaW5lIExlYXJuaW5nXShodHRwczovL3JzdHVkaW8tcHVicy1zdGF0aWMuczMuYW1hem9uYXdzLmNvbS82ODQwMzlfNTc0NDY5MjA0ZmMyNDkxNWE4MjdlN2U1N2ViZWE0YWUuaHRtbCkgdW50dWsgbWVuZ2FuYWxpc2lzIGRhdGEuIA0KUGFkYSBiYWdpYW4gaW5pIGJpYXNhbnlhIGtpdGEgcGVybHUgbWVtYmFnaSBEYXRhIFNldCBhbnRhcmEgKlRyYWluIFNldCogZGFuICpUZXN0IFNldCouIFRyYWluIHNldCBtZW11bmdraW5rYW4gYWxnb3JpdG1hIHVudHVrIGJlbGFqYXIgZGFyaSBkYXRhLiBVbnR1ayBtZW5ndWppIGtpbmVyamEgbW9kZWwga2l0YSwga2l0YSBkYXBhdCBtZW5nZ3VuYWthbiBzZXQgcGVuZ3VqaWFuIHVudHVrIG1lbmdlbWJhbGlrYW4gdWt1cmFuIGtpbmVyamEuIEphZGksIGxpaGF0IGJlYmVyYXBhIHBhY2thZ2UgeWFuZyBkYXBhdCBBbmRhIGd1bmFrYW4gdW50dWsgcGVtYmFnaWFuIGRhdGEgOg0KDQojIyMgMTAuMSBEcGx5cg0KDQpBbmRhIGJpc2EgbWVuZ2d1bmFrYW4gYGRwbHlyYCB1bnR1ayBtZW1idWF0bnlhIG1lbmphZGkgc2FuZ2F0IHNlZGVyaGFuYS4gSGFsIGluaSBtZW1hbmcgbWVtYnV0dWhrYW4gdmFyaWFiZWwgaWQgcGFkYSBEYXRhIFNldCBhbmRhLCB5YW5nIG1lcnVwYWthbiBpZGUgeWFuZyBiYWd1cywgdGlkYWsgaGFueWEgdW50dWsgbWVtYnVhdCBzZXQgdGV0YXBpIGp1Z2EgdW50dWsga2V0ZXJsYWNha2FuIHNlbGFtYSBwcm95ZWsuIFRhbWJhaGthbiBqaWthIGJlbHVtIGFkYSBkaWRhbGFtIGRhdGEuDQoNCmBgYHtyfQ0KbGlicmFyeShkcGx5cikgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBNZW11YXQgcGFja2FnZSBgZHBseXJgDQpkYXRhKG10Y2FycykgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIEd1bmFrYW4gZGF0YSBkYXJpIFIgZW52aXJvbm1lbnQNCnNldC5zZWVkKDEyMykgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgdW50dWsgbWVtYXN0aWthbiBraXRhIG1lbmdoYXNpbGthbiBkYXRhIHlhbmcgc2FtYQ0KbXRjYXJzJGlkIDwtIDE6bnJvdyhtdGNhcnMpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBUYW1iYWhrYW4gYGlkYCBrZWRhbGFtIGRhdGEgamlrYSBiZWx1bSBhZGENCnRyYWluICAgICA8LSBtdGNhcnMgJT4lIGRwbHlyOjpzYW1wbGVfZnJhYyguNzUpICAgICAgICAgICAgICMgVGV0YXBrYW4gdHJhaW4gc2V0DQp0ZXN0ICAgICAgPC0gZHBseXI6OmFudGlfam9pbihtdGNhcnMsIHRyYWluLCBieSA9ICdpZCcpICAgICAjIFRldGFwa2FuIHRlc3Qgc2V0DQpkaW0odHJhaW4pICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIENlayBkaW1lbnNpIGRhcmkgVHJhaW4gU2V0DQpkaW0odGVzdCkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIE1lbmdlY2VrIGRpbWVuc2kgZGFyaSBUZXN0IFNldA0KYGBgDQoNCioqQ2F0YXRhbioqIDogUGFkYSBEYXRhIFNldCBpbmksIG1pc2FsbnlhLCB0aWRhayBjb2NvayB1bnR1ayB0dWdhcyBNYWNoaW5lIExlYXJuaW5nIGthcmVuYSBkYXRhbnlhIHRlcmxhbHUga2VjaWwuDQoNCiMjIyAxMC4yIGNhVG9vbHMNCg0KQWRhIGJhbnlhayBwZW5kZWthdGFuIHVudHVrIG1lbGFrdWthbiBwYXJ0aXNpIGRhdGEuIFVudHVrIHBlbmRla2F0YW4geWFuZyBsZWJpaCBsZW5na2FwLCBsaWhhdCBmdW5nc2kgYGNyZWF0ZURhdGFQYXJ0aXRpb25gIGRhbGFtIHBhY2thZ2UgYGNhVG9vbHNgLg0KDQpgYGB7cn0NCmxpYnJhcnkoY2FUb29scykgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgTWVtdWF0IHBhY2thZ2UgYGNhVG9vbHNgDQpkYXRhKG10Y2FycykgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIEd1bmFrYW4gZGF0YSBkYXJpIFIgZW52aXJvbm1lbnQNCnNldC5zZWVkKDEyMykgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgdW50dWsgbWVtYXN0aWthbiBraXRhIG1lbmdoYXNpbGthbiBkYXRhIHlhbmcgc2FtYQ0Kc21wX3NpemUgIDwtIGZsb29yKDAuNzUgKiBucm93KG10Y2FycykpICAgICAgICAgICAgICAgICAgICAgIyA3NSUgZGFyaSB1a3VyYW4gc2FtcGVsDQp0cmFpbl9pbmQgPC0gc2FtcGxlKHNlcV9sZW4obnJvdyhtdGNhcnMpKSwgDQogICAgICAgICAgICAgICAgICAgIHNpemUgPSBzbXBfc2l6ZSkNCnRyYWluICAgICA8LSBtdGNhcnNbdHJhaW5faW5kLF0gICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgVGV0YXBrYW4gVHJhaW4gU2V0DQp0ZXN0ICAgICAgPC0gbXRjYXJzWy10cmFpbl9pbmQsXSAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIFRldGFwa2FuIFRlc3QgU2V0DQpkaW0odHJhaW4pICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIENlayBkaW1lbnNpIGRhcmkgVHJhaW4gU2V0DQpkaW0odGVzdCkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIENlayBkaW1lbnNpIGRhcmkgVGVzdCBTZXQNCmBgYA0KDQojIyMgMTAuMyBDYXJldA0KDQpQYWNrYWdlIGtlcmVuIGxhaW5ueWEgeWFuZyBkYXBhdCBraXRhIGd1bmFrYW4gdW50dWsgU3BsaXQgYXRhdSBwZW1iYWdpYW4gZGF0YSB5YWl0dSBgY2FyZXRgLg0KDQpgYGB7cn0NCmxpYnJhcnkoY2FyZXQpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBNZW5qYWxhbmthbiBwYWNrYWdlIGBjYXJldGANCmRhdGEobXRjYXJzKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBNZW5nZ3VuYWthbiBkYXRhIGRhcmkgUg0Kc2V0LnNlZWQoMTIzKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIFVudHVrIG1lbWFzdGlrYW4ga2l0YSBtZW5qYWxhbmthbiBkYXRhIHlhbmcgc2FtYQ0KaW50cmFpbjwtY3JlYXRlRGF0YVBhcnRpdGlvbihtdGNhcnMkbXBnLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwPTAuNzUsbGlzdD1GQUxTRSkNCnRyYWluPC1tdGNhcnNbaW50cmFpbixdDQp0ZXN0PC1tdGNhcnNbLWludHJhaW4sXQ0KZGltKHRyYWluKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIE1lbmdlY2VrIGRpbWVuc2kgZGFyaSBUcmFpbiBTZXQNCmRpbSh0ZXN0KSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBNZW5nZWNlayBkaW1lbnNpIGRhcmkgVGVzdCBTZXQNCmBgYA0KDQpbKipNb3JlKipdKGh0dHBzOi8vd3d3LmRhdGFub3ZpYS5jb20vZW4vY291cnNlcy9kYXRhLW1hbmlwdWxhdGlvbi1pbi1yLyk=