R/quantmod: multiple charts all using the same y-axis - r

I'm trying to plot 6 days of intraday data as 6 charts. Quantmod's experimental chart_Series() function works with par() settings. I've pre-loaded the data into bars (a vector of XTS objects) so my code looks like this:
par(mfrow=c(3,2)) #3 rows, 2 columns
for(d in bars){
print(chart_Series(d, type = "candlesticks") )
}
This works, but each chart has its own different y-axis scale. I wanted to set a y-range that covers all 6 days, but cannot find a way to do this. I tried this:
ylim=c(18000,20000)
print(chart_Series(d, type = "candlesticks",ylim=ylim) )
but it fails with the "unused argument(s)" error. yrange=ylim also fails.
I can use chartSeries(d,yrange=ylim), and it works. But as far as I know I cannot put multiple charts in one display (?).
(It might strictly be off-subject, but suggestions for alternative R packages that can draw nice-looking candlestick charts, allow y-axis control and can draw multiple charts on one image would also be very welcome.)

With chartSeries, you can set the layout argument to NULL to prevent the layout() command from being called: this is what disables the mfrow setting.
library(quantmod)
getSymbols("AA")
op <- par(mfrow=c(3,2))
for(i in 1:6) {
chartSeries(
AA["2011-01"], "candlesticks",
TA=NULL, # No volume plot
layout=NULL,
yrange=c(15,18)
)
}
par(op)
If you want to keep the volume, you can call layout instead of setting mfrow: it does basically the same thing, but allows you to have plots of different sizes and choose the order in which they are plotted.
layout( matrix( c(
1, 3,
2, 4,
5, 7,
6, 8,
9, 11,
10, 12
), nc=2, byrow=TRUE),
heights = rep( c(2,1), 3 )
)
#layout.show(12) # To check that the order is as desired
for(i in 1:6) {
chartSeries(
AA[sprintf("2011-%02d",i)],
"candlesticks", layout=NULL, yrange=c(15,19)
)
}

