Training Workbook for dplyr Package

Author: Naki Cam, myBI, DPDHL IT Services GmbH Reference: Hadley Wickham, R for Data Science

notice: feel free to share !

# Pakete laden
library(tidyverse)
library(nycflights13)

nycflights13

Um die grundlegenden Manipulationsverben von dplyr zu untersuchen, verwenden wir nycflights13:: flights. Dieser Dataframe enthält alle 336.776 Flüge, die 2013 von New York City abgegangen sind.

# Einblick in die Daten
# Das dataframe wird als "tibble" angezeigt, das bedeutet es werden nur Zeilen angezeigt, welche auf die Bildschirmseite passen!
head(flights)
# Wieviele Daten sind im dataset
dim(flights)
[1] 336776     19
# Gebe mir Details zum Datentyp von "flights"
class(flights)
[1] "tbl_df"     "tbl"        "data.frame"
# Zeige mir Details zur Struktur von "flights"
str(flights)
Classes ‘tbl_df’, ‘tbl’ and 'data.frame':   336776 obs. of  19 variables:
 $ year          : int  2013 2013 2013 2013 2013 2013 2013 2013 2013 2013 ...
 $ month         : int  1 1 1 1 1 1 1 1 1 1 ...
 $ day           : int  1 1 1 1 1 1 1 1 1 1 ...
 $ dep_time      : int  517 533 542 544 554 554 555 557 557 558 ...
 $ sched_dep_time: int  515 529 540 545 600 558 600 600 600 600 ...
 $ dep_delay     : num  2 4 2 -1 -6 -4 -5 -3 -3 -2 ...
 $ arr_time      : int  830 850 923 1004 812 740 913 709 838 753 ...
 $ sched_arr_time: int  819 830 850 1022 837 728 854 723 846 745 ...
 $ arr_delay     : num  11 20 33 -18 -25 12 19 -14 -8 8 ...
 $ carrier       : chr  "UA" "UA" "AA" "B6" ...
 $ flight        : int  1545 1714 1141 725 461 1696 507 5708 79 301 ...
 $ tailnum       : chr  "N14228" "N24211" "N619AA" "N804JB" ...
 $ origin        : chr  "EWR" "LGA" "JFK" "JFK" ...
 $ dest          : chr  "IAH" "IAH" "MIA" "BQN" ...
 $ air_time      : num  227 227 160 183 116 150 158 53 140 138 ...
 $ distance      : num  1400 1416 1089 1576 762 ...
 $ hour          : num  5 5 5 5 6 5 6 6 6 6 ...
 $ minute        : num  15 29 40 45 0 58 0 0 0 0 ...
 $ time_hour     : POSIXct, format: "2013-01-01 05:00:00" ...

int steht für Ganzzahlen (Integer). dbl steht für Realzahlen (Double). chr steht für Zeichenvektoren oder Strings (Character). dttm steht für Datums-/ Zeitwerte (bestehend aus Datum und Uhrzeit).

