Change plot area background color - r

I would like to do a graph in R using our company colors. This means the background of all charts should be a light blue, the plotting region however should be white. I was searching for answers and found that drawing a rect does the job (almost). However the plotting region is now white and the graph not visible anymore. Is this even possible?
getSymbols('SPY', from='1998-01-01', to='2011-07-31', adjust=T)
GRAPH_BLUE<-rgb(43/255, 71/255,153/255)
GRAPH_ORANGE<-rgb(243/255, 112/255, 33/255)
GRAPH_BACKGROUND<-rgb(180/255, 226/255, 244/255)
par(bg=GRAPH_BACKGROUND)
colorPlottingBackground<-function(PlottingBackgroundColor = "white"){
rect(par("usr")[1], par("usr")[3], par("usr")[2], par("usr")[4], col ="white")
}
plot.xts(SPY, col=GRAPH_BLUE)
colorPlottingBackground()

I know you already accepted #plannapus's answer, but this is a much simpler solution
par(bg="lightblue")
plot(0, 0, type="n", ann=FALSE, axes=FALSE)
u <- par("usr") # The coordinates of the plot area
rect(u[1], u[3], u[2], u[4], col="white", border=NA)
par(new=TRUE)
plot(1:10, cumsum(rnorm(10)))
What you basically do is to overlay two plots using par(new=TRUE): one with only a white rectangle; and another one with the contents you actually want to plot.

The issue is that you plot your white rectangle after plotting your data, therefore overwriting them. Since plot.xts doesn't have an argument add that would allow you to call it after drawing the rectangle, the only solution I see would be to modify function plot.xts.
plot.xtsMODIFIED<-function (x, y = NULL, type = "l", auto.grid = TRUE, major.ticks = "auto",
minor.ticks = TRUE, major.format = TRUE, bar.col = "grey",
candle.col = "white", ann = TRUE, axes = TRUE, ...)
{
series.title <- deparse(substitute(x))
ep <- axTicksByTime(x, major.ticks, format.labels = major.format)
otype <- type
if (is.OHLC(x) && type %in% c("candles", "bars")) {
x <- x[, has.OHLC(x, TRUE)]
xycoords <- list(x = .index(x), y = seq(min(x), max(x),
length.out = NROW(x)))
type <- "n"
}
else {
if (NCOL(x) > 1)
warning("only the univariate series will be plotted")
if (is.null(y))
xycoords <- xy.coords(.index(x), x[, 1])
}
###The next three lines are the only modifications i made to the function####
plot(xycoords$x, xycoords$y, type = "n", axes = FALSE, ann = FALSE)
rect(par("usr")[1], par("usr")[3], par("usr")[2], par("usr")[4], col ="white")
if(type=="l"){lines(xycoords$x, xycoords$y, ...)}
if (auto.grid) {
abline(v = xycoords$x[ep], col = "grey", lty = 4)
grid(NA, NULL)
}
if (is.OHLC(x) && otype == "candles")
plot.ohlc.candles(x, bar.col = bar.col, candle.col = candle.col,
...)
dots <- list(...)
if (axes) {
if (minor.ticks)
axis(1, at = xycoords$x, labels = FALSE, col = "#BBBBBB",
...)
axis(1, at = xycoords$x[ep], labels = names(ep), las = 1,
lwd = 1, mgp = c(3, 2, 0), ...)
axis(2, ...)
}
box()
if (!"main" %in% names(dots))
title(main = series.title)
do.call("title", list(...))
assign(".plot.xts", recordPlot(), .GlobalEnv)
}
Then your script become:
library(quantmod)
getSymbols('SPY', from='1998-01-01', to='2011-07-31', adjust=T)
GRAPH_BLUE<-rgb(43/255, 71/255,153/255)
GRAPH_BACKGROUND<-rgb(180/255, 226/255, 244/255)
par(bg=GRAPH_BACKGROUND)
plot.xtsMODIFIED(SPY, col=GRAPH_BLUE)
The error you're getting (Error in axis(1, at = xycoords$x, labels = FALSE, col = "#BBBBBB", ...) : formal argument "col" matched by multiple actual arguments.) was also thrown with your previous script. It has to do with the fact that plot.xts uses several time argument ... and that argument col is both valid for axis and plot(or here in my modified version, lines). If you want to avoid it, i see two solutions:
Either you want your axis to be of the same color as your line and therefore you have to change the line that says:
...
axis(1, at = xycoords$x, labels = FALSE, col = "#BBBBBB",
...)
...
Into
...
axis(1, at = xycoords$x, labels = FALSE, ...)
...
Or you want the axis to have the color intended by the writer of the original plot.xts in which case you need to differenciate the color of the lines and that of the axis.
plot.xtsMODIFIED<-function (x, y = NULL, type = "l", auto.grid = TRUE, major.ticks = "auto",
minor.ticks = TRUE, major.format = TRUE, bar.col = "grey",
candle.col = "white", ann = TRUE, axes = TRUE,
lcol, ...)
{
...
if(type=="l"){lines(xycoords$x, xycoords$y, lcol, ...)}
...
}
And then in your actual call:
plot.xtsMODIFIED(SPY, lcol=GRAPH_BLUE)

