R: Stacked bar plot with barpot, ggplot or plotly - r

I've been searching to find a solution, but none of the already existing questions fit my problem.
I have a data.frame:
Pat <- c(1,1,1,1,1,1,2,2,2,2,2,2)
V_ID <- c(1,1,6,6,9,9,1,1,6,6,9,9)
T_ID <- c("A","B","A","B","A","B","A","B","A","B", "A","B")
apples <- c(1,1,1,1,1,1,1,1,1,1,1,1)
bananas <- c(2,2,2,2,2,2,2,2,2,2,2,2)
cranberries <- c(3,3,3,3,3,3,3,3,3,3,3,3)
df <- data.frame(Pat,V_ID, T_ID, apples, bananas, cranberries)
I am trying to plot:
barplot(as.matrix(df[,4:6]) ,
main="tobefound", horiz = FALSE,width = 1,
names.arg=colnames(df[,4:6]),
las=2,
col = c("blue", "red"),
legend = df[,3],
args.legend = list(x="topleft"),
beside= FALSE)
BARPLOT
I need two changes:
First of all I like to have all "B"s (so the red part in every stack) piled up together and then the blue ones on top. Second: is there a way of decreasing the legend to only A and B once besides addressing this via
legend = df[1:2,3],
I am also looking for a solution using plotly or ggplot.
Thanks,

First reshape:
df_long <- tidyr::gather(df, 'key', 'value', apples:cranberries)
Then plot:
ggplot(df_long, aes(key, value, fill = T_ID)) + geom_col(col = 'black')
Or perhaps without the borders:
ggplot(df_long, aes(key, value, fill = T_ID)) + geom_col()

Using base graphics, you needed to sort df by T_ID first.
df = df[order(df$T_ID), ]
barplot(as.matrix(df[,4:6]) ,
main="tobefound", horiz = FALSE,width = 1,
names.arg=colnames(df[,4:6]),
las=2,
ylim = c(0,40),
col = 1+as.numeric(as.factor(df$T_ID)),
border = NA,
beside= FALSE)
box()
legend('topleft', fill = 1+as.numeric(as.factor(levels(df$T_ID))), legend = levels(as.factor(df$T_ID)))

Related

How can I create line chart for 'WorldPhones' dataset in R?

How can I create line chart for 'WorldPhones' dataset in R?
The dataset is of class - "matrix" "array". I want to plot a line chart for the number of telephones in North America, Asia and Europe between 1956-1961.
The example in the help page for the dataset gives a lovely plot using matplot. For something slightly more pleasing, you can try ggplot.
library(tidyr) # For pivoting the data into long form
library(tibble) # For converting the rownames (Year) to a column
library(scales) # For scaing the y-axis and labels
library(ggplot2) # For the plot
WorldPhones %>%
as.data.frame() %>%
rownames_to_column("Year") %>%
pivot_longer(cols=-Year, names_to="Country", values_to="Users") %>%
ggplot(aes(Year, Users, group=Country, col=Country)) +
geom_line() +
scale_y_log10(n.breaks=5, labels = trans_format("log10", math_format(10^.x))) +
theme_minimal()
The following gives the years and continents you are after. Personally I prefer simplicity of this base-R code and the fine-grained control this gives you over the look of the chart, though beauty is in the eye of the beholder!
WP <- WorldPhones[as.character(1956:1961), c("N.Amer", "Asia", "Europe")]
matplot(x = rownames(WP), y = WP/1000,
type = "b", pch = 16, lty = 1, lwd = 2,
log = "y", ylim = c(2, 100),
main = "World phones data (AT&T 1961)",
xlab = "Year", ylab = "Number of telephones (millons)")
legend("bottom", legend = colnames(WP), horiz = TRUE,
lwd = 2, pch = 16, col = 1:3)

How to make three different bar charts of similar type clustered in the same plot?

