Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Data Analysis with Python (pandas, polars, DuckDB)  Module 12 – polars: expressions, lazy queries and speed

pandas to polars and back: migration and interop

Port pandas code to polars with an idiom map, convert frames both ways through Arrow, run SQL on polars frames and prove a port with a frame-equality test.

  • Intermediate
  • 40 minutes
  • Examples run with Python 3.14.8, pandas 3.0.6, polars 1.44.2, pyarrow 25.0.1 and duckdb 1.5.6
  • By MySmartCoPilot
Free core, Pro depth: 14 of 40 minutes free
Free: Port choices expose index and missing-value rules · Conversion preserves values while types and index can change · SQL and expressions query the same in-scope frames · Frame equality detects the missing city revenue. With a pass: 3 in-depth parts and 3 interview answers, about 26 minutes.

What you will learn

  • Translate common pandas idioms to polars and spot those with no direct equivalent
  • Convert between pandas, polars and Arrow, and state what each conversion changes
  • Run SQL on polars frames with pl.sql and SQLContext
  • Decide which library a given team and task should use

Before you start

On a fifteen-line shop export, moving code from pandas to polars and back means translating idioms, converting frames, and proving that the result did not change. Most pandas operations have a direct polars method: a boolean mask becomes filter, assign becomes with_columns, groupby().agg becomes group_by().agg, transform becomes an expression with over(), merge becomes join and melt becomes unpivot. Code that relies on the index, such as set_index, loc or alignment by label, has no direct equivalent, because polars has no index: it becomes key columns, filters and joins. Frames cross through an Arrow interchange boundary: A transfer of typed column data between libraries using Arrow representations; values, nulls, types and key semantics still need a contract., with pl.from_pandas one way and to_pandas back: to_pandas needs the pyarrow package, and so does from_pandas for any column that is not a plain NumPy one, such as text. pl.sql runs SQL over polars frames, and frame equivalence: Agreement on the intended rows, values, column order, row order and required types, tested under an explicit comparison policy. is checked with assert_frame_equal under an explicit value, order and type policy.

Port choices expose index and missing-value rules

The examples use 15 order lines of a clothing shop in sales.csv, with the usual flaws of an export: city names in mixed case with stray spaces, a missing city, a missing status, a repeated order and missing discounts.

pandas Details
df[df["qty"] > 1] polars: df.filter(pl.col("qty") > 1); What to watch: A null condition drops the row
df[["city", "qty"]] polars: df.select("city", "qty"); What to watch:
df.assign(amount=...) polars: df.with_columns(amount=...); What to watch:
df["amount"] = ... polars: df = df.with_columns(...); What to watch: Nothing changes in place
np.where(cond, a, b) polars: pl.when(cond).then(a).otherwise(b); What to watch: Strings in then are column names
df.groupby("city").agg(...) polars: df.group_by("city").agg(...); What to watch: Groups unsorted; a null key kept
groupby(...).transform("sum") polars: pl.col("x").sum().over("city"); What to watch:
df.merge(other, on="k") polars: df.join(other, on="k"); What to watch: Null keys never match
melt, pivot_table polars: unpivot, pivot; What to watch:
fillna, isna polars: fill_null, is_null; What to watch: NaN is a separate value
drop_duplicates() polars: unique(keep="first", maintain_order=True); What to watch: The default keeps any row
df.apply(f, axis=1) polars: An expression; map_elements only as a last resort; What to watch: Python per row is slow
set_index, loc, reset_index polars: A key column with filter and join; What to watch: There is no index

Most of a port from pandas to polars is mechanical: one line of pandas becomes one step of a polars chain, and the chain reads top to bottom. Three behaviours change results rather than syntax, and the right-hand column of the table names them. A missing value in a condition counts as “not true”, so filter drops the row where the original object-valued pandas examples keep it. Nullable pandas Boolean masks can drop unknown conditions too; state the dtype and policy. A missing group key forms a group of its own, where pandas leaves it out. And polars keeps no group or join order unless it is asked to, so a result needs an explicit sort before it is compared or published.

Follow the input and compare the result with the recorded example.

Interactive · stage by stage Port choices expose index and missing-value rules Follow the original input through the stated transformation and check its output.
Port choices expose index and missing-value rules, step 3 of 3. Showing Choose operation, State missingness, Make key explicit. In focus: Make key explicit.Choose operationfilter; with_columns; joinState missingnessnull city retainedMake key explicitno pandas index

