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)
)
Related
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
Occasionally hist(..., nclass=nclass.scott) produces a histogram where the maximum bar extends over the top of the y axis. You may try this example a few times:
x <- sample(1000000, 500, replace=TRUE)
h <- hist(x,nclass=nclass.scott)
text(x=h$mids, y=h$counts, labels=h$counts, pos=3, col="red")
Example:
Occasionally the red number over the highest bar cannot be presented as it seems to be clipped by the plot region. I could add ylim=..., but it's quite tricky to get the maximum height of the bar.
Even when knowing the maximum height, ylim=(0, max) has the problem that max may be ignored: For example, when maximum is 527, then the upper displayed y-axis label is 500, even if ylim=(0, 527) is specified. When using 600 instead, it works, but then the y-axis is a bit too long...
If that is not a bug of R (3.3.3), what is an elegant (minimalistic) solution?
I think you need to set par(xpd= T) in your graph to avoid the trimming.
?par
xpd
A logical value or NA. If FALSE, all plotting is clipped to the
plot region, if TRUE, all plotting is clipped to the figure region,
and if NA, all plotting is clipped to the device region. See also
clip.
You can do it better by collaborating with usr option and xpd.Upon observation the bars seems going out of chart but it is not the bars that are going outside the chart but the axis being restricted to the labels. Hence to fix the labels we can choose to use usr. In case someone wants to play with the margin, one can also use mar.
library(RColorBrewer)
par(mfrow=c(1,1),xpd=T,yaxs="i")
x <- sample(1000000, 500, replace=TRUE)
h <- hist(x,nclass=nclass.scott,axes=FALSE,col=brewer.pal(10,"Set3"))
# usr <- par("usr")
at <- c(0, 10,30, par("usr")[4])
axis(2,at=at,labels = round(at))
text(x=h$mids, y=h$counts, labels=h$counts, pos=3, col="red")
usr
A vector of the form c(x1, x2, y1, y2) giving the extremes of the
user coordinates of the plotting region. When a logarithmic scale is
in use (i.e., par("xlog") is true, see below), then the x-limits will
be 10 ^ par("usr")[1:2]. Similarly for the y-axis.
You may want to run it several times, I have run it for many times, the bar won't seems to go outside the chart now.
Output:
What you describe is not a bug. You are using functionality to draw a histogram and then you want to add text to it. The function has not been designed for that, hence you need to reserve some additional white space for the text.
I suggest you run the function once, to get the "base values" of the graph. Then run the function again with adjusted scale (extra space for the text). In order to achieve this, you could use the following code
set.seed(9876) ### for reproducibility
x <- sample(1000000, 500, replace = TRUE)
h <- hist(x, nclass = nclass.scott, plot = FALSE)
### use the info from the previous call to adjust the y-scale with a constant
hist(x, nclass = nclass.scott, ylim = c(0, max(h$counts) + 10))
text(x = h$mids, y = h$counts, labels = h$counts, pos = 3, col = "red")
### ... or add a proportion (a little bit more robust)
hist(x, nclass = nclass.scott, ylim = c(0, max(h$counts) * 1.075))
text(x = h$mids, y = h$counts, labels = h$counts, pos = 3, col = "red")
Please let me know whether this is what you want.
I'm working on an R plot that will be separated into two parts by a vertical line I'm creating with abline().I would like that abline to go over the boundaries of my plot on one side only.
I found this helpful post about setting par(xpd=). However, I could not figure out how to get this command make the line go over the plot border on one side only, as outlined in the screenshot below.
Is there a way to do this in base R? (Or do I have to rebuild the
whole thing in ggplot etc?)
I feel that the key might the differences between figure region,
device region, etc. but was unable to really determine the difference
between those. I looked at this
source
but at least for my plot those settings seem to amount to the same
thing - it would be cool if anyone had some help here as well!
Here's my approach:
plot(100, 100)
par(xpd=TRUE)
abline(v=70, lty=3)
And what I get / want:
Any help is appreciated!
Use lines. Set limits of y as you want.
plot(100, 100)
par(xpd = TRUE)
lines(x = c(70,70), y = c(45, par('usr')[4]), lty = 2)
# par('usr') gives the 4 extremes of plot
# par('usr')[4] gives the extreme on top
Another option is to not mess with xpd and use axis instead. Use tck to define the length of line beyond the plot. tck = -0.25 means the length is one fourth of the plot height towards bottom.
plot(100, 100)
par(xpd = FALSE) #Only because we made TRUE above
abline(v = 70, lty = 2)
axis(1, at = 70, labels = NA, tck = -0.25, lty = 2)
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)
How can I go about removing the box around an xyplot, while keeping the axis scale tick marks? In the spirit of Edward Tufte's minimalist data graphic aesthetic, these axis lines are "non-data ink," and can (should?) be "erased."
library(lattice)
my.df <- data.frame(x=-10:10)
my.df$y <- my.df$x^2
xyplot(y~x,data=my.df)
It seems that the trellis display parameters (e.g. axis.line$col) control both the axis lines and axis ticks together:
xyplot(y~x,data=my.df,
par.settings=list(axis.line=list(col="transparent")))
...which is not the desired result, so it doesn't look like there's a simple way to turn off the lines while leaving the box.
The best I've been able to come up with is a brute-force hack, where I build the tick marks by hand using panel.segments:
at.x=pretty(c(-10,10))
at.y=pretty(c(0,100))
xyplot(y~x,data=my.df,
par.settings=list(axis.line=list(col="transparent")),
scales=list(x=list(at=at.x,labels=at.x),
y=list(at=at.y,labels=at.y)),
panel=function(...){
panel.xyplot(...)
panel.segments(x0=at.x,x1=at.x,y0=-4,y1=-2)
panel.segments(x0=-11.5,x1=-11,y0=at.y,y1=at.y)
}
)
This is close to the desired result, but there's quite a bit of fiddling required to get the tick marks to be a reasonable length and offset a "nice" distance from the data points. These values won't translate from one graphic to the next. Plus, note that the axis labels are now padded too far from the tick marks. I'm sure there's a way to reduce that padding, but that would only make the code even uglier and less portable.
So how can one go about suppressing just the lines that make up the "box" around the plot area, while leaving the tick marks and axis labels intact? Bonus points if this approach could also be used to suppress some, but not all of the lines (e.g. leave the left and lower lines, but suppress the top and right lines).
This is still a bit hacky, but at least you don't have to do any figuring by hand. It uses a combination of par.settings and a custom axis function that takes an argument line.col and temporarily changes the axis line color by a call to trellis.par.set:
EDIT (removed unnecessary changing of trellis settings)
xyplot(y~x,data=my.df, par.settings = list(axis.line = list(col = "transparent")),
# Pass custom axis function to argument 'axis'
axis = function(side, line.col = "black", ...) {
# Only draw axes on the left and bottom
if(side %in% c("left","bottom")) {
# Call default axis drawing function
axis.default(side = side, line.col = "black", ...)
}
}
)
At the moment, I chalk up why line.col = "black" is required in the arguments of the custom axis function to magic. My guess is that it has to do with argument matching with the ellipses (...). Perhaps I'll be wiser tomorrow and find the true reason.
This results in:
The easiest thing to do is to use the custom axis function (axis). Just set lwd (line width) to zero and tick marks (lwd.ticks) to something else. It worked like a charm!
plot(NA,NA,type="n",xaxt="n", lwd=linewidth, xlim=c(1,24), xlab="", ylab="",ylim=c(-300,500))
axis(side = 4, tck = .05, **lwd=0, lwd.ticks=1**, line = 0, labels = NA, col= cols_border[1], col.axis = cols_black)
axis(side = 4, lwd = 0, line = -4.5, las = 1, cex.axis=axis_fontsize, col= cols_border[1], col.axis = cols_black)
mtext("Light deviations (lum/sec)",side=4, padj=-2.5, cex=title_fontsize, col="black")