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)
Related
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
I am normally a maple user currently working with R, and I have a problem with correctly indexing variables.
Say I want to define 2 vectors, v1 and v2, and I want to call the nth element in v1. In maple this is easily done:
v[1]:=some vector,
and the nth element is then called by the command
v[1][n].
How can this be done in R? The actual problem is as follows:
I have a sequence M (say of length 10, indexed by k) of simulated negbin variables. For each of these simulated variables I want to construct a vector X of length M[k] with entries given by some formula. So I should end up with 10 different vectors, each of different length. My incorrect code looks like this
sims<-10
M<-rnegbin(sims, eks_2016_kasko*exp(-2.17173), 840.1746)
for(k in 1:sims){
x[k]<-rep(NA,M[k])
X[k]<-rep(NA,M[k])
for(i in 1:M[k]){x[k][i]<-runif(1,min=0,max=1)
if(x[k][i]>=0 & x[i]<=0.1056379){
X[k][i]<-rlnorm(1, 6.228244, 0.3565041)}
else{
X[k][i]<-rlnorm(1, 8.910837, 1.1890874)
}
}
}
The error appears to be that x[k] is not a valid name for a variable. Any way to make this work?
Thanks a lot :)
I've edited your R script slightly to get it working and make it reproducible. To do this I had to assume that eks_2016_kasko was an integer value of 10.
require(MASS)
sims<-10
# Because you R is not zero indexed add one
M<-rnegbin(sims, 10*exp(-2.17173), 840.1746) + 1
# Create a list
x <- list()
X <- list()
for(k in 1:sims){
x[[k]]<-rep(NA,M[k])
X[[k]]<-rep(NA,M[k])
for(i in 1:M[k]){
x[[k]][i]<-runif(1,min=0,max=1)
if(x[[k]][i]>=0 & x[[k]][i]<=0.1056379){
X[[k]][i]<-rlnorm(1, 6.228244, 0.3565041)}
else{
X[[k]][i]<-rlnorm(1, 8.910837, 1.1890874)
}
}
This will work and I think is what you were trying to do, BUT is not great R code. I strongly recommend using the lapply family instead of for loops, learning to use data.table and parallelisation if you need to get things to scale. Additionally if you want to read more about indexing in R and subsetting Hadley Wickham has a comprehensive break down here.
Hope this helps!
Let me start with a few remarks and then show you, how your problem can be solved using R.
In R, there is most of the time no need to use a for loop in order to assign several values to a vector. So, for example, to fill a vector of length 100 with uniformly distributed random variables, you do something like:
set.seed(1234)
x1 <- rep(NA, 100)
for (i in 1:100) {
x1[i] <- runif(1, 0, 1)
}
(set.seed() is used to set the random seed, such that you get the same result each time.) It is much simpler (and also much faster) to do this instead:
x2 <- runif(100, 0, 1)
identical(x1, x2)
## [1] TRUE
As you see, results are identical.
The reason that x[k]<-rep(NA,M[k]) does not work is that indeed x[k] is not a valid variable name in R. [ is used for indexing, so x[k] extracts the element k from a vector x. Since you try to assign a vector of length larger than 1 to a single element, you get an error. What you probably want to use is a list, as you will see in the example below.
So here comes the code that I would use instead of what you proposed in your post. Note that I am not sure that I correctly understood what you intend to do, so I will also describe below what the code does. Let me know if this fits your intentions.
# define M
library(MASS)
eks_2016_kasko <- 486689.1
sims<-10
M<-rnegbin(sims, eks_2016_kasko*exp(-2.17173), 840.1746)
# define the function that calculates X for a single value from M
calculate_X <- function(m) {
x <- runif(m, min=0,max=1)
X <- ifelse(x > 0.1056379, rlnorm(m, 6.228244, 0.3565041),
rlnorm(m, 8.910837, 1.1890874))
}
# apply that function to each element of M
X <- lapply(M, calculate_X)
As you can see, there are no loops in that solution. I'll start to explain at the end:
lapply is used to apply a function (calculate_X) to each element of a list or vector (here it is the vector M). It returns a list. So, you can get, e.g. the third of the vectors with X[[3]] (note that [[ is used to extract elements from a list). And the contents of X[[3]] will be the result of calculate_X(M[3]).
The function calculate_X() does the following: It creates a vector of m uniformly distributed random values (remember that m runs over the elements of M) and stores that in x. Then it creates a vector X that contains log normally distributed random variables. The parameters of the distribution depend on the value x.
I'm trying to formulate the code below using the lapply function (or actually the mclapply function) instead of the apply function. I want it to return a matrix or similar and not a list. The hi function is very complicated in my actual code, so I just presented a very basic example.
hi <- function(a, matrix) {
hi <- a[1] / a[2] * t(matrix) %*% matrix
return(hi)
}
a_1 <- t(matrix(1:4))
a_2 <- t(matrix(5:8))
choleski <- matrix(1:4)
result <- apply(rbind(a_1, a_2), 2, hi, matrix=choleski)
So my question is basically, how do I reformulate the code above using lapply instead of apply, i.e. apply the lapply function to the hi function instead of using the apply procedure. An efficient solution would be awesome.
Thanks.
If you aim to apply hi to each column of rbind(a_1, a_2), then you could do something like this:
A <- rbind(a_1, a_2)
sapply(seq_len(ncol(A)), function(i) hi(A[, i], choleski))
or, more simply (if it's ok to make rbind(a_1, a_2) a data.frame):
A <- as.data.frame(rbind(a_1, a_2))
sapply(A, function(x) hi(x, choleski))
The latter works since a data.frame is a type of list (where columns are its elements), and sapply applies function to list elements. By default, sapply simplifies its output if possible.
I'm new to R programming and I know I could write a loop to do this, but everything I read says that for simplicity its best to avoid loops and use apply instead.
I have a matrix and i would like to run this function on each element in the matrix.
cellresidue <- function(i,j){
result <- (cluster[i,j] - cluster.I[i,] - cluster.J[j,] - cluster.IJ)/(cluster.N*cluster.M)
return (result)
}
i= element row
j= element column
cluster.J is a matrix of column means
cluster.I is a matrix of row means
cluster.IJ is the mean of the entire matrix named cluster
What I can't figure out is how do I get the row and column of the element (I think should use row() and column col() functions) that mapply is working with and how do pass those arguments to mapply or apply?
There is no need for loops or *apply functions. You can just use plain matrix operations:
nI <- nrows(cluster)
nJ <- ncols(cluster)
cluster.I <- matrix(rowMeans(cluster), nI, nJ, byrow = FALSE)
cluster.J <- matrix(rowMeans(cluster), nI, nJ, byrow = TRUE)
cluster.IJ <- matrix( mean(cluster), nI, nJ)
residue.mat <- (cluster - cluster.I - cluster.J - cluster.IJ) /
(cluster.N * cluster.M)
(You did not explain what cluster.N and cluster.M are but I assume they are scalars)
It is not clear from your question what you are trying to do. It is best on this site to provide some mock data (preferably generated by the code, not pasted), and then show what form the end result should look like. It seems that the apply family is not what you seek.
Quick disambiguation between apply, sapply and mapply:
#providing data for examples
X=matrix(rnorm(9),3,3)
apply: apply a function to either columns (2) or rows (1) of a matrix or array
#here, sum by columns, same as colSums(X)
apply(X, 2, sum)
sapply: apply a function against (usually) a list of objects
#create a list with three vectors
mylist=list(1:4, 5:10, c(1,1,1))
#get the mean of each vector
sapply(mylist, mean)
#remove 2 to each element of X, same as c(X-2)
sapply(X, FUN=function(x) x-2)
mapply: a multivariate version of sapply, taking an arbitrary number of arguments. Never had much use of it… Some rock-bottom examples:
#same as c(1,2,3,4) + c(15,16,17,18)
mapply(sum, 1:4, 15:18)
#same as c(X+X), the vectorized matrix sum
mapply(sum, X, X)
Side note: It's perfectly ok to use loops in R; use whichever suits the best your thoughts. The issue is that if you have a "really big" number of iterations, this is where you could meet bottlenecks, depending on your patience. There are two solutions to this: rewrite your function in C/FORTRAN (and boost speed), or use built-in functions if applicable (which are, by the way, often writen in C or FORTRAN).
I have a list of means, for example
avgs = c(1,2,3)
and a function:
simulate <- function (avg)
{ rnorm(n=10,m=avg,sd=1) }
What is the best way to get a vector of 30 values, rather than a multidimensional array from
sapply(avgs,simulate)?
In your case, just take advantage of the fact that rnorm is vectorized and thus will accept entire vectors as arguments:
rnorm(30, avgs, 1)
You can also remove dimensions from your matrix with c:
c(sapply(avgs, simulate))
but this approach is slower and less direct.