ggplot: boxplot number of observations as x-axis labels - r

I have successfully created a very nice boxplot (for my purposes) categorized by a factor and binned, according to the answer in my previous post here:
ggplot: arranging boxplots of multiple y-variables for each group of a continuous x
Now, I would like to customize the x-axis labels according to the number of observations in each boxplot.
require (ggplot2)
require (plyr)
library(reshape2)
set.seed(1234)
x<- rnorm(100)
y.1<-rnorm(100)
y.2<-rnorm(100)
y.3<-rnorm(100)
y.4<-rnorm(100)
df<- (as.data.frame(cbind(x,y.1,y.2,y.3,y.4)))
dfmelt<-melt(df, measure.vars = 2:5)
dfmelt$bin <- factor(round_any(dfmelt$x,0.5))
dfmelt.sum<-summary(dfmelt$bin)
ggplot(dfmelt, aes(x=bin, y=value, fill=variable))+
geom_boxplot()+
facet_grid(.~bin, scales="free")+
labs(x="number of observations")+
scale_x_discrete(labels= dfmelt.sum)
dfmelt.sum only gives me the total number of observations for each bin not for each boxplot.
Boxplots statistics give me the number of observations for each boxplot.
dfmelt.stat<-boxplot(value~variable+bin, data=dfmelt)
dfmelt.n<-dfmelt.stat$n
But how do I add tick marks and labels for each boxplot?
Thanks, Sina
UPDATE
I have continued working on this. The biggest problem is that in the code above, only one tick mark is provided per facet. Since I also wanted to plot the means for each boxplot, I have used interaction to plot each boxplot individually, which also adds tick marks on the x-axis for each boxplot:
require (ggplot2)
require (plyr)
library(reshape2)
set.seed(1234) x<- rnorm(100)
y.1<-rnorm(100)
y.2<-rnorm(100)
y.3<-rnorm(100)
y.4<-rnorm(100)
df<- (as.data.frame(cbind(x,y.1,y.2,y.3,y.4))) dfmelt<-melt(df, measure.vars = 2:5)
dfmelt$bin <- factor(round_any(dfmelt$x,0.5))
dfmelt$f2f1<-interaction(dfmelt$variable,dfmelt$bin)
dfmelt_mean<-aggregate(value~variable*bin, data=dfmelt, FUN=mean)
dfmelt_mean$f2f1<-interaction(dfmelt_mean$variable, dfmelt_mean$bin)
dfmelt_length<-aggregate(value~variable*bin, data=dfmelt, FUN=length)
dfmelt_length$f2f1<-interaction(dfmelt_length$variable, dfmelt_length$bin)
On the side: maybe there is a more elegant way to combine all those interactions. I'd be happy to improve.
ggplot(aes(y = value, x = f2f1, fill=variable), data = dfmelt)+
geom_boxplot()+
geom_point(aes(x=f2f1, y=value),data=dfmelt_mean, color="red", shape=3)+
facet_grid(.~bin, scales="free")+
labs(x="number of observations")+
scale_x_discrete(labels=dfmelt_length$value)
This gives me tick marks on for each boxplot which can be potentially labeled. However, using labels in scale_x_discrete only repeats the first four values of dfmelt_length$value in each facet.
How can that be circumvented?
Thanks, Sina

look at this answer, It is not on the label but it works - I have used this
Modify x-axis labels in each facet
You can also do as follows, I also have used that
library(ggplot2)
df <- data.frame(group=sample(c("a","b","c"),100,replace=T),x=rnorm(100),y=rnorm(100)*rnorm(100))
xlabs <- paste(levels(df$group),"\n(N=",table(df$group),")",sep="")
ggplot(df,aes(x=group,y=x,color=group))+geom_boxplot()+scale_x_discrete(labels=xlabs)
This also works
library(ggplot2)
library(reshape2)
df <- data.frame(group=sample(c("a","b","c"),100,replace=T),x=rnorm(100),y=rnorm(100)*rnorm(100))
df1 <- melt(df)
df2 <- ddply(df1,.(group,variable),transform,N=length(group))
df2$label <- paste0(df2$group,"\n","(n=",df2$N,")")
ggplot(df2,aes(x=label,y=value,color=group))+geom_boxplot()+facet_grid(.~variable)

