class: title-slide, left, bottom # Object Orientated Programming with S3 ---- ## **NHS-R Conference 2021** ### **[Tom Jemmett][tj_email]** | Senior Healthcare Analyst ### 8<sup>th</sup> November 2021 --- # About The Strategy Unit / Me .pull-left[ ***"Leading research, analysis and change from within the NHS"*** **The Strategy Unit** is a specialist **NHS** team, based in [**Midlands and Lancashire CSU**][mlcsu]. We focus on the application of high-quality, multi-disciplinary analytical work. Our team comes from diverse backgrounds. Our academic qualifications include maths, economics, history, natural sciences, medicine, sociology, business and management, psychology and political science. Our career and personal histories are just as varied. Our staff are **NHS** employees, animated by **NHS** values. **The Strategy Unit** covers all its costs through project funding. But this is driven by need, not what we can sell. Any surplus is recycled for public benefit. [<svg viewBox="0 0 576 512" style="height:1em;position:relative;display:inline-block;top:.1em;" xmlns="http://www.w3.org/2000/svg"> <path d="M528 0H48C21.5 0 0 21.5 0 48v320c0 26.5 21.5 48 48 48h192l-16 48h-72c-13.3 0-24 10.7-24 24s10.7 24 24 24h272c13.3 0 24-10.7 24-24s-10.7-24-24-24h-72l-16-48h192c26.5 0 48-21.5 48-48V48c0-26.5-21.5-48-48-48zm-16 352H64V64h448v288z"></path></svg>][su_web] | [<svg viewBox="0 0 512 512" style="height:1em;position:relative;display:inline-block;top:.1em;" xmlns="http://www.w3.org/2000/svg"> <path d="M464 64H48C21.49 64 0 85.49 0 112v288c0 26.51 21.49 48 48 48h416c26.51 0 48-21.49 48-48V112c0-26.51-21.49-48-48-48zm0 48v40.805c-22.422 18.259-58.168 46.651-134.587 106.49-16.841 13.247-50.201 45.072-73.413 44.701-23.208.375-56.579-31.459-73.413-44.701C106.18 199.465 70.425 171.067 48 152.805V112h416zM48 400V214.398c22.914 18.251 55.409 43.862 104.938 82.646 21.857 17.205 60.134 55.186 103.062 54.955 42.717.231 80.509-37.199 103.053-54.947 49.528-38.783 82.032-64.401 104.947-82.653V400H48z"></path></svg>][su_email] | [<svg viewBox="0 0 512 512" style="height:1em;position:relative;display:inline-block;top:.1em;" xmlns="http://www.w3.org/2000/svg"> <path d="M459.37 151.716c.325 4.548.325 9.097.325 13.645 0 138.72-105.583 298.558-298.558 298.558-59.452 0-114.68-17.219-161.137-47.106 8.447.974 16.568 1.299 25.34 1.299 49.055 0 94.213-16.568 130.274-44.832-46.132-.975-84.792-31.188-98.112-72.772 6.498.974 12.995 1.624 19.818 1.624 9.421 0 18.843-1.3 27.614-3.573-48.081-9.747-84.143-51.98-84.143-102.985v-1.299c13.969 7.797 30.214 12.67 47.431 13.319-28.264-18.843-46.781-51.005-46.781-87.391 0-19.492 5.197-37.36 14.294-52.954 51.655 63.675 129.3 105.258 216.365 109.807-1.624-7.797-2.599-15.918-2.599-24.04 0-57.828 46.782-104.934 104.934-104.934 30.213 0 57.502 12.67 76.67 33.137 23.715-4.548 46.456-13.32 66.599-25.34-7.798 24.366-24.366 44.833-46.132 57.827 21.117-2.273 41.584-8.122 60.426-16.243-14.292 20.791-32.161 39.308-52.628 54.253z"></path></svg>][su_twitter] | [<svg viewBox="0 0 496 512" style="height:1em;position:relative;display:inline-block;top:.1em;" xmlns="http://www.w3.org/2000/svg"> <path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z"></path></svg>][su_github] | [<svg viewBox="0 0 448 512" style="height:1em;position:relative;display:inline-block;top:.1em;" xmlns="http://www.w3.org/2000/svg"> <path d="M416 32H31.9C14.3 32 0 46.5 0 64.3v383.4C0 465.5 14.3 480 31.9 480H416c17.6 0 32-14.5 32-32.3V64.3c0-17.8-14.4-32.3-32-32.3zM135.4 416H69V202.2h66.5V416zm-33.2-243c-21.3 0-38.5-17.3-38.5-38.5S80.9 96 102.2 96c21.2 0 38.5 17.3 38.5 38.5 0 21.3-17.2 38.5-38.5 38.5zm282.1 243h-66.4V312c0-24.8-.5-56.7-34.5-56.7-34.6 0-39.9 27-39.9 54.9V416h-66.4V202.2h63.7v29.2h.9c8.9-16.8 30.6-34.5 62.9-34.5 67.2 0 79.7 44.3 79.7 101.9V416z"></path></svg>][su_linkedin] ] .pull-right[ **Tom Jemmett** *Senior Healthcare Analyst* [thomas.jemmett@nhs.net][tj_email] - 10+ years experience within the NHS as a data analyst - BSc Computer Science and Pure Mathematics ([Open University][open_uni]) - [MBCS][bcs]/[AMIMA][ima] - Active member of NHS-R community - Senior Fellow of NHS-R academy - [AphA][apha] member, West Midlands branch champion [<svg viewBox="0 0 512 512" style="height:1em;position:relative;display:inline-block;top:.1em;" xmlns="http://www.w3.org/2000/svg"> <path d="M464 64H48C21.49 64 0 85.49 0 112v288c0 26.51 21.49 48 48 48h416c26.51 0 48-21.49 48-48V112c0-26.51-21.49-48-48-48zm0 48v40.805c-22.422 18.259-58.168 46.651-134.587 106.49-16.841 13.247-50.201 45.072-73.413 44.701-23.208.375-56.579-31.459-73.413-44.701C106.18 199.465 70.425 171.067 48 152.805V112h416zM48 400V214.398c22.914 18.251 55.409 43.862 104.938 82.646 21.857 17.205 60.134 55.186 103.062 54.955 42.717.231 80.509-37.199 103.053-54.947 49.528-38.783 82.032-64.401 104.947-82.653V400H48z"></path></svg>][tj_email] | [<svg viewBox="0 0 512 512" style="height:1em;position:relative;display:inline-block;top:.1em;" xmlns="http://www.w3.org/2000/svg"> <path d="M459.37 151.716c.325 4.548.325 9.097.325 13.645 0 138.72-105.583 298.558-298.558 298.558-59.452 0-114.68-17.219-161.137-47.106 8.447.974 16.568 1.299 25.34 1.299 49.055 0 94.213-16.568 130.274-44.832-46.132-.975-84.792-31.188-98.112-72.772 6.498.974 12.995 1.624 19.818 1.624 9.421 0 18.843-1.3 27.614-3.573-48.081-9.747-84.143-51.98-84.143-102.985v-1.299c13.969 7.797 30.214 12.67 47.431 13.319-28.264-18.843-46.781-51.005-46.781-87.391 0-19.492 5.197-37.36 14.294-52.954 51.655 63.675 129.3 105.258 216.365 109.807-1.624-7.797-2.599-15.918-2.599-24.04 0-57.828 46.782-104.934 104.934-104.934 30.213 0 57.502 12.67 76.67 33.137 23.715-4.548 46.456-13.32 66.599-25.34-7.798 24.366-24.366 44.833-46.132 57.827 21.117-2.273 41.584-8.122 60.426-16.243-14.292 20.791-32.161 39.308-52.628 54.253z"></path></svg>][tj_twitter] | [<svg viewBox="0 0 496 512" style="height:1em;position:relative;display:inline-block;top:.1em;" xmlns="http://www.w3.org/2000/svg"> <path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z"></path></svg>][tj_github] | [<svg viewBox="0 0 448 512" style="height:1em;position:relative;display:inline-block;top:.1em;" xmlns="http://www.w3.org/2000/svg"> <path d="M416 32H31.9C14.3 32 0 46.5 0 64.3v383.4C0 465.5 14.3 480 31.9 480H416c17.6 0 32-14.5 32-32.3V64.3c0-17.8-14.4-32.3-32-32.3zM135.4 416H69V202.2h66.5V416zm-33.2-243c-21.3 0-38.5-17.3-38.5-38.5S80.9 96 102.2 96c21.2 0 38.5 17.3 38.5 38.5 0 21.3-17.2 38.5-38.5 38.5zm282.1 243h-66.4V312c0-24.8-.5-56.7-34.5-56.7-34.6 0-39.9 27-39.9 54.9V416h-66.4V202.2h63.7v29.2h.9c8.9-16.8 30.6-34.5 62.9-34.5 67.2 0 79.7 44.3 79.7 101.9V416z"></path></svg>][tj_linkedin] ] --- class: middle, center # What is Object Orientated Programming? Let's have a look at a specific example, the `summary()` function. --- # The `summary()` function .panelset[ .panel[.panel-name[numeric vector] ```r my_numbers <- rnorm(20) summary(my_numbers) ``` ``` ## Min. 1st Qu. Median Mean 3rd Qu. Max. ## -1.21768 -0.09469 0.54184 0.61429 1.44219 2.70522 ``` ] .panel[.panel-name[character vector] ```r my_text <- c("Hello", "World") summary(my_text) ``` ``` ## Length Class Mode ## 2 character character ``` ] .panel[.panel-name[data.frame] .scroll-panel[ ```r summary(penguins) ``` ``` ## species island bill_length_mm bill_depth_mm ## Adelie :152 Biscoe :168 Min. :32.10 Min. :13.10 ## Chinstrap: 68 Dream :124 1st Qu.:39.23 1st Qu.:15.60 ## Gentoo :124 Torgersen: 52 Median :44.45 Median :17.30 ## Mean :43.92 Mean :17.15 ## 3rd Qu.:48.50 3rd Qu.:18.70 ## Max. :59.60 Max. :21.50 ## NA's :2 NA's :2 ## flipper_length_mm body_mass_g sex year ## Min. :172.0 Min. :2700 female:165 Min. :2007 ## 1st Qu.:190.0 1st Qu.:3550 male :168 1st Qu.:2007 ## Median :197.0 Median :4050 NA's : 11 Median :2008 ## Mean :200.9 Mean :4202 Mean :2008 ## 3rd Qu.:213.0 3rd Qu.:4750 3rd Qu.:2009 ## Max. :231.0 Max. :6300 Max. :2009 ## NA's :2 NA's :2 ``` ] ] .panel[.panel-name[linear model] .scroll-panel[ ```r model <- lm(data = drop_na(penguins), body_mass_g ~ flipper_length_mm) summary(model) ``` ``` ## ## Call: ## lm(formula = body_mass_g ~ flipper_length_mm, data = drop_na(penguins)) ## ## Residuals: ## Min 1Q Median 3Q Max ## -1057.33 -259.79 -12.24 242.97 1293.89 ## ## Coefficients: ## Estimate Std. Error t value Pr(>|t|) ## (Intercept) -5872.09 310.29 -18.93 <2e-16 *** ## flipper_length_mm 50.15 1.54 32.56 <2e-16 *** ## --- ## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1 ## ## Residual standard error: 393.3 on 331 degrees of freedom ## Multiple R-squared: 0.7621, Adjusted R-squared: 0.7614 ## F-statistic: 1060 on 1 and 331 DF, p-value: < 2.2e-16 ``` ] ] ] --- class: middle, center # How does `summary()` know how to do different things for different types of data? --- # A big long list of if's and else's? ``` r summary <- function(object, ...) { if (is.numeric(object)) { summary.numeric(object, ...) } else if (is.character(object)) { summary.character(object, ...) } else if (is.data.frame(object)) { summary.data.frame(object) } else if (is.linear_model(object)) { summary.lm(object) } else { ... } } ``` --- class: middle, center ![this is fine meme](img/this-is-fine.jpg) --- # Is the previous code maintainable? - `summary()` would need to have an `if` statement for every single different type of data that comes with base R -- - and every single different type of data that other users have created -- - and every single different type of data that may be created before the next R release --- class: middle, center ![gif of a cat looking confused saying wait, what?](img/wait-what.gif) --- # So how does summary work? ```r get("summary") ``` ``` ## function (object, ...) ## UseMethod("summary") ## <bytecode: 0x55d23687f0e8> ## <environment: namespace:base> ``` -- ``` r ?UseMethod ``` > R possesses a simple generic function mechanism which can be used for an object-oriented style of programming. Method > dispatch takes place based on the class(es) of the first argument to the generic function or of the object supplied as > an argument to UseMethod or NextMethod. --- # What is a "class"? (in s3) .panelset[ .panel[.panel-name[numeric vector] ```r class(my_numbers) ``` ``` ## [1] "numeric" ``` ] .panel[.panel-name[character vector] ```r class(my_text) ``` ``` ## [1] "character" ``` ] .panel[.panel-name[data.frame] ```r class(penguins) ``` ``` ## [1] "tbl_df" "tbl" "data.frame" ``` ] .panel[.panel-name[linear model] ```r class(model) ``` ``` ## [1] "lm" ``` ] ] Every value (or, "object") has one or more classes. A function like `summary()` is called a generic function which will take an object, look at the objects class(es), and then look to find a function that matches the first class, e.g. `summary.lm()`. If it doesn't find a function for the first class, it will move onto the second class. If it doesn't manage to find a function at all it will try `summary.default()`. --- # What other generic functions are there? .pull-left[ The big ones that you will no doubt be using day after day are: - `c()` - `plot()` - `print()` (called any time you type a variable into the console in R<sup>1</sup>) - `ggplot()` - most of the `{dplyr}` verbs (e.g. `mutate()`, `select()`, `filter()`) .footnote[ [1] (sort of) ] ] .pull-right[ It's also possible to create new generic functions. First, create the "generic" function: ``` r my_generic <- function(x, ...) UseMethod("my_generic") ``` Then, create implementations of this generic for the different types of data you want to support. ``` r my_generic.data.frame <- function(x, ...) do_stuff() my_generic.lm <- function(x, ...) do_other_stuff() ``` ] --- # Can you create your own class of data? Yes! And it's super easy! .panelset[ .panel[.panel-name[using a list] ```r my_data <- structure( list( name = "Tom", works_for = "The Strategy Unit", favourite_food = "π" ), class = "about_me" ) print.about_me <- function(x, ...) { cat("My name is ", x$name, ", and I work for ", x$works_for, ". ", "My favourite food is ", x$favourite_food, "\n", sep = "") } print(my_data) ``` ``` ## My name is Tom, and I work for The Strategy Unit. My favourite food is π ``` ] .panel[.panel-name[using a dataframe] .scroll-panel[ ```r my_penguins <- penguins # this is a tibble, which are a superset of data.frame # prepend our class to the existing classes class(my_penguins) <- c("my_penguins", class(my_penguins)) print.my_penguins <- function(x, ...) { cat("Penguins are awesome! π§\n") NextMethod() # now call the normal tibble print method } print(my_penguins) ``` ``` ## Penguins are awesome! π§ ## # A tibble: 344 Γ 8 ## species island bill_length_mm bill_depth_mm flipper_length_mm body_mass_g ## <fct> <fct> <dbl> <dbl> <int> <int> ## 1 Adelie Torgersen 39.1 18.7 181 3750 ## 2 Adelie Torgersen 39.5 17.4 186 3800 ## 3 Adelie Torgersen 40.3 18 195 3250 ## 4 Adelie Torgersen NA NA NA NA ## 5 Adelie Torgersen 36.7 19.3 193 3450 ## 6 Adelie Torgersen 39.3 20.6 190 3650 ## 7 Adelie Torgersen 38.9 17.8 181 3625 ## 8 Adelie Torgersen 39.2 19.6 195 4675 ## 9 Adelie Torgersen 34.1 18.1 193 3475 ## 10 Adelie Torgersen 42 20.2 190 4250 ## # β¦ with 334 more rows, and 2 more variables: sex <fct>, year <int> ``` ] ] ] --- # In Summary: - s3 is a way to make generic code that works with different types of data, but will produce different results - s3 works by having a "generic" function which will "dispatch" to the right function based on the "class" of the data - you can inspect the class of an object using `class(my_obj)`, or set it using `class(my_obj) <- "class"` - you can create new generic methods by creating a function that uses `UseMethod()` - you can create implementations of a generic by creating a function named `[generic].[class]()` --- # What next: - Go read the OOP chapters in [Advanced-R][advr-oop] - There are other types of OOP in R - s3 - s4 - RC/R6 - ggproto - I would advice learning s3 and R6 (R6 is far more similar to OOP found in other languages like C++, Java, C#, Python) - ggproto is only useful if you want to create your own ggplot extensions/work on ggplot - learn another programming language! See how OOP is different elsewhere! π --- class: middle, center # Thanks! Enjoy the rest of the conference! ## join the [NHS-R slack][nhsr-slack] Extra special thanks to Silvia CanelΓ³n ([@spcanelon](https://twitter.com/spcanelon)) for building the `{xaringan}` theme for the [`{NHSRtheme}`][nhsrtheme] package. <!-- urls etc. --> [tj_email]: mailto:thomas.jemmett@nhs.net [tj_twitter]: https://twitter.com/tomjemmett [tj_github]: https://github.com/tomjemmett [tj_linkedin]: https://www.linkedin.com/in/tom-jemmett-3872a8159/ [su_web]: https://strategyunitwm.nhs.uk/ [su_email]: mailto:strategy.unit@nhs.net [su_twitter]: https://twitter.com/strategy_unit [su_github]: https://github.com/The-Strategy-Unit/ [su_linkedin]: https://www.linkedin.com/company/the-strategy-unit/ [mlcsu]: https://www.midlandsandlancashirecsu.nhs.uk/ [open_uni]: https://www.open.ac.uk/ [bcs]: https://www.bcs.org/ [ima]: https://ima.org.uk/ [apha]: https://www.aphanalysts.org/ [advr-oop]: https://adv-r.hadley.nz/oo.html [nhsr-slack]: https://join.slack.com/t/nhsrcommunity/shared_invite/zt-arabo68y-_Uv5uU2dmtfe8mk5ing9Fg [nhsrtheme]: https://github.com/nhs-r-community/NHSRtheme