I'm creating a stick plot using the "plotSticks" function in the oce library. The arrows that are created in the stick plot often go beyond the limits of the x axis. In my case, the x axis is usually a date range that I can't adjust to fit the data.
As you can see in this plot:
library(oce)
x<-1:10
u<-c(0,1,2,3,4,5,6,7,8,9)
v<-c(0,1,2,1,2,3,2,1,2,3)
plotSticks(x,0,u,v)
The ends of the arrows go beyond the limits of my x axis. Is there a way to include some space on either side of the x axis so that the tips of the arrows can be seen?
The plotSticks has a yscale function that can reduce the size of the arrows, but then the arrows do not match the y axis scale so I'd like to avoid using the yscale fix.
Will this get you the result you want? It extends the x axis and uses the yscale argument.
library(oce)
x<-1:10
u<-c(0,1,2,3,4,5,6,7,8,9)
v<-c(0,1,2,1,2,3,2,1,2,3)
plotSticks(x,0,u,v, xlim=c(0, 40), ylim=c(-.5, 4), yscale=2)
Related
Here is shown how to label histogram bars with data values or percents using labels = TRUE. Is it also possible to rotate those labels? My goal is to rotate them to 90 degrees because now the labels over bars overrides each other and it is unreadable.
PS: please note that my goal is not to rotate y-axis labels as it is shown e.g. here
Using mtcars, here's one brute-force solution (though it isn't very brutish):
h <- hist(mtcars$mpg)
maxh <- max(h$counts)
strh <- strheight('W')
strw <- strwidth(max(h$counts))
hist(mtcars$mpg, ylim=c(0, maxh + strh + strw))
text(h$mids, strh + h$counts, labels=h$counts, adj=c(0, 0.5), srt=90)
The srt=90 is the key here, rotating 90 degrees counter-clockwise (anti-clockwise?).
maxh, strh, and strw are used (1) to determine how much to extend the y-axis so that the text is not clipped to the visible figure, and (2) to provide a small pad between the bar and the start of the rotated text. (The first reason could be mitigated by xpd=TRUE instead, but it might impinge on the main title, and will be a factor if you set the top margin to 0.)
Note: if using density instead of frequency, you should use h$density instead of h$counts.
Edit: changed adj, I always forget the x/y axes on it stay relative to the text regardless of rotation.
Edit #2: changing the first call to hist so the string height/width are calculate-able. Unfortunately, plotting twice is required in order to know the actual height/width.
Lets say I want to have a plot and lose the box in R. But still I would need a scale bar so one can understand the scaling. I didn't find a solution.
plot(1,1, type="n", xlim=c(0,5), ylim=c(0,5))
When I use the scalebar function from the raster package, the scaling is not right:
require(raster)
scalebar(1)
The added scalebar is too short to represent 1 in the x axis.
I tried to find something else, but most scalebar functions are related to maps.
edit:
So what I want is something like this:
plot(1,1, type="n", xlim=c(0,5), ylim=c(0,5)
, yaxt="n",
xaxt="n", frame.plot=F, ann=F
# adding a blank plot without the axes
)
#adding some simple function
x=c(1:5)
y=x*x
lines(x=x, y=y)
#defining where the scale bar should appear
lines(x=c(4,5), y=c(5,5))
#placing the text right under the line
text(x=4.5, y=5, pos=1, label="1 km")
Is there an easier way to do something like this?
There might be a function that does what you want, but you can also create your own function that will hopefully serve well enough. See below for one possibility. You can of course adjust the function settings to get the positioning you want. In particular, I've included yadj as an argument to the function, with a default value of 1.5. You can change this if the scalebar label isn't positioned properly under the scale line.
If the x-axis spans a larger range than the values used below, you'll want to adjust the x-coordinates of the scale line so that it spans 10, 100, etc. x-units, as the case may be. If you want to get fancy, you can have the function itself determine how many x-units to span, based on the x-range of the plot and then use the magnitude of that span in the units label.
# Function to add a scalebar to a base-graphics plot
myScalebar = function(units_label, yadj=1.5) {
# Get plot coordinates
pc = par("usr")
# Position scale line between last two major x-axis tick marks
# and 1/10th of the total y-range above the lower y-axis coordinate
lines(c(floor(pc[2]-1),floor(pc[2])),
rep(pc[3] + 0.1*(pc[4] - pc[3]), 2))
# Place the units label at the midpoint of and just below the scale line
text(x=mean(c(floor(pc[2]-1), floor(pc[2]))),
y=pc[3] + 0.1*(pc[4] - pc[3]),
label=units_label, adj=c(0.5, yadj))
}
# Now redo your plot
# Start with blank plot
plot(1,1, type="n", xlim=c(0,5), ylim=c(0,5),
yaxt="n", xaxt="n", frame.plot=F, ann=F)
# Add a simple function
x=c(1:5)
y=x*x
lines(x=x, y=y)
# Add scalebar
myScalebar("1 km")
I usually use this sort of function that allows for lots of flexibility across plots. I have expanded the variables names to help with debugging. Please note: this is designed to work with raster converted to utms only (don't use geographic projections).
ScaleBar <- function(reference_raster_utm, round_to_nearest_km, width_percent, y_percent_from_bottom, x_percent_from_left, y_text_percent_from_bottom, ...) {
# Round by max to nearest... e.g. 5 km
mround <- function(x,base){
base*round(x/base)
}
# scale bar size adjustment to avoid decimals
scale_size <- ((xmax(reference_raster_utm)-xmin(reference_raster_utm))*width_percent)/1000
scale_size_adj <- mround(scale_size, round_to_nearest_km)
scale_size_adj_plot <- (scale_size_adj*1000)/2
# Horizontal percent position (x) for scale bar
x_position <- ((xmax(reference_raster_utm)-xmin(reference_raster_utm))*x_percent_from_left)+xmin(reference_raster_utm)
# Vertical percent position y for scale bar
y_position <- ((ymax(reference_raster_utm)-ymin(reference_raster_utm))*y_percent_from_bottom)+ymin(reference_raster_utm)
y_position_text <- ((ymax(reference_raster_utm)-ymin(reference_raster_utm))*y_text_percent_from_bottom)+ymin(reference_raster_utm)
# Draw line on plot
library(sp)
x_ends <- c((x_position-scale_size_adj_plot), (x_position+scale_size_adj_plot))
y_ends <- c((y_position), (y_position))
scale_bar_line <- SpatialLines(list(Lines(Line(cbind(x_ends, y_ends)), ID="length")))
projection(scale_bar_line) <- projection(reference_raster_utm)
plot(scale_bar_line, add=TRUE, ...)
text(x_position, y_position_text, paste0(scale_size_adj, "km"))
}
Arguments:
reference_raster_utm: One of your personal raster files to source extent/projection from.
round_to_nearest_km: round to nearest kilometre e.g. max out on 2km, 5km ect.
width_percent: percent of plot width that the scale bar should cover (e.g. big 50% small 10%).
y_percent_from_bottom: vertical position from bottom. 0% at bottom, 100% at top, 50% in the middle.
x_percent_from_left: horizontal position from left. 0% at left, 100% at right, 50% in the middle.
y_text_percent_from_bottom: same as y_percent_from_bottom but for text.
Example:
plot(my_raster)
ScaleBar(reference_raster_utm=my_raster, round_to_nearest_km=5, width_percent=0.25, y_percent_from_bottom=0.10, x_percent_from_left=0.50, y_text_percent_from_bottom=0.07, lwd=2)
How can I use R to make axes always square in scatter plots? for example in:
> plot(iris)
or
> plot(iris$Petal.Width, iris$Petal.Length)
I'd like the axes to be square, i.e. the same length and tick labels for the x and y axes.
The current proposed answer does not work: the call,
plot(iris$Petal.Width, iris$Petal.Length, xlim=c(0,10), ylim=c(0,10), asp=1)
Generates:
which is not square, and does not have the same axis ticks and tick labels. The spaces between the x tick labels must be the same and the plot should be square, not rectangular.
You need to also set pty="s" in the graphics parameters to make the plot region square (independent of device size and limits):
par(pty="s")
plot(iris$Petal.Width, iris$Petal.Length, asp=1)
lines(2+c(0,1,1,0,0),3+c(0,0,1,1,0)) # confirm square visually
First of all, for me the plot already comes out square (big image). Clearly for you this is not the case, and you might need to make plots larger than the screen anyhow.
So, the size of the plot is controlled by the size of the output area, ie the plot window, the image file, or whatever else. Using Rstudio, you can use the built-in GUI the specify plot size. If you insist on using the base R console, you'll need to manually do the exporting. First open the file:
png("image.png", width=600, height=600)
This will open an image file in the working directory with equal proportions. Now plot:
x = iris$Petal.Width
y = iris$Petal.Length
all = c(x,y)
range = c(min(all), max(all))
plot(x, y, xlim=range, ylim=range)
And close the file:
dev.off()
The result:
I have a problem in plotting uneven scale plot on x axis with R
Here is an example:
plot(1:100,1:100)
will give the equal tick space on x axis.
However, I want to show the graph with first half of space showing 1 to 10, and the left half space showing 10 to 100, so the points in the 10 to 100 more dense, and points in 1:10 are easier to see. How to do it with R?
Like this:
This is not an easy one-off task to complete. You'll actually need to transform to the scaled data and supply custom tick marked axes. Any reason you haven't considered simply logging the x-axis instead? (supplying the option plot(x, y, log='x') will do that).
What I think you've described is this:
xnew <- ifelse(x<10, x, x/10)
plot(xnew, y, axes=FALSE, xlab='x')
axis(1, at=c(0, 10, 20), labels=c(0, 10, 100))
axis(2)
box()
You could log the x axis:
x<-1:100
y<-1:100
plot(log(x,base=10),y,axes=F)
axis(2)
axis(1,at=0:2,labels=10^(0:2))
For a logarithmic axis, use:
plot(x,y,log="x") ## specifies which axis to put on log scale
For determining how many "tick marks" to use, check
par()$lab
Default is 5,5,7. To put more x axis labels, do
par(lab=c(10,5,7))
And for y:
par(lab=c(5,10,7))
Is it possible to add more than one x-axis to a plot in R? And to put an annotation next to each scale?
Edit > here's the result of Nick Sabbe idea. For the annotation (a little text at the left of each axis), is it possible ?
You can use the line argument of axis() to place an axis higher or lower, this way you can make multiple axes. With mtext() you can then add a label to the side. Do note that the plot itself is only on one scale so you need to rescale the points and labels of the other scale accordingly:
# Plot and first axis:
plot(1:10,1:10,bty="n",col="red",pch=16,axes=FALSE,xlab="",ylab="")
axis(2,0:11,las=1)
axis(1,0:11,line=1,col="red",col.ticks="red",col.axis="red")
mtext("Label 1",1,line=1,at=0.2,col="red")
# Secondary points and axis:
points(rnorm(10,50,20)/10, rnorm(10,5,2),pch=16, col="blue" )
axis(1,0:11,labels=0:11*10,line=3,col="blue",col.ticks="blue",col.axis="blue")
mtext("Label 2",1,line=3,at=0.2,col="blue")
You can use ?axis for that. Parameter at is in the scale of the original axis of the plot, and you can pass labels to show other values.
You have to scale the axess labels yourself, though.
A very simple/silly example:
plot(1:10,1:10)
axis(side=4, at=c(3,7), labels=c(30,70))
Finally, note that most people consider adding multiple axes to a plot bad form...