Skip to content

Commit 624fb1a

Browse files
authored
Merge pull request #5 from jiyunson/feature/add_gf_resid_squaresid
feat: add gf_resid and gf_squaresid
2 parents c532484 + 49bf54a commit 624fb1a

File tree

1 file changed

+116
-0
lines changed

1 file changed

+116
-0
lines changed

R/gf_resid_gf_squaresid.R

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
#' Add Residual Lines to a Plot
2+
#'
3+
#' This function adds vertical lines representing residuals from a linear model to a ggformula plot.
4+
#' The residuals are drawn from the observed data points to the predicted values from the model.
5+
#'
6+
#' @param plot A ggformula plot object, typically created with `gf_point()`.
7+
#' @param model A fitted linear model object created using `lm()`.
8+
#' @param linewidth A numeric value specifying the width of the residual lines. Default is `0.2`.
9+
#' @param ... Additional aesthetics passed to `geom_segment()`, such as `color`, `alpha`, `linetype`.
10+
#'
11+
#' @return A ggplot object with residual lines added.
12+
#'
13+
#' @export
14+
#' @examples
15+
#' gf_point(Thumb ~ Height, data = Fingers) %>%
16+
#' gf_model(Height_model) %>%
17+
#' gf_resid(Height_model, color = "red", alpha = 0.5)
18+
gf_resid <- function(plot, model, linewidth = 0.2, ...) {
19+
# Handles random jitter
20+
rand_int <- sample(1:100, 1)
21+
set.seed(rand_int)
22+
23+
# Get model predictions and residuals and assign them to the model data
24+
model_data <- model$model
25+
model_data$prediction <- predict(model)
26+
model_data$residual <- resid(model)
27+
28+
# Access the x and y coordinates used in the plot
29+
plot_data <- ggplot_build(plot)$data[[1]]
30+
x_loc <- plot_data$x
31+
y_loc <- plot_data$y
32+
33+
# Ensures same jitter as the x and y coord from plot
34+
set.seed(rand_int)
35+
plot +
36+
geom_segment(aes(
37+
x = x_loc,
38+
y = model_data$prediction,
39+
xend = x_loc,
40+
yend = y_loc
41+
),
42+
inherit.aes = TRUE,
43+
linewidth = linewidth,
44+
...
45+
)
46+
}
47+
48+
#' Add Squared Residual Visualization to a Plot
49+
#'
50+
#' This function adds squared residual representations to a ggformula plot, illustrating squared error as a polygon.
51+
#' The function dynamically adjusts the aspect ratio to ensure proper scaling of squares.
52+
#'
53+
#' @param plot A ggformula plot object, typically created with `gf_point()`.
54+
#' @param model A fitted linear model object created using `lm()`.
55+
#' @param aspect A numeric value controlling the square's aspect ratio. Default is `4/6`.
56+
#' @param alpha A numeric value specifying the transparency level of the polygon fill. Default is `0.1`.
57+
#' @param ... Additional aesthetics passed to `geom_polygon()`, such as `color` and `fill`.
58+
#'
59+
#' @return A ggplot object with squared residuals added.
60+
#'
61+
#' @export
62+
#' @examples
63+
#' gf_point(Thumb ~ Height, data = Fingers) %>%
64+
#' gf_model(Height_model) %>%
65+
#' gf_squaresid(Height_model, color = "blue", alpha = 0.5)
66+
gf_squaresid <- function(plot, model, aspect = 4/6, alpha = 0.1, ...) {
67+
# Handles random jitter
68+
rand_int <- sample(1:100, 1)
69+
set.seed(rand_int)
70+
71+
# Get model predictions and residuals and assign them to the model data
72+
model_data <- model$model
73+
model_data$prediction <- predict(model)
74+
model_data$residual <- resid(model)
75+
76+
# Access the x and y coordinates used in the plot
77+
plot_data <- ggplot_build(plot)$data[[1]]
78+
model_data$x_loc <- plot_data$x
79+
model_data$y_loc <- plot_data$y
80+
81+
# Access the range of x and y used in the panel
82+
plot_layout <- ggplot_build(plot)$layout
83+
panel_params <- plot_layout$panel_params[[1]]
84+
x_range <- panel_params$x.range
85+
y_range <- panel_params$y.range
86+
87+
# Compute ratio for proper aspect scaling
88+
range_ratio <- (x_range[2] - x_range[1]) / (y_range[2] - y_range[1])
89+
model_data$dir <- ifelse(model_data$x_loc > mean(x_range), -1, 1)
90+
model_data$adj_side <- model_data$x_loc + model_data$dir * abs(model_data$residual * aspect * range_ratio)
91+
92+
# Create a dataframe for plotting polygons
93+
squares_data <- do.call(rbind, lapply(1:nrow(model_data), function(i) {
94+
resid_side <- model_data$x_loc[i]
95+
top <- model_data$prediction[i]
96+
bottom <- model_data$y_loc[i]
97+
opp_side <- model_data$adj_side[i]
98+
99+
data.frame(
100+
x = c(resid_side, opp_side, opp_side, resid_side),
101+
y = c(bottom, bottom, top, top),
102+
id = i # Unique identifier for each square
103+
)
104+
}))
105+
106+
# Ensures same jitter as the x and y coord from plot
107+
set.seed(rand_int)
108+
plot +
109+
geom_polygon(
110+
data = squares_data,
111+
aes(x = x, y = y, group = id),
112+
inherit.aes = FALSE,
113+
alpha = alpha,
114+
...
115+
)
116+
}

0 commit comments

Comments
 (0)