Labels on grouped bars in barplot() [duplicate] - r

This question already has an answer here:
Show element values in barplot
(1 answer)
Closed 4 years ago.
I have a barplot with grouped bars. Is it possible to include a label for each bar ? Example of plot without bar labels:
test <- structure(c(0.431031856834624, 0.54498742364355, 0.495317895592119,0.341002949852507, 0.40229990800368, 0.328769657724329,0.258600583090379,0.343181818181818, 0.260619469026549), .Dim = c(3L, 3L), .Dimnames = list(
c("2015", "2016", "2017"), c("a", "b", "c")))
barplot(test,ylim=c(0,1),beside=T)

p <- barplot(test, ylim=c(0, 1), beside=T)
text(p, test + .05*sign(test), labels=format(round(test, digits=2), nsmall=2))
The last line adds the labeling over the bar plots.
p takes the return values of the barplot() which are the x-axis bar positions.
In this example this is of the format 3x3 matrix.
text() needs then p for his x= argument. And for his y= argument it needs a slightly offsetted value than its bar plot heights (test). sign() determines the direction (above or below, +1 or -1) of the bar and .05 I determined empirically by trying, it is dependent on your values of the table.
So, x= and y= are the x and y coordinates for the labeling.
And finally, labels= determines which text should be printed.
The combination of format() and round() gives you full control over how many digits you want to display and that the display is absolutely regular in turns of number of digits displayed, which is not, if you use only round().
With xpd=T you could determine, whether labeling is allowed to go outside of region or not.
cex= could determine the fontsize of the label,
col= the colouring and font= the font.
alternatively, you can give just test for y= and determine via pos=3 that it should be above and offset=1 how many characterwidths the offset of the text shoul be.
p <- barplot(test, ylim=c(0, 1), beside=T)
text(x=p, y=test, pos=3, offset=1, labels=format(round(test, digits=2), nsmall=2))
You can find plenty of more instructions by looking into the documentation by
?text
# and
?barplot
in the R console

You can add a label using text function by extending your barplot. You can play with the parameters as you wish. Here is the sample code and its output.
x= barplot(test,ylim=c(0,1),beside=T)
text(x, test, labels=test, pos=1, offset=.5, col="red", srt = 90) #srt is used for vertical labels
If you really want to make a better plot, I would recommend ggplot as it has several other features like adding a theme to your plots and it is more easy for customizations.

If you're looking to label ever bar's category not it's value you can do something like this
allPermutations <- unlist(lapply(colnames(test), function(x) paste(x, rownames(test)) ))
barplot(test,ylim=c(0,1),beside=T, names.arg = allPermutations, las=2)
the file line gets all the combinations of categories. The plot call allows you to specify individual values with "names.arg" while las=2 rotates the names so it shows a bit nicer

Related

Why is `ann=FALSE` not working in the boxplot call in R?

Trying to produce both a stripchart and a boxplot of the same (transformed) data but (because the boxplot is shifted down a tad) I don't want the axis labels twice:
set.seed(3121975)
bee = list(x1=rnbinom(50, mu = 4, size = .1),
x2=rnbinom(30,mu=6,size=.1),
x3=rnbinom(40,mu=2,size=.1))
f = function(x) asinh(sqrt(4*x+1.5))
stripchart(lapply(bee,f),method="stack",offset=.13,ylim=c(.8,3.9))
boxplot(lapply(bee,f),horizontal=TRUE,boxwex=.05,at=(1:3)-.1,add=TRUE,ann=FALSE)
Other things that don't work include: (i) leaving ann to take its default value of !add, (ii) specifying labels for ylab.
I presume I have missed something obvious but I am not seeing what it might be.
Just add yaxt = 'n' into boxplot() to suppress plotting of the y-axis. The argument ann controls axis titles and overall titles, not the axis itself.

Base Plot, correctly defining axis [duplicate]