plot.xts will accept the panel.first argument, which is another way to draw the rectangle before plotting the line.
library(quantmod)
getSymbols('SPY', from='1998-01-01', to='2011-07-31', adjust=T)
GRAPH_BLUE<-rgb(43/255, 71/255,153/255)
GRAPH_BACKGROUND<-rgb(180/255, 226/255, 244/255)
par(bg=GRAPH_BACKGROUND)
white.rect=function() do.call(rect,as.list(c(par()$usr[c(1,3,2,4)],col="white")))
plot.xts(SPY,panel.first=white.rect())
This does not address the issue with col=GRAPH_BLUE pointed out by #plannapus.

Related

How to adjust legend position of interaction.plot and lineplot.CI?

I'm a beginner in coding. I was trying to create an interaction plot. Here's my code:
data is clinicaltrials from the data of the book "Learning Statistics with R."
library(sciplot)
library(lsr)
library(gplots)
lineplot.CI(x.factor = clin.trial$drug,
response = clin.trial$mood.gain,
group = clin.trial$therapy,
ci.fun = ciMean,
xlab = "Drug",
ylab = "Mood Gain")
and it produces the graph like this:
As can be seen in the graph, the legend box is not within my screen.
Also I tried creating another plot using the following code:
interaction.plot(x.factor = clin.trial$drug,
trace.factor = clin.trial$therapy,
response = clin.trial$mood.gain,
fun = mean,
type = "l",
lty = 1, # line type
lwd = 2, # line width
legend = T,
xlab = "Drug", ylab = "Mood Gain",
col = c("#00AFBB", "#E7B800"),
xpd = F,
trace.label = "Therapy")
For this code, I got the graph like this:
In this graph, the legend does not have labels.
Could anyone help me with these problems regarding legend?
You probably plan to save the plot via RStudio GUI. When you resize the plot window with your mouse, you need to run the code again to refresh the legend dimensions.
However, it's advantageous to use a more sophisticated method, e.g. to save it as a png with fixed dimensions like so:
library("sciplot")
library("lsr")
library("gplots")
png("Plot_1.png", height=400, width=500)
lineplot.CI(x.factor=clin.trial$drug,
response=clin.trial$mood.gain,
group=clin.trial$therapy,
ci.fun=ciMean,
xlab="Drug",
ylab="Mood Gain"
)
dev.off()
png("Plot_2.png", height=400, width=500)
interaction.plot(x.factor=clin.trial$drug,
trace.factor=clin.trial$therapy,
response=clin.trial$mood.gain,
fun=mean,
type="l",
lty=1, # line type
lwd=2, # line width
legend=T,
xlab="Drug", ylab="Mood Gain",
col=c("#00AFBB", "#E7B800"),
xpd=F,
trace.label="Therapy")
dev.off()
The plots are saved into your working directory, check getwd() .
Edit
You could also adjust the legend position.
In lineplot.CI you may use arguments; either by using characters just for x, e.g. x.leg="topleft" or both coordinates as numeric x.leg=.8, y.leg=2.2.
interaction.plot does not provide yet this functionality. I provide a hacked version below. Arguments are called xleg and yleg, functionality as above.
See ?legend for further explanations.
interaction.plot <- function (x.factor, trace.factor, response, fun = mean,
type = c("l", "p", "b", "o", "c"), legend = TRUE,
trace.label = deparse(substitute(trace.factor)),
fixed = FALSE, xlab = deparse(substitute(x.factor)),
ylab = ylabel, ylim = range(cells, na.rm = TRUE),
lty = nc:1, col = 1, pch = c(1L:9, 0, letters),
xpd = NULL, leg.bg = par("bg"), leg.bty = "n",
xtick = FALSE, xaxt = par("xaxt"), axes = TRUE,
xleg=NULL, yleg=NULL, ...) {
ylabel <- paste(deparse(substitute(fun)), "of ", deparse(substitute(response)))
type <- match.arg(type)
cells <- tapply(response, list(x.factor, trace.factor), fun)
nr <- nrow(cells)
nc <- ncol(cells)
xvals <- 1L:nr
if (is.ordered(x.factor)) {
wn <- getOption("warn")
options(warn = -1)
xnm <- as.numeric(levels(x.factor))
options(warn = wn)
if (!anyNA(xnm))
xvals <- xnm
}
xlabs <- rownames(cells)
ylabs <- colnames(cells)
nch <- max(sapply(ylabs, nchar, type = "width"))
if (is.null(xlabs))
xlabs <- as.character(xvals)
if (is.null(ylabs))
ylabs <- as.character(1L:nc)
xlim <- range(xvals)
if (is.null(xleg)) {
xleg <- xlim[2L] + 0.05 * diff(xlim)
xlim <- xlim + c(-0.2/nr, if (legend) 0.2 + 0.02 * nch else 0.2/nr) *
diff(xlim)
}
dev.hold()
on.exit(dev.flush())
matplot(xvals, cells, ..., type = type, xlim = xlim, ylim = ylim,
xlab = xlab, ylab = ylab, axes = axes, xaxt = "n",
col = col, lty = lty, pch = pch)
if (axes && xaxt != "n") {
axisInt <- function(x, main, sub, lwd, bg, log, asp,
...) axis(1, x, ...)
mgp. <- par("mgp")
if (!xtick)
mgp.[2L] <- 0
axisInt(1, at = xvals, labels = xlabs, tick = xtick,
mgp = mgp., xaxt = xaxt, ...)
}
if (legend) {
yrng <- diff(ylim)
if (is.null(yleg))
yleg <- ylim[2L] - 0.1 * yrng
if (!is.null(xpd) || {
xpd. <- par("xpd")
!is.na(xpd.) && !xpd. && (xpd <- TRUE)
}) {
op <- par(xpd = xpd)
on.exit(par(op), add = TRUE)
}
# text(xleg, ylim[2L] - 0.05 * yrng, paste(" ",
# trace.label), adj = 0)
if (!fixed) {
ord <- sort.list(cells[nr, ], decreasing = TRUE)
ylabs <- ylabs[ord]
lty <- lty[1 + (ord - 1)%%length(lty)]
col <- col[1 + (ord - 1)%%length(col)]
pch <- pch[ord]
}
legend(xleg, yleg, legend = ylabs, col = col,
title = if (trace.label == "") NULL else trace.label,
pch = if (type %in% c("p", "b"))
pch, lty = if (type %in% c("l", "b"))
lty, bty = leg.bty, bg = leg.bg)
}
invisible()
}
Data:
lk <- "https://learningstatisticswithr.com/data.zip"
tmp <- tempfile()
tmp.dir <- tempdir()
download.file(lk, tmp)
unzip(tmp, exdir=tmp.dir)
load("data/clinicaltrial.Rdata")