The drawing shows the whole scene; its 3 steps are listed below.

Every step as text
  1. Translate the operation's meaning, not just its spelling. A mask becomes a filter and key matching becomes an explicit join.
  2. Polars and pandas defaults can differ on missing conditions and group keys. Decide those policies in the port.
  3. A pandas row index with business meaning becomes a Polars column; lookups and alignment use filters and joins.

Checkpoint

Check: idiom map

  1. Question 1 of 2 How should pandas label alignment be represented in Polars?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: An explicit key-based join

    Polars has no pandas index and requires an explicit matching rule. Assuming row positions align can combine unrelated records.

  2. Question 2 of 2 A port starts with fifteen rows and removes one repeated order and one cancelled order. How many remain?

    Type a number.

    Show the answer: Question 2 of 2

    Answer: 13 orders

    15−1−1=13, as the original cleaning port reports.

Conversion preserves values while types and index can change

pandas and polars frames convert through Apache Arrow; each direction changes the index, missing values or types.pandas DataFramean index · NaN or pd.NApl.from_pandas(df), via ArrowNaN → null · index droppednumbers without nulls: not copiedpolars DataFrameno index · null in every typedf.to_pandas(), via ArrowInt64 with a null → float64use_pyarrow_extension_array=Truekeeps the typespandas DataFrame againa new 0, 1, 2 … index

What changes at the boundary between pandas and polars

Text description of the diagram

A pandas DataFrame has an index and marks missing values with NaN or pd.NA; its integers are int64, or the nullable Int64 when values are missing. An Apache Arrow table has typed columns and one null marker for every type. A polars DataFrame has no index and uses null everywhere.

  1. pl.from_pandas goes from pandas to polars through Arrow. NaN in a float column becomes null, and the index is dropped unless include_index=True. Numeric columns without missing values are taken over without being copied.
  2. to_pandas() goes back, through Arrow again. An Int64 column that contains a null comes back as float64, with NaN for the null, unless use_pyarrow_extension_array=True keeps the Arrow types.
  3. The pandas DataFrame that comes back has a new index, numbered 0, 1, 2 and so on.
Types on the way to polars and on the way back Python · convert.py
import pandas as pd
import polars as pl

# A pandas frame with the types an analyst meets: a nullable integer, a float with NaN, text, a category, a timestamp.
pdf = pd.DataFrame({
    "qty": pd.array([2, None, 5], dtype="Int64"),
    "price": [899.0, float("nan"), 349.0],
    "city": ["Pune", None, "Thane"],
    "kind": pd.Categorical(["kurta", "saree", "kurta"]),
    "at": pd.to_datetime(["2026-04-02 10:00", "2026-04-05 11:30", None]),
}, index=pd.Index(["a", "b", "c"], name="row"))
print("pandas:", {c: str(t) for c, t in pdf.dtypes.items()})

# pandas to polars, through Arrow: NaN becomes null, and the index is dropped unless you ask for it.
p = pl.from_pandas(pdf)
print("polars:", p.schema)
print("with the index:", pl.from_pandas(pdf, include_index=True).columns)

# polars to pandas: an integer column with a null comes back as float64 unless Arrow-backed types are asked for.
print("back:  ", {c: str(t) for c, t in p.to_pandas().dtypes.items()})
print("arrow: ", {c: str(t) for c, t in p.to_pandas(use_pyarrow_extension_array=True).dtypes.items()})
print("qty after the round trip:", p.to_pandas()["qty"].tolist())

Output

pandas: {'qty': 'Int64', 'price': 'float64', 'city': 'str', 'kind': 'category', 'at': 'datetime64[us]'}
polars: Schema({'qty': Int64, 'price': Float64, 'city': String, 'kind': Categorical, 'at': Datetime(time_unit='us', time_zone=None)})
with the index: ['row', 'qty', 'price', 'city', 'kind', 'at']
back:   {'qty': 'float64', 'price': 'float64', 'city': 'str', 'kind': 'category', 'at': 'datetime64[us]'}
arrow:  {'qty': 'int64[pyarrow]', 'price': 'double[pyarrow]', 'city': 'large_string[pyarrow]', 'kind': 'dictionary<values=large_string, indices=int64, ordered=0>[pyarrow]', 'at': 'timestamp[us][pyarrow]'}
qty after the round trip: [2.0, nan, 5.0]

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 convert.py

