Traditional approach, pre-computation w/ vline

library(tidyverse)
mean_wt <- data.frame(cyl = c(4, 6, 8), 
                      wt = c(2.28, 3.11, 4.00))

ggplot(mtcars, aes(wt,mpg,  colour = wt)) +
  geom_point() +
  geom_vline(aes(xintercept = wt, colour = wt), mean_wt) +
  facet_wrap(~ cyl,
             ncol = 1)

Auto compute experiments. Use base ggplot2 to give compute the xmean

Use geom_smooth, w/ ‘y’ orientation and formula y~1

ggplot(mtcars) + 
  aes(x = wt, y = mpg, color = factor(am)) + 
  geom_rug() + 
  geom_point() + 
  geom_smooth(orientation = "y", xseq = c(10,35),
              method = lm, se = F,
              formula = y ~ 1)

# can fullrange be used?  Unsure of what's going on here, maybe x and y got switched
ggplot(mtcars) + 
  aes(x = wt, y = mpg, color = factor(am)) + 
  geom_rug() + 
  geom_point() + 
  geom_smooth(orientation = "y", 
              method = lm, se = T,
              formula = y ~ 1,
              fullrange = TRUE)

# can fullrange be used?  Unsure of what's going on here, maybe x and y got switched
ggplot(mtcars) + 
  aes(x = mpg, y = wt, color = factor(am)) + 
  geom_rug() + 
  geom_point() + 
  geom_smooth(orientation = "x", 
              method = lm, se = FALSE,
              formula = y ~ 1,
              fullrange = TRUE)

# seems true...
last_plot() + 
  coord_flip()

univariate (no y) gets more hacky

ggplot(mtcars) + 
  aes(x = wt) + 
  geom_rug() + 
  geom_histogram() +
  geom_smooth(
    orientation = "y", 
              method = lm, 
              formula = y ~ 1,
              se = F, 
              aes(y = new_var), # it a required aesthetic - you can kind of put any var so we just put x again...      
              data = . %>% mutate(new_var = row_number()), # so you can use the genaric new_var...
              xseq = c(0, Inf) # determined looking histogram first
              )
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

# not good cause of the row numbers 'randomly' covering different groups
last_plot() + 
  aes(color = factor(am))
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

geom_x_mean <- function(...){
  
    geom_smooth(
    orientation = "y", 
              method = lm, 
              formula = y ~ 1,
              se = F, 
              aes(y = new_var), # it a required aesthetic - you can kind of put any var so we just put x again...
              data = . %>% mutate(new_var = row_number()),
              xseq = c(0, Inf), # determined looking histogram first
    ...
              )
  
}


ggplot(mtcars) + 
  aes(x = wt) + 
  geom_rug() + 
  geom_histogram() +
  geom_x_mean(linetype = "dotted")
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

# not good cause of the row numbers 'randomly' covering different groups
last_plot() + 
  aes(color = factor(am))
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

# Not very hopeful about fullrange at present but maybe after bug fix?

Use stat_summary w/ geom = “point”, shape = 3 (cross hairs)

a terrible(ly fun?) hack

ggplot(mtcars) + 
  aes(x = wt) +
  geom_rug() + 
  geom_histogram() +
  stat_summary(fun = "mean", # calculate the mean
               orientation = "y", # for x
               geom = "point", # place a point there
               aes(y = 0), # with the value 0 for y
               shape = 3, # use the 'cross hairs' shape
               size = 300, # make the cross hairs huge
               show.legend = F # legend with will overwhelm plot because of size
               ) 
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
last_plot() +
  aes(color = factor(am)) + 
  aes(fill = factor(am)) +
  facet_wrap(~factor(am),
             ncol = 1) 
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.


use geom_segment with stage, after_stat?

library(tidyverse)
ggplot(mtcars) + 
  aes(x = wt) +
  geom_rug() + 
  geom_histogram() + 
  geom_segment(aes(xend = wt, y = -Inf, yend = Inf))
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

layer_data(last_plot())
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
##        x PANEL group colour linewidth linetype alpha
## 1  2.620     1    -1  black       0.5        1    NA
## 2  2.875     1    -1  black       0.5        1    NA
## 3  2.320     1    -1  black       0.5        1    NA
## 4  3.215     1    -1  black       0.5        1    NA
## 5  3.440     1    -1  black       0.5        1    NA
## 6  3.460     1    -1  black       0.5        1    NA
## 7  3.570     1    -1  black       0.5        1    NA
## 8  3.190     1    -1  black       0.5        1    NA
## 9  3.150     1    -1  black       0.5        1    NA
## 10 3.440     1    -1  black       0.5        1    NA
## 11 3.440     1    -1  black       0.5        1    NA
## 12 4.070     1    -1  black       0.5        1    NA
## 13 3.730     1    -1  black       0.5        1    NA
## 14 3.780     1    -1  black       0.5        1    NA
## 15 5.250     1    -1  black       0.5        1    NA
## 16 5.424     1    -1  black       0.5        1    NA
## 17 5.345     1    -1  black       0.5        1    NA
## 18 2.200     1    -1  black       0.5        1    NA
## 19 1.615     1    -1  black       0.5        1    NA
## 20 1.835     1    -1  black       0.5        1    NA
## 21 2.465     1    -1  black       0.5        1    NA
## 22 3.520     1    -1  black       0.5        1    NA
## 23 3.435     1    -1  black       0.5        1    NA
## 24 3.840     1    -1  black       0.5        1    NA
## 25 3.845     1    -1  black       0.5        1    NA
## 26 1.935     1    -1  black       0.5        1    NA
## 27 2.140     1    -1  black       0.5        1    NA
## 28 1.513     1    -1  black       0.5        1    NA
## 29 3.170     1    -1  black       0.5        1    NA
## 30 2.770     1    -1  black       0.5        1    NA
## 31 3.570     1    -1  black       0.5        1    NA
## 32 2.780     1    -1  black       0.5        1    NA
ggtrace::ggtrace_inspect_args(
  x = last_plot(),
  method = ggplot2:::Layer$compute_statistic
)$data %>% head()
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
##       x PANEL group
## 1 2.620     1    -1
## 2 2.875     1    -1
## 3 2.320     1    -1
## 4 3.215     1    -1
## 5 3.440     1    -1
## 6 3.460     1    -1
# computes for all data, after_stat alone errors
ggplot(mtcars) + 
  aes(x = wt) +
  geom_rug() + 
  geom_histogram() + 
  geom_segment(aes(x = stage(start = wt, after_stat = mean(x)),
                   xend = stage(start = wt, after_stat = mean(x)),
                   y = -Inf, yend = Inf))
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

# does not work as expected
last_plot() + 
  aes(color = factor(am))
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

other notes

# also, expect 2 rows of data, but get 32
layer_data(last_plot(), 3) %>%
  head()
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
##    colour       x    xend    y yend PANEL group linewidth linetype alpha
## 1 #00BFC4 3.21725 3.21725 -Inf  Inf     1     2       0.5        1    NA
## 2 #00BFC4 3.21725 3.21725 -Inf  Inf     1     2       0.5        1    NA
## 3 #00BFC4 3.21725 3.21725 -Inf  Inf     1     2       0.5        1    NA
## 4 #F8766D 3.21725 3.21725 -Inf  Inf     1     1       0.5        1    NA
## 5 #F8766D 3.21725 3.21725 -Inf  Inf     1     1       0.5        1    NA
## 6 #F8766D 3.21725 3.21725 -Inf  Inf     1     1       0.5        1    NA
# a knock against segment shown below..
# requires y, whereas vline will draw without, via range back computation
ggplot(mtcars) + 
  aes(x = wt) +
  geom_rug() + 
  geom_segment(aes(xend = wt, y = -Inf, yend = Inf))

Closing remarks, Other Relevant Work, Caveats