Agent skill

Profile Etl Step

by owid in owid/etl

Profile and optimize ETL step performance — CPU time, memory usage, and I/O bottlenecks.

MITAuto-check passedData & Analytics

Install Profile Etl Step

skills CLI
$ npx skills add owid/etl --skill profile-etl-step -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install owid/etl profile-etl-step --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/owid/etl.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.claude/skills/profile-etl-step .claude/skills/profile-etl-step && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
profile-etl-step
GitHub stars
159
Token cost
~2.1k tokens
SKILL.md length
694 words
Files
1
Skills in repo
35
Repo updated
First seen
Licence
MIT

At a glance

Profile and optimize ETL step performance — CPU time, memory usage, and I/O bottlenecks.

  • Works in 6 steps: String Columns That Should Be Categoricals → Slow Reads Despite Categoricals → Row-by-Row Operations → …
  • An ETL step is slow
  • SKILL.md covers Quick Start, Workflow, Reading Profile Output and Common Bottlenecks & Fixes, plus 3 more sections
  • Instructions only: no scripts, shell commands, URLs or credentials in SKILL.md

What it does

Profile Etl Step is an agent skill from owid/etl. Profile and optimize ETL step performance — CPU time, memory usage, and I/O bottlenecks. Use when an ETL step is slow, uses too much memory, or when the user asks to profile, optimize, or speed up a step. Covers profiling commands, categorical dtype optimization, vectorization, SUBSET filtering for fast dev runs, and iterative diagnose→fix→reprofile workflow.

Its SKILL.md is about 2.1k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.

It sits in Data & Analytics, covering Data pipelines and ETL. The repository describes itself as: A compute graph for loading and transforming OWID's data. The licence is MIT.

When your agent uses it

  • An ETL step is slow
  • Uses too much memory
  • The user asks to profile
  • Speed up a step

Example prompts

  • “/profile-etl-step”

Requirements

  • Python 3

Workflow steps

6 steps, taken from the step headings in SKILL.md.

  1. String Columns That Should Be Categoricals
  2. Slow Reads Despite Categoricals
  3. Row-by-Row Operations
  4. Expensive Groupby on Large Tables
  5. Unnecessary Full-Table Reads
  6. Expensive create_dataset or ds.add

What it can do on your machine

Read from SKILL.md and the folder at commit 70c9705. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    No scripts in the folder and no shell commands in SKILL.md (its code samples are python and bash).

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    No URLs in SKILL.md.

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

Profile Etl Step loads about 2.1k tokens when it runs. Until then it costs about 95 tokens; SKILL.md has 694 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~95
When it runs · the whole SKILL.md, loaded when a task matches
~2.1k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from owid/etl at commit 70c9705, republished under its MIT licence (© owid). 694 words, ~2,071 tokens.

