0

Problem

Using targets package in r, we make functions and targets obejcts in _targets.R file. In a function, I want to calculate something not just based on the input obejects but also based on the objects in the global environment. However, seems that it doesn't work in this way.

What I have done

I will give two example, showing how we write code without and with targets package. The first one runs without problem, but the second code doesn't work due to the problem I mentioned above.

The first example. Let's start from a empty project folder. I make a new R script, and this is how we do generally without using targets package.

# a function to sum 2 values then minus 1
fun_sum_1 <- function(x, y) {
  res <- sum(x, y) - 1
  return(res)
}
my_x <- 2
my_y <- 3
my_res <- fun_sum_1(my_x, my_y)
my_res
# [1] 4

Then I do almost the same thing using targets package. Firstly, make a new R script and name it "_targets.R". Write the following code in this _targets.R and save the file.

library(targets)

# a function to sum 2 values then minus 1
fun_sum_1 <- function(x, y) {
  res <- sum(x, y) - 1
  return(res)
}

# targets list
list(
  tar_target(my_x, 2), 
  tar_target(my_y, 3), 
  tar_target(my_res, fun_sum_2(my_x, my_y))
)

Then we run the _targets.R in console pannel with the following code.

library(targets)
tar_make()

You will see the progress of the runing. The difference between this and general practice above is that there is not "my_x" or "my_y" or "my_res" or "fun_sum_1" showing up in the environment pannel. Then we load the results to our global environment by runing the following code in the console pannel.

tar_load(my_res)
my_res
# [1] 4

And if we run tar_visnetwork(), we can see a flowchart of the target obejcts.

Then we go to the second example, the example with problem. First, we write code as usual.

# clean the global environment first
rm(list = ls())

# a function to sum 2 values and 1 existing value of global environment then minus 1
# it differs from fun_sum_2 that it doesn't only rely on input obejects
# but also the "my_z" in the global environment
fun_sum_2 <- function(x, y) {
  res <- sum(x, y, my_z) - 1
  return(res)
}
my_x <- 2
my_y <- 3
my_z <- 4
my_res <- fun_sum_2(my_x, my_y)
my_res
# [1] 8

Now we try to make this using targets package. We revise the _targets.R into the code below, and save the file.

library(targets)

# a function to sum 2 values then minus 1
fun_sum_2 <- function(x, y) {
  res <- sum(x, y, my_z) - 1
  return(res)
}

# targets list
list(
  tar_target(my_x, 2), 
  tar_target(my_y, 3), 
  tar_target(my_z, 4), 
  tar_target(my_res, fun_sum_2(my_x, my_y))
)

Then we run the following code in console pannel. But then error happens.

library(targets)
tar_make()
# ✔ skip target my_x
# ✔ skip target my_y
# ✔ skip target my_z
# • start target my_res
# ✖ error target my_res
# • end pipeline [0.095 seconds]
# Error:
#   ! Error running targets::tar_make()
# Error messages: targets::tar_meta(fields = error, complete_only = TRUE)
# Debugging guide: https://books.ropensci.org/targets/debugging.html
# How to ask for help: https://books.ropensci.org/targets/help.html
# Last error: object 'my_z' not found

Though I make a my_z in the targets list, but seems that fun_sum_2 can't read it. Of course, I can make my_z an input into the fun_sum_2 function, which will solve the problem. However, what if I have a function relying on many objects in the global environment?

I wonder how can I solve this problem without making my_z one of the inputs for the fun_sum_2 function.

Liang Zhang
  • 753
  • 7
  • 20
Kang
  • 31
  • 3

1 Answers1

1

This is related to function scope or environment. You should know that your function fun_sum_2() is defined in the global environment, within which you cannot find an object named my_z. To make it work, you should move your definition of my_z to the global blocks, too. The following codes should work:

targets::tar_dir({
  targets::tar_script({
    library(targets)
    
    # a function to sum 2 values then minus 1
    fun_sum_2 <- function(x, y) {
      res <- sum(x, y, my_z) - 1
      return(res)
    }
    my_z <- 4
    
    # targets list
    list(
      tar_target(my_x, 2), 
      tar_target(my_y, 3), 
      tar_target(my_res, fun_sum_2(my_x, my_y))
    )
  })
  targets::tar_make()
})
#> ▶ start target my_x
#> ● built target my_x [0 seconds]
#> ▶ start target my_y
#> ● built target my_y [0.015 seconds]
#> ▶ start target my_res
#> ● built target my_res [0 seconds]
#> ▶ end pipeline [0.062 seconds]

Created on 2023-07-11 with reprex v2.0.2

For more about environment, you can read this part of the book written by Hadley.

Liang Zhang
  • 753
  • 7
  • 20