Is there a way to get the axes, with labels in the center of a ggplot2 plot, like a traditional graphing calculator? I've looked through the docs and there doesn't seem to be that functionality, but other plotting packages are not as graphically customizable as ggplot2. To clarify, I was looking to go from something like this:
To this:
The first plot is made with the following code:
dat = data.frame(x = 1, y =1)
p = ggplot(data = dat, aes(x=x, y=y)) + geom_point(size = 5)
p + xlim(-2,2) + ylim(-2,2)
The second plot is made with Mathematica. The main problem I am having is figuring out how to make the axis, with labels, go to the center (I can make the theme blank, etc., no problem). There seems to be no theme parameter you can edit to make this a quick fix.
I think this is what you are looking for:
I have constructed a function that does just that:
theme_geometry <- function(xvals, yvals, xgeo = 0, ygeo = 0,
color = "black", size = 1,
xlab = "x", ylab = "y",
ticks = 10,
textsize = 3,
xlimit = max(abs(xvals),abs(yvals)),
ylimit = max(abs(yvals),abs(xvals)),
epsilon = max(xlimit,ylimit)/50){
#INPUT:
#xvals .- Values of x that will be plotted
#yvals .- Values of y that will be plotted
#xgeo .- x intercept value for y axis
#ygeo .- y intercept value for x axis
#color .- Default color for axis
#size .- Line size for axis
#xlab .- Label for x axis
#ylab .- Label for y axis
#ticks .- Number of ticks to add to plot in each axis
#textsize .- Size of text for ticks
#xlimit .- Limit value for x axis
#ylimit .- Limit value for y axis
#epsilon .- Parameter for small space
#Create axis
xaxis <- data.frame(x_ax = c(-xlimit, xlimit), y_ax = rep(ygeo,2))
yaxis <- data.frame(x_ax = rep(xgeo, 2), y_ax = c(-ylimit, ylimit))
#Add axis
theme.list <-
list(
theme_void(), #Empty the current theme
geom_line(aes(x = x_ax, y = y_ax), color = color, size = size, data = xaxis),
geom_line(aes(x = x_ax, y = y_ax), color = color, size = size, data = yaxis),
annotate("text", x = xlimit + 2*epsilon, y = ygeo, label = xlab, size = 2*textsize),
annotate("text", x = xgeo, y = ylimit + 4*epsilon, label = ylab, size = 2*textsize),
xlim(-xlimit - 7*epsilon, xlimit + 7*epsilon), #Add limits to make it square
ylim(-ylimit - 7*epsilon, ylimit + 7*epsilon) #Add limits to make it square
)
#Add ticks programatically
ticks_x <- round(seq(-xlimit, xlimit, length.out = ticks),2)
ticks_y <- round(seq(-ylimit, ylimit, length.out = ticks),2)
#Add ticks of x axis
nlist <- length(theme.list)
for (k in 1:ticks){
#Create data frame for ticks in x axis
xtick <- data.frame(xt = rep(ticks_x[k], 2),
yt = c(xgeo + epsilon, xgeo - epsilon))
#Create data frame for ticks in y axis
ytick <- data.frame(xt = c(ygeo + epsilon, ygeo - epsilon),
yt = rep(ticks_y[k], 2))
#Add ticks to geom line for x axis
theme.list[[nlist + 4*k-3]] <- geom_line(aes(x = xt, y = yt),
data = xtick, size = size,
color = color)
#Add labels to the x-ticks
theme.list[[nlist + 4*k-2]] <- annotate("text",
x = ticks_x[k],
y = ygeo - 2.5*epsilon,
size = textsize,
label = paste(ticks_x[k]))
#Add ticks to geom line for y axis
theme.list[[nlist + 4*k-1]] <- geom_line(aes(x = xt, y = yt),
data = ytick, size = size,
color = color)
#Add labels to the y-ticks
theme.list[[nlist + 4*k]] <- annotate("text",
x = xgeo - 2.5*epsilon,
y = ticks_y[k],
size = textsize,
label = paste(ticks_y[k]))
}
#Add theme
#theme.list[[3]] <-
return(theme.list)
}
As an example you can run the following code to create an image similar to the one above:
simdata <- data.frame(x = rnorm(50), y = rnorm(50))
ggplot(simdata) +
theme_geometry(simdata$x, simdata$y) +
geom_point(aes(x = x, y = y), size = 3, color = "red") +
ggtitle("More geometric example")
ggsave("Example1.png", width = 10, height = 10)
There are some other useful answers, but the following comes closer to the target visual and avoids looping:
library(ggplot2)
library(magrittr)
# constants
axis_begin <- -2
axis_end <- 2
total_ticks <- 21
# DATA ----
# point to plot
my_point <- data.frame(x=1,y=1)
# chart junk data
tick_frame <-
data.frame(ticks = seq(axis_begin, axis_end, length.out = total_ticks),
zero=0) %>%
subset(ticks != 0)
lab_frame <- data.frame(lab = seq(axis_begin, axis_end),
zero = 0) %>%
subset(lab != 0)
tick_sz <- (tail(lab_frame$lab, 1) - lab_frame$lab[1]) / 128
# PLOT ----
ggplot(my_point, aes(x,y)) +
# CHART JUNK
# y axis line
geom_segment(x = 0, xend = 0,
y = lab_frame$lab[1], yend = tail(lab_frame$lab, 1),
size = 0.5) +
# x axis line
geom_segment(y = 0, yend = 0,
x = lab_frame$lab[1], xend = tail(lab_frame$lab, 1),
size = 0.5) +
# x ticks
geom_segment(data = tick_frame,
aes(x = ticks, xend = ticks,
y = zero, yend = zero + tick_sz)) +
# y ticks
geom_segment(data = tick_frame,
aes(x = zero, xend = zero + tick_sz,
y = ticks, yend = ticks)) +
# labels
geom_text(data=lab_frame, aes(x=lab, y=zero, label=lab),
family = 'Times', vjust=1.5) +
geom_text(data=lab_frame, aes(x=zero, y=lab, label=lab),
family = 'Times', hjust=1.5) +
# THE DATA POINT
geom_point(color='navy', size=5) +
theme_void()
A first approximation:
dat = data.frame(x = 1, y =1)
p = ggplot(data = dat, aes(x=x, y=y)) + theme_bw() +
geom_point(size = 5) +
geom_hline(yintercept = 0) +
geom_vline(xintercept = 0)
Adjust limits as per SlowLearner's answer.
I would just use xlim and ylim.
dat = data.frame(x = 1, y =1)
p = ggplot(data = dat, aes(x=x, y=y)) +
geom_point(size = 5) +
xlim(-2, 2) +
ylim(-2, 2)
p
Recently, I've added coord_axes_inside() to ggh4x that might be suitable for this problem.
library(ggplot2)
library(ggh4x)
df <- data.frame(x = c(-1, 1))
ggplot(df, aes(x, x)) +
geom_point() +
theme(axis.line = element_line()) +
coord_axes_inside(labels_inside = TRUE)
Aside from being syntactically pretty convenient, it really plots axes, so they respond to theme elements and guide declarations as you'd expect.
last_plot() +
guides(x = guide_axis(angle = 45)) +
theme(axis.ticks.length = unit(-5, "pt"))
Created on 2022-09-01 by the reprex package (v2.0.0)
For centering, I recommend setting the scale limits to centering functions:
last_plot() +
scale_x_continuous(limits = ~ c(-1, 1) * max(abs(.x))) +
scale_y_continuous(limits = ~ c(-1, 1) * max(abs(.x)))
(Disclaimer: I'm the author of ggh4x)
Related
I want to plot a diagram with a log scales y axis:
data <- data.frame(x = seq_len(5), y = c(2,1,100,500,30))
ggplot(data) + aes(x,y) + geom_col() + scale_y_log10()
But because the horizontal axis always crosses the vertical axis at y = 1, the bar for y = 1 is never drawn.
I know that log(1) = 0, but can I set the plot base to 0.1 to let the bar start below 1?
Can't you just multiply the values of y by 10 but divide the labels by the same amount?
ggplot(data) + aes(x, y * 10) + geom_col() +
scale_y_log10(labels = function(x) x/10, name = "y")
You could do the same with a histogram:
set.seed(2)
ggplot(data.frame(x = rnorm(1000)), aes(x)) +
geom_histogram(aes(y = ..count.. * 10), color = "black", fill = "gold") +
scale_y_log10(labels = function(x) x/10, name = "count")
An alternative solution is to place the bottom of the bars at -Inf, so that the log10(1) still shows up. This is a bit tricky, because you have to reparameterise the bars as rectangles and get the geom to understand that the -Inf is after scale transformation.
library(ggplot2)
data <- data.frame(x = seq_len(5), y = c(2,1,100,500,30))
ggplot(data) +
geom_rect(
aes(xmin = x - 0.45, xmax = x + 0.45,
ymin = stage(1, after_scale = -Inf), ymax = y)
) +
scale_y_log10()
Created on 2020-11-03 by the reprex package (v0.3.0)
Is there a way to get the axes, with labels in the center of a ggplot2 plot, like a traditional graphing calculator? I've looked through the docs and there doesn't seem to be that functionality, but other plotting packages are not as graphically customizable as ggplot2. To clarify, I was looking to go from something like this:
To this:
The first plot is made with the following code:
dat = data.frame(x = 1, y =1)
p = ggplot(data = dat, aes(x=x, y=y)) + geom_point(size = 5)
p + xlim(-2,2) + ylim(-2,2)
The second plot is made with Mathematica. The main problem I am having is figuring out how to make the axis, with labels, go to the center (I can make the theme blank, etc., no problem). There seems to be no theme parameter you can edit to make this a quick fix.
I think this is what you are looking for:
I have constructed a function that does just that:
theme_geometry <- function(xvals, yvals, xgeo = 0, ygeo = 0,
color = "black", size = 1,
xlab = "x", ylab = "y",
ticks = 10,
textsize = 3,
xlimit = max(abs(xvals),abs(yvals)),
ylimit = max(abs(yvals),abs(xvals)),
epsilon = max(xlimit,ylimit)/50){
#INPUT:
#xvals .- Values of x that will be plotted
#yvals .- Values of y that will be plotted
#xgeo .- x intercept value for y axis
#ygeo .- y intercept value for x axis
#color .- Default color for axis
#size .- Line size for axis
#xlab .- Label for x axis
#ylab .- Label for y axis
#ticks .- Number of ticks to add to plot in each axis
#textsize .- Size of text for ticks
#xlimit .- Limit value for x axis
#ylimit .- Limit value for y axis
#epsilon .- Parameter for small space
#Create axis
xaxis <- data.frame(x_ax = c(-xlimit, xlimit), y_ax = rep(ygeo,2))
yaxis <- data.frame(x_ax = rep(xgeo, 2), y_ax = c(-ylimit, ylimit))
#Add axis
theme.list <-
list(
theme_void(), #Empty the current theme
geom_line(aes(x = x_ax, y = y_ax), color = color, size = size, data = xaxis),
geom_line(aes(x = x_ax, y = y_ax), color = color, size = size, data = yaxis),
annotate("text", x = xlimit + 2*epsilon, y = ygeo, label = xlab, size = 2*textsize),
annotate("text", x = xgeo, y = ylimit + 4*epsilon, label = ylab, size = 2*textsize),
xlim(-xlimit - 7*epsilon, xlimit + 7*epsilon), #Add limits to make it square
ylim(-ylimit - 7*epsilon, ylimit + 7*epsilon) #Add limits to make it square
)
#Add ticks programatically
ticks_x <- round(seq(-xlimit, xlimit, length.out = ticks),2)
ticks_y <- round(seq(-ylimit, ylimit, length.out = ticks),2)
#Add ticks of x axis
nlist <- length(theme.list)
for (k in 1:ticks){
#Create data frame for ticks in x axis
xtick <- data.frame(xt = rep(ticks_x[k], 2),
yt = c(xgeo + epsilon, xgeo - epsilon))
#Create data frame for ticks in y axis
ytick <- data.frame(xt = c(ygeo + epsilon, ygeo - epsilon),
yt = rep(ticks_y[k], 2))
#Add ticks to geom line for x axis
theme.list[[nlist + 4*k-3]] <- geom_line(aes(x = xt, y = yt),
data = xtick, size = size,
color = color)
#Add labels to the x-ticks
theme.list[[nlist + 4*k-2]] <- annotate("text",
x = ticks_x[k],
y = ygeo - 2.5*epsilon,
size = textsize,
label = paste(ticks_x[k]))
#Add ticks to geom line for y axis
theme.list[[nlist + 4*k-1]] <- geom_line(aes(x = xt, y = yt),
data = ytick, size = size,
color = color)
#Add labels to the y-ticks
theme.list[[nlist + 4*k]] <- annotate("text",
x = xgeo - 2.5*epsilon,
y = ticks_y[k],
size = textsize,
label = paste(ticks_y[k]))
}
#Add theme
#theme.list[[3]] <-
return(theme.list)
}
As an example you can run the following code to create an image similar to the one above:
simdata <- data.frame(x = rnorm(50), y = rnorm(50))
ggplot(simdata) +
theme_geometry(simdata$x, simdata$y) +
geom_point(aes(x = x, y = y), size = 3, color = "red") +
ggtitle("More geometric example")
ggsave("Example1.png", width = 10, height = 10)
There are some other useful answers, but the following comes closer to the target visual and avoids looping:
library(ggplot2)
library(magrittr)
# constants
axis_begin <- -2
axis_end <- 2
total_ticks <- 21
# DATA ----
# point to plot
my_point <- data.frame(x=1,y=1)
# chart junk data
tick_frame <-
data.frame(ticks = seq(axis_begin, axis_end, length.out = total_ticks),
zero=0) %>%
subset(ticks != 0)
lab_frame <- data.frame(lab = seq(axis_begin, axis_end),
zero = 0) %>%
subset(lab != 0)
tick_sz <- (tail(lab_frame$lab, 1) - lab_frame$lab[1]) / 128
# PLOT ----
ggplot(my_point, aes(x,y)) +
# CHART JUNK
# y axis line
geom_segment(x = 0, xend = 0,
y = lab_frame$lab[1], yend = tail(lab_frame$lab, 1),
size = 0.5) +
# x axis line
geom_segment(y = 0, yend = 0,
x = lab_frame$lab[1], xend = tail(lab_frame$lab, 1),
size = 0.5) +
# x ticks
geom_segment(data = tick_frame,
aes(x = ticks, xend = ticks,
y = zero, yend = zero + tick_sz)) +
# y ticks
geom_segment(data = tick_frame,
aes(x = zero, xend = zero + tick_sz,
y = ticks, yend = ticks)) +
# labels
geom_text(data=lab_frame, aes(x=lab, y=zero, label=lab),
family = 'Times', vjust=1.5) +
geom_text(data=lab_frame, aes(x=zero, y=lab, label=lab),
family = 'Times', hjust=1.5) +
# THE DATA POINT
geom_point(color='navy', size=5) +
theme_void()
A first approximation:
dat = data.frame(x = 1, y =1)
p = ggplot(data = dat, aes(x=x, y=y)) + theme_bw() +
geom_point(size = 5) +
geom_hline(yintercept = 0) +
geom_vline(xintercept = 0)
Adjust limits as per SlowLearner's answer.
I would just use xlim and ylim.
dat = data.frame(x = 1, y =1)
p = ggplot(data = dat, aes(x=x, y=y)) +
geom_point(size = 5) +
xlim(-2, 2) +
ylim(-2, 2)
p
Recently, I've added coord_axes_inside() to ggh4x that might be suitable for this problem.
library(ggplot2)
library(ggh4x)
df <- data.frame(x = c(-1, 1))
ggplot(df, aes(x, x)) +
geom_point() +
theme(axis.line = element_line()) +
coord_axes_inside(labels_inside = TRUE)
Aside from being syntactically pretty convenient, it really plots axes, so they respond to theme elements and guide declarations as you'd expect.
last_plot() +
guides(x = guide_axis(angle = 45)) +
theme(axis.ticks.length = unit(-5, "pt"))
Created on 2022-09-01 by the reprex package (v2.0.0)
For centering, I recommend setting the scale limits to centering functions:
last_plot() +
scale_x_continuous(limits = ~ c(-1, 1) * max(abs(.x))) +
scale_y_continuous(limits = ~ c(-1, 1) * max(abs(.x)))
(Disclaimer: I'm the author of ggh4x)
As you can see on the image, R automatically assigns the values 0, 0.25... 1 for the size of the point. I was wondering if I could replace the 0, 0.25... 1 and make these text values instead while keeping the actual numerical values from the data.
library(ggplot2)
library(scales)
data(SLC4A1, package="ggplot2")
SLC4A1 <- read.csv(file.choose(), header = TRUE)
# bubble chart showing position of polymorphisms on gene, the frequency of each of these
# polymorphisms, where they are prominent on earth, and p-value
SLC4A1ggplot <- ggplot(SLC4A1, aes(Position, log10(Frequency)))+
geom_jitter(aes(col=Geographical.Location, size =(p.value)))+
labs(subtitle="Frequency of Various Polymorphisms", title="SLC4A1 Gene") +
labs(color = "Geographical Location") +
labs(size = "p-value") + labs(x = "Position of Polymorphism on SLC4A1 Gene") +
scale_size_continuous(range=c(1,4.5), trans = "reverse") +
guides(size = guide_legend(reverse = TRUE))
library(tidyver)
df <- data.frame(x = 1:5, y = 1:5,z = 1:5)
ggplot(df,aes(x = x, y = y, size = z)) +
geom_point()
ggplot(df,aes(x = x, y = y, size = z)) +
geom_point() +
scale_size_continuous(range = 1:2) # control range of circle size
See more here:
https://ggplot2.tidyverse.org/reference/scale_size.html
I have a time series with different variables and different units that I want to display on the same plot.
ggplot does not support multiple axis (as explained here), so I followed the advice and tried to plot the curves with facets:
x <- seq(0, 10, by = 0.1)
y1 <- sin(x)
y2 <- sin(x + pi/4)
y3 <- cos(x)
my.df <- data.frame(time = x, currentA = y1, currentB = y2, voltage = y3)
my.df <- melt(my.df, id.vars = "time")
my.df$Unit <- as.factor(rep(c("A", "A", "V"), each = length(x)))
ggplot(my.df, aes(x = time, y = value)) + geom_line(aes(color = variable)) + facet_wrap(~Unit, scales = "free_y", nrow = 2)
Here is the result:
The thing is that there is only one y label, saying "value" and I would like two: one with "Currents (A)" and the other one with "Voltage (V)".
Is this possible?
In ggplot2_2.2.1 you could move the panel strips to be the y axis labels by using the strip.position argument in facet_wrap. Using this method you don't have both strip labels and different y axis labels, though, which may not be ideal.
Once you've put the strip labels to be on the y axis (the "left"), you can change the labels by giving a named vector to labeller to be used as a look-up table.
The strip labels can be moved outside the y-axis via strip.placement in theme.
Remove the strip background and y-axis labels to get a final graphic with two panes and distinct y-axis labels.
ggplot(my.df, aes(x = time, y = value) ) +
geom_line( aes(color = variable) ) +
facet_wrap(~Unit, scales = "free_y", nrow = 2,
strip.position = "left",
labeller = as_labeller(c(A = "Currents (A)", V = "Voltage (V)") ) ) +
ylab(NULL) +
theme(strip.background = element_blank(),
strip.placement = "outside")
Removing the strip from the top makes the two panes pretty close together. To change the spacing you can add, e.g., panel.margin = unit(1, "lines") to theme.
Here is a manual solution, this question can be solved faster and more easily with other methods:
Working with margins and labs will permit you to stick the 2 plot if you really need it.
x <- seq(0, 10, by = 0.1)
y1 <- sin(x)
y2 <- sin(x + pi/4)
y3 <- cos(x)
my.df <- data.frame(time = x, currentA = y1, currentB = y2, voltage = y3)
my.df <- melt(my.df, id.vars = "time")
my.df$Unit <- as.factor(rep(c("A", "A", "V"), each = length(x)))
# Create 3 plots :
# A: currentA and currentB plot
A = ggplot(my.df, aes(x = time, y = value, color = variable, alpha = variable)) +
geom_line() + ylab("A") +
scale_alpha_manual(values = c("currentA" = 1, "currentB" = 1, "voltage" = 0)) +
guides(alpha = F, color = F)
# B: voltage plot
B = ggplot(my.df, aes(x = time, y = value, color = variable, alpha = variable)) +
geom_line() + ylab("A") +
scale_alpha_manual(values = c("currentA" = 0, "currentB" = 0, "voltage" = 1)) +
guides(alpha = F, color = F)
# C: get the legend
C = ggplot(my.df, aes(x = time, y = value, color = variable)) + geom_line() + ylab("A")
library(gridExtra)
# http://stackoverflow.com/questions/12539348/ggplot-separate-legend-and-plot
# use this trick to get the legend as a grob object
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]]
legend
}
#extract legend from C plot
legend = g_legend(C)
#arrange grob (the 2 plots)
plots = arrangeGrob(A,B)
# arrange the plots and the legend
grid.arrange(plots, legend , ncol = 2, widths = c(3/4,1/4))
Super late entry, but just solved this for myself...A super simple hack that lets you retain all strip.text is to just enter a bunch of spaces in ylab(" Voltage (V) Current (A)\n") in order to put the title across both plots. You can left-justify using axis.title.y = element_text(hjust = 0.25) to align everything well.
library(tidyverse)
library(reshape2)
x <- seq(0, 10, by = 0.1)
y1 <- sin(x)
y2 <- sin(x + pi / 4)
y3 <- cos(x)
my.df <-
data.frame(
time = x,
currentA = y1,
currentB = y2,
voltage = y3
)
my.df <- melt(my.df, id.vars = "time")
my.df$Unit <- as.factor(rep(c("A", "A", "V"), each = length(x)))
ggplot(my.df, aes(x = time, y = value)) +
geom_line(aes(color = variable)) +
facet_wrap( ~Unit, scales = "free_y", nrow = 2) +
ylab(" Voltage (V) Current (A)\n") +
theme(
axis.title.y = element_text(hjust = 0.25)
)
ggsave("Volts_Amps.png",
height = 6,
width = 8,
units = "in",
dpi = 600)
Is there a way to get the axes, with labels in the center of a ggplot2 plot, like a traditional graphing calculator? I've looked through the docs and there doesn't seem to be that functionality, but other plotting packages are not as graphically customizable as ggplot2. To clarify, I was looking to go from something like this:
To this:
The first plot is made with the following code:
dat = data.frame(x = 1, y =1)
p = ggplot(data = dat, aes(x=x, y=y)) + geom_point(size = 5)
p + xlim(-2,2) + ylim(-2,2)
The second plot is made with Mathematica. The main problem I am having is figuring out how to make the axis, with labels, go to the center (I can make the theme blank, etc., no problem). There seems to be no theme parameter you can edit to make this a quick fix.
I think this is what you are looking for:
I have constructed a function that does just that:
theme_geometry <- function(xvals, yvals, xgeo = 0, ygeo = 0,
color = "black", size = 1,
xlab = "x", ylab = "y",
ticks = 10,
textsize = 3,
xlimit = max(abs(xvals),abs(yvals)),
ylimit = max(abs(yvals),abs(xvals)),
epsilon = max(xlimit,ylimit)/50){
#INPUT:
#xvals .- Values of x that will be plotted
#yvals .- Values of y that will be plotted
#xgeo .- x intercept value for y axis
#ygeo .- y intercept value for x axis
#color .- Default color for axis
#size .- Line size for axis
#xlab .- Label for x axis
#ylab .- Label for y axis
#ticks .- Number of ticks to add to plot in each axis
#textsize .- Size of text for ticks
#xlimit .- Limit value for x axis
#ylimit .- Limit value for y axis
#epsilon .- Parameter for small space
#Create axis
xaxis <- data.frame(x_ax = c(-xlimit, xlimit), y_ax = rep(ygeo,2))
yaxis <- data.frame(x_ax = rep(xgeo, 2), y_ax = c(-ylimit, ylimit))
#Add axis
theme.list <-
list(
theme_void(), #Empty the current theme
geom_line(aes(x = x_ax, y = y_ax), color = color, size = size, data = xaxis),
geom_line(aes(x = x_ax, y = y_ax), color = color, size = size, data = yaxis),
annotate("text", x = xlimit + 2*epsilon, y = ygeo, label = xlab, size = 2*textsize),
annotate("text", x = xgeo, y = ylimit + 4*epsilon, label = ylab, size = 2*textsize),
xlim(-xlimit - 7*epsilon, xlimit + 7*epsilon), #Add limits to make it square
ylim(-ylimit - 7*epsilon, ylimit + 7*epsilon) #Add limits to make it square
)
#Add ticks programatically
ticks_x <- round(seq(-xlimit, xlimit, length.out = ticks),2)
ticks_y <- round(seq(-ylimit, ylimit, length.out = ticks),2)
#Add ticks of x axis
nlist <- length(theme.list)
for (k in 1:ticks){
#Create data frame for ticks in x axis
xtick <- data.frame(xt = rep(ticks_x[k], 2),
yt = c(xgeo + epsilon, xgeo - epsilon))
#Create data frame for ticks in y axis
ytick <- data.frame(xt = c(ygeo + epsilon, ygeo - epsilon),
yt = rep(ticks_y[k], 2))
#Add ticks to geom line for x axis
theme.list[[nlist + 4*k-3]] <- geom_line(aes(x = xt, y = yt),
data = xtick, size = size,
color = color)
#Add labels to the x-ticks
theme.list[[nlist + 4*k-2]] <- annotate("text",
x = ticks_x[k],
y = ygeo - 2.5*epsilon,
size = textsize,
label = paste(ticks_x[k]))
#Add ticks to geom line for y axis
theme.list[[nlist + 4*k-1]] <- geom_line(aes(x = xt, y = yt),
data = ytick, size = size,
color = color)
#Add labels to the y-ticks
theme.list[[nlist + 4*k]] <- annotate("text",
x = xgeo - 2.5*epsilon,
y = ticks_y[k],
size = textsize,
label = paste(ticks_y[k]))
}
#Add theme
#theme.list[[3]] <-
return(theme.list)
}
As an example you can run the following code to create an image similar to the one above:
simdata <- data.frame(x = rnorm(50), y = rnorm(50))
ggplot(simdata) +
theme_geometry(simdata$x, simdata$y) +
geom_point(aes(x = x, y = y), size = 3, color = "red") +
ggtitle("More geometric example")
ggsave("Example1.png", width = 10, height = 10)
There are some other useful answers, but the following comes closer to the target visual and avoids looping:
library(ggplot2)
library(magrittr)
# constants
axis_begin <- -2
axis_end <- 2
total_ticks <- 21
# DATA ----
# point to plot
my_point <- data.frame(x=1,y=1)
# chart junk data
tick_frame <-
data.frame(ticks = seq(axis_begin, axis_end, length.out = total_ticks),
zero=0) %>%
subset(ticks != 0)
lab_frame <- data.frame(lab = seq(axis_begin, axis_end),
zero = 0) %>%
subset(lab != 0)
tick_sz <- (tail(lab_frame$lab, 1) - lab_frame$lab[1]) / 128
# PLOT ----
ggplot(my_point, aes(x,y)) +
# CHART JUNK
# y axis line
geom_segment(x = 0, xend = 0,
y = lab_frame$lab[1], yend = tail(lab_frame$lab, 1),
size = 0.5) +
# x axis line
geom_segment(y = 0, yend = 0,
x = lab_frame$lab[1], xend = tail(lab_frame$lab, 1),
size = 0.5) +
# x ticks
geom_segment(data = tick_frame,
aes(x = ticks, xend = ticks,
y = zero, yend = zero + tick_sz)) +
# y ticks
geom_segment(data = tick_frame,
aes(x = zero, xend = zero + tick_sz,
y = ticks, yend = ticks)) +
# labels
geom_text(data=lab_frame, aes(x=lab, y=zero, label=lab),
family = 'Times', vjust=1.5) +
geom_text(data=lab_frame, aes(x=zero, y=lab, label=lab),
family = 'Times', hjust=1.5) +
# THE DATA POINT
geom_point(color='navy', size=5) +
theme_void()
A first approximation:
dat = data.frame(x = 1, y =1)
p = ggplot(data = dat, aes(x=x, y=y)) + theme_bw() +
geom_point(size = 5) +
geom_hline(yintercept = 0) +
geom_vline(xintercept = 0)
Adjust limits as per SlowLearner's answer.
I would just use xlim and ylim.
dat = data.frame(x = 1, y =1)
p = ggplot(data = dat, aes(x=x, y=y)) +
geom_point(size = 5) +
xlim(-2, 2) +
ylim(-2, 2)
p
Recently, I've added coord_axes_inside() to ggh4x that might be suitable for this problem.
library(ggplot2)
library(ggh4x)
df <- data.frame(x = c(-1, 1))
ggplot(df, aes(x, x)) +
geom_point() +
theme(axis.line = element_line()) +
coord_axes_inside(labels_inside = TRUE)
Aside from being syntactically pretty convenient, it really plots axes, so they respond to theme elements and guide declarations as you'd expect.
last_plot() +
guides(x = guide_axis(angle = 45)) +
theme(axis.ticks.length = unit(-5, "pt"))
Created on 2022-09-01 by the reprex package (v2.0.0)
For centering, I recommend setting the scale limits to centering functions:
last_plot() +
scale_x_continuous(limits = ~ c(-1, 1) * max(abs(.x))) +
scale_y_continuous(limits = ~ c(-1, 1) * max(abs(.x)))
(Disclaimer: I'm the author of ggh4x)