dplyr cheatsheet.pdf

library("nycflights13")
library("sqldf")

关系型数据

关系数据最常见于关系数据库管理系统(relational database management system, RDBMS),该系统几乎囊括了所有的现代数据库。

在处理关系数据时,一般来说,dplyr 包要比 SQL 语言更容易使用1

预备:数据表之间的关系

nycflights13 包中有勾稽关系的五张表:

head(airlines, n = 5) # 可以根据航空公司的缩写码查到公司全名
#> # A tibble: 5 x 2
#>   carrier name                  
#>   <chr>   <chr>                 
#> 1 9E      Endeavor Air Inc.     
#> 2 AA      American Airlines Inc.
#> 3 AS      Alaska Airlines Inc.  
#> 4 B6      JetBlue Airways       
#> 5 DL      Delta Air Lines Inc.
head(airports, n = 5) # 给出了每个机场的信息,通过faa机场编码进行标识
#> # A tibble: 5 x 8
#>   faa   name                            lat   lon   alt    tz dst   tzone       
#>   <chr> <chr>                         <dbl> <dbl> <dbl> <dbl> <chr> <chr>       
#> 1 04G   Lansdowne Airport              41.1 -80.6  1044    -5 A     America/New~
#> 2 06A   Moton Field Municipal Airport  32.5 -85.7   264    -6 A     America/Chi~
#> 3 06C   Schaumburg Regional            42.0 -88.1   801    -6 A     America/Chi~
#> 4 06N   Randall Airport                41.4 -74.4   523    -5 A     America/New~
#> 5 09J   Jekyll Island Airport          31.1 -81.4    11    -5 A     America/New~
head(planes, n = 5) # 给出了每架飞机的信息,通过 tailnum 进行标识
#> # A tibble: 5 x 9
#>   tailnum  year type               manufacturer model engines seats speed engine
#>   <chr>   <int> <chr>              <chr>        <chr>   <int> <int> <int> <chr> 
#> 1 N10156   2004 Fixed wing multi ~ EMBRAER      EMB-~       2    55    NA Turbo~
#> 2 N102UW   1998 Fixed wing multi ~ AIRBUS INDU~ A320~       2   182    NA Turbo~
#> 3 N103US   1999 Fixed wing multi ~ AIRBUS INDU~ A320~       2   182    NA Turbo~
#> 4 N104UW   1999 Fixed wing multi ~ AIRBUS INDU~ A320~       2   182    NA Turbo~
#> 5 N10575   2002 Fixed wing multi ~ EMBRAER      EMB-~       2    55    NA Turbo~
head(weather, n = 5) # 给出了纽约机场每小时的天气状况
#> # A tibble: 5 x 15
#>   origin  year month   day  hour  temp  dewp humid wind_dir wind_speed wind_gust
#>   <chr>  <int> <int> <int> <int> <dbl> <dbl> <dbl>    <dbl>      <dbl>     <dbl>
#> 1 EWR     2013     1     1     1  39.0  26.1  59.4      270      10.4         NA
#> 2 EWR     2013     1     1     2  39.0  27.0  61.6      250       8.06        NA
#> 3 EWR     2013     1     1     3  39.0  28.0  64.4      240      11.5         NA
#> 4 EWR     2013     1     1     4  39.9  28.0  62.2      250      12.7         NA
#> 5 EWR     2013     1     1     5  39.0  28.0  64.4      260      12.7         NA
#> # ... with 4 more variables: precip <dbl>, pressure <dbl>, visib <dbl>,
#> #   time_hour <dttm>
head(flights, n = 5) # 2013年从纽约市出发的所有336,776次航班的信息
#> # A tibble: 5 x 19
#>    year month   day dep_time sched_dep_time dep_delay arr_time sched_arr_time
#>   <int> <int> <int>    <int>          <int>     <dbl>    <int>          <int>
#> 1  2013     1     1      517            515         2      830            819
#> 2  2013     1     1      533            529         4      850            830
#> 3  2013     1     1      542            540         2      923            850
#> 4  2013     1     1      544            545        -1     1004           1022
#> 5  2013     1     1      554            600        -6      812            837
#> # ... with 11 more variables: arr_delay <dbl>, carrier <chr>, flight <int>,
#> #   tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>,
#> #   hour <dbl>, minute <dbl>, time_hour <dttm>

5张表之间的关系如下图:

其中,flights居于核心地位

用于连接每对数据表的变量称为键。键是能唯一标识观测的变量或变量集合(可能一个表找不到任何单一变量作为键,那么就必须使用多个变量作为键)。键的类型有两种:

  • 主键:唯一标识其所在数据表中的观测。例如,planes$tailnum 是一个主键,因为其可以唯一标识 planes 表中的每架飞机。

  • 外键:唯一标识另一个数据表中的观测。例如,flights$tailnum 是一个外键,因为其出现在 flights 表中,并可以将每次航班与唯一一架飞机匹配(对应 planes 表的 tailnum)。

一个变量既可以是主键(或其一部分),也可以是外键(或其一部分)。例如,origin 是 weather 表主键(year、month、day、hour和origin)的一部分,同时也是 airports 表的外键(对应airport表的faa或name,它们都是airport的主键)。

一旦识别出表的主键,最好验证一下,看看它们能否真正唯一标识每个观测。一种验证方法是对主键进行 count() 操作2,然后查看是否有n大于1的记录:

planes %>%
  count(tailnum) %>%
  filter(n > 1)
#> # A tibble: 0 x 2
#> # ... with 2 variables: tailnum <chr>, n <int>
weather %>%
  count(year, month, day, hour, origin) %>%
  filter(n > 1)
#> # A tibble: 3 x 6
#>    year month   day  hour origin     n
#>   <int> <int> <int> <int> <chr>  <int>
#> 1  2013    11     3     1 EWR        2
#> 2  2013    11     3     1 JFK        2
#> 3  2013    11     3     1 LGA        2

如果一张表没有主键,有时就需要使用 mutate() %>% row_number() 函数为表加上一个主键3。这种主键称为代理键。

主键与另一张表中与之对应的外键构成关系

数据表的关系运算:Data Join

合并 join

合并连接可以将两个表格中的变量组合起来,它先通过两个表格的键匹配观测,然后将一个表格中的变量复制到另一个表格中

一个例子

flights2 <- flights %>%
  select(year:day, hour, origin, dest, tailnum, carrier)
head(flights2, n = 5)
#> # A tibble: 5 x 8
#>    year month   day  hour origin dest  tailnum carrier
#>   <int> <int> <int> <dbl> <chr>  <chr> <chr>   <chr>  
#> 1  2013     1     1     5 EWR    IAH   N14228  UA     
#> 2  2013     1     1     5 LGA    IAH   N24211  UA     
#> 3  2013     1     1     5 JFK    MIA   N619AA  AA     
#> 4  2013     1     1     5 JFK    BQN   N804JB  B6     
#> 5  2013     1     1     6 LGA    ATL   N668DN  DL

想要将航空公司的全名加入flights2数据集,你可以通过left_join()函数组合airlines 和 flights2 数据框

flights2 %>%
  select(-origin, -dest) %>%
  left_join(airlines, by = "carrier")
#> # A tibble: 336,776 x 7
#>     year month   day  hour tailnum carrier name                    
#>    <int> <int> <int> <dbl> <chr>   <chr>   <chr>                   
#>  1  2013     1     1     5 N14228  UA      United Air Lines Inc.   
#>  2  2013     1     1     5 N24211  UA      United Air Lines Inc.   
#>  3  2013     1     1     5 N619AA  AA      American Airlines Inc.  
#>  4  2013     1     1     5 N804JB  B6      JetBlue Airways         
#>  5  2013     1     1     6 N668DN  DL      Delta Air Lines Inc.    
#>  6  2013     1     1     5 N39463  UA      United Air Lines Inc.   
#>  7  2013     1     1     6 N516JB  B6      JetBlue Airways         
#>  8  2013     1     1     6 N829AS  EV      ExpressJet Airlines Inc.
#>  9  2013     1     1     6 N593JB  B6      JetBlue Airways         
#> 10  2013     1     1     6 N3ALAA  AA      American Airlines Inc.  
#> # ... with 336,766 more rows
# 另一种操作方法
flights2 %>%
  select(-origin, -dest) %>%
  mutate(name = airlines$name[match(carrier, airlines$carrier)])
#> # A tibble: 336,776 x 7
#>     year month   day  hour tailnum carrier name                    
#>    <int> <int> <int> <dbl> <chr>   <chr>   <chr>                   
#>  1  2013     1     1     5 N14228  UA      United Air Lines Inc.   
#>  2  2013     1     1     5 N24211  UA      United Air Lines Inc.   
#>  3  2013     1     1     5 N619AA  AA      American Airlines Inc.  
#>  4  2013     1     1     5 N804JB  B6      JetBlue Airways         
#>  5  2013     1     1     6 N668DN  DL      Delta Air Lines Inc.    
#>  6  2013     1     1     5 N39463  UA      United Air Lines Inc.   
#>  7  2013     1     1     6 N516JB  B6      JetBlue Airways         
#>  8  2013     1     1     6 N829AS  EV      ExpressJet Airlines Inc.
#>  9  2013     1     1     6 N593JB  B6      JetBlue Airways         
#> 10  2013     1     1     6 N3ALAA  AA      American Airlines Inc.  
#> # ... with 336,766 more rows
# 但这种方式很难推广到需要匹配多个变量的情况,而且需要仔细阅读代码才能搞清楚操作目的。

内连接

inner_join(x, y, by = NULL, copy = FALSE, suffix = c(".x", ".y"),...)

内连接的结果是一个新数据框,其中包含键和两表键以外的列,保留两表均有的行。我们使用by参数告诉dplyr哪个变量是键。

x <- tribble(
  ~key, ~val_x,
  1, "x1",
  2, "x2",
  3, "x3"
)
y <- tribble(
  ~key, ~val_y,
  1, "y1",
  2, "y2",
  4, "y3"
)

