Skip to main content

PolarsGrouper

PolarsGrouper is a Rust-based extension for Polars that provides efficient graph analysis capabilities, with a focus on component grouping and network analysis.

Core Features

Component Grouping

  • super_merger: Easy-to-use wrapper for grouping connected components
  • super_merger_weighted: Component grouping with weight thresholds
  • Efficient implementation using Rust and Polars
  • Works with both eager and lazy Polars DataFrames

Hierarchy / BOM Explosion

  • hierarchy_totals, hierarchy_levels, hierarchy_paths: explode a parent → child edge list (bill of materials, chart of accounts, WBS) into its transitive closure, with quantities rolled up along every path
  • Replaces WITH RECURSIVE / CONNECT BY queries and for-loops, and runs inside lazy queries

Additional Graph Analytics

  • Shortest Path Analysis: Find shortest paths between nodes
  • PageRank: Calculate node importance scores
  • Betweenness Centrality: Identify key bridge nodes
  • Association Rules: Discover item relationships and patterns

Installation

pip install polars-grouper

# For development:
python -m venv .venv
source .venv/bin/activate
maturin develop

Usage Examples

Basic Component Grouping

The core functionality uses super_merger to identify connected components:

import polars as pl
from polars_grouper import super_merger

df = pl.DataFrame({
    "from": ["A", "B", "C", "D", "E", "F"],
    "to": ["B", "C", "A", "E", "F", "D"],
    "value": [1, 2, 3, 4, 5, 6]
})

result = super_merger(df, "from", "to")
print(result)

Weighted Component Grouping

For cases where edge weights matter:

from polars_grouper import super_merger_weighted

df = pl.DataFrame({
    "from": ["A", "B", "C", "D", "E"],
    "to": ["B", "C", "D", "E", "A"],
    "weight": [0.9, 0.2, 0.05, 0.8, 0.3]
})

result = super_merger_weighted(
    df, 
    "from", 
    "to", 
    "weight",
    weight_threshold=0.3
)
print(result)

Hierarchy / BOM Explosion

Resolve a bill of materials in one lazy expression. Quantities multiply along a path (4 wheels × 5 screws) and add up across paths (+ 20 screws directly on the car):

import polars as pl
from polars_grouper import hierarchy_totals, hierarchy_levels, hierarchy_paths

bom = pl.LazyFrame({
    "parent": ["car", "car", "car", "wheel", "wheel", "wheel", "rim"],
    "child": ["wheel", "steering_wheel", "screw", "tyre", "rim", "screw", "iron"],
    "qty": [4.0, 1.0, 20.0, 1.0, 1.0, 5.0, 2.5],
})

totals = bom.select(hierarchy_totals("parent", "child", "qty").alias("bom")).unnest("bom")

# Raw materials needed for one car
totals.filter(pl.col("ancestor") == "car", pl.col("is_leaf")).collect()
# ┌──────────┬────────────────┬───────┬──────────┬─────────┐
# │ ancestor ┆ descendant     ┆ level ┆ quantity ┆ is_leaf │
# ╞══════════╪════════════════╪═══════╪══════════╪═════════╡
# │ car      ┆ steering_wheel ┆ 1     ┆ 1.0      ┆ true    │
# │ car      ┆ screw          ┆ 1     ┆ 40.0     ┆ true    │
# │ car      ┆ tyre           ┆ 2     ┆ 4.0      ┆ true    │
# │ car      ┆ iron           ┆ 3     ┆ 10.0     ┆ true    │
# └──────────┴────────────────┴───────┴──────────┴─────────┘

Three functions, one per level of detail. They share column names, so each is an aggregation of the next:

function one row per extra columns
hierarchy_totals ancestor, descendant level is the shallowest level
hierarchy_levels ancestor, descendant, level
hierarchy_paths path (an indented BOM) parent, quantity_per, path

All three take top_level_only (explode finished products only), include_self (add a level-0 row per node) and max_depth. A cycle in the data raises an error that names it.

The same works for any hierarchy. To roll up general-ledger balances along a chart of accounts, link every account to itself and to everything below it, then aggregate:

closure = accounts.select(
    hierarchy_totals("parent_account", "account", include_self=True).alias("closure")
).unnest("closure")

balances = (
    closure.join(transactions, left_on="descendant", right_on="account")
    .group_by("ancestor")
    .agg(pl.col("amount").sum())
)

Additional Graph Analytics

Shortest Path Analysis

Find shortest paths between nodes:

from polars_grouper import calculate_shortest_path

df = pl.DataFrame({
    "from": ["A", "A", "B", "C"],
    "to": ["B", "C", "C", "D"],
    "weight": [1.0, 2.0, 1.0, 1.5]
})

paths = df.select(
    calculate_shortest_path(
        pl.col("from"),
        pl.col("to"),
        pl.col("weight"),
        directed=False
    ).alias("paths")
).unnest("paths")

PageRank Calculation

Calculate node importance:

from polars_grouper import page_rank

df = pl.DataFrame({
    "from": ["A", "A", "B", "C", "D"],
    "to": ["B", "C", "C", "A", "B"]
})

rankings = df.select(
    page_rank(
        pl.col("from"),
        pl.col("to"),
        damping_factor=0.85
    ).alias("pagerank")
).unnest("pagerank")

Association Rule Mining

Discover item relationships:

from polars_grouper import graph_association_rules

transactions = pl.DataFrame({
    "transaction_id": [1, 1, 1, 2, 2, 3],
    "item_id": ["A", "B", "C", "B", "D", "A"],
    "frequency": [1, 2, 1, 1, 1, 1]
})

