0

I'm stuck and can't for the heck of it figure out what I'm missing. I'm sure it's something minor, but I just really don't understand why my code isn't working.

myfunction <- function( X ) {

plot(X, type="l")
max_B <- max(X$B)   
max_A <- X$A[ X$B  == max_B ]
results<-c(max_A, max_B)
points(max_A, max_B, col="red")  

} 

myfunction(dat)

It's plotting just fine, it's adding the point at max_A/max_B (so it's actually getting information for max_A and max_B, but it's not creating the object results. If I do every line within the function separately, it works, but it just doesn't work as a function and I have no idea why.

moodymudskipper
  • 46,417
  • 11
  • 121
  • 167
Anke
  • 527
  • 1
  • 6
  • 19
  • related: https://stackoverflow.com/questions/7537273/how-does-r-handle-object-in-function-call – jogo Feb 15 '18 at 07:38

3 Answers3

3

you might need to return the object.

myfunction <- function( X ) {
  plot(X, type="l")
  max_B <- max(X$B)   
  max_A <- X$A[ X$B  == max_B ]
  results<-c(max_A, max_B)
  points(max_A, max_B, col="red")  
  return(results)
} 

results <- myfunction(dat)

In case this doesn't solve the problem, please provide date for dat by providing the result of dput(dat) in your question.

You don't need always the return in the function. The default setting of functions is to return the last saved object or last instructuion. In your case, the points function has no or invisible return or at least not the one you expect. I think for clean code practice it is always good to define the return value, even if it is one more line to write.

drmariod
  • 11,106
  • 16
  • 64
  • 110
  • Thanks, that did the trick. I could have sworn I had tried return and it didn't help. I'm still learning and tried many different approaches, none of which worked. So I guess I just hadn't found the right combination with saving to the object AND returning. Edit: Actually, it didn't work. It prints to screen, but still doesn't create the object – Anke Feb 15 '18 at 07:26
  • I added an additional comment, maybe this explains it a little. – drmariod Feb 15 '18 at 07:30
  • I'm not sure what you mean by "put(dat)". If I try that, I'm getting an unknown function error. Unfortunately, adding the return function doesn't help. It's still not creating an object, only printing the results to the console. – Anke Feb 15 '18 at 07:37
  • should have ben `dput`, my auto correct is a bit aggressive :-) Please provide some data to your question using this `dput` or if it has to many lines, please use `dput(head(dat))`. – drmariod Feb 15 '18 at 07:53
  • 1
    It's not necessarily returning the last assigned object, but the output of the last instruction. You could for instance end you function with `1+1` and `2` would be returned – moodymudskipper Feb 15 '18 at 07:56
  • If you're returning local variable `results` from `myfunction` and expecting `results` to be in your global environment, you need to assign the result from `myfunction` to `results` explicitly when calling `myfunction`. – ytu Feb 15 '18 at 08:22
  • I just tried initializing the objects and then it works. So I guess the problem really was that I was creating local objects that I didn't have access to, globally. However, I was taught that there is no need to initialize in R and that the alternative of using "<<-" is a really bad solution (as per comment below), so I'm wondering what the "good coding" solution is. Using "return" does print the results to console, but does not pass on the objects, as mentioned above – Anke Feb 16 '18 at 00:35
  • Anke, I updated my function already yesterday. So if you use `results <- myfunction(dat)` instead of `myfunction(dat)` the result gets saved in `results` instead of printing it to the console. – drmariod Feb 16 '18 at 06:03
0

I wanted to provide some feedback in case someone else comes across this. The right answer was actually posted by drmariod, but I missed the most important part. I not only have to return my object, but actually save the returned output into a new object when executing the function. Which it says right there - I just totally overlooked that part.

myfunction<-function()
{...
return(object)}

results<-myfunction(dat)

Thank you again for your help.

Anke
  • 527
  • 1
  • 6
  • 19
-1

Push the assignment of results one more level above to your global environment should solve your problem.

myfunction <- function( X ) {

  plot(X, type="l")
  max_B <- max(X$B)   
  max_A <- X$A[ X$B  == max_B ]
  results <<- c(max_A, max_B)
  points(max_A, max_B, col="red")  

} 

myfunction(dat)

For more information of <<-, check How do you use “<<-” (scoping assignment) in R?, and also the Environment chapter in Advanced R written by Hadley Wickham. You'll learn more about how R scopes variables across environments.

Why isn't the normal return working?

We may return local variables out to Global Environment, but remember that returns only the values of returned local variables, but not the local variables themselves.

For example, if we create a function simply adds one to a given number:

add1 <- function(x) {
  y <- x + 1
  return(y)
  # return(x + 1) will also do but I save it as y for better demonstration
}

add1(1) gives us 2 on the screen, but doesn't give us a variable called "y" in Global Environment. If we really want an y out there, we have to save the results from add(1) and name it as "y", i.e. y <- add1(1). Instead of showing "2" on the screen, now it is saved as y.

The trick of <<- is pushing the assignment to its parent environment. In this answer, results <<- c(max_A, max_B) takes place in myfunction, whose parent environment happens to be Global Environment, so we'll find results after executing myfunction.

Why can I use the objects for to plot the point, but not to save the very same objects into a new object?

The objects max_A and max_B are effective locally in myfunction, so you can do whatever you want within that scope. In your original codes, results was indeed saved but still existed only in myfunction environment.

One thing worth mentioning is that, unlike most other functions, plot and points returns no object but graphics, so they are not limited by the environment scopes. You can try x <- 1:10; y <- x + rnorm(10); myplot <- plot(x, y), and you'll see a plot but myplot is empty.

ytu
  • 1,822
  • 3
  • 19
  • 42
  • I think this is a really bad solution and should only be used in worst cases, since you might overwrite values you don't expect to overwrite. – drmariod Feb 15 '18 at 08:15
  • @drmariod you can be right but since OP is expecting the object `results` as `c(max_A, max_B)`, I think it is the simplest solution. – ytu Feb 15 '18 at 08:17
  • This actually worked. Thank you. I'd still like to understand why the normal return isn't working, though. Or rather, why can I use the objects for to plot the point, but not to save the very same objects into a new object. I feel like I'm missing something – Anke Feb 15 '18 at 23:54