# 5er Statistik zu den Daten in "flights"
summary(flights)
      year          month             day       
 Min.   :2013   Min.   : 1.000   Min.   : 1.00  
 1st Qu.:2013   1st Qu.: 4.000   1st Qu.: 8.00  
 Median :2013   Median : 7.000   Median :16.00  
 Mean   :2013   Mean   : 6.549   Mean   :15.71  
 3rd Qu.:2013   3rd Qu.:10.000   3rd Qu.:23.00  
 Max.   :2013   Max.   :12.000   Max.   :31.00  
                                                
    dep_time    sched_dep_time   dep_delay      
 Min.   :   1   Min.   : 106   Min.   : -43.00  
 1st Qu.: 907   1st Qu.: 906   1st Qu.:  -5.00  
 Median :1401   Median :1359   Median :  -2.00  
 Mean   :1349   Mean   :1344   Mean   :  12.64  
 3rd Qu.:1744   3rd Qu.:1729   3rd Qu.:  11.00  
 Max.   :2400   Max.   :2359   Max.   :1301.00  
 NA's   :8255                  NA's   :8255     
    arr_time    sched_arr_time   arr_delay       
 Min.   :   1   Min.   :   1   Min.   : -86.000  
 1st Qu.:1104   1st Qu.:1124   1st Qu.: -17.000  
 Median :1535   Median :1556   Median :  -5.000  
 Mean   :1502   Mean   :1536   Mean   :   6.895  
 3rd Qu.:1940   3rd Qu.:1945   3rd Qu.:  14.000  
 Max.   :2400   Max.   :2359   Max.   :1272.000  
 NA's   :8713                  NA's   :9430      
   carrier              flight       tailnum         
 Length:336776      Min.   :   1   Length:336776     
 Class :character   1st Qu.: 553   Class :character  
 Mode  :character   Median :1496   Mode  :character  
                    Mean   :1972                     
                    3rd Qu.:3465                     
                    Max.   :8500                     
                                                     
    origin              dest              air_time    
 Length:336776      Length:336776      Min.   : 20.0  
 Class :character   Class :character   1st Qu.: 82.0  
 Mode  :character   Mode  :character   Median :129.0  
                                       Mean   :150.7  
                                       3rd Qu.:192.0  
                                       Max.   :695.0  
                                       NA's   :9430   
    distance         hour           minute     
 Min.   :  17   Min.   : 1.00   Min.   : 0.00  
 1st Qu.: 502   1st Qu.: 9.00   1st Qu.: 8.00  
 Median : 872   Median :13.00   Median :29.00  
 Mean   :1040   Mean   :13.18   Mean   :26.23  
 3rd Qu.:1389   3rd Qu.:17.00   3rd Qu.:44.00  
 Max.   :4983   Max.   :23.00   Max.   :59.00  
                                               
   time_hour                  
 Min.   :2013-01-01 05:00:00  
 1st Qu.:2013-04-04 13:00:00  
 Median :2013-07-03 10:00:00  
 Mean   :2013-07-03 05:02:36  
 3rd Qu.:2013-10-01 07:00:00  
 Max.   :2013-12-31 23:00:00  
                              

dplyr-Grundlagen

Beobachtungen nach ihren Werten herausgreifen (filter()).

Zeilen neu anordnen (arrange()).

Variablen nach ihren Namen auswählen (select()).

Neue Variablen mit Funktionen von vorhandenen Variablen erzeugen (mutate()).

Viele Werte zu einer einzigen Kurzdarstellung zusammenfassen (summarize()).

! Alle diese Funktionen lassen sich in Verbindung mit group_by() verwenden,

# Zeilen mit filter() 
# Die Funktion filter() erlaubt es, Teilmengen von #Beobachtungen basierend auf ihren Werten zu bilden.
filter(flights, month== 1, day == 1)
# in einer Variablen speichern
jan1 <- filter(flights, month== 1, day == 1)
# Inhaltsvergleich
nov_dec <- filter(flights, month %in% c(11,12))
head(nov_dec)
# Zeilen mit "NA"" Werten ermitteln
(
  Null_Werte <- filter(flights, is.na(flights$dep_time) | is.na(flights$dep_delay))
)

Übungen:

1.) Ermitteln Sie alle Flüge, die: a) eine Ankunftsverspätung von zwei oder mehr Stunden hatten, b) nach Houston (IAH oder HOU) geflogen sind, c) von United, American oder Delta durchgeführt wurden, d) im Sommer (Juli, August und September) abgeflogen sind, e) mehr als zwei Stunden zu spät angekommen, aber nicht verspätet gestartet sind, f) mindestens eine Stunde Verspätung hatten, aber mehr als 30 Minuten im Flug aufgeholt haben, g) zwischen Mitternacht und 6 Uhr (jeweils inklusive) abgeflogen sind.

? flights
(
two <- filter(flights, between(month, 1,2))
)
(
  three <- filter(flights, is.na(dep_time))
)
(NA ^ 0 )
[1] 1
  
(NA | TRUE )
[1] TRUE
  
(FALSE & NA)
[1] FALSE
  
(NA * 0)
[1] NA

Zeilen mit arrange() anordnen

(
  arrange(flights, year, month, day)
)
(
  arrange(flights, desc(arr_delay))
)