I need to map my Erosion values for different levels of tillage (colomns) with three levels of soil depth (rows (A1, A2, A3)). I want all of this to be shown as a barchart in a single plot.
Here is a reproducible example:
a<- matrix(c(1:36), byrow = T, ncol = 4)
rownames(a)<-(c("A1","B1","C1","A2","B2","C2","A3","B3","C3"))
colnames(a)<-(c("Int_till", "Redu_till", "mulch_till", "no_till"))
barplot(a[1,], xlab = "A1", ylab = "Erosion")
barplot(a[4,], xlab = "A2", ylab = "Erosion")
barplot(a[7,], xlab = "A3", ylab = "Erosion")
##I want these three barchart side by side in a single plot
## for comparison
### and need similar plots for all the "Bs" and "Cs"
### Lastly, i want these three plots in the same page.
I have seen people do similar things using "fill" in ggplot (for lines) and specifying the factor which nicely separates the chart for different categories but I tried doing it but always run into error maybe because my data is continuous.
If any body could help me with these two things.. It will be a great help. I will appreciate it.
Thank you!
We can use ggplot
library(reshape2)
library(ggplot2)
library(dplyr)
melt(a) %>%
ggplot(., aes(x = Var2, y = value, fill = Var1)) +
geom_bar(stat = 'identity',
position = position_dodge2(preserve = "single")) +
facet_wrap(~ Var1)
Set mfcol to specify a 3x3 grid and then for each row generate its bar plot. Also, you could consider adding the barplot argument ylim = c(0, max(a)) so that all graphs use the same Y axis. title and mtext can be used to set the overall title and various margin text as we do below. See ?par, ?title and ?mtext for more information.
opar <- par(mfcol = c(3, 3), oma = c(0, 3, 0, 0))
for(r in rownames(a)) barplot(a[r, ], xlab = r, ylab = "Erosion")
par(opar)
title("My Plots", outer = TRUE, line = -1)
mtext(LETTERS[1:3], side = 2, outer = TRUE, line = -1,
at = c(0.85, 0.5, 0.17), las = 2)

Label the x axis correct in a histogram in R

I tried to name the x axis correct.
hist(InsectSprays$count, col='pink', xlab='Sprays', labels=levels(InsectSprays$spray), xaxt='n')
axis(1, at=unique(InsectSprays$spray), labels=levels(InsectSprays$spray))
But this produces
I want the letters below the bars and not on top.
You have to plot the labels at the histogram bin midpoints. If you want to remove the axis and just have lettering, the padj will move the letters closer to the axis which you just removed.
h <- hist(InsectSprays$count, plot = FALSE)
plot(h, xaxt = "n", xlab = "Insect Sprays", ylab = "Counts",
main = "", col = "pink")
axis(1, h$mids, labels = LETTERS[1:6], tick = FALSE, padj= -1.5)
I generally think barplot are more suited for categorical variables. A solution in base R could be, with some rearrangement of the data:
d <- aggregate(InsectSprays$count, by=list(spray=InsectSprays$spray), FUN=sum)
d <- d[order(d$x, decreasing = T),]
t <- d$x
names(t) <- d$spray
barplot(t, las = 1, space = 0, col = "pink", xlab = "Sprays", ylab = "Count")
The output is the following:
Since you mentioned a ggplot solution would be nice:
library(ggplot)
library(dplyr)
InsectSprays %>%
group_by(spray) %>%
summarise(count = sum(count)) %>%
ggplot(aes(reorder(spray, -count),count)) +
geom_bar(stat = "identity", fill = "pink2") +
xlab("Sprays")
The output being:

Global legend using grid.arrange (gridExtra) and lattice based plots

