I am trying to create a histogram in ggplot2 where the x-axis transitions from linear scaling to log2 scaling after a pre-defined point. In other words, I want the x-axis to be of a linear scale up to some threshold, and then after that threshold, use the log2 scale.
So, before the threshold, the x-axis should look like what you would get from simply doing:
ggplot(data,aes(x=value)) + geom_histogram()
and after the threshold, the x-axis should look like what you would get from doing:
ggplot(data,aes(x=value)) + geom_histogram() + scale_x_continuous(trans='log2')
The problem is that while I can make those histograms individually (one where everything is on a linear scale, and one where everything is on a log2 scale), I don't know how to get it to transition and have both in one histogram.
I agree with the commenters that this would be problematic as a single figure. However, it could be informative, if you have one figure showing all data, and then an inset/subplot to show a subset. Here I used cowplot::plot_grid to combine two figures, but there are other packages out there for arranging (like gridExtra). Do be extremely cautious about how you label the figures.
library(ggplot2)
x <- rexp(1000, .05) + rep(c(0, 5), each = 500)
cowplot::plot_grid(
ggplot(data.frame(x = x[x<5]), aes(x)) +
geom_histogram() +
labs(title = "Subset, x<5, linear-scale"),
ggplot(data.frame(x), aes(x)) +
geom_vline(xintercept = 5, color = "red", size = 2) +
geom_histogram() +
scale_x_log10() +
labs(title = "All data, log-scale")
)
Related
currently, I'm using ggplot2 to make density plot.
ggplot(data=resultFile,aes(x=V19, colour=V1) ) +
geom_line(stat="density") +
xlab("score") +
ylab("density") +
ggtitle(paste(data_name,protocol,level,sep=" ")) +
theme(legend.title=element_blank(), legend.position=c(0.92,0.9)) +
scale_color_manual(values=c("blue","red"),
labels=c("A", "B"))
using this code, I can get the plot below.
However, I can get different plot if I used plot(density()...) function in R.
Y value starts from 0.
How can I make the ggplot's plot as like plot(density()...) in R?
ggplot(data=resultFile,aes(x=V19, colour=V1) ) +
ylim(0,range) #you can use this .
geom_line(stat="density") +
xlab("score") +
ylab("density") +
ggtitle(paste(data_name,protocol,level,sep=" ")) +
theme(legend.title=element_blank(), legend.position=c(0.92,0.9)) +
scale_color_manual(values=c("blue","red"),
labels=c("A", "B"))
ggplot obviously cut off the x-axis at the min and max of the empirical distribution. You can extend the x-axis by adding xlim to the plot but please make sure that the plot does not exceed the theoretical limit of the distribution (in the example below, the theoretical limit is [0, 1], so there is not much reason to show outside the range).
set.seed(1)
temp <- data.frame(x =runif(100)^3)
library(ggplot2)
ggplot(temp, aes(x = x)) + geom_line(stat = "density" + xlim(-.2, 1.2)
plot(density(temp$x))
I am plotting some payment distribution information and I aggregated the data after scaling it to log-normal (base-e). The histograms turn out great but I want to modify the x-axis to display the non-log equivalents.
My current axis displays [0:2.5:10] values
Alternatively, I would like to see values for exp(2.5), exp(5), etc.
Any suggestions on how to accomplish this? Anything I can add to my plotting statement to scale the x-axis values? Maybe there's a better approach - thoughts?
Current code:
ggplot(plotData, aes_string(pay, fill = pt)) + geom_histogram(bins = 50) + facet_wrap(~M_P)
Answered...Final plot:
Not sure if this is exactly what you are after but you can change the text of the x axis labels to whatever you want using scale_x_continuous.
Here's without:
ggplot(data = cars) + geom_histogram(aes(x = speed), binwidth = 1)
Here's with:
ggplot(data = cars) + geom_histogram(aes(x = speed), binwidth = 1) +
scale_x_continuous(breaks=c(5,10,15,20,25), labels=c(exp(5), exp(10), exp(15), exp(20), exp(25)))
I am trying to improve the clarity and aspect of a histogram of discrete values which I need to represent with a log scale.
Please consider the following MWE
set.seed(99)
data <- data.frame(dist = as.integer(rlnorm(1000, sdlog = 2)))
class(data$dist)
ggplot(data, aes(x=dist)) + geom_histogram()
which produces
and then
ggplot(data, aes(x=dist)) + geom_line() + scale_x_log10(breaks=c(1,2,3,4,5,10,100))
which probably is even worse
since now it gives the impression that the something is missing between "1" and "2", and also is not totally clear which bar has value "1" (bar is on the right of the tick) and which bar has value "2" (bar is on the left of the tick).
I understand that technically ggplot provides the "right" visual answer for a log scale. Yet as observer I have some problem in understanding it.
Is it possible to improve something?
EDIT:
This what happen when I applied Jaap solution to my real data
Where do the dips between x=0 and x=1 and between x=1 and x=2 come from? My value are discrete, but then why the plot is also mapping x=1.5 and x=2.5?
The first thing that comes to mind, is playing with the binwidth. But that doesn't give a great solution either:
ggplot(data, aes(x=dist)) +
geom_histogram(binwidth=10) +
scale_x_continuous(expand=c(0,0)) +
scale_y_continuous(expand=c(0.015,0)) +
theme_bw()
gives:
In this case it is probably better to use a density plot. However, when you use scale_x_log10 you will get a warning message (Removed 524 rows containing non-finite values (stat_density)). This can be resolved by using a log plus one transformation.
The following code:
library(ggplot2)
library(scales)
ggplot(data, aes(x=dist)) +
stat_density(aes(y=..count..), color="black", fill="blue", alpha=0.3) +
scale_x_continuous(breaks=c(0,1,2,3,4,5,10,30,100,300,1000), trans="log1p", expand=c(0,0)) +
scale_y_continuous(breaks=c(0,125,250,375,500,625,750), expand=c(0,0)) +
theme_bw()
will give this result:
I am wondering, what if, y-axis is scaled instead of x-axis. It will results into few warnings wherever values are 0, but may serve your purpose.
set.seed(99)
data <- data.frame(dist = as.integer(rlnorm(1000, sdlog = 2)))
class(data$dist)
ggplot(data, aes(x=dist)) + geom_histogram() + scale_y_log10()
Also you may want to display frequencies as data labels, since people might ignore the y-scale and it takes some time to realize that y scale is logarithmic.
ggplot(data, aes(x=dist)) + geom_histogram(fill = 'skyblue', color = 'grey30') + scale_y_log10() +
stat_bin(geom="text", size=3.5, aes(label=..count.., y=0.8*(..count..)))
A solution could be to convert your data to a factor:
library(ggplot2)
set.seed(99)
data <- data.frame(dist = as.integer(rlnorm(1000, sdlog = 2)))
ggplot(data, aes(x=factor(dist))) +
geom_histogram(stat = "count") +
theme(axis.text.x = element_text(angle = 90, hjust = 1))
Resulting in:
I had the same issue and, inspired by #Jaap's answer, I fiddled with the histogram binwidth using the x-axis in log scale.
If you use binwidth = 0.201, the bars will be juxtaposed as expected. However, this means you can only have up to five bars between two x coordinates.
set.seed(99)
data <- data.frame(dist = as.integer(rlnorm(1000, sdlog = 2)))
class(data$dist)
ggplot(data, aes(x=dist)) +
geom_histogram(binwidth = 0.201, color = 'red') +
scale_x_log10()
Result:
I am trying to use the excellent ggplot2 using the bar geom to plot the probability mass rather than the count. However, using aes(y=..density..) the distribution does not sum to one (but is close). I think the problem might be due to the default binwidth for factors. Here is an example of the problem,
age <- c(rep(0,4), rep(1,4))
mppf <- c(1,1,1,0,1,1,0,0)
data.test <- as.data.frame(cbind(age,mppf))
data.test$age <- as.factor(data.test$age)
data.test$mppf <- as.factor(data.test$mppf)
p.test.density <- ggplot(data.test, aes(mppf, group=age, fill=age)) +
geom_bar(aes(y=..density..), position='dodge') +
scale_y_continuous(limits=c(0,1))
dev.new()
print(p.test.density)
I can get around this problem by keeping the x-variable as continuous and setting binwidth=1, but it doesn't seem very elegant.
data.test$mppf.numeric <- as.numeric(data.test$mppf)
p.test.density.numeric <- ggplot(data.test, aes(mppf.numeric, group=age, fill=age)) +
geom_histogram(aes(y=..density..), position='dodge', binwidth=1)+
scale_y_continuous(limits=c(0,1))
dev.new()
print(p.test.density.numeric)
I think you almost have it figured out, and would have once you realized you needed a bar plot and not a histogram.
The default width for bars with categorical data is .9 (See ?stat_bin. The help page for geom_bar doesn't give the default bar width but does send you to stat_bin for further reading.). Given that, your plots show the correct density for a bar width of .9. Simply change to a width of 1 and you will see the density values you expected to see.
ggplot(data.test, aes(x = mppf, group = age, fill = age)) +
geom_bar(aes(y=..density..), position = "dodge", width = 1) +
scale_y_continuous(limits=c(0,1))
The most commonly cited example of how to visualize a logistic fit using ggplot2 seems to be something very much like this:
data("kyphosis", package="rpart")
ggplot(data=kyphosis, aes(x=Age, y = as.numeric(Kyphosis) - 1)) +
geom_point() +
stat_smooth(method="glm", family="binomial")
This visualisation works great if you don't have too much overlapping data, and the first suggestion for crowded data seems to be to use injected jitter in the x and y coordinates of the points then adjust the alpha value of the points. When you get to the point where individual points aren't useful but distributions of points are, is it possible to use geom_density(), geom_histogram(), or something else to visualise the data but continue to split the categorical variable along the y-axis as it is done with geom_point()?
From what I have found, geom_density() and geom_histogram() can easily be split/grouped by the categorical variable and both levels can easily be reversed using scale_y_reverse() but I can't figure out if it is even possible to move only one of the categorical variable distributions to the top of the plot. Any help/suggestions would be appreciated.
The annotate() function in ggplot allows you to add geoms to a plot with properties that "are not mapped from the variables of a data frame, but are instead in as vectors," meaning that you can add layers that are unrelated to your data frame. In this case your two density curves are related to the data frame (since the variables are in it), but because you're trying to position them differently, using annotate() is useful.
Here's one way to go about it:
data("kyphosis", package="rpart")
model.only <- ggplot(data=kyphosis, aes(x=Age, y = as.numeric(Kyphosis) - 1)) +
stat_smooth(method="glm", family="binomial")
absents <- subset(kyphosis, Kyphosis=="absent")
presents <- subset(kyphosis, Kyphosis=="present")
dens.absents <- density(absents$Age)
dens.presents <- density(presents$Age)
scaling.factor <- 10 # Make the density plots taller
model.only + annotate("line", x=dens.absents$x, y=dens.absents$y*scaling.factor) +
annotate("line", x=dens.presents$x, y=dens.presents$y*scaling.factor + 1)
This adds two annotated layers with scaled density plots for each of the kyphosis groups. For the presents variable, y is scaled and increased by 1 to shift it up.
You can also fill the density plots instead of just using a line. Instead of annotate("line"...) you need to use annotate("polygon"...), like so:
model.only + annotate("polygon", x=dens.absents$x, y=dens.absents$y*scaling.factor, fill="red", colour="black", alpha=0.4) +
annotate("polygon", x=dens.presents$x, y=dens.presents$y*scaling.factor + 1, fill="green", colour="black", alpha=0.4)
Technically you could use annotate("density"...), but that won't work when you shift the present plot up by one. Instead of shifting, it fills the whole plot:
model.only + annotate("density", x=dens.absents$x, y=dens.absents$y*scaling.factor, fill="red") +
annotate("density", x=dens.presents$x, y=dens.presents$y*scaling.factor + 1, fill="green")
The only way around that problem is to use a polygon instead of a density geom.
One final variant: flipping the top density plot along y-axis = 1:
model.only + annotate("polygon", x=dens.absents$x, y=dens.absents$y*scaling.factor, fill="red", colour="black", alpha=0.4) +
annotate("polygon", x=dens.presents$x, y=(1 - dens.presents$y*scaling.factor), fill="green", colour="black", alpha=0.4)
I am not sure I get your point, but here an attempt:
dat <- rbind(kyphosis,kyphosis)
dat$grp <- factor(rep(c('smooth','dens'),each = nrow(kyphosis)),
levels = c('smooth','dens'))
ggplot(dat,aes(x=Age)) +
facet_grid(grp~.,scales = "free_y") +
#geom_point(data=subset(dat,grp=='smooth'),aes(y = as.numeric(Kyphosis) - 1)) +
stat_smooth(data=subset(dat,grp=='smooth'),aes(y = as.numeric(Kyphosis) - 1),
method="glm", family="binomial") +
geom_density(data=subset(dat,grp=='dens'))