# White Center healthy-value restaurant dataset

Which restaurant menu items within 2 miles of 47.50709834148783, -122.35509354690024 (White Center, Seattle)
are the healthiest for the money. Built 2026-09-05 with Google Maps as the primary source.

## Deliverables

| File | What it is |
|---|---|
| `data/healthy_value.sqlite` | Everything: `restaurants`, `gbp`, `reviews`, `popular_dishes`, `menu_items`, `menu_status`, `item_scores`, `item_rank`, `restaurant_scores` |
| `data/restaurants.csv` | 118 restaurants from Google Maps with distance, rating, price band, category, links |
| `data/menu_items.csv` | 771 extracted items with price, section, type and health score |
| `data/item_rank.csv` | Mains ranked by value index, with nutrition estimate and match diagnostics |
| `data/restaurant_scores.csv` | Per-restaurant aggregates: mean/best health, cheapest item scoring >= 70, menu status |
| `data/price_verification_sample.csv` | The 40 hand-checked prices |
| `data/verification_log.md` | What was checked at each stage, and what failed |
| `report/index.html` | The shareable report |
| `raw/` | Every API response and page fetch, immutable, keyed by request |

## Method

1. **Discovery (Google Maps).** DataForSEO Maps SERP, 3x3 grid of search origins 1.5 km apart, 10 keywords, 90 live calls.
   Deduped on `place_id`, filtered to <= 3.22 km and to food categories by Google category id. 118 restaurants.
2. **Enrichment (Google).** Business profile per place (attributes, description, menu link, review-topic chips) and the 20 newest reviews.
   Review text and topic chips become `popular_dishes`.
3. **Menus.** Google's own Menu link first, then the website's menu path, then the homepage. Pages extracted with Firecrawl JSON
   extraction; one Squarespace menu parsed from cached HTML. Extractions that looked fabricated were rejected (see the log).
   National chains were not itemized: their menu pages show no store prices.
4. **Health score (0-100).** `0.6 x nutrient_density + 0.4 x rules` when a USDA FNDDS food matches, rules alone otherwise.
   - Nutrient density (per matched FNDDS food, default portion, mains floored at 150 g): protein and fibre per 100 kcal up,
     saturated fat and sodium per 100 kcal down, calories per portion penalised above 800. Weights and reference points are the
     dicts at the top of `pipeline/04_score.py`.
   - Rules (`pipeline/lib/rules.py`): regex points for preparation (fried -18, grilled/steamed +8), protein (fish +8, plant +8,
     processed meat -10), vegetables and whole grains (+6 to +10), heaviness (cheese -8, creamy -8, loaded/double -8). Starts at 50.
   - FNDDS matching (`pipeline/lib/usda.py`): menu vocabulary translated (carne asada -> beef, pho -> soup pho, larb -> ground chicken
     with vegetables ...), rapidfuzz token-set match, penalties for a mismatched protein or a different dish form, non-dish foods
     excluded. Accepted at >= 75. Every match and its score is in `item_scores`.
5. **Value index** = `health_score x sqrt(median_main_price / price)`, mains priced >= $6 only. Median main price across the
   itemized menus is $15.45. `price_per_100kcal` and `protein_g_per_dollar` are also emitted.

## Results at a glance

- 118 restaurants within 2 miles. 73 carry a Google price band (49 inexpensive, 24 moderate). 35 list vegetarian options, 25 "healthy options".
- 22 restaurants (19%) have a menu Google links to that can be read as text. 771 items, 525 mains.
- Hand-checked price accuracy: 33 of 34 verifiable samples correct (97%).
- Top healthy-value mains cluster in three places: house/garden salads at pizza and South American spots ($7.45-$8.50),
  broth soups (miso, bean, pho) at $8-$11, and Thai/Vietnamese vegetable-forward noodle plates at $15.

## Limitations (read before using the numbers)

- **Google Maps has no itemized prices and no nutrition.** Prices come from the menu page Google links to; nutrition is an FNDDS
  estimate, not a lab value. About 1 in 4 FNDDS matches is a loose proxy (a combo plate matched to a generic stew).
- **Coverage is the binding constraint.** 96 of 118 restaurants have no readable text menu: image menus, ordering apps that render
  nothing to a crawler, no website, or a national chain. They appear in the restaurant table with Google-level signals only.
- Online-ordering prices (Toast, HungerRush, Slice) can differ from the printed in-store menu.
- Prices are as published on 2026-09-05.

## Cost

DataForSEO $0.996 (Maps SERP $0.18, profiles $0.64, reviews $0.18). Firecrawl about 325 credits. USDA free.

## Reproduce

```bash
source pipeline/00_env.sh
.venv/bin/python pipeline/01_discover.py
.venv/bin/python pipeline/02_enrich.py && .venv/bin/python pipeline/02b_dishes.py
.venv/bin/python pipeline/03_resolve_menus.py        # then Firecrawl extractions into raw/menus/tsv/
.venv/bin/python pipeline/03b_ingest_menu.py
.venv/bin/python pipeline/04_score.py && .venv/bin/python pipeline/05_rank.py
.venv/bin/python pipeline/06_report.py
```
Every remote call is cached under `raw/`, so a rerun is free.