How can I change the spacing of tick marks on the axis of a plot?
What parameters should I use with base plot or with rgl?
There are at least two ways for achieving this in base graph (my examples are for the x-axis, but work the same for the y-axis):
Use par(xaxp = c(x1, x2, n)) or plot(..., xaxp = c(x1, x2, n)) to define the position (x1 & x2) of the extreme tick marks and the number of intervals between the tick marks (n). Accordingly, n+1 is the number of tick marks drawn. (This works only if you use no logarithmic scale, for the behavior with logarithmic scales see ?par.)
You can suppress the drawing of the axis altogether and add the tick marks later with axis().
To suppress the drawing of the axis use plot(... , xaxt = "n").
Then call axis() with side, at, and labels: axis(side = 1, at = v1, labels = v2). With side referring to the side of the axis (1 = x-axis, 2 = y-axis), v1 being a vector containing the position of the ticks (e.g., c(1, 3, 5) if your axis ranges from 0 to 6 and you want three marks), and v2 a vector containing the labels for the specified tick marks (must be of same length as v1, e.g., c("group a", "group b", "group c")). See ?axis and my updated answer to a post on stats.stackexchange for an example of this method.
With base graphics, the easiest way is to stop the plotting functions from drawing axes and then draw them yourself.
plot(1:10, 1:10, axes = FALSE)
axis(side = 1, at = c(1,5,10))
axis(side = 2, at = c(1,3,7,10))
box()
I have a data set with Time as the x-axis, and Intensity as y-axis. I'd need to first delete all the default axes except the axes' labels with:
plot(Time,Intensity,axes=F)
Then I rebuild the plot's elements with:
box() # create a wrap around the points plotted
axis(labels=NA,side=1,tck=-0.015,at=c(seq(from=0,to=1000,by=100))) # labels = NA prevents the creation of the numbers and tick marks, tck is how long the tick mark is.
axis(labels=NA,side=2,tck=-0.015)
axis(lwd=0,side=1,line=-0.4,at=c(seq(from=0,to=1000,by=100))) # lwd option sets the tick mark to 0 length because tck already takes care of the mark
axis(lwd=0,line=-0.4,side=2,las=1) # las changes the direction of the number labels to horizontal instead of vertical.
So, at = c(...) specifies the collection of positions to put the tick marks. Here I'd like to put the marks at 0, 100, 200,..., 1000. seq(from =...,to =...,by =...) gives me the choice of limits and the increments.
And if you don't want R to add decimals or zeros, you can stop it from drawing the x axis or the y axis or both using ...axt. Then, you can add your own ticks and labels:
plot(x, y, xaxt="n")
plot(x, y, yaxt="n")
axis(1 or 2, at=c(1, 5, 10), labels=c("First", "Second", "Third"))
I just discovered the Hmisc package:
Contains many functions useful for data analysis, high-level graphics, utility operations, functions for computing sample size and power, importing and annotating datasets, imputing missing values, advanced table making, variable clustering, character string manipulation, conversion of R objects to LaTeX and html code, and recoding variables.
library(Hmisc)
plot(...)
minor.tick(nx=10, ny=10) # make minor tick marks (without labels) every 10th

axis length (not scaling!) in a scatter plot

