data-analysis
Patterns for data loading, exploration, and statistical analysis
Best practices for designing reproducible experiments
$ npx -y skills add Yeachan-Heo/My-Jogyo --skill experiment-design --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/experiment-designContext preview
The summary Claude sees to decide when to auto-load this skill.
Best practices for designing reproducible experiments
name: experiment-design description: Best practices for designing reproducible experiments
Load this skill when designing experiments that need to be reproducible and statistically valid.
import random
import numpy as np
SEED = 42
random.seed(SEED)
np.random.seed(SEED)
print(f"[DECISION] Using random seed: {SEED}")import sys
print(f"[INFO] Python: {sys.version}")
print(f"[INFO] NumPy: {np.__version__}")
print(f"[INFO] Pandas: {pd.__version__}")from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=SEED, stratify=y
)
print(f"[EXPERIMENT] Train: {len(X_train)}, Test: {len(X_test)}")from sklearn.model_selection import cross_val_score
scores = cross_val_score(model, X, y, cv=5, scoring='accuracy')
print(f"[METRIC] CV Accuracy: {scores.mean():.3f} (+/- {scores.std()*2:.3f})")print("[EXPERIMENT] A/B Test Design")
print(f"[INFO] Control group: {len(control)}")
print(f"[INFO] Treatment group: {len(treatment)}")
# Power analysis
from statsmodels.stats.power import TTestIndPower
power = TTestIndPower()
sample_size = power.solve_power(effect_size=0.5, alpha=0.05, power=0.8)
print(f"[CALC] Required sample size per group: {sample_size:.0f}")Power analysis ensures your experiment has sufficient sample size to detect meaningful effects. Without adequate power, you risk false negatives (missing real effects).
from statsmodels.stats.power import TTestIndPower, FTestAnovaPower, NormalIndPower
import numpy as np
print("[DECISION] Conducting a priori power analysis before data collection")
# For two-group comparison (t-test)
power_analysis = TTestIndPower()
# Parameters:
# - effect_size: Expected Cohen's d (0.2=small, 0.5=medium, 0.8=large)
# - alpha: Significance level (typically 0.05)
# - power: Desired statistical power (typically 0.80 or 0.90)
# - ratio: Ratio of group sizes (1.0 = equal groups)
effect_size = 0.5 # Medium effect size
alpha = 0.05
desired_power = 0.80
sample_size = power_analysis.solve_power(
effect_size=effect_size,
alpha=alpha,
power=desired_power,
ratio=1.0,
alternative='two-sided'
)
print(f"[STAT:estimate] Required n per group: {np.ceil(sample_size):.0f}")
print(f"[STAT:estimate] Total sample needed: {np.ceil(sample_size)*2:.0f}")
print(f"[DECISION] Targeting {effect_size} effect size (Cohen's d = medium)")# After data collection, calculate achieved power
actual_n = 50 # Actual sample size per group
achieved_power = power_analysis.solve_power(
effect_size=effect_size,
alpha=alpha,
nobs1=actual_n,
ratio=1.0,
alternative='two-sided'
)
print(f"[STAT:estimate] Achieved power: {achieved_power:.3f}")
if achieved_power < 0.80:
print(f"[LIMITATION] Study is underpowered ({achieved_power:.0%} < 80%)")
print("[LIMITATION] Negative results may be due to insufficient sample size")
else:
print(f"[DECISION] Adequate power achieved ({achieved_power:.0%} ≥ 80%)")# For ANOVA (multiple groups)
from statsmodels.stats.power import FTestAnovaPower
anova_power = FTestAnovaPower()
n_groups = 3
effect_size_f = 0.25 # Cohen's f (0.1=small, 0.25=medium, 0.4=large)
n_per_group = anova_power.solve_power(
effect_size=effect_size_f,
alpha=0.05,
power=0.80,
k_groups=n_groups
)
print(f"[STAT:estimate] ANOVA: {np.ceil(n_per_group):.0f} per group needed")
# For proportions (chi-square, A/B tests)
from statsmodels.stats.power import GofChisquarePower
from statsmodels.stats.proportion import proportion_effectsize
# Convert expected proportions to effect size
p1, p2 = 0.10, 0.15 # e.g., 10% baseline, 15% expected with treatment
prop_effect = proportion_effectsize(p1, p2)
print(f"[DECISION] Effect size h = {prop_effect:.3f} for proportions test")Pre-registration prevents HARKing (Hypothesizing After Results are Known) and distinguishes confirmatory from exploratory analyses.
print("[DECISION] Pre-registering analysis plan before examining data")
# Document your analysis plan BEFORE looking at the data
preregistration = {
"primary_hypothesis": "H1: Treatment group shows higher conversion rate than control",
"null_hypothesis": "H0: No difference in conversion rates between groups",
"primary_endpoint": "conversion_rate",
"secondary_endpoints": ["time_to_convert", "revenue_per_user"],
"alpha": 0.05,
"correction_method": "Bonferroni for secondary endpoints",
"minimum_effect_size": "5 percentage points (10% → 15%)",
"planned_sample_size": 500,
"analysis_method": "Two-proportion z-test",
"exclusion_criteria": "Users with < 1 day exposure"
}
print(f"[EXPERIMENT] Pre-registered analysis plan:")
for key, value in preregistration.items():
print(f" {key}: {value}")# After analysis, clearly label findings
print("[FINDING] Treatment increases conversion by 4.2pp (95% CI: [1.8, 6.6])")
print("[STAT:ci] 95% CI [1.8, 6.6]")
print("[STAT:effect_size] Cohen's h = 0.12 (small)")
print("[STAT:p_value] p = 0.001")
# This is CONFIRMATORY because it tests pre-registered hypothesis
print("[OBSERVATION] Exploratory: Effect stronger for mobile users (+7.1pp)")
print("[LIMITATION] Mobile subgroup analysis was NOT pre-registered")
print("[DECISION] Flagging as exploratory - requires replication before action")
# Document finding type
CONFIRMATORY = True # Pre-registered hypothesis
EXPLORATORY = False # Post-hoc discovery
def label_finding(finding: str,"Every great professor needs a great teaching assistant." Gyoshu (교수, Professor) orchestrates. Jogyo (조교, Teaching Assistant) executes.
Patterns for data loading, exploration, and statistical analysis
Enforces baseline comparisons, cross-validation, interpretation, and leakage prevention for ML pipelines