In this lab, we will learn how to get information about environments and the search path.

Goal: by the end of this lab, you should be able to understand how loading packages affects the search path.

The search path

Understanding the search path is crucial to understanding how R looks for the values that are bound to names. When you start a new R session, the search path contains only those packages that are loaded by default.

search()
##  [1] ".GlobalEnv"        "package:htmltools" "package:knitr"    
##  [4] "package:forcats"   "package:stringr"   "package:dplyr"    
##  [7] "package:purrr"     "package:readr"     "package:tidyr"    
## [10] "package:tibble"    "package:ggplot2"   "package:tidyverse"
## [13] "package:stats"     "package:graphics"  "package:grDevices"
## [16] "package:utils"     "package:datasets"  "package:methods"  
## [19] "Autoloads"         "package:base"

Note that if we use a function from another package using the :: operator, the package is loaded, but it is not added to the search path (i.e., attached).

rlang::search_envs()
##  [[1]] $ <env: global>
##  [[2]] $ <env: package:htmltools>
##  [[3]] $ <env: package:knitr>
##  [[4]] $ <env: package:forcats>
##  [[5]] $ <env: package:stringr>
##  [[6]] $ <env: package:dplyr>
##  [[7]] $ <env: package:purrr>
##  [[8]] $ <env: package:readr>
##  [[9]] $ <env: package:tidyr>
## [[10]] $ <env: package:tibble>
## [[11]] $ <env: package:ggplot2>
## [[12]] $ <env: package:tidyverse>
## [[13]] $ <env: package:stats>
## [[14]] $ <env: package:graphics>
## [[15]] $ <env: package:grDevices>
## [[16]] $ <env: package:utils>
## [[17]] $ <env: package:datasets>
## [[18]] $ <env: package:methods>
## [[19]] $ <env: Autoloads>
## [[20]] $ <env: package:base>
  1. How can you tell from the previous result that the rlang package is not part of the search path?

To add a package to the search path, use the library() command.

library(rlang)
## 
## Attaching package: 'rlang'
## The following objects are masked from 'package:purrr':
## 
##     %@%, as_function, flatten, flatten_chr, flatten_dbl, flatten_int,
##     flatten_lgl, flatten_raw, invoke, splice
search()
##  [1] ".GlobalEnv"        "package:rlang"     "package:htmltools"
##  [4] "package:knitr"     "package:forcats"   "package:stringr"  
##  [7] "package:dplyr"     "package:purrr"     "package:readr"    
## [10] "package:tidyr"     "package:tibble"    "package:ggplot2"  
## [13] "package:tidyverse" "package:stats"     "package:graphics" 
## [16] "package:grDevices" "package:utils"     "package:datasets" 
## [19] "package:methods"   "Autoloads"         "package:base"

The tidyverse is a kind of meta-package that loads several other packages. Note the order in which the new packages are loaded.

library(tidyverse)
search()
##  [1] ".GlobalEnv"        "package:rlang"     "package:htmltools"
##  [4] "package:knitr"     "package:forcats"   "package:stringr"  
##  [7] "package:dplyr"     "package:purrr"     "package:readr"    
## [10] "package:tidyr"     "package:tibble"    "package:ggplot2"  
## [13] "package:tidyverse" "package:stats"     "package:graphics" 
## [16] "package:grDevices" "package:utils"     "package:datasets" 
## [19] "package:methods"   "Autoloads"         "package:base"
  1. How many different packages did the tidyverse add to the search path? Why do you think the developers chose the order they did?

Your environment

You can find out what environment you are in with current_env().

current_env()
## <environment: R_GlobalEnv>

To see what is in an environment, use env_print().

env_print()
## <environment: global>
## Parent: <environment: package:rlang>
## Bindings:
## • projects: <tibble[,2]>
## • x: <int>
## • li: <fn>
## • mpg_by_year: <list<tibble[,11]>>
## • mods: <list>
## • starwars: <tibble[,15]>
## • fib_df: <tibble[,3]>
## • fibonacci: <fn>
## • posted: <lgl>
## • .Random.seed: <int>
## • sw2: <tibble[,3]>
## • i: <int>
## • unique_values_safe: <fn>
## • fib: <dbl>
## • unique_values: <fn>
## • talks: <tibble[,2]>

Note that while the current environment is usually the global environment, that is is not always the case.

global_env()
## <environment: R_GlobalEnv>

Let’s write a function that returns the environment that runs during its execution. Note that this is not the global environment.

func_env <- function() {
  x <- "what?"
  current_env()
}

env_print(func_env())
## <environment: 0x7fe574fea558>
## Parent: <environment: global>
## Bindings:
## • x: <chr>
  1. What is the parent environment of the execution environment shown above?

Name masking

Let’s now change the global environment by writing a function called filter(). This function will just pass its argument to dplyr::filter() after printing a message to the screen.

filter <- function(.data, ...) {
  message(paste("Filtering a", first(class(.data)), "object with", nrow(.data), "rows..."))
  dplyr::filter(.data, ...)
}
  1. Use the new filter() function to find all the human characters in starwars. Does it work? Why or why not?
# SAMPLE SOLUTION

starwars %>%
  filter(species == "Human")
## Filtering a tbl_df object with 87 rows...

Note that our new function filter() is now in the global environment, which is first in the search path.

env_has(global_env(), "filter")
## filter 
##   TRUE
search()
##  [1] ".GlobalEnv"        "package:rlang"     "package:htmltools"
##  [4] "package:knitr"     "package:forcats"   "package:stringr"  
##  [7] "package:dplyr"     "package:purrr"     "package:readr"    
## [10] "package:tidyr"     "package:tibble"    "package:ggplot2"  
## [13] "package:tidyverse" "package:stats"     "package:graphics" 
## [16] "package:grDevices" "package:utils"     "package:datasets" 
## [19] "package:methods"   "Autoloads"         "package:base"

But there are other environments that contain objects called filter. We can use map() to search through the chain of environments.

search_envs() %>%
  map_lgl(env_has, "filter")
##            global     package:rlang package:htmltools     package:knitr 
##              TRUE             FALSE             FALSE             FALSE 
##   package:forcats   package:stringr     package:dplyr     package:purrr 
##             FALSE             FALSE              TRUE             FALSE 
##     package:readr     package:tidyr    package:tibble   package:ggplot2 
##             FALSE             FALSE             FALSE             FALSE 
## package:tidyverse     package:stats  package:graphics package:grDevices 
##             FALSE              TRUE             FALSE             FALSE 
##     package:utils  package:datasets   package:methods         Autoloads 
##             FALSE             FALSE             FALSE             FALSE 
##      package:base 
##             FALSE

We can also use find() to show us directly which environments contain an object that is bound to the name filter.

find("filter")
## [1] ".GlobalEnv"    "package:dplyr" "package:stats"
  1. How can we use the filter() function from the stats package?
# SAMPLE SOLUTION

stats::filter()

Engagement

Prompt: If you could clarify one thing about environments, what would it be?