Reference
tidyr
The tidyr functions use tidyr’s argument names. Arguments that start with a dot in tidyr keep the dot here as a leading underscore, camelCase after it: .names_sep on pack() is _namesSep:. See Underscored arguments.
Pivoting
pivot_longer() turns columns into rows of names and values. namesPrefix strips a regular expression from the start of each name. namesSep or namesPattern split names into several namesTo columns, and the special name '.value' takes the value column’s name from that part. valuesDropNa: true drops rows whose values are all NA. namesTransform and valuesTransform take a closure, or an array of closures by column. colsVary: 'slowest' keeps each source column’s rows together.
pivot_wider() spreads names and values into columns. idCols identify the rows (by default every other column). valuesFill fills missing cells, valuesFn combines duplicate cells, and namesPrefix, namesSep, namesGlue, and namesSort control the new names. Without valuesFn, duplicate cells turn the value column into list-columns and raise tidyr’s warning. namesVary: 'slowest' groups each name’s value columns together. namesExpand and idExpand add every combination of the namesFrom and idCols values (sorted, factors by level). unusedFn summarizes the columns that are in none of idCols, namesFrom, and valuesFrom.
Both functions build tidyr’s pivot spec in phplyr and pass it to the table’s pivotLongerSpec() or pivotWiderSpec(), so a backend can run the pivot natively. build_longer_spec() and build_wider_spec() return that spec as a table, and pivot_longer_spec() and pivot_wider_spec() run an edited one. The superseded gather() and spread() are also available.
$scores = DataFrame::fromRows([
['student' => 'ann', 'wk1' => 10, 'wk2' => null, 'wk3' => 30],
['student' => 'bob', 'wk1' => 7, 'wk2' => 8, 'wk3' => 9],
]);
$long = $scores |> pivot_longer(starts_with('wk'), namesTo: 'week', namesPrefix: 'wk', valuesTo: 'points', valuesDropNa: true);
echo $long;
echo "\n", $long |> pivot_wider(namesFrom: 'week', valuesFrom: 'points', namesPrefix: 'week_', valuesFill: 0, namesSort: true);$scores = DataFrame::fromRows([
['student' => 'ann', 'wk1' => 10, 'wk2' => null, 'wk3' => 30],
['student' => 'bob', 'wk1' => 7, 'wk2' => 8, 'wk3' => 9],
]);
$long = phplyr($scores)->pivotLonger(starts_with('wk'), namesTo: 'week', namesPrefix: 'wk', valuesTo: 'points', valuesDropNa: true)->get();
echo $long;
echo "\n", phplyr($long)->pivotWider(namesFrom: 'week', valuesFrom: 'points', namesPrefix: 'week_', valuesFill: 0, namesSort: true);# A tibble: 5 × 3
student week points
<chr> <chr> <int>
1 ann 1 10
2 ann 3 30
3 bob 1 7
4 bob 2 8
5 bob 3 9
# A tibble: 2 × 4
student week_1 week_2 week_3
<chr> <int> <int> <int>
1 ann 10 0 30
2 bob 7 8 9
Missing combinations and values
complete() adds missing combinations of the given columns and fills other columns with NA, or with values from fill:. A column name expands to its unique values (a factor to its levels), nesting() keeps only combinations that occur, and a named list supplies the values. It works within groups. expand() returns only the combinations, and crossing() and expand_grid() build a new table from lists.
echo DataFrame::fromRows([
['shop' => 'north', 'month' => 1, 'sales' => 10],
['shop' => 'north', 'month' => 3, 'sales' => 12],
['shop' => 'south', 'month' => 2, 'sales' => 7],
]) |>
complete('shop', month: [1, 2, 3], fill: ['sales' => 0]);echo phplyr(DataFrame::fromRows([
['shop' => 'north', 'month' => 1, 'sales' => 10],
['shop' => 'north', 'month' => 3, 'sales' => 12],
['shop' => 'south', 'month' => 2, 'sales' => 7],
]))
->complete('shop', month: [1, 2, 3], fill: ['sales' => 0]);# A tibble: 6 × 3
shop month sales
<chr> <int> <int>
1 north 1 10
2 north 2 0
3 north 3 12
4 south 1 0
5 south 2 7
6 south 3 0
fill() carries values down or up over NA within groups (_direction: 'down' | 'up' | 'downup' | 'updown'). drop_na() drops rows with NA in the given columns, and replace_na() replaces NA per column.
echo DataFrame::fromRows([
['quarter' => 'Q1', 'year' => 2024, 'sales' => 10],
['quarter' => 'Q2', 'year' => null, 'sales' => null],
['quarter' => 'Q3', 'year' => null, 'sales' => 14],
]) |>
fill('year') |>
replace_na(['sales' => 0]);echo phplyr(DataFrame::fromRows([
['quarter' => 'Q1', 'year' => 2024, 'sales' => 10],
['quarter' => 'Q2', 'year' => null, 'sales' => null],
['quarter' => 'Q3', 'year' => null, 'sales' => 14],
]))
->fill('year')
->replaceNa(['sales' => 0]);# A tibble: 3 × 3
quarter year sales
<chr> <int> <int>
1 Q1 2024 10
2 Q2 2024 0
3 Q3 2024 14
Splitting and combining
| Function | Use |
|---|---|
separate_wider_delim(col, delim, names:) |
Split on a fixed string into columns |
separate_wider_position(col, widths) |
Split by character widths |
separate_wider_regex(col, patterns) |
Split with named regular expressions |
separate_longer_delim(col, delim) |
Split into rows |
separate_longer_position(col, width) |
Split into rows of fixed width |
separate(col, into, sep:) |
The superseded splitter, with extra, fill, and convert |
unite(col, ...cols, sep:) |
Paste columns together |
uncount('n', _id:) |
Repeat rows by a weight column |
separate_rows(...cols, sep:) |
The superseded row splitter, on a regular expression |
extract(col, into, regex) |
The superseded regex splitter |
nest(data: [...]), unnest('data') |
List-columns of tables |
full_seq(x, period) |
Every value from the smallest to the largest in steps of period |
tooFew and tooMany handle pieces that don’t fit, as in tidyr.
$t = DataFrame::fromRows([
['id' => 1, 'name' => 'Ada Lovelace', 'tags' => 'math,code'],
['id' => 2, 'name' => 'Grace Hopper', 'tags' => 'navy'],
]);
echo $t |> separate_wider_delim('name', ' ', names: ['first', 'last']);
echo "\n", $t |> select('id', 'tags') |> separate_longer_delim('tags', ',');
echo "\n", $t |> select('id', 'name') |> unite('label', 'id', 'name', sep: ': ');$t = DataFrame::fromRows([
['id' => 1, 'name' => 'Ada Lovelace', 'tags' => 'math,code'],
['id' => 2, 'name' => 'Grace Hopper', 'tags' => 'navy'],
]);
echo phplyr($t)->separateWiderDelim('name', ' ', names: ['first', 'last']);
echo "\n", phplyr($t)->select('id', 'tags')->separateLongerDelim('tags', ',');
echo "\n", phplyr($t)->select('id', 'name')->unite('label', 'id', 'name', sep: ': ');# A tibble: 2 × 4
id first last tags
<int> <chr> <chr> <chr>
1 1 Ada Lovelace math,code
2 2 Grace Hopper navy
# A tibble: 3 × 2
id tags
<int> <chr>
1 1 math
2 1 code
3 2 navy
# A tibble: 2 × 1
label
<chr>
1 1: Ada Lovelace
2 2: Grace Hopper
List-columns
A list element is a PHP array. A list is R’s unnamed list or vector, and an array with string keys is a named list.
| Function | Use |
|---|---|
unnest_wider(col, namesSep:) |
One column per name; unnamed elements need namesSep |
unnest_longer(col, valuesTo:, indicesTo:, indicesInclude:, keepEmpty:) |
One row per element, with names or positions in {col}_id |
hoist(col, name: path, _remove:) |
Pull components out by name, 1-based position, or a list path |
chop(cols), unchop(cols, keepEmpty:) |
Collapse rows into list-columns and back |
pack(name: cols, _namesSep:), unpack(cols, namesSep:) |
Columns into a column of named arrays and back |
chop, pack, unpack, and extract share names with PHP built-ins, so import them with use function Phplyr\{chop, pack, unpack, extract};.
use function Phplyr\chop;
$people = DataFrame::fromColumns([
'id' => [1, 2, 3],
'info' => [
['name' => 'ann', 'langs' => ['php', 'r'], 'city' => ['name' => 'Oslo']],
['name' => 'bob', 'langs' => ['sql']],
null,
],
]);
echo $people |> hoist('info', town: ['city', 'name']), "\n";
echo $people |> unnest_wider('info') |> select('id', 'langs') |> unnest_longer('langs', indicesInclude: true), "\n";
echo DataFrame::fromColumns(['g' => ['a', 'a', 'b'], 'x' => [1, 2, 3]]) |> chop('x');use function Phplyr\chop;
$people = DataFrame::fromColumns([
'id' => [1, 2, 3],
'info' => [
['name' => 'ann', 'langs' => ['php', 'r'], 'city' => ['name' => 'Oslo']],
['name' => 'bob', 'langs' => ['sql']],
null,
],
]);
echo phplyr($people)->hoist('info', town: ['city', 'name']), "\n";
echo phplyr($people)->unnestWider('info')->select('id', 'langs')->unnestLonger('langs', indicesInclude: true), "\n";
echo phplyr(DataFrame::fromColumns(['g' => ['a', 'a', 'b'], 'x' => [1, 2, 3]]))->chop('x');# A tibble: 3 × 3
id town info
<int> <chr> <list>
1 1 Oslo <named list [3]>
2 2 <NA> <named list [2]>
3 3 <NA> <NULL>
# A tibble: 3 × 3
id langs_id langs
<int> <int> <chr>
1 1 1 php
2 1 2 r
3 2 1 sql
# A tibble: 2 × 2
g x
<chr> <list>
1 a <int [2]>
2 b <int [1]>
From phpstats/phplyr@d319dc5.