pl.from_pandas turns NaN in a float column into null, keeps the nullable Int64, maps pandas’ str to String and a category to Categorical, and drops the index unless include_index=True keeps it as a column. The way back changes one type silently: an integer column with a null comes back as float64, with NaN in place of the null, because NumPy integers cannot be missing. to_pandas(use_pyarrow_extension_array=True) preserves Arrow-backed representations and nulls for these columns; inspect the resulting schema rather than assuming every dtype is identical. Convert once, at the edge of a pipeline, and say in the code which types you expect back.

Follow the input and compare the result with the recorded example.

Interactive · stage by stage Conversion preserves values while types and index can change Follow the original input through the stated transformation and check its output.
Conversion preserves values while types and index can change, step 3 of 3. Showing Nullable integer, Default pandas output, Arrow-backed output. In focus: Arrow-backed output.Nullable integer2; null; 5Default pandas output2.0; NaN; 5.0Arrow-backed outputint64 with null

The drawing shows the whole scene; its 3 steps are listed below.

Every step as text
  1. The original frame contains an Int64 quantity column with a missing middle value.
  2. The default conversion uses a floating-point representation for this nullable integer column.
  3. Requesting Arrow-backed pandas columns preserves the integer representation and null in this example. Index values must be kept separately if needed.

Checkpoint

Check: frame conversion

  1. Question 1 of 2 What preserves the example's nullable integer representation on return to pandas?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: Arrow-backed columns or an explicit nullable cast

    A representation choice can retain both integer values and genuine missingness. Replacing missing data with zero changes the values rather than merely the representation.

  2. Question 2 of 2 The quantity column holds two observed integers and one null. How many observed values are there?

    Type a number.

    Show the answer: Question 2 of 2

    Answer: 2 values

    The two values are 2 and 5; null remains missing.

SQL and expressions query the same in-scope frames

One report as SQL and as expressions Python · sql_on_frames.py
import polars as pl

sales = pl.read_csv("sales.csv").with_columns(
    pl.col("city").str.strip_chars().str.to_titlecase(),
    amount=pl.col("qty") * pl.col("unit_price"),
)

# pl.sql finds polars (and pandas) frames by their variable name; it returns a LazyFrame unless eager=True.
by_sql = pl.sql("""
    SELECT city, COUNT(*) AS orders, SUM(amount) AS revenue
    FROM sales
    WHERE status = 'Delivered' AND city IS NOT NULL
    GROUP BY city
    ORDER BY revenue DESC
""").collect()

# The same query as expressions.
by_expr = (
    sales.filter((pl.col("status") == "Delivered") & pl.col("city").is_not_null())
    .group_by("city")
    .agg(orders=pl.len(), revenue=pl.col("amount").sum())
    .sort("revenue", descending=True)
)
print(by_sql)
print("same values as the expressions:", by_sql.equals(by_expr.cast({"orders": pl.Int64})))

# SQLContext registers frames under names of your choice.
ctx = pl.SQLContext(orders=sales, eager=True)
print(ctx.tables(), ctx.execute("SELECT product, SUM(qty) AS units FROM orders GROUP BY product ORDER BY product").rows())

Output

shape: (3, 3)
┌────────┬────────┬─────────┐
│ city   ┆ orders ┆ revenue │
│ ---    ┆ ---    ┆ ---     │
│ str    ┆ i64    ┆ i64     │
╞════════╪════════╪═════════╡
│ Mumbai ┆ 3      ┆ 8742    │
│ Pune   ┆ 3      ┆ 6796    │
│ Nagpur ┆ 4      ┆ 6444    │
└────────┴────────┴─────────┘
same values as the expressions: True
['orders'] [('Dupatta', 10), ('Kurta', 12), ('Saree', 5)]

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 sql_on_frames.py

pl.sql finds polars and pandas frames by their variable names and returns a LazyFrame unless eager=True asks for a DataFrame. SQLContext registers frames under names of your choice, which keeps larger programs readable. Both run through the same engine as expressions, so the SQL report and the expression report give the same values; only COUNT(*) comes back as Int64 where pl.len() gives UInt32, which the comparison casts away. SQL is often the shortest path when the code being ported was itself SQL, or when the people who maintain it think in SQL.

Follow the input and compare the result with the recorded example.

Interactive · stage by stage SQL and expressions query the same in-scope frames Follow the original input through the stated transformation and check its output.
SQL and expressions query the same in-scope frames, step 3 of 3. Showing Frames in scope, Grouped result, Compare types. In focus: Compare types.Frames in scopesales DataFrameGrouped result3 citiesCompare typesInt64 vs UInt32 count