x %>% inner_join(y, by = "key")
#> # A tibble: 2 x 3
#>     key val_x val_y
#>   <dbl> <chr> <chr>
#> 1     1 x1    y1   
#> 2     2 x2    y2

内连接最重要的性质是,没有匹配的行不会包含在结果中。这意味着内连接一般不适合在分析中使用,因为太容易丢失观测了。

外连接

内连接保留同时存在于两个表中的观测,外连接则保留至少存在于一个表中的观测。无法充分匹配的观测,会产生NA值。

外连接有 3 种类型:左连接,保留 x 中的所有观测;右连接,保留 y 中的所有观测;全连接,保留 x 和 y 中的所有观测。

left_join(x, y, by = NULL, copy = FALSE, suffix = c(".x", ".y"), ...)

right_join(x, y, by = NULL, copy = FALSE, suffix = c(".x", ".y"), ...)

full_join(x, y, by = NULL, copy = FALSE, suffix = c(".x", ".y"), ...)

最常用的连接是左连接:只要想从另一张表中添加新变量,就可以使用左连接,因为它会保留原表中的所有观测,即使它没有匹配。左连接是默认选择,除非有足够充分的理由选择其他的连接方式。

x %>% left_join(y, by = "key")
#> # A tibble: 3 x 3
#>     key val_x val_y
#>   <dbl> <chr> <chr>
#> 1     1 x1    y1   
#> 2     2 x2    y2   
#> 3     3 x3    <NA>
x %>% right_join(y, by = "key")
#> # A tibble: 3 x 3
#>     key val_x val_y
#>   <dbl> <chr> <chr>
#> 1     1 x1    y1   
#> 2     2 x2    y2   
#> 3     4 <NA>  y3
x %>% full_join(y, by = "key")
#> # A tibble: 4 x 3
#>     key val_x val_y
#>   <dbl> <chr> <chr>
#> 1     1 x1    y1   
#> 2     2 x2    y2   
#> 3     3 x3    <NA> 
#> 4     4 <NA>  y3

重复键

一张表有重复键

通常来说,当存在一对多关系时,如果你想要向表中添加额外信息,就会出现这种情况。

x <- tribble(
  ~key, ~val_x,
  1, "x1",
  2, "x2",
  2, "x3",
  1, "x4"
)
y <- tribble(
  ~key, ~val_y,
  1, "y1",
  2, "y2"
)

left_join(x, y, by = "key")
#> # A tibble: 4 x 3
#>     key val_x val_y
#>   <dbl> <chr> <chr>
#> 1     1 x1    y1   
#> 2     2 x2    y2   
#> 3     2 x3    y2   
#> 4     1 x4    y1

两张表都有重复键

这通常意味着出现了错误,因为键在任意一张表中都不能唯一标识观测。当连接这样的重复键时,你会得到所有可能的组合,即笛卡儿积。

x <- tribble(
  ~key, ~val_x,
  1, "x1",
  2, "x2",
  2, "x3",
  3, "x4"
)
y <- tribble(
  ~key, ~val_y,
  1, "y1",
  2, "y2",
  2, "y3",
  3, "y4"
)

left_join(x, y, by = "key")
#> # A tibble: 6 x 3
#>     key val_x val_y
#>   <dbl> <chr> <chr>
#> 1     1 x1    y1   
#> 2     2 x2    y2   
#> 3     2 x2    y3   
#> 4     2 x3    y2   
#> 5     2 x3    y3   
#> 6     3 x4    y4

筛选 join

筛选连接匹配两表数据的方式与合并连接相同,但不添加新列。筛选连接有两种类型:半连接和反连接。

  • semi_join(x, y), 半连接。依据key筛选 x 中也在 y 里出现的行(rows of x that have a match in y),不在key中的列不作为筛选标准。
  • anti_join(x, y), 反连接。依据key筛选 x 中但不在 y 里出现的行(rows of x that do not have a match in y)。反连接可用于删除,如x为分词后的文本数据,y为停用词数据)

例:已经找出了最受欢迎的前 10 个目的地(即去那里的航班数最多)

top_dest <- flights %>%
  count(dest, sort = TRUE) %>%
  head(10)
top_dest
#> # A tibble: 10 x 2
#>    dest      n
#>    <chr> <int>
#>  1 ORD   17283
#>  2 ATL   17215
#>  3 LAX   16174
#>  4 BOS   15508
#>  5 MCO   14082
#>  6 CLT   14064
#>  7 SFO   13331
#>  8 FLL   12055
#>  9 MIA   11728
#> 10 DCA    9705

现在想要找出飞往这些目的地的所有航班,你可以自己构造一个筛选器

flights %>%
  filter(dest %in% top_dest$dest) %>%
  head(n = 5)
#> # A tibble: 5 x 19
#>    year month   day dep_time sched_dep_time dep_delay arr_time sched_arr_time
#>   <int> <int> <int>    <int>          <int>     <dbl>    <int>          <int>
#> 1  2013     1     1      542            540         2      923            850
#> 2  2013     1     1      554            600        -6      812            837
#> 3  2013     1     1      554            558        -4      740            728
#> 4  2013     1     1      555            600        -5      913            854
#> 5  2013     1     1      557            600        -3      838            846
#> # ... with 11 more variables: arr_delay <dbl>, carrier <chr>, flight <int>,
#> #   tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>,
#> #   hour <dbl>, minute <dbl>, time_hour <dttm>

但这种方法很难扩展到多个变量。例如,假设已经找出了平均延误时间最长的10天,那么你应该如何使用year、month和day来构造筛选语句,才能在flights中找出这10天的观测?此时你应该使用半连接,它可以像合并连接一样连接两个表,但不添加新列,而是保留x表中那些可以匹配4y表的行:

flights %>%
  semi_join(top_dest) %>%
  head(n = 5)
#> # A tibble: 5 x 19
#>    year month   day dep_time sched_dep_time dep_delay arr_time sched_arr_time
#>   <int> <int> <int>    <int>          <int>     <dbl>    <int>          <int>
#> 1  2013     1     1      542            540         2      923            850
#> 2  2013     1     1      554            600        -6      812            837
#> 3  2013     1     1      554            558        -4      740            728
#> 4  2013     1     1      555            600        -5      913            854
#> 5  2013     1     1      557            600        -3      838            846
#> # ... with 11 more variables: arr_delay <dbl>, carrier <chr>, flight <int>,
#> #   tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>,
#> #   hour <dbl>, minute <dbl>, time_hour <dttm>

半连接的图形表示如下所示。重要的是存在匹配,匹配了哪条观测则无关紧要。这说明筛选连接不会像合并连接那样造成重复的行。

半连接的逆操作是反连接。反连接保留x表中那些没有匹配y表的行。

反连接可以用于诊断连接中的不匹配。例如,在连接 flights 和 planes 时,你可能想知道 flights 中是否有很多行在 planes 中没有匹配记录:

flights %>%
  anti_join(planes, by = "tailnum") %>%
  count(tailnum, sort = TRUE)
#> # A tibble: 722 x 2
#>    tailnum     n
#>    <chr>   <int>
#>  1 <NA>     2512
#>  2 N725MQ    575
#>  3 N722MQ    513
#>  4 N723MQ    507
#>  5 N713MQ    483
#>  6 N735MQ    396
#>  7 N0EGMQ    371
#>  8 N534MQ    364
#>  9 N542MQ    363
#> 10 N531MQ    349
#> # ... with 712 more rows

定义 key

默认 by = NULL

这会使用同时存在于两个表中的所有变量,这种方式称为自然连接

例如匹配航班表和天气表时使用的就是其公共变量:year、month、day、 hour和origin

flights2 %>%
  left_join(weather) %>%
  head(n = 5)
#> # A tibble: 5 x 18
#>    year month   day  hour origin dest  tailnum carrier  temp  dewp humid
#>   <int> <int> <int> <dbl> <chr>  <chr> <chr>   <chr>   <dbl> <dbl> <dbl>
#> 1  2013     1     1     5 EWR    IAH   N14228  UA       39.0  28.0  64.4
#> 2  2013     1     1     5 LGA    IAH   N24211  UA       39.9  25.0  54.8
#> 3  2013     1     1     5 JFK    MIA   N619AA  AA       39.0  27.0  61.6
#> 4  2013     1     1     5 JFK    BQN   N804JB  B6       39.0  27.0  61.6
#> 5  2013     1     1     6 LGA    ATL   N668DN  DL       39.9  25.0  54.8
#> # ... with 7 more variables: wind_dir <dbl>, wind_speed <dbl>, wind_gust <dbl>,
#> #   precip <dbl>, pressure <dbl>, visib <dbl>, time_hour <dttm>

by = c("a", "b", ...)

这种方式与自然连接很相似,但只使用一部分公共变量。

例如,flights和planes表中都有year变量,但是它们的意义不同,因此我们只通过tailnum进行连接:

flights2 %>%
  left_join(planes, by = "tailnum") %>%
  head(n = 5)
#> # A tibble: 5 x 16
#>   year.x month   day  hour origin dest  tailnum carrier year.y type             
#>    <int> <int> <int> <dbl> <chr>  <chr> <chr>   <chr>    <int> <chr>            
#> 1   2013     1     1     5 EWR    IAH   N14228  UA        1999 Fixed wing multi~
#> 2   2013     1     1     5 LGA    IAH   N24211  UA        1998 Fixed wing multi~
#> 3   2013     1     1     5 JFK    MIA   N619AA  AA        1990 Fixed wing multi~
#> 4   2013     1     1     5 JFK    BQN   N804JB  B6        2012 Fixed wing multi~
#> 5   2013     1     1     6 LGA    ATL   N668DN  DL        1991 Fixed wing multi~
#> # ... with 6 more variables: manufacturer <chr>, model <chr>, engines <int>,
#> #   seats <int>, speed <int>, engine <chr>

by = c("a" = "b")

这种方式会匹配 x 表中的 a 变量和 y 表中的 b 变量。输出结果中使用的是 x 表中的变量。

