These are not suggestions - they are mandatory safeguards developed from decades of statistical failures and corrections.
import pandas as pd
import numpy as np
import sys
import warnings
from datetime import datetime
# MANDATORY: Input validation
assert 'df' in locals(), "DataFrame 'df' must be provided"
required_columns = {'outcome', 'treatment', 'unit_id'}
missing = required_columns - set(df.columns)
assert not missing, f"Missing required columns: {missing}"
# MANDATORY: Data type validation
numeric_columns = ['outcome']
for col in numeric_columns:
assert pd.api.types.is_numeric_dtype(df[col]), f"{col} must be numeric"
# MANDATORY: Document data provenance
print("=" * 60)
print("DATA PROVENANCE")
print("=" * 60)
print("Original data source: [Document where data came from]")
print("Collection method: [How was data collected? Survey, administrative records, etc.]")
print("Collection dates: [When was data collected?]")
print("Data access date: [When did you access/download the data?]")
print("Preprocessing steps (before this analysis):")
print(" - [List any cleaning, merging, or transformations done outside this notebook]")
print(" - [Document any data preparation steps]")
print("Data version/identifier: [File hash, version number, or unique identifier]")
# MANDATORY: Document data properties
print("\n" + "=" * 60)
print("DATA VALIDATION")
print("=" * 60)
print(f"N observations: {len(df):,}")
print(f"N units: {df['unit_id'].nunique():,}")
print(f"Missing outcome: {df['outcome'].isna().sum()} ({df['outcome'].isna().mean()*100:.1f}%)")
# MANDATORY: Show any data transformations explicitly
if df['outcome'].isna().any():
print(f"\nWARNING: Dropping {df['outcome'].isna().sum()} observations with missing outcome")
df_analysis = df.dropna(subset=['outcome']).copy()
else:
df_analysis = df.copy()
# MANDATORY: State assumptions
assumptions = """
STATISTICAL ASSUMPTIONS:
1. Independence: Observations are independent (may be violated if clustered)
2. Correct specification: The model captures the true relationship
3. No measurement error: Variables are measured accurately
"""
print(assumptions)
# [Actual analysis code here]
# MANDATORY: Reproducibility information (always the last cell)
import platform
import json as _json
_repro = {
"python": sys.version,
"platform": platform.platform(),
"pandas": pd.__version__,
"statsmodels": __import__('statsmodels').__version__,
"numpy": np.__version__,
"matplotlib": __import__('matplotlib').__version__,
}
print("Reproducibility Information")
print("=" * 60)
print(_json.dumps(_repro, indent=2))
return df_analysis,
</code_template>
</implementation_pattern>
<examples>
<example context="encoding_detection" difficulty="basic">
<description>Detecting file encoding before reading CSV files</description>
<code>
```python
@app.cell
def detect_and_load_csv(filepath):
#RATIONALE: Encoding mismatches cause silent character corruption.
# We detect encoding first, document it for reproducibility, and
# handle common encoding issues before reading data.
import pandas as pd
import os
import chardet # pip install chardet
# MANDATORY: Check file exists
assert os.path.exists(filepath), f"File not found: {filepath}"
# MANDATORY: Detect encoding before reading
print("=" * 60)
print("ENCODING DETECTION")
print("=" * 60)
# Read a sample to detect encoding (faster than reading entire file)
with open(filepath, 'rb') as f:
raw_data = f.read(10000) # Read first 10KB for detection
detection_result = chardet.detect(raw_data)
detected_encoding = detection_result['encoding']
confidence = detection_result['confidence']
print(f"Detected encoding: {detected_encoding}")
print(f"Confidence: {confidence:.2%}")
# Common encodings to try if detection is uncertain
encodings_to_try = [
detected_encoding, # Try detected first
'utf-8', # Most common modern encoding
'latin-1', # Common for European data
'iso-8859-1', # Alternative to latin-1
'cp1252', # Windows encoding
'utf-16', # Unicode with BOM
]
# Remove duplicates while preserving order
encodings_to_try = list(dict.fromkeys(encodings_to_try))
# Try each encoding until one works
df = None
used_encoding = None
for encoding in encodings_to_try:
if encoding is None:
continue
try:
print(f"\nAttempting to read with encoding: {encoding}")
df = pd.read_csv(filepath, encoding=encoding, nrows=5) # Test with 5 rows first
used_encoding = encoding
print(f"SUCCESS: File readable with {encoding}")
break
except (UnicodeDecodeError, UnicodeError) as e:
print(f" Failed: {str(e)[:60]}...")
continue
except Exception as e:
# Other errors (not encoding-related) - re-raise
raise
if df is None:
raise ValueError(f"Could not read file with any encoding tried: {encodings_to_try}")
# Now read full file with correct encoding
print(f"\nReading full file with encoding: {used_encoding}")
df = pd.read_csv(filepath, encoding=used_encoding)
# MANDATORY: Document encoding for reproducibility
print("\n" + "=" * 60)
print("ENCODING INFORMATION (for reproducibility)")
print("=" * 60)
print(f"File encoding: {used_encoding}")
print(f"Detection confidence: {confidence:.2%}")
print(f"File size: {os.path.getsize(filepath):,} bytes")
return df, used_encoding,
Always document the encoding used for reproducibility.
For CSV files:
- Detect encoding before reading (use chardet or charset-normalizer)
- Try common encodings if detection uncertain
- Document encoding used in reproducibility section
- If encoding detection unavailable, try: utf-8, latin-1, cp1252 in order
- Check for encoding errors in string columns after loading
import pandas as pd
import numpy as np
# Scenario: User thinks 'treatment' is binary but it's actually continuous
print("Checking treatment variable...")
# MANDATORY: Validate assumptions about the data
unique_vals = df['treatment'].unique()
print(f"Unique treatment values: {unique_vals}")
if not set(unique_vals).issubset({0, 1}):
print("\nERROR: Treatment is not binary!")
print(f"Found values: {unique_vals}")
print("\nThis would have caused:")
print("- Wrong model specification")
print("- Invalid causal interpretation")
print("- Meaningless 'treatment effect'")
# Show what would happen with silent failure
if len(unique_vals) > 2:
# Demonstrate the error
mean_by_treatment = df.groupby('treatment')['outcome'].mean()
print(f"\nMeans by 'treatment': \n{mean_by_treatment}")
print("\nWARNING: These are NOT treatment effects!")
raise ValueError("Treatment must be binary (0/1) for causal analysis")
print("SUCCESS: Treatment is binary")
# Additional validation
assert df['outcome'].dtype in ['float64', 'int64'], "Outcome must be numeric"
assert not df[['treatment', 'outcome']].isna().all(axis=1).any(), "Complete observations required"
return None,
</code>
<output_interpretation>
This validation would catch a critical error where continuous dose is mistaken for binary treatment, preventing false causal claims.
</output_interpretation>
</example>
<example context="transformation_transparency" difficulty="intermediate">
<description>Making all data transformations explicit</description>
<code>
```python
@app.cell
def transparent_transformations(df):
#Every transformation is shown and justified to prevent
# hidden researcher degrees of freedom. Simmons et al. (2011) show
# how undisclosed flexibility in data analysis inflates false positive rates.
import pandas as pd
import numpy as np
print("DATA TRANSFORMATION DOCUMENTATION")
print("=" * 60)
# Original data summary
print(f"Original N: {len(df)}")
print(f"Original outcome range: [{df['outcome'].min():.2f}, {df['outcome'].max():.2f}]")
# MANDATORY: Document each transformation step
df_transformed = df.copy()
# Step 1: Handle outliers (document cutoff and rationale)
Q1 = df['outcome'].quantile(0.25)
Q3 = df['outcome'].quantile(0.75)
IQR = Q3 - Q1
outlier_threshold = Q3 + 3*IQR # Using 3*IQR for extreme outliers only
n_outliers = (df['outcome'] > outlier_threshold).sum()
print(f"\nStep 1: Windsorizing {n_outliers} extreme outliers (>{outlier_threshold:.2f})")
print(f" Rationale: Values >3*IQR likely measurement errors")
print(f" Alternative: Could use log transformation instead")
df_transformed.loc[df_transformed['outcome'] > outlier_threshold, 'outcome'] = outlier_threshold
# Step 2: Create categorical variable (document cutoffs)
median_val = df_transformed['outcome'].median()
print(f"\nStep 2: Creating binary indicator at median ({median_val:.2f})")
print(f" Rationale: Testing for heterogeneous effects above/below median")
print(f" Note: This reduces power but aids interpretation")
df_transformed['outcome_high'] = (df_transformed['outcome'] > median_val).astype(int)
# Step 3: Log transformation (document reason)
print(f"\nStep 3: Log transformation of outcome")
print(f" Skewness before: {df['outcome'].skew():.2f}")
# Handle zeros if present
min_positive = df_transformed[df_transformed['outcome'] > 0]['outcome'].min()
if (df_transformed['outcome'] == 0).any():
offset = min_positive / 2
print(f" Adding offset {offset:.4f} to handle zeros")
df_transformed['log_outcome'] = np.log(df_transformed['outcome'] + offset)
else:
df_transformed['log_outcome'] = np.log(df_transformed['outcome'])
print(f" Skewness after: {df_transformed['log_outcome'].skew():.2f}")
# MANDATORY: Summary of all changes
print("\n" + "=" * 60)
print("TRANSFORMATION SUMMARY")
print("=" * 60)
print(f"Records modified: {(df['outcome'] != df_transformed['outcome']).sum()}")
print(f"New variables created: outcome_high, log_outcome")
print(f"Original data preserved in: df")
print(f"Transformed data in: df_transformed")
# MANDATORY: Note impact on interpretation
print("\nINTERPRETATION NOTE:")
print("- Results apply to windsorized distribution")
print("- Log coefficients represent % changes")
print("- Binary outcome reduces power but simplifies interpretation")
return df_transformed,
import hashlib
import os
from datetime import datetime
provenance_info = {
"data_sources": {
"primary_dataset": {
"source_name": "RCT Baseline Survey",
"source_type": "Primary data collection",
"collection_method": "Face-to-face interviews using ODK Collect",
"collection_dates": {
"start": "2024-01-15",
"end": "2024-03-20"
},
"collection_location": "Rural districts in Northern Uganda",
"sampling_method": "Randomized controlled trial baseline",
"sample_size_collected": 1200,
"data_access_date": "2024-04-01",
"data_access_method": "Downloaded from SurveyCTO server",
"file_path": "/mnt/data/baseline_survey.csv",
"file_hash": None, # Will compute below
"file_size_bytes": None,
"preprocessing_steps": [
"Exported from SurveyCTO in CSV format",
"No manual edits performed",
"Variable names preserved as exported"
]
},
"treatment_assignments": {
"source_name": "Randomization List",
"source_type": "Administrative data",
"collection_method": "Generated using random number generator",
"collection_dates": {
"date": "2024-01-10"
},
"randomization_method": "Stratified block randomization",
"strata": ["district", "gender"],
"block_size": 4,
"data_access_date": "2024-04-01",
"file_path": "/mnt/data/treatment_assignments.csv",
"file_hash": None,
"preprocessing_steps": [
"Generated using R script: randomization.R",
"Exported to CSV for merging"
]
}
},
"data_merging": {
"merge_date": "2024-04-01",
"merge_method": "Left join on unit_id",
"merge_key": "unit_id",
"merge_validation": "All baseline observations matched",
"merged_file_path": "/mnt/data/merged_analysis_data.csv"
},
"data_limitations": [
"Baseline survey had 5% non-response rate",
"Missing income data for 12% of sample (documented in analysis)",
"Treatment assignments were double-blind"
]
}
# Compute file hashes for data integrity
for source_key, source_info in provenance_info["data_sources"].items():
filepath = source_info.get("file_path")
if filepath and os.path.exists(filepath):
with open(filepath, 'rb') as f:
file_hash = hashlib.sha256(f.read()).hexdigest()
file_size = os.path.getsize(filepath)
provenance_info["data_sources"][source_key]["file_hash"] = file_hash
provenance_info["data_sources"][source_key]["file_size_bytes"] = file_size
# Print human-readable provenance documentation
print("=" * 70)
print("DATA PROVENANCE DOCUMENTATION")
print("=" * 70)
for source_key, source_info in provenance_info["data_sources"].items():
print(f"\n{source_info['source_name']}")
print(f" Type: {source_info['source_type']}")
print(f" Collection method: {source_info['collection_method']}")
if 'collection_dates' in source_info:
dates = source_info['collection_dates']
if 'start' in dates:
print(f" Collection period: {dates['start']} to {dates['end']}")
else:
print(f" Collection date: {dates['date']}")
print(f" Data accessed: {source_info['data_access_date']}")
print(f" File: {source_info['file_path']}")
if source_info.get('file_hash'):
print(f" SHA-256: {source_info['file_hash'][:16]}...")
print(f" Size: {source_info['file_size_bytes']:,} bytes")
if 'preprocessing_steps' in source_info:
print(f" Preprocessing steps:")
for step in source_info['preprocessing_steps']:
print(f" - {step}")
if 'data_merging' in provenance_info:
merge_info = provenance_info['data_merging']
print(f"\nData Merging")
print(f" Date: {merge_info['merge_date']}")
print(f" Method: {merge_info['merge_method']}")
print(f" Key: {merge_info['merge_key']}")
print(f" Validation: {merge_info['merge_validation']}")
if 'data_limitations' in provenance_info:
print(f"\nData Limitations")
for limitation in provenance_info['data_limitations']:
print(f" - {limitation}")
# Save structured provenance to JSON
import json
with open('./data_provenance.json', 'w') as f:
json.dump(provenance_info, f, indent=2)
print(f"\nSUCCESS: Full provenance documentation saved to ./data_provenance.json")
return provenance_info,
</code>
<best_practice>
Data provenance must document:
1. **Original sources**: Where data came from (surveys, administrative records, APIs, etc.)
2. **Collection methods**: How data was collected (survey instruments, measurement tools, protocols)
3. **Collection dates**: When data was collected (affects external validity)
4. **Data access**: When and how you accessed the data
5. **Preprocessing**: Any cleaning, merging, or transformations done BEFORE your analysis code
6. **File integrity**: File hashes to detect if data changes
7. **Limitations**: Known data quality issues or constraints
This enables others to:
- Verify data authenticity
- Assess data quality
- Understand potential biases
- Replicate data preparation steps
- Detect preprocessing errors
</best_practice>
</example>
<example context="reproducibility_appendix" difficulty="basic">
<description>Reproducibility cell -- MUST be the last cell in every notebook response</description>
<code>
```python
import sys, platform, json
_repro = {
"python": sys.version,
"platform": platform.platform(),
"pandas": pd.__version__,
"statsmodels": __import__('statsmodels').__version__,
"numpy": np.__version__,
"matplotlib": __import__('matplotlib').__version__,
}
print("Reproducibility Information")
print("=" * 60)
print(json.dumps(_repro, indent=2))
1---2name: core-methodology3description: Core statistical methodology and pedagogical approach for all Inquiro analyses. Always loaded to maintain teaching quality and reproducibility standards.4---56<skill_content>78<overview>9Core methodology establishes the fundamental principles of rigorous statistical analysis. These principles protect against errors that have invalidated countless published studies and ensure reproducibility. This skill is ALWAYS loaded because its requirements apply to every analysis.1011These are not suggestions - they are mandatory safeguards developed from decades of statistical failures and corrections.12</overview>1314<mandatory_requirements>1516<requirement priority="critical">17 <name>Defensive Input Validation</name>18 <description>MUST validate all inputs before any analysis, including file encoding detection for text files</description>19 <rationale>Silent failures on different datasets have led to retracted papers. Gelman & Loken (2014) document the "garden of forking paths" problem where unstated data decisions invalidate p-values. Encoding mismatches cause silent character corruption (e.g., accented characters become garbage), leading to incorrect data interpretation</rationale>20 <consequence>Analysis fails silently on new data, producing incorrect results that appear valid. Wrong encoding causes character corruption, missing data from parsing failures, and incorrect string comparisons</consequence>21</requirement>2223<requirement priority="critical">24 <name>RATIONALE Comments</name>25 <description>Every statistical choice MUST have a RATIONALE comment explaining why</description>26 <rationale>Reproducibility requires understanding not just what was done but why. Nosek et al. (2015) found only 39% of psychology studies replicated, often due to undocumented choices</rationale>27 <consequence>Future researchers cannot understand or replicate the analysis</consequence>28</requirement>2930<requirement priority="critical">31 <name>No Silent Data Transformations</name>32 <description>Every data modification must be explicit and explained</description>33 <rationale>Simmons et al. (2011) show how "researcher degrees of freedom" in data handling can produce false positives. Silent transformations hide these choices</rationale>34 <consequence>P-hacking and false discoveries from undisclosed analytical flexibility</consequence>35</requirement>3637<requirement priority="high">38 <name>Data Authenticity and Simulation Transparency</name>39 <description>MUST use real data from documented sources when analyzing actual datasets. Synthetic/simulated data is ONLY acceptable for: (1) power calculations, (2) pedagogical examples, (3) algorithm demonstrations. ALL simulated data MUST be explicitly labeled with clear warnings</description>40 <rationale>Fabricated data masquerading as real invalidates causal claims and erodes scientific credibility. Nosek et al. (2015) emphasize working with authentic data for reproducible science. Context matters: simulation for legitimate purposes (e.g., Monte Carlo studies, power analysis) is acceptable when transparent</rationale>41 <consequence>If fabrication detected in real analysis: invalid findings, undetectable p-hacking, publication of false results presented as discovered truth, erosion of trust in research</consequence>42</requirement>4344<requirement priority="critical">45 <name>Reproducibility Information</name>46 <description>Document seeds, versions, and file hashes</description>47 <rationale>Computational reproducibility is fundamental to science. Ioannidis et al. (2009) found most computational results cannot be reproduced without full environment details</rationale>48 <consequence>Results cannot be verified or replicated</consequence>49</requirement>5051<requirement priority="critical">52 <name>Data Provenance Documentation</name>53 <description>MUST document the complete lineage of all data: original sources, collection methods, preprocessing steps performed before analysis, data access dates, and any transformations applied outside the analysis code</description>54 <rationale>Data provenance is essential for reproducibility and validity assessment. Without knowing where data came from and what happened to it before analysis, results cannot be verified or understood. Stodden et al. (2016) emphasize that data provenance enables others to assess data quality, detect errors, and understand limitations. Missing provenance information makes it impossible to evaluate whether data collection methods introduce bias or whether preprocessing steps affect conclusions</rationale>55 <consequence>Inability to verify data authenticity, assess data quality, detect preprocessing errors, understand limitations, or replicate results. Hidden preprocessing steps can introduce bias or errors that invalidate findings</consequence>56</requirement>5758<requirement priority="critical">59 <name>Explicit Assumption Statements</name>60 <description>State all statistical assumptions before analysis</description>61 <rationale>Violations of unstated assumptions are a primary source of invalid inference. Every statistical method has assumptions that must be checked</rationale>62 <consequence>Invalid statistical inference from violated assumptions</consequence>63</requirement>6465</mandatory_requirements>6667<pedagogical_principles>6869<principle name="Socratic Method">70 <description>Teach through questions, not commands</description>71 <implementation>Ask "What would it mean if..." rather than stating conclusions</implementation>72 <rationale>Develops statistical intuition rather than rote application</rationale>73</principle>7475<principle name="Effect Size Over Significance">76 <description>Always discuss practical significance alongside statistical significance</description>77 <implementation>Report magnitudes with units and context</implementation>78 <rationale>P-values alone are meaningless; effect size determines practical importance</rationale>79</principle>8081<principle name="Uncertainty Quantification">82 <description>Always show uncertainty in estimates</description>83 <implementation>Confidence intervals, standard errors, prediction intervals</implementation>84 <rationale>Point estimates without uncertainty are misleading</rationale>85</principle>8687</pedagogical_principles>8889<thinking_process>90For EVERY analysis:911. Document data provenance (sources, collection, preprocessing)922. Validate data structure and types933. State assumptions explicitly944. Check for data quality issues955. Document all transformations966. Choose appropriate methods977. Implement with defensive checks988. Include reproducibility info999. Interpret with appropriate caution100</thinking_process>101102<implementation_pattern>103104<code_template>105```python106@app.cell107def rigorous_analysis_pattern(dependencies):108 #This template demonstrates the mandatory structure for109 # all statistical analyses. Every element serves a specific purpose110 # in ensuring reproducibility and validity.111112 import pandas as pd113 import numpy as np114 import sys115 import warnings116 from datetime import datetime117118 # MANDATORY: Input validation119 assert 'df' in locals(), "DataFrame 'df' must be provided"120 required_columns = {'outcome', 'treatment', 'unit_id'}121 missing = required_columns - set(df.columns)122 assert not missing, f"Missing required columns: {missing}"123124 # MANDATORY: Data type validation125 numeric_columns = ['outcome']126 for col in numeric_columns:127 assert pd.api.types.is_numeric_dtype(df[col]), f"{col} must be numeric"128129 # MANDATORY: Document data provenance130 print("=" * 60)131 print("DATA PROVENANCE")132 print("=" * 60)133 print("Original data source: [Document where data came from]")134 print("Collection method: [How was data collected? Survey, administrative records, etc.]")135 print("Collection dates: [When was data collected?]")136 print("Data access date: [When did you access/download the data?]")137 print("Preprocessing steps (before this analysis):")138 print(" - [List any cleaning, merging, or transformations done outside this notebook]")139 print(" - [Document any data preparation steps]")140 print("Data version/identifier: [File hash, version number, or unique identifier]")141 142 # MANDATORY: Document data properties143 print("\n" + "=" * 60)144 print("DATA VALIDATION")145 print("=" * 60)146 print(f"N observations: {len(df):,}")147 print(f"N units: {df['unit_id'].nunique():,}")148 print(f"Missing outcome: {df['outcome'].isna().sum()} ({df['outcome'].isna().mean()*100:.1f}%)")149150 # MANDATORY: Show any data transformations explicitly151 if df['outcome'].isna().any():152 print(f"\nWARNING: Dropping {df['outcome'].isna().sum()} observations with missing outcome")153 df_analysis = df.dropna(subset=['outcome']).copy()154 else:155 df_analysis = df.copy()156157 # MANDATORY: State assumptions158 assumptions = """159 STATISTICAL ASSUMPTIONS:160 1. Independence: Observations are independent (may be violated if clustered)161 2. Correct specification: The model captures the true relationship162 3. No measurement error: Variables are measured accurately163 """164 print(assumptions)165166 # [Actual analysis code here]167168 # MANDATORY: Reproducibility information (always the last cell)169 import platform170 import json as _json171 _repro = {172 "python": sys.version,173 "platform": platform.platform(),174 "pandas": pd.__version__,175 "statsmodels": __import__('statsmodels').__version__,176 "numpy": np.__version__,177 "matplotlib": __import__('matplotlib').__version__,178 }179 print("Reproducibility Information")180 print("=" * 60)181 print(_json.dumps(_repro, indent=2))182183 return df_analysis,184```185</code_template>186187</implementation_pattern>188189<examples>190191<example context="encoding_detection" difficulty="basic">192<description>Detecting file encoding before reading CSV files</description>193<code>194```python195@app.cell196def detect_and_load_csv(filepath):197 #RATIONALE: Encoding mismatches cause silent character corruption.198 # We detect encoding first, document it for reproducibility, and199 # handle common encoding issues before reading data.200201 import pandas as pd202 import os203 import chardet # pip install chardet204205 # MANDATORY: Check file exists206 assert os.path.exists(filepath), f"File not found: {filepath}"207208 # MANDATORY: Detect encoding before reading209 print("=" * 60)210 print("ENCODING DETECTION")211 print("=" * 60)212 213 # Read a sample to detect encoding (faster than reading entire file)214 with open(filepath, 'rb') as f:215 raw_data = f.read(10000) # Read first 10KB for detection216 217 detection_result = chardet.detect(raw_data)218 detected_encoding = detection_result['encoding']219 confidence = detection_result['confidence']220 221 print(f"Detected encoding: {detected_encoding}")222 print(f"Confidence: {confidence:.2%}")223 224 # Common encodings to try if detection is uncertain225 encodings_to_try = [226 detected_encoding, # Try detected first227 'utf-8', # Most common modern encoding228 'latin-1', # Common for European data229 'iso-8859-1', # Alternative to latin-1230 'cp1252', # Windows encoding231 'utf-16', # Unicode with BOM232 ]233 234 # Remove duplicates while preserving order235 encodings_to_try = list(dict.fromkeys(encodings_to_try))236 237 # Try each encoding until one works238 df = None239 used_encoding = None240 241 for encoding in encodings_to_try:242 if encoding is None:243 continue244 try:245 print(f"\nAttempting to read with encoding: {encoding}")246 df = pd.read_csv(filepath, encoding=encoding, nrows=5) # Test with 5 rows first247 used_encoding = encoding248 print(f"SUCCESS: File readable with {encoding}")249 break250 except (UnicodeDecodeError, UnicodeError) as e:251 print(f" Failed: {str(e)[:60]}...")252 continue253 except Exception as e:254 # Other errors (not encoding-related) - re-raise255 raise256 257 if df is None:258 raise ValueError(f"Could not read file with any encoding tried: {encodings_to_try}")259 260 # Now read full file with correct encoding261 print(f"\nReading full file with encoding: {used_encoding}")262 df = pd.read_csv(filepath, encoding=used_encoding)263 264 # MANDATORY: Document encoding for reproducibility265 print("\n" + "=" * 60)266 print("ENCODING INFORMATION (for reproducibility)")267 print("=" * 60)268 print(f"File encoding: {used_encoding}")269 print(f"Detection confidence: {confidence:.2%}")270 print(f"File size: {os.path.getsize(filepath):,} bytes")271 272 return df, used_encoding,273```274</code>275<output_interpretation>276Encoding detection prevents silent character corruption. Common issues:277- UTF-8 files read as latin-1: accented characters become garbage278- Windows-1252 files read as UTF-8: parsing fails on special characters279- Mixed encodings: some rows readable, others fail silently280281Always document the encoding used for reproducibility.282</output_interpretation>283<best_practice>284For CSV files:2851. Detect encoding before reading (use chardet or charset-normalizer)2862. Try common encodings if detection uncertain2873. Document encoding used in reproducibility section2884. If encoding detection unavailable, try: utf-8, latin-1, cp1252 in order2895. Check for encoding errors in string columns after loading290</best_practice>291</example>292293<example context="validation_catches_error" difficulty="basic">294<description>Defensive validation preventing silent failure</description>295<code>296```python297@app.cell298def validate_before_analysis(df):299 #This example shows how defensive validation catches300 # errors that would otherwise produce invalid results silently.301 # Based on real retractions where analyses ran on wrong data types.302303 import pandas as pd304 import numpy as np305306 # Scenario: User thinks 'treatment' is binary but it's actually continuous307 print("Checking treatment variable...")308309 # MANDATORY: Validate assumptions about the data310 unique_vals = df['treatment'].unique()311 print(f"Unique treatment values: {unique_vals}")312313 if not set(unique_vals).issubset({0, 1}):314 print("\nERROR: Treatment is not binary!")315 print(f"Found values: {unique_vals}")316 print("\nThis would have caused:")317 print("- Wrong model specification")318 print("- Invalid causal interpretation")319 print("- Meaningless 'treatment effect'")320321 # Show what would happen with silent failure322 if len(unique_vals) > 2:323 # Demonstrate the error324 mean_by_treatment = df.groupby('treatment')['outcome'].mean()325 print(f"\nMeans by 'treatment': \n{mean_by_treatment}")326 print("\nWARNING: These are NOT treatment effects!")327328 raise ValueError("Treatment must be binary (0/1) for causal analysis")329330 print("SUCCESS: Treatment is binary")331332 # Additional validation333 assert df['outcome'].dtype in ['float64', 'int64'], "Outcome must be numeric"334 assert not df[['treatment', 'outcome']].isna().all(axis=1).any(), "Complete observations required"335336 return None,337```338</code>339<output_interpretation>340This validation would catch a critical error where continuous dose is mistaken for binary treatment, preventing false causal claims.341</output_interpretation>342</example>343344<example context="transformation_transparency" difficulty="intermediate">345<description>Making all data transformations explicit</description>346<code>347```python348@app.cell349def transparent_transformations(df):350 #Every transformation is shown and justified to prevent351 # hidden researcher degrees of freedom. Simmons et al. (2011) show352 # how undisclosed flexibility in data analysis inflates false positive rates.353354 import pandas as pd355 import numpy as np356357 print("DATA TRANSFORMATION DOCUMENTATION")358 print("=" * 60)359360 # Original data summary361 print(f"Original N: {len(df)}")362 print(f"Original outcome range: [{df['outcome'].min():.2f}, {df['outcome'].max():.2f}]")363364 # MANDATORY: Document each transformation step365 df_transformed = df.copy()366367 # Step 1: Handle outliers (document cutoff and rationale)368 Q1 = df['outcome'].quantile(0.25)369 Q3 = df['outcome'].quantile(0.75)370 IQR = Q3 - Q1371 outlier_threshold = Q3 + 3*IQR # Using 3*IQR for extreme outliers only372373 n_outliers = (df['outcome'] > outlier_threshold).sum()374 print(f"\nStep 1: Windsorizing {n_outliers} extreme outliers (>{outlier_threshold:.2f})")375 print(f" Rationale: Values >3*IQR likely measurement errors")376 print(f" Alternative: Could use log transformation instead")377378 df_transformed.loc[df_transformed['outcome'] > outlier_threshold, 'outcome'] = outlier_threshold379380 # Step 2: Create categorical variable (document cutoffs)381 median_val = df_transformed['outcome'].median()382 print(f"\nStep 2: Creating binary indicator at median ({median_val:.2f})")383 print(f" Rationale: Testing for heterogeneous effects above/below median")384 print(f" Note: This reduces power but aids interpretation")385386 df_transformed['outcome_high'] = (df_transformed['outcome'] > median_val).astype(int)387388 # Step 3: Log transformation (document reason)389 print(f"\nStep 3: Log transformation of outcome")390 print(f" Skewness before: {df['outcome'].skew():.2f}")391392 # Handle zeros if present393 min_positive = df_transformed[df_transformed['outcome'] > 0]['outcome'].min()394 if (df_transformed['outcome'] == 0).any():395 offset = min_positive / 2396 print(f" Adding offset {offset:.4f} to handle zeros")397 df_transformed['log_outcome'] = np.log(df_transformed['outcome'] + offset)398 else:399 df_transformed['log_outcome'] = np.log(df_transformed['outcome'])400401 print(f" Skewness after: {df_transformed['log_outcome'].skew():.2f}")402403 # MANDATORY: Summary of all changes404 print("\n" + "=" * 60)405 print("TRANSFORMATION SUMMARY")406 print("=" * 60)407 print(f"Records modified: {(df['outcome'] != df_transformed['outcome']).sum()}")408 print(f"New variables created: outcome_high, log_outcome")409 print(f"Original data preserved in: df")410 print(f"Transformed data in: df_transformed")411412 # MANDATORY: Note impact on interpretation413 print("\nINTERPRETATION NOTE:")414 print("- Results apply to windsorized distribution")415 print("- Log coefficients represent % changes")416 print("- Binary outcome reduces power but simplifies interpretation")417418 return df_transformed,419```420</code>421<lesson>422Transparency in data transformation prevents p-hacking and enables replication. Every choice must be documented and justified.423</lesson>424</example>425426<example context="data_provenance_documentation" difficulty="intermediate">427<description>Documenting complete data provenance</description>428<code>429```python430@app.cell431def document_data_provenance():432 #Data provenance documents the complete lineage of data:433 # where it came from, how it was collected, what preprocessing434 # occurred, and when it was accessed. This is essential for435 # reproducibility and validity assessment (Stodden et al., 2016).436437 import hashlib438 import os439 from datetime import datetime440441 provenance_info = {442 "data_sources": {443 "primary_dataset": {444 "source_name": "RCT Baseline Survey",445 "source_type": "Primary data collection",446 "collection_method": "Face-to-face interviews using ODK Collect",447 "collection_dates": {448 "start": "2024-01-15",449 "end": "2024-03-20"450 },451 "collection_location": "Rural districts in Northern Uganda",452 "sampling_method": "Randomized controlled trial baseline",453 "sample_size_collected": 1200,454 "data_access_date": "2024-04-01",455 "data_access_method": "Downloaded from SurveyCTO server",456 "file_path": "/mnt/data/baseline_survey.csv",457 "file_hash": None, # Will compute below458 "file_size_bytes": None,459 "preprocessing_steps": [460 "Exported from SurveyCTO in CSV format",461 "No manual edits performed",462 "Variable names preserved as exported"463 ]464 },465 "treatment_assignments": {466 "source_name": "Randomization List",467 "source_type": "Administrative data",468 "collection_method": "Generated using random number generator",469 "collection_dates": {470 "date": "2024-01-10"471 },472 "randomization_method": "Stratified block randomization",473 "strata": ["district", "gender"],474 "block_size": 4,475 "data_access_date": "2024-04-01",476 "file_path": "/mnt/data/treatment_assignments.csv",477 "file_hash": None,478 "preprocessing_steps": [479 "Generated using R script: randomization.R",480 "Exported to CSV for merging"481 ]482 }483 },484 "data_merging": {485 "merge_date": "2024-04-01",486 "merge_method": "Left join on unit_id",487 "merge_key": "unit_id",488 "merge_validation": "All baseline observations matched",489 "merged_file_path": "/mnt/data/merged_analysis_data.csv"490 },491 "data_limitations": [492 "Baseline survey had 5% non-response rate",493 "Missing income data for 12% of sample (documented in analysis)",494 "Treatment assignments were double-blind"495 ]496 }497498 # Compute file hashes for data integrity499 for source_key, source_info in provenance_info["data_sources"].items():500 filepath = source_info.get("file_path")501 if filepath and os.path.exists(filepath):502 with open(filepath, 'rb') as f:503 file_hash = hashlib.sha256(f.read()).hexdigest()504 file_size = os.path.getsize(filepath)505 provenance_info["data_sources"][source_key]["file_hash"] = file_hash506 provenance_info["data_sources"][source_key]["file_size_bytes"] = file_size507508 # Print human-readable provenance documentation509 print("=" * 70)510 print("DATA PROVENANCE DOCUMENTATION")511 print("=" * 70)512 513 for source_key, source_info in provenance_info["data_sources"].items():514 print(f"\n{source_info['source_name']}")515 print(f" Type: {source_info['source_type']}")516 print(f" Collection method: {source_info['collection_method']}")517 518 if 'collection_dates' in source_info:519 dates = source_info['collection_dates']520 if 'start' in dates:521 print(f" Collection period: {dates['start']} to {dates['end']}")522 else:523 print(f" Collection date: {dates['date']}")524 525 print(f" Data accessed: {source_info['data_access_date']}")526 print(f" File: {source_info['file_path']}")527 528 if source_info.get('file_hash'):529 print(f" SHA-256: {source_info['file_hash'][:16]}...")530 print(f" Size: {source_info['file_size_bytes']:,} bytes")531 532 if 'preprocessing_steps' in source_info:533 print(f" Preprocessing steps:")534 for step in source_info['preprocessing_steps']:535 print(f" - {step}")536 537 if 'data_merging' in provenance_info:538 merge_info = provenance_info['data_merging']539 print(f"\nData Merging")540 print(f" Date: {merge_info['merge_date']}")541 print(f" Method: {merge_info['merge_method']}")542 print(f" Key: {merge_info['merge_key']}")543 print(f" Validation: {merge_info['merge_validation']}")544 545 if 'data_limitations' in provenance_info:546 print(f"\nData Limitations")547 for limitation in provenance_info['data_limitations']:548 print(f" - {limitation}")549 550 # Save structured provenance to JSON551 import json552 with open('./data_provenance.json', 'w') as f:553 json.dump(provenance_info, f, indent=2)554 555 print(f"\nSUCCESS: Full provenance documentation saved to ./data_provenance.json")556 557 return provenance_info,558```559</code>560<best_practice>561Data provenance must document:5621. **Original sources**: Where data came from (surveys, administrative records, APIs, etc.)5632. **Collection methods**: How data was collected (survey instruments, measurement tools, protocols)5643. **Collection dates**: When data was collected (affects external validity)5654. **Data access**: When and how you accessed the data5665. **Preprocessing**: Any cleaning, merging, or transformations done BEFORE your analysis code5676. **File integrity**: File hashes to detect if data changes5687. **Limitations**: Known data quality issues or constraints569570This enables others to:571- Verify data authenticity572- Assess data quality573- Understand potential biases574- Replicate data preparation steps575- Detect preprocessing errors576</best_practice>577</example>578579<example context="reproducibility_appendix" difficulty="basic">580<description>Reproducibility cell -- MUST be the last cell in every notebook response</description>581<code>582```python583import sys, platform, json584585_repro = {586 "python": sys.version,587 "platform": platform.platform(),588 "pandas": pd.__version__,589 "statsmodels": __import__('statsmodels').__version__,590 "numpy": np.__version__,591 "matplotlib": __import__('matplotlib').__version__,592}593print("Reproducibility Information")594print("=" * 60)595print(json.dumps(_repro, indent=2))596```597</code>598<best_practice>599This cell is MANDATORY in every response. It documents the computational600environment so results can be verified. Include it as the final cell, always.601Additional keys (e.g. random seed, file hashes) may be appended when relevant.602</best_practice>603</example>604605</examples>606607<common_mistakes>608609<mistake severity="critical">610 <what>Running analysis without input validation</what>611 <consequence>Silent failures producing wrong results that look valid</consequence>612 <prevention>ALWAYS validate data types, ranges, and required columns first</prevention>613</mistake>614615<mistake severity="critical">616 <what>Transforming data without documentation</what>617 <consequence>Hidden researcher degrees of freedom, unreproducible results</consequence>618 <prevention>Document every transformation with rationale</prevention>619</mistake>620621<mistake severity="high">622 <what>Omitting software versions from reports</what>623 <consequence>Results cannot be reproduced when packages update</consequence>624 <prevention>Always include sessionInfo() or equivalent</prevention>625</mistake>626627<mistake severity="high">628 <what>Using magic numbers without explanation</what>629 <consequence>Future researchers don't understand choices</consequence>630 <prevention>Define all constants with explanatory comments</prevention>631</mistake>632633<mistake severity="medium">634 <what>Not setting random seeds</what>635 <consequence>Results vary between runs</consequence>636 <prevention>Set seeds for all stochastic operations</prevention>637</mistake>638639<mistake severity="critical">640 <what>Omitting data provenance documentation</what>641 <consequence>Cannot verify data authenticity, assess quality, detect preprocessing errors, or understand limitations. Hidden preprocessing steps can introduce bias that invalidates findings</consequence>642 <prevention>ALWAYS document: data sources, collection methods, access dates, and any preprocessing done before analysis. Include file hashes for integrity verification</prevention>643</mistake>644645<mistake severity="high">646 <what>Reading CSV files without checking encoding</what>647 <consequence>Silent character corruption (accented characters become garbage), parsing failures, incorrect string comparisons, missing data from encoding errors</consequence>648 <prevention>ALWAYS detect encoding before reading text files. Use chardet or charset-normalizer, try common encodings (utf-8, latin-1, cp1252) if detection uncertain, and document encoding used for reproducibility</prevention>649</mistake>650651</common_mistakes>652653<interpretation_guide>654655<interpreting_results>656- Statistical significance ≠ practical importance657- Effect sizes need context and units658- Confidence intervals show precision659- Multiple testing inflates Type I error660</interpreting_results>661662<red_flags>663- P-values just below 0.05 (possible p-hacking)664- Missing data patterns correlating with treatment665- Outliers driving results666- Assumptions clearly violated667</red_flags>668669<next_steps>670- All validations pass → Proceed with analysis671- Assumptions violated → Document and use robust methods672- Missing data substantial → Implement appropriate handling673- Results surprising → Check for errors and run sensitivity analyses674</next_steps>675676</interpretation_guide>677678<references>679<paper>Simmons, J.P., Nelson, L.D. & Simonsohn, U. (2011). "False-Positive Psychology." Psychological Science. Researcher degrees of freedom.</paper>680<paper>Gelman, A. & Loken, E. (2014). "The Statistical Crisis in Science." American Scientist. Garden of forking paths.</paper>681<paper>Nosek, B.A. et al. (2015). "Estimating the reproducibility of psychological science." Science. Reproducibility crisis.</paper>682<paper>Stodden, V., Seiler, J. & Ma, Z. (2016). "An empirical analysis of journal policy effectiveness for computational reproducibility." PNAS. Data provenance and reproducibility.</paper>683</references>684685</skill_content>