Googling to understand Vincent's answer led me to the layout() command. It seems incompatible with par(mfrow), but some more experimentation found it can be used as an alternative.
ylim=c(18000,20000)
layout(matrix(1:12,nrow=6,ncol=2), height=c(4,2,4,2,4,2))
for(d in bars){
chartSeries(d,layout=NULL,TA=c(addVo(),addBBands()),yrange=ylim)
}
(You'll notice I added bollinger bands too, to be sure overlays still work too.)

Related

Need help fixing the plot for a simple curve() function code

I am trying to plot four separate functions on the same graph using the curve() function in r. I came up with the following code:
for (n in 1:4){
curve(n*sin(x), -5, 5, add = TRUE)
}
Unfortunately, when I try that, the resulting plot is extremely zoomed in on one arbitrary point of the graph (axes labels nor graph borders can be seen either). Just to clarify, there is no resulting error message in the console at all, the plot is just very zoomed in.
Instead of plotting them in a for loop, I tried plotting them separately to see if it would work and it did. I used:
curve(4*sin(x), -5, 5)
curve(3*sin(x), -5, 5, add = TRUE)
curve(2*sin(x), -5, 5, add = TRUE)
curve(1*sin(x), -5, 5, add = TRUE)
I had also thought that it could be that I used curve() in a for loop; however, it has worked for this code (demonstrating that the function doesn't seem to care whether I use it in a loop-type function):
for (n in 0:5){
curve(x^n, -3, 3, add = TRUE)
}
Besides trying out different code, I have closed my graphics device, turned it off with dev.off(), restarted RStudio, but none of it has worked.
If I were only using a sequence from 1 to 4, like I mentioned above, I wouldn't care about typing them separately; however, I plan on using a much larger range of sequences in the future (like 1:50 or 1:100 for example).
I'm using RStudio version 3.4.4 with macOS 10.14.2 if that matters.

Replay recorded plot with new layout in R

I am trying to create and record plots in a 1x1 device:
par(mfrow = c(1, 1) )
plot(rnorm(10) )
p1 <- recordPlot()
plot(rnorm(20) )
p2 <- recordPlot()
and then to put them in a new layout (e.g., a 1x2 device):
par(mfrow = c(1, 2) )
p1
p2
However, this produce the same effect (i.e., plotting each plot in a 1x1 device). It seems replaying plots uses the original layout (graphical parameters) that was in effect when they were recorded.
Is there some method that allows a saved plot to be replayed in a new layout ?
NB: I am aware this would be easier via ggplot2, but my question is about base plots.
I did some digging, and I don't think this is possible. I used the following to look at what attributes are available inside the object. None of them seemed to indicate the layout could be adjusted.
summary(p1)
p1[[1]]
p1[[2]]
If you need the same plot across two different layouts could you use set.seed() to recreated the same plot? See the example below.
par(mfrow = c(1, 1))
set.seed(1234)
plot(rnorm(10))
par(mfrow = c(1, 2))
set.seed(1234)
plot(rnorm(10))
I'd be interested to see if anyone else has a better answer!

Using loops to set layout dimensions R

I am not sure this is possible. Basically what I'm trying to do is create a plot loop where if more than 5 plots are to be plotted then a second row of plots should be done ncol = to 5- number of plots.
data=matrix(rbinom(10*1000, 1, .5), ncol=10)
subdata1 = data[1:5,]
subdata2 = data[1:7,]
if (nrow(subdata1) <= 5){
par(mfrow = c(1, nrow(subdata1)))
for (i in 1:nrow(subdata1)){
plot(as.numeric(subdata1[i,1:5]), as.numeric(subdata1[i,6:10]))
}
}else{
## need to figure out how to bind layout based on nrows
## i.e. subdata2
return(NULL)
}
Basically I'm building a shinny app where based on users selections there could be anywhere from 1 plot to 10 and I want to be able to display this as nice as possible.
If you want to be as nice as possible perhaps you should look at the easy option of using the n2mfrow() function. This takes a number and turns it into the best row/column combination. With your example you can do par(mfrow = n2mfrow(nrow(data))) before running your for-loop plot. However, this will not fix the plots to 5 columns.

How to overlay multiple TA in new plot using quantmod?

We can plot candle stick chart using chart series function chartSeries(Cl(PSEC)) I have created some custom values (I1,I2 and I3) which I want to plot together(overlay) outside the candle stick pattern. I have used addTA() for this purpose
chartSeries(Cl(PSEC)), TA="addTA(I1,col=2);addTA(I2,col=3);addTA(I3,col=4)")
The problem is that it plots four plots for Cl(PSEC),I1,I2 and I3 separately instead of two plots which I want Cl(PSEC) and (I1,I2,I3)
EDITED
For clarity I am giving a sample code with I1, I2 and I3 variable created for this purpose
library(quantmod)
PSEC=getSymbols("PSEC",auto.assign=F)
price=Cl(PSEC)
I1=SMA(price,3)
I2=SMA(price,10)
I3=SMA(price,15)
chartSeries(price, TA="addTA(I1,col=2);addTA(I2,col=3);addTA(I3,col=4)")
Here is an option which preserves largely your original code.
You can obtain the desired result using the option on=2 for each TA after the first:
library(quantmod)
getSymbols("PSEC")
price <- Cl(PSEC)
I1 <- SMA(price,3)
I2 <- SMA(price,10)
I3 <- SMA(price,15)
chartSeries(price, TA=list("addTA(I1, col=2)", "addTA(I2, col=4, on=2)",
"addTA(I3, col=5, on=2)"), subset = "last 6 months")
If you want to overlay the price and the SMAs in one chart, you can use the option on=1 for each TA.
Thanks to #hvollmeier who made me realize with his answer that I had misunderstood your question in the previous version of my answer.
PS: Note that several options are described in ?addSMA(), including with.col which can be used to select a specific column of the time series (Cl is the default column).
If I understand you correctly you want the 3 SMAs in a SUBPLOT and NOT in your main chart window.You can do the following using newTA.
Using your data:
PSEC=getSymbols("PSEC",auto.assign=F)
price=Cl(PSEC)
Now plotting a 10,30,50 day SMA in a window below the main window:
chartSeries(price['2016'])
newSMA <- newTA(SMA, Cl, on=NA)
newSMA(10)
newSMA(30,on=2)
newSMA(50,on=2)
The key is the argument on. Use on = NA in defining your new TA function, because the default value foron is 1, which is the main window. on = NA plots in a new window. Then plot the remaining SMAs to the same window as the first SMA. Style the colours etc.to your liking :-).
You may want to consider solving this task using plotting with the newer quantmod charts in the quantmod package (chart_Series as opposed to chartSeries).
Pros:
-The plots look cleaner and better (?)
-have more flexibility via editing the pars and themes options to chart_Series (see other examples here on SO for the basics of things you can do with pars and themes)
Cons:
-Not well documented.
PSEC=getSymbols("PSEC",auto.assign=F)
price=Cl(PSEC)
chart_Series(price, subset = '2016')
add_TA(SMA(price, 10))
add_TA(SMA(price, 30), on = 2, col = "green")
add_TA(SMA(price, 50), on = 2, col = "red")
# Make plot all at once (this approach is useful in shiny applications):
print(chart_Series(price, subset = '2016', TA = 'add_TA(SMA(price, 10), yaxis = list(0, 10));
add_TA(SMA(price, 30), on = 2, col = "purple"); add_TA(SMA(price, 50), on = 2, col = "red")'))

lattice or latticeExtra combine multiple plots different yscaling (log10 and non-transformed)

I have a multiple variable time series were some of the variables have rather large ranges. I wish to make a single-page plot with multiple stacked plots of each variable were some of the variables have a log10 y-axis scaling. I am relatively new to lattice and have not been able to figure out how to effectively mix the log10 scaling with non-transformed axes and get a publication quality plot. If print.trellis is used the plots are not aligned and the padding needs some work, if c.trellis is used the layout is good, but only the y-scaling from only one plot is used. Any suggestions for an efficient solution, where I can replicate the output of c.trellis using the different y-scaling for each (original) object?
Example below:
require(lattice)
require(latticeExtra)
# make data.frame
d.date <- as.POSIXct(c("2009-12-15", "2010-01-15", "2010-02-15", "2010-03-15", "2010-04-15"))
CO2dat <- c(100,200,1000,9000,2000)
pHdat <- c(10,9,7,6,7)
tmp <- data.frame(date=d.date ,CO2dat=CO2dat ,pHdat=pHdat)
# make plots
plot1 <- xyplot(pHdat ~ date, data=tmp
, ylim=c(5,11)
, ylab="pHdat"
, xlab="Date"
, origin = 0, border = 0
, scales=list(y=list(alternating=1))
, panel = function(...){
panel.xyarea(...)
panel.xyplot(...)
}
)
# make plot with log y scale
plot2 <- xyplot(CO2dat ~ date, data=tmp
, ylim=c(10,10^4)
, ylab="CO2dat"
, xlab="Date"
, origin = 0, border = 0
, scales=list(y=list(alternating=1,log=10))
, yscale.components = yscale.components.log10ticks
, panel = function(...){
panel.xyarea(...)
panel.xyplot(...)
# plot CO2air uatm
panel.abline(h=log10(390),col="blue",type="l",...)
}
)
# plot individual figures using split
print(plot2, split=c(1,1,1,2), more=TRUE)
print(plot1, split=c(1,2,1,2), more=F)
# combine plots (more convenient)
comb <- c(plot1, plot2, x.same=F, y.same=F, layout = c(1, 2))
# plot combined figure
update(comb, ylab = c("pHdat","log10 CO2dat"))
Using #joran's idea, I can get the axes to be closer but not exact; also, reducing padding gets them closer together but changes the aspect ratio. In the picture below I've reduced the padding perhaps by too much to show the not exactness; if this close were desired, you'd clearly want to remove the x-axis labels on the top as well.
I looked into the code that sets up the layout and the margin on the left side is calculated from the width of the labels, so #joran's idea is probably the only thing that will work based on the printing using split, unless one were to rewrite the plot.trellis command. Perhaps the c method could work but I haven't found a way yet to set the scale components separately depending on the panel. That does seem more promising though.
mtheme <- standard.theme("pdf")
mtheme$layout.heights$bottom.padding <- -10
plot1b <- update(plot1, scales=list(y=list(alternating=1, at=5:10, labels=paste(" ",c(5:10)))))
plot2b <- update(plot2, par.settings=mtheme)
pdf(file="temp.pdf")
print(plot2b, split=c(1,1,1,2), more=TRUE)
print(plot1b, split=c(1,2,1,2), more=F)

Resources