I am producing four plots using xyplot (lattice) and further combine them with grid.arrange (gridExtra).
I would like to obtain a graph with a common global legend. The closest that I have reached is the following. They have to be in a matrix layout, otherwise an option would be to put them in a column and include only a legend for the top or bottom one.
# Load packages
require(lattice)
require(gridExtra)
# Generate some values
x1<-rnorm(100,10,4)
x2<-rnorm(100,10,4)
x3<-rnorm(100,10,4)
x4<-rnorm(100,10,4)
y<-rnorm(100,10,1)
cond<-rbinom(100,1,0.5)
groups<-sample(c(0:10),100,replace=TRUE)
dataa<-data.frame(y,x1,x2,x3,x4,cond,groups)
# ploting function
plott<-function(x){
xyplot(y~x|cond,groups=groups,
col = gray(seq(0.01,0.7,length=length(levels(as.factor(groups))))),
pch = 1:length(levels(as.factor(groups))),
key = list(space="top",
text = list(as.character(levels(as.factor(groups)))),
points = TRUE, lines = TRUE, columns = 3,
pch = 1:length(levels(as.factor(groups))),
col = gray(seq(0.01,0.7,length=length(levels(as.factor(groups))))),
cex=1))
}
plot1<-plott(x=x1)
plot2<-plott(x=x2)
plot3<-plott(x=x3)
plot4<-plott(x=x4)
grid.arrange(plot1,plot2,plot2,plot4,ncol=2)
In a similar post, I have seen that it can be performed with the use of ggplot2 e.g. here and here but is there a way to include a global common legend using gridExtra and a lattice based plot e.g. xyplot?
Thank you.
One possible solution is to use ggplot, hinted here.
my.cols <- 1:3
my.grid.layout <- rbind(c(1,2),
c(3,3))
g_legend<-function(a.gplot){
tmp <- ggplot_gtable(ggplot_build(a.gplot))
leg <- which(sapply(tmp$grobs, function(x) x$name) == "guide-box")
legend <- tmp$grobs[[leg]]
return(legend)
}
legend.plot <- ggplot(iris, aes(x=Petal.Length, y=Sepal.Width,colour=Species)) +
geom_line(size=1) + # legend should show lines, not points or rects ...
theme(legend.position="right", legend.background = element_rect(colour = "black"),
legend.key = element_rect(fill = "white")) + # position, box and background colour of legend
scale_color_manual(values=my.cols, name = "Categories") + # manually insert colours as used in corresponding xyplot
guides(colour = guide_legend(reverse=T)) # inverts order of colours in legend
mylegend <- g_legend(legend.plot)
plot1 <- xyplot(Sepal.Width ~ Petal.Length, groups = Species, data = iris, type = 'l',
par.settings = simpleTheme(col=my.cols))
plot2 <- xyplot(Sepal.Length ~ Petal.Length, groups = Species, data = iris, type = 'l',
par.settings = simpleTheme(col=my.cols))
grid.arrange(plot1,plot2,mylegend,layout_matrix=my.grid.layout,
top=textGrob(gp=gpar(col='black',fontsize=20),"Some useless example"))
I managed to produce something more close to what I first imagined. For that I am including an extra graphical element and I am using the layout_matrix option in grid.arrange to minimize its effect. That way I am keeping the legend and almost exclude the plot.
# Load packages
require(lattice)
require(gridExtra)
# Generate some values
x1<-rnorm(100,10,4)
x2<-rnorm(100,10,4)
x3<-rnorm(100,10,4)
x4<-rnorm(100,10,4)
y<-rnorm(100,10,1)
cond<-rbinom(100,1,0.5)
groups<-sample(c(0:10),100,replace=TRUE)
dataa<-data.frame(y,x1,x2,x3,x4,cond,groups)
# ploting function
plottNolegend<-function(x){
xyplot(y~x|cond,groups=groups,
col = gray(seq(0.01,0.7,length=length(levels(as.factor(groups))))),
pch = 1:length(levels(as.factor(groups)))
)
}
plott<-function(x){
xyplot(y~x|cond,groups=groups,
col = gray(seq(0.01,0.7,length=length(levels(as.factor(groups))))),
pch = 1:length(levels(as.factor(groups))),
key = list(space="top",
text = list(as.character(levels(as.factor(groups)))),
points = TRUE, lines = TRUE, columns = 3,
pch = 1:length(levels(as.factor(groups))),
col = gray(seq(0.01,0.7,length=length(levels(as.factor(groups))))),
cex=1))
}
plot1<-plottNolegend(x=x1)
plot2<-plottNolegend(x=x2)
plot3<-plottNolegend(x=x3)
plot4<-plottNolegend(x=x4)
legend<-plott(x=x4)
lay <- rbind(c(1,2),
c(1,2),
c(3,4),
c(3,4),
c(5,5))
grid.arrange(plot1,plot2,plot2,plot4,legend, layout_matrix = lay)
Updated: The answer was much simpler than I expected. Thank you all for your help.
# Load packages
require(lattice)
require(gridExtra)
require(grid)
# Generate some values
x1<-rnorm(100,10,4)
x2<-rnorm(100,10,4)
x3<-rnorm(100,10,4)
x4<-rnorm(100,10,4)
y<-rnorm(100,10,1)
cond<-rbinom(100,1,0.5)
groups<-sample(c(0:10),100,replace=TRUE)
dataa<-data.frame(y,x1,x2,x3,x4,cond,groups)
# ploting function
plott<-function(x){
xyplot(y~x|cond,groups=groups,
col = gray(seq(0.01,0.7,length=length(levels(as.factor(groups))))),
pch = 1:length(levels(as.factor(groups))),
key = NULL)
}
plot1<-plott(x=x1)
plot2<-plott(x=x2)
plot3<-plott(x=x3)
plot4<-plott(x=x4)
grid.arrange(plot1,plot2,plot2,plot4,ncol=2)
KeyA<-list(space="top",
text = list(as.character(levels(as.factor(groups)))),
points = TRUE, lines = TRUE, columns = 11,
pch = 1:length(levels(as.factor(groups))),
col = gray(seq(0.01,0.7,length=length(levels(as.factor(groups))))),
cex=1)
draw.key(KeyA, draw = TRUE, vp =
viewport(.50, .99))
I think the better solution is to use c.trellis from latticeExtra:
library(latticeExtra)
c(plot1, plot2, plot3, plot4)

