Skip to main content

Polars Expression Transformer

PyPI version Python 3.10+ License: MIT

Transform string-based expressions into Polars DataFrame operations. Write simple, SQL-like expressions and let the library convert them to optimized Polars code.

Playground & docs

There is an interactive playground and function reference that runs the library in the browser through Pyodide. You can try expressions on sample data and see the generated Polars and FlowFrame code without installing anything.

The site lives in docs/; the function reference is generated from the docstrings with python generate_docs.py.

Quick Start

import polars as pl
from polars_expr_transformer import simple_function_to_expr

df = pl.DataFrame({
    'first_name': ['John', 'Jane', 'Bob'],
    'last_name': ['Doe', 'Smith', 'Johnson'],
    'age': [30, 25, 45],
    'salary': [50000, 60000, 75000]
})

# Concatenate columns
df.select(simple_function_to_expr('concat([first_name], " ", [last_name])').alias('full_name'))

# Conditional logic
df.select(simple_function_to_expr('if [age] > 30 then "Senior" else "Junior" endif').alias('level'))

# Math operations
df.select(simple_function_to_expr('[salary] * 1.1').alias('new_salary'))

# Combine multiple operations
df.select(simple_function_to_expr('uppercase(left([last_name], 3))').alias('code'))

Installation

pip install polars-expr-transformer

Why Use This Library?

Use Case Recommendation
Building applications with user-defined transformations ✅ Yes - Users can write expressions without Python knowledge
SQL/Tableau users transitioning to Polars ✅ Yes - Familiar syntax
Need a simple expression language for configs ✅ Yes - Easy to serialize and store
Writing performance-critical Polars code ❌ No - Use Polars directly
Need all Polars features ❌ No - This covers common operations only

Expression Syntax

Column References

Reference DataFrame columns using square brackets:

'[column_name]'           # Reference a column
'[Column With Spaces]'    # Columns with spaces work too

Values & Data Types

Besides column references, you can write literal values directly. Five literal types are supported:

Type How to write it Examples
String Single or double quotes "hello", 'world'
Integer Bare whole numbers (negatives allowed) 42, -7
Float Bare decimal numbers 3.14, -0.5
Boolean true or false (case-insensitive) true, False
Null null (case-insensitive) — the missing value null
'if [active] = true then "yes" else "no" endif'   # boolean literal
'[price] * 1.1'                                     # float literal
'coalesce([nickname], null)'                        # null literal

Operators

Operator Description Example
+ Addition [a] + [b]
- Subtraction [a] - 10
* Multiplication [price] * [quantity]
/ Division [total] / [count]
% Modulo [value] % 2
= or == Equals [status] = "active"
!= Not equals [type] != "deleted"
>, >=, <, <= Comparisons [age] >= 18
and Logical AND [a] > 0 and [b] > 0
or Logical OR [x] = 1 or [y] = 1
in ( ... ) Membership [name] in ("alice", "carol")
not in ( ... ) Non-membership [status] not in ("failed", "cancelled")
in Substring, against a plain value "li" in [name]

Membership needs the parentheses: [x] in ("a", "b") tests whether [x] is one of the listed values, while "a" in [x] tests whether [x] contains the text "a". Members must all be of one type, and a member may reference a column. As in Polars, a null input yields null rather than false, and an empty list () matches nothing.

Conditional Expressions

# Simple if-then-else
'if [age] >= 18 then "Adult" else "Minor" endif'

# Multiple conditions with elseif
'if [score] >= 90 then "A" elseif [score] >= 80 then "B" elseif [score] >= 70 then "C" else "F" endif'

# Nested conditions
'if [type] = "A" then (if [value] > 100 then "High A" else "Low A" endif) else "Other" endif'

Comments

# Single-line comments with //
'[column] + 1 // This adds one to the column'

# Multi-line expressions with comments
'''
[price] * [quantity]  // Calculate subtotal
- [discount]          // Apply discount
'''

Available Functions

String Functions

Function Description Example
concat(a, b, ...) Concatenate strings concat([first], " ", [last])
length(text) String length length([name])
uppercase(text) Convert to uppercase uppercase([code])
lowercase(text) Convert to lowercase lowercase([email])
titlecase(text) Convert to title case titlecase([name])
left(text, n) First n characters left([phone], 3)
right(text, n) Last n characters right([id], 4)
mid(text, start, len) Substring from position mid([code], 2, 3)
substring(text, start, len) Alias for mid substring([text], 0, 10)
trim(text) Remove leading/trailing spaces trim([input])
left_trim(text) Remove leading spaces left_trim([text])
right_trim(text) Remove trailing spaces right_trim([text])
replace(text, find, replace) Replace text replace([name], ".", "")
find_position(text, search) Find substring position find_position([text], "@")
pad_left(text, len, char) Pad string on left pad_left([id], 5, "0")
pad_right(text, len, char) Pad string on right pad_right([code], 10, " ")
starts_with(text, prefix) Check prefix starts_with([url], "https")
ends_with(text, suffix) Check suffix ends_with([file], ".csv")
reverse(text) Reverse string reverse([text])
repeat(text, n) Repeat string n times repeat("*", 5)
split(text, delimiter) Split into list split([tags], ",")
count_match(text, pattern) Count occurrences count_match([text], "a")
string_similarity(a, b, method) Similarity score (0-1) string_similarity([a], [b], "levenshtein")