Übungen: 1. Wie können Sie mit arrange() alle fehlenden Werte am Anfang einsortieren? (Hinweis: Verwenden Sie is.na().) 2. Sortieren Sie flights, um die Flüge mit den größten Verspätungen zu ermitteln. Suchen Sie die Flüge, die am frühesten gestartet sind. 3. Sortieren Sie flights, um die schnellsten Flüge zu ermitteln. 4. Welche Flüge hatten die längsten Reisezeiten? Bei welchen Flügen waren die Reisezeiten am kürzesten?

Spalten mit select() auswählen

# Select columns by name
(
  select(flights, dest, hour, minute)
)
(
  select(flights, dest:minute)
)
# Select all columns except those from year to day (inclusive)
(
 select (flights, -(year:day))
)

Es gibt eine Reihe von Hilfsfunktionen, die Sie innerhalb von select() verwenden können: starts_with(" abc“) sucht nach Namen, die mit »abc« beginnen. ends_with(” xyz“) sucht nach Namen, die auf »xyz« enden. contains(” ijk“) sucht nach Namen, die »ijk« enthalten. matches(”(.)\ 1“) wählt Variablen aus, die einem regulären Ausdruck entsprechen, im angegebenen Beispiel allen Variablen, die sich wiederholende Zeichen enthalten. num_range(” x“, 1: 3) sucht nach x1, x2 und x3.

Mit ?select zeigen Sie weitere Details an.

?select
# nutze rename um Variablen umzubenennen
(
  rename(flights, tail_num = tailnum)
)
# verschieben von Variablen an den Beginn des dataframe
(
  select(flights, time_hour, dest, hour, minute, everything())
)

Übungen:

Denken Sie sich möglichst viele Wege aus, um dep_time, dep_delay, arr_time und arr_delay von flights auszuwählen.

Was passiert, wenn Sie den Namen einer Variablen mehrfach in einen select()-Aufruf einbinden?

Welche Aufgabe hat die Funktion one_of()? Warum könnte sie in Verbindung mit dem folgenden Vektor hilfreich sein?

vars <- c( “year”, “month”, “day”, “dep_delay”, “arr_delay” )

(
  select(flights, dep_time, dep_delay, arr_time, arr_delay)
) 
(
  select(flights, dep_time:arr_delay)
) 
(
  # dupikate in spaltennamen werden ignoriert
  select(flights, dep_time, dep_time)
)
(
  ?one_of
)
vars <- c( "year", "month", "day", "dep_delay", "arr_delay" )
(
  select(flights, one_of(vars))
  
)
(
  # Groß- und Kleinschreibung wird ignoriert
  select(flights, contains("TIME"))
)

Neue Variablen mit mutate() hinzufügen

LS0tDQp0aXRsZTogInRpZHl2ZXJzZSBkcGx5ciINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQojIFRyYWluaW5nIFdvcmtib29rIGZvciBkcGx5ciBQYWNrYWdlDQoNCkF1dGhvcjogTmFraSBDYW0sIG15QkksIERQREhMIElUIFNlcnZpY2VzIEdtYkgNClJlZmVyZW5jZTogSGFkbGV5IFdpY2toYW0sIFIgZm9yIERhdGEgU2NpZW5jZQ0KDQpub3RpY2U6IGZlZWwgZnJlZSB0byBzaGFyZSAhDQoNCmBgYHtyfQ0KIyBQYWtldGUgbGFkZW4NCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShueWNmbGlnaHRzMTMpDQoNCmBgYA0KIyBueWNmbGlnaHRzMTMgDQpVbSBkaWUgZ3J1bmRsZWdlbmRlbiBNYW5pcHVsYXRpb25zdmVyYmVuIHZvbiBkcGx5ciB6dSB1bnRlcnN1Y2hlbiwgdmVyd2VuZGVuIHdpciBueWNmbGlnaHRzMTM6OiBmbGlnaHRzLiANCkRpZXNlciBEYXRhZnJhbWUgZW50aMOkbHQgYWxsZSAzMzYuNzc2IEZsw7xnZSwgZGllIDIwMTMgdm9uIE5ldyBZb3JrIENpdHkgYWJnZWdhbmdlbiBzaW5kLg0KDQpgYGB7cn0NCiMgRWluYmxpY2sgaW4gZGllIERhdGVuDQoNCiMgRGFzIGRhdGFmcmFtZSB3aXJkIGFscyAidGliYmxlIiBhbmdlemVpZ3QsIGRhcyBiZWRldXRldCBlcyB3ZXJkZW4gbnVyIFplaWxlbiBhbmdlemVpZ3QsIHdlbGNoZSBhdWYgZGllIEJpbGRzY2hpcm1zZWl0ZSBwYXNzZW4hDQpoZWFkKGZsaWdodHMpDQpgYGANCmBgYHtyfQ0KIyBXaWV2aWVsZSBEYXRlbiBzaW5kIGltIGRhdGFzZXQgKFplaWxlbiwgU3BhbHRlbikNCmRpbShmbGlnaHRzKQ0KYGBgDQoNCg0KYGBge3J9DQojIEdlYmUgbWlyIERldGFpbHMgenVtIERhdGVudHlwIHZvbiAiZmxpZ2h0cyINCmNsYXNzKGZsaWdodHMpDQpgYGANCg0KYGBge3J9DQojIFplaWdlIG1pciBEZXRhaWxzIHp1ciBTdHJ1a3R1ciB2b24gImZsaWdodHMiDQpzdHIoZmxpZ2h0cykNCmBgYA0KDQppbnQgc3RlaHQgZsO8ciBHYW56emFobGVuIChJbnRlZ2VyKS4gZGJsIHN0ZWh0IGbDvHIgUmVhbHphaGxlbiAoRG91YmxlKS4gY2hyIHN0ZWh0IGbDvHIgWmVpY2hlbnZla3RvcmVuIG9kZXIgU3RyaW5ncyAoQ2hhcmFjdGVyKS4gZHR0bSBzdGVodCBmw7xyIERhdHVtcy0vIFplaXR3ZXJ0ZSAoYmVzdGVoZW5kIGF1cyBEYXR1bSB1bmQgVWhyemVpdCkuDQogDQoNCmBgYHtyfQ0KIyA1ZXIgU3RhdGlzdGlrIHp1IGRlbiBEYXRlbiBpbiAiZmxpZ2h0cyINCnN1bW1hcnkoZmxpZ2h0cykNCmBgYA0KDQojIGRwbHlyLUdydW5kbGFnZW4NCg0KIyMjIEJlb2JhY2h0dW5nZW4gbmFjaCBpaHJlbiBXZXJ0ZW4gaGVyYXVzZ3JlaWZlbiAoZmlsdGVyKCkpLg0KIyMjIFplaWxlbiBuZXUgYW5vcmRuZW4gKGFycmFuZ2UoKSkuIA0KIyMjIFZhcmlhYmxlbiBuYWNoIGlocmVuIE5hbWVuIGF1c3fDpGhsZW4gKHNlbGVjdCgpKS4gDQojIyMgTmV1ZSBWYXJpYWJsZW4gbWl0IEZ1bmt0aW9uZW4gdm9uIHZvcmhhbmRlbmVuIFZhcmlhYmxlbiBlcnpldWdlbiAobXV0YXRlKCkpLiANCiMjIyBWaWVsZSBXZXJ0ZSB6dSBlaW5lciBlaW56aWdlbiBLdXJ6ZGFyc3RlbGx1bmcgenVzYW1tZW5mYXNzZW4gKHN1bW1hcml6ZSgpKS4NCg0KIyMjICEgQWxsZSBkaWVzZSBGdW5rdGlvbmVuIGxhc3NlbiBzaWNoIGluIFZlcmJpbmR1bmcgbWl0IGdyb3VwX2J5KCkgdmVyd2VuZGVuLA0KDQpgYGB7cn0NCiMgWmVpbGVuIG1pdCBmaWx0ZXIoKSANCiMgRGllIEZ1bmt0aW9uIGZpbHRlcigpIGVybGF1YnQgZXMsIFRlaWxtZW5nZW4gdm9uICNCZW9iYWNodHVuZ2VuIGJhc2llcmVuZCBhdWYgaWhyZW4gV2VydGVuIHp1IGJpbGRlbi4NCg0KZmlsdGVyKGZsaWdodHMsIG1vbnRoPT0gMSwgZGF5ID09IDEpDQoNCiMgaW4gZWluZXIgVmFyaWFibGVuIHNwZWljaGVybg0KamFuMSA8LSBmaWx0ZXIoZmxpZ2h0cywgbW9udGg9PSAxLCBkYXkgPT0gMSkNCmBgYA0KDQpgYGB7cn0NCiMgSW5oYWx0c3ZlcmdsZWljaA0Kbm92X2RlYyA8LSBmaWx0ZXIoZmxpZ2h0cywgbW9udGggJWluJSBjKDExLDEyKSkNCg0KaGVhZChub3ZfZGVjKQ0KDQpgYGANCg0KDQpgYGB7cn0NCiMgWmVpbGVuIG1pdCAiTkEiIiBXZXJ0ZW4gZXJtaXR0ZWxuLCBkdXJjaCBkaWUgdm9yYW5zdGVoZW5kZSBLbGFtbWVyIHdpcmQgZGllIEF1c2dhYmUgZWJlbmZhbGxzIGVyemV1Z3QNCigNCiAgTnVsbF9XZXJ0ZSA8LSBmaWx0ZXIoZmxpZ2h0cywgaXMubmEoZmxpZ2h0cyRkZXBfdGltZSkgfCBpcy5uYShmbGlnaHRzJGRlcF9kZWxheSkpDQopDQoNCg0KYGBgDQoNCg0KIyDDnGJ1bmdlbjoNCg0KMS4pIEVybWl0dGVsbiBTaWUgYWxsZSBGbMO8Z2UsIGRpZToNCmEpIGVpbmUgQW5rdW5mdHN2ZXJzcMOkdHVuZyB2b24gendlaSBvZGVyIG1laHIgU3R1bmRlbiBoYXR0ZW4sDQpiKSBuYWNoIEhvdXN0b24gKElBSCBvZGVyIEhPVSkgZ2VmbG9nZW4gc2luZCwgDQpjKSB2b24gVW5pdGVkLCBBbWVyaWNhbiBvZGVyIERlbHRhIGR1cmNoZ2Vmw7xocnQgd3VyZGVuLCANCmQpIGltIFNvbW1lciAoSnVsaSwgQXVndXN0IHVuZCBTZXB0ZW1iZXIpIGFiZ2VmbG9nZW4gc2luZCwgZSkgbWVociBhbHMgendlaSBTdHVuZGVuIHp1IHNww6R0IGFuZ2Vrb21tZW4sIGFiZXIgbmljaHQgdmVyc3DDpHRldCBnZXN0YXJ0ZXQgc2luZCwgDQpmKSBtaW5kZXN0ZW5zIGVpbmUgU3R1bmRlIFZlcnNww6R0dW5nIGhhdHRlbiwgYWJlciBtZWhyIGFscyAzMCBNaW51dGVuIGltIEZsdWcgYXVmZ2Vob2x0IGhhYmVuLCANCmcpIHp3aXNjaGVuIE1pdHRlcm5hY2h0IHVuZCA2IFVociAoamV3ZWlscyBpbmtsdXNpdmUpIGFiZ2VmbG9nZW4gc2luZC4NCmBgYHtyfQ0KIyBWYXJpYWJsZW4gQmVkZXV0dW5nZW4gYW56ZWlnZW4gbGFzc2VuDQo/IGZsaWdodHMNCmBgYA0KDQoNCg0KYGBge3J9DQojIGEpDQooDQogIGEgPC0gZmlsdGVyKGZsaWdodHMsIGFycl90aW1lID49MTIwKQ0KKQ0KDQooDQogIGIgPC0gZmlsdGVyKGZsaWdodHMsIGRlc3QgPT0gIklBSCIgfCBkZXN0ID09ICJIT1UiKQ0KKQ0KDQooIA0KICAjIExvb2sgdXAgYWlybGluZSBuYW1lcyBmcm9tIHRoZWlyIGNhcnJpZXIgY29kZXMuDQogIGFpcmxpbmVzDQopDQoNCigNCiAgYyA8LSBmaWx0ZXIoZmxpZ2h0cywgY2FycmllciAlaW4lIGMoIkFBIiwgIkRMIiwgIlVBIikpDQopDQoNCigNCmQgPC0gZmlsdGVyKGZsaWdodHMsIG1vbnRoICVpbiUgYyg3LDgsOSkpDQopDQoNCigNCiAgZyA8LSBmaWx0ZXIoZmxpZ2h0cywgaG91ciA+PSAwMCAmIGhvdXIgPD0gMDYpDQopDQpgYGANCg0KYGBge3J9DQooDQp0d28gPC0gZmlsdGVyKGZsaWdodHMsIGJldHdlZW4obW9udGgsIDEsMikpDQopDQoNCmBgYA0KDQpgYGB7cn0NCigNCiAgdGhyZWUgPC0gZmlsdGVyKGZsaWdodHMsIGlzLm5hKGRlcF90aW1lKSkNCikNCg0KDQpgYGANCg0KYGBge3J9DQooTkEgXiAwICkNCiAgDQooTkEgfCBUUlVFICkNCiAgDQooRkFMU0UgJiBOQSkNCiAgDQooTkEgKiAwKQ0KDQoNCmBgYA0KDQoNCiMgWmVpbGVuIG1pdCBhcnJhbmdlKCkgYW5vcmRuZW4NCg0KYGBge3J9DQooDQogIGFycmFuZ2UoZmxpZ2h0cywgeWVhciwgbW9udGgsIGRheSkNCikNCg0KKA0KICBhcnJhbmdlKGZsaWdodHMsIGRlc2MoYXJyX2RlbGF5KSkNCikNCg0KYGBgDQoNCg0Kw5xidW5nZW46DQoxLiBXaWUga8O2bm5lbiBTaWUgbWl0IGFycmFuZ2UoKSBhbGxlIGZlaGxlbmRlbiBXZXJ0ZSBhbSBBbmZhbmcgZWluc29ydGllcmVuPyAoSGlud2VpczogVmVyd2VuZGVuIFNpZSBpcy5uYSgpLikgDQoyLiBTb3J0aWVyZW4gU2llIGZsaWdodHMsIHVtIGRpZSBGbMO8Z2UgbWl0IGRlbiBncsO2w590ZW4gVmVyc3DDpHR1bmdlbiB6dSBlcm1pdHRlbG4uIFN1Y2hlbiBTaWUgZGllIEZsw7xnZSwgZGllIGFtIGZyw7xoZXN0ZW4gZ2VzdGFydGV0IHNpbmQuIA0KMy4gU29ydGllcmVuIFNpZSBmbGlnaHRzLCB1bSBkaWUgc2NobmVsbHN0ZW4gRmzDvGdlIHp1IGVybWl0dGVsbi4gDQo0LiBXZWxjaGUgRmzDvGdlIGhhdHRlbiBkaWUgbMOkbmdzdGVuIFJlaXNlemVpdGVuPyBCZWkgd2VsY2hlbiBGbMO8Z2VuIHdhcmVuIGRpZSBSZWlzZXplaXRlbiBhbSBrw7xyemVzdGVuPw0KDQpgYGB7cn0NCigNCmUxIDwtIGFycmFuZ2UoZmxpZ2h0cywgZGVzYyhpcy5uYShhcnJfdGltZSkpKQ0KKQ0KDQooDQogIGUyIDwtIGFycmFuZ2UoZmxpZ2h0cywgZGVzYyhhcnJfZGVsYXkpLCBkZXBfdGltZSkNCikNCg0KKA0KICBlMyA8LSBhcnJhbmdlKGZsaWdodHMsIGFycl9kZWxheSkNCikNCg0KKA0KICBlNCA8LSBhcnJhbmdlKGZsaWdodHMsIGRlc2MoYWlyX3RpbWUpKQ0KKQ0KDQpgYGANCg0KIyBTcGFsdGVuIG1pdCBzZWxlY3QoKSBhdXN3w6RobGVuDQoNCmBgYHtyfQ0KIyBTZWxlY3QgY29sdW1ucyBieSBuYW1lDQoNCigNCiAgc2VsZWN0KGZsaWdodHMsIGRlc3QsIGhvdXIsIG1pbnV0ZSkNCikNCg0KKA0KICAjIFNlbGVjdCBhbGwgY29sdW1ucyBiZXR3ZWVuIGRlc3QgYW5kIG1pbnV0ZSAoaW5jbHVzaXZlKQ0KDQogIHNlbGVjdChmbGlnaHRzLCBkZXN0Om1pbnV0ZSkNCikNCg0KYGBgDQoNCmBgYHtyfQ0KIyBTZWxlY3QgYWxsIGNvbHVtbnMgZXhjZXB0IHRob3NlIGZyb20geWVhciB0byBkYXkgKGluY2x1c2l2ZSkNCigNCiBzZWxlY3QgKGZsaWdodHMsIC0oeWVhcjpkYXkpKQ0KKQ0KDQpgYGANCg0KRXMgZ2lidCBlaW5lIFJlaWhlIHZvbiBIaWxmc2Z1bmt0aW9uZW4sIGRpZSBTaWUgaW5uZXJoYWxiIHZvbiBzZWxlY3QoKSB2ZXJ3ZW5kZW4ga8O2bm5lbjogDQpzdGFydHNfd2l0aCgiIGFiYyIpIHN1Y2h0IG5hY2ggTmFtZW4sIGRpZSBtaXQgwrthYmPCqyBiZWdpbm5lbi4gDQplbmRzX3dpdGgoIiB4eXoiKSBzdWNodCBuYWNoIE5hbWVuLCBkaWUgYXVmIMK7eHl6wqsgZW5kZW4uIGNvbnRhaW5zKCIgaWprIikgc3VjaHQgbmFjaCBOYW1lbiwgZGllIMK7aWprwqsgZW50aGFsdGVuLiBtYXRjaGVzKCIoLilcXCAxIikgd8OkaGx0IFZhcmlhYmxlbiBhdXMsIGRpZSBlaW5lbSByZWd1bMOkcmVuIEF1c2RydWNrIGVudHNwcmVjaGVuLCBpbSBhbmdlZ2ViZW5lbiBCZWlzcGllbCBhbGxlbiBWYXJpYWJsZW4sIGRpZSBzaWNoIHdpZWRlcmhvbGVuZGUgWmVpY2hlbiBlbnRoYWx0ZW4uIA0KbnVtX3JhbmdlKCIgeCIsIDE6IDMpIHN1Y2h0IG5hY2ggeDEsIHgyIHVuZCB4My4gDQoNCk1pdCA/c2VsZWN0IHplaWdlbiBTaWUgd2VpdGVyZSBEZXRhaWxzIGFuLg0KDQpgYGB7cn0NCj9zZWxlY3QNCmBgYA0KDQpgYGB7cn0NCiMgbnV0emUgcmVuYW1lIHVtIFZhcmlhYmxlbiB1bXp1YmVuZW5uZW4NCigNCiAgcmVuYW1lKGZsaWdodHMsIHRhaWxfbnVtID0gdGFpbG51bSkNCikNCg0KYGBgDQoNCmBgYHtyfQ0KIyB2ZXJzY2hpZWJlbiB2b24gVmFyaWFibGVuIGFuIGRlbiBCZWdpbm4gZGVzIGRhdGFmcmFtZQ0KKA0KICBzZWxlY3QoZmxpZ2h0cywgdGltZV9ob3VyLCBkZXN0LCBob3VyLCBtaW51dGUsIGV2ZXJ5dGhpbmcoKSkNCikNCg0KYGBgDQoNCg0KIyDDnGJ1bmdlbjoNCkRlbmtlbiBTaWUgc2ljaCBtw7ZnbGljaHN0IHZpZWxlIFdlZ2UgYXVzLCB1bSBkZXBfdGltZSwgZGVwX2RlbGF5LCBhcnJfdGltZSB1bmQgYXJyX2RlbGF5IHZvbiBmbGlnaHRzIGF1c3p1d8OkaGxlbi4NCg0KV2FzIHBhc3NpZXJ0LCB3ZW5uIFNpZSBkZW4gTmFtZW4gZWluZXIgVmFyaWFibGVuIG1laHJmYWNoIGluIGVpbmVuIHNlbGVjdCgpLUF1ZnJ1ZiBlaW5iaW5kZW4/IA0KDQpXZWxjaGUgQXVmZ2FiZSBoYXQgZGllIEZ1bmt0aW9uIG9uZV9vZigpPyBXYXJ1bSBrw7ZubnRlIHNpZSBpbiBWZXJiaW5kdW5nIG1pdCBkZW0gZm9sZ2VuZGVuIFZla3RvciBoaWxmcmVpY2ggc2Vpbj8gDQoNCnZhcnMgPC0gYyggInllYXIiLCAibW9udGgiLCAiZGF5IiwgImRlcF9kZWxheSIsICJhcnJfZGVsYXkiICkNCg0KYGBge3J9DQooDQogIHNlbGVjdChmbGlnaHRzLCBkZXBfdGltZSwgZGVwX2RlbGF5LCBhcnJfdGltZSwgYXJyX2RlbGF5KQ0KKSANCg0KKA0KICBzZWxlY3QoZmxpZ2h0cywgZGVwX3RpbWU6YXJyX2RlbGF5KQ0KKSANCg0KKA0KICAjIGR1cGlrYXRlIGluIHNwYWx0ZW5uYW1lbiB3ZXJkZW4gaWdub3JpZXJ0DQogIHNlbGVjdChmbGlnaHRzLCBkZXBfdGltZSwgZGVwX3RpbWUpDQopDQoNCigNCiAgP29uZV9vZg0KKQ0KdmFycyA8LSBjKCAieWVhciIsICJtb250aCIsICJkYXkiLCAiZGVwX2RlbGF5IiwgImFycl9kZWxheSIgKQ0KDQoNCigNCiAgc2VsZWN0KGZsaWdodHMsIG9uZV9vZih2YXJzKSkNCiAgDQopDQoNCigNCiAgIyBHcm/Dny0gdW5kIEtsZWluc2NocmVpYnVuZyB3aXJkIGlnbm9yaWVydA0KICBzZWxlY3QoZmxpZ2h0cywgY29udGFpbnMoIlRJTUUiKSkNCikNCmBgYA0KDQoNCg0KIyBOZXVlIFZhcmlhYmxlbiBtaXQgbXV0YXRlKCkgaGluenVmw7xnZW4NCg0KYGBge3J9DQooDQpmbGlnaHRzX3NtbCA8LSBzZWxlY3QoZmxpZ2h0cywgeWVhcjpkYXksIGVuZHNfd2l0aCgiZGVsYXkiKSwgZGlzdGFuY2UsIGFpcl90aW1lKQ0KKQ0KDQooDQogIG11dGF0ZShmbGlnaHRzX3NtbCwgZ2FpbiA9IGFycl9kZWxheSAtIGRlcF9kZWxheSwgc3BlZWQgPSBkaXN0YW5jZSAvIGFpcl90aW1lKjYwKQ0KKQ0KDQooDQogIG11dGF0ZShmbGlnaHRzX3NtbCwgZ2FpbiA9IGFycl9kZWxheSAtIGRlcF9kZWxheSwNCiAgICAgICAgIGhvdXJzID0gYWlyX3RpbWUgLyA2MCwNCiAgICAgICAgIGdhaW5fcGVyX2hvdXIgPSBnYWluIC8gaG91cnMpDQopDQoNCigNCiAgIyBXZW5uIFNpZSBudXIgZGllIG5ldWVuIFZhcmlhYmxlbiBiZWhhbHRlbiB3b2xsZW4sIHZlcndlbmRlbiBTaWUgDQogICMgdHJhbnNtdXRlKCk6DQp0cmFuc211dGUoZmxpZ2h0c19zbWwsIA0KICAgICAgICAgIGdhaW4gPSBhcnJfZGVsYXkgLSBkZXBfZGVsYXksDQogICAgICAgICAgaG91cnMgPSBhaXJfdGltZSAvIDYwLA0KICAgICAgICAgIGdhaW5fcGVyX2hvdXIgPSBnYWluIC8gaG91cnMpDQoNCikNCmBgYA0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQo=