3

I'm using the {targets} package, trying to create new targets based on existing targets. Whereas it's simple to refer to an existing target by typing its name in NSE-style, I fail to do the same by using a character string as "alias".

Just to be clear about what I'm talking about. I'll show what I mean outside the {targets}-land first.
If we create a data object such as:

my_vec <- 1:10

We can get it by either typing its name without quotes:

my_vec
#>  [1]  1  2  3  4  5  6  7  8  9 10

or as a string using get():

get("my_vec")
#>  [1]  1  2  3  4  5  6  7  8  9 10

And if, for whatever reason, we want to use an "alias" to my_vec when we call it, one way (out of many) could be:

get_object_from_alias <- function(alias) {
  switch(alias,
         "the_1_to_10_vector" = get("my_vec"))
}

get_object_from_alias("the_1_to_10_vector")
#>  [1]  1  2  3  4  5  6  7  8  9 10

So just to sum up this little intro: I can call the same object directly by its name:

  • my_vec; or
  • get("my_vec")

Or indirectly using its alias "the_1_to_10_vector" via get_object_from_alias().

In {targets} context

Here's a reproducible example. The bottom line that clearly conveys my question is at the end.

1. To keep things clean, let's create a new R project and load Rstudio.

library(usethis)
usethis::create_project(path = "my_reproducible_project", open = TRUE, rstudio = TRUE)

2. Now we should be inside a new RStudio window, part of my_reproducible_project .Rproj.

# install.packages("targets")
library(targets)

3. Open a new R markdown file.

4. The following chunks set up the file and create the "targets".

---
title: "Target Markdown"
output: html_document
---
```{r setup, include = FALSE}
knitr::opts_chunk$set(tar_interactive = FALSE, collapse = TRUE)
```
# Setup
```{r}
library(targets)
tar_unscript()
```

# Targets
```{targets raw-data}
library(tibble)
library(magrittr)
tar_target(raw_data, airquality %>% tibble::rowid_to_column("id"))
```

```{targets processed-data}
library(dplyr)
list(
  tar_target(filtered_data_ozone, raw_data %>% filter(!is.na(Ozone))),
  tar_target(filtered_data_solar_r , raw_data %>% filter(!is.na(Solar.R)))
)
```

# Pipeline
```{r}
tar_make()
```

5. At this point, if we run the code above, it should create 3 targets:

  • raw_data
  • filtered_data_ozone
  • filtered_data_solar_r

They are not in the environment, but rather saved as .rds files in the project's directory. We can bring them to the current environment using tar_read(). Without assigning them to objects it will simply print them to the console:

# Read the `filtered_data_*` targets
```{r}
tar_read(filtered_data_solar_r)
tar_read(filtered_data_ozone)
```

So far so good! Here comes my problem
I now want to create a new target by referencing to filtered_data_* targets indirectly (like I did using "the_1_to_10_vector" alias in the beginning of this post).

Whereas the direct method works no problem:

```{targets join-them-directly-by-target-name}
library(dplyr)
tar_target(joined_filtered, inner_join(filtered_data_solar_r, filtered_data_ozone))
```

```{r}
tar_make() 
# will now create a new target called `joined_filtered` which we can bring using `tar_read()`.
```

But using "aliases" to filtered_data_solar_r and filtered_data_ozone won't work!

If we define a utility function for swapping aliases:

swap_alias_for_real_target_name <- function(alias) {
  
  switch(alias,
         #aliases               # targets actual names 
         "ozone_without_na"   = "filtered_data_ozone",
         "solar_r_without_na" = "filtered_data_solar_r")
}

swap_alias_for_real_target_name("ozone_without_na")
#> [1] "filtered_data_ozone"
swap_alias_for_real_target_name("solar_r_without_na")
#> [1] "filtered_data_solar_r"

It won't work when we use it inside tar_target(), even when converting the string to symbol.

```{targets join-them-by-utility-func-full-example}
swap_alias_for_real_target_name = function(alias) {
  
  switch(alias,
         #aliases               # targets actual names 
         "ozone_without_na"   = "filtered_data_ozone",
         "solar_r_without_na" = "filtered_data_solar_r")
}

tar_target(joined_filtered_full_example, inner_join(as.symbol(swap_alias_for_real_target_name("ozone_without_na")),
                                                    as.symbol(swap_alias_for_real_target_name("solar_r_without_na"))
                                                    )
           )
```

```{r}
tar_make(joined_filtered_full_example)
```

Error: callr subprocess failed: no applicable method for 'inner_join' applied to an object of class "name"

Emman
  • 3,695
  • 2
  • 20
  • 44
  • `targets` detects dependency relationships using static code analysis. That means it scans your code for global variables, and if those global variables are other targets (or functions you defined) then `targets` will connect them in the graph. Strings are not detected this way, it's just a limitation of how the package works. The details are explained at https://books.ropensci.org/targets/targets.html#dependencies. – landau Oct 21 '21 at 14:20
  • @landau thanks. So the only way I can think of, to do what I want, is to assign a variable in the global environment with the alias name, e.g.: `ozone_without_na <- tar_read(filtered_data_ozone)`. Which has 2 shortcomings; (1) It forces us to create a copy of a (potentially) large data object, and bring it from behind the scenes (being a target) to the front, just for a computation. (2) In case we have many targets we seek to replace by alias name by iterating over names, we're unable to utilize a programmatic solution. Is there really no workaround to this? – Emman Oct 21 '21 at 14:58
  • I guess I am having trouble understanding the need for aliases. Why is it important? – landau Oct 21 '21 at 15:20
  • @landau, Yes I didn't discuss the real-life problem at all because I didn't want to go too niche with this. I'll try to create a good example and post a separate question. – Emman Oct 21 '21 at 15:29
  • @landau -- I've finally finished writing the question with an example to this problem: https://stackoverflow.com/q/69691481/6105259 – Emman Oct 23 '21 at 19:46

0 Answers0