I have a function called all.priors (see R code below). My goal is to get the x and y from the curve() call inside the for loop, and save these xs and ys as object h.
(I want to have 101 rows, and 2*length(d) columns in h. This way, each 2 columns, contain x and y from a curve() run in the for loop.)
Question:
how can I correctly save the xs and ys from the curve() call? [I get the error: incorrect number of subscripts on matrix]
all.priors = function(a, b, lo, hi, d, Bi = 55, n = 1e2){
h = matrix(NA, 101, 2*length(d))
for(i in 1:length(d)){
p = function(x) get(d[i])(x, a, b)
prior = function(x) p(x)/integrate(p, lo, hi)[[1]]
likelihood = function(x) dbinom(Bi, n, x)
posterior = function(x) prior(x)*likelihood(x)
h[i,] = curve(posterior, ty = "n", ann = FALSE, yaxt = "n", xaxt = "n", add = i!= 1, bty = "n")
}
}
#Example of use:
all.priors(lo = 0, hi = 1, a = 2, b = 3, d = c("dgamma", "dnorm", "dcauchy", "dlogis"))
You just need to carefully place the values in the matrix, and then return the matrix from your function. try this
all.priors = function(a, b, lo, hi, d, Bi = 55, n = 1e2){
h = matrix(NA, 101, 2*length(d))
for(i in 1:length(d)){
p = function(x) get(d[i])(x, a, b)
prior = function(x) p(x)/integrate(p, lo, hi)[[1]]
likelihood = function(x) dbinom(Bi, n, x)
posterior = function(x) prior(x)*likelihood(x)
cv <- curve(posterior, ty = "n", ann = FALSE, yaxt = "n", xaxt = "n", add = i!= 1, bty = "n")
h[,i*2-1] <- cv$x
h[,i*2] <- cv$y
}
h
}
all.priors(lo = 0, hi = 1, a = 2, b = 3, d = c("dgamma", "dnorm", "dcauchy", "dlogis"))
A different way to solve this might be to save the answers in a list rather than a matrix. I think that your function complicates the picture of what is going on, so I will use a simpler example.
h = list()
for(i in 1:5) {
h[i] = list(curve(sin(i*x), xlim=c(0,6.3))) }
The resulting data structure should be easy to use.
Related
I have an R function that plots some curves stacked on top of each other (see picture below the code).
I was wondering, however, how I could adjust the distance between the curves (see blue arrows) so that the curves don't look so flat?
Also, is it possible to use polygon instead of lines to plot these curves?
stacked = function(a, b, lo, hi, d, Bi = 55, n = 1e2){
h = list()
for(i in 1:length(d)){
p = function(x) get(d[i])(x, a, b)
prior = function(x) p(x)/integrate(p, lo, hi)[[1]]
likelihood = function(x) dbinom(Bi, n, x)
posterior = function(x) prior(x)*likelihood(x)
h[i] = list(curve(posterior, ty = "n", ann = FALSE, yaxt = "n", xaxt = "n", add = i!= 1, bty = "n", n = 1e3))
}
plot(matrix(c(rep(c(.4, .8), each = length(d))), length(d), 2), rep(1:length(d), 2), ty = "n", xlim = 0:1, ann = FALSE, xaxt = "n", ylim = c(1, length(d)+.4))
for(i in 1:length(d)){
lines(h[[i]]$x, h[[i]]$y+i, ty = "l", xpd = NA, col = i)
}
}
# Example of use:
stacked(lo = 0, hi = 1, a = 2, b = 3, d = c("dgamma", "dnorm", "dcauchy", "dbeta", "dlogis", "dweibull"))
You can just scale the y-values before drawing the lines. I have added a scale parameter to your function. Try scale =2 or 3
stacked = function(a, b, lo, hi, d, Bi = 55, n = 1e2, scale=1){
h = list()
for(i in 1:length(d)) {
p = function(x) get(d[i])(x, a, b)
prior = function(x) p(x)/integrate(p, lo, hi)[[1]]
likelihood = function(x) dbinom(Bi, n, x)
posterior = function(x) prior(x)*likelihood(x)
h[i] = list(curve(posterior, ty = "n", ann = FALSE, yaxt = "n", xaxt = "n", add = i!= 1, bty = "n", n = 1e3))
}
plot(matrix(c(rep(c(.4, .8), each = length(d))), length(d), 2), rep(1:length(d), 2), ty = "n", xlim = 0:1, ann = FALSE, xaxt = "n", ylim = c(1, length(d)+.4))
for(i in 1:length(d)){
lines(h[[i]]$x, scale*h[[i]]$y+i, ty = "l", xpd = NA, col = i)
}
}
stacked(lo = 0, hi = 1, a = 2, b = 3, scale=2,
d = c("dgamma", "dnorm", "dcauchy", "dbeta", "dlogis", "dweibull"))
If you wish to fill in the area under the curves, you can use polygon. Change the lines:
for(i in 1:length(d)){
lines(h[[i]]$x, scale*h[[i]]$y+i, ty = "l", xpd = NA, col = i)
}
TO
for(i in 1:length(d)){
polygon(x=h[[i]]$x, y=scale*h[[i]]$y+i, col = i)
}
I have a vectorized R function (see below). At each run, the function plots two histograms. My goal is that when argument n is a vector (see example of use below), the function plots length of n separate sets of these histograms (ex: if n is a vector of length 2, I expected two sets of histograms i.e., 4 individual histograms)?
I have tried the following with no success. Is there a way to do this?
t.sim = Vectorize(function(n, es, n.sim){
d = numeric(n.sim)
p = numeric(n.sim)
for(i in 1:n.sim){
N = sqrt((n^2)/(2*n))
x = rnorm(n, es, 1)
y = rnorm(n, 0, 1)
a = t.test(x, y, var.equal = TRUE)
d[i] = a[[1]]/N
p[i] = a[[3]]
}
par(mfcol = c(2, length(n)))
hist(p) ; hist(d)
}, "n")
# Example of use:
t.sim(n = c(30, 300), es = .1, n.sim = 1e3) # `n` is a vector of `2` so I expect
# 4 histograms in my graphical device
Vectorize seems to be based on mapply, which would essentially call the function numerous times while cycle through your inputs vector. Hence, the easier way out probably just calls it outside the function
t.sim = Vectorize(function(n, es, n.sim){
d = numeric(n.sim)
p = numeric(n.sim)
for(i in 1:n.sim){
N = sqrt((n^2)/(2*n))
x = rnorm(n, es, 1)
y = rnorm(n, 0, 1)
a = t.test(x, y, var.equal = TRUE)
d[i] = a[[1]]/N
p[i] = a[[3]]
}
# par(mfcol = c(2, npar))
hist(p) ; hist(d)
}, "n")
#inputs
data <- c(30,300)
par(mfcol = c(2, length(data)))
t.sim(n = data, es = c(.1), n.sim = 1e3)
In my R function below, I'm wondering how I can change my code such that I can get pe out of my fun function? Right now, fun only outputs L and U.
P.S. Of course, I want to keep the function work as it does right now, so therefore replicate may also need to change as a result of having fun output pe in addition to L and U.
CI.bi = function(n, p, n.sim){
fun <- function(n1 = n, p1 = p){
x <- rbinom(1, size = n1, prob = p1)
pe <- x/n1
res <- binom.test(x, n1, p1)[[4]]
c(L = res[1], U = res[2])
}
sim <- t(replicate(n.sim, fun()))
y = unlist(lapply(1:n.sim, function(x) c(x, x)))
plot(sim, y, ty = "n", ylab = NA, yaxt = "n")
segments(sim[ ,1], 1:n.sim, sim[ ,2], 1:n.sim, lend = 1)
}
# Example of use:
CI.bi(n = 15, p = .5, n.sim = 3)
You can have fun() return pe as an additional element of the return vector.
When referencing sim later on, just specify which columns you want to use. I believe the below code sample replicates your current functionality but has pe as an additional output of fun()
CI.bi = function(n, p, n.sim){
fun <- function(n1 = n, p1 = p){
x <- rbinom(1, size = n1, prob = p1)
pe <- x/n1
res <- binom.test(x, n1, p1)[[4]]
c(L = res[1], U = res[2], pe=pe)
}
sim <- t(replicate(n.sim, fun()))
y = unlist(lapply(1:n.sim, function(x) c(x, x)))
plot(sim[,1:2], y, ty = "n", ylab = NA, yaxt = "n")
segments(sim[ ,1], 1:n.sim, sim[ ,2], 1:n.sim, lend = 1)
}
CI.bi(n = 15, p = .5, n.sim = 3)
This question already has answers here:
How to use R's ellipsis feature when writing your own function?
(5 answers)
Split up `...` arguments and distribute to multiple functions
(4 answers)
Closed 6 years ago.
Is there a way to pass arbitrary arguments to more than one command inside a function? The following function clearly does not work but I hope it explains what I am trying to achieve.
test = function(x = rnorm(20), y = rnorm(20), ..., ---){
plot(x, y, type = "p", ...)
lines(x, y, ---)
}
The goal is to be able to write a function that creates plot with say lines and points and polygon and can take arbitrary arguments for each command and pass them to the respective commands without me having to explicitly specify arguments for each command.
Here is a hackish approach:
.. <- "/////" #or anything which won't be used as a valid parameter
f <- function(...){
arguments <- list(...)
if(.. %in% arguments){
i <- which(arguments == ..)
terms <- unlist(arguments[1:(i-1)])
factors <- unlist(arguments[(i+1):length(arguments)])
c(sum(terms),prod(factors))
}
}
Then, for example,
> f(2,3,4,..,7,8,10)
[1] 9 560
You could obviously extend the idea to multiple ... fields, each delimited with ..
OPTION 1
Function
test = function(x = rnorm(20), y = rnorm(20), plot_options = NA, ...){
if (is.na(plot_options) == FALSE){
eval(parse(text = paste0("plot(x, y, ", plot_options, ")")))
} else {
plot(x, y, type = "n")
}
lines(x, y, ...)
}
USAGE
test()
set.seed(42)
m = rnorm(20)
n = rnorm(20)
test(x = m, y = n,
plot_options = "type = 'p', col = 'red', pch = 19, xlab = 'Test Plot', ylab = 'Y-axis'")
OPTION 2 (#Gregor's Solution)
Function
test2 = function(x = rnorm(20), y = rnorm(20), ..., line_options){
plot(x, y, ...)
if (missing(line_options)) {
lines(x, y)
} else {
do.call(lines, c(list(x = x, y = y), line_options))
}
}
USAGE
par(mfrow = c(2, 2), mar = c(2, 2, 1, 1))
test2(main = 'default')
test2(line_options = list(lty = 2), main = 'line')
test2(col = 'red', main = 'plot')
test2(col = 'red', line_options = list(lty = 2, col = 'blue'), main = 'line and plot')
I need to compute the efficient frontier with different risk measure and to use a bootstrapping technique to simulate possible outcome. However, now I'm stuck: what I want to do is to generate via a loop (which will be integrated later into a function) multiple efficient frontier, each one associated to a possible future outcome, and to plot them on the same figure in such a way to see how they may change as the simulation goes on. Here is the loop that I wrote so far:
for (i in 1:B) {
idx <- sample(1:N, N, replace = TRUE)
new.x <- x[idx, ]
µ.b <- apply(X = new.x, 2, FUN = mean)
range.b[, i] <- seq(from = min(µ.b), to = max(µ.b), length.out = steps)
sigma.b <- apply(X = new.x, 2, FUN = sd)
riskCov.b[, i] <- sapply(range.b[, i], function(targetReturn) {
w <- MV_QP(new.x, targetReturn, Sigma)
sd(c(new.x %*% w))
})
xlim.b <- range(c(sigma.b, riskCov.b[, 1]), na.rm = TRUE)
ylim.b <- range(µ.b)
par(new = TRUE)
plot(x = riskCov.b[, i], y = range.b[, i], type = "l", xlim = xlim.b, ylim = ylim.b, xlab = "Risk", ylab = "Return", main = "Resampling EFs")
}
but the problem is that the elements on the x and y axis are rewriting each time the loop runs. How can this problem be solved?
I don't nknow if the optimization is correct. For ploting you can try the following:
for (i in 1:B) {
idx <- sample(1:N, N, replace = TRUE)
new.x <- x[idx, ]
µ.b <- apply(X = new.x, 2, FUN = mean)
range.b[, i] <- seq(from = min(µ.b), to = max(µ.b), length.out = steps)
#sigma.b <- apply(X = new.x, 2, FUN = sd)
riskCov.b[, i] <- sapply(range.b[, i], function(targetReturn) {
w <- MV_QP(new.x, targetReturn,Sigma=cov(new.x))
sd(c(new.x %*% w))
})
}
xlim.b <- range(c(apply(X = x, 2, FUN= sd), riskCov.b), na.rm = TRUE) *c(0.98,1.02)
ylim.b <- range(µ.b) *c(0.98,1.02)
#par(new = TRUE)
for (i in 1:B){
if (i==1) plot(x = riskCov.b[, i], y = range.b[, i], type = "l", xlim = xlim.b, ylim = ylim.b, xlab = "Risk", ylab = "Return", main = "Resampling EFs") else
lines(x = riskCov.b[, i], y = range.b[, i],col=rainbow(B)[i])
}
Depending on your data, you should end up with a similar plot: