Instead of adding mathematical symbols in x-labels, I'm trying to add $t_[1,n]$, $t_[2,n]$ and $t_[3,n]$ symbols at 0, 40 and 85 points in x-axis values, respectively. For doing so, my codes are
m=c(rnorm(40,0,.5),rnorm(45,5,.5));
plot(rep(1:85,1), m, type="l", lty=1, xaxt='n', yaxt='n',ann=FALSE, col=4);
windowsFonts(script=windowsFont("Script MT Bold"));
title(xlab=c(expression(t[1,n]), expression(t[2,n]), expression(t[3,n])), family="script");
Maybe try with ggplot2, like here:
library("ggplot2")
x <- 1:85
y <- c(rnorm(40,0,.5), rnorm(45,5,.5));
dane <- data.frame(x=x, y=y)
ggplot(dane, aes(x=x, y=y))+
geom_line()+
theme_bw()+
scale_x_discrete(breaks=c(1, 40, 85),
labels=c(expression(t[paste("[", 1, ",", n, "]")]),
expression(t[paste("[", 2, ",", n, "]")]),
expression(t[paste("[", 3, ",", n, "]")])))
Use axis instead of title.
axis(side = 1, at = c(0, 40, 85),
labels = c(expression(t["1,n"]),
expression(t["2,n"]),
expression(t["3,n"])))
Related
This question already has answers here:
Adding minor tick marks to the x axis in ggplot2 (with no labels)
(4 answers)
Closed last month.
This question has been raised a number of times on StackOverflow over the years (see here and here), however I'm yet to come across a way that I'm satisfied with for easily adding unlabelled minor ticks to my ggplot axes.
Let's generate some dummy data to play around with:
df <- data.frame(x = rnorm(1000, mean = 25, sd = 5),
y = rnorm(1000, mean = 23, sd = 3))
There are two methods I've come across for adding unlabelled minor ticks.
Method 1 - Manually construct axis label vectors
Concatenate the values that you would like to appear at major ticks with empty spaces defined using "". If you would like to add just one unlabelled minor tick in-between major tick values, you can construct the vector of axis labels like so:
axis_values <- c(0, "", 10, "", 20, "", 30, "", 40, "", 50)
Or if you'd like n unlabelled minor ticks:
# Where n = 2 and for an axis range [0, 50]
axis_values <- c(0, rep("", 2), 15, rep("", 2), 30, rep("", 2), 45, "")
The user can then supply this vector to the 'labels' argument in the ggplot2::scale_x_continuous or ggplot2::scale_y_continuous functions as long as the length of the vector of labels matches the length of the vector supplied to the 'breaks' argument in the same functions.
ggplot(df, aes(x = x, y = y)) +
geom_point() +
scale_x_continuous(breaks = seq(0, 50, 5), labels = axis_values, limits = c(0, 50)) +
scale_y_continuous(breaks = seq(0, 50, 5), labels = axis_values, limits = c(0, 50))
Method 2 - Define your own function for generating axis label vectors
This post describes a function to which the user can supply a vector of values to appear at major ticks, along with the number of unlabelled minor ticks desired:
insert_minor <- function(major_labs, n_minor) {
labs <- c( sapply( major_labs, function(x) c(x, rep("", n_minor) ) ) )
labs[1:(length(labs)-n_minor)]
}
# Generate plot
ggplot(df, aes(x = x, y = y)) +
geom_point() +
scale_x_continuous(breaks = seq(0, 50, 5), labels = insert_minor(major_labs = seq(0, 50, 10),
n_minor = 1), limits = c(0, 50)) +
scale_y_continuous(breaks = seq(0, 50, 5), labels = insert_minor(major_labs = seq(0, 50, 10),
n_minor = 1), limits = c(0, 50))
Method 2 is the best way of generating unlabelled minor ticks I've seen yet. However drawbacks are:
Not dummy-proof - Users need to make sure that the value given to the 'n_minor' argument is compatible with the data supplied to the 'breaks' and 'major_labs' arguments. Call me lazy, but I don't want to think about this when I'm trying to produce plots quickly.
Function management required - When you want to use this function in another script, you have to
retrieve it from the last script you used it in, or alternatively perhaps you can package it up in a library to call in future scripts.
In my eyes, the ideal solution is for the ggplot2 developers to add an argument to scale_x_continuous or scale_y_continuous ggplot2 functions that takes a user-defined value for the number of unlabelled minor ticks the user would like to add to their plot axes, which then takes the vector supplied to the 'breaks' argument and determines 'major_labs' in the background out of the user's sight.
Has anyone else found any other way of computing unlabelled minor ticks in ggplot2?
A quick, simple, and kinda sleek solution would be to define this one-liner labelling function that only shows breaks that occur at your chosen multiples:
label_at <- function(n) function(x) ifelse(x %% n == 0, x, "")
So you could do:
ggplot(df, aes(x = x, y = y)) +
geom_point() +
scale_x_continuous(breaks = seq(0, 50, 5), labels = label_at(10),
limits = c(0, 50)) +
scale_y_continuous(breaks = seq(0, 50, 5), labels = label_at(5),
limits = c(0, 50))
Which you can easily take to extremes:
ggplot(df, aes(x = x, y = y)) +
geom_point() +
scale_x_continuous(breaks = 1:50, labels = label_at(10), limits = c(0, 50)) +
scale_y_continuous(breaks = 1:50, labels = label_at(10), limits = c(0, 50))
I am looking for advice for plotting 2 similar wave forms with different y axes scales (one is mmHg and another is m/s) in the same plot. However, I would like to stagger the plots with respect to each other.
For example, using the below:
set.seed(123)
y <- sin(2*pi*x)
g <- sin(2*pi*x)+ rnorm(200, sd=0.1)
plot(y,type="l",
ann = F,
axes = F)
axis(side = 2)
par(new = T)
plot(g,type="l",
ann = F,
axes = F)
axis(side = 4)
Gives:
I would like to achieve something like this (see link below):
How to achieve this?
Here's a slightly cheaty solution:
x <- seq(from = 1, to = 3, by = 0.01)
y <- sin(2*pi*x)
set.seed(123)
g <- sin(2*pi*x)+ rnorm(length(x), sd=0.1)
stagger <- 2
glabels <- c(-1, 0, 1)
plot(c(min(y),max(y)+stagger) ~ c(1,length(y)), type="n", axes=FALSE, ann=FALSE)
lines(y)
axis(side = 2, at = min(y):max(y))
par(new = T)
lines(g+stagger)
axis(side = 4, at = glabels + stagger, labels = glabels)
Results in:
There's probably a better way to generate the positions and labels for the y-axis for g.
How to draw one line chart with 3 lines in R?
min<-c(1,1,4,5)
max<-c(8,9,8,10)
d<-c(-2,3,4,3)
We can use matplot after cbinding the vectors to create a matrix
matplot(cbind(min, max, d), type='l')
To change the 'x axis' labels, we can plot with xaxt=n and change the labels with axis
matplot(cbind(min, max, d), type='l', xaxt='n', col=2:4)
axis(1, at=1:4, labels=letters[1:4])
legend('topright', legend=c('min', 'max', 'd'), col=2:4, pch=1)
Another solution to complete #akrun's very good answer, and based on this page:
require(ggplot2)
require(reshape2)
require(directlabels)
min <- c( 1, 1, 4, 5)
max <- c( 8, 9, 8, 10)
d <- c(-2, 3, 4, 3)
df <- data.frame(min=min, max=max, d=d, x=1:4)
df.m <- melt(df,id.vars="x")
p <- ggplot(df.m, aes(x=x, y=value, color=variable)) + geom_line()
direct.label(p)
in R, with ecdf I can plot a empirical cumulative distribution function
plot(ecdf(mydata))
and with hist I can plot a histogram of my data
hist(mydata)
How I can plot the histogram and the ecdf in the same plot?
EDIT
I try make something like that
https://mathematica.stackexchange.com/questions/18723/how-do-i-overlay-a-histogram-with-a-plot-of-cdf
Also a bit late, here's another solution that extends #Christoph 's Solution with a second y-Axis.
par(mar = c(5,5,2,5))
set.seed(15)
dt <- rnorm(500, 50, 10)
h <- hist(
dt,
breaks = seq(0, 100, 1),
xlim = c(0,100))
par(new = T)
ec <- ecdf(dt)
plot(x = h$mids, y=ec(h$mids)*max(h$counts), col = rgb(0,0,0,alpha=0), axes=F, xlab=NA, ylab=NA)
lines(x = h$mids, y=ec(h$mids)*max(h$counts), col ='red')
axis(4, at=seq(from = 0, to = max(h$counts), length.out = 11), labels=seq(0, 1, 0.1), col = 'red', col.axis = 'red')
mtext(side = 4, line = 3, 'Cumulative Density', col = 'red')
The trick is the following: You don't add a line to your plot, but plot another plot on top, that's why we need par(new = T). Then you have to add the y-axis later on (otherwise it will be plotted over the y-axis on the left).
Credits go here (#tim_yates Answer) and there.
There are two ways to go about this. One is to ignore the different scales and use relative frequency in your histogram. This results in a harder to read histogram. The second way is to alter the scale of one or the other element.
I suspect this question will soon become interesting to you, particularly #hadley 's answer.
ggplot2 single scale
Here is a solution in ggplot2. I am not sure you will be satisfied with the outcome though because the CDF and histograms (count or relative) are on quite different visual scales. Note this solution has the data in a dataframe called mydata with the desired variable in x.
library(ggplot2)
set.seed(27272)
mydata <- data.frame(x= rexp(333, rate=4) + rnorm(333))
ggplot(mydata, aes(x)) +
stat_ecdf(color="red") +
geom_bar(aes(y = (..count..)/sum(..count..)))
base R multi scale
Here I will rescale the empirical CDF so that instead of a max value of 1, its maximum value is whatever bin has the highest relative frequency.
h <- hist(mydata$x, freq=F)
ec <- ecdf(mydata$x)
lines(x = knots(ec),
y=(1:length(mydata$x))/length(mydata$x) * max(h$density),
col ='red')
you can try a ggplot approach with a second axis
set.seed(15)
a <- rnorm(500, 50, 10)
# calculate ecdf with binsize 30
binsize=30
df <- tibble(x=seq(min(a), max(a), diff(range(a))/binsize)) %>%
bind_cols(Ecdf=with(.,ecdf(a)(x))) %>%
mutate(Ecdf_scaled=Ecdf*max(a))
# plot
ggplot() +
geom_histogram(aes(a), bins = binsize) +
geom_line(data = df, aes(x=x, y=Ecdf_scaled), color=2, size = 2) +
scale_y_continuous(name = "Density",sec.axis = sec_axis(trans = ~./max(a), name = "Ecdf"))
Edit
Since the scaling was wrong I added a second solution, calculatin everything in advance:
binsize=30
a_range= floor(range(a)) +c(0,1)
b <- seq(a_range[1], a_range[2], round(diff(a_range)/binsize)) %>% floor()
df_hist <- tibble(a) %>%
mutate(gr = cut(a,b, labels = floor(b[-1]), include.lowest = T, right = T)) %>%
count(gr) %>%
mutate(gr = as.character(gr) %>% as.numeric())
# calculate ecdf with binsize 30
df <- tibble(x=b) %>%
bind_cols(Ecdf=with(.,ecdf(a)(x))) %>%
mutate(Ecdf_scaled=Ecdf*max(df_hist$n))
ggplot(df_hist, aes(gr, n)) +
geom_col(width = 2, color = "white") +
geom_line(data = df, aes(x=x, y=Ecdf*max(df_hist$n)), color=2, size = 2) +
scale_y_continuous(name = "Density",sec.axis = sec_axis(trans = ~./max(df_hist$n), name = "Ecdf"))
As already pointed out, this is problematic because the plots you want to merge have such different y-scales. You can try
set.seed(15)
mydata<-runif(50)
hist(mydata, freq=F)
lines(ecdf(mydata))
to get
Although a bit late... Another version which is working with preset bins:
set.seed(15)
dt <- rnorm(500, 50, 10)
h <- hist(
dt,
breaks = seq(0, 100, 1),
xlim = c(0,100))
ec <- ecdf(dt)
lines(x = h$mids, y=ec(h$mids)*max(h$counts), col ='red')
lines(x = c(0,100), y=c(1,1)*max(h$counts), col ='red', lty = 3) # indicates 100%
lines(x = c(which.min(abs(ec(h$mids) - 0.9)), which.min(abs(ec(h$mids) - 0.9))), # indicates where 90% is reached
y = c(0, max(h$counts)), col ='black', lty = 3)
(Only the second y-axis is not working yet...)
In addition to previous answers, I wanted to have ggplot do the tedious calculation (in contrast to #Roman's solution, which was kindly enough updated upon my request), i.e., calculate and draw the histogram and calculate and overlay the ECDF. I came up with the following (pseudo code):
# 1. Prepare the plot
plot <- ggplot() + geom_hist(...)
# 2. Get the max value of Y axis as calculated in the previous step
maxPlotY <- max(ggplot_build(plot)$data[[1]]$y)
# 3. Overlay scaled ECDF and add secondary axis
plot +
stat_ecdf(aes(y=..y..*maxPlotY)) +
scale_y_continuous(name = "Density", sec.axis = sec_axis(trans = ~./maxPlotY, name = "ECDF"))
This way you don't need to calculate everything beforehand and feed the results to ggpplot. Just lay back and let it do everything for you!
I am trying to create the plot like following (many times I end up drawing a plot like this by hand, but this time I want to plot it myself).
Here is my data and my trial:
myd <- data.frame (period = c("Triassic", "Jurasic",
"Cretaceous", "Cenzoic"), myears = c(245, 208, 145, 65),
label = c(226, 176,105, 32 ))
myd2 <- data.frame (event = c("Diansaurs_strt", "Birds",
"Diansaurs_ext", "Human"), myears = c(235, 200, 60, 0.5))
myd2$x <- -0.25
with (myd2, plot(x,myears,ylim=c(0,250),
xlim = c(0, 10), axes=F,xlab="",ylab="",type="p",pch=17))
with (myd2,text(x,myears,event,pos=4,xpd=T))
axis(side=2,at = myd$label, labels = myd$period)
I have issues particularly matching of axis with plot and orientation of text and points. Any other idea or improvement help appreciated.
For constructing novel plots "from the ground up", and for maximal control over individual graphical elements, the grid graphical system is hard to beat:
library(grid)
## Set up plotting area with reasonable x-y limits
## and a "native" scale related to the scale of the data.
x <- -1:1
y <- extendrange(c(myd$myears, myd2$myears))
dvp <- dataViewport(x, y, name = "figure")
grid.newpage()
pushViewport(dvp)
## Plot the central timeline
grid.lines(unit(0, "native"), unit(c(0,245), "native"),
gp = gpar(col="dodgerblue"))
## Annotate LHS
grid.segments(x0=0.5, x1=0.47,
y0=unit(c(0, myd$myears), "native"),
y1=unit(c(0, myd$myears), "native"),
gp=gpar(col="dodgerblue"))
grid.text(label=c(0, myd$myears), x=0.44, y=unit(c(0, myd$myears), "native"))
grid.text(label=myd$period, x=0.3, y=unit(myd$label, "native"),
just=0, gp=gpar(col="dodgerblue", fontface="italic"))
## Annotate RHS
## Create a function that plots a pointer to the specified coordinate
pointer <- function(x, y, width=1) {
grid.polygon(x = x + unit(width*(c(0, .1, .1)), "npc"),
y = y + unit(width*(c(0, .03, -.03)), "npc"),
gp = gpar(fill="dodgerblue", col="blue", lwd=2))
}
## Call it once for each milestone
for(y in myd2$myears) {
pointer(unit(.5, "npc"), y=unit(y, "native"), width=0.3)
}
## Or, if you just want blue line segments instead of those gaudy pointers:
## grid.segments(x0=0.5, x1=0.53,
## y0=unit(c(myd2$myears), "native"),
## y1=unit(c(myd2$myears), "native"), gp=gpar(col="dodgerblue"))
grid.text(label=myd2$event, x=0.55, y=unit(myd2$myears, "native"),
just=0)
You can try something like this to get you started:
myd <- data.frame(period = c("", "Triassic", "Jurasic",
"Cretaceous", "Cenzoic", ""),
myears = c(260, 245, 208, 145, 65, -5),
label = c(260, 226, 176,105, 32, -5))
myd2 <- data.frame(event = c("Dinosaurs_strt", "Birds",
"Dinosaurs_ext", "Human"),
myears = c(235, 200, 60, 0.5))
myd2$x <- 1
with(myd2, plot(x, myears, ylim = c(-5, 250), xlim = c(0, 10),
axes = FALSE, xlab = "", ylab = "", type = "n"))
with(myd2, text(x, myears, event, pos = 4, xpd = TRUE))
axis(side = 2, at = myd$label, labels = myd$period, las = 2)
X0 <- rep(myd2$x, 4)
Y0 <- myd2$myears
X1 <- rep(-.25, 4)
Y1 <- Y0
arrows(X0, Y0, X1, Y1)
I've added an extra empty element at the start and end of your data in "myd" to help with the axis. Then, instead of using pch, I've used arrows to match the right hand labels with the axis.
Some tweaking could probably make it look a lot nicer.
Here are some enhancements ( I suggest to add 0 for now just to make scale well):
myd <- data.frame (period = c("Triassic", "Jurasic",
"Cretaceous", "Cenzoic", "now"), myears = c(245, 208, 145, 65, 0),
label = c(226, 176,105, 32, NA ))
myd2 <- data.frame (event = c("Diansaurs_strt", "Birds", "Diansaurs_ext", "Human"),
myears = c(235, 200, 60, 0.5))
myd2$x <- -0.25
with (myd2, plot(x,myears,ylim=c(0,250), xlim = c(0, 10),
axes=F,xlab="",ylab="",type="p",pch=17, col = "green"))
with (myd2, plot(x,myears,ylim=c(0,250),
xlim = c(0, 10), axes=F,xlab="",ylab="",type="p",pch="-", col = "green"))
with (myd2,text(x,myears,event,pos=4,xpd=T), col = "green")
axis(side=2,at = myd$label, labels = myd$period, tick = FALSE,
las = 2, col = "green", )
axis(side=2,at = myd$myears, labels = myd$myears, las = 2, col = "green")
There are few issues remaining you might want to change oriantation of the arrow (I belief that you can someway find <- symbol, but I do not know how to).
For drawing the triangles look at the my.symbols and ms.polygon functions in the TeachingDemos package.
In your right graph above the Dinosaurs are moved up, if you want this in general (moving labels that would otherwise be too close or overlap) then look at the spread.labs function in the TeachingDemos package.
Some other possible functions that could help with the plot are text, mtext, grconvertX, grconvertY, segments, and axis.