class: center, middle, inverse, title-slide # Functions in R ## ️ 👩💻 +
###
Ariel Muldoon ### May 4, 2021 --- ## Today's Goal Overall - **Gain understanding of and learn how to write basic functions in R** We will - Learn to control input and output of functions in R - Read through existing functions - Use conditions to return different outputs based on the inputs -- <br/> *Before we begin:* Make sure you saved [`week06_functions.Rmd`](files/week06_functions.Rmd) from the class website onto your computer. We will be running code from this file. --- ## Resources - For more on all aspects of functions, see the [Functions chapter](https://r4ds.had.co.nz/functions.html) in Wickham and Grolemund's *R for Data Science* - [Rice and Lumley's presentation](http://faculty.washington.edu/kenrice/rintro/intro17sess09v2.pdf) on writing functions is a nice overview - If you are ready to go further with functions see [Stephanie Kirmer's presentation](https://skirmer.github.io/presentations/functions_with_r.html#1) --- ## What is a function? .center[ <div class="figure"> <img src="figs/week06_files/function_diagram.png" alt="A schematic showing inputs going into a 'function' that produces output based on those inputs." width="75%" /> <p class="caption">From Stephanie Kirmer's Functions in R presentation</p> </div> ] ??? Functions take inputs, do some set of tasks, and make the output we want R is built on functions, and people have already been using ones others have created in their everyday R work --- ## When to write your own function? - When doing repetitive tasks <blockquote class="twitter-tweet tw-align-center" data-lang="en"><p lang="en" dir="ltr">Write same code three times, write a function</p>— David Robinson (@drob) <a href="https://twitter.com/drob/status/928447584712253440">Tweet November 8, 2017</a></blockquote> <script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script> <br/> - To do a specific task or get a specific kind of output ??? Mainly about copying and pasting code over and over again Also can get exactly what you want if other functions don't do it --- ## Naming your functions When you start making functions you'll need to *name* them. Some general style rules: - Make function names human readable and descriptive - Try to be concise, but meaningful names that indicate what the function does is more important - Be consistent in your naming style; for example, don't name one plotting function `make_scatterplot()` and another `barplotFunction()` --- ## Naming your functions .pull-left[ Common naming styles in R <div class="figure"> <img src="figs/week06_files/coding_cases.png" alt="Artwork by Allison Horst to show the different naming conventions in R, including snake case, camel case, kebab case, etc ." width="100%" /> <p class="caption">Artwork by @allison_horst</p> </div> ] .pull-right[ .footnote[ Read more about function naming conventions and styles in [*R for Data Science*](https://r4ds.had.co.nz/functions.html#functions-are-for-humans-and-computers) ] ] ??? We'll be using snake_case today, but you may decide to use something else in your own work. Also this is missing period separated, possible called dot case or staccato case: https://twitter.com/cantabile/status/1274640612315131905 --- class: center, middle, inverse, hide-logo # <font style="font-family: cursive; font-style:italic">Let's get started!</font> --- ## Running code - Open the copy of [`week06_functions.Rmd`](files/week06_functions.Rmd) that you saved - I recommend switching to using the visual editor using the ![](https://rstudio.github.io/visual-markdown-editing/images/visual_mode_2x.png) button - Make sure you are working in a clean R session (no other packages loaded) -- <br/><br/> **Set up** We'll learning to write functions by first running example code I've already written. At the end of each section you'll have a chance to practice what we just covered. --- ## Structure of a function We create functions with `function()`. This involves: .pull-left[ 1. Assigning a name for the new function. Use your preferred assignment operator. ] .pull-right[ ```r *my_function = function(argument1, etc.) { <Write code here> return(output) } ``` ] --- ## Structure of a function We create functions with `function()`. This involves: .pull-left[ 1. Assigning a name for the new function. Use your preferred assignment operator. 2. Allowing for inputs to the function as *arguments* within `function()`. The inputs are values or objects we will use within the function. ] .pull-right[ ```r *my_function = function(argument1, etc.) { <Write code here> return(output) } ``` ] --- ## Structure of a function We create functions with `function()`. This involves: .pull-left[ 1. Assigning a name for the new function. Use your preferred assignment operator. 2. Allowing for inputs to the function as *arguments* within `function()`. The inputs are values or objects we will use within the function. 3. Writing code to create the function output. This code goes between the curly braces (`{`) and uses the provided inputs to return a single output. ] .pull-right[ ```r my_function = function(argument1, etc.) { * <Write code here> * return(output) } ``` ] --- ## Structure of a function .pull-left[ Once we have run the code to create a function, we use it like other functions in R, passing inputs to the arguments. ] .pull-right[ ```r my_function(argument1 = input1, etc.) ``` ] --- ## Basic function .pull-left[ Let's start with a very basic function. This function, named `return_input`, will have a single argument, `input`. The function takes the input and returns it unchanged. ] .pull-right[ <br/> ```r return_input = function(input) { return(input) } ``` ] ??? Make sure you run this function in the Rmd document --- ## Basic function The goal here is for you to see how we give input to the `input` argument and the function returns an output based on that input. -- .pull-left[ For example, we could give a single, numeric value to `input`. ] .pull-right[ ```r return_input(input = 1) ``` ``` ## [1] 1 ``` ] --- ### Your turn Write code in the empty code chunk that is provided. Spend a few minutes passing different values and objects to the `input` argument of `return_input()` and looking at the output. Pass the following to the `input` argument: - The letter `"a"` - A series of letters using `letters[1:5]` - A vector of numbers like `c(1, 5, 9, 10)` - The data.frame `mtcars`
01
:
00
??? Students may want to follow up on what they saw. --- ## A function with two arguments A function can have as many arguments as we want to make. We keep adding them within `function()` when we define the function. Like with function naming, keeping your argument names descriptive is useful. ??? You don't want argument names too long but it is nice to have them related to the kind of input will be giving -- .pull-left[ <br/> Our next function, named `sum_two`, has two input arguments. It outputs the sum of the two inputs. ] .pull-right[ <br/> ```r sum_two = function(num1, num2) { sum = num1 + num2 return(sum) } ``` ] --- ## A function with two arguments .pull-left[ This is what it looks like if we pass single numeric values to the two arguments in `sum_two`. ] .pull-right[ ```r sum_two(num1 = 1, num2 = 2) ``` ``` ## [1] 3 ``` ] --- ### Your turn 1 Write code in the empty code chunk that is provided. Explore what happens when using different types of input for the two arguments in `sum_two`. Pass the following inputs to `num1` and `num2`: - Vectors of numbers, `c(1, 5, 9, 10)` and `c(2, 3, 4, 5)` - Letters, `"a"` and `"b"`
02
:
00
-- <br/><br/><br/> What happened when you put letters in? ??? We'll talk about an option to address this later today. --- ### Your turn 2 Write code in the empty code chunk that is provided. Practice writing your own function, named `norm_by_y`. - The function should have two numeric inputs. Name the arguments whatever you'd like. - The function output will be the difference between the inputs divided by the second input. An equation for what I'm describing looks like: (x - y)/y
04
:
00
--- ### Your turn 2 solution ```r norm_by_y = function(num1, num2) { result = (num1 - num2)/num2 return(result) } ``` --- ## Remove explicit `return()` I wanted to start out using `return()` in the function because I think it makes it very clear what the output is when first learning about function inputs and outputs. In R it is not standard to use this coding style. Instead, we print the output object to *return* the output. -- .pull-left[ Here are the same two functions we previously created but without the explicit use of `return()`. ```r return_input = function(input) { input } return_input(input = 1) ``` ``` ## [1] 1 ``` ] .pull-right[ <br/><br/> ```r sum_two = function(num1, num2) { sum = num1 + num2 sum } sum_two(num1 = 1, num2 = 2) ``` ``` ## [1] 3 ``` ] ??? Explicit returns are more common in other languages. Remove and the functions still work the same way. Using explicit returns are useful when you are getting started thinking about inputs and outputs. --- ## Explore existing function Learning how to write functions helps you understand existing functions. Taking a look at the underlying *source code* can help us figure out what a function is doing and, potentially, help us figure out where any problems we are having may arise from. -- <br/> .pull-left[ You can see the code within a function by running the function name without parentheses in R. Ignore the last two lines. ] .pull-right[ ```r union ``` ``` ## function (x, y) ## unique(c(as.vector(x), as.vector(y))) ## <bytecode: 0x000001243df8b4e8> ## <environment: namespace:base> ``` ] <br/> **Note:** Use function `getAnywhere()` to print source code for specific methods of generic functions, such as `plot.lm` for making plots based on **lm** objects. ??? The last two lines (bytecode and environment) aren't relevant at this time. Be ready to answer questions about the lack of the curly braces Not sure everyone will know about, e.g., `plot.lm`, the plotting function for lm objects. `plot()` is a generic function that does different things depending on what type of object you are using it on. --- ## Explore existing function .pull-left[ You can use `View()` to open the source code of a function in a new window in your `Source` pane. The `edit()` function is another useful option, and works better than `View()` when you need `getAnywhere()`. ] .pull-right[ ```r View(union) ``` .center[ <img src="figs/week06_files/view_function.png" title="A screenshot showing the new pane that opens when viewing a function. This uses the union() function as the example." alt="A screenshot showing the new pane that opens when viewing a function. This uses the union() function as the example." width="75%" /> ] ] <br/><br/> **Note:** Many base R functions are written in C++ and so do not return code. ??? May want to discuss the issue of functions not returning anything that is recognizable. Some is due to C++ but some is for functions that work on different types of objects like, e.g., plot.lm and the generic plot function doesn't show much edit() allows you to edit a function and make a new version, as well --- ### Your turn Write code in the code chunk that is provided. Take a few minutes and explore the code in the `union()` function. First, use the vectors `x` and `y` provided in the chunk as inputs to `union()`. Then go through the `union()` source code one step at a time. 1. Use `as.vector()` on each object. 2. Concatenate the two objects with `c()`. 3. Use `unique()` on the vector from step 2.
04
:
00
??? The goal here is to drive home the idea that looking at the source code helps us figure out what the function is doing and that we can run the code within the function outside the function to see how it works. --- ### Your turn solution Here's an example of how I would step through the `union()` code, breaking down each piece of the code. ```r x = c(1, 2, 3) y = c(2, 3, 4) # Using union() union(x = x, y = y) # Using code within union() as.vector(x) as.vector(y) c(as.vector(x), as.vector(y)) unique(c(as.vector(x), as.vector(y))) ``` --- ## Returning multiple outputs A function in R can only return a single output, even if we want to return more. -- .pull-left[ To demonstrate this, here's an attempt to return the sum of two numbers as well as the two original numbers using a function called `return_all()`. ] .pull-right[ ```r return_all = function(num1, num2) { sum = num1 + num2 sum num1 num2 } ``` ] --- ## Returning multiple outputs What happens when we use this function? .pull-left[ It returns the very last thing we asked for. In this case, the last thing we asked for is the input to `num2`. ] .pull-right[ ```r return_all(num1 = 1, num2 = 2) ``` ``` ## [1] 2 ``` ] --- ## Returning multiple outputs .pull-left[ If we want multiple outputs we'll need to combine the outputs into one. A *list* is one option for this. This example code shows a *named* list. ] .pull-right[ ```r return_all2 = function(num1, num2) { sum = num1 + num2 * list(sum = sum, * num1 = num1, * num2 = num2) } return_all2(num1 = 1, num2 = 2) ``` ``` ## $sum ## [1] 3 ## ## $num1 ## [1] 1 ## ## $num2 ## [1] 2 ``` ] --- ## Returning multiple outputs .pull-left[ A data.frame as output is useful in some cases. ] .pull-right[ ```r return_all3 = function(num1, num2) { sum = num1 + num2 * data.frame(sum = sum, * num1 = num1, * num2 = num2) } return_all3(num1 = 1, num2 = 2) ``` ``` ## sum num1 num2 ## 1 3 1 2 ``` ] ??? Of course a data.frame is a kind of list. This may be out of scope --- ### Your turn Write code in the empty code chunk that is provided. Go back to the function you made earlier called `norm_by_y()`. - Add a 2 to the end of the function name to name a new function `norm_by_y2`. - Edit the function so it returns both (x - y)/y and (x - y) in a list.
03
:
00
--- ### Your turn solution ```r norm_by_y2 = function(num1, num2) { result1 = (num1 - num2)/num2 result2 = num1 - num2 list(result1, result2) } ``` --- ## Default arguments We can assign *default values* to arguments. If we want to use the default value of an argument then we don't have to write that argument out. For example, many of you may know that `na.rm = FALSE` is the default in `mean()`. ??? The next sections are to show some concepts but we won't take the time to practice. -- .pull-left[ Let's make a function where the default for `na.rm` is `TRUE` so `NA` values are automatically stripped before calculating the mean. The key here is that we provide a default value `TRUE` when we define the `remove.na` argument within `function()`. ] .pull-right[ ```r mean_nona = function(vector, remove.na = TRUE) { mean(vector, na.rm = remove.na) } ``` <br/> Note `remove.na` is passed to the `na.rm` argument within `mean()` inside our function. ] ??? Making your own preferred output like this is a good reason to write a function --- ## Default arguments .pull-left[ Here's how this function works if the vector contains `NA`. We don't need to write out the `remove.na` argument if we want the default `TRUE`. ] .pull-right[ ```r mean_nona(vector = c(1, 2, NA)) ``` ``` ## [1] 1.5 ``` ] --- ## Default arguments .pull-left[ To change the default, pass `FALSE` to the `remove.na` argument. ] .pull-right[ ```r mean_nona(vector = c(1, 2, NA), * remove.na = FALSE) ``` ``` ## [1] NA ``` ] --- ## Arguments names vs positions This feels like a good time to talk about writing out argument *names* vs using argument *positions*. Both are allowed and can work fine. -- .pull-left[ Here is an example of using *positions* instead of writing out argument names. We pass a numeric vector to the first argument in `mean()` and 0.1 to the second argument. <br/> What argument does the second input pass to? ] .pull-right[ ```r mean(c(1:10, 50), 0.1) ``` ``` ## [1] 6 ``` ] ??? Encourage students to go to the documentation to see the argument order. --- ## Argument names vs positions .pull-left[ Writing out argument names in this case makes the code a little more understandable, especially for folks who don't use `trim` much in `mean()`. ] .pull-right[ ```r mean(x = c(1:10, 50), trim = 0.1) ``` ``` ## [1] 6 ``` ] --- ## Argument names vs positions I most often recommend writing out argument names for clear code. However, there are cases where we don't need to write out every single argument name, especially for well-known functions. ??? This is clear code for other but also for your future self -- .pull-left[ For example, for model fitting functions like `lm()`, the first argument is always the model formula. This is well known and it is fairly standard to leave the `formula` argument name off. ] .pull-right[ ```r lm(mpg ~ disp, data = mtcars) ``` ``` ## ## Call: ## lm(formula = mpg ~ disp, data = mtcars) ## ## Coefficients: ## (Intercept) disp ## 29.59985 -0.04122 ``` ] ??? But use argument names for other arguments in the function -- Functions from packages like **dplyr** use the dataset as the first argument, and you will rarely see the first argument name written out. --- ## Argument names vs positions You'll sometimes see code from advanced users where they stop writing out argument names for efficient typing. This can lead to confusion for newer users copying the code and I have seen it lead to mistakes. -- .pull-left[ For example, if I wanted to fit a weighted regression, I would leave off `formula` but then write out the argument names to define the dataset in `data` and weights in `weights`. ] .pull-right[ ```r lm(mpg ~ disp, data = mtcars, weights = cyl) ``` ``` ## ## Call: ## lm(formula = mpg ~ disp, data = mtcars, weights = cyl) ## ## Coefficients: ## (Intercept) disp ## 28.63238 -0.03809 ``` ] --- ## Argument names vs positions .pull-left[ What happens if I leave the argument names off? The model fits without error. ```r lm(mpg ~ disp, mtcars, cyl) ``` ``` ## ## Call: ## lm(formula = mpg ~ disp, data = mtcars, subset = cyl) ## ## Coefficients: ## (Intercept) disp ## 29.11391 -0.03525 ``` ] .pull-right[ <br/><br/> The third argument to `lm()` is `subset`, not `weights`. Luckily the `call` in the output shows the mistake, but that can be easy to miss. I've seen people make this sort of mistake and not catch it in a real analysis. ] ??? This problem arises because folks see more advanced user code without argument names written out without understanding that it relies on argument position --- ## Dot-dot-dot to pass additional arguments Including `...` within `function()` is a way to pass additional arguments into the function. This is used to pass arguments to existing functions used inside the one you are making. ??? This description will make more sense with an example -- .pull-left[ For example, let's say we were again using the existing function `mean()` inside a new function called `use_mean`. ] .pull-right[ ```r use_mean = function(vector) { mean(vector) } ``` As you know, if we use a vector that contains `NA` this returns `NA` by default. ```r use_mean(vector = c(1, 2, NA)) ``` ``` ## [1] NA ``` ] --- ## Dot-dot-dot to pass additional arguments We want to be able to pass in `na.rm = TRUE` to `mean()`. Since this is an existing argument to `mean()` we can use `...` to achieve this. -- .pull-left[ We literally add `...` into the arguments of the function and place it inside `mean()`. Inside the function we put `...` where we want to be able to input additional arguments. ] .pull-right[ ```r use_mean2 = function(vector, ...) { mean(vector, ...) } ``` ] --- ## Dot-dot-dot to pass additional arguments Now we can pass arguments from `mean()` into our newly-defined function. .pull-left[ Anything we put in after the `vector` argument is passed into `mean()`. Here I use `na.rm` but could also use `trim`. ] .pull-right[ ```r use_mean2(vector = c(1, 2, NA), * na.rm = TRUE) ``` ``` ## [1] 1.5 ``` ] --- ## Dot-dot-dot to pass additional arguments **Important note:** When using `...`, any misspelled arguments will not raise an error and so you may miss typos you've made. .pull-left[ Look what happens when I misspell `na.rm` as `na.rn`. The function still runs but now returns an unexpected result. ] .pull-right[ ```r use_mean2(vector = c(1, 2, NA), * na.rn = TRUE) ``` ``` ## [1] NA ``` ] ??? This is something that can happen when using functions someone else wrote, as well, so watch for `...` in the documentation --- ## Conditional returns Back when we made `sum_two()` I had you look at the error you got when you passed in characters when the function was expecting numbers. ```r sum_two(num1 = "a", num2 = "b") ``` ``` ## Error in num1 + num2: non-numeric argument to binary operator ``` -- <br/> That error message isn't particularly clear. Adding *conditions* in this function to return a useful message if the values aren't numeric is an example of what I've seen called conditional returns or *conditional execution*. ??? This starts getting at the idea of making functions that are more useful than for a single, specific use case. Like for other people to use. --- ## Conditional returns We add *conditions* via logical statements, often involving `if-else` statements. In the documentation for `if()` the **Usage** is: > if(cond) expr -- <br/><br/> More commonly in functions we'll use `if()` followed up with `else`: > if(cond) cons.expr else alt.expr The if-else condition can be interpreted as: > Test some condition. If the condition is met, return one thing. If it's not met, return something else. --- ## Conditional returns Here's a skeleton example of an if-else statement. Note the logical "condition" within `if()` and the two sets of curly braces (`{`). ```r if(condition) { Do something } else { Alternative something } ``` ??? You will see people skip writing out the curly braces (`{`) in some simple cases but I recommend sticking with them for the clearest code. You can read one opinion on coding style in [R for Data Science](https://r4ds.had.co.nz/functions.html#code-style). --- ## Conditional returns OK, we're ready to see an example. We'll add conditional returns to our `sum_two()` function. .pull-left[ This uses `is.character()` to test if the input to `num1` is a character. If it is a character, it returns an informative message. If it is not a character, it returns the sum. ] .pull-right[ ```r sum_two_if = function(num1, num2) { * if(is.character(num1)) { "You must enter a number or vector of numbers for num1." } else { num1 + num2 } } ``` ] --- ## Conditional returns .pull-left[ Now let's look at the output when we use a letter for `num1`. ```r sum_two_if(num1 = "a", num2 = "b") ``` ``` ## [1] "You must enter a number or vector of numbers for num1." ``` ] -- .pull-right[ And here's the output if we pass a number. ```r sum_two_if(num1 = 1, num2 = 2) ``` ``` ## [1] 3 ``` ] --- ## Conditional returns .pull-left[ We can expand the condition with an *or* statement via `|`. In this case it makes sense to test if the input to *either* argument is a character. If you need an *and* statement you can use `&`. ] .pull-right[ ```r sum_two_if2 = function(num1, num2) { * if(is.character(num1) | * is.character(num2)) { "You must enter a number or vector of numbers for both num1 and num2." } else { num1 + num2 } } sum_two_if2(num1 = 1, num2 = "b") ``` ``` ## [1] "You must enter a number or vector of numbers for both num1 and num2." ``` ] ??? I'm keeping these examples basic with a single OR but they can be complex --- ### Your turn 1 Write code in the code chunk that is provided. Starting with `use_mean2()` function, included in the chunk: 1. Add in a condition to test if the `vector` argument is numeric using `is.numeric()` 2. If it is, return the mean of the vector 3. Otherwise return a message explaining what is wrong
04
:
00
--- ### Your turn 1 solution ```r use_mean2 = function(vector, ...) { if(is.numeric(vector)) { mean(vector, ...) } else { "Your input is not numeric." } } ``` --- ### Your turn 2 Take a look at the code for the `as.factor()` function, which takes a vector of values for its `x` argument. This function contains multiple conditions use if-elseif-else, which we haven't seen yet. Note the function authors left out curly braces for simple conditions. Try to summarize what each condition is and generally what is done at each step. You don't need to go into great detail. I'll get you started for each of the three conditions: 1. First the function checks if... 2. Ignore the `!is.object(x)` part of the second condition. Second the function checks... 3. Finally, if the other two conditions aren't met the function...
04
:
00
--- ### Your turn 2 solution 1. First the function checks if the input is already a factor. If so, it returns the input. 2. Ignore the `!is.object(x)` part of the second condition. Second the function checks if the input is an integer. If so, it creates the levels by sorting the unique values, matches the levels to the values in the input, and makes the result a factor. 3. Finally, if the other two conditions aren't true the function using function `factor()` on the input. --- ## Doing more in a function So far we've written very basic, one-step functions. Of course you can do many, many steps in a function to achieve your desired output. Let's make one slightly longer function. -- .pull-left[ The `get_rsq()` function: - Takes a dataset as input - Fits a model to that dataset - Gets the model summary - Pulls out and rounds the `\(R^2\)` value - Returns the rounded `\(R^2\)` ] .pull-right[ ```r get_rsq = function(data) { mod = lm(mpg ~ disp, data = data) summ = summary(mod) rsq = round(summ$r.squared, digits = 2) rsq } ``` <br/> Note the `formula` variables in `lm()` are fixed, which is a common set up if analyzing many datasets with the same variables in them. ] --- ## Doing more in a function .pull-left[ The `get_rsq()` function is set up to use with the variables in the `mtcars` dataset. ```r get_rsq(data = mtcars) ``` ``` ## [1] 0.72 ``` ] -- .pull-right[ This means we can pass a subset of the data as input to this function. ```r get_rsq(data = subset(mtcars, cyl == 4) ) ``` ``` ## [1] 0.65 ``` ] <br/><br/> You'd use this sort of set-up to get ready to *iterate* through many similar datasets, which is one important use of functions. --- ## More on functions You may reach a point where you need to know more about function environments or testing. 👩🔧 Or you may find yourself with have a series of functions that you use a lot and want to make a local R package. 💖 If that's ever the case for you, [Stephanie Kirmer's talk](https://skirmer.github.io/presentations/functions_with_r.html#1) and exercises are a good place to start. 🙂 --- ## Next week - We'll return to Git <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="M100.59 334.24c48.57 3.31 58.95 2.11 58.95 11.94 0 20-65.55 20.06-65.55 1.52.01-5.09 3.29-9.4 6.6-13.46zm27.95-116.64c-32.29 0-33.75 44.47-.75 44.47 32.51 0 31.71-44.47.75-44.47zM448 80v352a48 48 0 0 1-48 48H48a48 48 0 0 1-48-48V80a48 48 0 0 1 48-48h352a48 48 0 0 1 48 48zm-227 69.31c0 14.49 8.38 22.88 22.86 22.88 14.74 0 23.13-8.39 23.13-22.88S258.62 127 243.88 127c-14.48 0-22.88 7.84-22.88 22.31zM199.18 195h-49.55c-25-6.55-81.56-4.85-81.56 46.75 0 18.8 9.4 32 21.85 38.11C74.23 294.23 66.8 301 66.8 310.6c0 6.87 2.79 13.22 11.18 16.76-8.9 8.4-14 14.48-14 25.92C64 373.35 81.53 385 127.52 385c44.22 0 69.87-16.51 69.87-45.73 0-36.67-28.23-35.32-94.77-39.38l8.38-13.43c17 4.74 74.19 6.23 74.19-42.43 0-11.69-4.83-19.82-9.4-25.67l23.38-1.78zm84.34 109.84l-13-1.78c-3.82-.51-4.07-1-4.07-5.09V192.52h-52.6l-2.79 20.57c15.75 5.55 17 4.86 17 10.17V298c0 5.62-.31 4.58-17 6.87v20.06h72.42zM384 315l-6.87-22.37c-40.93 15.37-37.85-12.41-37.85-16.73v-60.72h37.85v-25.41h-35.82c-2.87 0-2 2.52-2-38.63h-24.18c-2.79 27.7-11.68 38.88-34 41.42v22.62c20.47 0 19.82-.85 19.82 2.54v66.57c0 28.72 11.43 40.91 41.67 40.91 14.45 0 30.45-4.83 41.38-10.2z"></path></svg> and GitHub <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>, learning deal with merge conflicts and make pull requests - Make sure you have a current version of packages **usethis** and **gh** installed - Install [GitKraken](https://www.gitkraken.com/) .center[ <img src="figs/week07_files/usethis_logo.png" title="package usesthis logo" alt="package usesthis logo" width="15%" /> ] .footnote[ [Code for slides](https://github.com/aosmith16/spring-r-topics/tree/main/docs/slides) Slides created via the R packages: [**xaringan**](https://github.com/yihui/xaringan), [gadenbuie/xaringanthemer](https://github.com/gadenbuie/xaringanthemer), [gadenbuie/xaringanExtra](https://github.com/gadenbuie/xaringanExtra) .center[*This work is licensed under the Creative Commons Attribution-NonCommercial 4.0 International License. To view a copy of this license, visit http://creativecommons.org/licenses/by-nc/4.0/.*] ]