Package dplyr
- Tác giả: Hadley Wickham của RStudio.
- Phiên bản cải tiến của
plyr.
- Không cung cấp thêm chức năng cho R, nhưng làm đơn giản những chức năng hiện có.
- Cung cấp “văn phạm” để thao tác với data frame.
- Dễ hiểu.
- Chạy nhanh hơn, do được code qua C++.
Văn phạm dplyr
Một số “động từ” chính của package dplyr bao gồm
select: trả về một tập hợp con các cột trong data frame.
filter(): trích xuất một tập hợp con các hàng trong data frame dựa trên điều kiện logic.
arrange(): sắp xếp lại hàng trong data frame.
rename(): đổi tên biến số trong data frame.
mutate(): thêm biến/cột hoặc thay đổi biến hiện có.
summarise()/summarize(): tính toán số thống kê đại diện cho những biến số khác nhau trong data frame. Có thể làm theo dạng phân tầng.
%>%: Toán tử “pipe” được sử dụng để kết nối nhiều động từ thành một đường ống (pipeline).
Một số đặc điểm chức năng chung của dplyr()
- Đối số đầu tiên là data frame.
- Những đối số tiếp theo mô tả hành động thực hiện với data frame, và ta có thể truy xuất cột bằng tên mà không cần sử dụng toán tử
$.
- Kết quả trả về là một data frame mới.
- Data frame phải được định dạng và ghi chú phù hợp. Cụ thể, bộ dữ liệu phải gọn gàng. Tức là một quan sát ở mỗi hàng, và mỗi cột nên là một đặc điểm của quan sát đó.
select()
Ở phần này, chúng ta sẽ sử dụng bộ dữ liệu về ô nhiễm không khí và nhiệt độ tại thành phố Chicago, Mỹ.
Ta tải package dplyr().
Hàm select() được sử dụng để lựa chọn cột trong data frame. Giả sử chúng ta chỉ muốn làm việc với 3 cột đầu.
Lưu ý rằng dấu : không thể sử dụng cho tên hoặc character. Tuy nhiên, trong hàm select() ta có thể dùng nó để xác định một chuỗi tên biến số.
Ta cũng có thể dấu - trong select() để loại biến số ta không muốn.
Trong R, nếu ta muốn sử dụng
Ta cũng có thể sử dụng cú pháp để xác định nhiều biến số có cùng quy tắc đặt tên. Ví dụ, nếu ta muốn giữ những biến số có tên kết thúc với 2
Hoặc nếu ta muốn giữ các biến số bắt đầu với “d”, ta có thể
filter()
Hàm filter() được sử dụng để trích xuất tập hợp con các hàng từ một data frame. Hàm này tương tự như subset() trong R nhưng nhanh hơn.
Giả sử ta muốn trích xuất các hàng của data frame chicago ở mức PM2.5 > 30, ta có thể
'data.frame': 194 obs. of 8 variables:
$ city : chr "chic" "chic" "chic" "chic" ...
$ tmpd : num 23 28 55 59 57 57 75 61 73 78 ...
$ dptp : num 21.9 25.8 51.3 53.7 52 56 65.8 59 60.3 67.1 ...
$ date : Date, format: "1998-01-17" ...
$ pm25tmean2: num 38.1 34 39.4 35.4 33.3 ...
$ pm10tmean2: num 32.5 38.7 34 28.5 35 ...
$ o3tmean2 : num 3.18 1.75 10.79 14.3 20.66 ...
$ no2tmean2 : num 25.3 29.4 25.3 31.4 26.8 ...
Ta thấy rằng chỉ có 194 hàng trong data frame và phân bố của pm25.
Ta có thể truyền điều kiện vào filter().
arrange()
Hàm arrange() được sử dụng để sắp xếp lại hàng của data frame theo các biến số (cột). Theo thứ tự, hàng được xếp theo cột đầu tiên rồi lần lượt theo các cột sau.
Các cột có thể được sắp xếp theo thứ tự giảm dần với hàm desc().
mutate()
Hàm mutate() giúp tạo hoặc thay đổi biến số trong data frame.
Ví dụ, để biết liệu mức ô nhiễm cao hơn hay thấp hơn mức trung bình, ta trừ số liệu của các quan sát cho số trung bình.
Ta tạo một biến số mới là pm25new.
transmute()
transmute() hoạt động như mutate(), nhưng bỏ đi những biến số không được thay đổi.
group_by()
Hàm group_by() được sử dụng để tính số thống kê theo phân tầng. Ví dụ, ta có thể muốn biết số PM2.5 trung bình hàng năm. Do đó, dữ liệu được phân tầng theo năm, và đây là thông tin ta lấy được từ biến date. Kết hợp với group_by(), ta sử dụng hàm summarise().
Các thức hoạt động là ta phân tách data frame thành những phần riêng biệt theo một hay nhiều biến số (group_by()), và áp dụng hàm summarise() vào những phần riêng biệt này.
`summarise()` ungrouping output (override with `.groups` argument)
summarise() trả về data frame với year là cột đầu tiên, và sau đó là số trung bình hàng năm của pm25, o3, và no2.
%>%
Toán tử %>% rất hữu ích trong việc xâu chuỗi các hàm của dplyr.
Như ở ví dụ trên, chúng ta muốn tính trung bình o3, no2 và pm25 theo năm, ta phải:
- Tạo biến
year
- Phân tách data frame theo
year
- Tính toán trung bình
o3, no2, và pm25 theo year.
Với %>%, ta có thể thực hiện trong một câu lệnh.
Lưu ý rằng, khi gọi hàm mutate() ta có truyền data frame chicago, nhưng ở những lần gọi hàm sau, ta không cần phải truyền data frame vào đối số đầu tiên nữa. Khi sử dụng %>%, đầu ra ở lần gọi trước đã được ngầm đưa vào đối số đầu tiên ở lần gọi tiếp theo.
LS0tDQp0aXRsZTogJ0LDoGkgNjogTMOgbSBz4bqhY2ggZOG7ryBsaeG7h3UnDQphdXRob3I6ICJLaW0gVsSDbiBUaMOgbmgsIE0uRC4iDQpkYXRlOiAiMTcvMTAvMjAyMCINCm91dHB1dDogDQogIGh0bWxfbm90ZWJvb2s6IA0KICAgIGZpZ19jYXB0aW9uOiB5ZXMNCiAgICBoaWdobGlnaHQ6IHRhbmdvDQogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMNCiAgICB0aGVtZTogY2VydWxlYW4NCiAgICB0b2M6IHllcw0KLS0tDQoNClBhY2thZ2UgYGRwbHlyYA0KPT09PT09PT09PT09PT0NCg0KLSBUw6FjIGdp4bqjOiBIYWRsZXkgV2lja2hhbSBj4bunYSBSU3R1ZGlvLiAgDQotIFBoacOqbiBi4bqjbiBj4bqjaSB0aeG6v24gY+G7p2EgYHBseXJgLiAgDQotIEtow7RuZyBjdW5nIGPhuqVwIHRow6ptIGNo4bupYyBuxINuZyBjaG8gUiwgbmjGsG5nIGzDoG0gxJHGoW4gZ2nhuqNuIG5o4buvbmcgY2jhu6ljIG7Eg25nIGhp4buHbiBjw7MuICANCi0gQ3VuZyBj4bqlcCAidsSDbiBwaOG6oW0iIMSR4buDIHRoYW8gdMOhYyB24bubaSBkYXRhIGZyYW1lLiAgDQotIEThu4UgaGnhu4N1LiAgDQotIENo4bqheSBuaGFuaCBoxqFuLCBkbyDEkcaw4bujYyBjb2RlIHF1YSBDKysuIA0KDQpWxINuIHBo4bqhbSBgZHBseXJgDQo9PT09PT09PT09PT09PT0NCg0KTeG7mXQgc+G7kSAixJHhu5luZyB04burIiBjaMOtbmggY+G7p2EgcGFja2FnZSBgZHBseXJgIGJhbyBn4buTbQ0KDQotIGBzZWxlY3RgOiB0cuG6oyB24buBIG3hu5l0IHThuq1wIGjhu6NwIGNvbiBjw6FjIGPhu5l0IHRyb25nIGRhdGEgZnJhbWUuICANCi0gYGZpbHRlcigpYDogdHLDrWNoIHh14bqldCBt4buZdCB04bqtcCBo4bujcCBjb24gY8OhYyBow6BuZyB0cm9uZyBkYXRhIGZyYW1lIGThu7FhIHRyw6puIMSRaeG7gXUga2nhu4duIGxvZ2ljLiAgDQotIGBhcnJhbmdlKClgOiBz4bqvcCB44bq/cCBs4bqhaSBow6BuZyB0cm9uZyBkYXRhIGZyYW1lLiAgDQotIGByZW5hbWUoKWA6IMSR4buVaSB0w6puIGJp4bq/biBz4buRIHRyb25nIGRhdGEgZnJhbWUuICANCi0gYG11dGF0ZSgpYDogdGjDqm0gYmnhur9uL2Phu5l0IGhv4bq3YyB0aGF5IMSR4buVaSBiaeG6v24gaGnhu4duIGPDsy4gIA0KLSBgc3VtbWFyaXNlKCkvc3VtbWFyaXplKClgOiB0w61uaCB0b8OhbiBz4buRIHRo4buRbmcga8OqIMSR4bqhaSBkaeG7h24gY2hvIG5o4buvbmcgYmnhur9uIHPhu5Ega2jDoWMgbmhhdSB0cm9uZyBkYXRhIGZyYW1lLiBDw7MgdGjhu4MgbMOgbSB0aGVvIGThuqFuZyBwaMOibiB04bqnbmcuICANCi0gYCU+JWA6IFRvw6FuIHThu60gInBpcGUiIMSRxrDhu6NjIHPhu60gZOG7pW5nIMSR4buDIGvhur90IG7hu5FpIG5oaeG7gXUgxJHhu5luZyB04burIHRow6BuaCBt4buZdCDEkcaw4budbmcg4buRbmcgKHBpcGVsaW5lKS4gIA0KDQojIyMgTeG7mXQgc+G7kSDEkeG6t2MgxJFp4buDbSBjaOG7qWMgbsSDbmcgY2h1bmcgY+G7p2EgYGRwbHlyKClgDQoNCjEuIMSQ4buRaSBz4buRIMSR4bqndSB0acOqbiBsw6AgZGF0YSBmcmFtZS4gIA0KMi4gTmjhu69uZyDEkeG7kWkgc+G7kSB0aeG6v3AgdGhlbyBtw7QgdOG6oyBow6BuaCDEkeG7mW5nIHRo4buxYyBoaeG7h24gduG7m2kgZGF0YSBmcmFtZSwgdsOgIHRhIGPDsyB0aOG7gyB0cnV5IHh14bqldCBj4buZdCBi4bqxbmcgdMOqbiBtw6Aga2jDtG5nIGPhuqduIHPhu60gZOG7pW5nIHRvw6FuIHThu60gYCRgLiAgDQozLiBL4bq/dCBxdeG6oyB0cuG6oyB24buBIGzDoCBt4buZdCBkYXRhIGZyYW1lIG3hu5tpLiAgDQo0LiBEYXRhIGZyYW1lIHBo4bqjaSDEkcaw4bujYyDEkeG7i25oIGThuqFuZyB2w6AgZ2hpIGNow7ogcGjDuSBo4bujcC4gQ+G7pSB0aOG7gywgYuG7mSBk4buvIGxp4buHdSBwaOG6o2kgZ+G7jW4gZ8OgbmcuIFThu6ljIGzDoCBt4buZdCBxdWFuIHPDoXQg4bufIG3hu5dpIGjDoG5nLCB2w6AgbeG7l2kgY+G7mXQgbsOqbiBsw6AgbeG7mXQgxJHhurdjIMSRaeG7g20gY+G7p2EgcXVhbiBzw6F0IMSRw7MuIA0KDQpgc2VsZWN0KClgDQo9PT09PT09PT0NCg0K4bueIHBo4bqnbiBuw6B5LCBjaMO6bmcgdGEgc+G6vSBz4butIGThu6VuZyBi4buZIGThu68gbGnhu4d1IHbhu4Egw7Qgbmhp4buFbSBraMO0bmcga2jDrSB2w6Agbmhp4buHdCDEkeG7mSB04bqhaSB0aMOgbmggcGjhu5EgQ2hpY2FnbywgTeG7uS4NCg0KYGBge3J9DQpjaGljYWdvIDwtIHJlYWRSRFMoImRhdGFzZXQvY2hpY2Fnby5yZHMiKQ0KYGBgDQoNClRhIHThuqNpIHBhY2thZ2UgYGRwbHlyKClgLg0KDQpgYGB7cn0NCmxpYnJhcnkoZHBseXIpDQpgYGANCg0KSMOgbSBgc2VsZWN0KClgIMSRxrDhu6NjIHPhu60gZOG7pW5nIMSR4buDIGzhu7FhIGNo4buNbiBj4buZdCB0cm9uZyBkYXRhIGZyYW1lLiBHaeG6oyBz4butIGNow7puZyB0YSBjaOG7iSBtdeG7kW4gbMOgbSB2aeG7h2MgduG7m2kgMyBj4buZdCDEkeG6p3UuIA0KDQpgYGB7cn0NCnN1YnNldDEgPC0gY2hpY2Fnb1sxOjNdDQpzdWJzZXQyIDwtIHNlbGVjdChjaGljYWdvLCBjaXR5OmRwdHApDQpoZWFkKHN1YnNldDIpDQpgYGANCg0KTMawdSDDvSBy4bqxbmcgZOG6pXUgYDpgIGtow7RuZyB0aOG7gyBz4butIGThu6VuZyBjaG8gdMOqbiBob+G6t2MgY2hhcmFjdGVyLiBUdXkgbmhpw6puLCB0cm9uZyBow6BtIGBzZWxlY3QoKWAgdGEgY8OzIHRo4buDIGTDuW5nIG7DsyDEkeG7gyB4w6FjIMSR4buLbmggbeG7mXQgY2h14buXaSB0w6puIGJp4bq/biBz4buRLg0KDQpUYSBjxaluZyBjw7MgdGjhu4MgZOG6pXUgYC1gIHRyb25nIGBzZWxlY3QoKWAgxJHhu4MgbG/huqFpIGJp4bq/biBz4buRIHRhIGtow7RuZyBtdeG7kW4uIA0KDQpgYGB7cn0NCnN1YnNldDMgPC0gc2VsZWN0KGNoaWNhZ28sIC0oY2l0eTpkcHRwKSkNCmBgYA0KDQpUcm9uZyBSLCBu4bq/dSB0YSBtdeG7kW4gc+G7rSBk4bulbmcgDQoNCmBgYHtyfQ0KaSA8LSBtYXRjaCgiY2l0eSIsIG5hbWVzKGNoaWNhZ28pKQ0KaiA8LSBtYXRjaCgiZHB0cCIsIG5hbWVzKGNoaWNhZ28pKQ0KaGVhZChjaGljYWdvWywtKGk6aildKQ0KYGBgDQoNClRhIGPFqW5nIGPDsyB0aOG7gyBz4butIGThu6VuZyBjw7ogcGjDoXAgxJHhu4MgeMOhYyDEkeG7i25oIG5oaeG7gXUgYmnhur9uIHPhu5EgY8OzIGPDuW5nIHF1eSB04bqvYyDEkeG6t3QgdMOqbi4gVsOtIGThu6UsIG7hur91IHRhIG114buRbiBnaeG7ryBuaOG7r25nIGJp4bq/biBz4buRIGPDsyB0w6puIGvhur90IHRow7pjIHbhu5tpIGAyYA0KDQpgYGB7cn0NCnN1YnNldDQgPC0gc2VsZWN0KGNoaWNhZ28sIGVuZHNfd2l0aCgiMiIpKQ0Kc3RyKHN1YnNldDQpDQpgYGANCg0KSG/hurdjIG7hur91IHRhIG114buRbiBnaeG7ryBjw6FjIGJp4bq/biBz4buRIGLhuq90IMSR4bqndSB24bubaSAiZCIsIHRhIGPDsyB0aOG7gw0KDQpgYGB7cn0NCnN1YnNldDUgPC0gc2VsZWN0KGNoaWNhZ28sIHN0YXJ0c193aXRoKCJkIikpDQpzdHIoc3Vic2V0NSkNCmBgYA0KDQpgZmlsdGVyKClgDQo9PT09PT09PT09DQoNCkjDoG0gYGZpbHRlcigpYCDEkcaw4bujYyBz4butIGThu6VuZyDEkeG7gyB0csOtY2ggeHXhuqV0IHThuq1wIGjhu6NwIGNvbiBjw6FjIGjDoG5nIHThu6sgbeG7mXQgZGF0YSBmcmFtZS4gSMOgbSBuw6B5IHTGsMahbmcgdOG7sSBuaMawIGBzdWJzZXQoKWAgdHJvbmcgUiBuaMawbmcgbmhhbmggaMahbi4NCg0KR2nhuqMgc+G7rSB0YSBtdeG7kW4gdHLDrWNoIHh14bqldCBjw6FjIGjDoG5nIGPhu6dhIGRhdGEgZnJhbWUgYGNoaWNhZ29gIOG7nyBt4bupYyBQTTIuNSA+IDMwLCB0YSBjw7MgdGjhu4MNCg0KYGBge3J9DQpjaGljLmYgPC0gZmlsdGVyKGNoaWNhZ28sIHBtMjV0bWVhbjIgPiAzMCkNCnN0cihjaGljLmYpDQpgYGANCg0KVGEgdGjhuqV5IHLhurFuZyBjaOG7iSBjw7MgMTk0IGjDoG5nICB0cm9uZyBkYXRhIGZyYW1lIHbDoCBwaMOibiBi4buRIGPhu6dhIHBtMjUuDQoNCmBgYHtyfQ0Kc3VtbWFyeShjaGljLmYkcG0yNXRtZWFuMikNCmBgYA0KDQpUYSBjw7MgdGjhu4MgdHJ1eeG7gW4gxJFp4buBdSBraeG7h24gdsOgbyBgZmlsdGVyKClgLg0KDQpgYGB7cn0NCmNoaWMuZiA8LSBmaWx0ZXIoY2hpY2FnbywgcG0yNXRtZWFuMiA+IDMwICYgdG1wZCA+IDgwKQ0Kc2VsZWN0KGNoaWMuZiwgZGF0ZSwgdG1wZCwgcG0yNXRtZWFuMikNCmBgYA0KDQpgYXJyYW5nZSgpYA0KPT09PT09PT09PQ0KDQpIw6BtIGBhcnJhbmdlKClgIMSRxrDhu6NjIHPhu60gZOG7pW5nIMSR4buDIHPhuq9wIHjhur9wIGzhuqFpIGjDoG5nIGPhu6dhIGRhdGEgZnJhbWUgdGhlbyBjw6FjIGJp4bq/biBz4buRIChj4buZdCkuIFRoZW8gdGjhu6kgdOG7sSwgaMOgbmcgxJHGsOG7o2MgeOG6v3AgdGhlbyBj4buZdCDEkeG6p3UgdGnDqm4gcuG7k2kgbOG6p24gbMaw4bujdCB0aGVvIGPDoWMgY+G7mXQgc2F1Lg0KDQpgYGB7cn0NCmNoaWNhZ28gPC0gYXJyYW5nZShjaGljYWdvLCBkYXRlKQ0KYGBgDQoNCkPDoWMgY+G7mXQgY8OzIHRo4buDIMSRxrDhu6NjIHPhuq9wIHjhur9wIHRoZW8gdGjhu6kgdOG7sSBnaeG6o20gZOG6p24gduG7m2kgaMOgbSBgZGVzYygpYC4NCg0KYGBge3J9DQpjaGljYWdvIDwtIGFycmFuZ2UoY2hpY2FnbywgZGVzYyhkYXRlKSkNCmBgYA0KDQpgcmVuYW1lKClgDQo9PT09PT09PT09DQoNCg0KYGBge3J9DQpjaGljYWdvIDwtIHJlbmFtZShjaGljYWdvLCBkZXdwb2ludCA9IGRwdHAsIHBtMjUgPSBwbTI1dG1lYW4yKQ0KaGVhZChjaGljYWdvWywxOjVdLCAzKQ0KYGBgDQoNCmBtdXRhdGUoKWANCj09PT09PT09PT0NCg0KSMOgbSBgbXV0YXRlKClgIGdpw7pwIHThuqFvIGhv4bq3YyB0aGF5IMSR4buVaSBiaeG6v24gc+G7kSB0cm9uZyBkYXRhIGZyYW1lLiANCg0KVsOtIGThu6UsIMSR4buDIGJp4bq/dCBsaeG7h3UgbeG7qWMgw7Qgbmhp4buFbSBjYW8gaMahbiBoYXkgdGjhuqVwIGjGoW4gbeG7qWMgdHJ1bmcgYsOsbmgsIHRhIHRy4burIHPhu5EgbGnhu4d1IGPhu6dhIGPDoWMgcXVhbiBzw6F0IGNobyBz4buRIHRydW5nIGLDrG5oLg0KDQpUYSB04bqhbyBt4buZdCBiaeG6v24gc+G7kSBt4bubaSBsw6AgYHBtMjVuZXdgLg0KDQpgYGB7cn0NCmNoaWNhZ28gPC0gbXV0YXRlKGNoaWNhZ28sIHBtMjVuZXcgPSBwbTI1IC0gbWVhbihwbTI1LCBuYS5ybSA9IFRSVUUpKQ0KaGVhZChjaGljYWdvKQ0KYGBgDQoNCmB0cmFuc211dGUoKWANCj09PT09PT09PT09PQ0KDQpgdHJhbnNtdXRlKClgIGhv4bqhdCDEkeG7mW5nIG5oxrAgYG11dGF0ZSgpYCwgbmjGsG5nIGLhu48gxJFpIG5o4buvbmcgYmnhur9uIHPhu5Ega2jDtG5nIMSRxrDhu6NjIHRoYXkgxJHhu5VpLg0KDQpgYGB7cn0NCmhlYWQodHJhbnNtdXRlKGNoaWNhZ28sDQogICAgICAgICAgICAgICBwbTEwbmV3ID0gcG0xMHRtZWFuMiAtIG1lYW4ocG0xMHRtZWFuMiwgbmEucm0gPSBUKSwNCiAgICAgICAgICAgICAgIG8zbmV3ID0gbzN0bWVhbjIgLSBtZWFuKG8zdG1lYW4yLCBuYS5ybSA9IFRSVUUpKSkNCmBgYA0KDQpgZ3JvdXBfYnkoKWANCj09PT09PT09PT09PT0NCg0KSMOgbSBgZ3JvdXBfYnkoKWAgxJHGsOG7o2Mgc+G7rSBk4bulbmcgxJHhu4MgdMOtbmggc+G7kSB0aOG7kW5nIGvDqiB0aGVvIHBow6JuIHThuqduZy4gVsOtIGThu6UsIHRhIGPDsyB0aOG7gyBtdeG7kW4gYmnhur90IHPhu5EgUE0yLjUgdHJ1bmcgYsOsbmggaMOgbmcgbsSDbS4gRG8gxJHDsywgZOG7ryBsaeG7h3UgxJHGsOG7o2MgcGjDom4gdOG6p25nIHRoZW8gbsSDbSwgdsOgIMSRw6J5IGzDoCB0aMO0bmcgdGluIHRhIGzhuqV5IMSRxrDhu6NjIHThu6sgYmnhur9uIGBkYXRlYC4gS+G6v3QgaOG7o3AgduG7m2kgYGdyb3VwX2J5KClgLCB0YSBz4butIGThu6VuZyBow6BtIGBzdW1tYXJpc2UoKWAuDQoNCkPDoWMgdGjhu6ljIGhv4bqhdCDEkeG7mW5nIGzDoCB0YSBwaMOibiB0w6FjaCBkYXRhIGZyYW1lIHRow6BuaCBuaOG7r25nIHBo4bqnbiByacOqbmcgYmnhu4d0IHRoZW8gbeG7mXQgaGF5IG5oaeG7gXUgYmnhur9uIHPhu5EgKGBncm91cF9ieSgpYCksIHbDoCDDoXAgZOG7pW5nIGjDoG0gYHN1bW1hcmlzZSgpYCB2w6BvIG5o4buvbmcgcGjhuqduIHJpw6puZyBiaeG7h3QgbsOgeS4NCg0KDQpgYGB7cn0NCmNoaWNhZ28gPC0gbXV0YXRlKGNoaWNhZ28sIHllYXIgPSBhcy5QT1NJWGx0KGRhdGUpJHllYXIgKyAxOTAwKQ0KYGBgDQoNCmBgYHtyfQ0KeWVhcnMgPC0gZ3JvdXBfYnkoY2hpY2FnbywgeWVhcikNCmBgYA0KDQpgYGB7cn0NCnN1bW1hcmlzZSh5ZWFycywgcG0yNSA9IG1lYW4ocG0yNSwgbmEucm0gPSBUUlVFKSwNCiAgICAgICAgICBvMyA9IG1heChvM3RtZWFuMiwgbmEucm0gPSBUUlVFKSwNCiAgICAgICAgICBubzIgPSBtZWRpYW4obm8ydG1lYW4yLCBuYS5ybSA9IFRSVUUpKQ0KYGBgDQoNCmBzdW1tYXJpc2UoKWAgdHLhuqMgduG7gSBkYXRhIGZyYW1lIHbhu5tpIGB5ZWFyYCBsw6AgY+G7mXQgxJHhuqd1IHRpw6puLCB2w6Agc2F1IMSRw7MgbMOgIHPhu5EgdHJ1bmcgYsOsbmggaMOgbmcgbsSDbSBj4bunYSBwbTI1LCBvMywgdsOgIG5vMi4NCg0KYCU+JWANCj09PT09DQoNClRvw6FuIHThu60gYCU+JWAgcuG6pXQgaOG7r3Ugw61jaCB0cm9uZyB2aeG7h2MgeMOidSBjaHXhu5dpIGPDoWMgaMOgbSBj4bunYSBgZHBseXJgLg0KDQpOaMawIOG7nyB2w60gZOG7pSB0csOqbiwgY2jDum5nIHRhIG114buRbiB0w61uaCB0cnVuZyBiw6xuaCBgbzNgLCBgbm8yYCB2w6AgYHBtMjVgIHRoZW8gbsSDbSwgdGEgcGjhuqNpOg0KDQoxLiBU4bqhbyBiaeG6v24gYHllYXJgICANCjIuIFBow6JuIHTDoWNoIGRhdGEgZnJhbWUgdGhlbyBgeWVhcmAgIA0KMy4gVMOtbmggdG/DoW4gdHJ1bmcgYsOsbmggYG8zYCwgYG5vMmAsIHbDoCBgcG0yNWAgdGhlbyBgeWVhcmAuDQoNClbhu5tpIGAlPiVgLCB0YSBjw7MgdGjhu4MgdGjhu7FjIGhp4buHbiB0cm9uZyBt4buZdCBjw6J1IGzhu4duaC4NCg0KYGBge3J9DQptdXRhdGUoY2hpY2FnbywgeWVhciA9IGFzLlBPU0lYbHQoZGF0ZSkkeWVhciArIDE5MDApICU+JQ0KICBncm91cF9ieSh5ZWFyKSAlPiUNCiAgc3VtbWFyaXNlKHBtMjUgPSBtZWFuKHBtMjUsIG5hLnJtID0gVFJVRSksDQogICAgICAgICAgbzMgPSBtYXgobzN0bWVhbjIsIG5hLnJtID0gVFJVRSksDQogICAgICAgICAgbm8yID0gbWVkaWFuKG5vMnRtZWFuMiwgbmEucm0gPSBUUlVFKSkNCmBgYA0KDQpMxrB1IMO9IHLhurFuZywga2hpIGfhu41pIGjDoG0gYG11dGF0ZSgpYCB0YSBjw7MgdHJ1eeG7gW4gZGF0YSBmcmFtZSBgY2hpY2Fnb2AsIG5oxrBuZyDhu58gbmjhu69uZyBs4bqnbiBn4buNaSBow6BtIHNhdSwgdGEga2jDtG5nIGPhuqduIHBo4bqjaSB0cnV54buBbiBkYXRhIGZyYW1lIHbDoG8gxJHhu5FpIHPhu5EgxJHhuqd1IHRpw6puIG7hu69hLiBLaGkgc+G7rSBk4bulbmcgYCU+JWAsIMSR4bqndSByYSDhu58gbOG6p24gZ+G7jWkgdHLGsOG7m2MgxJHDoyDEkcaw4bujYyBuZ+G6p20gxJHGsGEgdsOgbyDEkeG7kWkgc+G7kSDEkeG6p3UgdGnDqm4g4bufIGzhuqduIGfhu41pIHRp4bq/cCB0aGVvLg0KDQpgYGB7cn0NCm11dGF0ZShjaGljYWdvLCBtb250aCA9IGFzLlBPU0lYbHQoZGF0ZSkkbW9uICsgMSkgJT4lIA0KICBncm91cF9ieShtb250aCkgJT4lIA0KICBzdW1tYXJpc2UocG0yNSA9IG1lYW4ocG0yNSwgbmEucm0gPSBUUlVFKSwNCiAgICAgICAgICAgIG8zID0gbWF4KG8zdG1lYW4yLCBuYS5ybSA9IFRSVUUpLA0KICAgICAgICAgICAgbm8yID0gbWVkaWFuKG5vMnRtZWFuMiwgbmEucm0gPSBUUlVFKSkNCmBgYA0KDQoNCg0K