An enterprise-grade causal inference and data engineering repository investigating hedonic price decay gradients and quality premiums within the municipal accommodation market of Vienna, Austria.
Engineered to demonstrate dual competency in rigorous econometrics (admissions standard for LSE/UCL causal data science programs) and production-grade software infrastructure (tech startup engineering standard).
| Category | Technology / Package | Target / Version | Architectural Function |
|---|---|---|---|
| Language | Python |
>= 3.12 |
Core computing environment & execution pipeline |
| Anti-Bot Driver | SeleniumBase |
>= 4.30.0 |
Undetected Chromedriver (uc=True) bypassing Cloudflare / anti-bot challenges |
| Network Router | proxy.py |
>= 2.4.0 |
Asynchronous local proxy tunnel isolating residential ISP footprint |
| HTML Parser | BeautifulSoup4 + lxml |
>= 4.12.0 |
C-speed DOM parsing on instantaneous in-memory HTML snapshots |
| Econometric Engine | Statsmodels |
>= 0.14.0 |
Hedonic OLS regressions, robust standard errors (HC3), parameter conditioning |
| Data Matrix | Pandas & NumPy |
>= 2.0.0 |
Vectorized quality gates, array transformations, and cross-sectional audits |
| Data Visualization | Seaborn & Matplotlib |
>= 3.7.0 |
Empirical density plots, hedonic spatial gradient scatter, box plots |
| Workflow Lab | Jupyter Notebooks |
nbformat 4 |
Interactive modular research notebooks (causal-notebooks/) |
| Package Manager | Astral uv |
Latest | High-performance deterministic virtual environment manager |
| Configuration | PyYAML |
>= 6.0 |
Decoupled YAML configuration separating code from parameters |
In urban economics and consumer search theory, spatial proximity to central commercial and cultural hubs commands a measurable pricing premium. However, estimating this gradient in digital accommodation markets introduces a critical identification hazard: the simultaneity and temporal price drift problem.
Modern online travel agencies (OTAs) utilize real-time algorithmic pricing engines that dynamically update rates based on demand, concurrent user sessions, and localized inventory shocks. If a data collection pipeline takes 30 minutes to paginate across a city, prices recorded at listing #1 and listing #400 reflect temporal market drift rather than true cross-sectional equilibrium prices.
-
Anti-Drift Dynamic DOM Snapshotting: The extraction infrastructure hydrates 436+ municipal properties into a single active DOM instance and executes an instantaneous in-memory snapshot (
driver.page_source). All listings are captured within a synchronized sub-second window, neutralizing algorithmic price drift. -
Construct Validity: All distances are geodetically standardized to statutory radial miles from Vienna's Kilometre Zero (Stephansplatz:
$48.2085^\circ\text{ N}, 16.3738^\circ\text{ E}$ ).
This repository adheres to enterprise separation of concerns, isolating data collection mechanics from causal estimation pipelines:
📁 vienna-causal-pricing/
│
├── 📄 .gitignore ◄── Enterprise hygiene (blocks cache, venv, and raw data binaries)
├── 📄 LICENSE ◄── Open-source MIT License
├── 📄 README.md ◄── Executive pitch, architecture map, and replication guide
├── 📄 requirements.txt ◄── Deterministic dependency locks (Pandas, Statsmodels, Seaborn)
│
├── 📁 infra-scraper/ ◄── THE COLLECTION LAB (Isolated Infrastructure)
│ ├── 📄 README.md ──► Technical specs of the anti-drift acquisition loop
│ ├── 📄 pipeline_scraper.py ──► Clean, parameterized collection code (SeleniumBase UC + proxy)
│ ├── 📄 config.yaml ──► Local execution parameters (Git-ignored)
│ └── 📄 config.yaml.example ──► Sanitized configuration template for external replication
│
├── 📁 data-warehouse/ ◄── MATRIX STORAGE (Git-Ignored Data Assets)
│ ├── 📄 .gitkeep ──► Preserves folder structure in Git without committing binaries
│ └── 📄 hotels_vienna_scraped_raw.csv ──► Local data asset (Git-ignored to avoid legal bloat)
│
└── 📁 causal-notebooks/ ◄── CAUSAL ESTIMATION PIPELINES (Chapter-by-Chapter)
├── 📄 phase1_module1_eda.ipynb ──► Exploratory Data Analysis & Quality Audits
├── 📄 phase3_module3_ols.ipynb ──► Hedonic Regression Conditioning Machine
└── 📄 phase6_module6_did.ipynb ──► Quasi-Experimental Policy Evaluator
To prevent Legal/Terms of Service redistribution flags and credential leakage:
- Zero Live Endpoints in Git: The scraper code (
pipeline_scraper.py) is completely parameterized throughconfig.yaml. No live URLs or tokens are hardcoded. - Scrubbed Template: Only
config.yaml.exampleis tracked in version control. - Binary Isolation: Raw data files (
*.csv,*.xlsx) are strictly git-ignored. Users store their raw matrices locally indata-warehouse/while keeping the remote repository lightweight and compliant. Raw replication assets are archived on open-science platforms (e.g., Open Science Framework - OSF).
The collection pipeline executes strict programmatic assertions prior to persisting datasets:
============================================================
RUNNING IN-LINE ECONOMETRIC QUALITY GATES
============================================================
PASS: df['hotel_id'].is_unique passed (436 unique records).
PASS: Sample size (436) meets critical threshold (>= 100).
PASS: B&K Econometric Benchmark achieved: 436 >= 428 properties.
MISSING VALUE REPORT:
price: 0.00% (Zero missingness)
distance: 0.00% (Zero missingness, normalized to statutory miles)
rating: 3.90% (Newly listed/unreviewed boutique units)
stars: 75.92% (Unrated apartments/pensions)
git clone https://github.com/hashexplaindata/vienna-causal-pricing.git
cd vienna-causal-pricing
# Install pinned dependencies
pip install -r requirements.txt
# Alternatively, with uv:
uv pip install -r requirements.txtOpen and run causal-notebooks/phase1_module1_eda.ipynb in your favorite Jupyter environment:
jupyter lab causal-notebooks/phase1_module1_eda.ipynbThe notebook utilizes zero-config relative pathing (../data-warehouse/hotels_vienna_scraped_raw.csv) and runs out-of-the-box.
cd infra-scraper
cp config.yaml.example config.yaml
# Run live scraper with headed browser
python pipeline_scraper.pyThis project is licensed under the MIT License - see the LICENSE file for details.