Related

ggplot: How to increase space between axis labels for categorical data?

I love ggplot, but find it hard to customize some elements such as X axis labels and grid lines. The title of the question says it all, but here's a reproducible example to go with it:
Reproducible example
library(ggplot2)
library(dplyr)
# Make a dataset
set.seed(123)
x1 <- c('2015_46','2015_47','2015_48','2015_49'
,'2015_50','2015_51','2015_52','2016_01',
'2016_02','2016_03')
y1 <- runif(10,0.0,1.0)
y2 <- runif(10,0.5,2.0)
# Make the dataset ggplot friendly
df_wide <- data.table(x1, y1, y2)
df_long <- melt(df_wide, id = 'x1')
# Plot it
p <- ggplot(df_long, aes(x=x1,
y=value,
group=variable,
colour=variable )) + geom_line(size=1)
plot(p)
# Now, plot the same thing with the same lines and numbers,
# but with increased space between x-axis labels
# and / or space between x-axis grid lines.
Plot1
The plot looks like this, and doesn't look too bad in it's current form:
Plot2
The problem occurs when the dataset gets bigger, and the labels on the x-axis start overlapping each other like this:
What I've tried so far:
I've made several attempts using scale_x_discrete as suggested here, but I've had no luck so far. What really bugs me is that I saw some tutorial about these things a while back, but despite two days of intense googling I just can't find it. I'm going to update this section when I try new things.
I'm looking forward to your suggestions!
As mentioned above, assuming that x1 represents a year_day, ggplot provides sensible defaults for date scales.
First make x1 into a valid date format, then plot as you already did:
df_long$x1 <- strptime(as.character(df_long$x1), format="%Y_%j")
ggplot(df_long, aes(x=x1, y=value, group=variable, colour=variable)) +
geom_line(size=1)
The plot looks a little odd because of the disconnected time series, but scales_x_date() provides an easy way to customize the axis:
http://docs.ggplot2.org/current/scale_date.html

Creating barplot with standard errors plotted in R

