analysis-assumptions-log
Systematic tracking of analysis assumptions and decisions. Use when documenting data filtering choices, handling edge cases, making assumptions about missing data, or logging methodology decisions.
When & Why to Use This Skill
This Claude skill provides a structured framework for the systematic tracking and documentation of analytical assumptions, trade-offs, and decision-making processes. It is designed to enhance data transparency, ensure reproducibility, and create a robust audit trail for complex analytical workflows, helping teams mitigate risks by identifying and validating high-impact assumptions.
Use Cases
- Case 1: Documenting data preprocessing choices, such as outlier treatment and missing value handling, to maintain a transparent and reproducible data pipeline.
- Case 2: Logging model selection rationales and statistical assumptions during experiment design to facilitate rigorous peer reviews and regulatory compliance.
- Case 3: Identifying and prioritizing critical, low-confidence assumptions for urgent validation to reduce the risk of biased or incorrect business insights.
- Case 4: Creating a comprehensive decision log for stakeholders to explain the methodology and 'why' behind specific analytical conclusions in high-stakes projects.
| name | analysis-assumptions-log |
|---|---|
| description | Track and document analytical assumptions and decisions. Use when making analytical choices, documenting trade-offs, ensuring transparency, or creating audit trails for analytical work. |
Analysis Assumptions Log
Quick Start
Systematically document all assumptions, decisions, and trade-offs made during analysis to ensure transparency, reproducibility, and informed decision-making.
Context Requirements
Before logging assumptions, I need:
- Analysis Context: What analysis is being performed
- Assumptions Made: Explicit and implicit assumptions
- Decision Points: Where choices were made
- Rationale: Why each assumption/decision was made
- Impact Assessment: What changes if assumption is wrong
Context Gathering
For Analysis Context:
"What analysis are we documenting assumptions for?
Context needed:
- Analysis objective
- Key stakeholders
- Decision being informed
- Time sensitivity
- Risk tolerance
Example: 'Customer churn prediction model for retention team. High stakes - informs $2M retention budget.'"
For Assumptions:
"What assumptions are you making? Let's categorize:
Data Assumptions:
- Data quality/completeness
- Sample representativeness
- Missing value handling
- Outlier treatment
Business Logic Assumptions:
- Metric definitions
- Customer segmentation rules
- Time windows
- Causality vs correlation
Statistical Assumptions:
- Distribution assumptions
- Independence of observations
- Stationarity
- Model assumptions
Technical Assumptions:
- System performance
- Data freshness
- Processing limitations"
For Decision Points:
"Where did you make consequential choices?
Common Decision Points:
- Which data to include/exclude
- How to handle edge cases
- Statistical methods chosen
- Threshold values set
- Segments defined
- Time periods selected
Document the alternative you didn't choose and why."
For Rationale:
"Why did you make each assumption/decision?
Good Rationales:
- 'Industry standard approach'
- 'Previous analysis validated this'
- 'Stakeholder requirement'
- 'Data quality constraint'
- 'Computational limitation'
- 'Time constraint'
Bad Rationales:
- 'Seemed reasonable'
- 'Default setting'
- 'Always done this way'
Be honest and specific!"
For Impact:
"What happens if assumption is wrong?
Impact Assessment:
- Best case if wrong
- Most likely case if wrong
- Worst case if wrong
- Probability of being wrong
- Cost to validate
- Cost if wrong
This helps prioritize which assumptions to validate."
Workflow
Step 1: Create Assumptions Log Structure
import pandas as pd
from datetime import datetime
import json
class AssumptionLog:
"""
Structured logging of analytical assumptions and decisions
"""
def __init__(self, analysis_name, analyst):
self.analysis_name = analysis_name
self.analyst = analyst
self.created_date = datetime.now()
self.assumptions = []
self.decisions = []
self.validations = []
def log_assumption(self,
category,
assumption,
rationale,
confidence,
impact_if_wrong,
validation_plan=None):
"""
Log a new assumption
"""
entry = {
'id': len(self.assumptions) + 1,
'timestamp': datetime.now().isoformat(),
'category': category,
'assumption': assumption,
'rationale': rationale,
'confidence': confidence, # high, medium, low
'impact_if_wrong': impact_if_wrong,
'validation_plan': validation_plan,
'status': 'active',
'validated': False
}
self.assumptions.append(entry)
return entry['id']
def log_decision(self,
decision_point,
chosen_option,
alternatives_considered,
rationale,
trade_offs):
"""
Log an analytical decision
"""
entry = {
'id': len(self.decisions) + 1,
'timestamp': datetime.now().isoformat(),
'decision_point': decision_point,
'chosen': chosen_option,
'alternatives': alternatives_considered,
'rationale': rationale,
'trade_offs': trade_offs
}
self.decisions.append(entry)
return entry['id']
def validate_assumption(self, assumption_id, validation_result, notes):
"""
Record validation of an assumption
"""
# Find assumption
assumption = next((a for a in self.assumptions if a['id'] == assumption_id), None)
if assumption:
assumption['validated'] = True
assumption['validation_result'] = validation_result
assumption['validation_notes'] = notes
assumption['validation_date'] = datetime.now().isoformat()
self.validations.append({
'assumption_id': assumption_id,
'result': validation_result,
'date': datetime.now().isoformat(),
'notes': notes
})
def get_critical_assumptions(self):
"""
Identify high-impact, low-confidence assumptions
"""
critical = [
a for a in self.assumptions
if a['confidence'] == 'low' and
a['impact_if_wrong'] in ['high', 'critical']
and not a['validated']
]
return critical
def export_to_markdown(self):
"""
Generate markdown documentation
"""
doc = f"# Assumptions Log: {self.analysis_name}\n\n"
doc += f"**Analyst:** {self.analyst}\n"
doc += f"**Created:** {self.created_date.strftime('%Y-%m-%d')}\n"
doc += f"**Last Updated:** {datetime.now().strftime('%Y-%m-%d')}\n\n"
doc += "---\n\n"
# Summary stats
doc += "## Summary\n\n"
doc += f"- Total Assumptions: {len(self.assumptions)}\n"
doc += f"- Total Decisions: {len(self.decisions)}\n"
validated = sum(1 for a in self.assumptions if a['validated'])
doc += f"- Validated: {validated}/{len(self.assumptions)}\n"
critical = len(self.get_critical_assumptions())
if critical > 0:
doc += f"- ⚠️ Critical Unvalidated: {critical}\n"
doc += "\n---\n\n"
# Assumptions
doc += "## Assumptions\n\n"
for category in ['data', 'business_logic', 'statistical', 'technical']:
cat_assumptions = [a for a in self.assumptions if a['category'] == category]
if cat_assumptions:
doc += f"### {category.replace('_', ' ').title()}\n\n"
for assum in cat_assumptions:
doc += f"**#{assum['id']}: {assum['assumption']}**\n\n"
doc += f"- **Rationale:** {assum['rationale']}\n"
doc += f"- **Confidence:** {assum['confidence'].upper()}\n"
doc += f"- **Impact if wrong:** {assum['impact_if_wrong']}\n"
if assum['validated']:
doc += f"- **Validated:** ✅ {assum['validation_result']}\n"
else:
doc += f"- **Validated:** ❌ Not yet validated\n"
if assum.get('validation_plan'):
doc += f"- **Validation plan:** {assum['validation_plan']}\n"
doc += "\n"
# Decisions
doc += "## Key Decisions\n\n"
for decision in self.decisions:
doc += f"**Decision #{decision['id']}: {decision['decision_point']}**\n\n"
doc += f"- **Chosen:** {decision['chosen']}\n"
doc += f"- **Alternatives considered:**\n"
for alt in decision['alternatives']:
doc += f" - {alt}\n"
doc += f"- **Rationale:** {decision['rationale']}\n"
doc += f"- **Trade-offs:** {decision['trade_offs']}\n\n"
return doc
def save(self, filepath):
"""
Save log to file
"""
data = {
'analysis_name': self.analysis_name,
'analyst': self.analyst,
'created_date': self.created_date.isoformat(),
'assumptions': self.assumptions,
'decisions': self.decisions,
'validations': self.validations
}
with open(filepath, 'w') as f:
json.dump(data, f, indent=2)
# Initialize log
log = AssumptionLog(
analysis_name="Customer Churn Prediction Model",
analyst="Data Science Team"
)
print("✅ Assumptions log initialized")
Step 2: Log Data Assumptions
# Example: Log data-related assumptions
# Assumption 1: Sample representativeness
log.log_assumption(
category='data',
assumption='Last 90 days of data is representative of future behavior',
rationale='90 days captures recent product changes and seasonal patterns',
confidence='medium',
impact_if_wrong='Model may not generalize to future periods. Could overfit to recent anomalies.',
validation_plan='Test model on holdout data from different time period'
)
# Assumption 2: Missing data
log.log_assumption(
category='data',
assumption='Users with missing engagement data are inactive (not data quality issue)',
rationale='Manual inspection of 100 samples confirmed pattern',
confidence='high',
impact_if_wrong='Would misclassify engaged users as inactive. Estimated <1% of users affected.',
validation_plan='Spot check with product team on specific user IDs'
)
# Assumption 3: Churned definition
log.log_assumption(
category='business_logic',
assumption='User is "churned" if no activity for 30 days',
rationale='Stakeholder requirement. Aligns with subscription billing cycle.',
confidence='high',
impact_if_wrong='Low - this is the business definition we must use',
validation_plan='Not applicable - this is a requirement, not an assumption'
)
print(f"✅ Logged {len(log.assumptions)} assumptions")
Step 3: Log Decision Points
# Example: Log key decisions made during analysis
# Decision 1: Feature selection
log.log_decision(
decision_point='Which features to include in churn model',
chosen_option='Use all engagement features (sessions, events, feature usage)',
alternatives_considered=[
'Only core features (sessions, last active)',
'Include demographic features (age, location)',
'Add external data (economic indicators)'
],
rationale='Engagement features most predictive in EDA. Demographics had privacy concerns. External data not readily available.',
trade_offs='More features = more complexity. Risk of overfitting. But better predictive power.'
)
# Decision 2: Model choice
log.log_decision(
decision_point='Which model algorithm to use',
chosen_option='Gradient Boosting (XGBoost)',
alternatives_considered=[
'Logistic Regression (interpretable)',
'Random Forest (robust)',
'Neural Network (flexible)'
],
rationale='XGBoost showed best performance in cross-validation (AUC 0.87 vs 0.82 for LR). Stakeholders prioritize accuracy over interpretability.',
trade_offs='Less interpretable than logistic regression. Longer training time. But worth it for 5% accuracy gain.'
)
# Decision 3: Threshold setting
log.log_decision(
decision_point='At what probability threshold to predict churn',
chosen_option='0.35 probability threshold',
alternatives_considered=[
'0.5 (balanced)',
'0.25 (more aggressive)',
'Dynamic threshold by segment'
],
rationale='Optimized for business cost function: $50 retention cost vs $500 churn cost. 0.35 maximizes expected value.',
trade_offs='More false positives (wasted retention efforts) but catch more true churners. Net positive EV.'
)
print(f"✅ Logged {len(log.decisions)} decisions")
Step 4: Identify Critical Assumptions
# Find assumptions that need validation most urgently
critical = log.get_critical_assumptions()
if critical:
print(f"\n⚠️ {len(critical)} CRITICAL ASSUMPTIONS NEED VALIDATION:\n")
for assum in critical:
print(f"#{assum['id']}: {assum['assumption']}")
print(f" Confidence: {assum['confidence']}")
print(f" Impact: {assum['impact_if_wrong']}")
if assum['validation_plan']:
print(f" Plan: {assum['validation_plan']}")
print()
else:
print("✅ No critical unvalidated assumptions")
Step 5: Validate Assumptions
# As analysis progresses, validate assumptions
# Validate assumption #1
log.validate_assumption(
assumption_id=1,
validation_result='Confirmed',
notes='Tested model on Nov 2024 data (outside training window). Performance held (AUC 0.86 vs 0.87 on training).'
)
# Validate assumption #2
log.validate_assumption(
assumption_id=2,
validation_result='Partially confirmed',
notes='Product team confirmed most missing data is true inactivity. Found 2% edge case of mobile users with tracking issues. Added filter to exclude.'
)
print("✅ Assumptions validated and logged")
Step 6: Generate Risk Assessment
def assess_assumption_risk(log):
"""
Quantify overall risk from assumptions
"""
risk_scores = {
('high', 'critical'): 9,
('high', 'high'): 8,
('high', 'medium'): 6,
('medium', 'critical'): 7,
('medium', 'high'): 6,
('medium', 'medium'): 4,
('low', 'critical'): 5,
('low', 'high'): 4,
('low', 'medium'): 2
}
total_risk = 0
risk_breakdown = []
for assum in log.assumptions:
if not assum['validated']:
# Parse confidence and impact
conf = assum['confidence']
# Parse impact (extract first word)
impact = assum['impact_if_wrong'].split()[0].lower()
if 'high' in impact or 'severe' in impact or 'critical' in impact:
impact_level = 'high'
elif 'medium' in impact or 'moderate' in impact:
impact_level = 'medium'
else:
impact_level = 'medium' # default
# Calculate risk
risk = risk_scores.get((conf, impact_level), 3)
total_risk += risk
risk_breakdown.append({
'assumption_id': assum['id'],
'assumption': assum['assumption'][:50] + '...',
'risk_score': risk,
'confidence': conf,
'impact': impact_level
})
# Overall risk assessment
avg_risk = total_risk / len(log.assumptions) if log.assumptions else 0
print(f"\n📊 Risk Assessment:")
print(f" Total Risk Score: {total_risk}")
print(f" Average per Assumption: {avg_risk:.1f}")
if avg_risk < 3:
print(f" Overall Risk: ✅ LOW")
elif avg_risk < 6:
print(f" Overall Risk: ⚠️ MEDIUM")
else:
print(f" Overall Risk: 🔴 HIGH")
# Top risks
print(f"\n Top Risk Contributors:")
for item in sorted(risk_breakdown, key=lambda x: x['risk_score'], reverse=True)[:3]:
print(f" #{item['assumption_id']}: {item['assumption']} (score: {item['risk_score']})")
return total_risk, avg_risk, risk_breakdown
risk_total, risk_avg, risk_details = assess_assumption_risk(log)
Step 7: Generate Documentation
# Export complete assumptions log
# Markdown format
markdown_doc = log.export_to_markdown()
# Save to file
with open('assumptions_log.md', 'w') as f:
f.write(markdown_doc)
# Also save structured data
log.save('assumptions_log.json')
print("\n✅ Documentation generated:")
print(" 📄 assumptions_log.md")
print(" 📄 assumptions_log.json")
# Print summary section
print("\n" + "="*60)
print(markdown_doc.split('---')[0]) # Print summary section
Step 8: Create Assumption Review Checklist
def generate_review_checklist(log):
"""
Create checklist for peer review
"""
checklist = []
# Check for missing categories
categories_found = set(a['category'] for a in log.assumptions)
expected_categories = ['data', 'business_logic', 'statistical', 'technical']
missing = set(expected_categories) - categories_found
if missing:
checklist.append({
'type': 'missing_category',
'item': f'No {", ".join(missing)} assumptions documented',
'action': 'Review if any assumptions in these categories exist'
})
# Check for low confidence, high impact assumptions
critical = log.get_critical_assumptions()
if critical:
for assum in critical:
checklist.append({
'type': 'critical_unvalidated',
'item': f'Assumption #{assum["id"]} not validated',
'action': f'Validate: {assum["assumption"][:50]}'
})
# Check for vague rationales
vague_keywords = ['reasonable', 'standard', 'typical', 'usually']
for assum in log.assumptions:
rationale_lower = assum['rationale'].lower()
if any(word in rationale_lower for word in vague_keywords):
checklist.append({
'type': 'vague_rationale',
'item': f'Assumption #{assum["id"]} has vague rationale',
'action': 'Provide more specific justification'
})
# Check validation status
unvalidated_pct = (len([a for a in log.assumptions if not a['validated']]) /
len(log.assumptions) * 100) if log.assumptions else 0
if unvalidated_pct > 50:
checklist.append({
'type': 'low_validation',
'item': f'{unvalidated_pct:.0f}% of assumptions unvalidated',
'action': 'Prioritize validation before finalizing analysis'
})
# Print checklist
if checklist:
print("\n📋 Review Checklist:")
for i, item in enumerate(checklist, 1):
print(f"\n{i}. {item['item']}")
print(f" Action: {item['action']}")
else:
print("\n✅ All checks passed!")
return checklist
review_items = generate_review_checklist(log)
Context Validation
Before finalizing assumptions log, verify:
- All major assumption categories covered
- Rationales are specific and defensible
- Impact assessments are realistic
- Critical assumptions have validation plans
- Decisions document alternatives considered
- Log is peer-reviewed
Output Template
# Assumptions Log: Customer Churn Prediction Model
**Analyst:** Data Science Team
**Created:** 2025-01-11
**Last Updated:** 2025-01-11
---
## Summary
- Total Assumptions: 8
- Total Decisions: 3
- Validated: 6/8
- ⚠️ Critical Unvalidated: 1
---
## Assumptions
### Data
**#1: Last 90 days of data is representative**
- **Rationale:** Captures recent product changes and seasonal patterns
- **Confidence:** MEDIUM
- **Impact if wrong:** Model may not generalize to future periods
- **Validated:** ✅ Confirmed - tested on holdout period
- **Validation plan:** Test on data from different time period
**#2: Missing engagement data indicates inactivity**
- **Rationale:** Manual inspection of 100 samples confirmed
- **Confidence:** HIGH
- **Impact if wrong:** Would misclassify 1% of users
- **Validated:** ✅ Partially confirmed - added filter for edge cases
- **Validation plan:** Spot check with product team
### Business Logic
**#3: Churned = no activity for 30 days**
- **Rationale:** Stakeholder requirement, aligns with billing
- **Confidence:** HIGH
- **Impact if wrong:** Low - this is required definition
- **Validated:** ✅ Confirmed by stakeholders
### Statistical
**#4: Features are independent**
- **Rationale:** Checked correlation matrix, max correlation 0.45
- **Confidence:** MEDIUM
- **Impact if wrong:** Model may over-weight correlated features
- **Validated:** ❌ Not yet validated
- **Validation plan:** Test with PCA to see if performance changes
---
## Key Decisions
**Decision #1: Feature selection**
- **Chosen:** All engagement features
- **Alternatives considered:**
- Only core features
- Include demographics
- Add external data
- **Rationale:** Engagement most predictive. Privacy concerns with demographics.
- **Trade-offs:** More complexity but better accuracy
**Decision #2: Model algorithm**
- **Chosen:** XGBoost
- **Alternatives considered:**
- Logistic Regression
- Random Forest
- Neural Network
- **Rationale:** Best cross-validation performance (AUC 0.87)
- **Trade-offs:** Less interpretable but 5% accuracy gain
---
## Risk Assessment
Total Risk Score: 24
Overall Risk: ⚠️ MEDIUM
Top Risks:
1. #4: Features independence assumption (score: 7)
2. #5: Stationarity assumption (score: 6)
---
## Validation Status
✅ 75% validated (6/8)
⚠️ 1 critical assumption pending validation
📅 Next review: Before model deployment
Common Scenarios
Scenario 1: "Starting new analysis - set up assumptions tracking"
→ Initialize assumption log → Document initial hypotheses → Set up validation framework → Share with stakeholders → Update as analysis progresses
Scenario 2: "Peer review of analysis"
→ Review assumptions log → Challenge vague rationales → Identify missing assumptions → Validate critical assumptions → Sign off on log
Scenario 3: "Analysis results unexpected"
→ Review assumptions → Identify which might be wrong → Validate suspicious assumptions → Update analysis if needed → Document learnings
Scenario 4: "Regulatory/audit requirement"
→ Generate complete audit trail → Document all decisions → Show validation evidence → Demonstrate rigor → Maintain records
Scenario 5: "Replicating analysis later"
→ Reference original assumptions → Check if still valid → Update for new context → Compare results → Document differences
Handling Missing Context
User hasn't thought about assumptions: "Let me help identify assumptions you might be making:
- Data: Is your sample representative?
- Business: How are you defining key concepts?
- Statistical: What distributions are you assuming?
- Technical: Any system limitations?
Let's walk through systematically."
User says 'no assumptions made': "Every analysis has assumptions! Some common ones:
- Your data is complete and accurate
- Past patterns predict future
- Your sample represents the population
- Correlation implies something meaningful
Let's make them explicit."
User only lists obvious assumptions: "Good start! Let's also document:
- The choices you made (why this method vs others?)
- Edge cases you're handling
- What you're NOT including
- Time/resource constraints
These are assumptions too!"
User unsure about confidence levels: "Let's assess:
- Have you validated it? (High confidence)
- Based on domain knowledge? (Medium)
- Just seems reasonable? (Low)
Being honest helps prioritize validation."
Advanced Options
After basic log, offer:
Automated Assumption Detection: "I can scan your code/queries to identify implicit assumptions you might have missed."
Assumption Testing Framework: "Set up automated tests that alert when assumptions become invalid (e.g., data distribution shifts)."
Sensitivity Analysis: "Quantify how much results change if each assumption is violated."
Assumption Dependencies: "Map which assumptions depend on others - helps understand cascading risks."
Historical Assumption Tracking: "Track assumptions across multiple analyses to identify patterns and improve future work."
Assumption Review Templates: "Create templates for peer review focused on assumption validation."