Complete spreadsheet methodology — data cleanup, transformation, analysis, dashboards, automation, and reporting. Works with CSV, Excel, Google Sheets, or any tabular data. Use when the user needs to clean messy data, build reports, create dashboards, automate recurring spreadsheet tasks, or transform data between formats.
SKILL.md
Spreadsheet & Data Wrangling Master
Turn messy data into clean insights, automated reports, and decision-ready dashboards. Platform-agnostic — works with CSV, Excel, Google Sheets, or any tabular format.
What does each row represent? (one customer? one transaction? one day?)
Are there header rows? Multiple header rows? Merged cells?
What are the data types? (dates, currencies, percentages, IDs, free text)
How much is missing? Which columns?
Are there duplicates? By which key?
Is there a unique identifier column?
What date format? (MM/DD/YYYY vs DD/MM/YYYY vs YYYY-MM-DD vs mixed)
What currency/number format? (1,000.00 vs 1.000,00 vs 1000)
Where did this data come from and how often is it updated?
Phase 2: Data Cleaning Decision Tree
Step-by-Step Cleaning Protocol
START
│
├─ Headers → Normalize (lowercase, snake_case, no spaces/special chars)
│
├─ Duplicates?
│ ├─ Exact duplicates → Remove, keep first
│ ├─ Near-duplicates → Flag for review (fuzzy match on name + address)
│ └─ Intentional duplicates → Leave (e.g., multiple orders same customer)
│
├─ Missing Values?
│ ├─ <5% of column → Fill (mean for numeric, mode for categorical, forward-fill for time series)
│ ├─ 5-30% → Flag + fill with "UNKNOWN" or interpolate with justification
│ ├─ >30% → Consider dropping column or flagging as unreliable
│ └─ Entire row missing key fields → Remove with log
│
├─ Data Types?
│ ├─ Dates as text → Parse to date (try multiple formats, log failures)
│ ├─ Numbers as text → Strip currency symbols, commas, whitespace, convert
│ ├─ IDs/zips with leading zeros → Keep as text (NEVER convert to number)
│ ├─ Phone numbers → Text, standardize format
│ ├─ Mixed types in column → Split or coerce with error log
│ └─ Boolean variants → Map (Yes/No/True/False/1/0/Y/N → consistent)
│
├─ Outliers?
│ ├─ Calculate IQR: Q1 - 1.5×IQR to Q3 + 1.5×IQR
│ ├─ Business logic check (negative revenue? age 200? date in 2099?)
│ ├─ Decide: fix (typo), cap (winsorize), remove, or keep with flag
│ └─ ALWAYS log which outliers were modified and why
│
├─ Standardization?
│ ├─ Text case → Consistent (Title Case for names, UPPER for codes)
│ ├─ Whitespace → Trim leading/trailing, collapse internal
│ ├─ Categories → Map variants ("US"/"USA"/"United States" → "US")
│ ├─ Dates → ISO 8601 (YYYY-MM-DD) internally
│ ├─ Currency → Consistent symbol placement, decimal precision
│ └─ Phone/email → Validate format
│
└─ Structural Issues?
├─ Multi-header rows → Flatten to single header
├─ Merged cells → Unmerge + fill down
├─ Pivot/crosstab → Unpivot to tidy format (1 row = 1 observation)
├─ Multiple tables in one sheet → Split to separate sheets/files
└─ Metadata rows (totals, notes) → Separate from data rows
7-day moving average, rank within group, running total
Join Strategy Decision Guide
Which join do you need?
│
├─ Need ALL rows from left table → LEFT JOIN
│ (customers who may or may not have orders)
│
├─ Need ONLY matching rows → INNER JOIN
│ (only customers WITH orders)
│
├─ Need ALL rows from both → FULL OUTER JOIN
│ (reconciliation: find mismatches)
│
├─ Need everything NOT in other table → LEFT JOIN + WHERE right IS NULL
│ (customers who NEVER ordered)
│
└─ Need every combination → CROSS JOIN (rare, use carefully)
(all products × all stores for pricing matrix)
⚠️ ALWAYS check join results:
- Row count: did it explode? (many-to-many join)
- Row count: did it shrink? (keys not matching)
- NULL columns: expected from outer join, unexpected = key mismatch
Frequency distributions: how many of each category?
Time trends: daily/weekly/monthly aggregates over time
Cross-tabs: category A × category B breakdown
Diagnostic (Why did it happen?)
Drill-down: which segment drove the change?
Cohort analysis: behavior by signup month
Correlation: which variables move together?
Variance analysis: actual vs budget/forecast, by category
Predictive (What might happen?)
Trend projection: linear/exponential fit + confidence
Moving averages: 7/30/90 day smoothing
Seasonality: same-period prior year comparison
Growth rate: MoM, QoQ, YoY percentage changes
Prescriptive (What should we do?)
Pareto (80/20): which 20% of X drives 80% of Y?
Scenario analysis: best/base/worst case with different assumptions
Sensitivity: which input variable has biggest impact?
Break-even: at what point does X cover Y?
Insight Formula
Every finding MUST follow this structure:
INSIGHT: [What you found — one sentence]
EVIDENCE: [The specific numbers]
SO WHAT: [Why it matters to the business]
ACTION: [What to do about it]
CONFIDENCE: [High/Medium/Low + why]
Example:
INSIGHT: Customer acquisition cost increased 43% in Q3 vs Q2
EVIDENCE: CAC went from $47 to $67, driven by paid search CPC increase (+62%)
SO WHAT: At current LTV of $180, payback period extended from 3.1 to 4.5 months
ACTION: Shift 30% of paid search budget to email/referral channels (CAC $12 and $23 respectively)
CONFIDENCE: High — based on complete Stripe + Google Ads data for full quarter
What are you showing?
│
├─ Change over time → LINE chart (≤5 series) or AREA (stacked composition)
│
├─ Comparison across categories → BAR chart (horizontal for long labels)
│
├─ Part of whole → PIE (≤5 slices) or STACKED BAR (>5 or over time)
│
├─ Distribution → HISTOGRAM or BOX PLOT
│
├─ Relationship between 2 variables → SCATTER PLOT
│
├─ Geographic → MAP (if location data exists)
│
├─ Ranked list → HORIZONTAL BAR sorted descending
│
└─ Single KPI → BIG NUMBER with trend indicator
⚠️ NEVER use:
- 3D charts (distorts perception)
- Dual Y-axes (misleads readers)
- Pie with >7 slices (use bar instead)
- Rainbow colors (use 2-3 colors max + grey)
Phase 6: Recurring Report Automation
Automation Checklist
recurring_report:
name: ""
frequency: "" # daily, weekly, monthly, quarterly
owner: ""
recipients: []
data_sources:
- source: "" # file path, API, database
refresh: "" # how data gets updated
format: ""
processing_steps:
- step: "load data"
tool: "" # Python, Excel macro, Google Apps Script
- step: "clean"
rules: [] # reference cleaning protocol
- step: "transform"
operations: []
- step: "analyze"
calculations: []
- step: "format"
template: "" # dashboard template to populate
- step: "deliver"
method: "" # email, Slack, shared drive, API push
quality_checks:
- "Row count within expected range (±20%)"
- "No NULL values in required columns"
- "Totals reconcile with source system"
- "Date range matches expected period"
- "Key metrics pass sanity check (no 10x jumps without explanation)"
error_handling:
- trigger: "data source unavailable"
action: "use cached last-good version + alert"
- trigger: "row count outside range"
action: "pause + flag for human review"
- trigger: "metric exceeds 3× historical std dev"
action: "include anomaly callout in report"
Sheet structure:
├── Assumptions → All editable inputs in ONE place (highlighted cells)
├── Revenue → Formulas reference Assumptions
├── Costs → Formulas reference Assumptions
├── P&L → Pulls from Revenue + Costs
├── Cash Flow → Derived from P&L + working capital
├── Balance Sheet → Derived from Cash Flow
├── Scenarios → Best/Base/Worst toggle that feeds Assumptions
└── Dashboard → Charts + KPIs pulling from P&L/Cash Flow
Rules:
- Inputs = blue font or yellow background (pick one, be consistent)
- Formulas = black font, never hardcode numbers in formula cells
- Every formula traces back to Assumptions or raw data
- No circular references
- Include version number + last-updated date
Pattern 2: CRM / Contact Tracker
Required columns:
- id (auto-increment or UUID)
- name, email, phone, company
- source (how they found us)
- status (lead → contacted → qualified → proposal → won/lost)
- last_contact_date
- next_action + next_action_date
- deal_value
- notes
Derived columns:
- days_since_last_contact = TODAY() - last_contact_date
- pipeline_stage_days = TODAY() - stage_entry_date
- is_stale = days_since_last_contact > 14
Dashboard metrics:
- Pipeline value by stage
- Conversion rate stage-to-stage
- Average days in each stage
- Stale leads count (action needed)
Pattern 3: Inventory / Stock Tracker
Required columns:
- sku, name, category
- quantity_on_hand, reorder_point, reorder_quantity
- unit_cost, unit_price
- last_received_date, last_sold_date
- supplier
Derived columns:
- stock_value = quantity_on_hand × unit_cost
- margin = (unit_price - unit_cost) / unit_price
- days_of_supply = quantity_on_hand / avg_daily_sales
- needs_reorder = quantity_on_hand <= reorder_point
Alerts:
- 🔴 Below reorder point
- 🟡 Within 7 days of stockout (based on velocity)
- ⚪ Dead stock (no sales in 90 days)
Pattern 4: Project / Task Tracker
Required columns:
- task_id, task_name, description
- assignee, priority (P0-P3)
- status (backlog → in_progress → review → done)
- start_date, due_date, completed_date
- estimated_hours, actual_hours
Derived columns:
- days_remaining = due_date - TODAY()
- is_overdue = due_date < TODAY() AND status != "done"
- effort_variance = actual_hours - estimated_hours
- completion_rate = done_tasks / total_tasks
Dashboard:
- Burndown chart (remaining vs time)
- Status distribution pie
- Overdue tasks list
- Team workload (tasks per assignee)
Pattern 5: Budget vs Actual
Structure:
- Rows: expense categories + revenue lines
- Columns: Budget | Actual | Variance | Variance %
- Group by: month or quarter
Key formulas:
- variance = actual - budget
- variance_pct = (actual - budget) / budget
- YTD_budget = SUM of months through current
- Run_rate = (YTD_actual / months_elapsed) × 12
Conditional formatting:
- Green: favorable variance (revenue over, cost under)
- Red: unfavorable variance (revenue under, cost over)
- Threshold: flag if |variance| > 10%
Phase 8: Data Quality Rules
Validation Checklist (run before any analysis)
validation:
structural:
- "No duplicate column names"
- "No completely empty columns"
- "No completely empty rows (except intentional separators)"
- "Consistent column count across all rows"
- "Headers in row 1 (no multi-row headers without handling)"
type_integrity:
- "Date columns parse as valid dates"
- "Numeric columns contain no text (except headers)"
- "ID columns are unique where expected"
- "Email columns match basic email pattern"
- "Phone columns are consistent format"
business_rules:
- "Revenue >= 0 (or explain negative = refund)"
- "Dates within expected range (not in future for historical data)"
- "Percentages between 0-100 (or 0-1, consistently)"
- "Status values match allowed list"
- "Foreign keys exist in reference table"
completeness:
- "Required columns have <5% missing"
- "No orphan records (child without parent)"
- "Date ranges are continuous (no gaps in daily data)"
Data Quality Score (0-100)
Dimension
Weight
Score 0-4
Criteria
Completeness
25%
% of non-null values in required fields
Uniqueness
15%
% of rows with valid unique keys
Consistency
20%
% of values matching expected format/type
Accuracy
20%
% passing business rule validation
Timeliness
10%
Data freshness vs expected refresh
Conformity
10%
% matching standard formats (dates, phones, emails)
Score = Σ(weight × score/4 × 100)
90-100: Production-ready
75-89: Minor fixes needed
50-74: Significant cleanup required
<50: Re-collect or restructure before use
Phase 9: Format Conversion & Interop
Conversion Decision Matrix
From → To
Best Method
Watch Out For
CSV → Excel
pandas + openpyxl
Encoding, date parsing, leading zeros
Excel → CSV
pandas or openpyxl
Multiple sheets, formulas lost, merged cells
JSON → CSV
pandas json_normalize
Nested objects need flattening
CSV → JSON
pandas to_json
Choose records vs columns orientation
Excel → Google Sheets
Upload directly
Macros stripped, some formulas differ
Google Sheets → Excel
Download as .xlsx
IMPORTRANGE breaks, custom functions lost
PDF table → CSV
Tabula, pdfplumber
Layout detection, merged cells, multi-page
HTML table → CSV
pandas read_html
Multiple tables, nested tables, encoding
Encoding Survival Guide
Garbled text? Try these encodings in order:
1. UTF-8 (default, handles all languages)
2. UTF-8-BOM (Windows exports often add BOM)
3. Latin-1 / ISO-8859-1 (Western European)
4. Windows-1252 (Windows "ANSI")
5. Shift-JIS (Japanese)
6. GB2312 / GBK (Chinese)
Python: pd.read_csv("file.csv", encoding="utf-8-sig")
Detection: chardet or charset-normalizer library
Phase 10: Performance & Scale
Size Thresholds
Rows
Tool Recommendation
<10K
Any tool (Excel, Sheets, pandas)
10K-100K
Excel (careful) or pandas
100K-1M
pandas with chunking, or DuckDB
1M-10M
DuckDB, Polars, or database
>10M
Database (PostgreSQL, BigQuery)
Performance Tips
Read only needed columns: pd.read_csv(file, usecols=["col1","col2"])
Merged cells — Only top-left has value, rest are empty
Number-as-text — "100" (text) vs 100 (number), looks identical
Locale-dependent decimals — 1,234.56 vs 1.234,56
Empty string vs NULL — "" and NaN behave differently
Trailing whitespace — "New York " ≠ "New York"
Excel 1904 date system — Mac-origin files, dates off by 4 years
Formula results vs formulas — Copy-paste values loses formulas silently
Multi-Currency Handling
currency_rules:
- Store amount AND currency code in separate columns
- Never mix currencies in a single column without code
- Use ISO 4217 codes (USD, GBP, EUR, BTC)
- Store exchange rate and rate date used for conversion
- Keep original amount + converted amount as separate columns
- Specify: is this the rate at transaction time or current rate?
Time Zone Handling
timezone_rules:
- Store timestamps in UTC internally
- Record the source timezone
- Convert to local time only for display
- "End of day" = 23:59:59 in the business timezone, not UTC
- Daylight saving transitions can cause 23h or 25h days
- Aggregate daily data in business timezone, not UTC
⚡ Level up your data game → AfrexAI Context Packs ($47) — industry-specific data templates, KPI frameworks, and reporting automations for SaaS, Fintech, Manufacturing, Ecommerce, and more.
🔗 More free skills by AfrexAI:
afrexai-data-analyst — Complete data analysis methodology