The drawing shows the whole scene; its 3 steps are listed below.

Every step as text
  1. pl.sql resolves the registered or visible frame name used in the query. It is not an automatic request to open sales.csv.
  2. The original SQL and expression versions report Mumbai, Pune and Nagpur after excluding the missing city.
  3. The values agree after the original explicit count-column cast. Shared query execution does not imply identical output dtypes.

Checkpoint

Check: sql frames

  1. Question 1 of 2 Where does pl.sql find the sales table in this example?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: The in-scope DataFrame named sales

    The SQL interface resolves frame names supplied by the calling context. A matching file name does not register or open a table for this call.

  2. Question 2 of 2 Three cities each have one grouped report row. How many rows are returned?

    Type a number.

    Show the answer: Question 2 of 2

    Answer: 3 rows

    Grouping by city gives three sorted rows.

Frame equality detects the missing city revenue

A small report in both libraries, and the test that compares them

compare_reports.py

import pandas as pd
import polars as pl
from polars.testing import assert_frame_equal

import report_pandas
import report_polars

from_pandas = pl.from_pandas(report_pandas.report("sales.csv"))
from_polars = report_polars.report("sales.csv")
print(from_pandas)
print(from_polars)
try:
    assert_frame_equal(from_pandas, from_polars, check_dtypes=False)
    print("the two reports agree")
except AssertionError as e:
    print("AssertionError:", str(e).splitlines()[0])

Output

shape: (3, 3)
┌────────┬────────┬─────────┐
│ city   ┆ orders ┆ revenue │
│ ---    ┆ ---    ┆ ---     │
│ str    ┆ i64    ┆ i64     │
╞════════╪════════╪═════════╡
│ Mumbai ┆ 3      ┆ 8742    │
│ Nagpur ┆ 4      ┆ 6444    │
│ Pune   ┆ 3      ┆ 6796    │
└────────┴────────┴─────────┘
shape: (4, 3)
┌────────┬────────┬─────────┐
│ city   ┆ orders ┆ revenue │
│ ---    ┆ ---    ┆ ---     │
│ str    ┆ u32    ┆ i64     │
╞════════╪════════╪═════════╡
│ Mumbai ┆ 3      ┆ 8742    │
│ Nagpur ┆ 4      ┆ 6444    │
│ Pune   ┆ 3      ┆ 6796    │
│ null   ┆ 1      ┆ 1396    │
└────────┴────────┴─────────┘
AssertionError: DataFrames are different (height (row count) mismatch)

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 compare_reports.py

report_pandas.py

import pandas as pd


def report(path):
    df = pd.read_csv(path)
    df["city"] = df["city"].str.strip().str.title()
    df = df[df["status"] == "Delivered"]
    df = df.assign(amount=df["qty"] * df["unit_price"])
    return df.groupby("city", as_index=False).agg(orders=("order_id", "size"), revenue=("amount", "sum")).sort_values("city", ignore_index=True)


if __name__ == "__main__":
    print(report("sales.csv"))

report_polars.py

import polars as pl


def report(path):
    return (
        pl.read_csv(path)
        .with_columns(pl.col("city").str.strip_chars().str.to_titlecase())
        .filter(pl.col("status") == "Delivered")
        .with_columns(amount=pl.col("qty") * pl.col("unit_price"))
        .group_by("city")
        .agg(orders=pl.len(), revenue=pl.col("amount").sum())
        .sort("city", nulls_last=True)
    )


if __name__ == "__main__":
    print(report("sales.csv"))

assert_frame_equal compares two polars frames column by column and stops at the first difference; check_dtypes=False lets integer widths differ, since pandas counts in int64 and polars in UInt32. The test fails, and it is right to: the polars report has a fourth row, the order with no city, which pandas’ group-by left out. Fill the city with “Unknown” in both versions, or pass dropna=False to pandas, and the test passes. Without the test, the two reports would have disagreed by 1,396 rupees in silence.

Which library a team should use depends on the work more than on speed. pandas has the widest ecosystem and suits small and medium tables that feed plotting, statistics or machine-learning libraries. polars suits long pipelines of expressions on large tables and lazy reads of big files. DuckDB suits work that is mostly SQL or mostly files on disk. The three exchange data through Arrow, so mixing them is normal.