I am trying to find the best way to create barplots in R with standard errors displayed. I have seen other articles but I cannot figure out the code to use with my own data (having not used ggplot before and this seeming to be the most used way and barplot not cooperating with dataframes). I need to use this in two cases for which I have created two example dataframes:
Plot df1 so that the x-axis has sites a-c, with the y-axis displaying the mean value for V1 and the standard errors highlighted, similar to this example with a grey colour. Here, plant biomass should the mean V1 value and treatments should be each of my sites.
Plot df2 in the same way, but so that before and after are located next to each other in a similar way to this, so pre-test and post-test equate to before and after in my example.
x <- factor(LETTERS[1:3])
site <- rep(x, each = 8)
values <- as.data.frame(matrix(sample(0:10, 3*8, replace=TRUE), ncol=1))
df1 <- cbind(site,values)
z <- factor(c("Before","After"))
when <- rep(z, each = 4)
df2 <- data.frame(when,df1)
Apologies for the simplicity for more experienced R users and particuarly those that use ggplot but I cannot apply snippets of code that I have found elsewhere to my data. I cannot even get enough code together to produce a start to a graph so I hope my descriptions are sufficient. Thank you in advance.
Something like this?
library(ggplot2)
get.se <- function(y) {
se <- sd(y)/sqrt(length(y))
mu <- mean(y)
c(ymin=mu-se, ymax=mu+se)
}
ggplot(df1, aes(x=site, y=V1)) +
stat_summary(fun.y=mean, geom="bar", fill="lightgreen", color="grey70")+
stat_summary(fun.data=get.se, geom="errorbar", width=0.1)
ggplot(df2, aes(x=site, y=V1, fill=when)) +
stat_summary(fun.y=mean, geom="bar", position="dodge", color="grey70")+
stat_summary(fun.data=get.se, geom="errorbar", width=0.1, position=position_dodge(width=0.9))
So this takes advantage of the stat_summary(...) function in ggplot to, first, summarize y for given x using mean(...) (for the bars), and then to summarize y for given x using the get.se(...) function for the error-bars. Another option would be to summarize your data prior to using ggplot, and then use geom_bar(...) and geom_errorbar(...).
Also, plotting +/- 1 se is not a great practice (although it's used often enough). You'd be better served plotting legitimate confidence limits, which you could do, for instance, using the built-in mean_cl_normal function instead of the contrived get.se(...). mean_cl_normal returns the 95% confidence limits based on the assumption that the data is normally distributed (or you can set the CL to something else; read the documentation).
I used group_by and summarise_each function for this and std.error function from package plotrix
library(plotrix) # for std error function
library(dplyr) # for group_by and summarise_each function
library(ggplot2) # for creating ggplot
For df1 plot
# Group data by when and site
grouped_df1<-group_by(df1,site)
#summarise grouped data and calculate mean and standard error using function mean and std.error(from plotrix)
summarised_df1<-summarise_each(grouped_df1,funs(mean=mean,std_error=std.error))
# Define the top and bottom of the errorbars
limits <- aes(ymax = mean + std_error, ymin=mean-std_error)
#Begin your ggplot
#Here we are plotting site vs mean and filling by another factor variable when
g<-ggplot(summarised_df1,aes(site,mean))
#Creating bar to show the factor variable position_dodge
#ensures side by side creation of factor bars
g<-g+geom_bar(stat = "identity",position = position_dodge())
#creation of error bar
g<-g+geom_errorbar(limits,width=0.25,position = position_dodge(width = 0.9))
#print graph
g
For df2 plot
# Group data by when and site
grouped_df2<-group_by(df2,when,site)
#summarise grouped data and calculate mean and standard error using function mean and std.error
summarised_df2<-summarise_each(grouped_df2,funs(mean=mean,std_error=std.error))
# Define the top and bottom of the errorbars
limits <- aes(ymax = mean + std_error, ymin=mean-std_error)
#Begin your ggplot
#Here we are plotting site vs mean and filling by another factor variable when
g<-ggplot(summarised_df2,aes(site,mean,fill=when))
#Creating bar to show the factor variable position_dodge
#ensures side by side creation of factor bars
g<-g+geom_bar(stat = "identity",position = position_dodge())
#creation of error bar
g<-g+geom_errorbar(limits,width=0.25,position = position_dodge(width = 0.9))
#print graph
g

Align X axis of scatterplot and boxplot

I'm superimposing two images in R. One image is a boxplot (using boxplot()), the other a scatterplot (using scatterplot()). I noticed a discrepancy in the scale along the x-axis. (A) is the boxplot scale. (B) is for the scatterplot.
What I've been trying to do is re-scale (B) to suit (A). I note there is a condition called xlim in scatterplot. Tried it, didn't work. I've also noted this example came up as I was typing out the question: Change Axis Label - R scatterplot.
Tried it, didn't work.
How can I modify the x-axis to change the scale from 1.0, 1.5, 2.0, 2.5, 3.0 to simply 1,2,3.
In Stata, I'm aware you can specify the x-axis range, and then indicate the step-ups between. For example, the range may be 0-100, and each measurable point would be set to 10. So you'd end up with 10, 20,....,100.
My R code, as it stands, looks something like this:
library(car)
boxplot(a,b,c)
par(new=T)
scatterplot(x, y, smooth=TRUE, boxplots=FALSE)
I've tried modifying scatterplot as such without any success:
scatterplot(x, y, smooth=TRUE, boxplots=FALSE, xlim=c(1,3))
As mentioned in comments use as.factor, then xaxis should align. Here is ggplot solution:
#dummy data
dat1 <- data.frame(group=as.factor(rep(1:3,4)),
var=c(runif(12)))
dat2 <- data.frame(x=as.factor(1:3),y=runif(3))
library(ggplot2)
library(grid)
library(gridExtra)
#plot points on top of boxplot
ggplot(dat1,aes(group,var)) +
geom_boxplot() +
geom_point(aes(x,y),dat2)
Plot as separate plots
gg_boxplot <-
ggplot(dat1,aes(group,var)) +
geom_boxplot()
gg_point <-
ggplot(dat2,aes(x,y)) +
geom_point()
grid.arrange(gg_boxplot,gg_point,
ncol=1,
main="Plotting is easier with ggplot")
EDIT
Using xlim as suggested by #RuthgerRighart
#dummy data - no factors
dat1 <- data.frame(group=rep(1:3,4),
var=c(runif(12)))
dat2 <- data.frame(x=1:3,y=runif(3))
par(mfrow=c(2,1))
boxplot(var~group,dat1,xlim=c(1,3))
plot(dat2$x,dat2$y,xlim=c(1,3))

Plot results from dist_tab() function from qdap library

I am interested in plotting the results from the following code which produces a frequency distribution table. I would like to graph the Freq column as a bar with the cum.Freq as a line both sharing the interval column as the x-axis.
library("qdap")
x <- c(1,2,3,2,4,2,5,4,6,7,8,9)
dist_tab(x)
I have been able to get the bar chart built using ggplot, but I want to take it further with the cum.Freq added as a secondary axis. I also want to add the percent and cum.percent values added as data labels. Any help is appreciated.
library("ggplot2")
ggplot(dist_tab(x), aes(x=interval)) + geom_bar(aes(y=Freq))
Not sure if I understand your question. Is this what you are looking for?
df <- dist_tab(x)
df.melt <- melt(df, id.vars="interval", measure.vars=c("Freq", "cum.Freq"))
#
ggplot(df.melt, aes(x=interval, y=value, fill=variable)) +
geom_bar(stat="identity", position="dodge")

log-scaled density plot: ggplot2 and freqpoly, but with points instead of lines

What I really want to do is plot a histogram, with the y-axis on a log-scale. Obviously this i a problem with the ggplot2 geom_histogram, since the bottom os the bar is at zero, and the log of that gives you trouble.
My workaround is to use the freqpoly geom, and that more-or less does the job. The following code works just fine:
ggplot(zcoorddist) +
geom_freqpoly(aes(x=zcoord,y=..density..),binwidth = 0.001) +
scale_y_continuous(trans = 'log10')
The issue is that at the edges of my data, I get a couple of garish vertical lines that really thro you off visually when combining a bunch of these freqpoly curves in one plot. What I'd like to be able to do is use points at every vertex of the freqpoly curve, and no lines connecting them. Is there a way to to this easily?
The easiest way to get the desired plot is to just recast your data. Then you can use geom_point. Since you don't provide an example, I used the standard example for geom_histogram to show this:
# load packages
require(ggplot2)
require(reshape)
# get data
data(movies)
movies <- movies[, c("title", "rating")]
# here's the equivalent of your plot
ggplot(movies) + geom_freqpoly(aes(x=rating, y=..density..), binwidth=.001) +
scale_y_continuous(trans = 'log10')
# recast the data
df1 <- recast(movies, value~., measure.var="rating")
names(df1) <- c("rating", "number")
# alternative way to recast data
df2 <- as.data.frame(table(movies$rating))
names(df2) <- c("rating", "number")
df2$rating <- as.numeric(as.character(df$rating))
# plot
p <- ggplot(df1, aes(x=rating)) + scale_y_continuous(trans="log10", name="density")
# with lines
p + geom_linerange(aes(ymax=number, ymin=.9))
# only points
p + geom_point(aes(y=number))

Resources