hpc
Skill7 starsChanged 53 days agoApache-2.0View on GitHub
High Performance Computing principles applied to Python data pipelines. Guides vectorization, parallelism, memory management, data locality, compact types, and I/O optimization with pandas/numpy. Use when optimizing data pipeline performance, reducing memory usage, or writing compute-intensive code.
- Installs packages
4 other projects use a version of this.
Adopted by
Most recently taken up by
- MusserLab/lab-claude-skillstheir own version, first seen Oct 13, 2026
- lacerbi/dotclaudetheir own version, first seen Sep 5, 2026
- openmodelingfoundation/skillstheir own version, first seen Aug 1, 2026
- MGIBiominers/claude_skillstheir own version, first seen Jun 19, 2026
Other versions4 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 4
- openmodelingfoundation/skills★ 5same name
- MusserLab/lab-claude-skills★ 4same name
- lacerbi/dotclaude★ 2same name
- MGIBiominers/claude_skills★ 0same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository10 other files it gives its agents.
- bend-formalizerSkill
- bendSkill
- dodSkill
- dopSkill
- okfSkill
- pr-reviewerSkill
- quality-testingSkill
- security-auditSkill
- simulate-user-testingSkill
- theorem-oriented-programmingSkill
The file
We split this skill into the rules it gives an agent
11 rules. 0 are common, 4 are distinctive, and 7 are very specific to this project.
0
Common
Most of the 4 edited versions we read keep these.
4
Distinctive
This author's own choices. Worth a look.
7
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 22
Avoid object columns with Python lists — prefer contiguous NumPy arrays
- Very specific: line 29
Never iterate over rows — use column operations, np.where, np.einsum
- Distinctive: line 149
When it matters: When your pipeline has independent operations that can run simultaneously — either multiple I/O calls (API requests, file …
- Distinctive: line 195
Process in chunks: when the full dataset doesn't fit in RAM.
- Very specific: line 229
Always specify
columns=when reading Parquet: If you need 3 of 30 columns, you read 10% of the data from disk. - Distinctive: line 254
When it matters: When your dataset is large enough that eager execution (computing each step immediately) becomes wasteful — either because …
- Distinctive: line 367
Measure before AND after: every optimization to validate the gain is real and significant.
- Very specific: line 407
What to do instead: Use column-level operations (arithmetic, .str accessor, np.where, boolean indexing).
- Very specific: line 422
What to do instead: Check if the operation can be expressed with pandas built-in methods (.str, .dt, arithmetic, np.where).
- Very specific: line 462
Why it's bad: Default float64 uses 8 bytes per value and int64 uses 8 bytes per value.
- Very specific: line 481
What to do instead: Always specify columns= when reading Parquet.
“Common” is measured: kept by at least 6 in 10 of the 4 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
High Performance Computing (HPC) for Data Pipelines
What It Is
High Performance Computing (HPC) is the discipline of maximizing computational throughput through optimizations in memory, parallelism, vectorization, and I/O. In data pipelines, HPC is not about clusters or supercomputers — it's about extracting maximum performance from available hardware by eliminating CPU, memory, and I/O waste.
HPC complements DOP: while DOP defines the structure of code (purity, immutability, composition), HPC defines the efficiency of execution (locality, vectorization, parallelism).
Core Principles
1. Data Locality
- Data accessed together should be stored together in memory
- Columnar layout (SoA — Struct of Arrays) is superior to row-based (AoS) for analytics
- Avoid
objectcolumns with Python lists — prefer contiguous NumPy arrays - Each individual
.assign()allocates a new DataFrame; grouping assignments reduces copies
2. Vectorization (SIMD)
- Operations on entire arrays via NumPy/pandas are 10-100x faster than Python loops
- The Python interpreter has overhead per iteration; vectorization eliminates this overhead
str.contains()in pandas operates in C;for row in df.iterrows()runs in pure Python- Never iterate over rows — use column operations,
np.where,np.einsum
3. Parallelism
- Data Parallelism: same operation applied to different data partitions simultaneously
- Task Parallelism: independent operations executed in parallel
- Pipeline Parallelism: independent pipeline stages can be overlapped
- Python's GIL blocks threads for CPU-bound work; use processes for real parallelism
4. Memory Management
- Immutability has a cost: each copy consumes RAM
- Copy-on-Write (CoW) maintains immutable semantics without physical copy until actual mutation
- Explicit
gc.collect()afterdelof large DataFrames frees memory immediately - Process in chunks when data exceeds available RAM
5. I/O Optimization
- Columnar storage (Parquet) allows reading only necessary columns — O(columns read)
- Partitioning by filter keys (date, entity_id) enables partition pruning
- Compression reduces disk/network I/O;
zstdbalances ratio vs speed
6. Lazy Evaluation
- Eager evaluation executes each operation immediately (default pandas)
- Lazy evaluation builds a plan and optimizes before executing (Polars, Spark)
- Automatic optimizations: predicate pushdown, column pruning, operator fusion
7. Efficient Linear Algebra
- BLAS (Basic Linear Algebra Subprograms) uses hardware SIMD instructions
np.einsumis more efficient than loops for batch dot products- For high-dimensional neighbor search: ANN (FAISS, annoy) is O(log n) vs O(n²)
8. Compact Data Types
float64uses 8 bytes;float32uses 4 bytes — 50% less RAM for normalized metricspd.Categoricalfor low-cardinality columns (labels, source) — ~90% less RAM- Downcasting reduces memory footprint without information loss
9. Profile Before Optimizing
- Amdahl's Law: maximum speedup is limited by the non-parallelizable fraction
- Optimize the real bottleneck (identified by profiling), not what "seems" slow
- Measure before and after each optimization to validate gains
Patterns
1. Data Locality — Memory Layout
When it matters: Any time you work with arrays, matrices, or DataFrames. Data that is accessed together should be stored together in contiguous memory. This is critical for CPU cache efficiency — accessing scattered memory (pointer chasing) is orders of magnitude slower than sequential access.
How to optimize:
- Prefer columnar layout (Struct of Arrays) over row-based (Array of Structs). DataFrames are naturally columnar; keep it that way. Never store Python lists inside DataFrame cells (creates scattered heap objects).
- Store numerical collections as contiguous NumPy arrays (
np.ndarray) rather than Python lists or lists-of-lists. NumPy arrays sit in a single contiguous block that the CPU can prefetch. - Batch column assignments into a single
.assign()call. Each individual.assign()allocates an entire new DataFrame. N calls = N copies; 1 call with N keywords = 1 copy. - Use Arrow-backed string types (
"string[pyarrow]") instead of the defaultobjectdtype for string columns. Arrow strings are stored in contiguous buffers rather than as scattered Pythonstrobjects.
import numpy as np
import pandas as pd
# ❌ Scattered: object column with Python lists (each row is a separate heap object)
df["vector"] = df["text"].apply(lambda t: some_function(t).tolist())
# ✅ Contiguous: external ndarray with shape (n, dim)
vectors = np.stack([some_function(t) for t in df["text"]], dtype=np.float32)
# ❌ Multiple copies: each .assign() allocates a new DataFrame
df = df.assign(a=va).assign(b=vb).assign(c=vc).assign(d=vd) # 4 copies
# ✅ Single copy: batch assign
df = df.assign(a=va, b=vb, c=vc, d=vd) # 1 copy
# ✅ Arrow-backed strings for better memory locality
df["name"] = df["name"].astype("string[pyarrow]")2. Vectorization — Eliminate Python Loops
When it matters: Every time you need to apply an operation to all rows/elements of a column or array. The Python interpreter has significant per-iteration overhead (~100ns per iteration). Vectorized operations delegate the loop to C/Fortran and process the entire array in one call.
How to optimize:
- Never use
iterrows()oritertuples()for computation. They create a Python object per row and are 50-100x slower than vectorized equivalents. - Replace
apply(lambda)with native pandas/numpy operations whenever possible. The.straccessor, arithmetic operators,np.where, and boolean indexing are all vectorized. - Use
np.einsumfor multi-dimensional operations that would otherwise require nested loops (dot products, outer products, traces over specific axes). - Use
str.contains()with regex for pattern matching — it runs in C internally. A Python loop checking substrings one by one is dramatically slower.
# ❌ Loop: Python overhead per row
totals = []
for _, row in df.iterrows():
totals.append(row["price"] * row["qty"])
df["total"] = totals
# ✅ Vectorized: single C-level operation on entire columns
df["total"] = df["price"] * df["qty"]
# ❌ apply: runs Python function per element
df["length"] = df["text"].apply(lambda t: len(t.split()))
# ✅ Vectorized string accessor: C-optimized
df["length"] = df["text"].str.split().str.len()
# ❌ Loop for conditional logic
labels = []
for val in df["score"]:
labels.append("high" if val > 0.8 else "low")
# ✅ Vectorized conditional
df["label"] = np.where(df["score"] > 0.8, "high", "low")
# ❌ Loop for regex classification
matches = []
for text in df["content"]:
matches.append(bool(re.search(pattern, text)))
# ✅ Vectorized regex (runs in C)
df["matches"] = df["content"].str.contains(pattern, na=False)3. Parallelism — Exploit Data Independence
When it matters: When your pipeline has independent operations that can run simultaneously — either multiple I/O calls (API requests, file reads) or CPU-intensive work on independent data partitions.
How to optimize:
- ThreadPoolExecutor for I/O-bound work (API calls, file reads, network requests). The GIL is released during I/O, so threads provide real concurrency.
- ProcessPoolExecutor for CPU-bound work (numerical computation, data transformation). Each process has its own interpreter and GIL, enabling true parallelism on multi-core CPUs.
- asyncio for high-concurrency I/O (hundreds/thousands of simultaneous requests). Lower overhead than threads when managing many connections.
- Identify independent pipeline stages and run them concurrently. If step A and step B don't depend on each other's output, they can execute in parallel.
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
import asyncio
# Task parallelism: two independent I/O operations
with ThreadPoolExecutor(max_workers=2) as pool:
future_a = pool.submit(load_dataset, "source_a.parquet")
future_b = pool.submit(load_dataset, "source_b.parquet")
data_a = future_a.result()
data_b = future_b.result()
# Data parallelism: same transform on independent chunks (CPU-bound)
def transform_chunk(chunk: pd.DataFrame) -> pd.DataFrame:
return chunk.pipe(normalize).pipe(compute_features)
chunks = np.array_split(df, num_cores)
with ProcessPoolExecutor(max_workers=num_cores) as pool:
results = list(pool.map(transform_chunk, chunks))
df_result = pd.concat(results, ignore_index=True)
# Async I/O: many concurrent network requests
async def fetch_all(urls: list[str]) -> list[dict]:
async with httpx.AsyncClient() as client:
tasks = [client.get(url) for url in urls]
responses = await asyncio.gather(*tasks)
return [r.json() for r in responses]4. Memory Management
When it matters: In multi-step pipelines where each step produces a new DataFrame. Without explicit cleanup, intermediate results accumulate and can exhaust available RAM. This is especially important when working with immutable data (DOP pattern) since each transformation creates a new copy.
How to optimize:
- Delete intermediate DataFrames with
delfollowed bygc.collect()as soon as they're no longer needed. Python's garbage collector may not reclaim memory immediately otherwise. - Enable Copy-on-Write (
pd.set_option("mode.copy_on_write", True)) to get immutable semantics without paying for physical copies until a mutation actually occurs. - Consolidate boolean filters into a single combined mask before applying. Each
df[mask]creates a new DataFrame; combining N masks into one reduces N allocations to 1. - Process in chunks when the full dataset doesn't fit in RAM. Read and process partitions sequentially, accumulating results.
import gc
import pandas as pd
# Enable CoW globally: immutable semantics without physical copies
pd.set_option("mode.copy_on_write", True)
# ✅ Explicit memory release between pipeline stages
df_clean = clean(df_raw)
del df_raw
gc.collect()
df_features = extract_features(df_clean)
del df_clean
gc.collect()
# ✅ Consolidated filters: one allocation instead of many
mask = (df["amount"] > 0) & (df["status"] == "active") & (df["date"] >= cutoff)
df_filtered = df[mask].copy()
# ❌ Sequential filters: multiple copies
df = df[df["amount"] > 0] # copy 1
df = df[df["status"] == "active"] # copy 2
df = df[df["date"] >= cutoff] # copy 35. I/O Optimization — Columnar Storage
When it matters: Whenever reading or writing tabular data to disk. CSV is row-oriented and requires reading the entire file. Columnar formats (Parquet, Arrow IPC) allow reading only the columns and row groups you need, dramatically reducing I/O.
How to optimize:
- Always specify
columns=when reading Parquet. If you need 3 of 30 columns, you read 10% of the data from disk. - Use compression (
zstdfor best ratio,snappyfor speed). Compressed files are smaller on disk, so less I/O even though decompression costs some CPU. - Partition large datasets by commonly-filtered keys (date, region, entity type). Query engines can skip entire partitions (partition pruning).
- Use predicate pushdown with
filters=parameter when reading partitioned Parquet directories. Rows are filtered at the storage level before loading into memory.
# ✅ Column pruning: read only what you need
df = pd.read_parquet("data.parquet", columns=["id", "date", "amount"])
# ✅ Write with compression
df.to_parquet("output.parquet", index=False, compression="zstd")
# ✅ Partitioning by filter key
df.to_parquet("data/", index=False, partition_cols=["year", "month"], compression="zstd")
# ✅ Predicate pushdown: filter at storage level
df = pd.read_parquet(
"data/",
filters=[("year", "==", 2025), ("month", ">=", 6)],
columns=["id", "amount"],
)6. Lazy Evaluation
When it matters: When your dataset is large enough that eager execution (computing each step immediately) becomes wasteful — either because intermediate results are never fully used, or because the query engine can optimize the execution plan (reorder operations, push filters down, prune columns).
How to optimize:
- Use Polars lazy mode (
pl.scan_parquet,pl.LazyFrame) for datasets that benefit from plan optimization. The engine automatically applies predicate pushdown, column pruning, and operator fusion. - In pandas, use
.query()for complex filters — it can be faster than chained boolean indexing because it avoids materializing intermediate boolean arrays. - Defer computation to the end — build up your transformation plan, then call
.collect()once. This gives the optimizer maximum freedom to rearrange and fuse operations.
import polars as pl
# Lazy plan: no execution until .collect()
result = (
pl.scan_parquet("data/*.parquet")
.filter(pl.col("date") >= "2025-01-01")
.select(["id", "amount", "category"])
.group_by("category")
.agg([
pl.col("amount").mean().alias("avg_amount"),
pl.col("amount").sum().alias("total"),
pl.len().alias("count"),
])
.collect() # optimized execution happens here
)
# In pandas: query() avoids intermediate boolean arrays
df_filtered = df.query("amount > 100 and status == 'active' and date >= @cutoff")7. Efficient Linear Algebra
When it matters: Any time you compute distances, similarities, projections, or aggregations over vectors/matrices — common in ML pipelines, recommendation systems, embedding comparisons, and feature engineering. Python loops over matrix elements are catastrophically slow compared to BLAS-backed operations.
How to optimize:
- Use
np.einsumfor batch operations (dot products across rows, element-wise products with reduction). It computes in a single C-level pass over contiguous memory. - Normalize vectors once, then use dot product for cosine similarity. Normalizing is O(n) and dot product on normalized vectors equals cosine similarity, avoiding repeated norm computation.
- For nearest-neighbor search at scale (>50k vectors), use Approximate Nearest Neighbors (FAISS, Annoy, ScaNN). Exact brute-force is O(n²); ANN indexes are O(log n) or O(1) amortized.
- Store vectors as contiguous
np.ndarray(not lists). BLAS operations require contiguous memory to use SIMD instructions.
import numpy as np
# ❌ Python loop: computes one similarity per iteration
similarities = []
for i in range(n):
sim = np.dot(a[i], b[i]) / (np.linalg.norm(a[i]) * np.linalg.norm(b[i]))
similarities.append(sim)
# ✅ einsum: batch dot product in one C-level pass
dot = np.einsum("ij,ij->i", a, b)
norm_a = np.linalg.norm(a, axis=1)
norm_b = np.linalg.norm(b, axis=1)
similarities = np.where((norm_a > 0) & (norm_b > 0), dot / (norm_a * norm_b), 0.0)
# ✅ Pre-normalize + dot = cosine (avoids recomputing norms)
a_norm = a / np.linalg.norm(a, axis=1, keepdims=True)
b_norm = b / np.linalg.norm(b, axis=1, keepdims=True)
similarities = np.einsum("ij,ij->i", a_norm, b_norm)
# ✅ ANN for large-scale search (O(log n) vs O(n²))
import faiss
index = faiss.IndexFlatIP(dim)
faiss.normalize_L2(vectors)
index.add(vectors)
distances, indices = index.search(query, k=10)
# ✅ Binary format for fast load/save of arrays
np.save("vectors.npy", vectors)
vectors = np.load("vectors.npy")8. Compact Data Types
When it matters: Whenever your DataFrame uses more memory than necessary for the information it holds. The default float64 and int64 types use 8 bytes per value regardless of the actual range. Downcasting to appropriate types can reduce memory 50-90% without information loss.
How to optimize:
float32for bounded metrics (scores, ratios, percentages in [0,1] or [0,100]). 32-bit precision (~7 significant digits) is more than enough for most analytics. Saves 50% RAM.pd.Categoricalfor low-cardinality string columns (status, category, label, source). Internally stored as integer codes + a small dictionary. Saves ~90% RAM for columns with < 100 distinct values.int16orint32for bounded counts (word counts, page views, sequence indices). If max value < 32,767,int16uses 2 bytes vs 8 forint64(75% savings).- Apply downcasting after all transformations — avoid casting back and forth during intermediate steps.
# Float downcasting: 50% savings for normalized metrics
float_cols = ["score", "ratio", "similarity", "confidence"]
for col in float_cols:
df[col] = df[col].astype("float32")
# Categorical: ~90% savings for low-cardinality strings
cat_cols = ["status", "category", "region", "type"]
for col in cat_cols:
df[col] = df[col].astype("category")
# Integer downcasting: 75% savings for small counts
int_cols = ["page_views", "word_count", "sequence_idx"]
for col in int_cols:
df[col] = pd.to_numeric(df[col], downcast="integer")
# Memory estimation for 100k rows × 25 columns:
# Default (all float64/object): ~20-50MB
# After optimization: ~5-10MB9. Profiling and Benchmarking
When it matters: ALWAYS before optimizing. Amdahl's Law states that the maximum speedup from optimizing a component is limited by the fraction of total time that component represents. Optimizing a step that takes 2% of total runtime can never yield more than 2% improvement regardless of how much faster you make it.
How to optimize:
- Measure wall-clock time per pipeline step to identify the actual bottleneck. Use
time.perf_counter()(nottime.time()) for sub-millisecond precision. - Use
%%timeitin notebooks for micro-benchmarking individual functions or expressions. - Use
memory_profilerto identify which functions allocate the most memory — essential for finding the step causing OOM errors. - Measure before AND after every optimization to validate the gain is real and significant.
import time
def timed_step(name: str, fn, *args, **kwargs):
"""Measure execution time and row count of a pipeline step."""
start = time.perf_counter()
result = fn(*args, **kwargs)
elapsed = time.perf_counter() - start
n = result.shape[0] if hasattr(result, "shape") else len(result)
print(f" [{name}] {elapsed:.2f}s | {n:,} rows")
return result
# Usage: wrap each pipeline step
df_clean = timed_step("clean", clean_data, df_raw)
df_features = timed_step("features", compute_features, df_clean)
df_final = timed_step("aggregate", aggregate, df_features)
# In notebooks:
# %%timeit
# expensive_function(data)
# Memory profiling:
# pip install memory-profiler
# python -m memory_profiler my_pipeline.py
# Line-level profiling:
# pip install line-profiler
# kernprof -l -v my_pipeline.pyAnti-Patterns to Avoid
Row-level iteration
Why it's bad: iterrows() creates a Python Series object for every single row, adding ~100x overhead compared to vectorized operations. The CPU cannot leverage SIMD or cache prefetching because each iteration goes through the Python interpreter.
What to do instead: Use column-level operations (arithmetic, .str accessor, np.where, boolean indexing). If the logic genuinely cannot be vectorized, consider itertuples() (namedtuples are cheaper than Series) or move the logic to Cython/Numba.
# ❌ iterrows
for idx, row in df.iterrows():
result.append(row["price"] * row["qty"])
# ✅ Vectorized
df["total"] = df["price"] * df["qty"]apply with pure Python lambdas
Why it's bad: apply() runs a Python function per element/row. It does not vectorize — it's syntactic sugar over a loop. The overhead is especially severe on large DataFrames (millions of rows).
What to do instead: Check if the operation can be expressed with pandas built-in methods (.str, .dt, arithmetic, np.where). If the function does complex logic that truly can't be vectorized, consider numba.jit or writing a ufunc.
# ❌ apply
df["length"] = df["text"].apply(lambda t: len(t.split()))
# ✅ Vectorized str accessor
df["length"] = df["text"].str.split().str.len()Chained .assign() calls
Why it's bad: Each .assign() allocates an entirely new DataFrame (full copy of all columns). Chaining N calls means N full copies, where only 1 is necessary.
What to do instead: Pass all new columns as keyword arguments in a single .assign() call.
# ❌ 4 copies
df = df.assign(a=1).assign(b=2).assign(c=3).assign(d=4)
# ✅ 1 copy
df = df.assign(a=1, b=2, c=3, d=4)Vectors stored as Python lists inside DataFrames
Why it's bad: A column with dtype object containing Python lists scatters each list across the heap. There's no memory contiguity, so NumPy/BLAS operations cannot be applied, and every access incurs pointer indirection and Python object overhead.
What to do instead: Store vectors externally as a contiguous np.ndarray with shape (n, dim). Reference by index if you need to associate with DataFrame rows.
# ❌ Scattered heap objects
df["vector"] = df["text"].apply(lambda t: encode(t).tolist())
# ✅ Contiguous array, separate from DataFrame
vectors = np.stack([encode(t) for t in df["text"]], dtype=np.float32)Oversized data types
Why it's bad: Default float64 uses 8 bytes per value and int64 uses 8 bytes per value. For normalized metrics in [0,1], float64 provides 15 digits of precision when 7 suffices. For counts under 1000, int64 uses 4x more memory than needed. This directly impacts cache utilization and memory bandwidth.
What to do instead: Downcast after all transformations are complete. Use float32 for bounded metrics, int16/int32 for small counts, and pd.Categorical for low-cardinality strings.
# ❌ Defaults waste memory
# float64 for scores [0,1] → 8 bytes (needs only 4)
# int64 for counts [0,200] → 8 bytes (needs only 2)
# ✅ Right-sized types
df["score"] = df["score"].astype("float32") # 50% savings
df["count"] = df["count"].astype("int16") # 75% savings
df["category"] = df["category"].astype("category") # ~90% savingsReading entire files when you need a subset
Why it's bad: Reading all columns from a Parquet file when you only need 2-3 of them multiplies I/O and memory usage by the ratio of total columns to needed columns. For a 30-column file where you need 3, you're doing 10x unnecessary I/O.
What to do instead: Always specify columns= when reading Parquet. Use filters= for row-level predicate pushdown on partitioned datasets.
# ❌ Reads all 30 columns, then discards 28
df = pd.read_parquet("data.parquet")
subset = df[["id", "date"]]
# ✅ Reads only 2 columns from disk
subset = pd.read_parquet("data.parquet", columns=["id", "date"])Rules for the Agent
When optimizing performance:
- Profile first — never optimize without measuring the real bottleneck
- Vectorize —
str.contains(),np.einsum,np.where> Python loops - Parallelize independent operations — ThreadPool for I/O, ProcessPool for CPU
- Reduce allocations — consolidate masks, batch assign, enable CoW
- Downcast types — float32 for metrics, int16 for small counts, Categorical for enums
- Column pruning — read only necessary columns from parquets
- Release memory early —
del+gc.collect()between pipeline steps - Store embeddings as ndarray — never as object column with lists
Decision hierarchy:
| Conflict | Priority |
| Purity vs Performance | HPC within function scope; purity at the interface |
| Immutability vs Memory | CoW resolves both; del + gc.collect() between steps |
| Readability vs Vectorization | Vectorize with clear docstring; keep readable version commented |
| Parallelism vs Simplicity | Parallelize only what profiling showed as bottleneck |
When to apply each technique:
| Data Size | Recommended Approach |
| < 10k rows | Vectorized pandas is sufficient |
| 10k - 1M rows | + compact types, column pruning, batch assign |
| 1M - 10M rows | + chunked processing, parallelism, Polars lazy |
| > 10M rows | + partitioned parquet, Spark/Dask, ANN for search |
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

