Skip to content

radyslav-datascience/substitution_coefficient_validation

Repository files navigation

Substitution Coefficient Validation

The median substitution coefficient is stable. But what lies beneath the median?
A four-study investigation into the structure, reliability limits, and open problems
of per-drug substitution coefficients in the Kyiv pharmacy market.

lomanov.dev Python 3.13 scipy 4 Studies 386 SKU License


Pairwise correlation of median substitution coefficients across 3 datasets and 3 coverage ranges. Pearson r β‰₯ 0.997 in all 9 comparisons.

Pairwise correlation of median substitution coefficients: SP1 (99 markets) vs SP3 (144) vs SP4 (243),
across three exclusive coverage ranges. Pearson r β‰₯ 0.997 in all 9 comparisons β€” zero degradation by coverage level.


Key Findings

  • The median is rock-solid: r β‰₯ 0.9969 across all 9 pairwise comparisons, mean deviation < 2%, no selection bias β€” coefficients are a property of the market, not the sample
  • But per-market distributions are wide: IQR ~14 pp, dominant bin covers only ~37% of markets β€” one number is not enough for per-pharmacy recommendations
  • 71.5% of drugs are UNIMODAL β€” one stable coefficient, production-ready, median 50 markets needed to confirm
  • 23.3% are SPREAD β€” wide uniform distribution with no natural peaks; attempting GMM segmentation yields r = 0.52, and this instability is fundamental: internal split r = 0.82, never β‰₯ 0.95 in 20 trials

The Discovery Chain

Each study was motivated by the finding of the previous one:

Study 01: Median coefficient is stable (r β‰₯ 0.997) across datasets
    β”‚
    └─► But IQR is large (~14 pp). One number isn't enough for per-pharmacy use.
        ↓
Study 02: Sequential algorithm classifies each drug's distribution type.
          71.5% UNIMODAL / 23.3% SPREAD / 0.8% PEAKS / 4.4% BORDERLINE.
          Median 50 markets needed per drug.
    β”‚
    └─► Now we know the type. Can we compute per-segment coefficients via GMM?
        ↓
Study 03: UNIMODAL cross-validates at r = 0.994 βœ…
          MULTIMODAL_SPREAD cross-validates at r = 0.52 β€” unstable regardless of method ❌
    β”‚
    └─► Is the instability a method problem, or something deeper?
        ↓
Study 03b: Internal SP3 50/50 splits yield r = 0.82. Never β‰₯ 0.95 in 20 trials.
           SPREAD instability is fundamental β€” not a method problem.
           These drugs have no natural segmentation point. Different modeling required.

Results

Study 01 β€” The median holds

Pearson r across coverage tiers: no degradation

Pearson r stays β‰₯ 0.997 regardless of how strictly drugs are filtered by coverage level. Drugs at the 70–80% coverage threshold are just as stable as drugs at β‰₯90%.

Selection bias check: no correlation between coverage and variability

Selection bias test: the high stability isn't because we're only looking at "easy" drugs. Coverage level has zero correlation with coefficient variability (r β‰ˆ 0).

Per-drug per-market coefficient distribution examples

But the per-market distribution around that stable median is wide. The chart above shows examples across coverage tiers β€” the spread within each drug motivates everything that follows.


Study 02 β€” How many markets per drug?

A Sequential Analyzer simulates the production scenario: markets arrive one by one per drug, and after each batch of 5 new markets, a Bootstrap Dip Test checks whether the distribution type has stabilized. The algorithm stops automatically as soon as confidence β‰₯ 80% AND IQR stability β‰₯ 80% are met simultaneously β€” saving compute on markets that would not change the result.

Drug class distribution across 386 SKU

How confidence builds with each additional market

Distribution of minimum markets required to classify each drug

Class Count Share What it means
UNIMODAL 276 71.5% Median is sufficient β€” one coefficient per drug
MULTIMODAL_SPREAD 90 23.3% Wide spread β€” customers behave differently across pharmacies
MULTIMODAL_PEAKS 3 0.8% Two distinct behavioral peaks β€” GMM finds them cleanly
BORDERLINE 17 4.4% Distribution is objectively ambiguous β€” cannot classify

95.6% of 386 drugs classified. Typical drug stabilizes in 26–50 markets (69.5% of confident cases). A production deployment of this algorithm would stop data collection automatically rather than running to a fixed market count.


Study 03 β€” Per-segment coefficients via GMM

For UNIMODAL drugs: fit a single median. For MULTIMODAL_SPREAD and PEAKS: use Gaussian Mixture Models to identify clusters of pharmacies with similar substitution behavior and compute a separate coefficient per cluster. Cross-validate by comparing the pipeline output on SP3 (144 markets) vs SP1 (99 different markets).

Cross-validation SP3 vs SP1: COEF_1 per drug class

The class split is sharp:

Class N Pearson r (SP3 vs SP1) Status
UNIMODAL 231 0.994 βœ… Production-ready
MULTIMODAL_SPREAD 61 0.516 ❌ Unstable
BORDERLINE 12 0.240 ❌ Not classifiable

Drug counts processed per dataset

UNIMODAL is solved. SPREAD is not β€” and Study 03b investigates why.


Study 03b β€” SPREAD is a fundamental problem

Pearson r across 4 segmentation methods for SPREAD class

Four segmentation methods tested: single median, 2-segment threshold, 3-segment threshold, current GMM. None broke r = 0.94 cross-study. The problem is not the method.

Within-SP3 internal 50/50 split validation: r = 0.82

Internal validation: 20 random 50/50 splits of SP3 markets per drug. Mean r = 0.82, never β‰₯ 0.95 in any trial. The SPREAD drug's substitution coefficient is sensitive to which specific pharmacies ended up in the sample β€” because these drugs have no natural clustering point: SHARE_INTERNAL is uniformly spread across [0, 1] with no dominant peak or segment.


Verdict

Class Result Path forward
UNIMODAL (71.5%) βœ… Solved Median coefficient β€” reliable, production-ready
MULTIMODAL_PEAKS (0.8%) βœ… Solved GMM identifies peaks cleanly
BORDERLINE (4.4%) ⚠️ Open Insufficient data β€” collect more markets
MULTIMODAL_SPREAD (23.3%) ❌ Open Fundamentally unstable β€” requires per-pharmacy observable features as segmentation criteria, not per-drug distribution clustering

Project Structure

substitution_coefficient_validation/
β”‚
β”œβ”€β”€ study_01_cross_study_validation/    # Median stability across 3 datasets
β”‚   β”œβ”€β”€ scripts/                        # 01_cross_study_comparison.py, 02_distribution_profile.py
β”‚   β”œβ”€β”€ docs/                           # Research context (full methodology)
β”‚   └── results/charts Β· tables Β· reports Β· conclusions/
β”‚
β”œβ”€β”€ study_02_sample_size_sufficiency/   # Sequential Analyzer: min markets per drug
β”‚   β”œβ”€β”€ scripts/                        # 00_prepare β†’ 01_sequential_analyzer β†’ 02_classify β†’ 03_reports
β”‚   β”œβ”€β”€ core_parameters/               # All algorithm thresholds
β”‚   β”œβ”€β”€ determining_markets_algorithm/ # Production-ready algorithm implementation
β”‚   β”œβ”€β”€ docs/                           # Context, parameters guide, glossary
β”‚   └── results/charts Β· reports Β· conclusions/
β”‚
β”œβ”€β”€ study_03_pharmacy_categorization/   # GMM per-segment coefficient pipeline
β”‚   β”œβ”€β”€ methods_research/               # 4 methods explored: unimodal / peaks / spread / borderline
β”‚   β”œβ”€β”€ pipeline/                       # Final pipeline: classify β†’ compute β†’ merge β†’ report β†’ cross-validate
β”‚   β”œβ”€β”€ docs/                           # Research context and work plan
β”‚   └── results/cross_validation/      # SP3 vs SP1 charts and reports
β”‚
β”œβ”€β”€ study_03_b/                         # SPREAD instability investigation
β”‚   β”œβ”€β”€ scripts/                        # 01_compute β†’ 02_cross_validate β†’ 03_within_split
β”‚   └── results/comparison Β· conclusions/
β”‚
└── config/                             # Shared paths across studies

How to Run

Requirements: Python 3.11+, input coefficient files from the canonical pipeline

pip install -r requirements.txt

Dataset routing is configured via config/paths.py for inter-study dependencies and pipeline/config/paths.py for Study 03. Set SOURCE_DATASET in the respective config before running.

# Study 01
python study_01_cross_study_validation/scripts/01_cross_study_comparison.py
python study_01_cross_study_validation/scripts/02_distribution_profile.py

# Study 02 (run in order; step 01 takes ~20 min)
python study_02_sample_size_sufficiency/scripts/00_data_preparation.py
python study_02_sample_size_sufficiency/scripts/01_sequential_analyzer.py
python study_02_sample_size_sufficiency/scripts/02_classification.py
python study_02_sample_size_sufficiency/scripts/03_reports.py

# Study 03 pipeline (configure SOURCE_DATASET in pipeline/config/paths.py first)
python study_03_pharmacy_categorization/pipeline/scripts/01_classify.py
python study_03_pharmacy_categorization/pipeline/scripts/02_compute_coefficients.py
python study_03_pharmacy_categorization/pipeline/scripts/03_merge_library.py
python study_03_pharmacy_categorization/pipeline/scripts/04_reports.py
python study_03_pharmacy_categorization/pipeline/scripts/05_cross_validation.py

# Study 03b
python study_03_b/scripts/01_compute.py
python study_03_b/scripts/02_cross_validate.py
python study_03_b/scripts/03_within_split.py

Data Privacy

Per-market coefficient files and drug-level result tables are excluded. Published results contain aggregate statistics only: correlation matrices, distribution charts, confidence summaries. No drug names are paired with quantitative metrics in any published file. No personally identifiable information is present at any stage.

See SECURITY.md for the full data privacy statement.


License

Β© 2026 Radyslav Lomanov. All Rights Reserved.
See LICENSE for full terms.


Author

Radyslav Lomanov β€” Data Scientist, Pharmaceutical Market Research

Website lomanov.dev
Email lomanov.mail@gmail.com
Telegram @radyslav_datascience
GitHub radyslav-datascience

Related projects:

About

Four-study investigation into drug substitution coefficient structure: cross-dataset stability, distribution type classification, GMM per-segment coefficients, and SPREAD instability analysis.

Resources

License

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages