Skip to main content

Expression Syntax Rules

Polars expressions follow Python syntax with some important rules you must know to avoid errors.

Wrap Comparisons in Parentheses​

This is the most common mistake. Due to Python operator precedence, & and | bind tighter than comparison operators (>, <, ==, etc.).

# WRONG - will error
pl.col("price") > 100 & pl.col("qty") > 0

# CORRECT - wrap each comparison in parentheses
(pl.col("price") > 100) & (pl.col("qty") > 0)

The incorrect version is parsed by Python as:

pl.col("price") > (100 & pl.col("qty")) > 0  # Not what you want!

Rule of Thumb​

Always parenthesize each comparison when using &, |, or ~:

# Two conditions with AND
(pl.col("a") > 10) & (pl.col("b") < 20)

# Three conditions with OR
(pl.col("status") == "active") | (pl.col("status") == "pending") | (pl.col("status") == "new")

# NOT with a comparison
~(pl.col("is_deleted") == True)

Multi-Line Expressions​

You can break a long expression across lines. Python allows line breaks inside parentheses, so wrap the whole expression in outer parentheses:

(
(pl.col("category") == "electronics")
& (pl.col("price") > 500)
& (pl.col("in_stock") == True)
)

With Comments​

Comments start with # and run to the end of the line:

(
(pl.col("amount") > 1000) # High value transaction
& (pl.col("status") == "pending") # Not yet processed
& (pl.col("risk_score") >= 7) # Flagged as risky
)

Method Chaining for Readability​

When an expression has several method calls, put one call on each line. Wrap the entire chain in parentheses:

(pl.col("customer_email")
.str.to_lowercase()
.str.strip_chars()
.str.replace_all("@gmail.com", "@google.com")
)

This is especially useful for complex string operations:

(pl.col("product_name")
.str.to_lowercase() # Normalize case
.str.strip_chars() # Remove leading/trailing whitespace
.str.replace_all(" ", " ") # Collapse multiple spaces
.str.replace("&", "and") # Expand ampersands
.str.slice(0, 50) # Truncate to 50 characters
)

Conditionals​

Use pl.when(), .then() and .otherwise(). You can repeat .when() and .then() for several cases:

(
pl.when(pl.col("customer_tier") == "gold")
.then(pl.col("purchase_amount") * 0.8) # 20% discount
.when(pl.col("customer_tier") == "silver")
.then(pl.col("purchase_amount") * 0.9) # 10% discount
.otherwise(pl.col("purchase_amount")) # No discount
)

Chaining Date Operations​

Date/time methods chain naturally:

(pl.col("order_date")
.dt.truncate("1d") # Truncate to day
.dt.offset_by("1w") # Add one week
.dt.strftime("%Y-%m-%d") # Format as string
)
Key Rule

Wrap a multi-line expression in outer parentheses.

Quote Characters​

Use single or double quotes for strings - they're interchangeable:

pl.col("name")      # Double quotes
pl.col('name') # Single quotes - same result

For strings containing quotes, use the other quote type:

pl.col("status") == 'it\'s done'     # Escaped single quote
pl.col("status") == "it's done" # Or use double quotes outside

What Loom Accepts​

An expression must be a single Polars expression that starts from pl. Loom checks it before running it and rejects imports, lambdas, comprehensions and names other than pl, True, False and None. The editor reports the reason.

Expressions also run in a restricted sandbox. Anything that reads or writes files or reaches the network is blocked, and so are pl.selectors, pl.Series and pl.sql_expr. Use the tool's own settings instead, for example a Read tool for files or Select for column choice.

Common Errors and Fixes​

TypeError: unsupported operand type(s)​

Usually means missing parentheses around comparisons:

# Error
pl.col("a") > 10 & pl.col("b") < 5

# Fix
(pl.col("a") > 10) & (pl.col("b") < 5)

SyntaxError: invalid syntax​

Often caused by unclosed parentheses or invalid Python:

# Error - unclosed parenthesis
(pl.col("a") > 10

# Fix
(pl.col("a") > 10)

ColumnNotFoundError​

Column name doesn't exist - check spelling and case:

# Error - typo in column name
pl.col("Stauts") == "active"

# Fix - correct spelling
pl.col("Status") == "active"

Summary​

RuleExample
Wrap comparisons with &/|(a > 10) & (b < 20)
Use ~ for NOT (booleans only)~pl.col("is_deleted")
Multi-line needs outer parens(\n (a > 1)\n & (b < 2)\n)
Chain methods with line breaks(col\n .method1()\n .method2()\n)
Use pl.lit() for constantspl.lit("active")
Use == for equalitypl.col("x") == 5