I'm constructing a plot using bargraph.CI from sciplot. The x-axis represents a categorical variable, so the values of this variable are the names for the different positions on the x-axis. Unfortunately these names are long, so at default settings, some of them just disappear. I solved this problem by splitting them into multiple lines by injecting "\n" where needed. This basically worked, but because the names are now multi-line, they look too close to the x-axis. I need to move them farther away. How?
I know I can do this with mgp, but that affects the y-axis too.
I know I can set axisnames=FALSE in my call to barplot.CI, then use axis to create a separate x-axis. (In fact, I'm already doing that, but only to make the x-axis extend farther than it would by default- see my code below.) Then I could give the x-axis its own mgp parameter that would not affect the y-axis. But as far as I can tell, axis() is well set up for ordinal or continuous variables and doesn't seem to work great for categorical variables. After some fiddling, I couldn't get it to put the names in the right locations (i.e. right under their correspondence bars)
Finally, I tried using mgp.axis.labels from Hmisc to set ONLY the x-axis mgp, which is precisely what I want, but as far as I could tell it had no effect on anything.
Ideas? Here's my code.
ylim = c(0.5,0.8)
yticks = seq(ylim[1],ylim[2],0.1)
ylab = paste(100*yticks,"%",sep="")
bargraph.CI(
response = D$accuracy,
ylab = "% Accuracy on Test",
ylim = ylim,
x.factor = D$training,
xlab = "Training Condition",
axes = FALSE
)
axis(
side = 1,
pos = ylim[1],
at = c(0,7),
tick = TRUE,
labels = FALSE
)
axis(
side = 2,
tick = TRUE,
at = yticks,
labels = ylab,
las = 1
)
axis works fine with cateory but you should set the right ticks values and play with pos parameter for offset translation. Here I use xvals the return value of bargraph.CI to set àxis tick marks.
Here a reproducible example:
library(sciplot)
# I am using some sciplot data
dat <- ToothGrowth
### I create along labels
labels <- c('aaaaaaaaaa\naaaaaaaaaaa\nhhhhhhhhhhhhhhh',
'bbbbbbbbbb\nbbbbbbbbbbb\nhhhhhhhhhhhhhh',
'cccccccccc\nccccccccccc\ngdgdgdgdgd')
## I change factor labels
dat$dose <- factor(dat$dose,labels=labels)
ll <- bargraph.CI(x.factor = dose, response = len, data = dat,axisnames=FALSE)
## set at to xvals
axis(side=1,at=ll$xvals,labels=labels,pos=-2,tick=FALSE)
Related
"R version 4.0.3 (2020-10-10)"
I have a some time-series data where I am trying to look at the change in consumption over time. Here is a reprex.
set.seed(1)
date <- seq(as.POSIXct('2010-01-01'),as.POSIXct('2010-04-10'),by=86400)
Consumption <- rnorm(99)
Data <- data.frame(date,Consumption)
plot(Data$Consumption~Data$date,type='l') # X-axis labels and ticks not printing
par(yaxt='n')
axis(side = 2,at=seq(-3,3,0.5),labels = seq(-3,3,0.5)) # This works on the first plot on the y axis
plot(Data$date~Data$Consumption,type='l') # X-axis refusing to print despite assigning it.
par(xaxt='n')
axis(side = 1,at=seq(-3,3,0.5),labels = seq(-3,3,0.5)) # This works on the first plot
The graph outputted in the initial plot() is exactly what I want except for the fact it doesn't have any labels for the x-axis.
I am using Base Plotting for an assignment rather than everyday use and would usually use ggplot.
I have been trying to figure out why the x-axis is not plotting. Initially I thought the problem was with the date variable and tried cleaning it up with lubridate::ymd(). However when I started making the above reprex for the purpose of this question it is clear to the X-axis labels and ticks as whole is not printing. In the second plot I put the consumption variable on the x-axis. I was surprised to find that the date is printing neatly on its own on the Y-axis.
What am I doing wrong?
When you want more control over what happens with axis labels and title, you could create them manually. So, first produce a plot without title and label. Then, create them manually with axis() and mtext(). In the process, you may increase the room at the bottom of the plot with par(mar=...). Finetuning is done with arguments like las, cex.axis, and line. And at the end, you reset mar to its old values.
You could use the code below to get more detailed X-axis labels
### format to day (probably not the best way to do this)
Data$date2 <-format(Data$date, "%d%b")
Data$date2 <- as.Date(Data$date2, format = "%d%b")
### increase room at bottom of the plot for X-axis title
### the labels will eat up space, if we do nothing it will be a mess
### set title with mtext later
par(mar = c(7,4,4,2))
### plot without X-axis labels (xaxt) and title (xlab)
### work with "at" and "labels" in axis-function
### rotate labels 90° (las) an reduce font (cex.axis)
### put title 5 lines below graph (line)
###
### Remark: the graph window has to be big enough
plot(Data$Consumption ~ Data$date, type= "l", xaxt = "n", xlab = NA)
axis(side = 1, at = Data$date, labels = Data$date2, las = 2, cex.axis=.75)
mtext(side = 1, text = "Date", line = 5)
This yields the following graph:
ALTERNATIVE
ticks and labels for every 7th item
per7 <- seq(1, 99, 7)
plot(Data$Consumption ~ Data$date, type= "l", xaxt = "n", xlab = NA)
axis(side = 1, at = Data$date[per7], labels = Data$date2[per7], las = 2, cex.axis=.75)
mtext(side = 1, text = "Date", line = 5)
### reset mar
par(mar = c(5,4,4,2))
which gives the following picture:
Please let me know whether this is what you wanted.
There are two issues I can see readily:
change: Consumption <- rnorm(99) to Consumption <- rnorm(100) to
match date column.
The problem is with 'par'. When there are multiple plots within a chunk, unlike ggplot, plot does not handle properly. Remove par and run the below it should work
set.seed(1)
date <- seq(as.POSIXct('2010-01-01'),as.POSIXct('2010-04-10'),by=86400)
Consumption <- rnorm(100)
Data <- data.frame(date,Consumption)
plot(Data$Consumption~Data$date,type='l')
plot(Data$date~Data$Consumption,type='l')
Please note whenever you define par and when you run each plot in two different chunks, the labels will display properly. You will not have any issue. But when you plot both the charts in a single chunk, you will always have problem if you have par.
This is my first time using RStudio, and I've tried to find the the answer to my solution along with tinkering around with the code but I haven't been able to figure it out. I've even looked here and found other users with the same issue How to display all x labels in R barplot? and Rotating x axis labels in R for barplot but I wasn't able to get the answered solutions to work for my code. I've also asked two people more familiar with R, but after looking over my code and trying for themselves they were also unable to figure it out as well.
Everything would just result in error messages (I don't have the error messages showing in my console since when someone was trying to figure it out they cleared the global environment).
I have already generated 15 bars for my barplot, and the barplot itself is generated with the labels for the ylab, the title for my xlab, and the main, and I even have it colored, but I can't name each individual column bar.
The names for all the labels are listed in the source, but it's only showing every third bar or so. I need all bars labeled.
setwd("C:/Users/Person/Desktop/Rfolder")
library(readxl)
data <- read_excel("filename.xlsx")
View(topic)
barplot(data$'per hundred',
main ="Title",
xlab = "Words",
ylab = "variable stats",
col = c("gray"))
axis(1, at =c(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15),
srt = 45,
labels=c("Apple","Butter","Banana","Bacon","Candy","Carrot","Spam","Ube","Ice cream","Italian ice","Jackfruit","Kale","Tofu","Udon","All types"))
Coded Food Barplot
Use las=2 to set labels from horizontal to vertical.
This one works, with working example added:
df <- cbind.data.frame("names" = c("Apple","Butter","Banana","Bacon","Candy","Carrot","Spam","Ube","Ice cream","Italian ice","Jackfruit","Kale","Tofu","Udon","All types"), "numbers" = sample(20:100, 15))
barplot(height = df$numbers,
names.arg = df$names,
main = "Title",
xlab = "Words",
ylim = c(0, 100),
ylab = "variable stats",
col = c("gray"),
las = 2)
To modify sizing of x axis names and labels, add options:
cex.names = 1 # controls magnification of x axis names. value starts at 1
cex.lab = 1 # control magnification of x & y axis labels. value starts at 1
to the barplot() function. Play around with sizing to find what works for you best. To escape the overlap of x axis label and x axis names, instead of xlab = "Words" use sub = "Words". Simple & efficient.
Here's the barplot generated with all the labels.
Food Labeled Barplot
For certain functions it is convenient and commonplace to plot one dataset with two x axes. My example at hand is a function of the form f(T)=A*exp(-H/(R*T)), which is to be plotted with 1/T on the x axis and log(f) on the y axis. In this form, it conveniently appears as a straight line, but for ease of reading the actual temperature T instead of 1/T, it is common to put the corresponding T values on a second x axis in the same plot (which is then of course reversed and also not linearly spaced). How do I achieve this with Julia and Plotly?
Here is an example of the type of plot I try to make. For what it's worth, in Gnuplot the additional second (upper) x-axis would be created with set link x2 via 1./x inverse 1./x.
For adding a second Y axis there is a twinx() method in the Plots.jl package. Unfortunately there is not twiny() method that could allow adding a secondary X axis.
However, you can take twinx() code and simply transpose it:
function twiny(sp::Plots.Subplot)
sp[:top_margin] = max(sp[:top_margin], 30Plots.px)
plot!(sp.plt, inset = (sp[:subplot_index], bbox(0,0,1,1)))
twinsp = sp.plt.subplots[end]
twinsp[:xaxis][:mirror] = true
twinsp[:background_color_inside] = RGBA{Float64}(0,0,0,0)
Plots.link_axes!(sp[:yaxis], twinsp[:yaxis])
twinsp
end
twiny(plt::Plots.Plot = current()) = twiny(plt[1])
And now use it like this:
using Plots
plot(1:10,rand(10), label = "randData", ylabel = "Y axis",color = :red, legend = :topleft, grid = :off, xlabel = "Numbers Rand")
p = twiny()
plot!(p,5:15,log.(5:15), label = "log(x)", legend = :topright, box = :on, grid = :off, xlabel = "Log values")
I need help understanding why the bars print incorrectly when using barp() when I manually set the ylim argument.
Essentially, I have a vector of values (for the height argument) that contains only positive numeric values, but I want the barp() y-axis set to a y-axis limit of, say, [-100,500]. When I set a y-limit that that goes from negative to positive, barp() creates large bars that start from the very bottom, even though they should only start from the zero line.
I tried replicating the exact bars using the original barplot() function and I get exactly what I wanted. However, I prefer using the plotrix's barp() function because 1) it produces the zero line x axis (which I do not know how to do on barplot()) and 2) barp() allows me to plot bars and lines on the same graph while barplot() does not align added lines with the bars.
Below, I wrote out some code that produces the issue. The Figure 2 plot is the problematic one I want to call attention to. I want to create an image just like Figure 4, but using barp(). If that is not possible, I would settle for using barplot() if it were possible to create the x-axis line (where the zero line would be).
I tried reading the documentation but I did not catch anything that would explain this issue. Could someone assist me in identifying why one barplot function works but the other does not?
Thanks!
library(plotrix)
test.vector.mixed = as.numeric(c(-200,-20,50,170,200,250,200,220,205,230,350))
test.vector.positives = as.numeric(c(200,20,50,170,200,250,200,220,205,230,350))
months.index.abb = month.abb[c(8:12,1:6)]
# this displays correctly
barp(
test.vector.mixed,
names.arg = months.index.abb,
col = ifelse(test.vector.mixed < 0, "red", "#108a4c"),
main = "Figure 1: barp() test with Negatives",
ylim = c(-500,400)
)
# this does not display correctly
barp(
test.vector.positives,
names.arg = months.index.abb,
col = ifelse(test.vector.positives < 0, "red", "#108a4c"),
main = "Figure 2: barp() test with Positives",
ylim = c(-500,400)
)
# this displays correctly
barplot(
test.vector.mixed,
names.arg = months.index.abb,
col = ifelse(test.vector.mixed < 0, "red", "#108a4c"),
main = "Figure 3: barplot() test with Negatives",
ylim = c(-500,400)
)
# this is what I want barp() to do
barplot(
test.vector.positives,
names.arg = months.index.abb,
col = ifelse(test.vector.positives < 0, "red", "#108a4c"),
main = "Figure 4: barplot() test with positives",
ylim = c(-500,400)
)
When I manually add the following labels with (axis(1, at=1:27, labels=labs[0:27])):
> labs[0:27]
[1] "0\n9.3%" "1\n7.6%" "2\n5.6%" "3\n5.1%" "4\n5.7%" "5\n6.5%" "6\n7.3%" "7\n7.6%" "8\n7.5%" "9\n7%" "10\n6.2%" "11\n5.2%"
[13] "12\n4.2%" ........
I get the following:
How do I force all labels to be drawn so 1,3,5,6, and 11 are not skipped? (also, for extra credit, how do I shift the whole thing down a few pixels?)
If you want to force all labels to display, even when they are very close or overlapping, you can "trick" R into displaying them by adding odd and even axis labels with separate calls to the axis function, as follows:
labs <-c("0\n9.3%","1\n7.6%","2\n5.6%","3\n5.1%","4\n5.7%","5\n6.5%","6\n7.3%",
"7\n7.6%","8\n7.5%","9\n7%", "10\n6.2%","11\n5.2%","12\n4.2%",13:27)
n=length(labs)
plot(1:28, xaxt = "n")
axis(side=1, at=seq(1,n,2), labels=labs[seq(1,n,2)], cex.axis=0.6)
axis(side=1, at=seq(2,n,2), labels=labs[seq(2,n,2)], cex.axis=0.6)
You can play with cex.axis to get the text size that you want. Note, also, that you may have to adjust the number of values in at= and/or labels= so that they are equal.
I agree with #PLapointe and #joran that it's generally better not to tamper with R's default behavior regarding overlap. However, I've had a few cases where axis labels looked fine even when they weren't quite a full "m-width" apart, and I hit on the trick of alternating odd and even labels as a way to get the behavior I wanted.
?axis tells you that:
The code tries hard not to draw overlapping tick labels, and so will omit labels where they would abut or overlap previously drawn labels. This can result in, for example, every other tick being labelled. (The ticks are drawn left to right or bottom to top, and space at least the size of an ‘m’ is left between labels.)
Play with cex.axis so that labels are small enough to fit without overlapping
labs <-c("0\n9.3%","1\n7.6%","2\n5.6%","3\n5.1%","4\n5.7%","5\n6.5%","6\n7.3%",
"7\n7.6%","8\n7.5%","9\n7%", "10\n6.2%","11\n5.2%","12\n4.2%",12:27)
plot(1:27,xaxt = "n")
axis(side=1, at=1:27, labels=labs[0:27],cex.axis=0.35)
If you widen you graph (manually by dragging or programmatically), you can increase the size of your labels.
Although there are some good answers here, the OP didn't want to resize the labels or change anything about the plot besides fitting all of the axis labels. It's annoying, since often there appears to be plenty of room to fit all of the axis labels.
Here's another solution. Draw the plot without the axis, then add ticks with empty labels. Store the positions of the ticks in an object, so then you can go through each one and place it in the correct position on the axis.
plot(1:10, 1:10, yaxt = "n")
axis_ticks = axis(2, axTicks(2), labels = rep("", length(axTicks(2))))
for(i in axis_ticks) axis(2, i)
#PLapointe just posted what I was going to say, but omitted the bonus answer.
Set padj = 0.5 in axis to move the labels down slightly.
Perhaps draw and label one tick at a time, by calling axis repeatedly using mapply...
For example, consider the following data:
x = runif(100)*20
y = 10^(runif(100)*3)
The formula for y might look a bit odd; it gives random numbers distributed across three orders of magnitude such that the data will be evenly distributed on a plot where the y axis is on a log scale. This will help demonstrate the utility of axTicks() by calculating nice tick locations for us on a logged axis.
By default:
plot(x, y, log = "y")
returns:
Notice that 100 and 1000 labels are missing.
We can instead use:
plot(x, y, log = "y", yaxt = "n")
mapply(axis, side = 2, at = axTicks(2), labels = axTicks(2))
which calls axis() once for each tick location returned by axTicks(), thus plotting one tick at a time. The result:
What I like about this solution is that is uses only one line of code for drawing the axis, it prints exactly the default axis R would have made, except all ticks are labeled, and the labels don't go anywhere when the plot is resized:
I can't say the axis is useful in the resized example, but it makes the point about axis labels being permanent!
For the first (default) plot, note that R will recalculate tick locations when resizing.
For the second (always labeled) plot, the number and location of tick marks are not recalculated when the image is resized. The axis ticks calculated by axTicks depend upon the size of the display window when the plot is first drawn.
If you want want to force specific tick locations, try something like:
plot(x, y, log = "y", yaxt = "n")
mapply(axis, side = 2, at = c(1,10,100, 1000), labels = c("one", "ten", "hundred", "thousand"))
which yields:
axis() includes a gap.axis parameter that controls when labels are omitted. Setting this to a very negative number will force all labels to display, even if they overlap.
The padj parameter of axis() controls the y offset whilst plotting an individual axis.
par(mgp = c(3, 2, 0) will adjust the position of all axis labels for the duration of a plotting session: the second value (here 2, default 1) controls the position of the labels.
# Set axis text position, including for Y axis
par(mgp = c(3, 2, 0))
# Plot
plot(1:12, 1:12, log = 'x', ann = FALSE, axes = FALSE)
# Some numbers not plotted:
axis(1, 1:12)
# All numbers plotted, with manual offset
axis(1, 1:12, gap.axis = -100, padj = 0.5)
I had a similar problem where I wanted to stagger the labels and get them to print without losing some. I created two sets of ticks showing second set below the other to make it look as if it staggers.
xaxis_stagger = function(positions,labels) {
odd=labels[seq(1,length(labels),2)]
odd_pos=positions[seq(1,length(positions),2)]
even=labels[seq(2,length(labels),2)]
even_pos=positions[seq(2,length(positions),2)]
axis(side=1,at=odd_pos,labels=odd)
axis(side=1,at=even_pos,labels=even,padj=1.5)
}
So you give the positions where you want the ticks to be and the labels for those ticks and this would then re-organise it into two sets of axis and plot them on the original plot. Original plot would be done with xaxt="n".