pandas-expert
Writes correct, vectorized pandas for cleaning, joining, reshaping, and aggregating tabular data, with validated joins and deliberate dtype and missing-data handling. Use when someone asks "why did my merge duplicate rows", "how do I clean this CSV in pandas", "my groupby numbers look wrong", "this apply is too slow", or is transforming a DataFrame for analysis or a pipeline. Do NOT use for first-pass profiling of an unfamiliar dataset - use eda-playbook instead; for data too large for one machine use spark-jobs; for answering business questions directly in SQL use sql-to-insights.
- License 许可
- MIT
- 最近更新
- 2026-10-14
- Stars
- ★ 25
- Forks
- 3
01 前言 Front Matter
- name
- pandas-expert
- metadata
- { "title": "Pandas Expert" }
- description
- Writes correct, vectorized pandas for cleaning, joining, reshaping, and aggregating tabular data, with validated joins and deliberate dtype and missing-data handling. Use when someone asks "why did my merge duplicate rows", "how do I clean this CSV in pandas", "my groupby numbers look wrong", "this apply is too slow", or is transforming a DataFrame for analysis or a pipeline. Do NOT use for first-pass profiling of an unfamiliar dataset - use eda-playbook instead; for data too large for one machine use spark-jobs; for answering business questions directly in SQL use sql-to-insights.
03 SKILL.md 正文
Pandas Expert
Most pandas bugs are silent: a join that fans out rows, a dtype that upcast to object, a NaN that vanished from a sum. The output looks plausible and is wrong. This skill produces transformations that validate themselves - every join asserted, every coercion counted, every aggregate reconciled - so errors surface at the line that caused them instead of in a dashboard three weeks later.
Operating procedure
Follow the steps in order. Types must be fixed before cleaning, cleaning before joining, joining before aggregating - each later step silently produces wrong numbers if an earlier one was skipped.
Step 1: gather inputs
Before writing any transformation, establish:
- The grain of each table: what one row represents. If the user cannot state it, derive it with
df.duplicated(subset=candidate_keys).sum()and label the result a guess until confirmed. - The expected output: shape, grain, and one or two spot-check numbers the user already trusts (last month's total, a known customer's count).
- Whether the source can be mutated. Default to no: work on a copy, never mutate the caller's DataFrame.
- Approximate size. Pandas wants roughly 3-5x the on-disk dataset size in free RAM for comfortable transformation work. Above a few GB in memory, reach for chunked reads (
pd.read_csv(..., chunksize=...)), pyarrow-backed dtypes, or hand off to spark-jobs.
Step 2: inspect before touching
Run df.info(), df.describe(), df.isna().sum(), and df.nunique() first. You are looking for: object columns that should be numeric or datetime, null counts, and cardinality. This is a two-minute sanity pass, not a full audit - for a genuinely unfamiliar dataset, run eda-playbook first.
Step 3: fix dtypes early
- Parse dates with
pd.to_datetime; passformat=when known (it is faster and catches malformed rows),format="mixed"only when formats genuinely vary. - Coerce numerics with
pd.to_numeric(col, errors="coerce"), then count and report what failed:col.isna().sum(). Silent coercion is how "N/A" strings become invisible revenue holes. - Cast low-cardinality strings to
categorywhen distinct values are under roughly 50% of row count - it cuts memory and speeds groupbys. - Downcast large numeric columns (
pd.to_numeric(col, downcast="integer")) when memory is tight.
Step 4: handle missing data deliberately
Choose drop, fill, or flag - and state which and why in a comment. If more than ~5% of a key column is null, stop and ask whether the nulls are structural (a pipeline branch) before imputing; that determination changes the right treatment entirely.
Step 5: join with validation, never on faith
- Always state
how=andon=explicitly. - Always pass
validate=("one_to_one", "many_to_one", "many_to_many") so pandas raises on unexpected key duplication instead of silently fanning out. - After the join, assert the row count: a left join must preserve
len(left). - Check match rate with
indicator=Truewhen a low match rate would be a data problem.
Step 6: reshape and aggregate
pivot_tablefor long→wide,meltfor wide→long.groupby().agg()with named aggregations -agg(revenue=("amount", "sum"))- so output columns are self-describing.- Remember
sum()skips NaN silently; if a NaN-contaminated group should show as missing, usemin_count=1.
Step 7: verify the output
Reconcile at least one aggregate against the pre-transformation source (total rows, total revenue). Show the transformation step by step with a comment on each step explaining intent, not mechanics.
Worked example
Messy orders joined to a customer table with a duplicate key - the validation catches it:
import pandas as pd
orders = pd.DataFrame({
"order_id": [1001, 1002, 1003, 1004, 1005],
"customer_id": ["C01", "C02", "C01", "C03", "C02"],
"amount": ["120.50", "80.00", "N/A", "45.25", "200.00"],
})
customers = pd.DataFrame({
"customer_id": ["C01", "C02", "C03", "C03"], # C03 duplicated upstream
"region": ["West", "East", "South", "South-dup"],
})
# Coerce and count failures instead of letting them vanish
orders["amount"] = pd.to_numeric(orders["amount"], errors="coerce")
print(orders["amount"].isna().sum(), "amounts failed to parse")
# validate= raises instead of silently duplicating order 1004
try:
orders.merge(customers, on="customer_id", how="left", validate="many_to_one")
except pd.errors.MergeError as e:
print("MergeError:", str(e).splitlines()[0])
customers = customers.drop_duplicates("customer_id", keep="first")
merged = orders.merge(customers, on="customer_id", how="left", validate="many_to_one")
assert len(merged) == len(orders)
summary = (
merged.groupby("region", as_index=False)
.agg(orders=("order_id", "count"), revenue=("amount", "sum"))
.sort_values("revenue", ascending=False)
)
print(summary.to_string(index=False))
Output:
1 amounts failed to parse
MergeError: Merge keys are not unique in right dataset; not a many-to-one merge
region orders revenue
East 2 280.00
West 2 120.50
South 1 45.25
Read it: the "N/A" string was counted, not swallowed; the duplicate C03 row raised at the merge line instead of inflating South's revenue; West shows 2 orders but only 120.50 revenue because the failed parse became NaN and sum skipped it - a fact the failure count already disclosed.
Performance rules
- Avoid
applyover rows when a vectorized op,np.where, ornp.selectexists - vectorized versions are commonly 10-100x faster. - Avoid chained indexing (
df[a][b] = ...); use.loc[rows, cols]for all assignment. - Watch for silent upcasts to
objectdtype after concatenation or fillna - they destroy both speed and memory. - Prefer
mergeover row-wise lookups; prefergroupby().transformover apply-per-group when the output is row-aligned.
Deliverable
Produce a runnable, commented transformation script (or notebook cells) containing: the dtype-fixing block with coercion-failure counts, every join carrying how=, on=, validate=, and a row-count assertion, named aggregations, and a final reconciliation check against a number the user already trusts.
Do NOT
- Do not merge without
validate=- fan-out from duplicated keys is the single most common source of inflated metrics. - Do not use
errors="coerce"without counting what was coerced; a clean-looking column can hide hundreds of destroyed values. - Do not use
applywith a lambda for arithmetic a vectorized expression can do. - Do not mutate the input DataFrame in place inside a function; copy when in doubt.
- Do not trust
sum()on a column with NaN to mean "complete total" - it means "total of what parsed". - Do not profile an unknown dataset with ad-hoc snippets; run eda-playbook and come back with its decision log.
Quality bar
A finished transformation ships only when: every join is validated and row-count-asserted; every type coercion reports its failure count; at least one output aggregate reconciles to a trusted source number; no chained indexing or row-wise apply remains where a vectorized form exists; and a reader can follow the intent from comments without running the code.
04 同仓库更多 Skills
zone-2-cardio-plan
Use when someone asks to build a steady-state cardio plan, train an aerobic base or Zone 2, structure easy or endurance weekly cardio, train by heart-rate zones, or improve resting heart rate, VO2, or endurance. Builds a heart-rate-zone aerobic training block with session structure, modality choice, and progression. General fitness guidance, not medical advice. Do NOT use to design a strength or lifting program (use Strength Training Plan), high-intensity interval conditioning, HIIT, or circuits (use Conditioning & HIIT Program), or a mobility/flexibility routine (use Mobility Routine).
youtube-script-writer
Writes retention-engineered long-form YouTube scripts on a hook/body/payoff architecture - a first-30-seconds hook that survives the retention cliff, a beat-sheet body with mini-hooks and bridges, timed pattern interrupts, and a payoff that over-delivers - with B-roll and editing cues marked inline. Use when someone says "script my next video", "my retention graph dies at the start", "tighten this draft", or "outline a video on X". Do NOT use for the 3-second opener of TikTok, Reels, or Shorts - use video-hook-writer instead; do NOT use for spoken voiceover synced to on-screen tutorial actions - use narration-script instead; do NOT use for a sales demo built on a buyer's discovery notes - use demo-script instead; do NOT use to design the thumbnail and title - use thumbnail-concept instead.
win-back-campaign
Designs segmented win-back email sequences for dormant users - dormancy tiers, a three-beat sequence with a single proportional incentive, and a suppression rule - and measures success by re-activation, not opens. Use when someone says "our users went quiet", "design a win-back campaign", "how do I re-engage lapsed customers", "should we email people who haven't logged in for months", or lifecycle messaging has stopped landing with a lapsed segment. Do NOT use for recovering an unfinished checkout - use abandoned-cart-sequence instead. Do NOT use for diagnosing and fixing why active customers cancel - use churn-reduction instead. Do NOT use for building always-on onboarding or nurture flows - use email-drip-builder instead.
wikipedia-style
Writes neutral, verifiable, encyclopedic prose following Wikipedia's core content policies and manual of style - NPOV, inline citations, notability-backed claims, and a strict ban on promotional language. Use when someone asks "write this like a Wikipedia article", "draft a Wikipedia page for this company or person", "make this neutral and encyclopedic", or "why does my article draft keep getting rejected". Do NOT use for persuasive or opinion writing - use op-ed-writer instead; for thesis-driven academic papers, use academic-essay; for verifying claims in existing text, use fact-checker.
whitepapers
Structures and writes authoritative B2B whitepapers - a standalone executive summary, a data-sized problem statement, methodology, evidence-driven findings, recommendations, and a soft call to action - flagging every claim that needs a real citation. Use when a marketer asks "write a whitepaper on this topic", "turn our research into a lead-gen asset", "make this credible enough for executives", or a draft reads like a product pitch instead of analysis. Do NOT use for customer-story-driven proof - use case-study-builder instead; for policy audiences, use policy-brief.
white-space-analysis
Finds unmet market gaps by ranking jobs-to-be-done on importance versus satisfaction, overlaying them against current solutions and workarounds, and validating that each gap is real, winnable, and worth winning. Use when someone asks "where are the gaps in this market", "what's underserved here", "where should we play next", or is hunting for product, feature, or market-entry opportunities. Do NOT use for profiling competitors and building battle cards - use competitive-intelligence instead; for putting a dollar figure on an identified opportunity - use market-sizing instead; for extracting jobs-to-be-done from raw customer interviews - use jtbd-extractor instead.