Skip to main content

A utility library for generating Polars expressions to work with nested data structures

Project description

Polars Nexpresso ☕

Python Version Polars License: MIT

Polars Nexpresso - A utility library for generating Polars expressions to work with nested data structures. Easily select, modify, and create columns and nested fields in Polars DataFrames, particularly for complex nested structures like lists of structs and deeply nested hierarchies.

Nexpresso = Nested Expression + ☕ (espresso) - because why not?

Motivation

Working with deeply nested columns in Polars can quickly become verbose and hard to read. Consider modifying a field nested within a list of structs that contains another list of structs:

Without Nexpresso:

import polars as pl

df = pl.DataFrame(
    {
        "orders": [
            [
                {"items": [{"quantity": 1, "price": 10}, {"quantity": 2, "price": 20}]},
                {"items": [{"quantity": 3, "price": 30}, {"quantity": 4, "price": 40}]},
            ],
            [
                {"items": [{"quantity": 5, "price": 50}, {"quantity": 6, "price": 60}]},
                {"items": [{"quantity": 7, "price": 70}, {"quantity": 8, "price": 80}]},
            ],
        ]
    }
)


verbose_expr = (
    pl.col("orders")
    .list.eval(
        pl.element().struct.with_fields(
            pl.element()
            .struct.field("items")
            .list.eval(
                pl.element().struct.with_fields(
                    (pl.element().struct.field("quantity") * 2)
                )
            )
        )
    )
)

print(df.select(verbose_expr))

With Nexpresso:

from nexpresso import generate_nested_exprs

nexpresso_expr = {
    'orders': {
        'items': {
            'quantity': lambda x: x * 2
        }
    }
}
exprs = generate_nested_exprs(fields, df.schema, struct_mode='with_fields')
print(df.select(exprs))

Much cleaner and easier to read! 🎉

Installation

pip install polars-nexpresso

Or using uv:

uv add polars-nexpresso

Quick Start

import polars as pl
from nexpresso import generate_nested_exprs

# Create a DataFrame with nested structures
df = pl.DataFrame({
    "customer": [
        {"name": "Alice", "address": {"city": "NYC", "zip": "10001"}},
        {"name": "Bob", "address": {"city": "LA", "zip": "90001"}},
    ]
})

# Define operations on nested fields
fields = {
    "customer": {
        "name": None,  # Keep as-is 
        "address": {
            "city": None,  # Keep city
            "zip": lambda x: x.cast(pl.Int64),  # Transform zip to integer
            "state": pl.lit("Unknown"),  # Add new field
        },
    }
}

# Generate expressions and apply them
exprs = generate_nested_exprs(fields, df.schema, struct_mode="with_fields")
result = df.select(exprs)

Core Concepts

Field Value Types

When defining operations, you can use several types of values:

  • None: Keep the field as-is (select it without modification)
  • dict: Recursively process nested structures
  • Callable: Apply a function to the field (e.g., lambda x: x * 2)
  • pl.Expr: Use a full Polars expression to create/modify the field

Struct Modes

  • "select" (default): Only keep the fields specified in the dictionary
  • "with_fields": Keep all existing fields and add/modify only the specified ones. If this is used, the fields that are not specified will be kept as-is.

Examples

Lists of Structs

df = pl.DataFrame({
    "order_id": [1, 2, 3],
    "items": [
        [{"product": "Apple", "quantity": 5, "price": 1.0}],
        [{"product": "Banana", "quantity": 10, "price": 0.5}],
    ],
})

fields = {
    "order_id": None,
    "items": {
        "product": None,
        "quantity": lambda x: x * 2,  # Double quantity
        "price": None,
        "subtotal": pl.field("quantity") * pl.field("price"),  # Original qty * price
    },
}

exprs = generate_nested_exprs(fields, df.schema, struct_mode="with_fields")
result = df.select(exprs)

Select Mode vs With Fields Mode

df = pl.DataFrame({
    "product": [
        {"name": "Widget", "price": 10.0, "cost": 5.0, "stock": 100},
    ]
})