rules = transactions.select(
    graph_association_rules(
        pl.col("transaction_id"),
        pl.col("item_id"),
        pl.col("frequency"),
        min_support=0.1
    ).alias("rules")
).unnest("rules")

Betweenness Centrality

Identify bridge nodes:

from polars_grouper import betweenness_centrality

df = pl.DataFrame({
    "from": ["A", "A", "B", "C", "D", "E"],
    "to": ["B", "C", "C", "D", "E", "A"]
})

centrality = df.select(
    betweenness_centrality(
        pl.col("from"),
        pl.col("to"),
        normalized=True
    ).alias("centrality")
).unnest("centrality")

Performance

The library is implemented in Rust for high performance:

  • Efficient memory usage
  • Fast computation for large graphs
  • Seamless integration with Polars' lazy evaluation

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Release files for polars-grouper 0.6.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for polars-grouper 0.6.0
File Size Uploaded
polars_grouper-0.6.0.tar.gz 54.9 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for polars-grouper 0.6.0
File
polars_grouper-0.6.0-cp310-abi3-win_amd64.whl CPython 3.10 abi3 Windows x86-64 Details
polars_grouper-0.6.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl CPython 3.10 abi3 Linux glibc 2.17+ x86-64 Details
polars_grouper-0.6.0-cp310-abi3-manylinux_2_17_i686.manylinux2014_i686.whl CPython 3.10 abi3 Linux glibc 2.17+ x86-32 Details
polars_grouper-0.6.0-cp310-abi3-macosx_11_0_arm64.whl CPython 3.10 abi3 macOS 11.0+ ARM64 Details
polars_grouper-0.6.0-cp310-abi3-macosx_10_12_x86_64.whl CPython 3.10 abi3 macOS 10.12+ x86-64 Details

Total release size: 25.1 MB

Release files / polars_grouper-0.6.0.tar.gz

Download URL polars_grouper-0.6.0.tar.gz
Size 54.9 kB
Tags Source
SHA-256 checksum
How to use checksums
a855d32380c67d55f91300dc60abcf9cd5699248558b781c967f923485480c47
BLAKE2b-256 checksum
How to use checksums
dded9e4a83822c94eda1debf2265d9a5720db452521bc56ea455d300a3d80c34
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via maturin/1.15.0

Release files / polars_grouper-0.6.0-cp310-abi3-win_amd64.whl

Download URL polars_grouper-0.6.0-cp310-abi3-win_amd64.whl
Size 4.7 MB
Tags CPython 3.10 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
5435d8a8d6b276b2830b65227056c2ec068a47e90e6d41dd74b69d9e44e80deb
BLAKE2b-256 checksum
How to use checksums
4243c6d1c4c08ab803d3747f86969f58f5a5772d5bf2a988e99ebe39748bd2e2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via maturin/1.15.0

Release files / polars_grouper-0.6.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL polars_grouper-0.6.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 5.3 MB
Tags CPython 3.10 Linux glibc 2.17+ x86-64 abi3
SHA-256 checksum
How to use checksums
a27419509237899275326c12019ccdbb67baca12b4fe93bff562c1fedf6135c3
BLAKE2b-256 checksum
How to use checksums
3224f35944f860d734bb592ae3a3eda45b522bb5ccdd6625961b44b551451bb2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via maturin/1.15.0

Release files / polars_grouper-0.6.0-cp310-abi3-manylinux_2_17_i686.manylinux2014_i686.whl

Download URL polars_grouper-0.6.0-cp310-abi3-manylinux_2_17_i686.manylinux2014_i686.whl
Size 6.1 MB
Tags CPython 3.10 Linux glibc 2.17+ x86-32 abi3
SHA-256 checksum
How to use checksums
47b8d01ebba4b77a2453e7a147dfe374ad6094bf11e759f4a56bf853df00deb9
BLAKE2b-256 checksum
How to use checksums
81b7ef12b24a6b7819fd7052bf1a604450c6289fb211a200cefb3331c45e438c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via maturin/1.15.0

Release files / polars_grouper-0.6.0-cp310-abi3-macosx_11_0_arm64.whl

Download URL polars_grouper-0.6.0-cp310-abi3-macosx_11_0_arm64.whl
Size 4.4 MB
Tags CPython 3.10 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
6860b9ddae4e594961dd4ddd4100511a47473fb22352c87d601ae342931f6bfd
BLAKE2b-256 checksum
How to use checksums
8e73429d713be960c4221ce752d73002df9b3d176c86018eb07c7db3d1c3e364
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via maturin/1.15.0

Release files / polars_grouper-0.6.0-cp310-abi3-macosx_10_12_x86_64.whl

Download URL polars_grouper-0.6.0-cp310-abi3-macosx_10_12_x86_64.whl
Size 4.6 MB
Tags CPython 3.10 abi3 macOS 10.12+ x86-64
SHA-256 checksum
How to use checksums
f7db74f46df1a2c1e534abdecd0319f1226e1afdf486967a3fa2ecb240311dbd
BLAKE2b-256 checksum
How to use checksums
10e61c272c141ced3034a62b116319b7ac2d800949f7d17c58c8e42f08b9e2eb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via maturin/1.15.0

Release history Release notifications | RSS feed

This release

0.6.0 This release

6 release files

0.5.0

6 release files

0.4.0

8 release files

0.3.0

6 release files

0.2.0

6 release files

0.1.0

6 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page