How to change the axis length? for ex:
s <- data.table(school=rep(1:3,5), wave=c(rep(1,7), rep(2,8)), v1=rpois(15,10))
plot(s$wave,s$v2)
I get a scatter plot where the data is at the edges of the plot (a lot of white space in the graph). changing the xaxp values doesn't help (tried xaxp=c(-1, +2,4)) but nothing happened) and when I try to define it a factor I get a box plot. I know I can "squeeze" it when i save to .png but is there any other way?
I tried to upload pictures to convey the problem but I don't have enough reputation.
edit-thanks for whoever uploaded it (although the axis are reversed - wave is the x and V2 is the y). the thing is that there is a lot of "free space" between the 1st and the 2nd wave. the position is perfect when i define the wave a factor (it's centered and each factor is half the axis length) but it keeps giving me a box plot!
You can add a lot of values to your plot function, like colour, title, and also the limits of the axsis
Your code:
s <- data.frame(school=rep(1:3,5), wave=c(rep(1,7), rep(2,8)), v1=rpois(15,10))
plot(s$wave,s$v2)
And now just add some more:
plot(
x = s$wave,
y = s$v2,
col = "red",
main = "This is my title",
xlab = "the label of the x-axis",
ylab = "the label of the y-axis",
xlim = c(-5, 5), # the limits of the x-axis,
ylim = c(-4, 10) # the limits of the y-axis
)
You can add much more like size and type of the points ...
just as jlhoward mentioned
i found a function in the "lattice" package that does exactly what i want - a boxplot without the box.
the function is called stripplot.
http://www.math.ucla.edu/~anderson/rw1001/library/base/html/stripplot.html
thank you all for the help

plotmeans legends formatting

I am plotting means of grouped data and I'm having trouble getting the legends to be right. The text is so large that one can only see the names of two groups, not all four. I have spent a long time trying to use cex-like commands to change the size, but it doesn't work. I have tried rotating them with las=3, but it doesn't work.
I cannot share the data, but the code is here:
plot.question = function(number){
#which question to plot? get ID
question = names(sorted.by.n)[number]
#the formula
form = paste0("DF.scored.g.scale ~ ",question)
#fit it to data
fit = lm(form, DF.merged.g)
#get ANOVA results
fit.anova = anova(fit)
#get ANOVA p value
p.value = round(fit.anova[[5]][2],4) #p value
#plot it
plotmeans(as.formula(form), DF.merged.g,
ylab = "4 g-items sumscore",
xlab = "Answer",
main = paste0(questions.unique[question,"text"],"\nANOVA p=",p.value),
cex.main = .8,
cex.axis = .8,
cex.lab = .8,
cex.sub = .8,
las=3,) #size of main title
}
Preferably, I'd like to simply make the text smaller, so it can fit. Alternatively, I'd like to rotate it so it can fit (perhaps along with a margin change). If not what else?
One can suppress the legends with xaxt="n", but then one has to add them some other way. Can it really not be done within the plotmeans() function?
Well I tried many things and this was the only thing that worked. Apparently plotmeans() creates a plot that you cannot modify in any way. The only thing I was able to do is to overlay text as a new only-text-plot on top of the plotmeans plot.
myfactor <- factor(rep(c('cat1','cat2','cat3'),20)) #make a factor
mynum <- runif(60) #make a numeric field
plotmeans(mynum ~ myfactor,xaxt='n') #plot them
labs <- paste(names(table(myfactor)), "") #make the names
par(new=T) #create new plot
a<-rev(as.numeric(unique(myfactor))) #count the unique factors to make a vector of their numbers to serve as the positions on the x axis
text(cex=1, x=a, y=0.2, labs, xpd=TRUE, srt=35) #insert the text on the graph.
#here you need to modify y according to your data to find the best place to plot them.
#In my case x=c(1,2,3) because I have 3 categories and y=0.2
#because this is the lowest value of the y axis. The srt argument rotates the text.
You should probably be able to either fix the y axis to have standard values and then use the minimum of that number in the y argument of the text function to make a generic function, or calculate the min value of the y axis each time.
Hope that helps!

How can I make my vertical labels fit within my plotting window?

I'm creating a histogram in R which displays the frequency of several events in a vector. Each event is represented by an integer in the range [1, 9]. I'm displaying the label for each count vertically below the chart. Here's the code:
hist(vector, axes = FALSE, breaks = chartBreaks)
axis(1, at = tickMarks, labels = eventTypes, las = 2, tick = FALSE)
Unfortunately, the labels are too long, so they are cut off by the bottom of the window. How can I make them visible? Am I even using the right chart?
Look at help(par), in particular fields mar (for the margin) and oma (for outer margin).
It may be as simple as
par(mar=c(5,3,1,1)) # extra large bottom margin
hist(vector, axes = FALSE, breaks = chartBreaks)
axis(1, at = tickMarks, labels = eventTypes, las = 2, tick = FALSE)
This doesn't sound like a job for a histogram - the event is not a continuous variable. A barplot or dotplot may be more suitable.
Some dummy data
set.seed(123)
vec <- sample(1:9, 100, replace = TRUE)
vec <- factor(vec, labels = paste("My long event name", 1:9))
A barplot is produced via the barplot() function - we provide it the counts of each event using the table() function for convenience. Here we need to rotate labels using las = 2 and create some extra space of the labels in the margin
## lots of extra space in the margin for side 1
op <- par(mar = c(10,4,4,2) + 0.1)
barplot(table(vec), las = 2)
par(op) ## reset
A dotplot is produced via function dotchart() and has the added convenience of sorting out the plot margins for us
dotchart(table(vec))
The dotplot has the advantage over the barplot of using much less ink to display the same information and focuses on the differences in counts across groups rather than the magnitudes of the counts.
Note how I've set the data up as a factor. This allows us to store the event labels as the labels for the factor - thus automating the labelling of the axes in the plots. It also is a natural way of storing data like I understand you to have.
Perhaps adding \n into your labels so they will wrap onto 2 lines? It's not optimal, but it may work.
You might want to look at this post from Cross Validated

Resources