Title: | Vector Field Visualizations with 'ggplot2' |
---|---|
Description: | A 'ggplot2' extension for visualizing vector fields in two-dimensional space. Provides flexible tools for creating vector and stream field layers, visualizing gradients and potential fields, and smoothing vector and scalar data to estimate underlying patterns. |
Authors: | Dusty Turner [aut, cre], David Kahle [aut], Rodney X. Sturdivant [aut] |
Maintainer: | Dusty Turner <[email protected]> |
License: | MIT + file LICENSE |
Version: | 1.0.0 |
Built: | 2025-03-16 05:21:38 UTC |
Source: | https://github.com/dusty-turner/ggvfields |
The vector field generated by collection of fixed electrical charges, as dictated by Coulomb's law. This function is mainly used to provide examples for visualizing vector fields with ggvfields.
efield(u, charge_positions, charges, k = 1, q_test = +1) efield_maker( charge_positions = rbind(c(-1, -1), c(1, 1)), charges = c(-1, +1), k = 1, q_test = +1 )
efield(u, charge_positions, charges, k = 1, q_test = +1) efield_maker( charge_positions = rbind(c(-1, -1), c(1, 1)), charges = c(-1, +1), k = 1, q_test = +1 )
u |
The position of the test charge. |
charge_positions |
The positions of the fixed charges generating the
electric field. Defaulted in |
charges |
The charges of the points placed at the positions of
charge_positions. Defaulted in |
k |
The constant of proportionality, defaulted to 1. See examples for a more rigorous use of physical constants. |
q_test |
The test charge, defaulted to +1. |
A vector containing the force felt by the test charge on account of the electric field.
https://en.wikipedia.org/wiki/Coulomb%27s_law
# set a - charge at (-1,-1) and a + charge at (1,1) charge_positions <- rbind(c(-1,-1), c(1,1)) charges <- c(-1, +1) # calculate force on test charge (+1) at c(0,1), ignoring physical constants efield(c(0,1), charge_positions, charges) # efield_maker() simply wraps this function, defaulting to those charges f <- efield_maker() f(c(0,1)) ggplot() + geom_stream_field(fun = f, xlim = c(-2,2), ylim = c(-2,2)) + scale_color_viridis_c(trans = "log10") # electric constant from https://en.wikipedia.org/wiki/Vacuum_permittivity ep0 <- 8.854187818814e-12 k <- (4*pi*ep0)^-1 efield(c(0,1), charge_positions, charges, k)
# set a - charge at (-1,-1) and a + charge at (1,1) charge_positions <- rbind(c(-1,-1), c(1,1)) charges <- c(-1, +1) # calculate force on test charge (+1) at c(0,1), ignoring physical constants efield(c(0,1), charge_positions, charges) # efield_maker() simply wraps this function, defaulting to those charges f <- efield_maker() f(c(0,1)) ggplot() + geom_stream_field(fun = f, xlim = c(-2,2), ylim = c(-2,2)) + scale_color_viridis_c(trans = "log10") # electric constant from https://en.wikipedia.org/wiki/Vacuum_permittivity ep0 <- 8.854187818814e-12 k <- (4*pi*ep0)^-1 efield(c(0,1), charge_positions, charges, k)
These functions provide convenient ggplot2 layers for drawing gradient fields
by computing the gradient of a scalar field. A user-defined function (fun
)
specifies the behavior of the scalar field by taking a numeric vector of
length 2 (representing ) and returning a single numeric value.
The underlying StatStreamField computes the gradient via numerical
differentiation (using
numDeriv::grad()
) and GeomStream renders the
resulting vectors.
geom_gradient_field( mapping = NULL, data = NULL, stat = StatStreamField, position = "identity", ..., na.rm = FALSE, show.legend = TRUE, inherit.aes = TRUE, fun, xlim = NULL, ylim = NULL, n = 11, max_it = 1000, T = NULL, L = NULL, center = TRUE, type = "vector", normalize = TRUE, tail_point = FALSE, eval_point = FALSE, grid = NULL, lineend = "butt", linejoin = "round", linemitre = 10, arrow = grid::arrow(angle = 30, length = grid::unit(0.02, "npc"), type = "closed") ) stat_gradient_field( mapping = NULL, data = NULL, geom = GeomStream, position = "identity", ..., na.rm = FALSE, show.legend = TRUE, inherit.aes = TRUE, fun, xlim = NULL, ylim = NULL, n = 11, max_it = 1000, T = NULL, L = NULL, center = TRUE, type = "vector", normalize = TRUE, tail_point = FALSE, eval_point = FALSE, grid = NULL, lineend = "butt", linejoin = "round", linemitre = 10, arrow = grid::arrow(angle = 30, length = grid::unit(0.02, "npc"), type = "closed") ) geom_gradient_field2( mapping = NULL, data = NULL, stat = StatStreamField, position = "identity", ..., na.rm = FALSE, show.legend = TRUE, inherit.aes = TRUE, fun, xlim = NULL, ylim = NULL, n = 11, max_it = 1000, T = NULL, L = NULL, center = FALSE, type = "stream", normalize = TRUE, tail_point = TRUE, eval_point = FALSE, grid = NULL, lineend = "butt", linejoin = "round", linemitre = 10, arrow = NULL ) stat_gradient_field2( mapping = NULL, data = NULL, geom = GeomStream, position = "identity", ..., na.rm = FALSE, show.legend = TRUE, inherit.aes = TRUE, fun, xlim = NULL, ylim = NULL, n = 11, max_it = 1000, T = NULL, L = NULL, center = FALSE, type = "stream", normalize = TRUE, tail_point = TRUE, eval_point = FALSE, grid = NULL, lineend = "butt", linejoin = "round", linemitre = 10, arrow = NULL )
geom_gradient_field( mapping = NULL, data = NULL, stat = StatStreamField, position = "identity", ..., na.rm = FALSE, show.legend = TRUE, inherit.aes = TRUE, fun, xlim = NULL, ylim = NULL, n = 11, max_it = 1000, T = NULL, L = NULL, center = TRUE, type = "vector", normalize = TRUE, tail_point = FALSE, eval_point = FALSE, grid = NULL, lineend = "butt", linejoin = "round", linemitre = 10, arrow = grid::arrow(angle = 30, length = grid::unit(0.02, "npc"), type = "closed") ) stat_gradient_field( mapping = NULL, data = NULL, geom = GeomStream, position = "identity", ..., na.rm = FALSE, show.legend = TRUE, inherit.aes = TRUE, fun, xlim = NULL, ylim = NULL, n = 11, max_it = 1000, T = NULL, L = NULL, center = TRUE, type = "vector", normalize = TRUE, tail_point = FALSE, eval_point = FALSE, grid = NULL, lineend = "butt", linejoin = "round", linemitre = 10, arrow = grid::arrow(angle = 30, length = grid::unit(0.02, "npc"), type = "closed") ) geom_gradient_field2( mapping = NULL, data = NULL, stat = StatStreamField, position = "identity", ..., na.rm = FALSE, show.legend = TRUE, inherit.aes = TRUE, fun, xlim = NULL, ylim = NULL, n = 11, max_it = 1000, T = NULL, L = NULL, center = FALSE, type = "stream", normalize = TRUE, tail_point = TRUE, eval_point = FALSE, grid = NULL, lineend = "butt", linejoin = "round", linemitre = 10, arrow = NULL ) stat_gradient_field2( mapping = NULL, data = NULL, geom = GeomStream, position = "identity", ..., na.rm = FALSE, show.legend = TRUE, inherit.aes = TRUE, fun, xlim = NULL, ylim = NULL, n = 11, max_it = 1000, T = NULL, L = NULL, center = FALSE, type = "stream", normalize = TRUE, tail_point = TRUE, eval_point = FALSE, grid = NULL, lineend = "butt", linejoin = "round", linemitre = 10, arrow = NULL )
mapping |
A set of aesthetic mappings created by |
data |
A data frame containing the input data. |
stat |
The statistical transformation to use on the data for this layer. Defaults to StatStreamField. |
position |
Position adjustment, either as a string or the result of a position adjustment function. |
... |
Other arguments passed on to |
na.rm |
Logical. If |
show.legend |
Logical. Should this layer be included in the legends? |
inherit.aes |
Logical. If |
fun |
A function that defines the scalar field. It should take a numeric
vector of length 2 (representing |
xlim |
Numeric vector of length two. Specifies the limits of the x-axis
domain. Defaults to |
ylim |
Numeric vector of length two. Specifies the limits of the y-axis
domain. Defaults to |
n |
Integer. Grid resolution specifying the number of seed points along
each axis. Higher values produce a denser gradient field. Defaults to |
max_it |
Integer. Maximum number of integration steps allowed when
computing the gradient stream. Defaults to |
T |
Numeric. Time increment used for numerical integration when
|
L |
Numeric. Target length for the gradient vectors or streamlines. When
|
center |
Logical. If |
type |
Character. Specifies the type of field to compute: use |
normalize |
Logical. If |
tail_point |
Logical. If |
eval_point |
Logical. If |
grid |
A data frame containing precomputed grid points for seed
placement. If |
lineend |
Line end style (round, butt, square). |
linejoin |
Line join style (round, mitre, bevel). |
linemitre |
Line mitre limit (number greater than 1). |
arrow |
A |
geom |
The geometric object used to render the streamline (only used in
|
Two variants are provided:
geom_gradient_field() uses a default mapping that sets color = after_stat(norm)
.
geom_gradient_field2() uses a default mapping that sets length = after_stat(norm)
(with color
unmapped by default).
A ggplot2 layer that computes and plots a gradient field by numerically differentiating a scalar field.
geom_gradient_field()
and geom_gradient_field2()
understand the following aesthetics (required aesthetics are in bold):
x
: The x-coordinate of the seed point.
y
: The y-coordinate of the seed point.
color
: In geom_gradient_field, the color of the gradient vector.
In geom_gradient_field2, color is not mapped by default.
length
: In geom_gradient_field2, the computed vector norm.
size
, linetype
, alpha
: Additional aesthetics to control appearance.
The following variables are computed internally by StatStreamField when generating the gradient field from a scalar function:
The Euclidean norm of the gradient vector, calculated as
. This value is used, by default, for mapping color or scaling
arrow lengths in the visualization.
This variable may represent an average speed computed
from the gradient magnitude. In the default mapping for geom_gradient_field, the
color aesthetic is mapped to after_stat(avg_spd)
.
Si <- matrix(c(1, 0.75, 0.75, 1), nrow = 2) f <- function(u) exp(-as.numeric(u %*% solve(Si) %*% u) / 2) / (2 * pi * det(Si)) ggplot() + geom_gradient_field(fun = f, xlim = c(-3, 3), ylim = c(-3, 3)) df <- expand.grid(x = seq(-3, 3, 0.1), y = seq(-3, 3, 0.1)) |> transform(fxy = apply(cbind(x, y), 1, f)) ggplot() + geom_raster(aes(x, y, fill = fxy), data = df) + geom_gradient_field(fun = f, xlim = c(-3, 3), ylim = c(-3, 3)) + coord_equal() fxy <- function(x, y) apply(cbind(x,y), 1, f) ggplot() + ggdensity::geom_hdr_fun(fun = fxy, xlim = c(-3,3), ylim = c(-3,3)) + geom_gradient_field(fun = f, xlim = c(-3,3), ylim = c(-3,3)) + coord_equal() library("ggdensity") fxy <- function(x, y) apply(cbind(x, y), 1, f) fxy(1, 2) f(1:2) ggplot() + geom_hdr_fun(fun = fxy, xlim = c(-3, 3), ylim = c(-3, 3)) + geom_gradient_field(fun = f, xlim = c(-3, 3), ylim = c(-3, 3)) + coord_equal()
Si <- matrix(c(1, 0.75, 0.75, 1), nrow = 2) f <- function(u) exp(-as.numeric(u %*% solve(Si) %*% u) / 2) / (2 * pi * det(Si)) ggplot() + geom_gradient_field(fun = f, xlim = c(-3, 3), ylim = c(-3, 3)) df <- expand.grid(x = seq(-3, 3, 0.1), y = seq(-3, 3, 0.1)) |> transform(fxy = apply(cbind(x, y), 1, f)) ggplot() + geom_raster(aes(x, y, fill = fxy), data = df) + geom_gradient_field(fun = f, xlim = c(-3, 3), ylim = c(-3, 3)) + coord_equal() fxy <- function(x, y) apply(cbind(x,y), 1, f) ggplot() + ggdensity::geom_hdr_fun(fun = fxy, xlim = c(-3,3), ylim = c(-3,3)) + geom_gradient_field(fun = f, xlim = c(-3,3), ylim = c(-3,3)) + coord_equal() library("ggdensity") fxy <- function(x, y) apply(cbind(x, y), 1, f) fxy(1, 2) f(1:2) ggplot() + geom_hdr_fun(fun = fxy, xlim = c(-3, 3), ylim = c(-3, 3)) + geom_gradient_field(fun = f, xlim = c(-3, 3), ylim = c(-3, 3)) + coord_equal()
geom_gradient_smooth()
creates a ggplot2 layer that visualizes the gradient
of a scalar field computed from raw data. A linear model is fitted using the
supplied formula
(default: z ~ x + y + I(x^2) + I(y^2)
) on the raw data,
and the numerical gradient is computed using numDeriv::grad(). The computed
gradient field is then visualized using GeomStream()
.
geom_gradient_smooth( mapping = NULL, data = NULL, stat = StatStreamField, position = "identity", ..., na.rm = FALSE, show.legend = TRUE, inherit.aes = TRUE, formula = z ~ x + y + I(x^2) + I(y^2), xlim = NULL, ylim = NULL, n = 11, max_it = 1000, T = NULL, L = NULL, center = TRUE, type = "vector", normalize = TRUE, tail_point = FALSE, eval_point = FALSE, grid = NULL, lineend = "butt", linejoin = "round", linemitre = 10, arrow = grid::arrow(angle = 30, length = grid::unit(0.02, "npc"), type = "closed") ) stat_gradient_smooth( mapping = NULL, data = NULL, geom = GeomStream, position = "identity", ..., na.rm = FALSE, show.legend = TRUE, inherit.aes = TRUE, formula = z ~ x + y + I(x^2) + I(y^2), xlim = NULL, ylim = NULL, n = 11, max_it = 1000, T = NULL, L = NULL, center = TRUE, type = "vector", normalize = TRUE, tail_point = FALSE, eval_point = FALSE, grid = NULL, lineend = "butt", linejoin = "round", linemitre = 10, arrow = grid::arrow(angle = 30, length = grid::unit(0.02, "npc"), type = "closed") )
geom_gradient_smooth( mapping = NULL, data = NULL, stat = StatStreamField, position = "identity", ..., na.rm = FALSE, show.legend = TRUE, inherit.aes = TRUE, formula = z ~ x + y + I(x^2) + I(y^2), xlim = NULL, ylim = NULL, n = 11, max_it = 1000, T = NULL, L = NULL, center = TRUE, type = "vector", normalize = TRUE, tail_point = FALSE, eval_point = FALSE, grid = NULL, lineend = "butt", linejoin = "round", linemitre = 10, arrow = grid::arrow(angle = 30, length = grid::unit(0.02, "npc"), type = "closed") ) stat_gradient_smooth( mapping = NULL, data = NULL, geom = GeomStream, position = "identity", ..., na.rm = FALSE, show.legend = TRUE, inherit.aes = TRUE, formula = z ~ x + y + I(x^2) + I(y^2), xlim = NULL, ylim = NULL, n = 11, max_it = 1000, T = NULL, L = NULL, center = TRUE, type = "vector", normalize = TRUE, tail_point = FALSE, eval_point = FALSE, grid = NULL, lineend = "butt", linejoin = "round", linemitre = 10, arrow = grid::arrow(angle = 30, length = grid::unit(0.02, "npc"), type = "closed") )
mapping |
A set of aesthetic mappings created by |
data |
A data frame containing the raw vector data. |
stat |
The statistical transformation to use on the data. Defaults to
|
position |
Position adjustment, either as a string or the result of a position adjustment function. |
... |
Additional arguments passed to the layer. |
na.rm |
Logical. If |
show.legend |
Logical. Should this layer be included in the legends? |
inherit.aes |
Logical. If |
formula |
A formula specifying the linear model for the scalar field.
Defaults to |
xlim |
Numeric vector of length 2 specifying the domain limits in the
|
ylim |
Numeric vector of length 2 specifying the domain limits in the
|
n |
An integer vector specifying the grid resolution for smoothing. |
max_it |
Maximum number of iterations for field integration (when used in streamlines). |
T |
If |
L |
If |
center |
Logical. If |
type |
Character. Either |
normalize |
Logical. If |
tail_point |
Logical. If |
eval_point |
Logical. If |
grid |
A user-supplied data frame or pattern (e.g., "hex") for specifying custom evaluation points. |
lineend |
Line end style (round, butt, square). |
linejoin |
Line join style (round, mitre, bevel). |
linemitre |
Line mitre limit (number greater than 1). |
arrow |
An optional |
geom |
The geometric object used to render the streamline (only used in
|
A ggplot2 layer that can be added to a ggplot object.
geom_gradient_smooth()
supports the following aesthetics (required aesthetics are in bold):
x
: The x-coordinate of the data point.
y
: The y-coordinate of the data point.
z
: The scalar value used for computing the gradient.
color
: The color used for the gradient vectors. Defaults depend on the
selected type
.
Gradient Calculation:
A linear model is fitted using the provided formula
and the raw data. The scalar
field defined by the model is then differentiated numerically with
numDeriv::grad()
to yield gradient vectors.
Visualization:
The resulting gradient field is visualized using GeomStream()
. Since z
is only
used internally, it is dropped from the final visual output.
# Define several scalar field functions: # Example 1: f(x, y) = x^2 - y^2 f <- function(u) { x <- u[1] y <- u[2] x^2 - y^2 } # Example 2: g(x, y) = sin(x) * cos(y) g <- function(u) { x <- u[1] y <- u[2] sin(x) * cos(y) } # Example 3: h(x, y) = log(|x| + 1) + sqrt(|y|) h <- function(u) { x <- u[1] y <- u[2] log(abs(x) + 1) + sqrt(abs(y)) } # Create a grid of evaluation points grid_data <- expand.grid( x = seq(-5, 5, length.out = 30), y = seq(-5, 5, length.out = 30) ) # Compute the scalar field for f and plot its gradient grid_data$z <- apply(grid_data, 1, f) ggplot(grid_data, aes(x = x, y = y, z = z)) + geom_gradient_smooth() # Compute and plot for g: grid_data$z <- apply(grid_data, 1, g) ggplot(grid_data, aes(x = x, y = y, z = z)) + geom_gradient_smooth() # Compute and plot for h: grid_data$z <- apply(grid_data, 1, h) ggplot(grid_data, aes(x = x, y = y, z = z)) + geom_gradient_smooth()
# Define several scalar field functions: # Example 1: f(x, y) = x^2 - y^2 f <- function(u) { x <- u[1] y <- u[2] x^2 - y^2 } # Example 2: g(x, y) = sin(x) * cos(y) g <- function(u) { x <- u[1] y <- u[2] sin(x) * cos(y) } # Example 3: h(x, y) = log(|x| + 1) + sqrt(|y|) h <- function(u) { x <- u[1] y <- u[2] log(abs(x) + 1) + sqrt(abs(y)) } # Create a grid of evaluation points grid_data <- expand.grid( x = seq(-5, 5, length.out = 30), y = seq(-5, 5, length.out = 30) ) # Compute the scalar field for f and plot its gradient grid_data$z <- apply(grid_data, 1, f) ggplot(grid_data, aes(x = x, y = y, z = z)) + geom_gradient_smooth() # Compute and plot for g: grid_data$z <- apply(grid_data, 1, g) ggplot(grid_data, aes(x = x, y = y, z = z)) + geom_gradient_smooth() # Compute and plot for h: grid_data$z <- apply(grid_data, 1, h) ggplot(grid_data, aes(x = x, y = y, z = z)) + geom_gradient_smooth()
geom_potential()
adds a raster layer to a ggplot object, visualizing the
potential function derived from a conservative vector field. It computes the
potential numerically over a specified grid and displays it as a heatmap.
geom_potential( mapping = NULL, data = NULL, stat = StatPotential, position = "identity", ..., na.rm = FALSE, inherit.aes = TRUE, show.legend = NA, fun, xlim = NULL, ylim = NULL, n = 51, tol = 1e-06, verify_conservative = FALSE ) stat_potential( mapping = NULL, data = NULL, geom = GeomPotential, position = "identity", ..., na.rm = FALSE, inherit.aes = TRUE, show.legend = NA, fun, xlim = NULL, ylim = NULL, n = 51, tol = 1e-06, verify_conservative = FALSE )
geom_potential( mapping = NULL, data = NULL, stat = StatPotential, position = "identity", ..., na.rm = FALSE, inherit.aes = TRUE, show.legend = NA, fun, xlim = NULL, ylim = NULL, n = 51, tol = 1e-06, verify_conservative = FALSE ) stat_potential( mapping = NULL, data = NULL, geom = GeomPotential, position = "identity", ..., na.rm = FALSE, inherit.aes = TRUE, show.legend = NA, fun, xlim = NULL, ylim = NULL, n = 51, tol = 1e-06, verify_conservative = FALSE )
mapping |
A set of aesthetic mappings created by |
data |
The data to be displayed in this layer. If |
stat |
The statistical transformation to use on the data (default: StatPotential). |
position |
Position adjustment, either as a string or the result of a position adjustment function. |
... |
Other arguments passed to |
na.rm |
Logical. If |
inherit.aes |
Logical. If |
show.legend |
Logical. Should this layer be included in the legends? |
fun |
A function that takes a numeric vector of length 2 ( |
xlim |
Numeric vector of length 2 defining the domain limits on the
x-axis. Defaults to |
ylim |
Numeric vector of length 2 defining the domain limits on the
y-axis. Defaults to |
n |
Integer. Number of grid points along each axis for computing the
potential. Defaults to |
tol |
Numeric. Tolerance for verifying if the vector field is
conservative. Defaults to |
verify_conservative |
Logical. If |
geom |
The geometric object used to render the potential function. Defaults to GeomPotential. |
Note: the potential is only known up to a constant. The point of reference
used is the lower left corner c(xlim[1], ylim[1])
, where the potential is
assumed to be 0.
A ggplot2 layer that produces a potential function heatmap.
geom_potential()
accepts all aesthetics supported by
GeomRaster
. In particular, the key aesthetics include:
fill: The computed potential value at each grid cell, which is mapped to a color scale.
x
and y
: The coordinates of the grid cell centers. (calculated)
alpha
: Controls the transparency of the raster fill.
Additional raster-specific aesthetics (e.g. those controlled by
scale_fill_gradient()
, scale_fill_viridis_c()
, etc.) can be applied to
modify the appearance of the potential heatmap.
The following variable is computed internally by StatPotential during the potential function calculation:
The scalar potential value computed numerically at each grid point.
It represents the accumulated potential from a reference point (typically the lower
bounds of xlim
and ylim
) to the given point. This value is mapped to
the fill
aesthetic in the raster layer.
scalar_field <- function(u){ x <- u[1]; y <- u[2] (x + y)^2 + 4*(x - y)^2 - 8*(.5)^2 } gradient_field <- function(u) numDeriv::grad(scalar_field, u) s <- seq(-1, 1, length.out = 51) expand.grid("x" = s, "y" = s) |> transform(phi = apply(cbind(x, y), 1, scalar_field)) |> ggplot(aes(x, y)) + geom_raster(aes(fill = phi)) ggplot() + geom_potential(fun = gradient_field) ggplot() + geom_potential(fun = gradient_field, verify_conservative = TRUE)
scalar_field <- function(u){ x <- u[1]; y <- u[2] (x + y)^2 + 4*(x - y)^2 - 8*(.5)^2 } gradient_field <- function(u) numDeriv::grad(scalar_field, u) s <- seq(-1, 1, length.out = 51) expand.grid("x" = s, "y" = s) |> transform(phi = apply(cbind(x, y), 1, scalar_field)) |> ggplot(aes(x, y)) + geom_raster(aes(fill = phi)) ggplot() + geom_potential(fun = gradient_field) ggplot() + geom_potential(fun = gradient_field, verify_conservative = TRUE)
geom_stream()
generates a ggplot2 layer that visualizes data as continuous
streams over a temporal variable t
. Each stream is defined by the
required aesthetics x
, y
, and t
, and optionally grouped by
group
(or mapped from id
). Within each group, data points are
automatically ordered by t
to form a continuous path.
geom_stream( mapping = NULL, data = NULL, stat = StatStream, position = "identity", ..., na.rm = FALSE, show.legend = NA, inherit.aes = TRUE, arrow.fill = NULL, lineend = "butt", linejoin = "round", linemitre = 10, arrow = grid::arrow(angle = 25, length = unit(0.025, "npc"), type = "closed") ) stat_stream( mapping = NULL, data = NULL, geom = GeomStream, position = "identity", ..., na.rm = FALSE, show.legend = NA, inherit.aes = TRUE, arrow.fill = NULL, lineend = "butt", linejoin = "round", linemitre = 10, arrow = grid::arrow(angle = 25, length = unit(0.025, "npc"), type = "closed") )
geom_stream( mapping = NULL, data = NULL, stat = StatStream, position = "identity", ..., na.rm = FALSE, show.legend = NA, inherit.aes = TRUE, arrow.fill = NULL, lineend = "butt", linejoin = "round", linemitre = 10, arrow = grid::arrow(angle = 25, length = unit(0.025, "npc"), type = "closed") ) stat_stream( mapping = NULL, data = NULL, geom = GeomStream, position = "identity", ..., na.rm = FALSE, show.legend = NA, inherit.aes = TRUE, arrow.fill = NULL, lineend = "butt", linejoin = "round", linemitre = 10, arrow = grid::arrow(angle = 25, length = unit(0.025, "npc"), type = "closed") )
mapping |
A set of aesthetic mappings created by |
data |
A data frame or other object, as in |
stat |
The statistical transformation to use on the data for this layer; defaults to StatStream. |
position |
Position adjustment, either as a string or the result of a position adjustment function. |
... |
Other arguments passed to the underlying layers for further customization. |
na.rm |
Logical. If |
show.legend |
Logical. Should this layer be included in the legends? |
inherit.aes |
Logical. If |
arrow.fill |
An optional parameter specifying the color of the arrow
head. Defaults to |
lineend |
Line end style (round, butt, square). |
linejoin |
Line join style (round, mitre, bevel). |
linemitre |
Line mitre limit (number greater than 1). |
arrow |
An optional |
geom |
The geometric object used to render the streamline (only used in
|
There are two variants:
geom_stream()
: A convenient wrapper that sets stat = StatStream
and uses
ggplot2::GeomPath by default.
stat_stream()
: Provides direct access to the reordering stat (i.e. StatStream
)
for advanced customization, using GeomStream for drawing.
A ggplot2 layer that can be added to a plot to produce a streamline visualization.
geom_stream()
and stat_stream()
understand the
following aesthetics (required aesthetics are in bold):
x
: Horizontal position.
y
: Vertical position.
t
: Temporal or ordered variable used to sequence data points.
group
: Grouping variable for multiple streams (automatically mapped from id
if absent).
color
: Color of the stream.
linetype
: Type of line used to draw the stream.
linewidth
: Thickness of the stream line.
alpha
: Transparency of the stream.
Data Ordering: If t
is not provided, an error is thrown. When present,
points within each group are sorted by t
prior to drawing the stream.
Arrows: The arrow
parameter can be used to indicate direction along each stream.
These are calculated by the 'stat' part of layers and can be accessed with delayed evaluation.
This variable is calculated as the Euclidean distance derived
from the ranges of the x
and y
values. It serves as a
normalization factor for vector lengths when the normalize
parameter is active.
Represents the average speed, which is defined as the length of the stream divided by the time it took to traverse that distance.
n <- 25 s <- seq(0, 1, length.out = n+1)[-(n+1)] df <- data.frame( "t" = s, "x" = cos(2*pi*s), "y" = sin(2*pi*s) ) ggplot(df) + geom_stream(aes(x, y, t = t)) + coord_equal() ggplot(df) + geom_stream(aes(x, y, t = t, alpha = t), size = 5) + coord_equal() ggplot(df) + geom_path(aes(x, y, alpha = t), size = 5) + coord_equal() stream_1 <- data.frame( x = c(0, 3), y = c(0, 0), t = 0:1 ) stream_2 <- data.frame( x = c(1, 1), y = c(1, 5), t = 0:1 ) stream_3 <- data.frame( x = c(2, 5), y = c(2, 6), t = 0:1 ) streams <- rbind( cbind(stream_1, id = 1), cbind(stream_2, id = 2), cbind(stream_3, id = 3) ) ggplot(stream_1) + geom_stream(aes(x = x, y = y, t = t)) # set group aes if multiple vectors ggplot(streams) + geom_stream(aes(x = x, y = y, t = t, group = id))
n <- 25 s <- seq(0, 1, length.out = n+1)[-(n+1)] df <- data.frame( "t" = s, "x" = cos(2*pi*s), "y" = sin(2*pi*s) ) ggplot(df) + geom_stream(aes(x, y, t = t)) + coord_equal() ggplot(df) + geom_stream(aes(x, y, t = t, alpha = t), size = 5) + coord_equal() ggplot(df) + geom_path(aes(x, y, alpha = t), size = 5) + coord_equal() stream_1 <- data.frame( x = c(0, 3), y = c(0, 0), t = 0:1 ) stream_2 <- data.frame( x = c(1, 1), y = c(1, 5), t = 0:1 ) stream_3 <- data.frame( x = c(2, 5), y = c(2, 6), t = 0:1 ) streams <- rbind( cbind(stream_1, id = 1), cbind(stream_2, id = 2), cbind(stream_3, id = 3) ) ggplot(stream_1) + geom_stream(aes(x = x, y = y, t = t)) # set group aes if multiple vectors ggplot(streams) + geom_stream(aes(x = x, y = y, t = t, group = id))
geom_stream_field()
creates a ggplot2 layer that integrates a user-defined
vector field function over a grid of seed points
within a specified domain. The function numerically integrates the field
starting from these seeds, producing streamlines that visualize the flow.
This is useful for visualizing vector fields, flow patterns, or trajectories,
such as in fluid dynamics or gradient fields.
geom_stream_field( mapping = NULL, data = NULL, stat = StatStreamField, position = "identity", ..., na.rm = FALSE, show.legend = NA, inherit.aes = FALSE, fun, xlim = NULL, ylim = NULL, n = 11, args = list(), max_it = 1000L, tol = sqrt(.Machine$double.eps), T = NULL, L = NULL, center = TRUE, type = "stream", normalize = TRUE, tail_point = FALSE, eval_point = FALSE, grid = NULL, method = "rk4", lineend = "butt", linejoin = "round", linemitre = 10, arrow = grid::arrow(angle = 30, length = unit(0.02, "npc"), type = "closed") ) stat_stream_field( mapping = NULL, data = NULL, geom = GeomStream, position = "identity", ..., na.rm = FALSE, show.legend = NA, inherit.aes = TRUE, fun, xlim = NULL, ylim = NULL, n = 11, args = list(), max_it = 1000, tol = sqrt(.Machine$double.eps), T = NULL, L = NULL, center = TRUE, type = "stream", normalize = TRUE, tail_point = FALSE, eval_point = FALSE, grid = NULL, method = "rk4", lineend = "butt", linejoin = "round", linemitre = 10, arrow = grid::arrow(angle = 30, length = unit(0.02, "npc"), type = "closed") ) geom_stream_field2( mapping = NULL, data = NULL, stat = StatStreamField, position = "identity", ..., na.rm = FALSE, show.legend = NA, inherit.aes = FALSE, fun, xlim = NULL, ylim = NULL, n = 11, args = list(), max_it = 1000, tol = sqrt(.Machine$double.eps), L = NULL, center = FALSE, type = "stream", tail_point = TRUE, eval_point = FALSE, grid = NULL, lineend = "butt", linejoin = "round", linemitre = 10, method = "rk4" ) stat_stream_field2( mapping = NULL, data = NULL, geom = GeomStream, position = "identity", ..., na.rm = FALSE, show.legend = NA, inherit.aes = FALSE, fun, xlim = NULL, ylim = NULL, n = 11, args = list(), max_it = 1000, tol = sqrt(.Machine$double.eps), L = NULL, center = FALSE, type = "stream", tail_point = TRUE, eval_point = FALSE, grid = NULL, lineend = "butt", linejoin = "round", linemitre = 10, method = "rk4" )
geom_stream_field( mapping = NULL, data = NULL, stat = StatStreamField, position = "identity", ..., na.rm = FALSE, show.legend = NA, inherit.aes = FALSE, fun, xlim = NULL, ylim = NULL, n = 11, args = list(), max_it = 1000L, tol = sqrt(.Machine$double.eps), T = NULL, L = NULL, center = TRUE, type = "stream", normalize = TRUE, tail_point = FALSE, eval_point = FALSE, grid = NULL, method = "rk4", lineend = "butt", linejoin = "round", linemitre = 10, arrow = grid::arrow(angle = 30, length = unit(0.02, "npc"), type = "closed") ) stat_stream_field( mapping = NULL, data = NULL, geom = GeomStream, position = "identity", ..., na.rm = FALSE, show.legend = NA, inherit.aes = TRUE, fun, xlim = NULL, ylim = NULL, n = 11, args = list(), max_it = 1000, tol = sqrt(.Machine$double.eps), T = NULL, L = NULL, center = TRUE, type = "stream", normalize = TRUE, tail_point = FALSE, eval_point = FALSE, grid = NULL, method = "rk4", lineend = "butt", linejoin = "round", linemitre = 10, arrow = grid::arrow(angle = 30, length = unit(0.02, "npc"), type = "closed") ) geom_stream_field2( mapping = NULL, data = NULL, stat = StatStreamField, position = "identity", ..., na.rm = FALSE, show.legend = NA, inherit.aes = FALSE, fun, xlim = NULL, ylim = NULL, n = 11, args = list(), max_it = 1000, tol = sqrt(.Machine$double.eps), L = NULL, center = FALSE, type = "stream", tail_point = TRUE, eval_point = FALSE, grid = NULL, lineend = "butt", linejoin = "round", linemitre = 10, method = "rk4" ) stat_stream_field2( mapping = NULL, data = NULL, geom = GeomStream, position = "identity", ..., na.rm = FALSE, show.legend = NA, inherit.aes = FALSE, fun, xlim = NULL, ylim = NULL, n = 11, args = list(), max_it = 1000, tol = sqrt(.Machine$double.eps), L = NULL, center = FALSE, type = "stream", tail_point = TRUE, eval_point = FALSE, grid = NULL, lineend = "butt", linejoin = "round", linemitre = 10, method = "rk4" )
mapping |
A set of aesthetic mappings created by |
data |
A data frame or other object, as in |
stat |
The statistical transformation to use on the data (default: StatStreamField). |
position |
Position adjustment, either as a string or the result of a position adjustment function. |
... |
Other arguments passed to |
na.rm |
Logical. If |
show.legend |
Logical. Should this layer be included in the legends? |
inherit.aes |
Logical. If |
fun |
A function of two variables, |
xlim |
Numeric vector of length 2 specifying the domain limits in the
|
ylim |
Numeric vector of length 2 specifying the domain limits in the
|
n |
Integer or two-element numeric vector specifying the grid resolution
(number of seed points) along each axis. Defaults to |
args |
A list of additional arguments passed to |
max_it |
|
tol |
|
T |
Numeric. When |
L |
Numeric. Maximum arc length for each streamline. When |
center |
Logical. If |
type |
Character. Either |
normalize |
Logical.
When |
tail_point |
Logical. If |
eval_point |
Logical. If |
grid |
A data frame containing precomputed grid points for seed
placement. If |
method |
Character. Integration method (e.g. |
lineend |
Line end style (round, butt, square). |
linejoin |
Line join style (round, mitre, bevel). |
linemitre |
Line mitre limit (number greater than 1). |
arrow |
A |
geom |
The geometric object used to render the streamlines (defaults to GeomStream). |
A ggplot2 layer that computes and renders streamlines over the specified domain.
geom_stream_field()
(and its stat variant) inherit
aesthetics from GeomStream and understand the following:
x
: x-coordinate of the seed point.
y
: y-coordinate of the seed point.
color
: Color, typically used to represent computed statistics (e.g. average speed).
linetype
: Type of line used to draw the streamlines.
linewidth
: Thickness of the streamlines.
alpha
: Transparency of the streamlines.
The streamlines are generated by numerically integrating
the vector field defined by fun(x, y)
. When normalize = TRUE
,
integration stops once the cumulative arc length reaches L
; otherwise,
integration runs until time T
is reached. If both T
and L
are
provided in incompatible combinations, one parameter is ignored. The
computed paths are rendered by GeomStream.
The following variables are computed internally by StatStreamField during the integration of the vector field:
For vector fields, this is computed as the total arc length divided
by the integration time, providing an estimate of the average speed. It is used to
scale the vector lengths when mapping length = after_stat(norm)
.
The integration time at each computed point along a streamline.
The distance between consecutive points along the computed path.
The cumulative arc length along the streamline, calculated as the cumulative
sum of d
.
f <- function(u) c(-u[2], u[1]) # the basic usage involves providing a fun, xlim, and ylim ggplot() + geom_stream_field(fun = f, xlim = c(-1,1), ylim = c(-1,1)) # if unspecified, xlim and ylim default to c(-1,1). we use this in what # follows to focus on other parts of the code ggplot() + geom_stream_field(fun = f) ggplot() + geom_stream_field(fun = f, center = FALSE) ggplot() + geom_stream_field(fun = f, normalize = FALSE) ggplot() + geom_stream_field(fun = f, normalize = FALSE, center = FALSE) # run systems until specified lengths ggplot() + geom_stream_field(fun = f, normalize = TRUE, L = .8) ggplot() + geom_vector_field(fun = f, normalize = TRUE, L = .3) ggplot() + geom_vector_field(fun = f, normalize = FALSE, L = 2) # run systems for specified times ggplot() + geom_stream_field(fun = f, normalize = FALSE, T = .1) # tail and eval points ggplot() + geom_stream_field(fun = f, tail_point = TRUE) ggplot() + geom_stream_field(fun = f, eval_point = TRUE) # changing the grid of evaluation ggplot() + geom_stream_field(fun = f) ggplot() + geom_stream_field(fun = f, grid = "hex") ggplot() + geom_stream_field(fun = f, grid = "hex", n = 5) ggplot() + geom_stream_field(fun = f, n = 5) ggplot() + geom_stream_field(fun = f, xlim = c(-5, 5)) + coord_equal() ggplot() + geom_stream_field(fun = f, xlim = c(-5, 5), n = c(21, 11)) + coord_equal() ggplot() + geom_stream_field(fun = f) ggplot() + geom_stream_field(fun = f, grid = grid_hex(c(-1,1), c(-1,1), .2)) # using other ggplot2 tools f <- efield_maker() ggplot() + geom_stream_field(fun = f, xlim = c(-2,2), ylim = c(-2,2)) ggplot() + geom_stream_field(fun = f, xlim = c(-2,2), ylim = c(-2,2)) + scale_color_viridis_c(trans = "log10") ggplot() + geom_stream_field(fun = f, xlim = c(-2,2), ylim = c(-2,2)) + scale_color_viridis_c(trans = "log10") + coord_equal() # other vector fields f <- function(u) u ggplot() + geom_stream_field(fun = f, xlim = c(-1,1), ylim = c(-1,1)) f <- function(u) c(2,1) ggplot() + geom_stream_field(fun = f, xlim = c(-1,1), ylim = c(-1,1)) # neat examples f <- function(u) { x <- u[1]; y <- u[2] c(y, y*(-x^2 - 2*y^2 + 1) - x) } ggplot() + geom_stream_field(fun = f, xlim = c(-2,2), ylim = c(-2,2)) ggplot() + geom_stream_field(fun = f, xlim = c(-2,2), ylim = c(-2,2), type = "vector") f <- function(u) { x <- u[1]; y <- u[2] c(y, x - x^3) } ggplot() + geom_stream_field(fun = f, xlim = c(-2,2), ylim = c(-2,2)) ggplot() + geom_stream_field(fun = f, xlim = c(-2,2), ylim = c(-2,2), grid = grid_hex(c(-2,2), c(-2,2), .35)) f <- function(u) { x <- u[1]; y <- u[2] c(x^2 - y^2, x^2 + y^2 - 2) } ggplot() + geom_stream_field(fun = f, xlim = c(-2,2), ylim = c(-2,2)) ggplot() + geom_stream_field(fun = f, xlim = c(-2,2), ylim = c(-2,2), grid = grid_hex(c(-2,2), c(-2,2), .35)) ggplot() + geom_stream_field(fun = f, aes(alpha = after_stat(t)), xlim = c(-2,2), ylim = c(-2,2)) + scale_alpha(range = c(0,1)) ggplot() + geom_stream_field( fun = f, xlim = c(-1,1), ylim = c(-1,1), linewidth = .75, arrow = arrow(length = unit(0.015, "npc")) )
f <- function(u) c(-u[2], u[1]) # the basic usage involves providing a fun, xlim, and ylim ggplot() + geom_stream_field(fun = f, xlim = c(-1,1), ylim = c(-1,1)) # if unspecified, xlim and ylim default to c(-1,1). we use this in what # follows to focus on other parts of the code ggplot() + geom_stream_field(fun = f) ggplot() + geom_stream_field(fun = f, center = FALSE) ggplot() + geom_stream_field(fun = f, normalize = FALSE) ggplot() + geom_stream_field(fun = f, normalize = FALSE, center = FALSE) # run systems until specified lengths ggplot() + geom_stream_field(fun = f, normalize = TRUE, L = .8) ggplot() + geom_vector_field(fun = f, normalize = TRUE, L = .3) ggplot() + geom_vector_field(fun = f, normalize = FALSE, L = 2) # run systems for specified times ggplot() + geom_stream_field(fun = f, normalize = FALSE, T = .1) # tail and eval points ggplot() + geom_stream_field(fun = f, tail_point = TRUE) ggplot() + geom_stream_field(fun = f, eval_point = TRUE) # changing the grid of evaluation ggplot() + geom_stream_field(fun = f) ggplot() + geom_stream_field(fun = f, grid = "hex") ggplot() + geom_stream_field(fun = f, grid = "hex", n = 5) ggplot() + geom_stream_field(fun = f, n = 5) ggplot() + geom_stream_field(fun = f, xlim = c(-5, 5)) + coord_equal() ggplot() + geom_stream_field(fun = f, xlim = c(-5, 5), n = c(21, 11)) + coord_equal() ggplot() + geom_stream_field(fun = f) ggplot() + geom_stream_field(fun = f, grid = grid_hex(c(-1,1), c(-1,1), .2)) # using other ggplot2 tools f <- efield_maker() ggplot() + geom_stream_field(fun = f, xlim = c(-2,2), ylim = c(-2,2)) ggplot() + geom_stream_field(fun = f, xlim = c(-2,2), ylim = c(-2,2)) + scale_color_viridis_c(trans = "log10") ggplot() + geom_stream_field(fun = f, xlim = c(-2,2), ylim = c(-2,2)) + scale_color_viridis_c(trans = "log10") + coord_equal() # other vector fields f <- function(u) u ggplot() + geom_stream_field(fun = f, xlim = c(-1,1), ylim = c(-1,1)) f <- function(u) c(2,1) ggplot() + geom_stream_field(fun = f, xlim = c(-1,1), ylim = c(-1,1)) # neat examples f <- function(u) { x <- u[1]; y <- u[2] c(y, y*(-x^2 - 2*y^2 + 1) - x) } ggplot() + geom_stream_field(fun = f, xlim = c(-2,2), ylim = c(-2,2)) ggplot() + geom_stream_field(fun = f, xlim = c(-2,2), ylim = c(-2,2), type = "vector") f <- function(u) { x <- u[1]; y <- u[2] c(y, x - x^3) } ggplot() + geom_stream_field(fun = f, xlim = c(-2,2), ylim = c(-2,2)) ggplot() + geom_stream_field(fun = f, xlim = c(-2,2), ylim = c(-2,2), grid = grid_hex(c(-2,2), c(-2,2), .35)) f <- function(u) { x <- u[1]; y <- u[2] c(x^2 - y^2, x^2 + y^2 - 2) } ggplot() + geom_stream_field(fun = f, xlim = c(-2,2), ylim = c(-2,2)) ggplot() + geom_stream_field(fun = f, xlim = c(-2,2), ylim = c(-2,2), grid = grid_hex(c(-2,2), c(-2,2), .35)) ggplot() + geom_stream_field(fun = f, aes(alpha = after_stat(t)), xlim = c(-2,2), ylim = c(-2,2)) + scale_alpha(range = c(0,1)) ggplot() + geom_stream_field( fun = f, xlim = c(-1,1), ylim = c(-1,1), linewidth = .75, arrow = arrow(length = unit(0.015, "npc")) )
geom_stream_smooth()
creates a ggplot2 layer that visualizes a smooth
vector field based on raw vector data. The function fits a multivariate
linear model (by default, using the formula cbind(fx, fy) ~ x * y
) to
predict the vector displacements at any given location. It also handles
different input formats by converting polar coordinates or endpoint data to
vector displacements.
geom_stream_smooth( mapping = NULL, data = NULL, stat = StatStreamField, position = "identity", ..., na.rm = FALSE, show.legend = NA, inherit.aes = TRUE, n = 11, xlim = NULL, ylim = NULL, normalize = TRUE, center = FALSE, type = "vector", formula = cbind(fx, fy) ~ x * y, eval_points = NULL, lineend = "butt", linejoin = "round", linemitre = 10, arrow = grid::arrow(angle = 20, length = unit(0.015, "npc"), type = "closed") ) stat_stream_smooth( mapping = NULL, data = NULL, geom = GeomStream, position = "identity", ..., na.rm = FALSE, show.legend = NA, inherit.aes = TRUE, n = 11, xlim = NULL, ylim = NULL, normalize = TRUE, center = FALSE, type = "vector", formula = cbind(fx, fy) ~ x * y, eval_points = NULL, lineend = "butt", linejoin = "round", linemitre = 10, arrow = grid::arrow(angle = 20, length = unit(0.015, "npc"), type = "closed") )
geom_stream_smooth( mapping = NULL, data = NULL, stat = StatStreamField, position = "identity", ..., na.rm = FALSE, show.legend = NA, inherit.aes = TRUE, n = 11, xlim = NULL, ylim = NULL, normalize = TRUE, center = FALSE, type = "vector", formula = cbind(fx, fy) ~ x * y, eval_points = NULL, lineend = "butt", linejoin = "round", linemitre = 10, arrow = grid::arrow(angle = 20, length = unit(0.015, "npc"), type = "closed") ) stat_stream_smooth( mapping = NULL, data = NULL, geom = GeomStream, position = "identity", ..., na.rm = FALSE, show.legend = NA, inherit.aes = TRUE, n = 11, xlim = NULL, ylim = NULL, normalize = TRUE, center = FALSE, type = "vector", formula = cbind(fx, fy) ~ x * y, eval_points = NULL, lineend = "butt", linejoin = "round", linemitre = 10, arrow = grid::arrow(angle = 20, length = unit(0.015, "npc"), type = "closed") )
mapping |
A set of aesthetic mappings created by |
data |
A data frame containing the raw vector data. |
stat |
The statistical transformation to use on the data. Defaults to
|
position |
Position adjustment, either as a string or the result of a position adjustment function. |
... |
Additional arguments passed to the layer. If a fixed parameter
|
na.rm |
Logical. If |
show.legend |
Logical. Should this layer be included in the legends? |
inherit.aes |
Logical. If |
n |
An integer vector specifying the grid resolution for smoothing. |
xlim |
Numeric vector of length 2 specifying the domain limits in the
|
ylim |
Numeric vector of length 2 specifying the domain limits in the
|
normalize |
Logical. If |
center |
Logical. If |
type |
Character. Either |
formula |
A formula specifying the multivariate linear model used for
smoothing. Defaults to |
eval_points |
A data frame of evaluation points, or |
lineend |
Line end style (round, butt, square). |
linejoin |
Line join style (round, mitre, bevel). |
linemitre |
Line mitre limit (number greater than 1). |
arrow |
An optional |
geom |
The geometric object used to render the streamline (only used in
|
A ggplot2 layer that can be added to a ggplot object to display a smoothed vector field.
Computed as the Euclidean norm of the displacement,
, this variable is used to normalize and scale the
vector lengths.
The integration time or evaluation time at each computed point along the smoothed field (when applicable).
The incremental distance between consecutive computed points.
The cumulative arc length along the smoothed vector field, calculated
as the cumulative sum of d
.
geom_stream_smooth()
supports the following aesthetics
(required aesthetics are in bold):
x
: The x-coordinate of the vector's starting point.
y
: The y-coordinate of the vector's starting point.
fx
: The displacement along the x-axis.
fy
: The displacement along the y-axis.
color
: The fixed color for the vector. Defaults to "blue"
.
linewidth
: The thickness of the vector line.
linetype
: The type of the vector line (e.g., solid or dashed).
alpha
: The transparency level of the vector.
arrow
: Specifies arrowheads for the vectors.
Data Conversion:
If xend
/yend
are missing or all NA
, the function computes them. It
first checks for vector displacements (fx
and fy
); if present, it
computes Otherwise, it checks for
polar coordinates (
angle
and distance
) and computes An error is thrown if neither set is
available.
Smoothing:
The multivariate linear model is fitted using the provided formula
and
data
. This model is then used to predict vector displacements at any
specified grid point, generating a smooth approximation of the vector
field.
Prediction Intervals: Two types of prediction intervals can be displayed:
Ellipse: Depicts the joint uncertainty (covariance) in the predicted fx
and fy
.
Wedge: Indicates the range of possible vector directions and magnitudes.
# Define a true vector field function f <- function(u) { x <- u[1]; y <- u[2] c(x^2 - y^2, x^2 + y^2 - 2) } # Alternative example function f <- function(u) c(-u[2], u[1]) # Visualize the vector field ggplot() + geom_stream_field(fun = f, xlim = c(-2, 2), ylim = c(-2, 2)) # Generate design points n <- 20 df <- data.frame(x = runif(n, -2, 2), y = runif(n, -2, 2)) # Sample function values at design points fdf <- as.data.frame(t(apply(df, 1, f))) colnames(fdf) <- c("fx", "fy") df <- cbind(df, fdf) # Visualize raw vector field data ggplot(df) + geom_vector(aes(x, y, fx = fx, fy = fy)) # Add smoothed layer using default model ggplot(df) + geom_vector(aes(x, y, fx = fx, fy = fy)) + geom_stream_smooth(formula = cbind(fx, fy) ~ x * y) # Use a more complex polynomial model ggplot(df) + geom_vector(aes(x, y, fx = fx, fy = fy)) + geom_stream_smooth(formula = cbind(fx, fy) ~ poly(x, 2) * poly(y, 2), data = df) # Fit a linear model and use it for prediction fhat <- function(u) { model <- lm(cbind(fx, fy) ~ x * y, data = df) predict(model, newdata = data.frame(x = u[1], y = u[2])) |> as.numeric() } # Visualize estimated field with the raw vector field ggplot(df) + geom_stream_field(fun = fhat, normalize = FALSE, color = "#3366FF") + geom_vector(aes(x, y, fx = fx, fy = fy)) # Generate a hexagonal grid hex_lattice <- grid_hex(xlim = c(-5, 5), ylim = c(-5, 5), d = 1) # Use the hexagonal grid in geom_stream_field ggplot(data = df) + geom_vector(aes(x, y, fx = fx, fy = fy), color = "black", normalize = FALSE) + geom_stream_smooth(eval_points = hex_lattice) # user specified point eval_pts <- data.frame(x = c(0, 1), y = c(2, -1)) ggplot(data = df) + geom_vector(aes(x, y, fx = fx, fy = fy), color = "black", normalize = FALSE) + geom_stream_smooth(eval_points = eval_pts)
# Define a true vector field function f <- function(u) { x <- u[1]; y <- u[2] c(x^2 - y^2, x^2 + y^2 - 2) } # Alternative example function f <- function(u) c(-u[2], u[1]) # Visualize the vector field ggplot() + geom_stream_field(fun = f, xlim = c(-2, 2), ylim = c(-2, 2)) # Generate design points n <- 20 df <- data.frame(x = runif(n, -2, 2), y = runif(n, -2, 2)) # Sample function values at design points fdf <- as.data.frame(t(apply(df, 1, f))) colnames(fdf) <- c("fx", "fy") df <- cbind(df, fdf) # Visualize raw vector field data ggplot(df) + geom_vector(aes(x, y, fx = fx, fy = fy)) # Add smoothed layer using default model ggplot(df) + geom_vector(aes(x, y, fx = fx, fy = fy)) + geom_stream_smooth(formula = cbind(fx, fy) ~ x * y) # Use a more complex polynomial model ggplot(df) + geom_vector(aes(x, y, fx = fx, fy = fy)) + geom_stream_smooth(formula = cbind(fx, fy) ~ poly(x, 2) * poly(y, 2), data = df) # Fit a linear model and use it for prediction fhat <- function(u) { model <- lm(cbind(fx, fy) ~ x * y, data = df) predict(model, newdata = data.frame(x = u[1], y = u[2])) |> as.numeric() } # Visualize estimated field with the raw vector field ggplot(df) + geom_stream_field(fun = fhat, normalize = FALSE, color = "#3366FF") + geom_vector(aes(x, y, fx = fx, fy = fy)) # Generate a hexagonal grid hex_lattice <- grid_hex(xlim = c(-5, 5), ylim = c(-5, 5), d = 1) # Use the hexagonal grid in geom_stream_field ggplot(data = df) + geom_vector(aes(x, y, fx = fx, fy = fy), color = "black", normalize = FALSE) + geom_stream_smooth(eval_points = hex_lattice) # user specified point eval_pts <- data.frame(x = c(0, 1), y = c(2, -1)) ggplot(data = df) + geom_vector(aes(x, y, fx = fx, fy = fy), color = "black", normalize = FALSE) + geom_stream_smooth(eval_points = eval_pts)
Create layers for drawing vectors on ggplot2 plots. These functions accept
wide-format data with the required aesthetics x
and y
plus either
xend
and yend
or one of the alternative specifications: fx
and fy
, or angle
/ angle_deg
and distance
.
geom_vector( mapping = NULL, data = NULL, stat = StatVector, position = "identity", ..., na.rm = FALSE, show.legend = NA, inherit.aes = TRUE, center = TRUE, normalize = TRUE, tail_point = FALSE, eval_point = FALSE, L = NULL, arrow = grid::arrow(angle = 25, length = unit(0.025, "npc"), type = "closed") ) stat_vector( mapping = NULL, data = NULL, geom = GeomStream, position = "identity", ..., na.rm = FALSE, show.legend = NA, inherit.aes = TRUE, center = TRUE, normalize = TRUE, tail_point = FALSE, eval_point = FALSE, L = NULL, arrow = grid::arrow(angle = 25, length = unit(0.025, "npc"), type = "closed") ) geom_vector2( mapping = NULL, data = NULL, stat = StatVector, position = "identity", ..., na.rm = FALSE, show.legend = NA, inherit.aes = TRUE, center = FALSE, tail_point = TRUE, eval_point = FALSE, L = NULL, arrow = NULL ) stat_vector2( mapping = NULL, data = NULL, geom = GeomStream, position = "identity", ..., na.rm = FALSE, show.legend = NA, inherit.aes = TRUE, center = FALSE, tail_point = TRUE, eval_point = FALSE, L = NULL, arrow = NULL )
geom_vector( mapping = NULL, data = NULL, stat = StatVector, position = "identity", ..., na.rm = FALSE, show.legend = NA, inherit.aes = TRUE, center = TRUE, normalize = TRUE, tail_point = FALSE, eval_point = FALSE, L = NULL, arrow = grid::arrow(angle = 25, length = unit(0.025, "npc"), type = "closed") ) stat_vector( mapping = NULL, data = NULL, geom = GeomStream, position = "identity", ..., na.rm = FALSE, show.legend = NA, inherit.aes = TRUE, center = TRUE, normalize = TRUE, tail_point = FALSE, eval_point = FALSE, L = NULL, arrow = grid::arrow(angle = 25, length = unit(0.025, "npc"), type = "closed") ) geom_vector2( mapping = NULL, data = NULL, stat = StatVector, position = "identity", ..., na.rm = FALSE, show.legend = NA, inherit.aes = TRUE, center = FALSE, tail_point = TRUE, eval_point = FALSE, L = NULL, arrow = NULL ) stat_vector2( mapping = NULL, data = NULL, geom = GeomStream, position = "identity", ..., na.rm = FALSE, show.legend = NA, inherit.aes = TRUE, center = FALSE, tail_point = TRUE, eval_point = FALSE, L = NULL, arrow = NULL )
mapping |
A set of aesthetic mappings created by |
data |
A data frame containing the vector data in wide format. |
stat |
The statistical transformation to use on the data (default: StatVector). |
position |
Position adjustment, either as a string or the result of a position adjustment function. |
... |
Other arguments passed on to |
na.rm |
Logical. If |
show.legend |
Logical. Should this layer be included in the legends? |
inherit.aes |
Logical. If |
center |
Logical. If |
normalize |
Logical. If |
tail_point |
Logical. If |
eval_point |
Logical. If |
L |
Numeric scalar. The desired length for the vectors in data units. If
|
arrow |
An optional |
geom |
The geometric object used to render the streamline (only used in
|
When specifying the vector direction using polar coordinates, you can provide either:
angle
: the vector direction in radians.
angle_deg
: the vector direction in degrees (which is
automatically converted to radians).
The endpoints are computed by translating the starting point using these
polar coordinates along with the supplied distance
.
The data is converted to long format (two rows per vector) via StatVector and rendered with GeomStream. Optionally, arrowheads can be added to indicate direction.
There are two variants:
geom_vector()
: Uses the user-supplied aesthetic mapping.
geom_vector2()
: Uses the same underlying stat (StatVector) but adds a
default mapping for length = after_stat(norm)
, making the computed vector
norm available as an aesthetic.
A ggplot2 layer that can be added to a plot.
geom_vector()
and geom_vector2()
understand the following aesthetics
(required aesthetics are in bold):
x
y
xend
yend
fx (alternative specification)
fy (alternative specification)
angle (vector direction in radians; alternative specification)
angle_deg (vector direction in degrees; alternative specification, converted to radians)
distance (with angle/angle_deg, used to compute endpoints)
alpha
color
fill
group
linetype
size
These are calculated by the 'stat' part of layers and can be accessed with delayed evaluation.
Calculated as the Euclidean distance between the starting point
(x
, y
) and the computed endpoint (xend
, yend
). This
value is used to normalize the vector length when the normalize
parameter
is set to TRUE
.
set.seed(1234) n <- 10 # Generate wind data in polar coordinates data <- data.frame( x = rnorm(n), y = rnorm(n), dir = runif(n, -pi, pi), # angle in radians spd = rchisq(n, df = 2) # speed ) |> transform(fx = spd * cos(dir), fy = spd * sin(dir)) # Using fx/fy to compute endpoints ggplot(data, aes(x, y)) + geom_vector(aes(fx = fx, fy = fy)) # Using angle (in radians) and distance to compute endpoints ggplot(data, aes(x, y)) + geom_vector(aes(angle = dir, distance = spd)) # Using angle_deg (in degrees) and distance to compute endpoints vectors3 <- data.frame( x = c(0, 1, 2), y = c(0, 1, 2), angle_deg = c(0, 90, 45), angle = c(0, pi/2, pi/4), distance = c(3, 4, 5) ) ggplot(vectors3, aes(x, y)) + geom_vector(aes(angle_deg = angle_deg, distance = distance)) # Basic usage with explicit start and end points: vectors1 <- data.frame( x = c(0, 1, 2), y = c(0, 1, 2), xend = c(3, 1, 5), yend = c(0, 5, 6) ) ggplot(vectors1, aes(x = x, y = y, xend = xend, yend = yend)) + geom_vector() # Using center = TRUE to recenter vectors: ggplot(vectors1, aes(x = x, y = y, xend = xend, yend = yend)) + geom_vector(center = TRUE) # Using normalize = TRUE to adjust vectors to unit length: ggplot(vectors3, aes(x = x, y = y, angle = angle, distance = distance)) + geom_vector(normalize = TRUE) # Using geom_vector2, which adds a default mapping for `length` ggplot(vectors1, aes(x = x, y = y, xend = xend, yend = yend)) + geom_vector2()
set.seed(1234) n <- 10 # Generate wind data in polar coordinates data <- data.frame( x = rnorm(n), y = rnorm(n), dir = runif(n, -pi, pi), # angle in radians spd = rchisq(n, df = 2) # speed ) |> transform(fx = spd * cos(dir), fy = spd * sin(dir)) # Using fx/fy to compute endpoints ggplot(data, aes(x, y)) + geom_vector(aes(fx = fx, fy = fy)) # Using angle (in radians) and distance to compute endpoints ggplot(data, aes(x, y)) + geom_vector(aes(angle = dir, distance = spd)) # Using angle_deg (in degrees) and distance to compute endpoints vectors3 <- data.frame( x = c(0, 1, 2), y = c(0, 1, 2), angle_deg = c(0, 90, 45), angle = c(0, pi/2, pi/4), distance = c(3, 4, 5) ) ggplot(vectors3, aes(x, y)) + geom_vector(aes(angle_deg = angle_deg, distance = distance)) # Basic usage with explicit start and end points: vectors1 <- data.frame( x = c(0, 1, 2), y = c(0, 1, 2), xend = c(3, 1, 5), yend = c(0, 5, 6) ) ggplot(vectors1, aes(x = x, y = y, xend = xend, yend = yend)) + geom_vector() # Using center = TRUE to recenter vectors: ggplot(vectors1, aes(x = x, y = y, xend = xend, yend = yend)) + geom_vector(center = TRUE) # Using normalize = TRUE to adjust vectors to unit length: ggplot(vectors3, aes(x = x, y = y, angle = angle, distance = distance)) + geom_vector(normalize = TRUE) # Using geom_vector2, which adds a default mapping for `length` ggplot(vectors1, aes(x = x, y = y, xend = xend, yend = yend)) + geom_vector2()
These functions provide convenient ggplot2 layers for drawing vector fields using streamlines.
geom_vector_field( mapping = NULL, data = NULL, stat = StatStreamField, position = "identity", ..., na.rm = FALSE, show.legend = NA, inherit.aes = FALSE, fun, xlim = NULL, ylim = NULL, n = 11, args = list(), center = TRUE, normalize = TRUE, tail_point = FALSE, eval_point = FALSE, grid = NULL, lineend = "butt", linejoin = "round", linemitre = 10, arrow = grid::arrow(angle = 30, length = unit(0.02, "npc"), type = "closed") ) stat_vector_field( mapping = NULL, data = NULL, stat = StatStreamField, geom = GeomStream, position = "identity", ..., na.rm = FALSE, show.legend = NA, inherit.aes = FALSE, fun, xlim = NULL, ylim = NULL, n = 11, args = list(), center = TRUE, normalize = TRUE, tail_point = FALSE, eval_point = FALSE, grid = NULL, lineend = "butt", linejoin = "round", linemitre = 10, arrow = grid::arrow(angle = 30, length = unit(0.02, "npc"), type = "closed") ) geom_vector_field2( mapping = NULL, data = NULL, stat = StatStreamField, position = "identity", ..., na.rm = FALSE, show.legend = TRUE, inherit.aes = FALSE, fun, xlim = NULL, ylim = NULL, n = 11, args = list(), center = FALSE, tail_point = TRUE, eval_point = FALSE, grid = NULL, lineend = "butt", linejoin = "round", linemitre = 10, arrow = NULL ) stat_vector_field2( mapping = NULL, data = NULL, geom = GeomStream, position = "identity", ..., na.rm = FALSE, show.legend = TRUE, inherit.aes = FALSE, fun, xlim = NULL, ylim = NULL, n = 11, args = list(), center = FALSE, tail_point = TRUE, eval_point = FALSE, grid = NULL, lineend = "butt", linejoin = "round", linemitre = 10, arrow = NULL )
geom_vector_field( mapping = NULL, data = NULL, stat = StatStreamField, position = "identity", ..., na.rm = FALSE, show.legend = NA, inherit.aes = FALSE, fun, xlim = NULL, ylim = NULL, n = 11, args = list(), center = TRUE, normalize = TRUE, tail_point = FALSE, eval_point = FALSE, grid = NULL, lineend = "butt", linejoin = "round", linemitre = 10, arrow = grid::arrow(angle = 30, length = unit(0.02, "npc"), type = "closed") ) stat_vector_field( mapping = NULL, data = NULL, stat = StatStreamField, geom = GeomStream, position = "identity", ..., na.rm = FALSE, show.legend = NA, inherit.aes = FALSE, fun, xlim = NULL, ylim = NULL, n = 11, args = list(), center = TRUE, normalize = TRUE, tail_point = FALSE, eval_point = FALSE, grid = NULL, lineend = "butt", linejoin = "round", linemitre = 10, arrow = grid::arrow(angle = 30, length = unit(0.02, "npc"), type = "closed") ) geom_vector_field2( mapping = NULL, data = NULL, stat = StatStreamField, position = "identity", ..., na.rm = FALSE, show.legend = TRUE, inherit.aes = FALSE, fun, xlim = NULL, ylim = NULL, n = 11, args = list(), center = FALSE, tail_point = TRUE, eval_point = FALSE, grid = NULL, lineend = "butt", linejoin = "round", linemitre = 10, arrow = NULL ) stat_vector_field2( mapping = NULL, data = NULL, geom = GeomStream, position = "identity", ..., na.rm = FALSE, show.legend = TRUE, inherit.aes = FALSE, fun, xlim = NULL, ylim = NULL, n = 11, args = list(), center = FALSE, tail_point = TRUE, eval_point = FALSE, grid = NULL, lineend = "butt", linejoin = "round", linemitre = 10, arrow = NULL )
mapping |
A set of aesthetic mappings created by |
data |
A data frame containing the input data. |
stat |
The statistical transformation to use on the data for this layer. Defaults to StatStreamField. |
position |
Position adjustment, either as a string or the result of a call to a position adjustment function. |
... |
Other arguments passed on to |
na.rm |
Logical. If |
show.legend |
Logical. Should this layer be included in the legends? |
inherit.aes |
Logical. If |
fun |
A function that defines the vector field. It should take a numeric
vector of length 2 (representing |
xlim |
Numeric vector of length two. Specifies the limits of the x-axis
domain. Defaults to |
ylim |
Numeric vector of length two. Specifies the limits of the y-axis
domain. Defaults to |
n |
Integer. Grid resolution specifying the number of seed points along
each axis. Higher values produce a denser vector field. Defaults to |
args |
List of additional arguments passed on to the function defined by
|
center |
Logical. If |
normalize |
Logical. If |
tail_point |
Logical. If |
eval_point |
Logical. If |
grid |
A data frame containing precomputed grid points for seed
placement. If |
lineend |
Line end style (round, butt, square). |
linejoin |
Line join style (round, mitre, bevel). |
linemitre |
Line mitre limit (number greater than 1). |
arrow |
A |
geom |
The geometric object used to render the streamline (only used in
|
A user-defined function (fun
) specifies the behavior of the vector field by
taking a numeric vector of length 2 (representing ) and returning
a numeric vector of length 2 (representing
). The underlying
StatStreamField computes the streamlines based on the vector field
function, and GeomStream renders them.
Two variants are provided:
geom_vector_field() uses a default mapping that sets color = after_stat(norm)
.
geom_vector_field2() uses a default mapping that sets length = after_stat(norm)
(with color
unmapped by default).
A ggplot2 layer that computes and plots a vector field using streamlines.
Calculated as the Euclidean distance between the starting point
(x
, y
) and the computed endpoint. Used to normalize the
vector.
geom_vector_field()
and geom_vector_field2()
understand the following aesthetics (required aesthetics are in bold):
x
: The x-coordinate of the vector's starting point.
y
: The y-coordinate of the vector's starting point.
fx
: The horizontal component of the vector displacement.
fy
: The vertical component of the vector displacement.
color
: The color of the vector lines (default mapping in geom_vector_field).
length
: The computed vector norm (default mapping in geom_vector_field2).
linetype
: The type of the vector line (e.g., solid, dashed).
linewidth
: The thickness of the vector line.
alpha
: The transparency of the vector.
f <- function(u) c(-u[2], u[1]) ggplot() + geom_vector_field(fun = f, xlim = c(-1,1), ylim = c(-1,1)) # xlim and ylim default to (-1,1), so for ease of illustration we remove them ggplot() + geom_vector_field(fun = f) ggplot() + geom_vector_field(fun = f, grid = "hex") ggplot() + geom_vector_field2(fun = f) ggplot() + geom_vector_field2(fun = f, grid = "hex") f <- efield_maker() ggplot() + geom_vector_field(fun = f, xlim = c(-2,2), ylim = c(-2,2)) ggplot() + geom_vector_field2(fun = f, xlim = c(-2,2), ylim = c(-2,2))
f <- function(u) c(-u[2], u[1]) ggplot() + geom_vector_field(fun = f, xlim = c(-1,1), ylim = c(-1,1)) # xlim and ylim default to (-1,1), so for ease of illustration we remove them ggplot() + geom_vector_field(fun = f) ggplot() + geom_vector_field(fun = f, grid = "hex") ggplot() + geom_vector_field2(fun = f) ggplot() + geom_vector_field2(fun = f, grid = "hex") f <- efield_maker() ggplot() + geom_vector_field(fun = f, xlim = c(-2,2), ylim = c(-2,2)) ggplot() + geom_vector_field2(fun = f, xlim = c(-2,2), ylim = c(-2,2))
A ggplot2 extension for visualizing vector fields in a two-dimensional space. Provides functions to create vector field layers using user-defined vector field functions.
Useful links:
This function generates a hexagonal lattice of points within the given x and y limits, using a specified hexagon diameter. The diameter is 2 times the distance between adjacent x (and y) values, see examples.
grid_hex(xlim, ylim, d)
grid_hex(xlim, ylim, d)
xlim |
A numeric vector of length 2 specifying the x-axis limits. |
ylim |
A numeric vector of length 2 specifying the y-axis limits. |
d |
A numeric value specifying the hexagon diameter. |
A data frame with two columns, x
and y
, containing the
coordinates of the hexagonal grid points.
xlim <- c(-1, 1) ylim <- c(-1, 0) grid <- grid_hex(xlim, ylim, .25) head( grid ) str( grid ) plot( grid, asp = 1 ) diff(sort(unique(grid$x)))
xlim <- c(-1, 1) ylim <- c(-1, 0) grid <- grid_hex(xlim, ylim, .25) head( grid ) str( grid ) plot( grid, asp = 1 ) diff(sort(unique(grid$x)))
scale_length_continuous()
provides a continuous scale for controlling the
length aesthetic in a ggplot. This is particularly useful when working with
vector plots where vector lengths are mapped to a continuous scale.
scale_length_continuous(max_range = 0.5, ...)
scale_length_continuous(max_range = 0.5, ...)
max_range |
The maximum value to which the input is rescaled. Numeric scalar specifying the upper bound of the output range. Should be between 0 and 1. |
... |
Other arguments passed to |
If max_range
is less than or equal to 0.5 (the default), a
continuous scale object (typically of class "ScaleContinuous"
)
mapping the length
aesthetic is returned. If max_range
is
greater than 0.5, a list is returned with two components:
the continuous scale object, and
a theme modification (a theme
object) that adjusts the legend key width based
on the value of max_range
.