I basically have a very simple question, how can I make bar charts shorter? If I search for it on the web I can only find "width", but this is not what I want. I want to make the lenght of the bars shorter.
library(ggplot2)
# data
data <- data.frame(
name=c("A","B","C","D","E") ,
value=c(3,12,5,18,45)
)
# bar chart
ggplot(data, aes(x=name, y=value)) +
geom_bar(stat = "identity", width=0.8) +
coord_flip()
I basically just want that the lenght of the bar goes up to like around 20, so somehow just half the length..
I think you mean how do you make the bars visibly shorter on the page without affecting their numerical representation. Perhaps you just need to set the axis limits?
If your plot looks like this...
p <- ggplot(data, aes(x=name, y=value)) +
geom_bar(stat = "identity", width=0.8) +
coord_flip()
p
Then you can just do this...
p + scale_y_continuous(limits = c(0, 2 * max(data$value)))
Created on 2020-11-10 by the reprex package (v0.3.0)
Related
When creating a graphic with ggplot, the size of the panel is adjusted with the other elements, for example the length of the labels in the legend.
We can see an example:
library(ggplot2)
# Create data
data <- data.frame(
name=c("A","B","C","D","E") ,
name2=c("A","B","C","D","E long, very long..... very very long........") ,
value=c(3,12,5,18,45)
)
ggplot(data, aes(x=name, y=value,fill=name)) +
geom_bar(stat = "identity")
ggplot(data, aes(x=name, y=value,fill=name2)) +
geom_bar(stat = "identity")
The problem is that if we create a series of graphics with different variables, the width of the bars is different.
My question is: is it possible to fix the size of the panel, and increase the width of the global ggplot graphic size to display legend?
(PS: one possibility is to create a legend in a separate graphic, but I would also like to explore the options for the overall size the ggplot graphics).
You can control the length of the text using str_wrap
ggplot(data, aes(x=name, y=value,fill=stringr::str_wrap(name2, 10))) +
geom_bar(stat = "identity") +
scale_fill_discrete(name = "Legend")+
theme(legend.margin = margin(r=10,l=5,t=5,b=5))
Is it possible to add horizontal lines from 0 to the points on the plot shown below?
This is the code thus far:
ggplot(data, aes(x=change, y=industry, color=geo)) + geom_point() +
scale_x_continuous(labels = scales::comma) + geom_vline(xintercept = 0)
Alternatively, I could use geom_bar() but I have been unsure how to show both London and the UK without them summing together.
tl;dr you can use geom_bar() with position="stack", stat="identity". Or you can use geom_segment().
set up data
dd <- expand.grid(industry=c("property",
"manufacturing",
"other"),
geo=c("London","UK"))
set.seed(101)
dd$change <- runif(6,min=-30,max=30)
This is how you could do it with geom_bar
library(ggplot2)
ggplot(dd,aes(x=industry,y=change,
fill=geo))+
geom_bar(stat="identity",
position="dodge")+
coord_flip()
Or with geom_segment():
ggplot(dd,aes(x=change,y=industry,
colour=geo))+
geom_point(size=2)+
geom_segment(aes(xend=0,yend=industry))
You might want to consider manually dodging the position in the second case, but position_dodge in ggplot can only dodge horizontally, so you should either switch x and y and use coord_flip(), or use position_dodgev from the ggstance package.
Since I was confused about the math last time I tried asking this, here's another try. I want to combine a histogram with a smoothed distribution fit. And I want the y axis to be in percent.
I can't find a good way to get this result. Last time, I managed to find a way to scale the geom_bar to the same scale as geom_density, but that's the opposite of what I wanted.
My current code produces this output:
ggplot2::ggplot(iris, aes(Sepal.Length)) +
geom_bar(stat="bin", aes(y=..density..)) +
geom_density()
The density and bar y values match up, but the scaling is nonsensical. I want percentage on the y axes, not well, the density.
Some new attempts. We begin with a bar plot modified to show percentages instead of counts:
gg = ggplot2::ggplot(iris, aes(Sepal.Length)) +
geom_bar(aes(y = ..count../sum(..count..))) +
scale_y_continuous(name = "%", labels=scales::percent)
Then we try to add a geom_density to that and somehow get it to scale properly:
gg + geom_density()
gg + geom_density(aes(y=..count..))
gg + geom_density(aes(y=..scaled..))
gg + geom_density(aes(y=..density..))
Same as the first.
gg + geom_density(aes(y = ..count../sum(..count..)))
gg + geom_density(aes(y = ..count../n))
Seems to be off by about factor 10...
gg + geom_density(aes(y = ..count../n/10))
same as:
gg + geom_density(aes(y = ..density../10))
But ad hoc inserting numbers seems like a bad idea.
One useful trick is to inspect the calculated values of the plot. These are not normally saved in the object if one saves it. However, one can use:
gg_data = ggplot_build(gg + geom_density())
gg_data$data[[2]] %>% View
Since we know the density fit around x=6 should be about .04 (4%), we can look around for ggplot2-calculated values that get us there, and the only thing I see is density/10.
How do I get geom_density fit to scale to the same y axis as the modified geom_bar?
Bonus question: why are the grouping of the bars different? The current function does not have spaces in between bars.
Here is an easy solution:
library(scales) # ! important
library(ggplot2)
ggplot(iris, aes(Sepal.Length)) +
stat_bin(aes(y=..density..), breaks = seq(min(iris$Sepal.Length), max(iris$Sepal.Length), by = .1), color="white") +
geom_line(stat="density", size = 1) +
scale_y_continuous(labels = percent, name = "percent") +
theme_classic()
Output:
Try this
ggplot2::ggplot(iris, aes(x=Sepal.Length)) +
geom_histogram(stat="bin", binwidth = .1, aes(y=..density..)) +
geom_density()+
scale_y_continuous(breaks = c(0, .1, .2,.3,.4,.5,.6),
labels =c ("0", "1%", "2%", "3%", "4%", "5%", "6%") ) +
ylab("Percent of Irises") +
xlab("Sepal Length in Bins of .1 cm")
I think your first example is what you want, you just want to change the labels to make it seem like it is percents, so just do that rather than mess around.
I have a dataset where measurements are made for different groups at different days.
I want to have side by side bars representing the measurements at the different days for the different groups with the groups of bars spaced according to day of measurement with errorbars overlaid to them.
I'm having trouble with making the dodging in geom_bar agree with the dodge on geom_errorbar.
Here is a simple piece of code:
days = data.frame(day=c(0,1,8,15));
groups = data.frame(group=c("A","B","C","D", "E"), means=seq(0,1,length=5));
my_data = merge(days, groups);
my_data$mid = exp(my_data$means+rnorm(nrow(my_data), sd=0.25));
my_data$sigma = 0.1;
png(file="bar_and_errors_example.png", height=900, width=1200);
plot(ggplot(my_data, aes(x=day, weight=mid, ymin=mid-sigma, ymax=mid+sigma, fill=group)) +
geom_bar (position=position_dodge(width=0.5)) +
geom_errorbar (position=position_dodge(width=0.5), colour="black") +
geom_point (position=position_dodge(width=0.5), aes(y=mid, colour=group)));
dev.off();
In the plot, the errorsegments appears with a fixed offset from its bar (sorry, no plots allowed for newbies even if ggplot2 is the subject).
When binwidth is adjusted in geom_bar, the offset is not fixed and changes from day to day.
Notice, that geom_errorbar and geom_point dodge in tandem.
How do I get geom_bar to agree with the other two?
Any help appreciated.
The alignment problems are due, in part, to your bars not representing the data you intend. The following lines up correctly:
ggplot(my_data, aes(x=day, weight=mid, ymin=mid-sigma, ymax=mid+sigma, fill=group)) +
geom_bar (position=position_dodge(), aes(y=mid), stat="identity") +
geom_errorbar (position=position_dodge(width=0.9), colour="black") +
geom_point (position=position_dodge(width=0.9), aes(y=mid, colour=group))
This is an old question, but since i ran into the problem today, i want to add the following:
In
geom_bar(position = position_dodge(width=0.9), stat = "identity") +
geom_errorbar( position = position_dodge(width=0.9), colour="black")
the width-argument within position_dodge controls the dodging width of the things to dodge from each other. However, this produces whiskers as wide as the bars, which is possibly undesired.
An additional width-argument outside the position_dodge controls the width of the whiskers (and bars):
geom_bar(position = position_dodge(width=0.9), stat = "identity", width=0.7) +
geom_errorbar( position = position_dodge(width=0.9), colour="black", width=0.3)
The first change I reformatted the code according to the advanced R style guide.
days <- data.frame(day=c(0,1,8,15))
groups <- data.frame(
group=c("A","B","C","D", "E"),
means=seq(0,1,length=5)
)
my_data <- merge(days, groups)
my_data$mid <- exp(my_data$means+rnorm(nrow(my_data), sd=0.25))
my_data$sigma <- 0.1
Now when we look at the data we see that day is a factor and everything else is the same.
str(my_data)
To remove blank space from the plot I converted the day column to factors. CHECK that the levels are in the proper order before proceeding.
my_data$day <- as.factor(my_data$day)
levels(my_data$day)
The next change I made was defining y in your aes arguments. As I'm sure you are aware, this lets ggplot know where to look for y values. Then I changed the position argument to "dodge" and added the stat="identity" argument. The "identity" argument tells ggplot to plot y at x. geom_errorbar inherits the dodge position from geom_bar so you can leave it unspecified, but geom_point does not so you must specify that value. The default dodge is position_dodge(.9).
ggplot(data = my_data,
aes(x=day,
y= mid,
ymin=mid-sigma,
ymax=mid+sigma,
fill=group)) +
geom_bar(position="dodge", stat = "identity") +
geom_errorbar( position = position_dodge(), colour="black") +
geom_point(position=position_dodge(.9), aes(y=mid, colour=group))
sometimes you put aes(x=tasks,y=val,fill=group) in geom_bar rather than ggplot. This causes the problem since ggplot looks forward x and you specify it by the location of each group.
How can I increase the space between the bars in a bar plot in ggplot2 ?
You can always play with the width parameter, as shown below:
df <- data.frame(x=factor(LETTERS[1:4]), y=sample(1:100, 4))
library(ggplot2)
ggplot(data=df, aes(x=x, y=y, width=.5)) +
geom_bar(stat="identity", position="identity") +
opts(title="width = .5") + labs(x="", y="") +
theme_bw()
Compare with the following other settings for width:
So far, so good. Now, suppose we have two factors. In case you would like to play with evenly spaced juxtaposed bars (like when using space together with beside=TRUE in barplot()), it's not so easy using geom_bar(position="dodge"): you can change bar width, but not add space in between adjacent bars (and I didn't find a convenient solution on Google). I ended up with something like that:
df <- data.frame(g=gl(2, 1, labels=letters[1:2]), y=sample(1:100, 4))
x.seq <- c(1,2,4,5)
ggplot(data=transform(df, x=x.seq), aes(x=x, y=y, width=.85)) +
geom_bar(stat="identity", aes(fill=g)) + labs(x="", y="") +
scale_x_discrete(breaks = NA) +
geom_text(aes(x=c(sum(x.seq[1:2])/2, sum(x.seq[3:4])/2), y=0,
label=c("X","Y")), vjust=1.2, size=8)
The vector used for the $x$-axis is "injected" in the data.frame, so that so you change the outer spacing if you want, while width allows to control for inner spacing. Labels for the $x$-axis might be enhanced by using scale_x_discrete().
For space between factor bars use
ggplot(data = d, aes(x=X, y=Y, fill=F))
+ geom_bar(width = 0.8, position = position_dodge(width = 0.9))
The width in geom_bar controls the bar width in relation to the x-axis while the width in position_dodge control the width of the space given to both bars also in relation to the x-axis. Play around with the width to find one that you like.
Thank you very much chl.! I had the same problem and you helped me solving it. Instead of using geom_text to add the X-labels I used scale_x_continuous (see below)
geom_text(aes(x=c(sum(x.seq[1:2])/2, sum(x.seq[3:4])/2), y=0,
label=c("X","Y")), vjust=1.2, size=8)
replaced by
scale_x_continuous(breaks=c(mean(x.seq[1:2]), mean(x.seq[3:4])), labels=c("X", "Y"))
For space between POSIXlt bars you need adjust the width from the number of seconds in a day
# POSIXlt example: full & half width
d <- data.frame(dates = strptime(paste(2016, "01", 1:10, sep = "-"), "%Y-%m-%d"),
values = 1:10)
ggplot(d, aes(dates, values)) +
geom_bar(stat = "identity", width = 60*60*24)
ggplot(d, aes(dates, values)) +
geom_bar(stat = "identity", width = 60*60*24*0.5)