3 different colour regions in levelplot

Trying to plot this matrix so that negative values are red, 0 is blue and positive values are green.
m <- matrix(c(0,0,-1,-2,0,0,1,0,1,3,3,3,-3,0,0,2),nrow = 4)
levelplot(m,at=c(-3,0,3),col.regions=c("red","blue","green"),xlab = xlab.a, ylab="", colorkey = FALSE, panel = function(...) {
panel.fill(col = "blue")
panel.levelplot(...)
})
Here is one more generic way using boolean comparisons:
levelplot((m>=0) + (m > 0),at=c(-0.1, 0:2),col.regions=c("red","blue","green"), xlab = "", ylab="", colorkey = FALSE, panel = function(...) {
panel.fill(col = "blue")
panel.levelplot(...)
})
m <- matrix(c(0,0,-1,-2,0,0,1,0,1,3,3,3,-3,0,0,2),nrow = 4)
levelplot(m,at=c(-3,-1,0,3),col.regions=c("red","blue","green"), xlab = "", ylab="", colorkey = FALSE, panel = function(...) {
panel.fill(col = "blue")
panel.levelplot(...)
})

set lwd of axis in par() function

I want to plot with a certain line width (about 1/5 of default lwd). All lines in the plot should have this lwd.
I do:
par(lwd = 0.2)
plot(1:10, type = "l")
The result is ok except for the thickness of the axes lines and ticks, which seems to be unaffected by the par() function.
The only option I know is to separately define the lwd for each axis:
par(lwd = 0.2)
plot(1:10, type = "l", axes = F)
axis(1, lwd = 0.2)
axis(2, lwd = 0.2)
box()
However, this is tedious and I can not imagine that there is no "global" lwd option. If you have an idea how this could be done efficiently for several plots please respond.
If we look at the formals of axis() function - default lwd is specified as 1:
> axis
function (side, at = NULL, labels = TRUE, tick = TRUE, line = NA,
pos = NA, outer = FALSE, font = NA, lty = "solid", lwd = 1,
lwd.ticks = lwd, col = NULL, col.ticks = NULL, hadj = NA,
padj = NA, ...)
{
And as you noticed they are not affected by the par() setting in this implementation.
One simple solution would be to make a wrapper for axis() function and make it use the par() setting by default. Here is how that might look like:
axislwd <- function(...) axis(lwd=par()$lwd, ...)
par(lwd = 0.2)
plot(1:10, type = "l", axes = F)
axislwd(1)
axislwd(2)
box()
Alternatively you can write a wrapper for the whole plot function instead:
plotlwd <- function(...) {
plot(axes = FALSE, ...)
axis(1, lwd=par()$lwd)
axis(2, lwd=par()$lwd)
box()
}
par(lwd = 0.2)
plotlwd(1:10, type="l")

Fix layout / plot options of plot(fevd()) function

I am currently busy with a Vector Autoregressive Analysis in R, using the vars package. I was wondering if the following things are possible:
1) the previous solution for a better fit on the page worked properly, but now with more variables, we have more plots and the layout is screwed again. I already played with some of the win.graph() parameters, but nothing gives me a proper readable solution.
2) The plot(irf(...) function of the vars package gives a one-graph per page output. I was wondering if this is also possible for the plot(fevd() function by adding some extra (unkown to me) parameters options
3) Also, for readability, I would like to color the graphs, plot(fevd() gives an all-kinds-of-gray output, Is it possible to change those colors?
Thank you in advance
Olivier
You will have to modify the plot function for fevd to do what you want. Here's a modified plot.varfevd function that removes all calls to par(). This allows to use layout properly. The lines that have been removed have been commented out (#). I also removed the parameter that asked for confirmation in "single" plots.
plot.varfevd <-function (x, plot.type = c("multiple", "single"), names = NULL,
main = NULL, col = NULL, ylim = NULL, ylab = NULL, xlab = NULL,
legend = NULL, names.arg = NULL, nc, mar = par("mar"), oma = par("oma"),
addbars = 1, ...)
{
K <- length(x)
ynames <- names(x)
plot.type <- match.arg(plot.type)
if (is.null(names)) {
names <- ynames
}
else {
names <- as.character(names)
if (!(all(names %in% ynames))) {
warning("\nInvalid variable name(s) supplied, using first variable.\n")
names <- ynames[1]
}
}
nv <- length(names)
# op <- par(no.readonly = TRUE)
ifelse(is.null(main), main <- paste("FEVD for", names), main <- rep(main,
nv)[1:nv])
ifelse(is.null(col), col <- gray.colors(K), col <- rep(col,
K)[1:K])
ifelse(is.null(ylab), ylab <- rep("Percentage", nv), ylab <- rep(ylab,
nv)[1:nv])
ifelse(is.null(xlab), xlab <- rep("Horizon", nv), xlab <- rep(xlab,
nv)[1:nv])
ifelse(is.null(ylim), ylim <- c(0, 1), ylim <- ylim)
ifelse(is.null(legend), legend <- ynames, legend <- legend)
if (is.null(names.arg))
names.arg <- c(paste(1:nrow(x[[1]])), rep(NA, addbars))
plotfevd <- function(x, main, col, ylab, xlab, names.arg,
ylim, ...) {
addbars <- as.integer(addbars)
if (addbars > 0) {
hmat <- matrix(0, nrow = K, ncol = addbars)
xvalue <- cbind(t(x), hmat)
barplot(xvalue, main = main, col = col, ylab = ylab,
xlab = xlab, names.arg = names.arg, ylim = ylim,
legend.text = legend, ...)
abline(h = 0)
}
else {
xvalue <- t(x)
barplot(xvalue, main = main, col = col, ylab = ylab,
xlab = xlab, names.arg = names.arg, ylim = ylim,
...)
abline(h = 0)
}
}
if (plot.type == "single") {
# par(mar = mar, oma = oma)
# if (nv > 1)
# par(ask = TRUE)
for (i in 1:nv) {
plotfevd(x = x[[names[i]]], main = main[i], col = col,
ylab = ylab[i], xlab = xlab[i], names.arg = names.arg,
ylim = ylim, ...)
}
}
else if (plot.type == "multiple") {
if (missing(nc)) {
nc <- ifelse(nv > 4, 2, 1)
}
nr <- ceiling(nv/nc)
par(mfcol = c(nr, nc), mar = mar, oma = oma)
for (i in 1:nv) {
plotfevd(x = x[[names[i]]], main = main[i], col = col,
ylab = ylab[i], xlab = xlab[i], names.arg = names.arg,
ylim = ylim, ...)
}
}
# on.exit(par(op))
}
Then, you will need short variable names. Pick acronyms if needed.
library(vars)
data(Canada)
colnames(Canada) <-c("name1","name2","name3","name4")
var <- VAR(Canada , p=4 , type = "both")
Using a wide plot window (using win.graph) and using layout (to get the placement of your eight plots), you can get all charts displayed properly. I also changed the colors of the plots as requested. Finally, we are now using single plots as there are no calls to par() which do not sit well with layout().
win.graph(width=15,height=8)
layout(matrix(1:8,ncol=2))
plot.varfevd(fevd(var, n.ahead = 10 ),plot.type = "single", col=1:4)
plot.varfevd(fevd(var, n.ahead = 10 ),plot.type = "single", col=1:4)

Put one line chart and bar chart in one plot in R (Not ggplot)?

how to
Combine a bar chart and line in single plot in R (from different data sources)?
Say I have two data sources as:
barData<-c(0.1,0.2,0.3,0.4) #In percentage
lineData<-c(100,22,534,52,900)
Note that they may not be in the same scale.
Can I plot both barData and LineData in one plot and make them good looking ?
I cant use ggplot in this case so this is not a duplicated question..
Something like the following:
Maybe this helps as a starting point:
par(mar = rep(4, 4))
barData<-c(0.1,0.2,0.3,0.4) * 100
y <- lineData<-c(100,22,534,900);
x <- barplot(barData,
axes = FALSE,
col = "blue",
xlab = "",
ylab = "",
ylim = c(0, 100) )[, 1]
axis(1, at = x, labels = c("Julia", "Pat", "Max", "Norman"))
ats <- c(seq(0, 100, 15), 100); axis(4, at = ats, labels = paste0(ats, "%"), las = 2)
axis(3, at = x, labels = NA)
par(new = TRUE)
plot(x = x, y = y, type = "b", col = "red", axes = FALSE, xlab = "", ylab = "")
axis(2, at = c(pretty(lineData), max(lineData)), las = 2)
mtext(text="Lines of code by Programmer", side = 3, line = 1)
box()

Resources