class: center, middle, inverse, title-slide .title[ # Geospatial Analysis with R ] .subtitle[ ## Class 7 ] --- ```r library(stars) s3url <- glue::glue("/vsis3/activemapper/", "planet/composite_sr_buf_fix/GS/", "tile486317_736815_736967.tif") # not accessible p <- read_stars(s3url, proxy = TRUE) png(here::here("external/slides/figures/ghana_planet_stars.png"), height = 2.9, width = 8, units = "in", res = 300, bg = "white") plot(p) dev.off() ``` --- # Today - Quiz - Building blocks of R: data types, functions, etc - Useful functions - Indexing and subsetting - Control structures --- ## The R Ecosystem ### Vectors <div class="figure" style="text-align: center"> <img src="figures/class2_vector.png" alt="Credit: L. Song" width="1921" /> <p class="caption">Credit: L. Song</p> </div> --- ## Let's create some data - `vector` - `matrix` - `data.frame` - `list` --- ## Creating a vector ```r v1 <- c(1,2,3,4,5) v2 <- 6:10 print(v1) ``` ``` ## [1] 1 2 3 4 5 ``` ```r print(v2) ``` ``` ## [1] 6 7 8 9 10 ``` --- ## Combining vectors ```r v1 <- c(1,2,3,4,5) v2 <- 6:10 v3 <- c(v1, v2) print(v3) ``` ``` ## [1] 1 2 3 4 5 6 7 8 9 10 ``` ```r print(class(v3)) ``` ``` ## [1] "numeric" ``` --- ## Combining vectors What happens when you combine data types in a vector? ```r v4 <- c(v1, "test") print(v4) ``` ``` ## [1] "1" "2" "3" "4" "5" "test" ``` ```r print(class(v4)) ``` ``` ## [1] "character" ``` --- ## Vectorized operations ```r v1 <- 1:9 print(v1) ``` ``` ## [1] 1 2 3 4 5 6 7 8 9 ``` ```r print(v1 + 5) ## addition applied to each element in vector ``` ``` ## [1] 6 7 8 9 10 11 12 13 14 ``` ```r print(v1 * 3) ## multiplication applied to each element in vector ``` ``` ## [1] 3 6 9 12 15 18 21 24 27 ``` --- ## Accessing elements in vector ```r v1 <- (1:9)*3 print(v1) ``` ``` ## [1] 3 6 9 12 15 18 21 24 27 ``` ```r print(v1[4]) ``` ``` ## [1] 12 ``` ```r v1 <- letters[1:7] print(paste0("This letter is ", v1)) # paste0 concatenates strings ``` ``` ## [1] "This letter is a" "This letter is b" "This letter is c" "This letter is d" ## [5] "This letter is e" "This letter is f" "This letter is g" ``` Question: How would you say "Letter 1 is a", "Letter 2 is b", etc. --- ## Creating a matrix ```r v1 <- 1:9 m1 <- matrix(v1, nrow = 3, byrow = T) m2 <- cbind(v1 = 1:3, v2 = 1:3, v3 = 0:0) print(m1) ``` ``` ## [,1] [,2] [,3] ## [1,] 1 2 3 ## [2,] 4 5 6 ## [3,] 7 8 9 ``` ```r print(m2) ``` ``` ## v1 v2 v3 ## [1,] 1 1 0 ## [2,] 2 2 0 ## [3,] 3 3 0 ``` --- ## Accessing elements in a matrix ```r m2 <- cbind(v1 = 1:3, v2 = 1:3, v3 = 0:0) print(m2) ``` ``` ## v1 v2 v3 ## [1,] 1 1 0 ## [2,] 2 2 0 ## [3,] 3 3 0 ``` ```r print("first row:") ``` ``` ## [1] "first row:" ``` ```r print(m2[1, ]) ``` ``` ## v1 v2 v3 ## 1 1 0 ``` ```r print("second column") ``` ``` ## [1] "second column" ``` ```r print(m2[, 2]) ``` ``` ## [1] 1 2 3 ``` ```r print("access by column name") ``` ``` ## [1] "access by column name" ``` ```r print(m2[, "v2"]) ``` ``` ## [1] 1 2 3 ``` Question: Create a 3 row, 4 column matrix, where the first column is (1, 2, 3), second column is (4, 5, 6 ) etc. --- ## Lists Lists can mix object of different types. ```r l <- list("a", 1, 0.5, TRUE) print(l) ``` ``` ## [[1]] ## [1] "a" ## ## [[2]] ## [1] 1 ## ## [[3]] ## [1] 0.5 ## ## [[4]] ## [1] TRUE ``` ```r print(str(l)) # structure of l ``` ``` ## List of 4 ## $ : chr "a" ## $ : num 1 ## $ : num 0.5 ## $ : logi TRUE ## NULL ``` --- ## Lists Lists can also be nested. ```r l1 <- list("a", 1, 0.5, TRUE) l2 <- list(l1, "test", matrix(1:9, nrow = 3)) ## first element of l2 is a list. print(l2) ``` ``` ## [[1]] ## [[1]][[1]] ## [1] "a" ## ## [[1]][[2]] ## [1] 1 ## ## [[1]][[3]] ## [1] 0.5 ## ## [[1]][[4]] ## [1] TRUE ## ## ## [[2]] ## [1] "test" ## ## [[3]] ## [,1] [,2] [,3] ## [1,] 1 4 7 ## [2,] 2 5 8 ## [3,] 3 6 9 ``` Question: what is the length of l1? what is the length of each element of l1? (use `length` function) --- ## Accessing elements in lists Use double brackets `[[ ]]` to access elements in lists. ```r l1 <- list("a", 1, 0.5, TRUE) l2 <- list(l1, "test", matrix(1:9, nrow = 3)) ## first element of l2 is a list. print(l2[[1]]) ``` ``` ## [[1]] ## [1] "a" ## ## [[2]] ## [1] 1 ## ## [[3]] ## [1] 0.5 ## ## [[4]] ## [1] TRUE ``` ```r print(l2[[2]]) ``` ``` ## [1] "test" ``` ```r print(l2[[3]]) ``` ``` ## [,1] [,2] [,3] ## [1,] 1 4 7 ## [2,] 2 5 8 ## [3,] 3 6 9 ``` Question: The first element of l2 is a list. Access this first element from l2, then the 4th element from l1. --- ## Data frames We will use data frames OFTEN. Data frames are really tables, but they are stored in R as lists, with the following conditions. - Each element in the list represents a column. - Each element in the list is an atomic vector. - The length of each vector is the same (the number of rows) ```r m1 <- cbind(1:3, letters[1:3]) print(m1) # a matrix cannot hold multiple data types. ``` ``` ## [,1] [,2] ## [1,] "1" "a" ## [2,] "2" "b" ## [3,] "3" "c" ``` ```r print(class(m1)) ``` ``` ## [1] "matrix" "array" ``` ```r df <- data.frame(numbers = 1:3, abc = letters[1:3]) print(df) # a data frame CAN hold multiple data types. ``` ``` ## numbers abc ## 1 1 a ## 2 2 b ## 3 3 c ``` ```r print(class(df)) ``` ``` ## [1] "data.frame" ``` --- ## Useful functions - `print` - You can print just about anything, but as a generic function, `print` works differently for different types of objects. ```r a <- "test" df <- data.frame(numbers = 1:3, abc = letters[1:3]) print(a) # a data frame CAN hold multiple data types. ``` ``` ## [1] "test" ``` ```r print(df) ``` ``` ## numbers abc ## 1 1 a ## 2 2 b ## 3 3 c ``` ```r methods(print) ## all the different ways to print... ``` ``` ## [1] print.acf* ## [2] print.AES* ## [3] print.anova* ## [4] print.aov* ## [5] print.aovlist* ## [6] print.ar* ## [7] print.Arima* ## [8] print.arima0* ## [9] print.AsIs ## [10] print.aspell* ## [11] print.aspell_inspect_context* ## [12] print.bibentry* ## [13] print.Bibtex* ## [14] print.browseVignettes* ## [15] print.bslib_fragment* ## [16] print.bslib_page* ## [17] print.by ## [18] print.cachem* ## [19] print.changedFiles* ## [20] print.check_bogus_return* ## [21] print.check_code_usage_in_package* ## [22] print.check_compiled_code* ## [23] print.check_demo_index* ## [24] print.check_depdef* ## [25] print.check_details* ## [26] print.check_details_changes* ## [27] print.check_doi_db* ## [28] print.check_dotInternal* ## [29] print.check_make_vars* ## [30] print.check_nonAPI_calls* ## [31] print.check_package_code_assign_to_globalenv* ## [32] print.check_package_code_attach* ## [33] print.check_package_code_data_into_globalenv* ## [34] print.check_package_code_startup_functions* ## [35] print.check_package_code_syntax* ## [36] print.check_package_code_unload_functions* ## [37] print.check_package_compact_datasets* ## [38] print.check_package_CRAN_incoming* ## [39] print.check_package_datalist* ## [40] print.check_package_datasets* ## [41] print.check_package_depends* ## [42] print.check_package_description* ## [43] print.check_package_description_encoding* ## [44] print.check_package_license* ## [45] print.check_packages_in_dir* ## [46] print.check_packages_used* ## [47] print.check_po_files* ## [48] print.check_pragmas* ## [49] print.check_Rd_line_widths* ## [50] print.check_Rd_metadata* ## [51] print.check_Rd_xrefs* ## [52] print.check_RegSym_calls* ## [53] print.check_S3_methods_needing_delayed_registration* ## [54] print.check_so_symbols* ## [55] print.check_T_and_F* ## [56] print.check_url_db* ## [57] print.check_vignette_index* ## [58] print.checkDocFiles* ## [59] print.checkDocStyle* ## [60] print.checkFF* ## [61] print.checkRd* ## [62] print.checkRdContents* ## [63] print.checkReplaceFuns* ## [64] print.checkS3methods* ## [65] print.checkTnF* ## [66] print.checkVignettes* ## [67] print.citation* ## [68] print.cli_ansi_html_style* ## [69] print.cli_ansi_string* ## [70] print.cli_ansi_style* ## [71] print.cli_boxx* ## [72] print.cli_diff_chr* ## [73] print.cli_doc* ## [74] print.cli_progress_demo* ## [75] print.cli_rule* ## [76] print.cli_sitrep* ## [77] print.cli_spark* ## [78] print.cli_spinner* ## [79] print.cli_tree* ## [80] print.codoc* ## [81] print.codocClasses* ## [82] print.codocData* ## [83] print.colorConverter* ## [84] print.compactPDF* ## [85] print.condition ## [86] print.connection ## [87] print.CRAN_package_reverse_dependencies_and_views* ## [88] print.css* ## [89] print.data.frame ## [90] print.Date ## [91] print.default ## [92] print.dendrogram* ## [93] print.density* ## [94] print.difftime ## [95] print.dist* ## [96] print.Dlist ## [97] print.DLLInfo ## [98] print.DLLInfoList ## [99] print.DLLRegisteredRoutines ## [100] print.document_context* ## [101] print.document_position* ## [102] print.document_range* ## [103] print.document_selection* ## [104] print.dummy_coef* ## [105] print.dummy_coef_list* ## [106] print.ecdf* ## [107] print.eigen ## [108] print.factanal* ## [109] print.factor ## [110] print.family* ## [111] print.fileSnapshot* ## [112] print.findLineNumResult* ## [113] print.formula* ## [114] print.fseq* ## [115] print.ftable* ## [116] print.function ## [117] print.getAnywhere* ## [118] print.glm* ## [119] print.glue* ## [120] print.hashtab* ## [121] print.hclust* ## [122] print.help_files_with_topic* ## [123] print.hexmode ## [124] print.HoltWinters* ## [125] print.hsearch* ## [126] print.hsearch_db* ## [127] print.htest* ## [128] print.html* ## [129] print.html_dependency* ## [130] print.htmltools.selector* ## [131] print.htmltools.selector.list* ## [132] print.infl* ## [133] print.integrate* ## [134] print.isoreg* ## [135] print.json* ## [136] print.key_missing* ## [137] print.kmeans* ## [138] print.knitr_kable* ## [139] print.Latex* ## [140] print.LaTeX* ## [141] print.libraryIQR ## [142] print.lifecycle_warnings* ## [143] print.listof ## [144] print.lm* ## [145] print.loadings* ## [146] print.loess* ## [147] print.logLik* ## [148] print.ls_str* ## [149] print.medpolish* ## [150] print.MethodsFunction* ## [151] print.mtable* ## [152] print.NativeRoutineList ## [153] print.news_db* ## [154] print.nls* ## [155] print.noquote ## [156] print.numeric_version ## [157] print.object_size* ## [158] print.octmode ## [159] print.packageDescription* ## [160] print.packageInfo ## [161] print.packageIQR* ## [162] print.packageStatus* ## [163] print.pairwise.htest* ## [164] print.person* ## [165] print.POSIXct ## [166] print.POSIXlt ## [167] print.power.htest* ## [168] print.ppr* ## [169] print.prcomp* ## [170] print.princomp* ## [171] print.proc_time ## [172] print.quosure* ## [173] print.quosures* ## [174] print.R6* ## [175] print.R6ClassGenerator* ## [176] print.raster* ## [177] print.Rd* ## [178] print.recordedplot* ## [179] print.restart ## [180] print.RGBcolorConverter* ## [181] print.rlang_box_done* ## [182] print.rlang_box_splice* ## [183] print.rlang_data_pronoun* ## [184] print.rlang_dict* ## [185] print.rlang_dyn_array* ## [186] print.rlang_envs* ## [187] print.rlang_error* ## [188] print.rlang_fake_data_pronoun* ## [189] print.rlang_lambda_function* ## [190] print.rlang_message* ## [191] print.rlang_trace* ## [192] print.rlang_warning* ## [193] print.rlang_zap* ## [194] print.rlang:::list_of_conditions* ## [195] print.rle ## [196] print.rlib_bytes* ## [197] print.rlib_error_3_0* ## [198] print.rlib_trace_3_0* ## [199] print.roman* ## [200] print.sass* ## [201] print.sass_bundle* ## [202] print.sass_layer* ## [203] print.scalar* ## [204] print.sessionInfo* ## [205] print.shiny.tag* ## [206] print.shiny.tag.env* ## [207] print.shiny.tag.list* ## [208] print.shiny.tag.query* ## [209] print.simple.list ## [210] print.smooth.spline* ## [211] print.socket* ## [212] print.srcfile ## [213] print.srcref ## [214] print.stepfun* ## [215] print.stl* ## [216] print.stringr_view* ## [217] print.StructTS* ## [218] print.subdir_tests* ## [219] print.summarize_CRAN_check_status* ## [220] print.summary.aov* ## [221] print.summary.aovlist* ## [222] print.summary.ecdf* ## [223] print.summary.glm* ## [224] print.summary.lm* ## [225] print.summary.loess* ## [226] print.summary.manova* ## [227] print.summary.nls* ## [228] print.summary.packageStatus* ## [229] print.summary.ppr* ## [230] print.summary.prcomp* ## [231] print.summary.princomp* ## [232] print.summary.table ## [233] print.summary.warnings ## [234] print.summaryDefault ## [235] print.table ## [236] print.tables_aov* ## [237] print.terms* ## [238] print.ts* ## [239] print.tskernel* ## [240] print.TukeyHSD* ## [241] print.tukeyline* ## [242] print.tukeysmooth* ## [243] print.undoc* ## [244] print.vctrs_bytes* ## [245] print.vctrs_sclr* ## [246] print.vctrs_unspecified* ## [247] print.vctrs_vctr* ## [248] print.vignette* ## [249] print.warnings ## [250] print.xfun_raw_string* ## [251] print.xfun_rename_seq* ## [252] print.xfun_strict_list* ## [253] print.xgettext* ## [254] print.xngettext* ## [255] print.xtabs* ## see '?methods' for accessing help and source code ``` --- ## Examining objects - `ls`, `class`, `str` ```r a <- "test" df <- data.frame(numbers = 1:3, abc = letters[1:3]) ls() ## list objects in global environemtn ``` ``` ## [1] "a" "df" "l" "l1" "l2" "m1" "m2" "v1" "v2" "v3" "v4" ``` ```r class(df) ## class ``` ``` ## [1] "data.frame" ``` ```r str(df) ## structure ``` ``` ## 'data.frame': 3 obs. of 2 variables: ## $ numbers: int 1 2 3 ## $ abc : chr "a" "b" "c" ``` --- ## Sampling - Sample without replacement ```r set.seed(1234) ## set a random seed v <- 1:100 s <- sample(v, 50) print(s) ``` ``` ## [1] 28 80 22 9 5 38 16 4 86 90 70 79 78 14 56 62 93 84 21 40 92 67 96 66 47 ## [26] 81 48 3 41 32 42 43 2 54 49 99 51 6 77 29 71 85 57 8 26 17 58 91 60 76 ``` ```r print(sort(s)) ``` ``` ## [1] 2 3 4 5 6 8 9 14 16 17 21 22 26 28 29 32 38 40 41 42 43 47 48 49 51 ## [26] 54 56 57 58 60 62 66 67 70 71 76 77 78 79 80 81 84 85 86 90 91 92 93 96 99 ``` - Sample with replacement ```r s2 <- sample(v, 50, replace = T) print(sort(s2)) ``` ``` ## [1] 2 3 3 6 6 9 10 17 17 19 20 22 22 25 27 28 30 32 35 36 41 41 48 50 51 ## [26] 55 57 58 58 60 61 63 63 65 66 70 70 71 72 76 77 80 83 85 85 86 86 87 90 96 ``` --- ## Does S2 have duplicates? ```r s2_unique <- unique(s2) print(length(s2)) ``` ``` ## [1] 50 ``` ```r print(length(s2_unique)) ``` ``` ## [1] 40 ``` ```r print(length(s2) == length(s2_unique)) ``` ``` ## [1] FALSE ``` --- ## In-class exercise - Create the following: - `a`: a random vector of integers with 10 elements drawn from 1-20: - Use the `sample` function with `set.seed(10)` - Name the elements of `a` with a vector of names starting with "V1" and ending with "V10". - Use the `paste0` function to create those names. - Create the identical vector of names using the `paste` function. - `b`: Using `a` as an index to select from `letters` - `d`: Use `rnorm` with a mean = 100 and an sd of 20 - Use `?rnorm` to see the help guide. - Why did I skip `c`? - Create a list `l` from `a`, `b`, `d`. - Assign the names of the vectors in `l` to the `l`'s elements --- ## 2-d structures - Create the following: - `m`: a matrix with three integer columns named "V1", "V2", "V3" - Create each column first as its own vector, then combine - `V1` = 1:10 - `V2` is a random sample between 1:100 - `V3` is drawn from a random uniform distribution between 0 and 50 - Use the same `set.seed(50)` as before - Inspect the `str` and `class` of `m` - `dat`, a data.frame built from `V1`, `V2`, `V3`, and `V4` - `V4` is a random selection of the letters A-E --- ## Functions ### Components ```r function_name <- function(arg1, arg2 = 1:10, arg3 = ifelse(arg2 == 2, TRUE, FALSE)) { body } ``` Three components of a function: - `formals()`: arguments - `body()`, the code, which returns the last object generated, unless specified with `return(x)`. - `environment()`, function finds the values Unnamed functions are **anonymous** functions. (Used in `*apply`) --- Using `x` in a function does not change its global value. ```r x <- 1:10 myfun <- function() { x * 10 } myfun() ``` ``` ## [1] 10 20 30 40 50 60 70 80 90 100 ``` ```r myfun <- function(x) { x <- x * 10 return(x) } x <- 10 myfun(x = 20) ``` ``` ## [1] 200 ``` ```r x ``` ``` ## [1] 10 ``` --- Each time you run `myfun`, a new function environment is created. ```r myfun <- function(x) { x <- x * 10 print(environment()) return(x) } myfun(x) ``` ``` ## <environment: 0x7fa06b10f088> ``` ``` ## [1] 100 ``` ```r myfun(x) ``` ``` ## <environment: 0x7fa04a12f358> ``` ``` ## [1] 100 ``` --- ## Global assignment. Use `<<-` to change value of global variable within a function. ```r a <- 10 myfun <- function(x) { a <<- x * 10 ## note <<- instead of <- return(a) } myfun(5) ``` ``` ## [1] 50 ``` ```r print(a) ``` ``` ## [1] 50 ``` --- ## Useful functions - `which` finds indices where a condition is true. ```r v <- 10:15 print(v) ``` ``` ## [1] 10 11 12 13 14 15 ``` ```r a <- which(v %% 3 == 0) ## subset to elements divisible by 3 print(a) ## shows indices where condition is true. ``` ``` ## [1] 3 6 ``` --- ## Useful functions - `which.min` finds index of min value ```r v <- sample(1:20, 10) print(v) ``` ``` ## [1] 16 11 4 3 10 18 5 20 6 12 ``` ```r print(which.min(v)) # index of min value ``` ``` ## [1] 4 ``` ```r print(which.max(v)) # index of max value ``` ``` ## [1] 8 ``` --- ## data.frame vs data.table vs. tibble - all 2D structures. - data.frame = Base R - tibble = `tidyverse` - data.table = fast. For now, we'll stick to data.frame --- ## data.frame indexing - `data.frame` uses the following to subset: `[*row conditions, *column conditions]` ```r df <- data.frame(v1 = 1:5, v2 = 6:10) rownames(df) <- LETTERS[1:5] print(df) ``` ``` ## v1 v2 ## A 1 6 ## B 2 7 ## C 3 8 ## D 4 9 ## E 5 10 ``` --- ## data.frame indexing - Index using names. - Empty index `[ , 'v2]` means "keep all rows" ```r df[,'v2'] ## column indexing ``` ``` ## [1] 6 7 8 9 10 ``` ```r df[c("A", "B", "D"), ] ## row indexing ``` ``` ## v1 v2 ## A 1 6 ## B 2 7 ## D 4 9 ``` --- ## data.frame subset - Logical subset ```r df[df$v1 > 3, ] ## get observations (rows) where first column is larger than 3 ``` ``` ## v1 v2 ## D 4 9 ## E 5 10 ``` --- ## Control structures ### Branching - Pay attention to `{ }` placement ```r a <- 5 if(a > 10) { print("Greater than 10!") } else { print("Less than or equal to 10") } ``` ``` ## [1] "Less than or equal to 10" ``` --- ### Looping ```r b <- 1:3 for(i in b) print(i) ``` ``` ## [1] 1 ## [1] 2 ## [1] 3 ``` ```r b <- 1:5 a <- 2 for(i in b){ a <- 2 * a print(a) } ``` ``` ## [1] 4 ## [1] 8 ## [1] 16 ## [1] 32 ## [1] 64 ``` --- ### *apply - A special form of looping - Intended for *applying* a function to data. Uses *anonymous* function. - 3 main kinds: `sapply`, `lapply`, `apply` --- ### `sapply` - `sapply` iterates over input and returns a vector. ```r v <- 1:10 sapply(v, function(x) x + 10) ## adds 10 to each element in v. ``` ``` ## [1] 11 12 13 14 15 16 17 18 19 20 ``` Use `{ }` for more complicated functions. BUT be careful with order of `{ }`, `( )` ```r v1 <- 1:10 v2 <- sapply(v1, function(x){ y <- x^2 return(y) }) # print(v2) ``` ``` ## [1] 1 4 9 16 25 36 49 64 81 100 ``` --- ### `sapply` If you don't specify `return`, the last object created will be returned. ```r v1 <- 1:10 v2 <- sapply(v1, function(x){ y <- x^2 ## y will be returned }) # print(v2) ``` ``` ## [1] 1 4 9 16 25 36 49 64 81 100 ``` --- ### `lapply` - Similar to `sapply`, except final object is returned as `list`. - Useful if you need to store more complex objects (data.frame, plot, raster etc.) ```r v1 <- 1:10 v2 <- lapply(v1, function(x){ y <- x^2 ## y will be returned }) # print(v2) ``` ``` ## [[1]] ## [1] 1 ## ## [[2]] ## [1] 4 ## ## [[3]] ## [1] 9 ## ## [[4]] ## [1] 16 ## ## [[5]] ## [1] 25 ## ## [[6]] ## [1] 36 ## ## [[7]] ## [1] 49 ## ## [[8]] ## [1] 64 ## ## [[9]] ## [1] 81 ## ## [[10]] ## [1] 100 ``` --- ### `apply` - `apply` works well for 2D data, when you want to apply function over a row or column. ```r v1 <- sample(1:100, 10) v2 <- sample(1:100, 10) DF <- data.frame(v1, v2) ## data frame columns will take names of vectors DF ``` ``` ## v1 v2 ## 1 9 38 ## 2 43 88 ## 3 19 30 ## 4 22 70 ## 5 46 94 ## 6 40 19 ## 7 29 79 ## 8 57 86 ## 9 16 14 ## 10 66 23 ``` --- Use `apply` to get column max value. The index 2 means "apply function to columns". ```r colMax <- apply(DF, 2, FUN = max) colMax ``` ``` ## v1 v2 ## 66 94 ``` --- Use `apply` to get row max value. The index 1 means "apply function to rows". ```r rowMax <- apply(DF, 1, FUN = max) rowMax ``` ``` ## [1] 38 88 30 70 94 40 79 86 16 66 ``` We can use `apply` or `sapply` to create a new column in a data frame. ```r DF$rowMax <- apply(DF, 1, FUN = max) DF ``` ``` ## v1 v2 rowMax ## 1 9 38 38 ## 2 43 88 88 ## 3 19 30 30 ## 4 22 70 70 ## 5 46 94 94 ## 6 40 19 40 ## 7 29 79 79 ## 8 57 86 86 ## 9 16 14 16 ## 10 66 23 66 ```