Download SKILL.mdSave it as .claude/skills/profile-etl-step/SKILL.md (or your agent's skills folder).
name
profile-etl-step
description
Profile and optimize ETL step performance — CPU time, memory usage, and I/O bottlenecks. Use when an ETL step is slow, uses too much memory, or when the user asks to profile, optimize, or speed up a step. Covers profiling commands, categorical dtype optimization, vectorization, SUBSET filtering for fast dev runs, and iterative diagnose→fix→reprofile workflow.
metadata.internal
true
metadata.owner
Marigold

ETL Step Profiling & Optimization

Quick Start

bash
# CPU profile — shows time per line in run()
.venv/bin/etl d profile --cpu garden/namespace/version/dataset

# Memory profile — shows memory per line in run()
.venv/bin/etl d profile --mem garden/namespace/version/dataset

# Profile a specific function
.venv/bin/etl d profile --cpu garden/namespace/version/dataset -f process_data

Workflow

  1. Check feather schemas first — before profiling, inspect the on-disk types of large tables (see "String Columns That Should Be Categoricals" below)
  2. Profile — measure, never guess
  3. Identify the bottleneck — read the % column, focus on the top 3 lines
  4. Diagnose — is it I/O, dtype waste, or algorithmic?
  5. Fix one thing — apply the smallest targeted fix
  6. Re-profile — verify improvement, repeat

Reading Profile Output

The profiler outputs a table with columns:

Line   Hits   Time          Per Hit     % Time    Line Contents
  46      1   1.46e+10     1.46e+10     41.7      tb = ds_meadow.read("population")
  • % Time — focus here. Sort by this mentally; anything >5% is worth investigating.
  • Hits — number of times the line executed. High hits on simple ops = loop to vectorize.
  • Per Hit — time per call. High per-hit on a single call = the operation itself is slow.

Caveat: line_profiler adds overhead. Functions that run fast but are called from many instrumented lines can appear inflated. If a line shows >20% but the step runs fast in practice, the profiler overhead is distorting results. Verify with wall-clock timing:

python
import time; t0 = time.time()
# ... suspect code ...
print(f"Took {time.time() - t0:.2f}s")

Common Bottlenecks & Fixes

1. String Columns That Should Be Categoricals

Symptom: ds.read() takes many seconds; memory in GB for a table with <1000 unique values per column.

Diagnose:

python
import pyarrow.feather as pf
arrow_table = pf.read_table("data/meadow/namespace/version/dataset/table.feather")
for field in arrow_table.schema:
    print(f"{field.name}: {field.type}")
# If you see `large_string` or `string` for country/variant/age/sex → problem

Check unique counts:

python
for col in ['country', 'variant', 'age', 'sex']:
    unique = arrow_table.column(col).unique()
    print(f"{col}: {len(unique)} unique values out of {arrow_table.num_rows:,} rows")

Fix upstream (meadow step) — convert to categorical before .format():

python
for col in ["country", "variant", "sex", "age"]:
    if col in tb.columns:
        tb[col] = tb[col].astype("category")
tb = tb.format(index_columns, short_name="table_name")

Fix downstream (garden step) — read with safe_types=False to preserve categoricals:

python
tb = ds_meadow.read("table_name", safe_types=False)

safe_types=True (the default) converts categoricals back to string[pyarrow], losing all the savings.

Impact: Typically 90-99% memory reduction and 5-30x faster reads for tables with >1M rows.

2. Slow Reads Despite Categoricals

Symptom: ds.read() is fast with safe_types=False but slow with default safe_types=True.

Fix: Use safe_types=False when you don't need the type safety guarantees. Be aware that categorical columns behave slightly differently (e.g., .replace() may warn about deprecated behavior — use .cat.rename_categories() instead).

3. Row-by-Row Operations

Symptom: .apply(lambda row: ..., axis=1) or Python loops over rows showing high time.

Fix with np.select:

python
# Bad — iterates row by row
tb["result"] = tb.apply(lambda row: row["a"] if row["a"] > 0 else row["b"], axis=1)

# Good — vectorized
import numpy as np
conditions = [tb["a"] > 0]
choices = [tb["a"]]
tb["result"] = np.select(conditions, choices, default=tb["b"])

Note on origins: np.where and np.select strip OWID metadata origins. To preserve them:

python
tb["result"] = tb["b"]  # default
tb.loc[tb["a"] > 0, "result"] = tb.loc[tb["a"] > 0, "a"]
4. Expensive Groupby on Large Tables

Symptom: .groupby().sum() or .groupby().agg() taking seconds on millions of rows.

Fixes:

  • Ensure groupby columns are categorical (much faster hashing)
  • Use observed=True to skip unused category combinations
  • Use as_index=False to avoid expensive multi-index creation
  • Never mix lambdas with string aggregations in .agg() — a single callable forces pandas off its fast C path, causing ~10× slowdown on ALL aggregations (including the string ones like "sum"). Split into two separate groupby calls instead.
python
# Good
tb.groupby(["country", "year", "sex"], as_index=False, observed=True)["value"].sum()

# Bad — lambda poisons the entire agg call
tb.groupby(cols).agg({"value": "sum", "country": lambda x: check(x)})

# Good — separate the fast and slow aggregations
result = tb.groupby(cols).agg({"value": "sum"})
checks = tb.groupby(cols)["country"].apply(lambda x: check(x))

Known issue: geo.add_region_aggregates() (deprecated) injects a per-group lambda to check countries_that_must_have_data, which causes this slowdown whenever that list is non-empty (it skips the lambda when no checks are needed). The newer paths.regions.add_aggregates() API doesn't have this issue.

Show full SKILL.md (266 more words)Show less
5. Unnecessary Full-Table Reads

Symptom: Reading a large table but only using a few columns or a subset of rows.

Fix: Filter early. Add a SUBSET env var pattern for dev runs:

python
import os
SUBSET = os.environ.get("SUBSET")

def run():
    tb = ds_meadow.read("big_table", safe_types=False)
    if SUBSET:
        countries = [c.strip() for c in SUBSET.split(",")]
        tb = tb[tb["country"].isin(countries)]
    # ... rest of processing

Usage: SUBSET='France,Germany' .venv/bin/etlr namespace/version/dataset

6. Expensive create_dataset or ds.add

Symptom: paths.create_dataset(tables=..., check_variables_metadata=True) showing high time in profiler.

Diagnosis: Often this is profiler overhead, not real time. Verify with wall-clock:

python
t0 = time.time()
ds = paths.create_dataset(tables=tables, ...)
print(f"create_dataset: {time.time() - t0:.2f}s")

If it's genuinely slow, the cost is usually in update_metadata (YAML parsing) or ds.add (feather serialization for large tables). These are typically fixed costs and not worth optimizing unless the tables themselves are unnecessarily large.

Memory-Specific Profiling

In the --mem profile (see Quick Start), look for:

  • Spikes >100 MB on a single line — likely creating a large intermediate copy
  • Cumulative growth that never drops — objects not being freed

Quick memory check in code:

python
print(f"Memory: {tb.memory_usage(deep=True).sum() / 1e6:.0f} MB")
print(tb.dtypes)  # object dtype = memory hog

Iteration Tips

  • Always use SUBSET for profiling iterations. Never run full data until you've confirmed the fix works.
  • Use etl d profile for measuring, not etlr — the latter has overhead from change detection, dependency resolution, and dataset saving that drowns out the signal.
  • Small SUBSET for correctness (2-3 values), medium SUBSET for timing (10-15 values). Only go bigger if the bottleneck doesn't show up at small scale.
  • -f function_name to drill into specific functions. Only works for functions defined in the step's main module, not imported ones.

Checklist Before Optimizing

  • Profiled with actual data (not guessing)
  • Identified top 3 bottleneck lines by % time
  • Checked feather schema for string vs dictionary columns
  • Checked safe_types setting on large table reads
  • Verified with wall-clock timing (not just profiler)
  • Re-profiled after each fix to confirm improvement

© owid, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

Just SKILL.md in .claude/skills/profile-etl-step of owid/etl.

Open the folder on GitHubat commit 70c9705

Compare with similar skills

Profile Etl Step next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

Profile Etl Step compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Profile Etl Step this skillowid/etl159—~2.1kAutomated safety check: PassMIT
Crawl4AI Web Scrapingsmallnest/goclaw5991 repos~2.5kAutomated safety check: PassMIT
Glue 09 10 Migrationaws-samples/aws-glue-samples1.5k—~2.4kAutomated safety check: PassMIT-0
Migrate Glue Devendpoint To Interactive Sessionsaws-samples/aws-glue-samples1.5k—~3.6kAutomated safety check: PassMIT-0
Dbt Databricks PR Readydatabricks/dbt-databricks380—~2.8kAutomated safety check: PassApache-2.0
Apache Spark EngineerJeffallan/claude-skills12k1 repos~1.7kAutomated safety check: PassMIT

Similar skills

  • Crawl4AI Web Scraping

    smallnest/goclaw

    Scrapes sites, handles JavaScript-heavy pages and extracts structured data with Crawl4AI, through its crwl CLI or Python SDK, including schema-based extraction without an LLM.

    599 GitHub starsUsed in 1 repo~2.5k tokens
    Data & AnalyticsAuto-check passed
  • Glue 09 10 Migration

    aws-samples/aws-glue-samples

    Official

    Upgrade an AWS Glue ETL job from Glue version 0.9 or 1.0 to Glue 4.0.

    1.5k GitHub stars~2.4k tokensUpdated 1 mo ago
    Data & AnalyticsAuto-check passed
  • Official

    Migrate a legacy AWS Glue development endpoint to a Glue interactive session, following the official AWS migration checklist.

    1.5k GitHub stars~3.6k tokensUpdated 1 mo ago
    Data & AnalyticsAuto-check passed
  • Dbt Databricks PR Ready

    databricks/dbt-databricks

    Official

    A skill your agent uses for an open dbt-databricks pull request, including your own PR or a fork PR, to assess merge readiness and optionally repair selected gaps on the PR head branch.

    380 GitHub stars~2.8k tokensUpdated yesterday
    Data & AnalyticsAuto-check passed
  • Apache Spark Engineer

    Jeffallan/claude-skills

    Guides writing and tuning Apache Spark jobs: DataFrame and RDD code, Spark SQL, partitioning, caching, shuffle tuning and structured streaming.

    12k GitHub starsUsed in 1 repo~1.7k tokens
    Data & AnalyticsAuto-check passed
  • Mz Dbt Release

    MaterializeInc/materialize

    Cut a dbt-materialize PyPI release: bump the version in version.py and setup.py, date the Unreleased CHANGELOG entry, and open the release PR with a Ship: <url body.

    6.4k GitHub stars~1.2k tokensUpdated today
    Data & AnalyticsAuto-check passed

More from owid/etl

All 35 skills in this repo
  • Find every OWID surface that references a chart, indicator, MDIM, or explorer — articles (links vs embeds), explorers, narrative charts, data insights, static viz, key-chart slots, MDIM views.

    159 GitHub stars~4.8k tokensUpdated today
    Auto-check passed
  • Add a scatter view (with GDP per capita on x) to existing OWID charts via the admin API, mirroring the admin UI's "Add scatter type" defaults, then retire the old standalone "X vs.

    159 GitHub stars~18k tokensUpdated today
    Auto-check passed
  • Add new survey question codes (e.g. An agent skill from owid/etl.

    159 GitHub stars~10k tokensUpdated today
    Auto-check: notes
  • Build or refresh an OWID static visualization end to end — resolve what data it needs from an old static viz image, an indicator, or a grapher chart; check both the ETL catalog and the producer's…

    159 GitHub stars~7.8k tokensUpdated today
    Auto-check passed
  • Propose redirects from (soon-to-sunset) grapher charts to the matching views of published MDIMs.

    159 GitHub stars~9.2k tokensUpdated today
    Auto-check: notes
  • Take (soon-to-sunset) OWID explorers to redirected MDIMs, end to end.

    159 GitHub stars~7.2k tokensUpdated today
    Auto-check: notes

Questions about Profile Etl Step

What does Profile Etl Step do?

Profile and optimize ETL step performance — CPU time, memory usage, and I/O bottlenecks. Profile Etl Step is an agent skill from owid/etl. Profile and optimize ETL step performance — CPU time, memory usage, and I/O bottlenecks.

When should I use Profile Etl Step?

Profile Etl Step fits situations like: an ETL step is slow; uses too much memory; the user asks to profile; speed up a step.

How do I install Profile Etl Step in Claude Code?

Run `npx skills add owid/etl --skill profile-etl-step -a claude-code`. Or copy the skill folder (.claude/skills/profile-etl-step in owid/etl) into .claude/skills/profile-etl-step in your project. Claude Code loads it when a task matches its description.

How do I install Profile Etl Step in Codex?

Run `npx skills add owid/etl --skill profile-etl-step -a codex`. Or copy the skill folder (.claude/skills/profile-etl-step in owid/etl) into .agents/skills/profile-etl-step in your project. Codex loads it when a task matches its description.

Can I use Profile Etl Step in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add owid/etl --skill profile-etl-step -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/profile-etl-step, .gemini/skills/profile-etl-step, .github/skills/profile-etl-step and .opencode/skills/profile-etl-step in your project.

What does Profile Etl Step need to run?

SKILL.md names no scripts, command-line tools or credentials: Profile Etl Step is instructions for the agent only. Our summary lists: Python 3.

Does Profile Etl Step access the network?

SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.

Is Profile Etl Step safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.

What licence does Profile Etl Step use?

Profile Etl Step is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Profile Etl Step use?

About 2.1k tokens (SKILL.md is roughly 8.3k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to Profile Etl Step?

Skills that share tags, products or a category with Profile Etl Step: Crawl4AI Web Scraping (smallnest/goclaw, 599 stars), Glue 09 10 Migration (aws-samples/aws-glue-samples, 1.5k stars), Migrate Glue Devendpoint To Interactive Sessions (aws-samples/aws-glue-samples, 1.5k stars) and Dbt Databricks PR Ready (databricks/dbt-databricks, 380 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Profile Etl Step?

owid (a GitHub organization) maintains it in owid/etl, which has 159 GitHub stars. The repository holds 35 skills in this directory. The repository was last updated on October 10, 2026.

Source: owid/etl on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.