Examples
Gallery
Each chart below is drawn from the code under it. The code uses the bundled datasets, loaded as $mpg = Datasets::mpg(), $mtcars = Datasets::mtcars(), $economics = Datasets::economics(), and $faithful = Datasets::faithful(). The statistics examples use a linear model fit and posterior draws defined at the top of examples/catalog.php, which holds every example.
Basics
Scatterplot
Points with a color mapping.
Plot::of($mpg)
->aes(x: 'displ', y: 'hwy', color: 'class')
->point()
->labs(x: 'Engine displacement (L)', y: 'Highway mpg', color: 'Class');Linear fit with confidence band
lm fits are computed in PHP, with a 95% band, per group.
Plot::of($mpg)
->aes(x: 'displ', y: 'hwy', color: 'drv')
->point(alpha: 0.5)
->smooth(method: 'lm')
->labs(title: 'Bigger engines, fewer miles', x: 'Displacement (L)', y: 'Highway mpg', color: 'Drive');With ggplot2-style functions
ggplot2-style functions compose with apply(), or with the pipe operator on PHP 8.5.
ggplot($mpg, aes(x: 'displ', y: 'hwy'))->apply(
geom_point(alpha: 0.4),
geom_smooth(span: 0.5),
labs(title: 'Loess smooth', x: 'Displacement (L)', y: 'Highway mpg'),
theme_minimal(),
);Time series
ISO dates are read as temporal fields.
Plot::of($economics)
->aes(x: 'date', y: 'unemploy')
->line(color: '#1F77B4')
->labs(title: 'US unemployment', x: null, y: 'Unemployed (thousands)');Stacked area
Areas stack by fill.
Plot::of(array_merge(...array_map(fn ($r) => [
['date' => $r['date'], 'series' => 'Unemployed', 'value' => $r['unemploy']],
['date' => $r['date'], 'series' => 'Median weeks unemployed (x1000)', 'value' => $r['uempmed'] * 1000],
], array_slice($economics, -120))))
->aes(x: 'date', y: 'value', fill: 'series')
->area(alpha: 0.8)
->theme(legendPosition: 'bottom');Labels
Text labels with nudges.
Plot::of(array_slice($mtcars, 0, 12))
->aes(x: 'wt', y: 'mpg', label: 'model')
->point()
->text(nudgeY: 8, size: 9)
->labs(x: 'Weight (1000 lbs)', y: 'mpg');Distributions
Histogram
Binned counts; bins sets the maximum number of bins.
Plot::of($faithful)
->aes(x: 'waiting')
->histogram(bins: 30, fill: '#4C78A8')
->labs(x: 'Minutes between eruptions', y: 'Count');Histogram with density
density: true scales bars to unit area, so a KDE overlays.
Plot::of($faithful)
->aes(x: 'eruptions')
->histogram(binwidth: 0.25, density: true, fill: '#DDDDDD', color: '#999999')
->density(color: '#D62728', linewidth: 2)
->labs(x: 'Eruption length (minutes)');Density by group
Kernel densities per group with Vega-Lite’s density transform.
Plot::of($mpg)
->aes(x: 'hwy', fill: 'drv')
->density(alpha: 0.4)
->labs(x: 'Highway mpg', fill: 'Drive');Boxplots
Tukey boxplots, flipped for readable labels.
Plot::of($mpg)
->aes(x: 'class', y: 'hwy', fill: 'class')
->boxplot()
->coordFlip()
->scaleFill(guide: false)
->labs(x: null, y: 'Highway mpg');Violins with boxplots
Densities per level, layered with boxplots, in ggplot2’s gray theme and hue palette.
Plot::of($mpg)
->aes(x: 'class', y: 'hwy', fill: 'class')
->violin(alpha: 0.6)
->boxplot(width: 8, outliers: false)
->scaleFill(guide: false)
->theme('gray')
->labs(x: null, y: 'Highway mpg');Jittered strip plot
Points spread within each band.
Plot::of($mpg)
->aes(x: factor('cyl'), y: 'hwy', color: factor('cyl'))
->jitter(width: 0.3, alpha: 0.6)
->labs(x: 'Cylinders', y: 'Highway mpg')
->scaleColor(guide: false);Empirical CDF
Cumulative distributions per group.
Plot::of($mpg)
->aes(x: 'cty', color: 'drv')
->ecdf()
->labs(x: 'City mpg', y: 'Proportion ≤ x', color: 'Drive');QQ plot
Sample quantiles against normal quantiles, with the quartile line.
Plot::of($mpg)
->aes(sample: 'hwy')
->qq(alpha: 0.6)
->qqLine(color: '#D62728');2D density
Filled density bands from MASS::kde2d, computed in PHP, with the points on top.
Plot::of($faithful)
->aes(x: 'eruptions', y: 'waiting')
->density2dFilled()
->point(size: 6, color: 'white', alpha: 0.5)
->labs(x: 'Eruption (minutes)', y: 'Waiting (minutes)', fill: 'density');Confidence ellipses
stat_ellipse: robust t ellipses per group.
Plot::of($mpg)
->aes(x: 'displ', y: 'hwy', color: 'drv')
->point(alpha: 0.5)
->ellipse()
->labs(x: 'Displacement (L)', y: 'Highway mpg', color: 'Drive');Counts
Bar chart of counts
bar() counts rows when y is unmapped.
Plot::of($mpg)
->aes(x: 'class')
->bar(fill: '#4C78A8')
->labs(x: null, y: 'Cars');Dodged bars
Groups side by side.
Plot::of($mpg)
->aes(x: 'class', fill: 'drv')
->bar(position: 'dodge')
->labs(x: null, y: 'Cars', fill: 'Drive');Proportions
position: fill stacks to 100%.
Plot::of($mpg)
->aes(x: 'class', fill: 'drv')
->bar(position: 'fill')
->scaleY(format: '%')
->labs(x: null, y: 'Share of cars', fill: 'Drive');Bars of values
col() draws the y values, here an aggregate.
Plot::of($mpg)
->aes(x: 'manufacturer', y: 'mean(hwy)')
->col(fill: '#72B7B2')
->coordFlip()
->labs(x: null, y: 'Mean highway mpg');Heatmap
Tiles filled by an aggregate.
Plot::of($mpg)
->aes(x: 'class', y: factor('cyl'), fill: 'mean(hwy)')
->tile(color: 'white')
->scaleFill(scheme: 'viridis')
->labs(x: null, y: 'Cylinders', fill: 'Mean hwy');2D histogram
Counts in x-by-y bins.
Plot::of($faithful)
->aes(x: 'eruptions', y: 'waiting')
->bin2d(bins: 20)
->scaleFill(scheme: 'blues');Donut
Arcs sized by counts.
Plot::of($mpg)
->aes(fill: 'drv')
->pie(innerRadius: 50)
->labs(fill: 'Drive');Polar bars
coordPolar() turns bars into a rose.
Plot::of($mpg)
->aes(x: 'class', fill: 'class')
->bar()
->coordPolar()
->labs(fill: null);Annotations
annotate() adds shaded ranges, segments, and text.
Plot::of($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('text', x: 30, y: 23, label: 'Most efficient', color: '#D62728')
->labs(x: 'City mpg', y: 'Highway mpg');Facets
Small multiples
facetWrap with a fitted line in every panel.
Plot::of($mpg)
->aes(x: 'displ', y: 'hwy')
->point(alpha: 0.5)
->smooth(method: 'lm')
->facetWrap('class', ncol: 4)
->size(150, 120);Grid of panels
Rows by drive, columns by year.
Plot::of($mpg)
->aes(x: 'cty', y: 'hwy')
->point(alpha: 0.5)
->abline(0, 1, linetype: 'dashed', color: '#888888')
->facetGrid(rows: 'drv', cols: 'year')
->size(200, 120);Side by side
Compose::beside lays plots out like patchwork.
Compose::beside(
Plot::of($faithful)->aes(x: 'eruptions', y: 'waiting')->point(alpha: 0.5)->size(260, 220),
Plot::of($faithful)->aes(x: 'waiting')->histogram(bins: 20)->coordFlip()->size(120, 220),
)->title('Old Faithful');Scales and themes
Log scale
scaleX(type: log) with chosen breaks.
Plot::of($economics)
->aes(x: 'pce', y: 'psavert')
->point(alpha: 0.4)
->scaleX(type: 'log', breaks: [500, 1000, 2000, 5000, 10000], name: 'Personal consumption ($bn, log)')
->labs(y: 'Savings rate (%)');Manual colors
A level => color map.
Plot::of($mpg)
->aes(x: 'displ', y: 'hwy', color: 'drv')
->point()
->scaleColor(values: ['4' => '#D95F02', 'f' => '#1B9E77', 'r' => '#7570B3'], name: 'Drive');Continuous color
A viridis gradient for a numeric field.
Plot::of($mpg)
->aes(x: 'displ', y: 'hwy', color: 'cty')
->point(size: 50)
->scaleColor(scheme: 'viridis');theme(“gray”)
ggplot2’s default look.
Plot::of($mpg)
->aes(x: 'displ', y: 'hwy', color: 'drv')
->point()
->theme('gray');theme(“classic”, baseSize: 13)
Axis lines, no grid, larger text.
Plot::of($mpg)
->aes(x: 'displ', y: 'hwy')
->point()
->theme('classic', baseSize: 13);theme(“dark”)
A dark preset.
Plot::of($economics)
->aes(x: 'date', y: 'psavert')
->line(color: '#F58518')
->theme('dark');Interaction
Tooltips, pan and zoom
Hover for values; drag and scroll to explore.
Plot::of($mpg)
->aes(x: 'displ', y: 'hwy', color: 'class')
->point()
->tooltip(['manufacturer', 'model', 'year', 'displ', 'hwy'])
->interactive();Linked brushing
Brush the scatterplot to filter the bars.
Compose::stack(
Plot::of($mpg)->aes(x: 'displ', y: 'hwy', color: 'drv')->point()->brush(encodings: ['x', 'y'])->size(420, 220),
Plot::of($mpg)->aes(x: 'class')->bar(filter: ['param' => 'brush'])->size(420, 140),
);Legend highlight
Click a legend entry.
Plot::of($mpg)
->aes(x: 'displ', y: 'hwy', color: 'drv')
->point(size: 40)
->highlight();Slider-driven reference line
A parameter bound to a slider moves the rule and filters points.
Plot::of($mpg)
->aes(x: 'displ', y: 'hwy')
->slider('cutoff', 15, 40, 1, 30, 'Highway mpg cutoff')
->point(filter: 'datum.hwy >= cutoff', color: '#D62728')
->point(filter: 'datum.hwy < cutoff', color: '#BBBBBB')
->hline(expr('cutoff'), linetype: 'dashed');Statistics
Coefficient plot
Straight from a phit fit (fit_lm, fit_glm, …).
coefplot($fit)
->labs(title: 'mpg ~ wt + hp + am');Comparing models
Several fits, dodged.
coefplot(['Full' => $fit, 'Without am' => $reduced]);Regression diagnostics
Residuals, QQ, scale-location, observed vs fitted.
diagnostic_plots($fit, width: 230, height: 170);Predicted values
Predictions with 95% bands as weight varies, by transmission.
effect_plot($fit, $mtcars, 'wt', by: 'am', design: fn ($r) => [1, $r['wt'], $r['hp'], $r['am']])
->labs(x: 'Weight (1000 lbs)', y: 'mpg', color: 'Manual', fill: 'Manual');Posterior intervals
From CmdStanPHP draws_columns(), as bayesplot’s mcmc_intervals.
interval_plot($draws);Means with error bars
Precomputed summaries with errorbar and point.
Plot::of(array_map(function ($class) use ($mpg) {
$values = array_column(array_filter($mpg, fn ($r) => $r['class'] === $class), 'hwy');
$mean = array_sum($values) / count($values);
$sd = sqrt(array_sum(array_map(fn ($v) => ($v - $mean) ** 2, $values)) / (count($values) - 1));
$se = $sd / sqrt(count($values));
return ['class' => $class, 'mean' => $mean, 'lower' => $mean - 1.96 * $se, 'upper' => $mean + 1.96 * $se];
}, array_values(array_unique(array_column($mpg, 'class')))))
->aes(x: 'class', y: 'mean', ymin: 'lower', ymax: 'upper')
->errorbar(width: 8)
->point(size: 50)
->labs(x: null, y: 'Mean highway mpg (95% CI)');Summaries
stat_summary: means with t intervals over the raw data.
Plot::of($mpg)
->aes(x: 'class', y: 'hwy')
->point(alpha: 0.2)
->summary(funData: 'mean_cl_normal', color: '#D62728')
->labs(x: null, y: 'Highway mpg');Functions
curve() draws a PHP function, here the normal density over a histogram.
Plot::of(array_map(fn ($i) => ['z' => Fit::normalQuantile(($i + 0.5) / 500)], range(0, 499)))
->aes(x: 'z')
->histogram(binwidth: 0.25, density: true, fill: '#DDDDDD')
->curve(fn (float $x) => exp(-$x * $x / 2) / sqrt(2 * M_PI), -3.5, 3.5, aes: aes(x: 'z', y: 'density'), color: '#D62728', linewidth: 2);From phpstats/phiz@bc7ed3e.