Extracting objects defined within a function in R - r

I'm writing a function in R and I want to be able to call different objects from the function. I've got simple example of the problem I'm talking about (not the real code obviously).
example <- function(a,b){
c <- a+b
d <- a*b
e <- a/b
e
}
a <- 10
b <- 20
output <- example(a,b)
str(output)
output$c
My goal is for the last line to show the value of c defined in the function. In this code the only thing saved in output is the returned value, e.
I've tried changing the local and global environments, using <<- etc. That doesn't solve the problem though. Any help would be appreciated.

We can return multiple output in a list and then extract the list element
example <- function(a,b){
c <- a+b
d <- a*b
e <- a/b
list(c=c, d= d, e = e)
}
a <- 10
b <- 20
output <- example(a,b)[['c']]
output
#[1] 30
example(a,b)[['d']]
#[1] 200

Related

Writing a function in R to solve Plank's equation

I am trying to write my first function in R to calculate emittance using Plank's function for different temperatures. I can do it manually as below for temperatures from 200 to 310 K.
pi <- 3.141593
h <- 6.626068963e-34
c <- 2.99792458e+8
lambda <- 4 * 1e-6
k <- 1.38e-23
t <- c (200:310)
a <- (2*pi*(c^2)*h)/(lambda^5)
b <- exp((h*c)/(lambda*k*t))
B <- a * (1/(b-1))
Where B is the vector of values I want.
Now here is an effort to write a function in R:
P_function <- function(t, pi = 3.141593, h = 6.626068963e-34, c = 2.99792458e+8,
lambda = 4 * 1e-6, k = 1.38e-2) {
((2*pi*(c^2)*h)/(lambda^5)) *((1/(exp((h*c)/(lambda*k*t))-1)))
}
Now for different values of t (200-300K), how do I implement this function?
Couple of problems. First, pi is already a defined constant at better precision than you are using.
> rm(pi) # remove your copy
> pi
[1] 3.141593 # default for console printing is only 8 digits
> print(pi, digits=18)
[1] 3.14159265358979312 # but there is more "depth" to be had
Second, it makes no sense to put scientific constants in the parameter list. Since they're constant they can be defined in the body. Parameter lists are for items that might vary from situation to situation.
newPfun <- function(t) { h <- 6.626068963e-34
c <- 2.99792458e+8
lambda <- 4 * 1e-6
k <- 1.38e-23
a <- (2*pi*(c^2)*h)/(lambda^5) #pi is already defined
b <- exp((h*c)/(lambda*k*t))
B <- a * (1/(b-1))
return(B) }
This is just your original code "packaged" to accept a vector of temperatures. (And I'm pretty sure that's not the right spelling the scientist's name.)
Not sure where your second function is flawed. Perhaps a mismatched parenthesis. After trying to duplicate the results with a single expression and failing multiple times, I'm now wondering if it's really a problem with numerical overflow (or underflow).

How to rename an object created within a function each run?

I made a function that generates a data frame. Since I want to store my data frame, I saved it in the global environment. I want to run the function again, but with new parameters and avoid overwriting my previous data frames. Basically, I want to rename my data frame each time I run my function.
fun <- function(x, y) {
a <- x*1000
b <- a + pi
c <- a + b
return(data_frame <- data.frame(a, b, c))
}
Thanks!
Here's one solution
fun <- function(x, y, name) {
a <- x*1000
b <- a + pi
c <- a + b
assign(deparse(substitute(name)),data.frame(a, b, c), envir=.GlobalEnv)}
fun(1,2,df.name)
df.name
This returns:
a b c
1 1000 1003.1 2003.1

making list of lists in R

Why in R
e=list(a,b,c,d)
is different than:
e=list(a,b)
e=list(e,c)
e=list(e,d)
?
The second approach can be easily used in a for loop, but it produces different result, and I create 1 object each iteration, so cant use first approach, any hints ?
If you absolutely want to use this approach, you can do this:
# Make up some data
a <- 1:3; b <- 4:5; c <- 6:10; d <- 11:17
# Build up the lists
e0 <- list(a, b, c, d)
e <- list(a, b)
e <- c(e, list(c))
e <- c(e, list(d))
# Compare the two
identical(e0, e) # TRUE
In a real-life case, however, instead of using a loop, you probably would be better off using function from the *apply family, such as lapply(), which will return a list of outputs directly.

How do I vectorize the ecdf function in R?

I have a data frame that looks like this:
set.seed(42)
data <- runif(1000)
utility <- sample(c("abc","bcd","cde","def"),1000,replace=TRUE)
stage <- sample(c("vwx","wxy","xyz"),1000,replace=TRUE)
x <- data.frame(data,utility,stage)
head(x)
data utility stage
1 0.9148060 def xyz
2 0.9370754 abc wxy
3 0.2861395 def xyz
4 0.8304476 cde xyz
5 0.6417455 bcd xyz
6 0.5190959 abc xyz
and I want to generate cumulative distribution functions for the unique combinations of utility and stage. In my real application I'll end up generating about 100 cdfs but this random data will have 12 (4x3) unique combinations. But I'll be using each of those cdfs thousands of times, so I don't want to calculate the cdf on the fly each time. The ecdf() function works exactly as I'd like, except I'd need to vectorize it. The following code doesn't work, but it's the gist of what I'm trying to do:
ecdf_multiple <- function(x)
{
i=0
utilities <- levels(x$utilities)
stages <- levels(x$stages)
for(utility in utilities)
{
for(stage in stages)
{
i <- i + 1
y <- ecdf(x[x$utilities == utility & x$stage == stage,1])
# calculate ecdf for the unique util/stage combo
z[i] <- list(y,utility,stage)
# then assign it to a data element (list, data frame, json, whatever) note-this doesn't actually work
}
}
z # return value
}
so after running ecdf_multiple and assigning it to a variable, I'd reference that variable somehow by passing a value (for which I wanted the cdf), the utility and the stage.
Is there a way to vectorize the ecdf function (or use/build another) so that I can the output several times without neededing to generate distributions over and over?
-------Added to respond to #Pascal 's excellent suggestion.-------
How might one expand this to a more general case of taking "n" dimensions of categories? This is my stab, based on Pascal's case of two dimensions. Notice how I tried to assign "y":
set.seed(42)
data <- runif(1000)
utility <- sample(c("abc","bcd","cde","def"),1000,replace=TRUE)
stage <- sample(c("vwx","wxy","xyz"),1000,replace=TRUE)
openclose <- sample(c("open","close"),1000,replace=TRUE)
x <- data.frame(data,utility,stage,openclose)
numlabels <- length(names(x))-1
y <- split(x, list(x[,2:(numlabels+1)]))
l <- lapply(y,function(x) ecdf(x[,"data"]))
#execute
utility <- "abc"
stage <- "xyz"
openclose <- "close"
comb <- paste(utility, stage, openclose, sep = ".")
# call the function
l[[comb]](.25)
During the assignment of "y" above, I get this error message:
"Error in sort.list(y) : 'x' must be atomic for 'sort.list'
Have you called 'sort' on a list?"
The following might help:
# we create a list of criteria by excluding
# the first column of the data.frame
y <- split(x, as.list(x[,-1]))
l <- lapply(y, function(x) ecdf(x[,"data"]))
utility <- "abc"
stage <- "xyz"
comb <- paste(utility, stage, sep = ".")
l[[comb]](0.25)
# [1] 0.2613636
plot(l[[comb]])

Multiple Condition in R code

Suppose, I have a data set like,
y <- c(0,0,1,2,2,1,0,1)
a <- c(0,2,1,1,0,2,0,3)
dat <- data.frame(y, a)
I want to calculate the value of f <- digamma(a+y) by using the condition
if(a>0 & y==0) then f
if(a==0 & y>0) then f
if (a==0 & y==0) then f = 1
How can I do it using R code?
I think this will give you what you're after post hoc:
f <- digamma(a+y)
f[a==0 & y==0] <- 1
or
f[is.nan(f)] <- 1
Well this is using indexing to see which elements in the vector are nan or in the first method both a and y are 0. Then the index [] says take these elements of f and the assignment <- says make these elements a one.

Resources