Follow the input and compare the result with the recorded example.

Interactive · stage by stage Frame equality detects the missing city revenue Follow the original input through the stated transformation and check its output.
Frame equality detects the missing city revenue, step 3 of 3. Showing Pandas result, Polars result, Explicit policy. In focus: Explicit policy.Pandas result3 city rowsPolars result4 rows; null-city 1,396Explicit policyname or retain missing key

The drawing shows the whole scene; its 3 steps are listed below.

Every step as text
  1. The original pandas group-by excludes the missing city and reports three rows.
  2. The Polars result retains that city group, so frame equality correctly detects a height mismatch.
  3. Naming the city consistently or choosing pandas dropna=False aligns the report's group policy. Compare rows, values, order and required types.

Checkpoint

Check: port proof

  1. Question 1 of 2 Can equal totals alone prove a port equivalent?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: No; compare the intended rows and schema

    The original mismatch exposes a missing group even when known-city totals agree. An omitted or duplicated record can alter downstream decisions.

  2. Question 2 of 2 One missing-city row contributes 1,396 rupees. How much revenue does dropping it omit?

    Type a number.

    Show the answer: Question 2 of 2

    Answer: 1396 rupees

    The entire 1,396 belongs to the missing-city group.

A 12-step cleaning script ported and proved, with the traps reproduced Pro: opens with a Pro, Premium or Ultimate pass

A real port is longer than a report, and every step is a chance for the two versions to drift apart. This part ports a 12-step pandas cleaning script to one polars chain, proves the result equal with a test, reproduces the four differences that most often break a first port, and leaves you a port of your own to finish.

In this part:

  • Explicit null and duplicate policies make ports agree
  • Four library defaults can change a report
  • A port preserves duplicates, filters, totals and types

3 diagrams · 5 runnable examples (CSV, Python, clean_pandas.py, clean_polars.py, compare_ports.py) · about 6 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

What crosses the boundary between pandas and polars, and at what cost Pro: opens with a Pro, Premium or Ultimate pass

Converting a frame is cheap for some columns and costly for others, and it changes some types on the way back. This part times a million-row conversion column type by column type, shows which columns are not copied at all, and sets out a table of the types that survive the round trip.

In this part:

  • Column types decide measured conversion work
  • Nulls and row labels need a boundary contract
  • Arrow interchange still requires type checks

3 diagrams · 2 runnable examples (Python) · about 6 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Where polars SQL differs, old code tested, and the choice of library Pro: opens with a Pro, Premium or Ultimate pass

SQL on polars frames is convenient, and it follows SQL’s rules rather than those of expressions in a few places that change numbers. This part reproduces those differences, tests suggested or older polars code with deprecation warnings turned into errors, and gives a rubric for choosing pandas, polars or DuckDB for a team.

In this part:

  • SQL sums and division state their intended meaning
  • Warnings as errors expose old API spellings
  • A measured pilot chooses where to migrate

3 diagrams · 2 runnable examples (Python) · about 6 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Practise the stated transformations

Practice that adapts to you

Practise this lesson

