I would like to define an arbitrary function of an arbitrary number of variables, for example, for 2 variables:
func2 <- function(time, temp) time + temp
I'd like to keep variable names that have a meaning in the problem (time and temperature above).
If I have a vector of values for these variables, for example in the 2-d case, c(10, 121), I'd like to apply my function (func2 here) and obtain the result. Conceptually, something like,
func2(c(10,121))
becomes
func2(10,121)
Is there a simple way to accomplish this, for an arbitrary number of variables?
Thanks
You could write a helper function to turn a simple vector into parameters with the help of do.call
splat <- function(f,v) {
do.call(f, as.list(v))
}
splat(func2, c(10,121))
Is this what you are looking for?
f = function(f,...) {
v = list(...)
Reduce(f, v)
}
> f(f = "+", x = pi, y = exp(1), z = pi^2)
15.72948
Related
I am trying to analyse a dataframe using hierarchical clustering hclust function in R.
I would like to pass in a vector of p values I'll write beforehand (maybe something like c(5/4, 3/2, 7/4, 9/4)) and be able to have these specified as the different p value options with Minkowski distance when I use expand.grid. Ideally, when hyperparams is viewed, it would also be clear which value of p has been used for each minkowski, i.e. they should be labelled. So for example, where (if you run my code for hyperparams) there would currently just be one minkowski under Dists, for each of the methods in Meths, there would be, if I supplied the p vector as c(5/4, 3/2, 7/4, 9/4), now instead 4 rows for Minkowski distance: minkowski, p=5/4, minkowski, p=3/2, minkowski, p=7/4, minkowski, p=9/4 (or looking something like that, making the p values clear). Any ideas?
(Note: no packages please, only base R!)
Edit: I worded it poorly before, now rewritten. Let's take the following example instead:
acc <- function(x){
first = sum(x)
second = sum(x^2)
return(list(First=first,Second=second))
}
iris0 <- iris
iris1 <- cbind(log(iris[,1:4]),iris[5])
iris2 <- cbind(sqrt(iris[,1:4]),iris[5])
Now the important bit:
tests <- expand.grid(Dists=c("euclidean","maximum","manhattan","canberra","binary"),
DS=c("iris0","iris1","iris2"))
Table <- Map(function(x, ds){acc(table(ds$Species, cutree(hclust(dist(get(ds)[,1:4], method=x)),3)))},tests[[1]], tests[[2]])
This will work. But now if I want to include a term like "minkowski",p=3 in expand.grid, how would I do it?
tests <- expand.grid(Dists=c("euclidean","maximum","manhattan","canberra","binary","minkowski,p=3"),
DS=c("iris0","iris1","iris2"))
Table <- Map(function(x, ds){acc(table(ds$Species, cutree(hclust(dist(get(ds)[,1:4], method=x)),3)))},tests[[1]], tests[[2]])
This gives an error.
In reality there should be no p argument unless the method="minkowski". I have tried to use strsplit to get the first part of the expression into ds, and a switch with strsplit to get the second part and then use parse (it would return NULL if the length of the strsplit was not 2 -- this should pass no argument, I think). The issue seems to be that strsplit is not strsplit(x,",") fails to evaluate the vectorized x but rather tries to evaluate the character x which is not a string. Can anyone suggest any workaround/fix or other method for including the minkowski,p=1.6 terms and the like?
We can create a 'p' value column
tests <- expand.grid(Dists=c("euclidean","maximum","manhattan","canberra","binary",
"minkowski3", "minkowski4", "minkowski5"),
DS=c("iris0","iris1","iris2"))
Suppose, we have another column of 'p' values in 'tests', the above solution can be changed to
tests$p <- as.list(args(dist))$p # default value
i1 <- grepl("minkowski", tests$Dists)
tests$Dists <- sub("[0-9.]+$", "", tests$Dists)
tests$p[i1] <- rep(3:5, length.out = sum(i1))
Map(function(x, ds, p){
dist1 <- dist(get(ds)[, 1:4], method = x, p = p)
ct <- cutree(hclust(dist1), 3)
acc(table(get(ds)$Species, ct))},
as.character(tests[[1]]), as.character(tests[[2]]), tests$p )
I want to make a function in terms of x and coef for multiple values of x so that the output is a vector, like I've tried here:
directpoly<-function(x,coef) {
for(n in length(coef)) {
total<-sum(coef*x^(0:(n-1)))
}
total
}
This works when I input one value for x and any vector for the coefficient values, but I want more than that. I want to input a certain amount of values for the coefficients, say c(5,9,-2), and have the function produce three different values, one for each input of x for, say, x<-2:4. So in that case I'd want output 15, 14, 9. Any ideas? I am new so all help is appreciated.
Edit: I took out an "<-" that I accidentally put in there. Sorry if that was any cause for confusion. Also what I want in the end is a function
P(x) = c1 + c2*x + ... + cn*x^n-1
Does this work?
directpoly <- function(x, coef) {
seqcoef <- seq_along(coef) - 1
sapply(x, function(z) sum(coef*z^seqcoef))
}
directpoly(2:4, c(5,9,-2))
# [1] 15 14 9
If so, the trick to solving this is two-steps:
Determine what you want to do with each value of x (no vector). In this case, it's simply from among:
sum(coef*x^(1:length(coef)-1))
sum(coef*x^(0:(length(coef)-1)))
sum(coef*x^(seq_along(coef)-1))
Because I'm eventually putting this into some loop/apply formulation, I don't need to recalculate the sequence each time, so I break it out:
seqcoef <- seq_along(coef) - 1
sum(coef*x^seqcoef)
Now that you know what to do with each x`, now map or apply over it:
sapply(x, function(z) ...)
where ... is what we determined above. For clear coding, many believe the technique of hard-defining this function is good, so something like:
directpoly1 <- function(x, coef, seqcoef = seq_along(coef) - 1) {
sum(coef*x^seqcoef)
}
directpoly <- function(x, coef) {
seqcoef <- seq_along(coef) - 1
sapply(x, directpoly1, coef, seqcoef)
}
(I took a little more liberty with this version to enable running it explicitly with a scalar argument, primarily for unit-testing. It is not strictly necessary, so the function at the top of this answer should suffice.)
I have written a simple function to calculate the average power of measurements in dBm. First I had to write the function to convert dBm to watts, find the average and the convert the value back to dBm.
This works great for a single vector
For example:
MeanDB <- function(dBVector) {
# Returns the variance for all the input data. First converts data to linear scale. Then mean() is applied.
# Then Data are converted back to log scale
return(10*log10(mean(10^(dBVector/10))))
}
Now I would like to apply the same function for elements of two vectors for example the vector1 and vector2.
I would like to call my written function for each pair of elements from vector 1 and vector 2 (these are of same size).
The easiest will be ofc a for loop
keepResults<-vector()
for i in seq(1,length(vector1)){
keepResults<-MeanDb(vector1[i],vector2[i])
}
but I am quite sure there should be in R a more efficient alternatives. Can you provide commands in R that can do that in a shorter way?
Regards and thanks
Alex
There is no need for *apply loops. Simply write a vectorized function:
MeanDB <- function(...) {
stopifnot(length(unique(lengths(list(...)))) == 1L)
M <- cbind(...)
return(10 * log10(rowMeans(10 ^ (M / 10))))
}
a = c(1, 2, 3)
b = c(2, 3, 4)
MeanDB(a, b)
This generalizes to an arbitrary number of vectors.
For just two lists, you could rewrite your function as:
MeanDB <- function(number1,number2) {
return(10*log10(mean(10^(c(number1,number2)/10))))
}
# Example of implementation:
a = list(1,2,3)
b = list(2,3,4)
mapply(MeanDB,a,b)
Hope this helps!
You can also use pmap (general case) or map2 (in this case) from the purrr package. If I used the function defined by #F.Maas above then,
MeanDB <- function(number1,number2) {
return(10*log10(mean(10^(c(number1,number2)/10))))
}
pmap_dbl(list(list(1,2,3),list(4,5,6)),MeanDB)
I have looked everywhere for this answer but I am having a hard time even figuring our how to ask this question. I am trying to create a function such that it creates a vector that is a function of two other vectors, where I use a for loop to index values at k and k+1. Here is an example of my code, which does not work:
x <- 1:10
y <- x^2
d <- data.frame(x,y)
invSlope <- NULL
invSlope.f <- function(X,Y){
for(k in 1:length(X)-1){
invSlope[k] = (X[k+1] - X[k])/ (Y[k+1] - Y[k])
invSlope[length(X)] = 0
return(invSlope)
}
}
d$invSlope <- invSlope.f(d$x,d$y)
What I am trying to accomplish is at d$invSlope[1] I have the inverse of the slope of the line that comes after it (delta x/delta y). The last value of the vector would just be 0. I can accomplish this with a for loop (or even nested for loops), but I would like to generalize this to a function.
Thanks
The diff function is a vectorized approach... we don't need no steenkin' loops:
finvslope <- function(xseq, yseq) { c( diff(xseq)/diff(yseq) , 0) }
I am writing a function where in one or more of the arguments are vectors generated by a loop within the function.
For ex:
myfunc<-function(rep, n, arm1, arm2)
{
for(i in 1:rep)
{
x<-rnorm(n,0,4)
y<-rnorm(n,0,5)
res[i]<-t.test(arm1,arm2)
}
return(res)
}
Now I would like to call the function as
myfunc(rep = 10, n=10, arm1 = x, arm2 = x) or
myfunc(rep = 10, n=10, arm1=x,arm2 = y)
The idea is compare different arms.
Hope I have stated my problem clearly.
Your help is highly appreciated.
Let's see if I got it...
You want run rep tests, each with two vetors of n normal random variates. And you want to be able to change the arguments... Frankly, this is not the best way to program.. BUT, I will try to help you.
First things first: you can't assign to an arbitrary position of the result res before creating the variable. So I'll add res <- list() to your code. Also t.test returns more information, so it must be appended to a list object, with double square brackets.
Now, for the arguments, you must make R understand that arm are symbol arguments, to be evaluated inside the function's environment. So you must capture it's expression using substitute and pass it to eval function:
myfunc<-function(rep, n, arm1, arm2)
{
res <- list() ###
for(i in 1:rep)
{
x<-rnorm(n,0,4)
y<-rnorm(n,0,5)
res[[i]]<-t.test(eval(substitute(arm1)),eval(substitute(arm2))) ###
}
return(res)
}
Try it...
A better way to do this is as follows:
newfunc <- function(rep, n, sd1, sd2)
{
lapply(1:rep, function(.) t.test(rnorm(n,0,sd1), rnorm(n,0,sd2)))
}
Now sd1 and sd2 are the standard deviations parameters.