Guide
Getting started
Installation
composer require phpstats/phplyrComposer also installs phibble, which provides the tables. The overview lists the supported PHP versions and the optional extensions.
Imports
phplyr’s functions are namespaced functions in Phplyr. Import the ones a file uses with use function:
use Phibble\DataFrame;
use function Phplyr\{phplyr, filter, mutate, group_by, summarize, arrange, desc, n, mean};A method chain needs phplyr() and the helpers it calls, such as n(), mean(), and desc(). The table functions are the chain’s methods, so they don’t need importing.
Some phplyr functions have the same names as PHP built-in functions. In a file that imports one of them, the bare name calls phplyr’s function. Write a leading backslash to call the built-in, as in \count($array).
| phplyr functions | Where they are documented |
|---|---|
count, rename |
Single-table functions |
min, max |
Vector helpers |
abs, sqrt, exp, log, log10, round, floor |
Expressions |
str_replace, str_split, str_pad |
Strings and dates |
extract, chop, pack, unpack |
tidyr |
A file that doesn’t import these names is not affected.
Loading data
phibble builds tables from CSV files, PDO queries, query builders, and PHP arrays:
$schools = DataFrame::fromCsv('schools.csv');
$rows = DataFrame::fromRows([['id' => 1, 'x' => 2.5], ['id' => 2, 'x' => null]]);
$cols = DataFrame::fromColumns(['id' => [1, 2], 'x' => [2.5, null]]);null is a missing value, R’s NA. A DataFrame is immutable, so every function returns a new table and leaves its input unchanged. echo $table prints the first rows in the format of R’s tibble package.
Pipelines
A table function such as filter(...) returns a closure that takes a table and returns a new one. A pipeline applies such closures in order.
The pipe operator |> calls the closure on its right with the value on its left:
$out = $schools |>
filter(fn ($r) => $r->score > 80) |>
arrange(desc('score'));phplyr($table) starts a method chain. Each method calls the phplyr function of the same name with the same arguments. A chained method is camelCase, so group_by() is ->groupBy() and n_groups() is ->nGroups(). get() returns the table:
$out = phplyr($schools)
->filter(fn ($r) => $r->score > 80)
->arrange(desc('score'))
->get();The two forms give the same result, and the documentation shows both.
A method chain stays a chain while each method returns a table. These functions return something else, and the chain returns that value:
| Functions | Return |
|---|---|
pull(), group_vars(), group_size(), group_indices(), group_rows(), group_split(), group_map() |
An array |
n_groups() |
An integer |
setequal() |
A boolean |
show_query() |
The SQL string |
lm(), glm(), lmer(), glmer() |
phit’s result array |
echo prints a chain’s table without get(). ->pipe($closure) applies a closure of your own and continues the chain with its result.
pipe() applies closures in order on every PHP version:
$out = pipe($schools, filter(fn ($r) => $r->score > 80), arrange(desc('score')));Columns, row closures, and vector helpers
Columns are named with strings: 'score' where dplyr takes a bare score.
A row closure is an ordinary PHP closure. It receives the row as a Phibble\Row, whose columns read as $r->score or $r['score'], and the table of the row’s group as a second argument:
mutate(gap: fn ($r) => $r->score - $r->prior_score)
filter(fn ($r, $group) => $r->score > mean('score')($group))A vector helper is one of phplyr’s functions for whole columns, such as lag('x'), cumsum('x'), min_rank('x'), or mean('x'). It is evaluated once per group. A summary helper in mutate() gives one value per group, and that value is repeated on every row of the group:
$schools = DataFrame::fromCsv('schools.csv') |> group_by('school');
echo json_encode([
'groups' => $schools |> group_vars(),
'n_groups' => $schools |> n_groups(),
'sizes' => \array_slice($schools |> group_size(), 0, 4),
]), "\n\n";
// Grouped mutate: helpers are evaluated within each group.
echo $schools |>
mutate(centered: fn ($r, $g) => round($r->score - mean('score')($g), 2), rank_in_school: min_rank(desc('score'))) |>
ungroup() |>
head(4);
// One table per group.
$parts = $schools |> group_split();
echo "\n", \count($parts), ' tables; the first has ', $parts[0]->nrow(), " rows\n";$schools = phplyr(DataFrame::fromCsv('schools.csv'))->groupBy('school')->get();
echo json_encode([
'groups' => phplyr($schools)->groupVars(),
'n_groups' => phplyr($schools)->nGroups(),
'sizes' => \array_slice(phplyr($schools)->groupSize(), 0, 4),
]), "\n\n";
// Grouped mutate: helpers are evaluated within each group.
echo phplyr($schools)
->mutate(centered: fn ($r, $g) => round($r->score - mean('score')($g), 2), rank_in_school: min_rank(desc('score')))
->ungroup()
->head(4);
// One table per group.
$parts = phplyr($schools)->groupSplit();
echo "\n", \count($parts), ' tables; the first has ', $parts[0]->nrow(), " rows\n";{"groups":["school"],"n_groups":12,"sizes":[18,24,15,22]}
# A tibble: 4 × 6
school study_hours prior_score score centered rank_in_school
<chr> <dbl> <dbl> <dbl> <dbl> <int>
1 school-01 11.6 77.3 78.3 5.34 4
2 school-01 2.7 51.7 67.2 -5.67 14
3 school-01 10.0 78.8 88.2 15.3 2
4 school-01 3.71 45.3 68.5 -4.37 13
12 tables; the first has 18 rows
The first argument of a vector helper is the column. It can be a column name, col('x'), another helper, or a row closure. Later arguments are values, so a string there is a literal. Wrap a string in col() to refer to a column.
A third form, col('x')->mul(2), builds an expression. Expressions give the same results as the helpers in memory, and they let a pipeline run as SQL on a database table. See Databases and expressions.
Named arguments and options
Functions that create columns name them with PHP named arguments: mutate(total: ...), summarize(n: n()). An option that dplyr writes with a leading dot, such as .by, is written with a leading underscore, such as _by:. Any other named argument is a column, so mutate(_before: 'id') places the new columns and mutate(before: 1) makes a column called before. Underscored arguments lists them.
PHP does not allow positional arguments after named ones. Put across() and other positional arguments first: summarize(across(...), n: n()).
Printing and inspecting
echo prints a table. glimpse() prints one line per column and returns the table unchanged, so it can sit inside a pipeline. pull('x') returns a column as a PHP array, and $table->toRows() returns every row as an array.
From phpstats/phplyr@d319dc5.