Help reproduce graph from excel

I would like to reproduce the following graph:
On the horizontal axis I would like to have 8 the question numbers, and I would like to plot two results for each question.
for example
questionnumbers<-c(1,2,3,4,5,6,7,8)
result1<-c(0.2,0.4,0.3,0.6,0.9,0.3,0.4,0.8)
result2<-c(0.4,0.9,0.3,0.1,0.4,0.6,0.3,0.2)
And i'd like to get a graph similar to this:
http://dl.dropbox.com/u/22681355/chart.tiff
Preferably I'd like to know how to do this in qplot using ggplot2
library(reshape2)
library(ggplot2)
qs <- data.frame(
questionnumbers = c(1,2,3,4,5,6,7,8),
result1 = c(0.2,0.4,0.3,0.6,0.9,0.3,0.4,0.8),
result2 = c(0.4,0.9,0.3,0.1,0.4,0.6,0.3,0.2)
)
mqs <- melt(qs, id.vars="questionnumbers")
ggplot(mqs, aes(x=questionnumbers, y=value, colour=variable)) + geom_line()
Edited.
Your follow-on question asks what is different with your diffferent data set. The answer is that your grouping variable is continuous, not categorical. By default, ggplot will group categorical variables together. If your grouping variable is not categorical, you need to make the grouping variable explicit in the aes call in ggplot, as follows `aes(..., group=variable, ...):
qs<-data.frame(
questionnumbers = c("1red","1blue","2red","2blue","3red","3blue","4red","4blue"),
Probability=c(0.59,0.60,0.55,0.55,0.60,0.58,0.67,0.68),
Chosing.colour=c(0.16,0.21,0.26,0.53,0.84,0.89,0.84,0.947))
mqs <-melt(qs, id.vars="questionnumbers")
str(mqs)
ggplot(mqs, aes(x=questionnumbers, y=value, group=variable, colour=variable)) +
geom_line()
In base graphics it would be...
questionnumbers<-c(1,2,3,4,5,6,7,8)
result1<-c(0.2,0.4,0.3,0.6,0.9,0.3,0.4,0.8)
result2<-c(0.4,0.9,0.3,0.1,0.4,0.6,0.3,0.2)
plot(questionnumbers, result2, type = 'b', ylim = c(0,0.9), col = 'green', xlab = 'Question Nunbers', ylab = '', main = 'Chart 2', panel.first = grid(nx = NA, ny = NULL))
lines(questionnumbers, result1, col = 'blue', type = 'b')
legend('bottomleft', c('result1','result2'), fill = c('blue', 'green'), cex = 0.8, bty = 'n', horiz = TRUE)
(you should really provide a y-axis label)

Resources