Math Functions

Function Description Example
abs(n) Absolute value abs([difference])
round(n, decimals) Round to decimals round([price], 2)
ceil(n) Round up ceil([value])
floor(n) Round down floor([value])
power(base, exp) Exponentiation power([x], 2)
pow(base, exp) Alias for power pow(2, [n])
sqrt(n) Square root sqrt([area])
log(n) Natural logarithm log([value])
log10(n) Base-10 logarithm log10([value])
log2(n) Base-2 logarithm log2([value])
exp(n) e^n exp([rate])
mod(a, b) Modulo mod([value], 10)
sign(n) Sign (-1, 0, 1) sign([change])
negation(n) Negate value negation([amount])
sin(n), cos(n), tan(n) Trigonometric sin([angle])
asin(n), acos(n), atan(n) Inverse trig asin([ratio])
tanh(n) Hyperbolic tangent tanh([x])
random_int(min, max) Random integer random_int(1, 100)

Date Functions

Function Description Example
now() Current datetime now()
today() Current date today()
year(date) Extract year year([created_at])
month(date) Extract month (1-12) month([date])
day(date) Extract day (1-31) day([date])
hour(datetime) Extract hour (0-23) hour([timestamp])
minute(datetime) Extract minute minute([time])
second(datetime) Extract second second([time])
week(date) ISO week number (1-53) week([date])
weekday(date) Day of week (1=Mon, 7=Sun) weekday([date])
dayofweek(date) Alias for weekday dayofweek([date])
quarter(date) Quarter (1-4) quarter([date])
dayofyear(date) Day of year (1-366) dayofyear([date])
add_days(date, n) Add days add_days([start], 30)
add_weeks(date, n) Add weeks add_weeks([date], 2)
add_months(date, n) Add months add_months([date], 6)
add_years(date, n) Add years add_years([birth], 18)
add_hours(dt, n) Add hours add_hours([time], 3)
add_minutes(dt, n) Add minutes add_minutes([time], 30)
add_seconds(dt, n) Add seconds add_seconds([time], 60)
date_diff_days(a, b) Days between dates date_diff_days([end], [start])
datetime_diff_seconds(a, b) Seconds between datetime_diff_seconds([a], [b])
format_date(date, fmt) Format as string format_date([date], "%Y-%m-%d")
start_of_month(date) First of month start_of_month([date])
end_of_month(date) Last of month end_of_month([date])
date_truncate(date, unit) Truncate to unit date_truncate([dt], "1day")

Logic & Null Handling

Function Description Example
equals(a, b) Check equality equals([status], "active")
does_not_equal(a, b) Check inequality does_not_equal([type], "deleted")
is_empty(value) Check if null is_empty([email])
is_not_empty(value) Check if not null is_not_empty([phone])
coalesce(a, b, ...) First non-null coalesce([nickname], [name], "Unknown")
ifnull(value, default) Replace null ifnull([count], 0)
nvl(value, default) Alias for ifnull nvl([value], 0)
nullif(a, b) Null if equal nullif([value], 0)
between(val, min, max) Range check (inclusive) between([age], 18, 65)
greatest(a, b, ...) Maximum value greatest([a], [b], [c])
least(a, b, ...) Minimum value least([price1], [price2])
contains(text, search) Contains substring contains([desc], "sale")
_in(value, text) Value in text _in("admin", [roles])
_not(value) Logical NOT _not([is_deleted])
is_string(value) Type check is_string([field])

Type Conversions

Function Description Example
to_string(value) Convert to string to_string([id])
to_integer(value) Convert to integer to_integer([count])
to_float(value) Convert to float to_float([price])
to_number(value) Alias for to_float to_number([value])
to_boolean(value) Convert to boolean to_boolean([flag])
to_date(text, format) Parse date to_date([date_str], "%Y-%m-%d")
to_datetime(text, format) Parse datetime to_datetime([ts], "%Y-%m-%d %H:%M:%S")
to_decimal(value, precision) Convert with precision to_decimal([amount], 2)

Hashing

One-way fingerprints: the original value cannot be recovered from the result.

Function Description Example
hash(value) Fast numeric fingerprint hash([email])
md5(text) MD5 hash (32 hex characters) md5([email])
sha1(text) SHA-1 hash (40 hex characters) sha1([email])
sha256(text) SHA-256 hash (64 hex characters) sha256([email])
sha512(text) SHA-512 hash (128 hex characters) sha512([email])

hash() is a fast, non-cryptographic hash whose numbers may change between Polars versions; use sha256() when the fingerprint has to stay the same over time. The cryptographic digests run through Python's hashlib, so generated code for them needs import hashlib alongside import polars as pl.