例如,如果想要画出一幅地图,那么我们就需要在航班数据中加入机场数据,后者包含了每个机场的位置(lat和lon)。因为每次航班都有起点机场和终点机场,所以需要指定使用哪个机场进行连接:

flights2 %>%
  left_join(airports, c("dest" = "faa")) %>%
  head(n = 5)
#> # A tibble: 5 x 15
#>    year month   day  hour origin dest  tailnum carrier name      lat   lon   alt
#>   <int> <int> <int> <dbl> <chr>  <chr> <chr>   <chr>   <chr>   <dbl> <dbl> <dbl>
#> 1  2013     1     1     5 EWR    IAH   N14228  UA      George~  30.0 -95.3    97
#> 2  2013     1     1     5 LGA    IAH   N24211  UA      George~  30.0 -95.3    97
#> 3  2013     1     1     5 JFK    MIA   N619AA  AA      Miami ~  25.8 -80.3     8
#> 4  2013     1     1     5 JFK    BQN   N804JB  B6      <NA>     NA    NA      NA
#> 5  2013     1     1     6 LGA    ATL   N668DN  DL      Hartsf~  33.6 -84.4  1026
#> # ... with 3 more variables: tz <dbl>, dst <chr>, tzone <chr>
flights2 %>%
  left_join(airports, c("origin" = "faa")) %>%
  head(n = 5)
#> # A tibble: 5 x 15
#>    year month   day  hour origin dest  tailnum carrier name      lat   lon   alt
#>   <int> <int> <int> <dbl> <chr>  <chr> <chr>   <chr>   <chr>   <dbl> <dbl> <dbl>
#> 1  2013     1     1     5 EWR    IAH   N14228  UA      Newark~  40.7 -74.2    18
#> 2  2013     1     1     5 LGA    IAH   N24211  UA      La Gua~  40.8 -73.9    22
#> 3  2013     1     1     5 JFK    MIA   N619AA  AA      John F~  40.6 -73.8    13
#> 4  2013     1     1     5 JFK    BQN   N804JB  B6      John F~  40.6 -73.8    13
#> 5  2013     1     1     6 LGA    ATL   N668DN  DL      La Gua~  40.8 -73.9    22
#> # ... with 3 more variables: tz <dbl>, dst <chr>, tzone <chr>

suffix = c("1", "2")

对于两表有相同列名但又不是key的列,加入后缀进行区分

base::merge()

参数为数据框

base::merge() 可以实现所有 4 种合并连接操作:

dplyr包 基础包merge函数
inner_join(x, y) merge(x, y)
left_join(x, y) merge(x, y, all.x = TRUE)
right_join(x, y) merge(x, y, all.y = TRUE)
full_join(x, y) merge(x, y, all.x = TRUE, all.y = TRUE)

dplyr 连接操作的优点是,可以更加清晰地表达出代码的意图:不同连接间的区别确实非常重要,但隐藏在 merge() 函数的参数中了。dplyr 连接操作的速度明显更快,而且不会弄乱行的顺序。

参数为向量

base::merge(x, y) 有一个额外的优点,就是它的参数 x, y 可以是向量,此时效果为对二元数值对 (x, y) 所有可能取值的遍历(返回数据框)

与之相似的函数是 expand.grid(x, y)

x <- 1:3
y <- letters[1:3]
z <- 1:3
base::merge(x, y)
#>   x y
#> 1 1 a
#> 2 2 a
#> 3 3 a
#> 4 1 b
#> 5 2 b
#> 6 3 b
#> 7 1 c
#> 8 2 c
#> 9 3 c
base::merge(x, z)
#>   x y
#> 1 1 1
#> 2 2 1
#> 3 3 1
#> 4 1 2
#> 5 2 2
#> 6 3 2
#> 7 1 3
#> 8 2 3
#> 9 3 3
expand.grid(x, y)
#>   Var1 Var2
#> 1    1    a
#> 2    2    a
#> 3    3    a
#> 4    1    b
#> 5    2    b
#> 6    3    b
#> 7    1    c
#> 8    2    c
#> 9    3    c
expand.grid(x, z)
#>   Var1 Var2
#> 1    1    1
#> 2    2    1
#> 3    3    1
#> 4    1    2
#> 5    2    2
#> 6    3    2
#> 7    1    3
#> 8    2    3
#> 9    3    3

SQL

SQL是dplyr连接操作的灵感来源,二者之间的转换非常简单明了(见下表)。与dplyr相比,SQL支持的连接类型更广泛,因为SQL可以使用除相等关系外的其他逻辑关系 (ON conditional_expression) 来连接两个表(有时这称为非等值连接)。

dplyr包 SQL语言
inner_join(x, y, by = "z") SELECT * FROM x INNER JOIN y USING (z)SELECT * FROM x INNER JOIN y ON conditional_expression
left_join(x, y, by = "z") SELECT * FROM x LEFT OUTER JOIN y USING (z)SELECT * FROM x LEFT OUTER JOIN y ON conditional_expression
right_join(x, y, by = "z") SELECT * FROM x RIGHT OUTER JOIN y USING (z)SELECT * FROM x RIGHT OUTER JOIN y ON conditional_expression
full_join(x, y, by = "z") SELECT * FROM x FULL OUTER JOIN y USING (z)SELECT * FROM x FULL OUTER JOIN y ON conditional_expression

合并 join 的应用场景

数据分析中许多常见问题,实为关系数据问题。

例:现有国别表,包含country和gdp两个变量,为了进一步分析,需要添加新变量region,说明这些国家所在的地理大区。那么应该如何操作呢?

乍一看,用dplyr::mutate()很难操作,因为新变量要根据country变量的不同取值赋不同的值,这在mutate()的框架下是无法实现的。仔细分析,其实这是一个关系数据问题,因为存在着两个表:country和gdp是一个表,country是主键;country和region的对应关系又是一个表,country还是主键。只不过我们习惯于接受前一个表是数据表,而忽视了后一个对应关系也是一个表。于是,这个添加新变量的过程,便可以表达为两个表通过country这个桥梁合并连接的过程,可以用dplyr::left_join()5 或SQL语言来实现了。

gdp <-
  tribble(~country, ~gdp, "德国", "20", "中国", "50", "美国", "70", "日本", "25")
gdp
#> # A tibble: 4 x 2
#>   country gdp  
#>   <chr>   <chr>
#> 1 德国    20   
#> 2 中国    50   
#> 3 美国    70   
#> 4 日本    25
code <-
  tribble(~country, ~region, "德国", "欧盟", "中国", "东亚", "美国", "北美", "日本", "东亚", "韩国", "东亚")
code
#> # A tibble: 5 x 2
#>   country region
#>   <chr>   <chr> 
#> 1 德国    欧盟  
#> 2 中国    东亚  
#> 3 美国    北美  
#> 4 日本    东亚  
#> 5 韩国    东亚
## 三种等价操作
# 方法1:使用下标索引,以向量为操作单位
join1 <- gdp
join1$region <- "欧盟"
join1[join1$country %in% c("中国", "日本", "韩国"), ]$region <- "东亚"
join1[join1$country == "美国", ]$region <- "北美"
join1
#> # A tibble: 4 x 3
#>   country gdp   region
#>   <chr>   <chr> <chr> 
#> 1 德国    20    欧盟  
#> 2 中国    50    东亚  
#> 3 美国    70    北美  
#> 4 日本    25    东亚
# 方法2:dplyr包,以数据框为操作单位
join2 <- left_join(gdp, code, by = "country")
join2
#> # A tibble: 4 x 3
#>   country gdp   region
#>   <chr>   <chr> <chr> 
#> 1 德国    20    欧盟  
#> 2 中国    50    东亚  
#> 3 美国    70    北美  
#> 4 日本    25    东亚
# 方法3:sqldf包和sql语言
join3 <- sqldf("select * from gdp left outer join code using (country)")
join3
#>   country gdp region
#> 1    德国  20   欧盟
#> 2    中国  50   东亚
#> 3    美国  70   北美
#> 4    日本  25   东亚

join 前要注意的问题

  1. 首先,需要找出每个表中可以作为主键的变量。一般应该基于对数据的理解来确定主键,而不是凭经验寻找能作为唯一标识符的变量组合。如果在确定主键时根本没有考虑过其意义,那么就可能步入歧途,虽然可以找出具有唯一性的变量组合,但它与数据间的关系却可能不是真实的。例如,经度和纬度虽然能够唯一标识每个机场,但却不是良好的标识符!
airports %>%
  count(alt, lon) %>%
  filter(n > 1)
#> # A tibble: 0 x 3
#> # ... with 3 variables: alt <dbl>, lon <dbl>, n <int>
  1. 确保主键中的每个变量都没有缺失值。如果有缺失值,那么这个变量就不能标识观测!

  2. 检查外键是否与另一张表的主键相匹配。最好的方法是使用anti_join(),由于数据录入错误,外键和主键不匹配的情况很常见。解决这种问题通常需要大量工作。

数据表的集合运算

观测(行)作为元素,数据表视为观测的集合。集合操作需要两张表具有完全相同的变量。

  • intersect(),交集,只保留两表中均有的行
  • setdiff(),集合的差,只保留第一个表含有、第二表不含的行
  • union(),并集,保留所有行,但重复的只保留一次
  • union_all(),全集且不舍弃重复行
  • setequal(),检测两个表所含的行是否完全相同(不考虑顺序,像集合一样)
df1 <- tribble(
  ~x, ~y,
  1, 1,
  2, 1
)
df2 <- tribble(
  ~x, ~y,
  1, 1,
  1, 2
)

