I am trying to plot some data points from a matrix complete with their standard deviation, but I am having troubles in plotting the latter.
My tools are:
a matrix with the data points to plot at a x coordinate within a properly xlim-defined x-axis;
a vector of as many y arbitrary coordinates for the plotting height, just not making them overlap;
a vector of lengths of the standard deviation lines, to be displayed horizontally around the data points.
Yeah, eventually it'll look like a flying saucer invasion.
I can easily plot the points at the given height, one by one - it is the way I want to do it.
Trouble comes in adding the standard deviation horizontal lines for each point.
Has someone an idea on how to do it?
x<-matrix(c(1:4,NA,NA,10:16), nrow=4, ncol=4)
y<-seq(0.001,0.006, 0.001)
std.dev<-c(runif(7, 0.1, 0.5), NA, NA, runif(7, 0.1, 0.5))
plot(0,0, xlim=c(min = 0, max(x), na.rm=T)+0.001), ylim = c(0,0.016), type = "n", xlab = "My x", yaxt = "n", ylab ="")
points(x = x[1,2], y = y[1], pch = 21, bg = "red", col = "red")
When working with base R it is amazing to find out that R does not provide a "built-in" support for error bars. You may want to consult doing this with other packages.
With base R the work-around is to use the arrow() function and setting the "arrow head angle" to 90 degrees.
Note: I had to change your given data definition as it threw errors. Also have a look at this part of your code.
I plot the error bars in vertical mode. You can easily adapt this for horizontal bars. I did this for presentation reasons to avoid overlapping error bars.
Using your full data will make it easier to deconflict the bars.
x<-matrix(c(1:7,NA,NA,10:16), nrow=4, ncol=4) # adapted to ensure same length
y<-seq(0.001,0.016, 0.001) # adapted to ensure same length
std.dev<-c(runif(7, 0.1, 0.5), NA, NA, runif(7, 0.1, 0.5))
plot(0,0
, xlim= c(min = 0, max(x, na.rm=T)) # had to fix xlim definition
, ylim = c(-1,1) # changed to show give std.dev
, type = "n", xlab = "My x", yaxt = "n", ylab ="")
points(x = x, y = y, pch = 21, bg = "red", col = "red") # set x and y to show all
# --------------- add arrows with "flat head --------------------------
arrows( x0 = x, , x1 = x
,y0 = y-std.dev, y1 = y+std.dev # center deviation on data point
, code=3, angle=90 # set the angle for the head to emulate error bar
, length=0.1)
This yields:
Related
I am trying to get the x axis labels to be rotated 45 degrees on a barplot with no luck. This is the code I have below:
barplot(((data1[,1] - average)/average) * 100,
srt = 45,
adj = 1,
xpd = TRUE,
names.arg = data1[,2],
col = c("#3CA0D0"),
main = "Best Lift Time to Vertical Drop Ratios of North American Resorts",
ylab = "Normalized Difference",
yaxt = 'n',
cex.names = 0.65,
cex.lab = 0.65)
use optional parameter las=2 .
barplot(mytable,main="Car makes",ylab="Freqency",xlab="make",las=2)
EDITED ANSWER PER DAVID'S RESPONSE:
Here's a kind of hackish way. I'm guessing there's an easier way. But you could suppress the bar labels and the plot text of the labels by saving the bar positions from barplot and do a little tweaking up and down. Here's an example with the mtcars data set:
x <- barplot(table(mtcars$cyl), xaxt="n")
labs <- paste(names(table(mtcars$cyl)), "cylinders")
text(cex=1, x=x-.25, y=-1.25, labs, xpd=TRUE, srt=45)
Rotate the x axis labels with angle equal or smaller than 90 degrees using base graphics. Code adapted from the R FAQ:
par(mar = c(7, 4, 2, 2) + 0.2) #add room for the rotated labels
#use mtcars dataset to produce a barplot with qsec colum information
mtcars = mtcars[with(mtcars, order(-qsec)), ] #order mtcars data set by column "qsec"
end_point = 0.5 + nrow(mtcars) + nrow(mtcars) - 1 #this is the line which does the trick (together with barplot "space = 1" parameter)
barplot(mtcars$qsec, col = "grey50",
main = "",
ylab = "mtcars - qsec", ylim = c(0,5 + max(mtcars$qsec)),
xlab = "",
xaxt = "n", # Do not plot the default labels
space = 1)
#rotate 60 degrees (srt = 60)
text(seq(1.5, end_point, by = 2), par("usr")[3]-0.25,
srt = 60, adj = 1, xpd = TRUE,
labels = paste(rownames(mtcars)), cex = 0.65)
You can simply pass your data frame into the following function:
rotate_x <- function(data, column_to_plot, labels_vec, rot_angle) {
plt <- barplot(data[[column_to_plot]], col='steelblue', xaxt="n")
text(plt, par("usr")[3], labels = labels_vec, srt = rot_angle, adj = c(1.1,1.1), xpd = TRUE, cex=0.6)
}
Usage:
rotate_x(mtcars, 'mpg', row.names(mtcars), 45)
You can change the rotation angle of the labels as needed.
You may use
par(las=2) # make label text perpendicular to axis
It is written here: http://www.statmethods.net/graphs/bar.html
You can use ggplot2 to rotate the x-axis label adding an additional layer
theme(axis.text.x = element_text(angle = 90, hjust = 1))
In the documentation of Bar Plots we can read about the additional parameters (...) which can be passed to the function call:
... arguments to be passed to/from other methods. For the default method these can
include further arguments (such as axes, asp and main) and graphical
parameters (see par) which are passed to plot.window(), title() and axis.
In the documentation of graphical parameters (documentation of par) we can see:
las
numeric in {0,1,2,3}; the style of axis labels.
0:
always parallel to the axis [default],
1:
always horizontal,
2:
always perpendicular to the axis,
3:
always vertical.
Also supported by mtext. Note that string/character rotation via argument srt to par does not affect the axis labels.
That is why passing las=2 makes the labels perpendicular, although not at 45°.
Andre Silva's answer works great for me, with one caveat in the "barplot" line:
barplot(mtcars$qsec, col="grey50",
main="",
ylab="mtcars - qsec", ylim=c(0,5+max(mtcars$qsec)),
xlab = "",
xaxt = "n",
space=1)
Notice the "xaxt" argument. Without it, the labels are drawn twice, the first time without the 60 degree rotation.
Just a minor question. I am trying to make a legend for the following plot.
# fitting the linear model
iris_lm = lm(Petal.Length ~ Sepal.Length, data = iris)
summary(iris_lm)
# calculating the confidence interval for the fitted line
preds = predict(iris_lm, newdata = data.frame(Sepal.Length = seq(4,8,0.1)),
interval = "confidence")
# making the initial plot
par(family = "serif")
plot(Petal.Length ~ Sepal.Length, data = iris, col = "darkgrey",
family = "serif", las = 1, xlab = "Sepal Length", ylab = "Pedal Length")
# shading in the confidence interval
polygon(
c(seq(8,4,-0.1), seq(4,8,0.1)), # all of the necessary x values
c(rev(preds[,3]), preds[,2]), # all of the necessary y values
col = rgb(0.2745098, 0.5098039, 0.7058824, 0.4), # the color of the interval
border = NA # turning off the border
)
# adding the regression line
abline(iris_lm, col = "SteelBlue")
# adding a legend
legend("bottomright", legend = c("Fitted Values", "Confidence Interval"),
lty = c(1,0))
Here's the output so far:
My goal is to put a box in the legend next to the "Confidence Interval" tab, and color it in the same shade that it is in the picture. Naturally, I thought to use the pch parameter. However, when I re-run my code with the additional legend option pch = c(NA, 25), I get the following:
It is not super noticeable, but if you look closely at the padding on the left margin of the legend, it actually has decreased, and the edge of the border is now closer to the line than I would like. Is there any way to work around this?
That's a curious behavior in legend(). I'm sure someone will suggest a ggplot2 alternative. However, legend() does offer a solution. This solution calls the function without plotting anything to capture the dimensions of the desired rectangle. The legend is then plotted with the elements you really want but no enclosing box (bty = "n"). The desired rectangle is added explicitly. I assume you mean pch = 22 to get the filled box symbol. I added pt.cex = 2 to make it a bit larger.
# Capture the confidence interval color, reusable variables
myCol <- rgb(0.2745098, 0.5098039, 0.7058824, 0.4)
legText <- c("Fitted Values", "Confidence Interval")
# Picking it up from 'adding a legend'
ans <- legend("bottomright", lty = c(1,0), legend = legText, plot = F)
r <- ans$rect
legend("bottomright", lty = c(1,0), legend = legText, pch = c(NA,22),
pt.bg = myCol, col = c(1, 0), pt.cex = 2, bty = "n")
# Draw the desired box
rect(r$left, r$top - r$h, r$left + r$w, r$top)
By the way, I don't think this will work without further tweaking if you place the legend on the left side.
I am using the packages rgl and plot3D to plot 3 dimensional data in a scatter plot. my data looks as follows:
install.packages("rgl")
library(rgl)
x <- Kg$GBMVP_SD
y <- Kg$GBMVP_Ret
z <- Kg$Asset_No
K<- as.matrix(Kg$GRN_No)
RF<- as.matrix(Kg$Risk_Free)
scatter3D(x , y , z , colvar = K,
pch = 16, cex = 1.5, xlab = "GMVP Standard Deviation", ylab = "GMVP Return",
zlab = "Total No of Assets in Portfolio", clab = c("Green Asset No"),
main = "Global Minimum Variance Portfolio(GMVP) Return and Standard Deviation vs No of Green Assets ", ticktype = "detailed",
colkey = list(length = 0.5, width = 0.5, cex.clab = 0.75))
which gives me:
I am trying to add a vertical plane to this where for x and z as specified above, the y is equal to RF, which is all one value. I have tried the rgl package with no luck, as all fit regression planes and when I use different z values in the plane as that specified for the scatter, it doesn't plot. I am trying to avoid using the scatterplot3d package due to the better graphics of the other packages, but will do if there is on other option. Any help is appreciated.
Is there a way to draw the lines in such a way that they would start on the side of the points, or allow the symbols to be in foreground?
My solution was to make the symbols bigger and more visible.
Edit 1: it's for plot {graphics} of the R program.
Edit 2: the code per popular request.
legend(2,.4,bty='n', c('sugar','citrus','none'), pch=c('s','c','u'), pt.bg='white',lty= c(1,2,3), lwd=1.5, title="Condition",pt.cex=c(1.5),cex=1.5)
Edit 3: This is solved for plot(type='b') but somehow not for legend.
Thanks for reading!
The only thing I can come up with is to manually finagle the dash lengths until they end up looking the way you want them. For instance, this:
> plot(1,1)
> legend(c("A", "B"), col = 1:2, x = 1, y = .8, lty="99", pch=1:2)
produces the image below.
The lty parameter allows you to specify the lengths of lines and dashes as hex characters. In this case, it's saying to create a line of length 9 then create a space of length 9 then repeat. It looks like 9 is about the best fit to space around a normal pch symbol.
Note that you'd probably need to adjust this depending on the size of the image, symbol, etc. My advice ultimately would be to export the image from R and touch up the image to meet your needs in graphic editing software.
Going with the suggestion by #JeffAllen, here is a way to get what I think you might want. It requires modifying the legend() function to return the position of the points (these are given by x1 and y1 in body(legend)[[46]]).
legend2 <- legend
body(legend2)[[49]] <- quote(
invisible(list(rect = list(w = w, h = h, left = left, top = top),
text = list(x = xt, y = yt), points = list(x = x1, y = y1)))
)
Make a plot:
plot(-100:100, -100:100, type = "b")
While drawing the legend, draw white circles (pch = 21 with pt.bg = 'white') over the lines, and assign the values invisibly returned by legend2() to an object. Note also the changes to pt.lwd and pt.cex.
myLegend <- legend2(1, .8, bty = 'n', c('sugar','citrus','none'), pch = 21,
pt.bg = 'white', pt.lwd = 0, lty = c(1, 2, 3), lwd = 1.5, title = "Condition",
pt.cex = c(1.8), cex = 1.5)
Finally, draw the characters you'd like to use in the legend using points(), supplying the x and y values from the object myLegend.
points(myLegend$points$x, myLegend$points$y, pch = c('s','c','u'), cex = 1.5)
And this should get you something like:
You could also use the filled points offered by R (pch=21:25) and specify the fill color using pc.bg which gets passed to the points call when creating a legend.
plot(1,1)
legend(c("A", "B"), col = 1:2, x = 1, y = .8, lty=1, pt.bg=1:2, pch=21:22)
generates the following:
Given such data:
#Cutpoint SN (1-PPV)
5 0.56 0.01
7 0.78 0.19
9 0.91 0.58
How can I plot ROC curve with R that produce similar result like the
attached ?
I know ROCR package but it doesn't take such input.
If you just want to create the plot (without that silly interpolation spline between points) then just plot the data you give in the standard way, prepending a point at (0,0) and appending one at (1,1) to give the end points of the curve.
## your data with different labels
dat <- data.frame(cutpoint = c(5, 7, 9),
TPR = c(0.56, 0.78, 0.91),
FPR = c(0.01, 0.19, 0.58))
## plot version 1
op <- par(xaxs = "i", yaxs = "i")
plot(TPR ~ FPR, data = dat, xlim = c(0,1), ylim = c(0,1), type = "n")
with(dat, lines(c(0, FPR, 1), c(0, TPR, 1), type = "o", pch = 25, bg = "black"))
text(TPR ~ FPR, data = dat, pos = 3, labels = dat$cutpoint)
abline(0, 1)
par(op)
To explain the code: The first plot() call sets up the plotting region, without doing an plotting at all. Note that I force the plot to cover the range (0,1) in both axes. The par() call tells R to plot axes that cover the range of the data - the default extends them by 4 percent of the range on each axis.
The next line, with(dat, lines(....)) draws the ROC curve and here we prepend and append the points at (0,0) and (1,1) to give the full curve. Here I use type = "o" to give both points and lines overplotted, the points are represented by character 25 which allows it to be filled with a colour, here black.
Then I add labels to the points using text(....); the pos argument is used to position the label away from the actual plotting coordinates. I take the labels from the cutpoint object in the data frame.
The abline() call draws the 1:1 line (here the 0, and 1 mean an intercept of 0 and a slope of 1 respectively.
The final line resets the plotting parameters to the defaults we saved in op prior to plotting (in the first line).
The resulting plot looks like this:
It isn't an exact facsimile and I prefer the plot using the default for the axis ranges(adding 4 percent):
plot(TPR ~ FPR, data = dat, xlim = c(0,1), ylim = c(0,1), type = "n")
with(dat, lines(c(0, FPR, 1), c(0, TPR, 1), type = "o", pch = 25, bg = "black"))
text(TPR ~ FPR, data = dat, pos = 3, labels = dat$cutpoint)
abline(0, 1)
Again, not a true facsimile but close.