Guides
Geoms
Each geom adds a layer. Every geom method accepts these common arguments:
aes:, mappings for this layer onlydata:, data for this layer onlyinheritAes: false, to ignore the plot’s mappingsfilter:, a Vega expression string or a selection predicate such as['param' => 'brush']- fixed styles by their ggplot2 names (
alpha,color,fill,size,linewidth,linetype,shape) or as any Vega-Lite mark property (strokeWidth: 2,cursor: 'pointer')
As in ggplot2, color is the outline of bars, areas, and tiles, and fill is their interior. For points, lines, and text, color is the main color. linetype accepts ggplot2’s names (dashed, dotted, dotdash, longdash, twodash), hex dash strings such as '44', or a dash array.
Distributions
histogram() bins a continuous variable. bins: sets the maximum number of bins, and Vega-Lite chooses rounded boundaries. binwidth: sets the width exactly and boundary: anchors the bins.
use Phiz\Datasets;
use Phiz\Plot;
$plot = Plot::of(Datasets::faithful())
->aes(x: 'waiting')
->histogram(binwidth: 2, fill: '#4C78A8')
->labs(x: 'Minutes between eruptions');density() draws a kernel density estimate per group with Vega-Lite’s density transform. bw: sets the bandwidth. adjust: multiplies a bandwidth computed with R’s bw.nrd0 rule. Mapping fill draws filled areas; otherwise the densities are lines.
use Phiz\Datasets;
use Phiz\Plot;
$plot = Plot::of(Datasets::mpg())
->aes(x: 'hwy', fill: 'drv')
->density(alpha: 0.4, adjust: 1.5)
->labs(x: 'Highway mpg', fill: 'Drive');boxplot() draws Tukey boxplots with whiskers at coef: interquartile ranges. Points layered on top show the raw data, here with jitter().
jitter(), or point(jitter:), moves points by a random amount: up to width: and height: of a band on a discrete axis, or in data units on a continuous one. Without a seed, the browser draws the offsets with Vega’s random(), so the points move on every render. seed: draws them in PHP with a fixed seed and embeds them in the data, so every render and every static export places the points the same way. A seed needs inline data.
use Phiz\Datasets;
use Phiz\Plot;
$plot = Plot::of(Datasets::mpg())
->aes(x: 'drv', y: 'hwy')
->boxplot(outliers: false, fill: '#DDDDDD', color: '#333333')
->jitter(width: 0.25, alpha: 0.35, seed: 1)
->labs(x: 'Drive', y: 'Highway mpg');violin() draws a kernel density of y for each level of a discrete x. Every violin has the same maximum width, set by width: as a share of the band, and violins layer with boxplots and points on the same axis. For inline data, each violin’s density is computed in PHP as ggplot2 computes it: its own bandwidth (bw:, or R’s bw.nrd0 times adjust:), evaluated at n: points (200 by default) across the group’s range. position: 'dodge' places groups side by side within the band.
use Phiz\Datasets;
use Phiz\Plot;
$plot = Plot::of(Datasets::mpg())
->aes(x: 'drv', y: 'hwy', fill: 'drv')
->violin(alpha: 0.6, adjust: 1.2)
->jitter(width: 0.08, alpha: 0.3, seed: 1)
->theme('gray')
->labs(x: 'Drive', y: 'Highway mpg', fill: 'Drive');ecdf(), freqpoly(), qq(), qqLine(), and rug() complete the set.
Amounts
bar() counts rows per level of x when y is unmapped, and col() draws the y values. position: is stack (the default), dodge, fill (stacked to 100%), or identity.
use Phiz\Datasets;
use Phiz\Plot;
$plot = Plot::of(Datasets::mpg())
->aes(x: 'class', fill: 'drv')
->bar(position: 'dodge')
->labs(x: null, y: 'Cars', fill: 'Drive');col() with xmin and xmax mapped draws precomputed bins. tile() and rect() draw heatmaps and rectangles, and bin2d() counts points in a two-dimensional grid.
use Phiz\Datasets;
use Phiz\Plot;
use function Phiz\factor;
$plot = Plot::of(Datasets::mpg())
->aes(x: 'class', y: factor('cyl'), fill: 'mean(hwy)')
->tile(color: 'white')
->scaleFill(scheme: 'viridis')
->labs(x: null, y: 'Cylinders', fill: 'Mean hwy');Lines and areas
line() connects points in order of x. path() connects them in data order, and step() draws stairsteps. area() fills from zero and stacks by fill, and ribbon() fills between ymin and ymax.
use Phiz\Datasets;
use Phiz\Plot;
$recent = array_slice(Datasets::economics(), -180);
$plot = Plot::of($recent)
->aes(x: 'date', y: 'uempmed')
->area(fill: '#9ECAE1', alpha: 0.5)
->line(color: '#08519C')
->labs(x: null, y: 'Median weeks unemployed');Intervals
errorbar(), linerange(), and pointrange() draw intervals from ymin to ymax. With xmin and xmax they are horizontal. position: 'dodge' places groups side by side, which the coefficient plots use to compare models.
use Phiz\Plot;
$estimates = [
['site' => 'North', 'mean' => 4.1, 'lower' => 3.2, 'upper' => 5.0],
['site' => 'East', 'mean' => 5.3, 'lower' => 4.6, 'upper' => 6.1],
['site' => 'South', 'mean' => 3.4, 'lower' => 2.1, 'upper' => 4.6],
['site' => 'West', 'mean' => 4.8, 'lower' => 4.2, 'upper' => 5.5],
];
$plot = Plot::of($estimates)
->aes(x: 'site', y: 'mean', ymin: 'lower', ymax: 'upper')
->errorbar(width: 10)
->point(size: 60)
->hline(4.5, linetype: 'dashed', color: '#888888')
->labs(x: null, y: 'Mean score (95% CI)');Reference lines
hline() and vline() draw rules at fixed values, at every value of a list, or at a mapped yintercept or xintercept field. abline() draws y = intercept + slope * x across the range of the data.
use Phiz\Datasets;
use Phiz\Plot;
$plot = Plot::of(Datasets::mpg())
->aes(x: 'cty', y: 'hwy')
->point(alpha: 0.4)
->abline(0, 1, linetype: 'dashed')
->hline(30, color: '#D62728')
->vline(20, color: '#1F77B4')
->labs(x: 'City mpg', y: 'Highway mpg');Text
text() and label() draw the label aesthetic. hjust, vjust, and angle follow ggplot2: angle turns text counterclockwise. nudgeX and nudgeY move labels in pixels.
use Phiz\Datasets;
use Phiz\Plot;
$fast = array_values(array_filter(Datasets::mtcars(), fn ($car) => $car['hp'] > 180));
$plot = Plot::of($fast)
->aes(x: 'hp', y: 'qsec', label: 'model')
->point()
->text(hjust: 0, nudgeX: 6, size: 10)
->labs(x: 'Horsepower', y: 'Quarter-mile time (s)');Fitted curves
smooth() fits a curve per group and, for inline data, draws a pointwise confidence band for the mean at level:, as ggplot2’s geom_smooth() does. The default method is loess: a local quadratic fit over the nearest span: share of the points (0.75 by default). phiz fits it in PHP, and the fit, its standard errors, and the t interval match R’s loess(y ~ x, span = 0.75, degree = 2) with surface = "direct" to within 1e-9. ggplot2’s own default interpolates the loess surface, so its curve can differ from R’s direct fit in the third decimal place.
use Phiz\Datasets;
use Phiz\Plot;
$plot = Plot::of(Datasets::mpg())
->aes(x: 'displ', y: 'hwy')
->point(alpha: 0.4)
->smooth(span: 0.75)
->labs(x: 'Displacement (L)', y: 'Highway mpg');method: 'lm', 'quad', and 'poly' (with order:) fit polynomials by least squares, and their bands match R’s predict(fit, interval = "confidence"). se: false and the other methods (log, exp, pow) use Vega-Lite’s regression and loess transforms in the browser, without a band. Above 3,000 rows a loess smooth also defaults to the browser, because the band costs O(n²) in PHP. Pass se: true to compute it anyway.
use Phiz\Datasets;
use Phiz\Plot;
$plot = Plot::of(Datasets::mtcars())
->aes(x: 'hp', y: 'mpg')
->point()
->smooth(method: 'poly', order: 2, level: 0.9)
->labs(x: 'Horsepower', y: 'mpg');curve() draws a PHP function over a range, as ggplot2’s geom_function() does.
use Phiz\Datasets;
use Phiz\Plot;
$eruptions = array_column(Datasets::faithful(), 'eruptions');
$mean = array_sum($eruptions) / count($eruptions);
$sd = sqrt(array_sum(array_map(fn ($x) => ($x - $mean) ** 2, $eruptions)) / (count($eruptions) - 1));
$normal = fn (float $x) => exp(-0.5 * (($x - $mean) / $sd) ** 2) / ($sd * sqrt(2 * M_PI));
$plot = Plot::of(Datasets::faithful())
->aes(x: 'eruptions')
->histogram(binwidth: 0.2, density: true, fill: '#DDDDDD')
->curve($normal, 1.5, 5.5, aes: ['x' => 'eruptions', 'y' => 'density'], color: '#D62728')
->labs(x: 'Eruption length (min)', subtitle: 'A single normal curve fits this bimodal distribution poorly');Summaries
summary() summarizes y at each value of x and draws the result with another geom, as ggplot2’s stat_summary() does. funData: returns the middle and the range together:
mean_se(the default): the mean, plus or minus one standard error (funArgs: ['mult' => 2]for two)mean_cl_normal: the mean with a t confidence interval (funArgs: ['conf' => 0.9])mean_sdl: the mean, plus or minus two standard deviationsmedian_hilow: the median with the outer quantiles atconf(0.95 by default)
A callable can take the place of any of them, returning [y, ymin, ymax]. fun:, funMin:, and funMax: set the parts separately, by name (mean, median, min, max, sum, sd, n) or as callables of the group’s values. geom: chooses how to draw the summaries: pointrange (the default), errorbar, linerange, crossbar, point, line, col, area, or ribbon. With a discrete y and a continuous x, x is summarized at each y instead.
use Phiz\Datasets;
use Phiz\Plot;
$plot = Plot::of(Datasets::mpg())
->aes(x: 'class', y: 'hwy')
->point(alpha: 0.25)
->summary(funData: 'mean_cl_normal', color: '#D62728')
->labs(x: null, y: 'Highway mpg', title: 'Means with 95% t intervals');crossbar() draws a box from ymin to ymax with a line at y, and it works with summary() and position: 'dodge'.
use Phiz\Datasets;
use Phiz\Plot;
$plot = Plot::of(Datasets::mpg())
->aes(x: 'class', y: 'hwy', fill: 'drv')
->summary(funData: 'median_hilow', funArgs: ['conf' => 0.5], geom: 'crossbar', position: 'dodge')
->labs(x: null, y: 'Highway mpg', title: 'Medians and quartiles');count() draws a point at each distinct x and y, sized by the number of rows there, as geom_count() does.
ellipse() draws a confidence ellipse per group, as stat_ellipse() does. type: 't' (the default) fits a multivariate t distribution with MASS’s cov.trob(), 'norm' uses the sample covariance, and 'euclid' draws a circle of radius level. The ellipses match ggplot2’s to within 1e-9. geom: 'polygon' fills them.
use Phiz\Datasets;
use Phiz\Plot;
$faithful = array_map(fn ($row) => $row + ['wait' => $row['waiting'] > 70 ? 'long' : 'short'], Datasets::faithful());
$plot = Plot::of($faithful)
->aes(x: 'eruptions', y: 'waiting', color: 'wait')
->point(alpha: 0.6)
->ellipse()
->ellipse(type: 'norm', linetype: 'dashed')
->labs(x: 'Eruption (minutes)', y: 'Waiting time (minutes)', color: 'Wait');Two-dimensional densities and contours
density2d() estimates a bivariate kernel density on a 100 × 100 grid and draws its contour lines, as geom_density_2d() does. The density is MASS’s kde2d() with bandwidth.nrd() bandwidths, computed in PHP, and the contour levels are R’s pretty() over the range of the density. h: sets the bandwidths, adjust: multiplies them, and bins:, binwidth:, or breaks: set the levels. Mapping color: 'level' colors each line by its level. Mapping color to a field estimates one density per group.
use Phiz\Datasets;
use Phiz\Plot;
$plot = Plot::of(Datasets::faithful())
->aes(x: 'eruptions', y: 'waiting')
->point(size: 10, alpha: 0.6)
->density2d()
->labs(x: 'Eruption (minutes)', y: 'Waiting time (minutes)');density2dFilled() fills the bands between consecutive levels and labels them as ggplot2 does, with the viridis palette. contourVar: 'ndensity' scales each group’s density to a maximum of 1, and 'count' multiplies it by the group’s size.
use Phiz\Datasets;
use Phiz\Plot;
$plot = Plot::of(Datasets::faithful())
->aes(x: 'eruptions', y: 'waiting')
->density2dFilled()
->labs(x: 'Eruption (minutes)', y: 'Waiting time (minutes)', fill: 'density');contour() and contourFilled() draw the same lines and bands for a z field on a regular grid of x and y, as geom_contour() and geom_contour_filled() do. phiz traces the contours in PHP with marching squares. The lines match ggplot2’s: the same levels and the same pieces.
use Phiz\Plot;
// z on a regular grid of x and y.
$grid = [];
foreach (range(-3, 3, 0.2) as $x) {
foreach (range(-3, 3, 0.2) as $y) {
$grid[] = ['x' => $x, 'y' => $y, 'z' => sin($x) * cos($y) + 0.1 * $x * $y];
}
}
$plot = Plot::of($grid)
->aes(x: 'x', y: 'y', z: 'z')
->contourFilled(bins: 7)
->contour(bins: 7, color: 'white', linewidth: 0.5);polygon() fills the shape through each group’s points in data order. Rows with null x and y start a new ring in the same group, and a ring drawn in the opposite direction cuts a hole.
Annotations
annotate() adds a layer from fixed values rather than data, as ggplot2’s annotate() does. It draws text, label, point, segment, rect, rule, pointrange, linerange, errorbar, or crossbar. Positions can be single values or lists, and -INF and INF reach the edges of the panel. Annotations ignore the plot’s mappings, keep its axis titles, and appear in every facet panel.
use Phiz\Datasets;
use Phiz\Plot;
$plot = Plot::of(Datasets::mpg())
->aes(x: 'cty', y: 'hwy')
->annotate('rect', xmin: 15, xmax: 20, ymin: -INF, ymax: INF, fill: 'orange', alpha: 0.2)
->count(alpha: 0.6)
->annotate('segment', x: 30, y: 25, xend: 33.5, yend: 42.5, color: '#D62728')
->annotate('text', x: 30, y: 23, label: 'Most efficient', color: '#D62728')
->labs(x: 'City mpg', y: 'Highway mpg');Raw layers
layer() adds any Vega-Lite layer as given, and geom() adds a geom by name with explicit parameter and style arrays.
From phpstats/phiz@bc7ed3e.