integrated-inferences / CausalQueries

Bayesian inference from binary causal models
Other
24 stars 7 forks source link

confounding edge placement #356

Closed till-tietz closed 1 week ago

till-tietz commented 1 month ago

@macartan merged the changes to the plotting function into master (I added a penalty for edge intersections and a gravitation term for layout compactness to the force directed layout algorithm which makes the automated layout for more complicated DAGs when no node positions are passed a bit nicer)

last open aesthetic issue here is deciding confounding edge curvature based on graph layout. Currently all confounding edges have positive relatively symmetric curvature when negative or more skewed curvature may be optimal depending on the layout.

I don't think this is super critical to implement but I'll keep tinkering.

till-tietz commented 1 month ago

@macartan I think geom_curve uses something like a quadratic bezier curve to draw curves between points. Here is a basic implementation with base R

bezier_curve <- function(x0, y0, x1, y1, curvature, angle) {
  ## convert angle from degrees to radians
  angle_rad <- angle * pi / 180

  ## calculate control point based on midpoint and skew
  xm <- (x0 + x1) / 2
  ym <- (y0 + y1) / 2

  # skew direction vector
  skew_vector_x <- cos(angle_rad)
  skew_vector_y <- sin(angle_rad)

  # control point 
  x_ctrl <- xm + curvature * skew_vector_x
  y_ctrl <- ym + curvature * skew_vector_y

  ## calculate bezier curve
  t <- seq(0, 1, length.out = 100)
  x <- (1 - t)^2 * x0 + 2 * (1 - t) * t * x_ctrl + t^2 * x1
  y <- (1 - t)^2 * y0 + 2 * (1 - t) * t * y_ctrl + t^2 * y1

  points <- data.frame(x = x, y = y)

  return(points)
}

curve <- bezier_curve(x0 = 0, y0 = 0, x1 = 1, y1 = 1, curvature = 1, angle = 90)

# plot
ggplot(curve, aes(x = x, y = y)) +
  geom_path() +
  theme_bw()

This gives us access to the actual points used to construct the curves so we can:

  1. properly set the extent of the plot so confounding edges don't get weirdly cut off sometimes
  2. set up some sort of loss function to optimize the curvature and angle parameters to reduce overlap between confounding edges and the other elements of the plot (currently working on figuring this out)
macartan commented 1 month ago

that looks pretty beautiful

note two other pressing issues with graphs:

  1. point positions are not off here, hiding a set of arrows: make_model("A -> B-> C <- A; A<->C") |> plot()

  2. arrows are sometimes going inside nodes (when the graph area is small); we don;t have the separation correct unfortunately; this is related to this: adjustment <- mean(nodesize sapply(extent, diff) / 300); but I am not sure how to calibrate this correctly; experimenting this worked fairly well but not perfectly for me: adjustment <- nodesize^.75 mean(sapply(extent, diff) / 100)