intersect(df1, df2)
#> # A tibble: 1 x 2
#>       x     y
#>   <dbl> <dbl>
#> 1     1     1
union(df1, df2)
#> # A tibble: 3 x 2
#>       x     y
#>   <dbl> <dbl>
#> 1     1     1
#> 2     2     1
#> 3     1     2
setdiff(df1, df2)
#> # A tibble: 1 x 2
#>       x     y
#>   <dbl> <dbl>
#> 1     2     1
setdiff(df2, df1)
#> # A tibble: 1 x 2
#>       x     y
#>   <dbl> <dbl>
#> 1     1     2

  1. 虽然SQL语言更全面。↩︎

  2. 以某变量或某些变量为依据,合并计数。↩︎

  3. 以序号为主键。这样一来,如果你完成了一些筛选工作,并想要使用原始数据检查的话,就可以更容易地匹配观测。↩︎

  4. 即在同样的变量下有同样的值。↩︎

  5. 考虑到gdp数据表中的国家可能不全,而国家与地区的对应关系表通常是稳定而完备的,因此应该用前者左连接后者,保存前者的所有观测。↩︎

LS0tDQp0aXRsZTogIuWFs+ezu+aVsOaNruWkhOeQhiINCnN1YnRpdGxlOiAnJw0KYXV0aG9yOiAiSHVtb29uIg0KZGF0ZTogImByIFN5cy5EYXRlKClgIg0Kb3V0cHV0OiBodG1sX2RvY3VtZW50DQpkb2N1bWVudGNsYXNzOiBjdGV4YXJ0DQpjbGFzc29wdGlvbjogaHlwZXJyZWYsDQotLS0NCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQpzb3VyY2UoIi4uL1JtYXJrZG93bi10ZW1wbGF0ZS9SbWFya2Rvd25fY29uZmlnLlIiKQ0KDQojIyBnbG9iYWwgb3B0aW9ucyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KA0KICB3aWR0aCA9IGNvbmZpZyR3aWR0aCwNCiAgZmlnLndpZHRoID0gY29uZmlnJGZpZy53aWR0aCwNCiAgZmlnLmFzcCA9IGNvbmZpZyRmaWcuYXNwLA0KICBvdXQud2lkdGggPSBjb25maWckb3V0LndpZHRoLA0KICBmaWcuYWxpZ24gPSBjb25maWckZmlnLmFsaWduLA0KICBmaWcucGF0aCA9IGNvbmZpZyRmaWcucGF0aCwNCiAgZmlnLnNob3cgPSBjb25maWckZmlnLnNob3csDQogIHdhcm4gPSBjb25maWckd2FybiwNCiAgd2FybmluZyA9IGNvbmZpZyR3YXJuaW5nLA0KICBtZXNzYWdlID0gY29uZmlnJG1lc3NhZ2UsDQogIGVjaG8gPSBjb25maWckZWNobywNCiAgZXZhbCA9IGNvbmZpZyRldmFsLA0KICB0aWR5ID0gY29uZmlnJHRpZHksDQogIGNvbW1lbnQgPSBjb25maWckY29tbWVudCwNCiAgY29sbGFwc2UgPSBjb25maWckY29sbGFwc2UsDQogIGNhY2hlID0gY29uZmlnJGNhY2hlLA0KICBjYWNoZS5jb21tZW50cyA9IGNvbmZpZyRjYWNoZS5jb21tZW50cywNCiAgYXV0b2RlcCA9IGNvbmZpZyRhdXRvZGVwDQopDQoNCiMjIHVzZSBuZWNlc3NhcnkgcGFja2FnZXMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkoZGF0YS50YWJsZSkNCmxpYnJhcnkobWFncml0dHIpDQpsaWJyYXJ5KHBsb3RseSkNCmxpYnJhcnkoaHRtbHdpZGdldHMpDQpgYGANCg0KPGEgaHJlZj0iLi4vcGRmL2NoZWF0c2hlZXQtZHBseXIucGRmIj4qZHBseXIgY2hlYXRzaGVldC5wZGYqPC9hPg0KDQo8b2JqZWN0IGRhdGE9Ii4uL3BkZi9jaGVhdHNoZWV0LWRwbHlyLnBkZiIgdHlwZT0iYXBwbGljYXRpb24vcGRmIiB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIj4NCg0KPC9vYmplY3Q+DQoNCmBgYHtyfQ0KbGlicmFyeSgibnljZmxpZ2h0czEzIikNCmxpYnJhcnkoInNxbGRmIikNCmBgYA0KDQojIyDlhbPns7vlnovmlbDmja4NCg0K5YWz57O75pWw5o2u5pyA5bi46KeB5LqO5YWz57O75pWw5o2u5bqT566h55CG57O757uf77yIcmVsYXRpb25hbCBkYXRhYmFzZSBtYW5hZ2VtZW50IHN5c3RlbSwgUkRCTVPvvInvvIzor6Xns7vnu5/lh6DkuY7lm4rmi6zkuobmiYDmnInnmoTnjrDku6PmlbDmja7lupPjgIINCg0K5Zyo5aSE55CG5YWz57O75pWw5o2u5pe277yM5LiA6Iis5p2l6K+077yMZHBseXIg5YyF6KaB5q+UIFNRTCDor63oqIDmm7TlrrnmmJPkvb/nlKhbXjFdIOOAgg0KDQpbXjFdOiDomb3nhLZTUUzor63oqIDmm7TlhajpnaLjgIINCg0KIyMjIOmihOWkh++8muaVsOaNruihqOS5i+mXtOeahOWFs+ezuw0KDQpueWNmbGlnaHRzMTMg5YyF5Lit5pyJ5Yu+56i95YWz57O755qE5LqU5byg6KGo77yaDQoNCmBgYHtyfQ0KaGVhZChhaXJsaW5lcywgbiA9IDUpICMg5Y+v5Lul5qC55o2u6Iiq56m65YWs5Y+455qE57yp5YaZ56CB5p+l5Yiw5YWs5Y+45YWo5ZCNDQpoZWFkKGFpcnBvcnRzLCBuID0gNSkgIyDnu5nlh7rkuobmr4/kuKrmnLrlnLrnmoTkv6Hmga/vvIzpgJrov4dmYWHmnLrlnLrnvJbnoIHov5vooYzmoIfor4YNCmhlYWQocGxhbmVzLCBuID0gNSkgIyDnu5nlh7rkuobmr4/mnrbpo57mnLrnmoTkv6Hmga/vvIzpgJrov4cgdGFpbG51bSDov5vooYzmoIfor4YNCmhlYWQod2VhdGhlciwgbiA9IDUpICMg57uZ5Ye65LqG57q957qm5py65Zy65q+P5bCP5pe255qE5aSp5rCU54q25Ya1DQpoZWFkKGZsaWdodHMsIG4gPSA1KSAjIDIwMTPlubTku47nur3nuqbluILlh7rlj5HnmoTmiYDmnIkzMzYsNzc25qyh6Iiq54+t55qE5L+h5oGvDQpgYGANCg0KNeW8oOihqOS5i+mXtOeahOWFs+ezu+WmguS4i+Wbvu+8mg0KDQohW10oaHR0cDovL2h1bW9vbi1pbWFnZS1ob3N0aW5nLXNlcnZpY2Uub3NzLWNuLWJlaWppbmcuYWxpeXVuY3MuY29tL2ltZy90eXBvcmEvMjAyMi8yMDIyMDMyMi1yZWxhdGlvbnMucG5nKQ0KDQrlhbbkuK3vvIxmbGlnaHRz5bGF5LqO5qC45b+D5Zyw5L2NDQoNCiMjIyDplK4NCg0K55So5LqO6L+e5o6l5q+P5a+55pWw5o2u6KGo55qE5Y+Y6YeP56ew5Li66ZSu44CC6ZSu5piv6IO95ZSv5LiA5qCH6K+G6KeC5rWL55qEKirlj5jph4/miJblj5jph4/pm4blkIgqKijlj6/og73kuIDkuKrooajmib7kuI3liLDku7vkvZXljZXkuIDlj5jph4/kvZzkuLrplK7vvIzpgqPkuYjlsLHlv4Xpobvkvb/nlKjlpJrkuKrlj5jph4/kvZzkuLrplK4p44CC6ZSu55qE57G75Z6L5pyJ5Lik56eN77yaDQoNCi0gICDkuLvplK7vvJrllK/kuIDmoIfor4blhbbmiYDlnKjmlbDmja7ooajkuK3nmoTop4LmtYvjgILkvovlpoLvvIxwbGFuZXNcJHRhaWxudW0g5piv5LiA5Liq5Li76ZSu77yM5Zug5Li65YW25Y+v5Lul5ZSv5LiA5qCH6K+GIHBsYW5lcyDooajkuK3nmoTmr4/mnrbpo57mnLrjgIINCg0KLSAgIOWklumUru+8muWUr+S4gOagh+ivhuWPpuS4gOS4quaVsOaNruihqOS4reeahOingua1i+OAguS+i+Wmgu+8jGZsaWdodHNcJHRhaWxudW0g5piv5LiA5Liq5aSW6ZSu77yM5Zug5Li65YW25Ye6546w5ZyoIGZsaWdodHMg6KGo5Lit77yM5bm25Y+v5Lul5bCG5q+P5qyh6Iiq54+t5LiO5ZSv5LiA5LiA5p626aOe5py65Yy56YWN77yI5a+55bqUIHBsYW5lcyDooajnmoQgdGFpbG51be+8ieOAgg0KDQrkuIDkuKrlj5jph4/ml6Llj6/ku6XmmK/kuLvplK7vvIjmiJblhbbkuIDpg6jliIbvvInvvIzkuZ/lj6/ku6XmmK/lpJbplK7vvIjmiJblhbbkuIDpg6jliIbvvInjgILkvovlpoLvvIxvcmlnaW4g5pivIHdlYXRoZXIg6KGo5Li76ZSuKHllYXLjgIFtb250aOOAgWRheeOAgWhvdXLlkoxvcmlnaW4p55qE5LiA6YOo5YiG77yM5ZCM5pe25Lmf5pivIGFpcnBvcnRzIOihqOeahOWklumUru+8iOWvueW6lGFpcnBvcnTooajnmoRmYWHmiJZuYW1l77yM5a6D5Lus6YO95pivYWlycG9ydOeahOS4u+mUru+8ieOAgg0KDQrkuIDml6bor4bliKvlh7rooajnmoTkuLvplK7vvIzmnIDlpb3pqozor4HkuIDkuIvvvIznnIvnnIvlroPku6zog73lkKbnnJ/mraPllK/kuIDmoIfor4bmr4/kuKrop4LmtYvjgIIqKuS4gOenjemqjOivgeaWueazleaYr+WvueS4u+mUrui/m+ihjCBjb3VudCgpIOaTjeS9nCoqW14yXe+8jOeEtuWQjuafpeeci+aYr+WQpuaciW7lpKfkuo4x55qE6K6w5b2V77yaDQoNClteMl06ICoq5Lul5p+Q5Y+Y6YeP5oiW5p+Q5Lqb5Y+Y6YeP5Li65L6d5o2u77yM5ZCI5bm26K6h5pWw44CCKioNCg0KYGBge3J9DQpwbGFuZXMgJT4lDQogIGNvdW50KHRhaWxudW0pICU+JQ0KICBmaWx0ZXIobiA+IDEpDQoNCndlYXRoZXIgJT4lDQogIGNvdW50KHllYXIsIG1vbnRoLCBkYXksIGhvdXIsIG9yaWdpbikgJT4lDQogIGZpbHRlcihuID4gMSkNCmBgYA0KDQrlpoLmnpzkuIDlvKDooajmsqHmnInkuLvplK7vvIzmnInml7blsLHpnIDopoHkvb/nlKggbXV0YXRlKCkgJVw+JSByb3dfbnVtYmVyKCkg5Ye95pWw5Li66KGo5Yqg5LiK5LiA5Liq5Li76ZSuW14zXeOAgui/meenjeS4u+mUruensOS4uuS7o+eQhumUruOAgg0KDQpbXjNdOiDku6Xluo/lj7fkuLrkuLvplK7jgILov5nmoLfkuIDmnaXvvIzlpoLmnpzkvaDlrozmiJDkuobkuIDkupvnrZvpgInlt6XkvZzvvIzlubbmg7PopoHkvb/nlKjljp/lp4vmlbDmja7mo4Dmn6XnmoTor53vvIzlsLHlj6/ku6Xmm7TlrrnmmJPlnLDljLnphY3op4LmtYvjgIINCg0KKirkuLvplK7kuI7lj6bkuIDlvKDooajkuK3kuI7kuYvlr7nlupTnmoTlpJbplK7mnoTmiJDlhbPns7sqKuOAgg0KDQojIyDmlbDmja7ooajnmoTlhbPns7vov5DnrpfvvJpEYXRhIEpvaW4NCg0KIyMjIOWQiOW5tiBqb2luDQoNCuWQiOW5tui/nuaOpeWPr+S7peWwhuS4pOS4quihqOagvOS4reeahOWPmOmHj+e7hOWQiOi1t+adpe+8jOWug+WFiOmAmui/h+S4pOS4quihqOagvOeahOmUruWMuemFjeingua1i++8jOeEtuWQjioq5bCG5LiA5Liq6KGo5qC85Lit55qE5Y+Y6YeP5aSN5Yi25Yiw5Y+m5LiA5Liq6KGo5qC85LitKirjgIINCg0KIyMjIyDkuIDkuKrkvovlrZANCg0KYGBge3J9DQpmbGlnaHRzMiA8LSBmbGlnaHRzICU+JQ0KICBzZWxlY3QoeWVhcjpkYXksIGhvdXIsIG9yaWdpbiwgZGVzdCwgdGFpbG51bSwgY2FycmllcikNCmhlYWQoZmxpZ2h0czIsIG4gPSA1KQ0KYGBgDQoNCuaDs+imgeWwhuiIquepuuWFrOWPuOeahOWFqOWQjeWKoOWFpWZsaWdodHMy5pWw5o2u6ZuG77yM5L2g5Y+v5Lul6YCa6L+HbGVmdF9qb2luKCnlh73mlbDnu4TlkIhhaXJsaW5lcyDlkowgZmxpZ2h0czIg5pWw5o2u5qGGDQoNCmBgYHtyfQ0KZmxpZ2h0czIgJT4lDQogIHNlbGVjdCgtb3JpZ2luLCAtZGVzdCkgJT4lDQogIGxlZnRfam9pbihhaXJsaW5lcywgYnkgPSAiY2FycmllciIpDQoNCiMg5Y+m5LiA56eN5pON5L2c5pa55rOVDQpmbGlnaHRzMiAlPiUNCiAgc2VsZWN0KC1vcmlnaW4sIC1kZXN0KSAlPiUNCiAgbXV0YXRlKG5hbWUgPSBhaXJsaW5lcyRuYW1lW21hdGNoKGNhcnJpZXIsIGFpcmxpbmVzJGNhcnJpZXIpXSkNCiMg5L2G6L+Z56eN5pa55byP5b6I6Zq+5o6o5bm/5Yiw6ZyA6KaB5Yy56YWN5aSa5Liq5Y+Y6YeP55qE5oOF5Ya177yM6ICM5LiU6ZyA6KaB5LuU57uG6ZiF6K+75Luj56CB5omN6IO95pCe5riF5qWa5pON5L2c55uu55qE44CCDQpgYGANCg0KIyMjIyDlhoXov57mjqUNCg0KYGlubmVyX2pvaW4oeCwgeSwgYnkgPSBOVUxMLCBjb3B5ID0gRkFMU0UsIHN1ZmZpeCA9IGMoIi54IiwgIi55IiksLi4uKWANCg0K5YaF6L+e5o6l55qE57uT5p6c5piv5LiA5Liq5paw5pWw5o2u5qGG77yM5YW25Lit5YyF5ZCr6ZSu5ZKM5Lik6KGo6ZSu5Lul5aSW55qE5YiX77yM5L+d55WZKirkuKTooajlnYfmnIkqKueahOihjOOAguaIkeS7rOS9v+eUqGJ55Y+C5pWw5ZGK6K+JZHBseXLlk6rkuKrlj5jph4/mmK/plK7jgIINCg0KIVtdKGltZy9pbm5lci1qb2luLnBuZykNCg0KDQpgYGB7cn0NCnggPC0gdHJpYmJsZSgNCiAgfmtleSwgfnZhbF94LA0KICAxLCAieDEiLA0KICAyLCAieDIiLA0KICAzLCAieDMiDQopDQp5IDwtIHRyaWJibGUoDQogIH5rZXksIH52YWxfeSwNCiAgMSwgInkxIiwNCiAgMiwgInkyIiwNCiAgNCwgInkzIg0KKQ0KDQp4ICU+JSBpbm5lcl9qb2luKHksIGJ5ID0gImtleSIpDQpgYGANCg0KKirlhoXov57mjqXmnIDph43opoHnmoTmgKfotKjmmK/vvIzmsqHmnInljLnphY3nmoTooYzkuI3kvJrljIXlkKvlnKjnu5PmnpzkuK3jgIIqKui/meaEj+WRs+edgOWGhei/nuaOpeS4gOiIrOS4jemAguWQiOWcqOWIhuaekOS4reS9v+eUqO+8jOWboOS4uuWkquWuueaYk+S4ouWkseingua1i+S6huOAgg0KDQojIyMjIOWklui/nuaOpSANCg0K5YaF6L+e5o6l5L+d55WZ5ZCM5pe25a2Y5Zyo5LqO5Lik5Liq6KGo5Lit55qE6KeC5rWL77yM5aSW6L+e5o6l5YiZ5L+d55WZ6Iez5bCR5a2Y5Zyo5LqO5LiA5Liq6KGo5Lit55qE6KeC5rWL44CC5peg5rOV5YWF5YiG5Yy56YWN55qE6KeC5rWL77yM5Lya5Lqn55SfTkHlgLzjgIINCg0KIVtdKGltZy9vdXRlci1qb2luLnBuZykNCg0KDQrlpJbov57mjqXmnIkgMyDnp43nsbvlnovvvJrlt6bov57mjqXvvIzkv53nlZkgeCDkuK3nmoTmiYDmnInop4LmtYvvvJvlj7Pov57mjqXvvIzkv53nlZkgeSDkuK3nmoTmiYDmnInop4LmtYvvvJvlhajov57mjqXvvIzkv53nlZkgeCDlkowgeSDkuK3nmoTmiYDmnInop4LmtYvjgIINCg0KYGxlZnRfam9pbih4LCB5LCBieSA9IE5VTEwsIGNvcHkgPSBGQUxTRSwgc3VmZml4ID0gYygiLngiLCAiLnkiKSwgLi4uKWANCg0KYHJpZ2h0X2pvaW4oeCwgeSwgYnkgPSBOVUxMLCBjb3B5ID0gRkFMU0UsIHN1ZmZpeCA9IGMoIi54IiwgIi55IiksIC4uLilgDQoNCmBmdWxsX2pvaW4oeCwgeSwgYnkgPSBOVUxMLCBjb3B5ID0gRkFMU0UsIHN1ZmZpeCA9IGMoIi54IiwgIi55IiksIC4uLilgDQoNCuacgOW4uOeUqOeahOi/nuaOpeaYr+W3pui/nuaOpe+8muWPquimgeaDs+S7juWPpuS4gOW8oOihqOS4rea3u+WKoOaWsOWPmOmHj++8jOWwseWPr+S7peS9v+eUqOW3pui/nuaOpe+8jOWboOS4uuWug+S8muS/neeVmeWOn+ihqOS4reeahOaJgOacieingua1i++8jOWNs+S9v+Wug+ayoeacieWMuemFjeOAguW3pui/nuaOpeaYr+m7mOiupOmAieaLqe+8jOmZpOmdnuaciei2s+Wkn+WFheWIhueahOeQhueUsemAieaLqeWFtuS7lueahOi/nuaOpeaWueW8j+OAgg0KDQpgYGB7cn0NCnggJT4lIGxlZnRfam9pbih5LCBieSA9ICJrZXkiKQ0KeCAlPiUgcmlnaHRfam9pbih5LCBieSA9ICJrZXkiKQ0KeCAlPiUgZnVsbF9qb2luKHksIGJ5ID0gImtleSIpDQpgYGANCg0KDQojIyMjIOmHjeWkjemUrg0KDQrkuIDlvKDooajmnInph43lpI3plK4NCg0K6YCa5bi45p2l6K+077yM5b2T5a2Y5Zyo5LiA5a+55aSa5YWz57O75pe277yM5aaC5p6c5L2g5oOz6KaB5ZCR6KGo5Lit5re75Yqg6aKd5aSW5L+h5oGv77yM5bCx5Lya5Ye6546w6L+Z56eN5oOF5Ya144CCDQoNCg0KIVtdKGltZy9kdXBsaWNhdGUta2V5cy1vbmUtdGFibGUucG5nKQ0KDQpgYGB7cn0NCnggPC0gdHJpYmJsZSgNCiAgfmtleSwgfnZhbF94LA0KICAxLCAieDEiLA0KICAyLCAieDIiLA0KICAyLCAieDMiLA0KICAxLCAieDQiDQopDQp5IDwtIHRyaWJibGUoDQogIH5rZXksIH52YWxfeSwNCiAgMSwgInkxIiwNCiAgMiwgInkyIg0KKQ0KDQpsZWZ0X2pvaW4oeCwgeSwgYnkgPSAia2V5IikNCmBgYA0KDQrkuKTlvKDooajpg73mnInph43lpI3plK4NCg0K6L+Z6YCa5bi45oSP5ZGz552A5Ye6546w5LqG6ZSZ6K+v77yM5Zug5Li66ZSu5Zyo5Lu75oSP5LiA5byg6KGo5Lit6YO95LiN6IO95ZSv5LiA5qCH6K+G6KeC5rWL44CC5b2T6L+e5o6l6L+Z5qC355qE6YeN5aSN6ZSu5pe277yM5L2g5Lya5b6X5Yiw5omA5pyJ5Y+v6IO955qE57uE5ZCI77yM5Y2z56yb5Y2h5YS/56ev44CCIA0KDQohW10oaW1nL2R1cGxpY2F0ZS1rZXlzLXR3by10YWJsZXMucG5nKQ0KDQoNCg0KYGBge3J9DQp4IDwtIHRyaWJibGUoDQogIH5rZXksIH52YWxfeCwNCiAgMSwgIngxIiwNCiAgMiwgIngyIiwNCiAgMiwgIngzIiwNCiAgMywgIng0Ig0KKQ0KeSA8LSB0cmliYmxlKA0KICB+a2V5LCB+dmFsX3ksDQogIDEsICJ5MSIsDQogIDIsICJ5MiIsDQogIDIsICJ5MyIsDQogIDMsICJ5NCINCikNCg0KbGVmdF9qb2luKHgsIHksIGJ5ID0gImtleSIpDQpgYGANCg0KDQojIyMg562b6YCJIGpvaW4NCg0K562b6YCJ6L+e5o6l5Yy56YWN5Lik6KGo5pWw5o2u55qE5pa55byP5LiO5ZCI5bm26L+e5o6l55u45ZCM77yM5L2G5LiN5re75Yqg5paw5YiX44CC562b6YCJ6L+e5o6l5pyJ5Lik56eN57G75Z6L77ya5Y2K6L+e5o6l5ZKM5Y+N6L+e5o6l44CCDQoNCiogYHNlbWlfam9pbih4LCB5KWAsIOWNiui/nuaOpeOAguS+neaNrmtleeetm+mAiSB4IOS4reS5n+WcqCB5IOmHjOWHuueOsOeahOihjO+8iHJvd3Mgb2YgeCB0aGF0IGhhdmUgYSBtYXRjaCBpbiB577yJ77yM5LiN5Zyoa2V55Lit55qE5YiX5LiN5L2c5Li6562b6YCJ5qCH5YeG44CCDQoqIGBhbnRpX2pvaW4oeCwgeSlgLCDlj43ov57mjqXjgILkvp3mja5rZXnnrZvpgIkgeCDkuK3kvYbkuI3lnKggeSDph4zlh7rnjrDnmoTooYzvvIhyb3dzIG9mIHggdGhhdCBkbyBub3QgaGF2ZSBhIG1hdGNoIGluIHnvvInjgILlj43ov57mjqXlj6/nlKjkuo7liKDpmaTvvIzlpoJ45Li65YiG6K+N5ZCO55qE5paH5pys5pWw5o2u77yMeeS4uuWBnOeUqOivjeaVsOaNru+8iQ0KDQrkvovvvJrlt7Lnu4/mib7lh7rkuobmnIDlj5fmrKLov47nmoTliY0gMTAg5Liq55uu55qE5Zyw77yI5Y2z5Y676YKj6YeM55qE6Iiq54+t5pWw5pyA5aSa77yJDQpgYGB7cn0NCnRvcF9kZXN0IDwtIGZsaWdodHMgJT4lDQogIGNvdW50KGRlc3QsIHNvcnQgPSBUUlVFKSAlPiUNCiAgaGVhZCgxMCkNCnRvcF9kZXN0DQpgYGANCg0K546w5Zyo5oOz6KaB5om+5Ye66aOe5b6A6L+Z5Lqb55uu55qE5Zyw55qE5omA5pyJ6Iiq54+t77yM5L2g5Y+v5LulKiroh6rlt7HmnoTpgKDkuIDkuKrnrZvpgInlmagqKu+8mg0KYGBge3J9DQpmbGlnaHRzICU+JQ0KICBmaWx0ZXIoZGVzdCAlaW4lIHRvcF9kZXN0JGRlc3QpICU+JQ0KICBoZWFkKG4gPSA1KQ0KYGBgDQoNCioq5L2G6L+Z56eN5pa55rOV5b6I6Zq+5omp5bGV5Yiw5aSa5Liq5Y+Y6YePKirjgILkvovlpoLvvIzlgYforr7lt7Lnu4/mib7lh7rkuoblubPlnYflu7bor6/ml7bpl7TmnIDplb/nmoQxMOWkqe+8jOmCo+S5iOS9oOW6lOivpeWmguS9leS9v+eUqHllYXLjgIFtb250aOWSjGRheeadpeaehOmAoOetm+mAieivreWPpe+8jOaJjeiDveWcqGZsaWdodHPkuK3mib7lh7rov5kxMOWkqeeahOingua1i++8n+atpOaXtuS9oOW6lOivpeS9v+eUqOWNiui/nuaOpe+8jOWug+WPr+S7peWDj+WQiOW5tui/nuaOpeS4gOagt+i/nuaOpeS4pOS4quihqO+8jOS9huS4jea3u+WKoOaWsOWIl++8jOiAjOaYr+S/neeVmXjooajkuK3pgqPkupvlj6/ku6UqKuWMuemFjSoqXlvljbPlnKjlkIzmoLfnmoTlj5jph4/kuIvmnInlkIzmoLfnmoTlgLzjgIJdeeihqOeahOihjO+8mg0KDQpgYGB7cn0NCmZsaWdodHMgJT4lDQogIHNlbWlfam9pbih0b3BfZGVzdCkgJT4lDQogIGhlYWQobiA9IDUpDQpgYGANCg0K5Y2K6L+e5o6l55qE5Zu+5b2i6KGo56S65aaC5LiL5omA56S644CC6YeN6KaB55qE5piv5a2Y5Zyo5Yy56YWN77yM5Yy56YWN5LqG5ZOq5p2h6KeC5rWL5YiZ5peg5YWz57Sn6KaB44CC6L+Z6K+05piO562b6YCJ6L+e5o6l5LiN5Lya5YOP5ZCI5bm26L+e5o6l6YKj5qC36YCg5oiQ6YeN5aSN55qE6KGM44CCDQoNCiFbXShpbWcvc2VtaS1qb2luLnBuZykNCg0K5Y2K6L+e5o6l55qE6YCG5pON5L2c5piv5Y+N6L+e5o6l44CC5Y+N6L+e5o6l5L+d55WZeOihqOS4remCo+S6m+ayoeacieWMuemFjXnooajnmoTooYzjgIINCg0KIVtdKGltZy9hbnRpLWpvaW4ucG5nKQ0KDQoNCuWPjei/nuaOpeWPr+S7peeUqOS6juiviuaWrei/nuaOpeS4reeahOS4jeWMuemFjeOAguS+i+Wmgu+8jOWcqOi/nuaOpSBmbGlnaHRzIOWSjCBwbGFuZXMg5pe277yM5L2g5Y+v6IO95oOz55+l6YGTIGZsaWdodHMg5Lit5piv5ZCm5pyJ5b6I5aSa6KGM5ZyoIHBsYW5lcyDkuK3msqHmnInljLnphY3orrDlvZXvvJoNCmBgYHtyfQ0KZmxpZ2h0cyAlPiUNCiAgYW50aV9qb2luKHBsYW5lcywgYnkgPSAidGFpbG51bSIpICU+JQ0KICBjb3VudCh0YWlsbnVtLCBzb3J0ID0gVFJVRSkNCmBgYA0KDQoNCg0KDQojIyMg5a6a5LmJIGtleQ0KDQojIyMjIOm7mOiupCBgYnkgPSBOVUxMYA0KDQrov5nkvJrkvb/nlKgqKuWQjOaXtuWtmOWcqOS6juS4pOS4quihqCoq5Lit55qE5omA5pyJ5Y+Y6YeP77yM6L+Z56eN5pa55byP56ew5Li6Kiroh6rnhLbov57mjqUqKuOAgg0KDQrkvovlpoLljLnphY3oiKrnj63ooajlkozlpKnmsJTooajml7bkvb/nlKjnmoTlsLHmmK/lhbblhazlhbHlj5jph4/vvJp5ZWFy44CBbW9udGjjgIFkYXnjgIEgaG91cuWSjG9yaWdpbg0KYGBge3J9DQpmbGlnaHRzMiAlPiUNCiAgbGVmdF9qb2luKHdlYXRoZXIpICU+JQ0KICBoZWFkKG4gPSA1KQ0KYGBgDQoNCiMjIyMgYGJ5ID0gYygiYSIsICJiIiwgLi4uKWANCg0K6L+Z56eN5pa55byP5LiO6Ieq54S26L+e5o6l5b6I55u45Ly877yM5L2G5Y+q5L2/55So5LiA6YOo5YiG5YWs5YWx5Y+Y6YeP44CCDQoNCuS+i+Wmgu+8jGZsaWdodHPlkoxwbGFuZXPooajkuK3pg73mnIl5ZWFy5Y+Y6YeP77yM5L2G5piv5a6D5Lus55qE5oSP5LmJ5LiN5ZCM77yM5Zug5q2k5oiR5Lus5Y+q6YCa6L+HdGFpbG51bei/m+ihjOi/nuaOpe+8mg0KYGBge3J9DQpmbGlnaHRzMiAlPiUNCiAgbGVmdF9qb2luKHBsYW5lcywgYnkgPSAidGFpbG51bSIpICU+JQ0KICBoZWFkKG4gPSA1KQ0KYGBgDQoNCiMjIyMgYGJ5ID0gYygiYSIgPSAiYiIpYA0KDQrov5nnp43mlrnlvI/kvJrljLnphY0geCDooajkuK3nmoQgYSDlj5jph4/lkowgeSDooajkuK3nmoQgYiDlj5jph4/jgILovpPlh7rnu5PmnpzkuK3kvb/nlKjnmoTmmK8geCDooajkuK3nmoTlj5jph4/jgIINCg0K5L6L5aaC77yM5aaC5p6c5oOz6KaB55S75Ye65LiA5bmF5Zyw5Zu+77yM6YKj5LmI5oiR5Lus5bCx6ZyA6KaB5Zyo6Iiq54+t5pWw5o2u5Lit5Yqg5YWl5py65Zy65pWw5o2u77yM5ZCO6ICF5YyF5ZCr5LqG5q+P5Liq5py65Zy655qE5L2N572u77yIbGF05ZKMbG9u77yJ44CC5Zug5Li65q+P5qyh6Iiq54+t6YO95pyJ6LW354K55py65Zy65ZKM57uI54K55py65Zy677yM5omA5Lul6ZyA6KaB5oyH5a6a5L2/55So5ZOq5Liq5py65Zy66L+b6KGM6L+e5o6l77yaDQoNCmBgYHtyfQ0KZmxpZ2h0czIgJT4lDQogIGxlZnRfam9pbihhaXJwb3J0cywgYygiZGVzdCIgPSAiZmFhIikpICU+JQ0KICBoZWFkKG4gPSA1KQ0KDQpmbGlnaHRzMiAlPiUNCiAgbGVmdF9qb2luKGFpcnBvcnRzLCBjKCJvcmlnaW4iID0gImZhYSIpKSAlPiUNCiAgaGVhZChuID0gNSkNCmBgYA0KDQojIyMjIGBzdWZmaXggPSBjKCIxIiwgIjIiKWANCg0K5a+55LqO5Lik6KGo5pyJ55u45ZCM5YiX5ZCN5L2G5Y+I5LiN5piva2V555qE5YiX77yM5Yqg5YWl5ZCO57yA6L+b6KGM5Yy65YiGDQoNCiMjIyBgYmFzZTo6bWVyZ2UoKWANCg0KIyMjIyDlj4LmlbDkuLrmlbDmja7moYYNCg0KYGJhc2U6Om1lcmdlKClgIOWPr+S7peWunueOsOaJgOaciSA0IOenjeWQiOW5tui/nuaOpeaTjeS9nO+8mg0KDQp8IGRwbHly5YyFIHwg5Z+656GA5YyFbWVyZ2Xlh73mlbAgfA0KfC0tLS0tfC0tLS0tLS0tLS0tLXwNCnxgaW5uZXJfam9pbih4LCB5KWB8YG1lcmdlKHgsIHkpYHwNCnxgbGVmdF9qb2luKHgsIHkpYHxgbWVyZ2UoeCwgeSwgYWxsLnggPSBUUlVFKWB8DQp8YHJpZ2h0X2pvaW4oeCwgeSlgfGBtZXJnZSh4LCB5LCBhbGwueSA9IFRSVUUpYHwNCnxgZnVsbF9qb2luKHgsIHkpYHxgbWVyZ2UoeCwgeSwgYWxsLnggPSBUUlVFLCBhbGwueSA9IFRSVUUpYHwNCg0KZHBseXIg6L+e5o6l5pON5L2c55qE5LyY54K55piv77yM5Y+v5Lul5pu05Yqg5riF5pmw5Zyw6KGo6L6+5Ye65Luj56CB55qE5oSP5Zu+77ya5LiN5ZCM6L+e5o6l6Ze055qE5Yy65Yir56Gu5a6e6Z2e5bi46YeN6KaB77yM5L2G6ZqQ6JeP5ZyoIG1lcmdlKCkg5Ye95pWw55qE5Y+C5pWw5Lit5LqG44CCZHBseXIg6L+e5o6l5pON5L2c55qE6YCf5bqm5piO5pi+5pu05b+r77yM6ICM5LiU5LiN5Lya5byE5Lmx6KGM55qE6aG65bqP44CCDQoNCiMjIyMg5Y+C5pWw5Li65ZCR6YePDQoNCmBiYXNlOjptZXJnZSh4LCB5KWAg5pyJ5LiA5Liq6aKd5aSW55qE5LyY54K577yM5bCx5piv5a6D55qE5Y+C5pWwIHgsIHkg5Y+v5Lul5piv5ZCR6YeP77yM5q2k5pe25pWI5p6c5Li6Kirlr7nkuozlhYPmlbDlgLzlr7kgKHgsIHkpIOaJgOacieWPr+iDveWPluWAvOeahOmBjeWOhu+8iOi/lOWbnuaVsOaNruahhu+8iSoq44CCDQoNCuS4juS5i+ebuOS8vOeahOWHveaVsOaYryBgZXhwYW5kLmdyaWQoeCwgeSlgDQoNCmBgYHtyfQ0KeCA8LSAxOjMNCnkgPC0gbGV0dGVyc1sxOjNdDQp6IDwtIDE6Mw0KYmFzZTo6bWVyZ2UoeCwgeSkNCmJhc2U6Om1lcmdlKHgsIHopDQpleHBhbmQuZ3JpZCh4LCB5KQ0KZXhwYW5kLmdyaWQoeCwgeikNCmBgYA0KDQojIyMgU1FMDQoNClNRTOaYr2RwbHly6L+e5o6l5pON5L2c55qE54G15oSf5p2l5rqQ77yM5LqM6ICF5LmL6Ze055qE6L2s5o2i6Z2e5bi4566A5Y2V5piO5LqG77yI6KeB5LiL6KGo77yJ44CC5LiOZHBseXLnm7jmr5TvvIxTUUzmlK/mjIHnmoTov57mjqXnsbvlnovmm7Tlub/ms5vvvIzlm6DkuLoqKlNRTOWPr+S7peS9v+eUqOmZpOebuOetieWFs+ezu+WklueahOWFtuS7lumAu+i+keWFs+ezuyAoKipgT04gY29uZGl0aW9uYWxfZXhwcmVzc2lvbmAqKikg5p2l6L+e5o6l5Lik5Liq6KGoKirvvIjmnInml7bov5nnp7DkuLrpnZ7nrYnlgLzov57mjqXvvInjgIINCg0KfCBkcGx5cuWMhSB8IFNRTOivreiogCB8DQp8LS0tLS18LS0tLS0tLS0tLS0tfA0KfGBpbm5lcl9qb2luKHgsIHksIGJ5ID0gInoiKWB8YFNFTEVDVCAqIEZST00geCBJTk5FUiBKT0lOIHkgVVNJTkcgKHopYCDmiJYgYFNFTEVDVCAqIEZST00geCBJTk5FUiBKT0lOIHkgT04gY29uZGl0aW9uYWxfZXhwcmVzc2lvbmB8DQp8YGxlZnRfam9pbih4LCB5LCBieSA9ICJ6IilgfGBTRUxFQ1QgKiBGUk9NIHggTEVGVCBPVVRFUiBKT0lOIHkgVVNJTkcgKHopYCDmiJYgYFNFTEVDVCAqIEZST00geCBMRUZUIE9VVEVSIEpPSU4geSBPTiBjb25kaXRpb25hbF9leHByZXNzaW9uYHwNCnxgcmlnaHRfam9pbih4LCB5LCBieSA9ICJ6IilgfGBTRUxFQ1QgKiBGUk9NIHggUklHSFQgT1VURVIgSk9JTiB5IFVTSU5HICh6KWAg5oiWIGBTRUxFQ1QgKiBGUk9NIHggUklHSFQgT1VURVIgSk9JTiB5IE9OIGNvbmRpdGlvbmFsX2V4cHJlc3Npb25gfA0KfGBmdWxsX2pvaW4oeCwgeSwgYnkgPSAieiIpYHxgU0VMRUNUICogRlJPTSB4IEZVTEwgT1VURVIgSk9JTiB5IFVTSU5HICh6KWAg5oiWIGBTRUxFQ1QgKiBGUk9NIHggRlVMTCBPVVRFUiBKT0lOIHkgT04gY29uZGl0aW9uYWxfZXhwcmVzc2lvbmB8DQoNCiMjIyDlkIjlubYgam9pbiDnmoTlupTnlKjlnLrmma8NCg0K5pWw5o2u5YiG5p6Q5Lit6K645aSa5bi46KeB6Zeu6aKY77yM5a6e5Li65YWz57O75pWw5o2u6Zeu6aKY44CCDQoNCuS+i++8mueOsOacieWbveWIq+ihqO+8jOWMheWQq2NvdW50cnnlkoxnZHDkuKTkuKrlj5jph4/vvIzkuLrkuobov5vkuIDmraXliIbmnpDvvIzpnIDopoHmt7vliqDmlrDlj5jph49yZWdpb27vvIzor7TmmI7ov5nkupvlm73lrrbmiYDlnKjnmoTlnLDnkIblpKfljLrjgILpgqPkuYjlupTor6XlpoLkvZXmk43kvZzlkaLvvJ8NCg0K5LmN5LiA55yL77yM55SoZHBseXI6Om11dGF0ZSgp5b6I6Zq+5pON5L2c77yM5Zug5Li65paw5Y+Y6YeP6KaB5qC55o2uY291bnRyeeWPmOmHj+eahOS4jeWQjOWPluWAvOi1i+S4jeWQjOeahOWAvO+8jOi/meWcqG11dGF0ZSgp55qE5qGG5p625LiL5piv5peg5rOV5a6e546w55qE44CC5LuU57uG5YiG5p6Q77yM5YW25a6e6L+Z5piv5LiA5Liq5YWz57O75pWw5o2u6Zeu6aKY77yM5Zug5Li65a2Y5Zyo552A5Lik5Liq6KGo77yaY291bnRyeeWSjGdkcOaYr+S4gOS4quihqO+8jGNvdW50cnnmmK/kuLvplK7vvJtjb3VudHJ55ZKMcmVnaW9u55qE5a+55bqU5YWz57O75Y+I5piv5LiA5Liq6KGo77yMY291bnRyeei/mOaYr+S4u+mUruOAguWPquS4jei/h+aIkeS7rOS5oOaDr+S6juaOpeWPl+WJjeS4gOS4quihqOaYr+aVsOaNruihqO+8jOiAjOW/veinhuS6huWQjuS4gOS4quWvueW6lOWFs+ezu+S5n+aYr+S4gOS4quihqOOAguS6juaYr++8jOi/meS4qua3u+WKoOaWsOWPmOmHj+eahOi/h+eoi++8jOS+v+WPr+S7peihqOi+vuS4uuS4pOS4quihqOmAmui/h2NvdW50cnnov5nkuKrmoaXmooHlkIjlubbov57mjqXnmoTov4fnqIvvvIzlj6/ku6XnlKhkcGx5cjo6bGVmdF9qb2luKCleW+iAg+iZkeWIsGdkcOaVsOaNruihqOS4reeahOWbveWutuWPr+iDveS4jeWFqO+8jOiAjOWbveWutuS4juWcsOWMuueahOWvueW6lOWFs+ezu+ihqOmAmuW4uOaYr+eos+WumuiAjOWujOWkh+eahO+8jOWboOatpOW6lOivpeeUqOWJjeiAheW3pui/nuaOpeWQjuiAhe+8jOS/neWtmOWJjeiAheeahOaJgOacieingua1i+OAgl0g5oiWU1FM6K+t6KiA5p2l5a6e546w5LqG44CCDQoNCmBgYHtyfQ0KZ2RwIDwtDQogIHRyaWJibGUofmNvdW50cnksIH5nZHAsICLlvrflm70iLCAiMjAiLCAi5Lit5Zu9IiwgIjUwIiwgIue+juWbvSIsICI3MCIsICLml6XmnKwiLCAiMjUiKQ0KZ2RwDQpjb2RlIDwtDQogIHRyaWJibGUofmNvdW50cnksIH5yZWdpb24sICLlvrflm70iLCAi5qyn55ufIiwgIuS4reWbvSIsICLkuJzkupoiLCAi576O5Zu9IiwgIuWMl+e+jiIsICLml6XmnKwiLCAi5Lic5LqaIiwgIumfqeWbvSIsICLkuJzkupoiKQ0KY29kZQ0KDQoNCiMjIOS4ieenjeetieS7t+aTjeS9nA0KIyDmlrnms5Ux77ya5L2/55So5LiL5qCH57Si5byV77yM5Lul5ZCR6YeP5Li65pON5L2c5Y2V5L2NDQpqb2luMSA8LSBnZHANCmpvaW4xJHJlZ2lvbiA8LSAi5qyn55ufIg0Kam9pbjFbam9pbjEkY291bnRyeSAlaW4lIGMoIuS4reWbvSIsICLml6XmnKwiLCAi6Z+p5Zu9IiksIF0kcmVnaW9uIDwtICLkuJzkupoiDQpqb2luMVtqb2luMSRjb3VudHJ5ID09ICLnvo7lm70iLCBdJHJlZ2lvbiA8LSAi5YyX576OIg0Kam9pbjENCg0KIyDmlrnms5Uy77yaZHBseXLljIXvvIzku6XmlbDmja7moYbkuLrmk43kvZzljZXkvY0NCmpvaW4yIDwtIGxlZnRfam9pbihnZHAsIGNvZGUsIGJ5ID0gImNvdW50cnkiKQ0Kam9pbjINCg0KIyDmlrnms5Uz77yac3FsZGbljIXlkoxzcWzor63oqIANCmpvaW4zIDwtIHNxbGRmKCJzZWxlY3QgKiBmcm9tIGdkcCBsZWZ0IG91dGVyIGpvaW4gY29kZSB1c2luZyAoY291bnRyeSkiKQ0Kam9pbjMNCmBgYA0KDQoNCiMjIyBqb2luIOWJjeimgeazqOaEj+eahOmXrumimA0KDQoxLiDpppblhYjvvIzpnIDopoHmib7lh7rmr4/kuKrooajkuK3lj6/ku6XkvZzkuLrkuLvplK7nmoTlj5jph4/jgILkuIDoiKzlupTor6Xln7rkuo7lr7nmlbDmja7nmoTnkIbop6PmnaXnoa7lrprkuLvplK7vvIzogIzkuI3mmK/lh63nu4/pqozlr7vmib7og73kvZzkuLrllK/kuIDmoIfor4bnrKbnmoTlj5jph4/nu4TlkIjjgILlpoLmnpzlnKjnoa7lrprkuLvplK7ml7bmoLnmnKzmsqHmnInogIPomZHov4flhbbmhI/kuYnvvIzpgqPkuYjlsLHlj6/og73mraXlhaXmrafpgJTvvIzomb3nhLblj6/ku6Xmib7lh7rlhbfmnInllK/kuIDmgKfnmoTlj5jph4/nu4TlkIjvvIzkvYblroPkuI7mlbDmja7pl7TnmoTlhbPns7vljbTlj6/og73kuI3mmK/nnJ/lrp7nmoTjgILkvovlpoLvvIznu4/luqblkoznuqzluqbomb3nhLbog73lpJ/llK/kuIDmoIfor4bmr4/kuKrmnLrlnLrvvIzkvYbljbTkuI3mmK/oia/lpb3nmoTmoIfor4bnrKbvvIENCmBgYHtyfQ0KYWlycG9ydHMgJT4lDQogIGNvdW50KGFsdCwgbG9uKSAlPiUNCiAgZmlsdGVyKG4gPiAxKQ0KYGBgDQoNCjIuIOehruS/neS4u+mUruS4reeahOavj+S4quWPmOmHj+mDveayoeaciee8uuWkseWAvOOAguWmguaenOaciee8uuWkseWAvO+8jOmCo+S5iOi/meS4quWPmOmHj+WwseS4jeiDveagh+ivhuingua1i++8gQ0KDQozLiDmo4Dmn6XlpJbplK7mmK/lkKbkuI7lj6bkuIDlvKDooajnmoTkuLvplK7nm7jljLnphY3jgILmnIDlpb3nmoTmlrnms5XmmK/kvb/nlKhhbnRpX2pvaW4oKe+8jOeUseS6juaVsOaNruW9leWFpemUmeivr++8jOWklumUruWSjOS4u+mUruS4jeWMuemFjeeahOaDheWGteW+iOW4uOingeOAguino+WGs+i/meenjemXrumimOmAmuW4uOmcgOimgeWkp+mHj+W3peS9nOOAgg0KDQoNCg0KIyMg5pWw5o2u6KGo55qE6ZuG5ZCI6L+Q566XDQoNCuingua1i++8iOihjO+8ieS9nOS4uuWFg+e0oO+8jOaVsOaNruihqOinhuS4uuingua1i+eahOmbhuWQiOOAgumbhuWQiOaTjeS9nOmcgOimgeS4pOW8oOihqOWFt+acieWujOWFqOebuOWQjOeahOWPmOmHj+OAgg0KDQoNCi0gYGludGVyc2VjdCgpYO+8jOS6pOmbhu+8jOWPquS/neeVmeS4pOihqOS4reWdh+acieeahOihjA0KLSBgc2V0ZGlmZigpYO+8jOmbhuWQiOeahOW3ru+8jOWPquS/neeVmeesrOS4gOS4quihqOWQq+acieOAgeesrOS6jOihqOS4jeWQq+eahOihjA0KLSBgdW5pb24oKWDvvIzlubbpm4bvvIzkv53nlZnmiYDmnInooYzvvIzkvYbph43lpI3nmoTlj6rkv53nlZnkuIDmrKENCi0gYHVuaW9uX2FsbCgpYO+8jOWFqOmbhuS4lOS4jeiIjeW8g+mHjeWkjeihjA0KLSBgc2V0ZXF1YWwoKWDvvIzmo4DmtYvkuKTkuKrooajmiYDlkKvnmoTooYzmmK/lkKblrozlhajnm7jlkIzvvIjkuI3ogIPomZHpobrluo/vvIzlg4/pm4blkIjkuIDmoLfvvIkNCg0KDQpgYGB7cn0NCmRmMSA8LSB0cmliYmxlKA0KICB+eCwgfnksDQogIDEsIDEsDQogIDIsIDENCikNCmRmMiA8LSB0cmliYmxlKA0KICB+eCwgfnksDQogIDEsIDEsDQogIDEsIDINCikNCg0KaW50ZXJzZWN0KGRmMSwgZGYyKQ0KdW5pb24oZGYxLCBkZjIpDQpzZXRkaWZmKGRmMSwgZGYyKQ0Kc2V0ZGlmZihkZjIsIGRmMSkNCmBgYA0K