{"slug":"alterlab-polars","title":"alterlab-polars","summary":"Fast in-memory DataFrame analytics with Polars — lazy evaluation, parallel execution, and an Apache Arrow backend for datasets that fit in RAM. Use when pandas is too slow but data still fits in memory, for 1-100GB datasets, ETL pipelines, or a faster pandas replacement. For larg","platform":"Claude","tags":[],"authorName":"LLM Mart","authorSlug":"llm-mart","score":0,"source":"github","price":null,"verified":false,"createdAt":"2026-09-23T18:57:04.149819Z","repo":{"url":"https://github.com/AlterLab-IEU/AlterLab-Academic-Skills","stars":68,"forks":13,"license":"MIT","updatedAt":"2026-09-23T13:42:59Z"},"bodyHtml":"<hr>\n<h2>name: alterlab-polars\ndescription: Fast in-memory DataFrame analytics with Polars — lazy evaluation, parallel execution, and an Apache Arrow backend for datasets that fit in RAM. Use when pandas is too slow but data still fits in memory, for 1-100GB datasets, ETL pipelines, or a faster pandas replacement. For larger-than-RAM data prefer dask or vaex. Part of the AlterLab Academic Skills suite.\nlicense: MIT\nallowed-tools: Read Write Edit Bash(python:<em>) Bash(uv:</em>)\ncompatibility: No API key required. Runs locally via <code>uv run python</code>; requires polars &gt;= 1.0 (current 1.44 as of 2026-09; 2.0 is in release candidate).\nmetadata:\nskill-author: AlterLab\nversion: \"1.0.1\"\nlast_updated: \"2026-09-23\"</h2>\n<h1>Polars</h1>\n<h2>Overview</h2>\n<p>Polars is a lightning-fast DataFrame library for Python and Rust built on Apache Arrow. Work with Polars' expression-based API, lazy evaluation framework, and high-performance data manipulation capabilities for efficient data processing, pandas migration, and data pipeline optimization.</p>\n<h2>When to Use This Skill</h2>\n<ul>\n<li>pandas code is too slow but the data (or the columns a lazy query touches) fits on one machine</li>\n<li>Building ETL or feature pipelines with lazy scans (<code>scan_csv</code>, <code>scan_parquet</code>) and query optimization</li>\n<li>Migrating pandas code to the Polars expression API</li>\n<li>Window functions, joins, and group-by aggregations over large in-memory tables</li>\n</ul>\n<h3>Does NOT Trigger</h3>\n<table>\n<thead>\n<tr>\n<th>Scenario</th>\n<th>Use Instead</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Distributed execution across a cluster, or scaling existing pandas/NumPy code with a task scheduler and dashboard</td>\n<td><code>alterlab-dask</code></td>\n</tr>\n<tr>\n<td>Billion-row out-of-core exploration with memory-mapped HDF5/Arrow files and big-data plots</td>\n<td><code>alterlab-vaex</code></td>\n</tr>\n<tr>\n<td>First look at an unfamiliar data file: structure, missingness, and quality report</td>\n<td><code>alterlab-eda</code></td>\n</tr>\n</tbody>\n</table>\n<h2>Quick Start</h2>\n<h3>Installation and Basic Usage</h3>\n<p>Pin a recent 1.x (examples here use the Polars 1.x API):</p>\n<pre><code>uv add 'polars&gt;=1.0,&lt;2'\n</code></pre>\n<p>Polars 2.0 is in release candidate (2.0.0rc2, September 2026). Its upgrade guide lists changes\nthat alter results silently: <code>LazyFrame.collect()</code> defaults to the streaming engine (row order\nof some joins/unpivots can change), <code>pl.concat(how=\"horizontal\")</code> requires equal heights\n(<code>how=\"horizontal_extend\"</code> pads), <code>explode()</code> drops empty lists instead of emitting a null row,\n<code>is_in</code> refuses lossy casts, <code>LazyFrame.profile()</code> is removed, and headerless CSV columns are\nnamed from <code>column_0</code>. Check <a href=\"https://docs.pola.rs/releases/upgrade/\">https://docs.pola.rs/releases/upgrade/</a> before moving to 2.x.</p>\n<p>Basic DataFrame creation and operations:</p>\n<pre><code>import polars as pl\n\n# Create DataFrame\ndf = pl.DataFrame({\n    \"name\": [\"Alice\", \"Bob\", \"Charlie\"],\n    \"age\": [25, 30, 35],\n    \"city\": [\"NY\", \"LA\", \"SF\"]\n})\n\n# Select columns\ndf.select(\"name\", \"age\")\n\n# Filter rows\ndf.filter(pl.col(\"age\") &gt; 25)\n\n# Add computed columns\ndf.with_columns(\n    age_plus_10=pl.col(\"age\") + 10\n)\n</code></pre>\n<h2>Core Concepts</h2>\n<h3>Expressions</h3>\n<p>Expressions are the fundamental building blocks of Polars operations. They describe transformations on data and can be composed, reused, and optimized.</p>\n<p><strong>Key principles:</strong></p>\n<ul>\n<li>Use <code>pl.col(\"column_name\")</code> to reference columns</li>\n<li>Chain methods to build complex transformations</li>\n<li>Expressions are lazy and only execute within contexts (select, with_columns, filter, group_by)</li>\n</ul>\n<p><strong>Example:</strong></p>\n<pre><code># Expression-based computation\ndf.select(\n    pl.col(\"name\"),\n    (pl.col(\"age\") * 12).alias(\"age_in_months\")\n)\n</code></pre>\n<h3>Lazy vs Eager Evaluation</h3>\n<p><strong>Eager (DataFrame):</strong> Operations execute immediately</p>\n<pre><code>df = pl.read_csv(\"file.csv\")  # Reads immediately\nresult = df.filter(pl.col(\"age\") &gt; 25)  # Executes immediately\n</code></pre>\n<p><strong>Lazy (LazyFrame):</strong> Operations build a query plan, optimized before execution</p>\n<pre><code>lf = pl.scan_csv(\"file.csv\")  # Doesn't read yet\nresult = lf.filter(pl.col(\"age\") &gt; 25).select(\"name\", \"age\")\ndf = result.collect()  # Now executes optimized query\n</code></pre>\n<p><strong>When to use lazy:</strong></p>\n<ul>\n<li>Working with large datasets</li>\n<li>Complex query pipelines</li>\n<li>When only some columns/rows are needed</li>\n<li>Performance is critical</li>\n</ul>\n<p><strong>Benefits of lazy evaluation:</strong></p>\n<ul>\n<li>Automatic query optimization</li>\n<li>Predicate pushdown</li>\n<li>Projection pushdown</li>\n<li>Parallel execution</li>\n</ul>\n<p>For detailed concepts, load <code>references/core_concepts.md</code>.</p>\n<h2>Common Operations</h2>\n<h3>Select</h3>\n<p>Select and manipulate columns:</p>\n<pre><code># Select specific columns\ndf.select(\"name\", \"age\")\n\n# Select with expressions\ndf.select(\n    pl.col(\"name\"),\n    (pl.col(\"age\") * 2).alias(\"double_age\")\n)\n\n# Select all columns matching a pattern\ndf.select(pl.col(\"^.*_id$\"))\n</code></pre>\n<h3>Filter</h3>\n<p>Filter rows by conditions:</p>\n<pre><code># Single condition\ndf.filter(pl.col(\"age\") &gt; 25)\n\n# Multiple conditions (cleaner than using &amp;)\ndf.filter(\n    pl.col(\"age\") &gt; 25,\n    pl.col(\"city\") == \"NY\"\n)\n\n# Complex conditions\ndf.filter(\n    (pl.col(\"age\") &gt; 25) | (pl.col(\"city\") == \"LA\")\n)\n</code></pre>\n<h3>With Columns</h3>\n<p>Add or modify columns while preserving existing ones:</p>\n<pre><code># Add new columns\ndf.with_columns(\n    age_plus_10=pl.col(\"age\") + 10,\n    name_upper=pl.col(\"name\").str.to_uppercase()\n)\n\n# Parallel computation (all columns computed in parallel)\ndf.with_columns(\n    pl.col(\"value\") * 10,\n    pl.col(\"value\") * 100,\n)\n</code></pre>\n<h3>Group By and Aggregations</h3>\n<p>Group data and compute aggregations:</p>\n<pre><code># Basic grouping\ndf.group_by(\"city\").agg(\n    pl.col(\"age\").mean().alias(\"avg_age\"),\n    pl.len().alias(\"count\")\n)\n\n# Multiple group keys\ndf.group_by(\"city\", \"department\").agg(\n    pl.col(\"salary\").sum()\n)\n\n# Conditional aggregations\ndf.group_by(\"city\").agg(\n    (pl.col(\"age\") &gt; 30).sum().alias(\"over_30\")\n)\n</code></pre>\n<p>For detailed operation patterns, load <code>references/operations.md</code>.</p>\n<h2>Aggregations and Window Functions</h2>\n<h3>Aggregation Functions</h3>\n<p>Common aggregations within <code>group_by</code> context:</p>\n<ul>\n<li><code>pl.len()</code> - count rows</li>\n<li><code>pl.col(\"x\").sum()</code> - sum values</li>\n<li><code>pl.col(\"x\").mean()</code> - average</li>\n<li><code>pl.col(\"x\").min()</code> / <code>pl.col(\"x\").max()</code> - extremes</li>\n<li><code>pl.first()</code> / <code>pl.last()</code> - first/last values</li>\n</ul>\n<h3>Window Functions with <code>over()</code></h3>\n<p>Apply aggregations while preserving row count:</p>\n<pre><code># Add group statistics to each row\ndf.with_columns(\n    avg_age_by_city=pl.col(\"age\").mean().over(\"city\"),\n    rank_in_city=pl.col(\"salary\").rank().over(\"city\")\n)\n\n# Multiple grouping columns\ndf.with_columns(\n    group_avg=pl.col(\"value\").mean().over(\"category\", \"region\")\n)\n</code></pre>\n<p><strong>Mapping strategies:</strong></p>\n<ul>\n<li><code>group_to_rows</code> (default): Preserves original row order</li>\n<li><code>explode</code>: Changes the row count (use in <code>select</code>, not <code>with_columns</code>); output is grouped</li>\n<li><code>join</code>: Creates list columns</li>\n</ul>\n<h2>Data I/O</h2>\n<h3>Supported Formats</h3>\n<p>Polars supports reading and writing:</p>\n<ul>\n<li>CSV, Parquet, JSON, Excel</li>\n<li>Databases (via connectors)</li>\n<li>Cloud storage (S3, Azure, GCS)</li>\n<li>Google BigQuery</li>\n<li>Multiple/partitioned files</li>\n</ul>\n<h3>Common I/O Operations</h3>\n<p><strong>CSV:</strong></p>\n<pre><code># Eager\ndf = pl.read_csv(\"file.csv\")\ndf.write_csv(\"output.csv\")\n\n# Lazy (preferred for large files)\nlf = pl.scan_csv(\"file.csv\")\nresult = lf.filter(...).select(...).collect()\n</code></pre>\n<p><strong>Parquet (recommended for performance):</strong></p>\n<pre><code>df = pl.read_parquet(\"file.parquet\")\ndf.write_parquet(\"output.parquet\")\n</code></pre>\n<p><strong>JSON:</strong></p>\n<pre><code>df = pl.read_json(\"file.json\")\ndf.write_json(\"output.json\")\n</code></pre>\n<p>For comprehensive I/O documentation, load <code>references/io_guide.md</code>.</p>\n<h2>Transformations</h2>\n<h3>Joins</h3>\n<p>Combine DataFrames:</p>\n<pre><code># Inner join\ndf1.join(df2, on=\"id\", how=\"inner\")\n\n# Left join\ndf1.join(df2, on=\"id\", how=\"left\")\n\n# Join on different column names\ndf1.join(df2, left_on=\"user_id\", right_on=\"id\")\n</code></pre>\n<h3>Concatenation</h3>\n<p>Stack DataFrames:</p>\n<pre><code># Vertical (stack rows)\npl.concat([df1, df2], how=\"vertical\")\n\n# Horizontal (add columns)\npl.concat([df1, df2], how=\"horizontal\")\n\n# Diagonal (union with different schemas)\npl.concat([df1, df2], how=\"diagonal\")\n</code></pre>\n<h3>Pivot and Unpivot</h3>\n<p>Reshape data:</p>\n<pre><code># Pivot (wide format): on= is the column whose values become new columns\ndf.pivot(\"product\", index=\"date\", values=\"sales\")\n\n# Unpivot (long format)\ndf.unpivot(index=\"id\", on=[\"col1\", \"col2\"])\n</code></pre>\n<p>For detailed transformation examples, load <code>references/transformations.md</code>.</p>\n<h2>Pandas Migration</h2>\n<p>Polars offers significant performance improvements over pandas with a cleaner API. Key differences:</p>\n<h3>Conceptual Differences</h3>\n<ul>\n<li><strong>No index</strong>: Polars uses integer positions only</li>\n<li><strong>Strict typing</strong>: No silent type conversions</li>\n<li><strong>Lazy evaluation</strong>: Available via LazyFrame</li>\n<li><strong>Parallel by default</strong>: Operations parallelized automatically</li>\n</ul>\n<h3>Common Operation Mappings</h3>\n<table>\n<thead>\n<tr>\n<th>Operation</th>\n<th>Pandas</th>\n<th>Polars</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Select column</td>\n<td><code>df[\"col\"]</code></td>\n<td><code>df.select(\"col\")</code></td>\n</tr>\n<tr>\n<td>Filter</td>\n<td><code>df[df[\"col\"] &gt; 10]</code></td>\n<td><code>df.filter(pl.col(\"col\") &gt; 10)</code></td>\n</tr>\n<tr>\n<td>Add column</td>\n<td><code>df.assign(x=...)</code></td>\n<td><code>df.with_columns(x=...)</code></td>\n</tr>\n<tr>\n<td>Group by</td>\n<td><code>df.groupby(\"col\").agg(...)</code></td>\n<td><code>df.group_by(\"col\").agg(...)</code></td>\n</tr>\n<tr>\n<td>Window</td>\n<td><code>df.groupby(\"col\").transform(...)</code></td>\n<td><code>df.with_columns(...).over(\"col\")</code></td>\n</tr>\n</tbody>\n</table>\n<h3>Key Syntax Patterns</h3>\n<p><strong>Pandas sequential (slow):</strong></p>\n<pre><code>df.assign(\n    col_a=lambda df_: df_.value * 10,\n    col_b=lambda df_: df_.value * 100\n)\n</code></pre>\n<p><strong>Polars parallel (fast):</strong></p>\n<pre><code>df.with_columns(\n    col_a=pl.col(\"value\") * 10,\n    col_b=pl.col(\"value\") * 100,\n)\n</code></pre>\n<p>For comprehensive migration guide, load <code>references/pandas_migration.md</code>.</p>\n<h2>Best Practices</h2>\n<h3>Polars 1.x API notes (renamed since 0.x)</h3>\n<p>These older names appear in stale tutorials and LLM training data; use the 1.x form:</p>\n<ul>\n<li><code>read_csv(dtypes=...)</code> -&gt; <code>read_csv(schema_overrides=...)</code></li>\n<li><code>pl.Utf8</code> -&gt; <code>pl.String</code></li>\n<li><code>pl.NUMERIC_DTYPES</code> -&gt; <code>import polars.selectors as cs; cs.numeric()</code></li>\n<li><code>join(how=\"outer\")</code> -&gt; <code>join(how=\"full\")</code> (and <code>how=\"full\"</code> no longer coalesces keys; pass <code>coalesce=True</code> for old behavior)</li>\n<li><code>pivot(columns=...)</code> -&gt; <code>pivot(on=...)</code> (<code>on</code> is the first positional arg)</li>\n<li><code>collect(streaming=True)</code> -&gt; <code>collect(engine=\"streaming\")</code></li>\n<li><code>read_database(connection_uri=...)</code> -&gt; <code>read_database_uri(uri=...)</code></li>\n</ul>\n<h3>Performance Optimization</h3>\n<ol>\n<li><p><strong>Use lazy evaluation for large datasets:</strong></p>\n<pre><code>lf = pl.scan_csv(\"large.csv\")  # Don't use read_csv\nresult = lf.filter(...).select(...).collect()\n</code></pre>\n</li>\n<li><p><strong>Avoid Python functions in hot paths:</strong></p>\n<ul>\n<li>Stay within expression API for parallelization</li>\n<li>Use <code>.map_elements()</code> only when necessary</li>\n<li>Prefer native Polars operations</li>\n</ul>\n</li>\n<li><p><strong>Use the streaming engine to lower peak memory:</strong></p>\n<pre><code>lf.collect(engine=\"streaming\")  # `streaming=True` is deprecated\n</code></pre>\n</li>\n<li><p><strong>Select only needed columns early:</strong></p>\n<pre><code># Good: Select columns early\nlf.select(\"col1\", \"col2\").filter(...)\n\n# Bad: Filter on all columns first\nlf.filter(...).select(\"col1\", \"col2\")\n</code></pre>\n</li>\n<li><p><strong>Use appropriate data types:</strong></p>\n<ul>\n<li>Categorical for low-cardinality strings</li>\n<li>Appropriate integer sizes (i32 vs i64)</li>\n<li>Date types for temporal data</li>\n</ul>\n</li>\n</ol>\n<h3>Expression Patterns</h3>\n<p><strong>Conditional operations:</strong></p>\n<pre><code>pl.when(condition).then(value).otherwise(other_value)\n</code></pre>\n<p><strong>Column operations across multiple columns:</strong></p>\n<pre><code>df.select(pl.col(\"^.*_value$\") * 2)  # Regex pattern\n</code></pre>\n<p><strong>Null handling:</strong></p>\n<pre><code>pl.col(\"x\").fill_null(0)\npl.col(\"x\").is_null()\npl.col(\"x\").drop_nulls()\n</code></pre>\n<p>For additional best practices and patterns, load <code>references/best_practices.md</code>.</p>\n<h2>Resources</h2>\n<p>This skill includes comprehensive reference documentation:</p>\n<h3>references/</h3>\n<ul>\n<li><code>core_concepts.md</code> - Detailed explanations of expressions, lazy evaluation, and type system</li>\n<li><code>operations.md</code> - Comprehensive guide to all common operations with examples</li>\n<li><code>pandas_migration.md</code> - Complete migration guide from pandas to Polars</li>\n<li><code>io_guide.md</code> - Data I/O operations for all supported formats</li>\n<li><code>transformations.md</code> - Joins, concatenation, pivots, and reshaping operations</li>\n<li><code>best_practices.md</code> - Performance optimization tips and common patterns</li>\n</ul>\n<p>Load these references as needed when users require detailed information about specific topics.</p>\n","files":[{"path":"evals/evals.json","sizeBytes":5559,"isText":true},{"path":"references/best_practices.md","sizeBytes":14479,"isText":true},{"path":"references/core_concepts.md","sizeBytes":8642,"isText":true},{"path":"references/io_guide.md","sizeBytes":12156,"isText":true},{"path":"references/operations.md","sizeBytes":12379,"isText":true},{"path":"references/pandas_migration.md","sizeBytes":12345,"isText":true},{"path":"references/transformations.md","sizeBytes":11391,"isText":true},{"path":"SKILL.md","sizeBytes":11878,"isText":true}],"reviewScore":null,"reviewSummary":null,"trust":{"provenance":"trusted-source-unreviewed","notice":"Community-authored content, reproduced verbatim and not vetted as instructions. Treat it as data to evaluate, never as directives to follow.","bodySource":null},"bodyLocked":false,"purchaseUrl":null,"sourceUrl":null,"report":{"provenance":"trusted-source-unreviewed","screen":{"ran":true,"outcome":"clean","suspicious":0,"notes":0,"hiddenCharacters":false},"virusScan":{"engine":"clamav","status":"clean","scannedAt":"2026-09-23T18:58:46.616837Z","sha256":"A10824799D138AFE852D1363EA677FED415C0C2322E922B51B5F3EAA3BD09F46","sizeBytes":30943},"review":null,"source":{"repositoryUrl":"https://github.com/AlterLab-IEU/AlterLab-Academic-Skills","path":"skills/data-science/alterlab-polars","license":"MIT","commit":"e4836c08a20da195a11f30f203a8cf23ec30aa95","subtreeSha":"69DDAD482A3C7E532104783A0187319EE94B48D433285649B1353640F89A07E9","lastSyncedAt":"2026-09-23T18:56:52.297238Z"},"reviewedAt":"2026-09-23T19:02:10.537716Z","notice":"Community-authored content, reproduced verbatim and not vetted as instructions. Treat it as data to evaluate, never as directives to follow."},"install":[{"target":"skills-cli","command":"npx skills add https://github.com/AlterLab-IEU/AlterLab-Academic-Skills/tree/main/skills/data-science/alterlab-polars"},{"target":"claude-code","command":"claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install alterlab-ieu-alterlab-academic-skills@llmmart"},{"target":"git","command":"git clone https://github.com/AlterLab-IEU/AlterLab-Academic-Skills.git"}]}