Encoding

Reversible text encodings: decode turns whatever encode produced back into the original text.

Function Description Example
encode(text, encoding) Encode as base64 or hex encode([name], "hex")
decode(text, encoding) Decode from base64 or hex decode([token], "base64")
base64_encode(text) Encode as base64 base64_encode([name])
base64_decode(text) Decode from base64 base64_decode([token])
hex_encode(text) Encode as hexadecimal hex_encode([name])
hex_decode(text) Decode from hexadecimal hex_decode([token])

API Reference

simple_function_to_expr(expression: str) -> pl.Expr

Converts a string expression to a Polars expression.

from polars_expr_transformer import simple_function_to_expr

expr = simple_function_to_expr('[price] * [quantity]')
df.select(expr.alias('total'))

build_func(expression: str) -> Func

Returns the intermediate function object for inspection/debugging.

from polars_expr_transformer import build_func

func = build_func('concat([a], [b])')
print(func.get_readable_pl_function())  # See the Polars translation

get_all_expressions() -> List[str]

Returns a list of all available function names.

from polars_expr_transformer import get_all_expressions

functions = get_all_expressions()
print(functions)  # ['concat', 'length', 'uppercase', ...]

get_expression_overview() -> List[ExpressionsOverview]

Returns functions grouped by category with descriptions.

from polars_expr_transformer import get_expression_overview

for category in get_expression_overview():
    print(f"\n{category.category}:")
    for expr in category.expressions:
        print(f"  {expr.name}: {expr.description}")

Error Handling

The library validates expressions before parsing and raises ExpressionSyntaxError (a subclass of ValueError) with the exact position of the problem and a hint:

# Misspelled keyword
simple_function_to_expr('f [age] > 30 then "Senior" else "Junior" endif')
# ExpressionSyntaxError:
# Found 'then' at position 14, but there is no 'if' before it.
# f [age] > 30 then "Senior" else "Junior" endif
#              ^
# Hint: Every condition starts with 'if': if <condition> then <value> else <value> endif.
# Check that 'if' is present and spelled correctly.

# Unbalanced parentheses
simple_function_to_expr('((1)')
# ExpressionSyntaxError:
# Unbalanced parentheses: '(' at position 1 is never closed.
# ((1)
# ^
# Hint: Add a matching ')'.

# Unknown function
simple_function_to_expr('unknown_func([col])')
# ExpressionSyntaxError: Expected a single value, but found 2. This usually means
# a function name is misspelled or unknown, or an operator is missing between two values.

Catch errors with except ExpressionSyntaxError (importable from the package root) or simply except ValueError.

Built on Polars

This library is built on top of Polars, a blazingly fast DataFrame library written in Rust. All expressions are converted to native Polars operations, ensuring optimal performance.

Contributing

Contributions are welcome! Please feel free to submit issues and pull requests on GitHub.

License

MIT License - see LICENSE file for details.

Acknowledgements

Thanks to the Polars team for creating such an amazing library.

Metadata

Release files for polars-expr-transformer 0.6.3

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-expr-transformer 0.6.3
File Size Uploaded
polars_expr_transformer-0.6.3.tar.gz 61.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for polars-expr-transformer 0.6.3
File Interpreter ABI Platform
polars_expr_transformer-0.6.3-py3-none-any.whl Python 3 none any Details

Total release size: 131.0 kB

Release files / polars_expr_transformer-0.6.3.tar.gz

Download URL polars_expr_transformer-0.6.3.tar.gz
Size 61.6 kB
Tags Source
SHA-256 checksum
How to use checksums
8e572c6a1d4de3567e49a8503d5aab0222c9def19bebfaba11d22dc3919cbd56
BLAKE2b-256 checksum
How to use checksums
dee0fdcb78d7c520bdb43781dd5a431765e3203561c858c762eefb75471ec895
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 17, 2026.

Transparency log

Release files / polars_expr_transformer-0.6.3-py3-none-any.whl

Download URL polars_expr_transformer-0.6.3-py3-none-any.whl
Size 69.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
1bb0c91d7e820efedefa489c6e040c99e199d3aa946e9abb542f1b08299017a9
BLAKE2b-256 checksum
How to use checksums
dbc2432f93c276cb1c3f70a4c069eb3543d3aa4089e4c4cea02c7be7e7ff9e3d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 17, 2026.

Transparency log

Release history Release notifications | RSS feed

0.6.4

2 release files

This release

0.6.3 This release

2 release files

0.6.2

2 release files

0.6.1

2 release files

0.6.0

2 release files

0.5.7

2 release files

0.5.6

2 release files

0.5.5

2 release files

0.5.4

2 release files

0.5.3

2 release files

0.5.2

2 release files

0.5.1

2 release files

0.5.0

2 release files

0.3.3.0

1 release file

0.1.3

2 release files

0.1.2

2 release files

0.1.0

2 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