phpolars

Polars pipeline

phplyr works on any phibble table. This example runs the pipeline from the SQL example on an in-memory DataFrame and on a PolarsTable, which holds the data in Polars through the php_polars extension from phpolars. The steps use col() expressions, so the Polars side runs them in Polars instead of in PHP.

The input stacks copies of the 3,000 simulated rentals, up to 300,000 rows. Polars runs the steps quickly, and the time to copy data in and out of Polars matters more at small sizes.

Code

$pipeline = fn (Table $rentals) => $rentals |>
    filter(col('bedrooms')->ge($minBedrooms)) |>
    mutate(rent_per_m2: col('monthly_rent')->div(col('area_m2'))) |>
    group_by('neighborhood') |>
    summarise(rentals: n(), mean_rent_per_m2: mean('rent_per_m2')) |>
    arrange(desc('mean_rent_per_m2'));

$inMemory = $df |> $pipeline;
$inPolars = PolarsTable::fromTable($df) |> $pipeline;
$pipeline = fn (Table $rentals) => phplyr($rentals)
    ->filter(col('bedrooms')->ge($minBedrooms))
    ->mutate(rent_per_m2: col('monthly_rent')->div(col('area_m2')))
    ->groupBy('neighborhood')
    ->summarise(rentals: n(), mean_rent_per_m2: mean('rent_per_m2'))
    ->arrange(desc('mean_rent_per_m2'))
    ->get();

$inMemory = $pipeline($df);
$inPolars = $pipeline(PolarsTable::fromTable($df));
Neighborhood Rentals DataFrame: rent per m² Polars: rent per m²
Harbor 3,900 31.8965 31.8965
Old Town 3,810 30.0595 30.0595
Hillcrest 3,590 28.3780 28.3780
University 3,740 27.8808 27.8808
Riverside 3,280 26.3653 26.3653
Northgate 3,840 24.9868 24.9868

This request

  • Input: 30,000 rows.
  • DataFrame pipeline: 19.5 ms.
  • Copying the DataFrame into Polars: 4.4 ms.
  • PolarsTable pipeline, including collecting the result: 2.0 ms.
  • The backends returned the same rows in the same order. The largest difference in mean rent per m² is 8.5e-14.
  • php_polars 0.2.0.

Timings come from one run on this server and vary between requests. The benchmarks page has repeated measurements.