# Select mode: only keep specified fields
fields_select = {
    "product": {
        "name": None,
        "price": lambda x: x * 1.2,  # cost and stock are excluded
    }
}

# With fields mode: keep all fields, add/modify some
fields_with = {
    "product": {
        "price": lambda x: x * 1.2,  # Modify price
        "profit": pl.field("price") * 1.2 - pl.field("cost"),  # New field
        # name, cost, stock are kept as-is
    }
}

exprs_select = generate_nested_exprs(fields_select, df.schema, struct_mode="select")
exprs_with = generate_nested_exprs(fields_with, df.schema, struct_mode="with_fields")

Convenience Function

from nexpresso import apply_nested_operations

result = apply_nested_operations(
    df,
    {"data": {"value": lambda x: x * 2, "result": pl.field("value") * pl.field("multiplier")}},
    struct_mode="with_fields",
    use_with_columns=True,  # Use with_columns instead of select
)

API Reference

generate_nested_exprs(fields, schema, struct_mode="select")

Generate Polars expressions for nested data operations.

Parameters:

  • fields (dict[str, FieldValue]): Dictionary defining operations on columns/fields

    • Keys are column/field names
    • Values specify the operation:
      • None: Select field as-is
      • dict: Recursively process nested structure
      • Callable: Apply function to field (e.g., lambda x: x + 1)
      • pl.Expr: Full expression to create/modify field
  • schema (pl.Schema): The schema of the DataFrame to work with

  • struct_mode (Literal["select", "with_fields"]): How to handle struct fields

    • "select": Only keep specified fields (default)
    • "with_fields": Keep all existing fields and add/modify specified ones

Returns:

  • list[pl.Expr]: List of Polars expressions ready for use in .select() or .with_columns()

apply_nested_operations(df, fields, struct_mode="select", use_with_columns=False)

Apply nested operations directly to a DataFrame or LazyFrame.

Parameters:

  • df (pl.DataFrame | pl.LazyFrame): The DataFrame or LazyFrame to operate on
  • fields (dict[str, FieldValue]): Dictionary defining operations (same as generate_nested_exprs)
  • struct_mode (Literal["select", "with_fields"]): How to handle struct fields
  • use_with_columns (bool): If True, use .with_columns() instead of .select()

Returns:

  • pl.DataFrame | pl.LazyFrame: DataFrame or LazyFrame with operations applied

Performance

The library generates native Polars expressions, so performance is equivalent to writing expressions manually. All operations are lazy and benefit from Polars' query optimization.

License

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

Acknowledgments

Built for the Polars data processing library. Special thanks to the Polars team for creating such an excellent tool.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

polars_nexpresso-0.1.0.tar.gz (46.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

polars_nexpresso-0.1.0-py3-none-any.whl (9.0 kB view details)

Uploaded Python 3

File details

Details for the file polars_nexpresso-0.1.0.tar.gz.

File metadata

  • Download URL: polars_nexpresso-0.1.0.tar.gz
  • Upload date:
  • Size: 46.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for polars_nexpresso-0.1.0.tar.gz
Algorithm Hash digest
SHA256 f05009eec2606f84c1acf7350d36b7285f745a547e346460f75312e60957959b
MD5 c80b57aa25446fe315dca5b39ce92c93
BLAKE2b-256 120d0c3691254becfb3175ca4469f8d41dd29200c105e76e9994efdffc077db4

See more details on using hashes here.

Provenance

The following attestation bundles were made for polars_nexpresso-0.1.0.tar.gz:

Publisher: publish.yml on heshamdar/polars-nexpresso

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file polars_nexpresso-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for polars_nexpresso-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ae5c24835b39011760ac2774772257a1b3ef6abb244e2078378a6f80f8280d58
MD5 6098b9dcf089731bb6230d9d2978b3bd
BLAKE2b-256 7b2f22cd0e20ff57bd708a721f91f23f9ff8037e4359389e2c5a8a4bc5fb0633

See more details on using hashes here.

Provenance

The following attestation bundles were made for polars_nexpresso-0.1.0-py3-none-any.whl:

Publisher: publish.yml on heshamdar/polars-nexpresso

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page