【Data Import】
system.file() #回傳根目錄"C:/PROGRA~1/R/R-35~1.0/library/base"
file.path(路徑,檔名) #會幫你自動選對的slash黏上
file.copy(被複製的路徑,欲複製到的路徑)
- read_lines() #查看有沒有header
- The import functions in the readr package all start as read_, while the import functions for base R all start with read.
【R的基本function VS readr ?】
- Base R functions import data as a data frame, while “readr” functions import data as a tibble.
- 如果read.csv時沒有用StringAsFactor=F,他會將character轉為factor
- The base R import functions can read .csv files,but cannot files with other delimiters, such as .tsv files, or fixed-width files.
- The base R import functions can read files with other delimiters(分隔號) like .tsv using read.delim and can read fixed-width files using read.fwf.
【What is Tibble ?】
- A tibble is also a data.frame.
- Tibble prints better in console (it fits to screen size) and it also prints data types in header.
- To put it simple, tibbles are “evolutions” of data.frames, printing and subsetting are less troublesome with tibbles than data.frames.
- For example, if you want to look 10 first rows of a data.frame, you will use head(data.frame), for tibbles you can simple use print(tibble) or call tibble in R Console.
- When subsetting, if you use “[” on tibble R will return always another tibble, but when you use “[[” R will return a vector.
- For data.frames, when using “[” you might get a vector or another data.frame, which is quite unpleasant.
- Another difference, tibble does not have partial matching.
url = https://~/murders.csv
read_csv(url) # 可以直接讀取網址檔
download.file(url, "murders.csv") # 把網上的檔案載下來並命名為murders.csv
tempdir()
tempfile()
# tempfile創建一個字符串,而不是文件,這可能是一個唯一的文件名
【Tidy Data】
dat_tidy <- dat_wide %>%
gather(key = disease, value = count, “Hepatitis A”: “Rubella”)
- specified that the “key” column will be called “disease”, the value of each entry will be called “count”, and that the columns Hepatitis A through Rubella will all be included in the gather command.
- speard跟gather相反,speard是把一個欄位裡面n個factor變成n個欄位,並把每row右邊的值輸入n個欄位下面
- gather是把n個欄位聚集成1個新的欄位,並創第2個新的欄位把n欄下的obs.輸入對應的row
gather(key = key, value = value, -age_group) # 把除了其中一個以外的全部的變數都拿去gather
D %>% gather(key=key, value=value, -age_group) %>%
separate(col=key, into=c("year", "variable_name"), "_") %>%
spread(key=variable_name, value=value) %>% data.frame
# 默認的separate就是"_"了,所以即使不寫也沒關係
stats %>%
separate(col=key, into=c("player", "variable_name"), sep="_", extra="merge") %>%
spread(key=variable_name, value=value)
# 等同於
stats %>%
separate(col=key, into=c("player", "variable_name1", "variable_name2"),
sep="_", fill="right") %>%
unite(col = variable_name, variable_name1, variable_name2, sep = "_") %>%
spread(key = variable_name, value = value)
# fill="right"是叫他把name1,name2的欄位黏在player右邊
【Merge data】
- left_join()
- all rows in the left-hand table are retained in the final table, columns from both tables will be included in the final table
- 左邊保持原樣,右邊對照左邊的obs.把右邊的column和值加入(NA來自右邊的)
- right_join()
- inner_join()
- 選兩邊都有obs.的column,把他們merge起來(取交集)
- full_join()
- 不管是否欄列對稱,全部資料保留,缺項塞NA(取聯集)
- semi_join()
- anti_join()
Bind_column(tb1,tb2) == cbind()
Bind_row(tb1,tb2,tb3) == rbind()
Binding functions combine by position, while join functions match by variables.
Joining functions can join datasets of different dimensions, but the bind functions must match on the appropriate dimension (either same row or column numbers).
Bind functions can combine both vectors and dataframes, while join functions work for only for dataframes.
intersect(c(1:10),c(6:15))
intersect(c("a","b"),c("b","c"))
setdiff(c(1:10),c(6:15))
[1] 1 2 3 4 5
setequal(c(1:10),c(6:15))
[1] FALSE
setequal(c(1:10),c(1:10))
[1] TRUE
union(c(1:10),c(6:15))
[1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
【Web Scraping】
- The html_node command only selects the first node of a specified type.
- html_nodes so that we could specify the elements we want.
- For example, the second “table” element using the tab[[2]] command.
- selector garget 教學:https://selectorgadget.com/
library(rvest)
url = https://~
h = read_html(url)
tab = h %>% html_nodes("table")
tab = tab[[2]] # 還是一行一行亂亂的
tab = tab %>% html_table
class(tab)
[1] "data.frame"
For the guacamole recipe page we already have done this and determined that we need the following selectors:
library(rvest)
h <- read_html("http://www.foodnetwork.com/recipes/alton-brown/guacamole-recipe-1940609")
recipe <- h %>% html_node(".o-AssetTitle__a-HeadlineText") %>% html_text()
prep_time <- h %>% html_node(".o-RecipeInfo__a-Description--Total") %>% html_text()
ingredients <- h %>% html_nodes(".o-Ingredients__a-ListItemText") %>% html_text()
You can see how complex the selectors are. In any case we are now ready to extract what we want and create a list:
guacamole <- list(recipe, prep_time, ingredients)
Since recipe pages from this website follow this general layout, we can use this code to create a function that extracts this information:
get_recipe <- function(url){
h <- read_html(url)
recipe <- h %>% html_node(".o-AssetTitle__a-HeadlineText") %>% html_text()
prep_time <- h %>% html_node(".o-RecipeInfo__a-Description--Total") %>% html_text()
ingredients <- h %>% html_nodes(".o-Ingredients__a-ListItemText") %>% html_text()
return(list(recipe = recipe, prep_time = prep_time, ingredients = ingredients))
}
get_recipe("http://www.foodnetwork.com/recipes/food-network-kitchen/pancakes-recipe-1913844")
Below is an example for Scraping rain data from a website:
year <- c(2013:2017)
date <- c(201301:201312,201401:201412,201501:201512,201601:201612,201701:201712)
td <- data.frame('rd'=NA)
url <- paste0('http://e-service.cwb.gov.tw/HistoryDataQuery/YearDataController.do?command=viewMain&station=466910&stname=%25E9%259E%258D%25E9%2583%25A8&datepicker=',year)
for( i in 1:5){
rd <- url[i] %>%
GET() %>%
read_html() %>%
html_nodes(.,css='td:nth-child(21)') %>%
html_text()
rd <- data.frame('rd'=rd)
td <- rbind(td,rd)
}
td <- td[-1,]
d1 <- data.frame('date'=date)
d1 <- cbind(d1,td)
d1$td <- gsub("\U00A0", "", d1$td)
View(d1)
write.csv(d1, file="C:/Users/shiki/Downloads/1.xlsx")
【String Processing】
str_detect(tab$population, ",") # 回傳TRUE/FALSE
str_subset(tab$population, ",") # 回傳字串 ex. 123,456/1,000
str_extract(tab$population, ",") # 回傳逗號(提取""裡面的東西)
LS0tDQp0aXRsZTogIkRhdGEgV2FyZ2xpbmcgUiBOb3RlYm9vayINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCiMjIyDjgJDnm67pjITjgJENCg0KWzEuIERhdGEgSW1wb3J0XSgjbjEpIDxicj4NClsyLiBUaWR5IERhdGFdKCNuMikgPGJyPg0KWzMuIFdlYiBTY3JhcGluZ10oI24zKSA8YnI+DQpbNC4gU3RyaW5nIFByb2Nlc3NpbmddKCNuNCkgPGJyPg0KDQo8aHI+DQoNCiMjIyA8YSBpZD0ibjEiPjwvYT7jgJBEYXRhIEltcG9ydOOAkQ0KDQpgYGB7cn0NCnN5c3RlbS5maWxlKCkgI+WbnuWCs+agueebrumMhCJDOi9QUk9HUkF+MS9SL1ItMzV+MS4wL2xpYnJhcnkvYmFzZSINCmZpbGUucGF0aCjot6/lvpEs5qqU5ZCNKSAj5pyD5bmr5L2g6Ieq5YuV6YG45bCN55qEc2xhc2jpu4/kuIogDQpmaWxlLmNvcHko6KKr6KSH6KO955qE6Lev5b6RLOassuikh+ijveWIsOeahOi3r+W+kSkNCmBgYA0KDQorIHJlYWRfbGluZXMoKSAj5p+l55yL5pyJ5rKS5pyJaGVhZGVyDQorIFRoZSBpbXBvcnQgZnVuY3Rpb25zIGluIHRoZSByZWFkciBwYWNrYWdlIGFsbCBzdGFydCBhcyByZWFkXywgd2hpbGUgdGhlIGltcG9ydCBmdW5jdGlvbnMgZm9yIGJhc2UgUiBhbGwgc3RhcnQgd2l0aCByZWFkLg0KDQo8YnI+DQoNCiMjIyMg44CQUueahOWfuuacrGZ1bmN0aW9uIFZTIHJlYWRyID/jgJENCg0KKyBCYXNlIFIgZnVuY3Rpb25zIGltcG9ydCBkYXRhIGFzIGEgZGF0YSBmcmFtZSwgd2hpbGUgInJlYWRyIiBmdW5jdGlvbnMgaW1wb3J0IGRhdGEgYXMgYSB0aWJibGUuDQorIOWmguaenHJlYWQuY3N25pmC5rKS5pyJ55SoU3RyaW5nQXNGYWN0b3I9Ru+8jOS7luacg+Wwh2NoYXJhY3Rlcui9ieeCumZhY3Rvcg0KKyBUaGUgYmFzZSBSIGltcG9ydCBmdW5jdGlvbnMgY2FuIHJlYWQgLmNzdiBmaWxlcyxidXQgY2Fubm90IGZpbGVzIHdpdGggb3RoZXIgZGVsaW1pdGVycywgc3VjaCBhcyAudHN2IGZpbGVzLCBvciBmaXhlZC13aWR0aCBmaWxlcy4NCisgVGhlIGJhc2UgUiBpbXBvcnQgZnVuY3Rpb25zIGNhbiByZWFkIGZpbGVzIHdpdGggb3RoZXIgZGVsaW1pdGVycyjliIbpmpTomZ8pIA0KbGlrZSAudHN2IHVzaW5nIHJlYWQuZGVsaW0gYW5kIGNhbiByZWFkIGZpeGVkLXdpZHRoIGZpbGVzIHVzaW5nIHJlYWQuZndmLg0KDQo8YnI+DQoNCiMjIyMg44CQV2hhdCBpcyBUaWJibGUgP+OAkQ0KDQorIEEgdGliYmxlIGlzIGFsc28gYSBkYXRhLmZyYW1lLiANCiAgICArIFRpYmJsZSBwcmludHMgYmV0dGVyIGluIGNvbnNvbGUgKGl0IGZpdHMgdG8gc2NyZWVuIHNpemUpIGFuZCBpdCBhbHNvIHByaW50cyBkYXRhIHR5cGVzIGluIGhlYWRlci4NCiAgICArIFRvIHB1dCBpdCBzaW1wbGUsIHRpYmJsZXMgYXJlICJldm9sdXRpb25zIiBvZiBkYXRhLmZyYW1lcywgcHJpbnRpbmcgYW5kIHN1YnNldHRpbmcgYXJlIGxlc3MgdHJvdWJsZXNvbWUgd2l0aCB0aWJibGVzIHRoYW4gZGF0YS5mcmFtZXMuDQogICAgKyBGb3IgZXhhbXBsZSwgaWYgeW91IHdhbnQgdG8gbG9vayAxMCBmaXJzdCByb3dzIG9mIGEgZGF0YS5mcmFtZSwgeW91IHdpbGwgdXNlIGhlYWQoZGF0YS5mcmFtZSksIGZvciB0aWJibGVzIHlvdSBjYW4gc2ltcGxlIHVzZSBwcmludCh0aWJibGUpIG9yIGNhbGwgdGliYmxlIGluIFIgQ29uc29sZS4NCisgV2hlbiBzdWJzZXR0aW5nLCBpZiB5b3UgdXNlICJbIiBvbiB0aWJibGUgUiB3aWxsIHJldHVybiBhbHdheXMgYW5vdGhlciB0aWJibGUsIGJ1dCB3aGVuIHlvdSB1c2UgIltbIiBSIHdpbGwgcmV0dXJuIGEgdmVjdG9yLg0KKyBGb3IgZGF0YS5mcmFtZXMsIHdoZW4gdXNpbmcgIlsiIHlvdSBtaWdodCBnZXQgYSB2ZWN0b3Igb3IgYW5vdGhlciBkYXRhLmZyYW1lLCB3aGljaCBpcyBxdWl0ZSB1bnBsZWFzYW50LiANCisgQW5vdGhlciBkaWZmZXJlbmNlLCB0aWJibGUgZG9lcyBub3QgaGF2ZSBwYXJ0aWFsIG1hdGNoaW5nLg0KDQpgYGB7cn0NCnVybCA9IGh0dHBzOi8vfi9tdXJkZXJzLmNzdg0KcmVhZF9jc3YodXJsKSAgICAgICAgICAgICAgICAgICAgICAgIyDlj6/ku6Xnm7TmjqXoroDlj5bntrLlnYDmqpQNCmRvd25sb2FkLmZpbGUodXJsLCAibXVyZGVycy5jc3YiKSAgICMg5oqK57ay5LiK55qE5qqU5qGI6LyJ5LiL5L6G5Lim5ZG95ZCN54K6bXVyZGVycy5jc3YNCnRlbXBkaXIoKSANCnRlbXBmaWxlKCkNCiMgdGVtcGZpbGXlibXlu7rkuIDlgIvlrZfnrKbkuLLvvIzogIzkuI3mmK/mlofku7bvvIzpgJnlj6/og73mmK/kuIDlgIvllK/kuIDnmoTmlofku7blkI0NCmBgYA0KDQo8YnI+DQo8aHI+DQoNCiMjIyA8YSBpZD0ibjIiPjwvYT7jgJBUaWR5IERhdGHjgJENCg0KYGBge3J9DQpkYXRfdGlkeSA8LSBkYXRfd2lkZSAlPiUNCmdhdGhlcihrZXkgPSBkaXNlYXNlLCB2YWx1ZSA9IGNvdW50LCDigJxIZXBhdGl0aXMgQeKAnTog4oCcUnViZWxsYeKAnSkNCmBgYA0KICANCisgc3BlY2lmaWVkIHRoYXQgdGhlIOKAnGtleeKAnSBjb2x1bW4gd2lsbCBiZSBjYWxsZWQg4oCcZGlzZWFzZeKAnSwgdGhlIHZhbHVlIG9mIGVhY2ggZW50cnkgd2lsbCBiZSBjYWxsZWQg4oCcY291bnTigJ0sIGFuZCB0aGF0IHRoZSBjb2x1bW5zIEhlcGF0aXRpcyBBIHRocm91Z2ggUnViZWxsYSB3aWxsIGFsbCBiZSBpbmNsdWRlZCBpbiB0aGUgZ2F0aGVyIGNvbW1hbmQuDQorIHNwZWFyZOi3n2dhdGhlcuebuOWPjSxzcGVhcmTmmK/miorkuIDlgIvmrITkvY3oo6HpnaJu5YCLZmFjdG9y6K6K5oiQbuWAi+ashOS9jSzkuKbmiormr49yb3flj7PpgornmoTlgLzovLjlhaVu5YCL5qyE5L2N5LiL6Z2iDQorIGdhdGhlcuaYr+aKim7lgIvmrITkvY3ogZrpm4bmiJAx5YCL5paw55qE5qyE5L2NLOS4puWJteesrDLlgIvmlrDnmoTmrITkvY3miopu5qyE5LiL55qEb2JzLui8uOWFpeWwjeaHieeahHJvdw0KDQpgYGB7cn0NCmdhdGhlcihrZXkgPSBrZXksIHZhbHVlID0gdmFsdWUsIC1hZ2VfZ3JvdXApICAjIOaKiumZpOS6huWFtuS4reS4gOWAi+S7peWklueahOWFqOmDqOeahOiuiuaVuOmDveaLv+WOu2dhdGhlcg0KYGBgDQoNCg0KYGBge3J9DQpEICU+JSBnYXRoZXIoa2V5PWtleSwgdmFsdWU9dmFsdWUsIC1hZ2VfZ3JvdXApICU+JQ0KICBzZXBhcmF0ZShjb2w9a2V5LCBpbnRvPWMoInllYXIiLCAidmFyaWFibGVfbmFtZSIpLCAiXyIpICU+JSANCiAgc3ByZWFkKGtleT12YXJpYWJsZV9uYW1lLCB2YWx1ZT12YWx1ZSkgJT4lIGRhdGEuZnJhbWUNCiMg6buY6KqN55qEc2VwYXJhdGXlsLHmmK8iXyLkuoYs5omA5Lul5Y2z5L2/5LiN5a+r5Lmf5rKS6Zec5L+CDQpgYGANCg0KDQpgYGB7cn0NCnN0YXRzICU+JQ0KICBzZXBhcmF0ZShjb2w9a2V5LCBpbnRvPWMoInBsYXllciIsICJ2YXJpYWJsZV9uYW1lIiksIHNlcD0iXyIsIGV4dHJhPSJtZXJnZSIpICU+JSANCiAgc3ByZWFkKGtleT12YXJpYWJsZV9uYW1lLCB2YWx1ZT12YWx1ZSkNCiMg562J5ZCM5pa8DQpzdGF0cyAlPiUNCiAgc2VwYXJhdGUoY29sPWtleSwgaW50bz1jKCJwbGF5ZXIiLCAidmFyaWFibGVfbmFtZTEiLCAidmFyaWFibGVfbmFtZTIiKSwgDQogICAgICAgICAgIHNlcD0iXyIsIGZpbGw9InJpZ2h0IikgJT4lIA0KICB1bml0ZShjb2wgPSB2YXJpYWJsZV9uYW1lLCB2YXJpYWJsZV9uYW1lMSwgdmFyaWFibGVfbmFtZTIsIHNlcCA9ICJfIikgJT4lIA0KICBzcHJlYWQoa2V5ID0gdmFyaWFibGVfbmFtZSwgdmFsdWUgPSB2YWx1ZSkNCiMgZmlsbD0icmlnaHQi5piv5Y+r5LuW5oqKbmFtZTEsbmFtZTLnmoTmrITkvY3pu4/lnKhwbGF5ZXLlj7PpgooNCmBgYA0KDQo8YnI+DQoNCiMjIyMg44CQTWVyZ2UgZGF0YeOAkQ0KDQorIGxlZnRfam9pbigpDQogICAgKyBhbGwgcm93cyBpbiB0aGUgbGVmdC1oYW5kIHRhYmxlIGFyZSByZXRhaW5lZCBpbiB0aGUgZmluYWwgdGFibGUsIGNvbHVtbnMgZnJvbSBib3RoIHRhYmxlcyB3aWxsIGJlIGluY2x1ZGVkIGluIHRoZSBmaW5hbCB0YWJsZQ0KICAgICsg5bem6YKK5L+d5oyB5Y6f5qijLOWPs+mCiuWwjeeFp+W3pumCiueahG9icy7miorlj7PpgornmoRjb2x1bW7lkozlgLzliqDlhaUoTkHkvoboh6rlj7PpgornmoQpDQorIHJpZ2h0X2pvaW4oKQ0KICAgICsg6IiHbGVmdF9qb2lu55u45Y+NDQorIGlubmVyX2pvaW4oKQ0KICAgICsg6YG45YWp6YKK6YO95pyJb2JzLueahGNvbHVtbizmiorku5blgJFtZXJnZei1t+S+hijlj5bkuqTpm4YpDQorIGZ1bGxfam9pbigpDQogICAgKyDkuI3nrqHmmK/lkKbmrITliJflsI3nqLEs5YWo6YOo6LOH5paZ5L+d55WZLOe8uumgheWhnk5BKOWPluiBr+mbhikNCisgc2VtaV9qb2luKCkNCiAgICArIOW+nmlubmVyX2pvaW7nmoTnu5PmnpzkuK3lj6rlj5blt6bpgornmoTlgLwNCisgYW50aV9qb2luKCkNCiAgICArIOiIh3NlbWlfam9pbuebuOWPjSzngrrlt6bpgornjajmnInnmoRvYnMuDQoNCjxicj4NCg0KYGBge3J9DQpCaW5kX2NvbHVtbih0YjEsdGIyKSA9PSBjYmluZCgpDQpCaW5kX3Jvdyh0YjEsdGIyLHRiMykgPT0gcmJpbmQoKQ0KYGBgDQoNCisgQmluZGluZyBmdW5jdGlvbnMgY29tYmluZSBieSBwb3NpdGlvbiwgd2hpbGUgam9pbiBmdW5jdGlvbnMgbWF0Y2ggYnkgdmFyaWFibGVzLg0KDQorIEpvaW5pbmcgZnVuY3Rpb25zIGNhbiBqb2luIGRhdGFzZXRzIG9mIGRpZmZlcmVudCBkaW1lbnNpb25zLCBidXQgdGhlIGJpbmQgZnVuY3Rpb25zIG11c3QgbWF0Y2ggb24gdGhlIGFwcHJvcHJpYXRlIGRpbWVuc2lvbiAoZWl0aGVyIHNhbWUgcm93IG9yIGNvbHVtbiBudW1iZXJzKS4NCg0KKyBCaW5kIGZ1bmN0aW9ucyBjYW4gY29tYmluZSBib3RoIHZlY3RvcnMgYW5kIGRhdGFmcmFtZXMsIHdoaWxlIGpvaW4gZnVuY3Rpb25zIHdvcmsgZm9yIG9ubHkgZm9yIGRhdGFmcmFtZXMuDQoNCg0KYGBge3J9DQppbnRlcnNlY3QoYygxOjEwKSxjKDY6MTUpKQ0KaW50ZXJzZWN0KGMoImEiLCJiIiksYygiYiIsImMiKSkgDQpgYGANCiANCmBgYHtyfQ0Kc2V0ZGlmZihjKDE6MTApLGMoNjoxNSkpDQpgYGANCg0KYGBge3J9DQpzZXRlcXVhbChjKDE6MTApLGMoNjoxNSkpDQpzZXRlcXVhbChjKDE6MTApLGMoMToxMCkpDQpgYGANCg0KYGBge3J9DQp1bmlvbihjKDE6MTApLGMoNjoxNSkpDQpgYGANCg0KPGJyPg0KPGhyPg0KDQojIyMgPGEgaWQ9Im4zIj48L2E+44CQV2ViIFNjcmFwaW5n44CRDQoNCjxicj4NCg0KKyBUaGUgaHRtbF9ub2RlIGNvbW1hbmQgb25seSBzZWxlY3RzIHRoZSBmaXJzdCBub2RlIG9mIGEgc3BlY2lmaWVkIHR5cGUuDQorIGh0bWxfbm9kZXMgc28gdGhhdCB3ZSBjb3VsZCBzcGVjaWZ5IHRoZSBlbGVtZW50cyB3ZSB3YW50LiANCiAgICArIEZvciBleGFtcGxlLCB0aGUgc2Vjb25kIOKAnHRhYmxl4oCdIGVsZW1lbnQgdXNpbmcgdGhlIHRhYltbMl1dIGNvbW1hbmQuDQorIHNlbGVjdG9yIGdhcmdldCDmlZnlrbg6aHR0cHM6Ly9zZWxlY3RvcmdhZGdldC5jb20vDQoNCmBgYHtyfQ0KbGlicmFyeShydmVzdCkNCnVybCA9IGh0dHBzOi8vfg0KaCA9IHJlYWRfaHRtbCh1cmwpDQp0YWIgPSBoICU+JSBodG1sX25vZGVzKCJ0YWJsZSIpDQp0YWIgPSB0YWJbWzJdXSAjIOmChOaYr+S4gOihjOS4gOihjOS6guS6gueahA0KdGFiID0gdGFiICU+JSBodG1sX3RhYmxlDQpjbGFzcyh0YWIpDQpbMV0gICJkYXRhLmZyYW1lIg0KYGBgDQogIA0KICANCkZvciB0aGUgZ3VhY2Ftb2xlIHJlY2lwZSBwYWdlIHdlIGFscmVhZHkgaGF2ZSBkb25lIHRoaXMgYW5kIGRldGVybWluZWQgdGhhdCB3ZSBuZWVkIHRoZSBmb2xsb3dpbmcgc2VsZWN0b3JzOg0KICANCmBgYHtyfQ0KbGlicmFyeShydmVzdCkNCmggPC0gcmVhZF9odG1sKCJodHRwOi8vd3d3LmZvb2RuZXR3b3JrLmNvbS9yZWNpcGVzL2FsdG9uLWJyb3duL2d1YWNhbW9sZS1yZWNpcGUtMTk0MDYwOSIpDQpyZWNpcGUgPC0gaCAlPiUgaHRtbF9ub2RlKCIuby1Bc3NldFRpdGxlX19hLUhlYWRsaW5lVGV4dCIpICU+JSBodG1sX3RleHQoKQ0KcHJlcF90aW1lIDwtIGggJT4lIGh0bWxfbm9kZSgiLm8tUmVjaXBlSW5mb19fYS1EZXNjcmlwdGlvbi0tVG90YWwiKSAlPiUgaHRtbF90ZXh0KCkNCmluZ3JlZGllbnRzIDwtIGggJT4lIGh0bWxfbm9kZXMoIi5vLUluZ3JlZGllbnRzX19hLUxpc3RJdGVtVGV4dCIpICU+JSBodG1sX3RleHQoKQ0KYGBgDQoNCllvdSBjYW4gc2VlIGhvdyBjb21wbGV4IHRoZSBzZWxlY3RvcnMgYXJlLiBJbiBhbnkgY2FzZSB3ZSBhcmUgbm93IHJlYWR5IHRvIGV4dHJhY3Qgd2hhdCB3ZSB3YW50IGFuZCBjcmVhdGUgYSBsaXN0Og0KICANCmBgYHtyfQ0KZ3VhY2Ftb2xlIDwtIGxpc3QocmVjaXBlLCBwcmVwX3RpbWUsIGluZ3JlZGllbnRzKQ0KYGBgDQoNClNpbmNlIHJlY2lwZSBwYWdlcyBmcm9tIHRoaXMgd2Vic2l0ZSBmb2xsb3cgdGhpcyBnZW5lcmFsIGxheW91dCwgd2UgY2FuIHVzZSB0aGlzIGNvZGUgdG8gY3JlYXRlIGEgZnVuY3Rpb24gdGhhdCBleHRyYWN0cyB0aGlzIGluZm9ybWF0aW9uOg0KICANCmBgYHtyfQ0KZ2V0X3JlY2lwZSA8LSBmdW5jdGlvbih1cmwpew0KICAgIGggPC0gcmVhZF9odG1sKHVybCkNCiAgICByZWNpcGUgPC0gaCAlPiUgaHRtbF9ub2RlKCIuby1Bc3NldFRpdGxlX19hLUhlYWRsaW5lVGV4dCIpICU+JSBodG1sX3RleHQoKQ0KICAgIHByZXBfdGltZSA8LSBoICU+JSBodG1sX25vZGUoIi5vLVJlY2lwZUluZm9fX2EtRGVzY3JpcHRpb24tLVRvdGFsIikgJT4lIGh0bWxfdGV4dCgpDQogICAgaW5ncmVkaWVudHMgPC0gaCAlPiUgaHRtbF9ub2RlcygiLm8tSW5ncmVkaWVudHNfX2EtTGlzdEl0ZW1UZXh0IikgJT4lIGh0bWxfdGV4dCgpDQogICAgcmV0dXJuKGxpc3QocmVjaXBlID0gcmVjaXBlLCBwcmVwX3RpbWUgPSBwcmVwX3RpbWUsIGluZ3JlZGllbnRzID0gaW5ncmVkaWVudHMpKQ0KfQ0KDQpnZXRfcmVjaXBlKCJodHRwOi8vd3d3LmZvb2RuZXR3b3JrLmNvbS9yZWNpcGVzL2Zvb2QtbmV0d29yay1raXRjaGVuL3BhbmNha2VzLXJlY2lwZS0xOTEzODQ0IikNCmBgYA0KDQoNCkJlbG93IGlzIGFuIGV4YW1wbGUgZm9yIFNjcmFwaW5nIHJhaW4gZGF0YSBmcm9tIGEgd2Vic2l0ZToNCiAgDQpgYGB7cn0NCnllYXIgPC0gYygyMDEzOjIwMTcpDQpkYXRlIDwtIGMoMjAxMzAxOjIwMTMxMiwyMDE0MDE6MjAxNDEyLDIwMTUwMToyMDE1MTIsMjAxNjAxOjIwMTYxMiwyMDE3MDE6MjAxNzEyKQ0KdGQgPC0gZGF0YS5mcmFtZSgncmQnPU5BKQ0KdXJsIDwtIHBhc3RlMCgnaHR0cDovL2Utc2VydmljZS5jd2IuZ292LnR3L0hpc3RvcnlEYXRhUXVlcnkvWWVhckRhdGFDb250cm9sbGVyLmRvP2NvbW1hbmQ9dmlld01haW4mc3RhdGlvbj00NjY5MTAmc3RuYW1lPSUyNUU5JTI1OUUlMjU4RCUyNUU5JTI1ODMlMjVBOCZkYXRlcGlja2VyPScseWVhcikNCmZvciggaSBpbiAxOjUpew0KICByZCA8LSB1cmxbaV0gJT4lIA0KICAgIEdFVCgpICU+JSANCiAgICByZWFkX2h0bWwoKSAlPiUgDQogICAgaHRtbF9ub2RlcyguLGNzcz0ndGQ6bnRoLWNoaWxkKDIxKScpICU+JSANCiAgICBodG1sX3RleHQoKQ0KICByZCA8LSBkYXRhLmZyYW1lKCdyZCc9cmQpDQogIHRkIDwtIHJiaW5kKHRkLHJkKQ0KfQ0KdGQgPC0gdGRbLTEsXQ0KZDEgPC0gZGF0YS5mcmFtZSgnZGF0ZSc9ZGF0ZSkNCmQxIDwtIGNiaW5kKGQxLHRkKQ0KDQpkMSR0ZCA8LSBnc3ViKCJcVTAwQTAiLCAiIiwgZDEkdGQpDQpWaWV3KGQxKQ0KDQp3cml0ZS5jc3YoZDEsIGZpbGU9IkM6L1VzZXJzL3NoaWtpL0Rvd25sb2Fkcy8xLnhsc3giKQ0KYGBgDQoNCg0KPGJyPjxocj4NCg0KIyMjIDxhIGlkPSJuNCI+PC9hPuOAkFN0cmluZyBQcm9jZXNzaW5n44CRDQogIA0KYGBge3J9DQpzdHJfZGV0ZWN0KHRhYiRwb3B1bGF0aW9uLCAiLCIpICAgIyDlm57lgrNUUlVFL0ZBTFNFDQpzdHJfc3Vic2V0KHRhYiRwb3B1bGF0aW9uLCAiLCIpICAgIyDlm57lgrPlrZfkuLIgZXguIDEyMyw0NTYvMSwwMDANCnN0cl9leHRyYWN0KHRhYiRwb3B1bGF0aW9uLCAiLCIpICAjIOWbnuWCs+mAl+iZnyjmj5Dlj5YiIuijoemdoueahOadseilvykNCmBgYA0KDQo8YnI+PGJyPjxocj48YnI+PGJyPjxicj4NCiAgDQoNCjxzdHlsZT4NCnAsbGkgew0KICBmb250LWZhbWlseTogIlRyZWJ1Y2hldCBNUyIsICLlvq7ou5/mraPpu5Hpq5QiLCAiTWljcm9zb2Z0IEpoZW5nSGVpIjsNCn0NCg0KdGl0bGV7DQogICAgZm9udC1mYW1pbHk6ICJUcmVidWNoZXQgTVMiLCAi5b6u6Luf5q2j6buR6auUIiwgIk1pY3Jvc29mdCBKaGVuZ0hlaSI7DQp9DQoNCmJvZHl7DQogIGZvbnQtZmFtaWx5OiAiVHJlYnVjaGV0IE1TIiwgIuW+rui7n+ato+m7kemrlCIsICJNaWNyb3NvZnQgSmhlbmdIZWkiOw0KfQ0KDQpoMSxoMixoMyxoNCxoNXsNCiAgICBmb250LWZhbWlseTogIlRyZWJ1Y2hldCBNUyIsICLlvq7ou5/mraPpu5Hpq5QiLCAiTWljcm9zb2Z0IEpoZW5nSGVpIjsNCn0NCjwvc3R5bGU+PGJyPjxicj48YnI+PGJyPjxicj4NCg0KPHN0eWxlPg0KLmNhcHRpb24gew0KICBjb2xvcjogIzc3NzsNCiAgbWFyZ2luLXRvcDogMTBweDsNCn0NCnAgY29kZSB7DQogIHdoaXRlLXNwYWNlOiBpbmhlcml0Ow0KfQ0KcHJlIHsNCiAgd29yZC1icmVhazogbm9ybWFsOw0KICB3b3JkLXdyYXA6IG5vcm1hbDsNCiAgbGluZS1oZWlnaHQ6IDE7DQp9DQpwcmUgY29kZSB7DQogIHdoaXRlLXNwYWNlOiBpbmhlcml0Ow0KfQ0KcCxsaSB7DQogIGZvbnQtZmFtaWx5OiAiVHJlYnVjaGV0IE1TIiwgIuW+rui7n+ato+m7kemrlCIsICJNaWNyb3NvZnQgSmhlbmdIZWkiOw0KfQ0KDQoucnsNCiAgbGluZS1oZWlnaHQ6IDEuMjsNCn0NCg0KdGl0bGV7DQogIGNvbG9yOiAjY2MwMDAwOw0KICBmb250LWZhbWlseTogIlRyZWJ1Y2hldCBNUyIsICLlvq7ou5/mraPpu5Hpq5QiLCAiTWljcm9zb2Z0IEpoZW5nSGVpIjsNCn0NCg0KYm9keXsNCiAgZm9udC1mYW1pbHk6ICJUcmVidWNoZXQgTVMiLCAi5b6u6Luf5q2j6buR6auUIiwgIk1pY3Jvc29mdCBKaGVuZ0hlaSI7DQp9DQoNCmgxLGgyLGgzLGg0LGg1ew0KICBjb2xvcjogIzAwODgwMDsNCiAgZm9udC1mYW1pbHk6ICJUcmVidWNoZXQgTVMiLCAi5b6u6Luf5q2j6buR6auUIiwgIk1pY3Jvc29mdCBKaGVuZ0hlaSI7DQp9DQoNCmgzew0KICBjb2xvcjogI2IzNmIwMDsNCiAgYmFja2dyb3VuZDogI2ZmZTBiMzsNCiAgbGluZS1oZWlnaHQ6IDI7DQogIGZvbnQtd2VpZ2h0OiBib2xkOw0KfQ0KDQpoNXsNCiAgY29sb3I6ICMwMDYwMDA7DQogIGJhY2tncm91bmQ6ICNmZmZmZTA7DQogIGxpbmUtaGVpZ2h0OiAyOw0KICBmb250LXdlaWdodDogYm9sZDsNCn0NCg0KZW17DQogIGNvbG9yOiAjMDAwMGMwOw0KICBiYWNrZ3JvdW5kOiAjZjBmMGYwOw0KICB9DQo8L3N0eWxlPg0KDQo=