11 questions about 12 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.

  1. Question 1 of 11 How should pandas label alignment be represented in Polars?

    Choose one answer.

    Show the answer: Question 1 of 11

    Answer: An explicit key-based join

    Polars has no pandas index and requires an explicit matching rule. Assuming row positions align can combine unrelated records.

  2. Question 2 of 11 A group key is missing on one of eleven delivered rows. How many rows remain if that null group is dropped?

    Type a number.

    Show the answer: Question 2 of 11

    Answer: 10 rows

    11−1=10; retaining it requires an explicit group policy.

  3. Question 3 of 11 What preserves the example's nullable integer representation on return to pandas?

    Choose one answer.

    Show the answer: Question 3 of 11

    Answer: Arrow-backed columns or an explicit nullable cast

    A representation choice can retain both integer values and genuine missingness. Replacing missing data with zero changes the values rather than merely the representation.

  4. Question 4 of 11 A named index is retained as one column alongside five data columns. How many columns result?

    Type a number.

    Show the answer: Question 4 of 11

    Answer: 6 columns

    Five data columns plus the index key gives six.

  5. Question 5 of 11 Where does pl.sql find the sales table in this example?

    Choose one answer.

    Show the answer: Question 5 of 11

    Answer: The in-scope DataFrame named sales

    The SQL interface resolves frame names supplied by the calling context. A matching file name does not register or open a table for this call.

  6. Question 6 of 11 The product SQL report has quantities ten, twelve and five. What is the total?

    Type a number.

    Show the answer: Question 6 of 11

    Answer: 27 units

    10+12+5=27.

  7. Question 7 of 11 Can equal totals alone prove a port equivalent?

    Choose one answer.

    Show the answer: Question 7 of 11

    Answer: No; compare the intended rows and schema

    The original mismatch exposes a missing group even when known-city totals agree. An omitted or duplicated record can alter downstream decisions.

  8. Question 8 of 11 The reference has three rows and the port four. What is the height difference?

    Type a number.

    Show the answer: Question 8 of 11

    Answer: 1 row

    The port has one more row than the three-row reference.

  9. Question 9 of 11 Which polars expression does what df.groupby("city")["amount"].transform("sum") does in pandas?

    Choose one answer.

    Show the answer: Question 9 of 11

    Answer: sum() of the amount column, followed by over("city")

    over("city") runs the sum within each city and writes the result on every row, as transform does. The group_by version returns one row per city instead, and a plain sum() is one number for the whole column.

  10. Question 10 of 11 A polars Int64 column holds 2, null and 5. What does it become after to_pandas() with the default settings?

    Choose one answer.

    Show the answer: Question 10 of 11

    Answer: float64, holding 2.0, NaN and 5.0

    NumPy integers have no missing value, so the default conversion turns the column into floats with NaN. With use_pyarrow_extension_array set to True, to_pandas keeps an integer column, int64[pyarrow], with a real null.

  11. Question 11 of 11 In pl.sql("SELECT city, SUM(amount) AS revenue FROM sales GROUP BY city"), where does polars find sales?

    Choose one answer.

    Show the answer: Question 11 of 11

    Answer: In a polars or pandas DataFrame called sales in the code that calls pl.sql

    pl.sql looks up the names in the query among the frames in scope, by their variable names. A SQLContext lets you register frames under names of your choice instead, which is clearer in larger programs.

Interview questions

  1. Fresher to mid · Warm-up

    polars has no index. How do you do in polars what pandas does with set_index, loc and reset_index?

    In pandas the index labels rows, so set_index("customer") followed by df.loc["C01"] looks a row up by its label, and reset_index turns the labels back into a column. In polars the key stays an ordinary column: df.filter(pl.col("customer") == "C01") finds the row, and there is nothing to reset. Alignment by label, where pandas matches the rows of two Series by their index, becomes an explicit join on the key column, and a MultiIndex becomes several key columns used in group_by, join or pivot. Access by position, such as iloc, becomes row(i), slice or head. The code gets a little longer, but every match on a key is written out, so nothing aligns by accident.

  2. Mid to senior

    A pipeline converts a pandas frame to polars, cleans it, and converts it back with to_pandas(). Afterwards a database load fails because the quantity column is now a float, and a lookup by customer id fails because the index is gone. Explain both failures and design the boundary.

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  3. Senior to staff

    Your team runs 40 pandas scripts in production, a few of them slow. A manager asks you to move all of them to polars. How do you decide what to migrate, and how do you reduce the risk of the migration?

    Model answer, Pro. First step and follow-ups

  4. Mid to senior

    After a polars upgrade, a report written with pl.sql shows null instead of 0 refunds for cities that had none, and a ratio of two integer columns changed from 3 to 3.5 on some rows. Explain both changes and how you protect SQL-based reports.

    Model answer, Pro. First step and follow-ups

Key takeaways

  • Most pandas idioms map to one polars method; code built on the index becomes key columns, filters and joins.
  • pl.from_pandas and to_pandas go through Arrow: NaN becomes null on the way in, and an integer column with a null becomes float64 on the way back unless Arrow types are kept.
  • pl.sql and SQLContext run SQL over polars frames with the same engine as expressions.
  • Prove every port with assert_frame_equal on real inputs, and sort before comparing.
Compare Two Excel or CSV Files Compare two exported CSV or Excel tables row by row, for a quick check of a port's output before a test exists. Run SQL on CSV & Excel Files Run SQL on a CSV file in your browser, to try a query before you move it into pl.sql.

Terms in this lesson

Arrow interchange boundary
A transfer of typed column data between libraries using Arrow representations; values, nulls, types and key semantics still need a contract.
frame equivalence
Agreement on the intended rows, values, column order, row order and required types, tested under an explicit comparison policy.

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.