Back to Portfolio
Case 03 / 2024

A coffee shop, measured like a website.

A group was opening a community space — a coffee shop, a bar, a makerspace, and a small gallery, all under one roof. They needed to prove to the city it would be worth a $2M grant, and then actually run the place once it opened. I built two analyses: one that projected what the space would be worth to the neighborhood, and one that tracked individual customers the way a website tracks users. The grant cleared. The tools still run the business today.

Data Analysis Python Economic Modeling Customer Analytics
RoleLead Analyst
Year2024
Outcome$2M grant secured
Read time~6 minutes
economic_impact_demo.ipynb Python 3.11 · idle
In [2]:
# Aggregate 12-month projection across all four venues from src.impact import build_forecast, rims_ii_multiplier forecast = build_forecast(venues=VENUES, scenario='base') forecast.summary()
Out[2]:
ANNUAL IMPACT SUMMARY · Year 1 (base case) ──────────────────────────────────────────── Annual visitors ................. 75,500 Direct FTE jobs ................. 19.0 Jobs supported (×1.4) ........... 26.6 Annual revenue .................. $1,035,920 Economic output (×1.8) .......... $1,864,656 Sales tax to city ............... $52,719 ──────────────────────────────────────────── [model ready — grant package exports to PDF]
The thesis, in one line: treat a building like a product. Count every visit, follow every regular, and the math of why someone walks in — and whether they come back — stops being a guess.
— Starting point
What this is

Two questions, answered in plain numbers.

The situation
Two empty buildings, a big idea, and a skeptical city.

The team was turning two vacant buildings into a community space with four businesses inside. To unlock a $2M city grant, they had to show — on paper, before opening day — that the space would bring real jobs, visitors, and tax revenue to the neighborhood.

Question 1 · the city
“What will this place be worth to the neighborhood?”

Visitors per year. Jobs created. Sales tax generated. A five-year revenue projection. The kind of numbers a grant committee can defend in a public meeting.

Question 2 · the operator
“Once we're open, who's actually coming back?”

A good bakery knows its regulars. A good website knows its users. I gave the operators the same thing: who walks in, how often they return, what they spend, and which ones are worth chasing for a membership.

How it's done
Two documents, read top to bottom.

Both analyses live in Jupyter notebooks — working documents that mix written explanation with live calculations, so every number on the page can be traced back to the assumption that produced it. Nothing is hidden in a spreadsheet.

How this page is laid out

A guided tour, in two halves.

  1. Part 1 — the grant case. We start with basic assumptions (how big is the coffee shop, what does a drink cost), simulate a year of foot traffic, then turn those visits into revenue, jobs, and tax dollars. Ends with a five-year projection.
  2. Part 2 — the customer view. Once the doors are open, we shift to individual customers. How to identify them, what a “funnel” looks like for a real-world venue, and why turning a regular into a member is worth roughly 7.5× more than keeping them casual.
  3. Part 3 — how it all connects. A simple diagram of the plumbing: the WiFi sign-in, the register, and the membership list all feed one unified picture of each customer.

No prior data experience required. Technical bits stay in boxes; the story is in the paragraphs around them.

Part 1 · the grant case

What will the space be worth?

The grant committee doesn't care about vibes. They want to know, in dollars and jobs, what this place will do for the neighborhood. To answer honestly, we build the number up from its smallest pieces — the size of each room, the price of a latte — instead of picking a hopeful total and working backwards.

Step 01 · The inputs

Start with four honest facts about each room.

Before any projection, we list what we actually know. How many square feet is the coffee shop? How many seats does the bar have? What does the average customer spend? These aren't guesses — they come from the lease, the floor plan, and comparable venues nearby.

Every chart later in this analysis is built from the four rows shown below. If the coffee shop ends up smaller than planned, we change one number here and every downstream result updates automatically. That's the whole point: no hidden math.

economic_impact_demo.ipynb — §1 Space Configuration idle
[ md ]

1. Space Configuration & Assumptions

Operating assumptions per venue. Edit here; all sections downstream re-derive.

In [3]:
SPACE_CONFIG = { 'coffee_shop': {'sq_ft': 2000, 'capacity': 45, 'avg_ticket': 8.50, 'conversion': 0.70}, 'bar': {'sq_ft': 2800, 'capacity': 95, 'avg_ticket': 18.00, 'conversion': 0.82}, 'makerspace': {'sq_ft': 2200, 'capacity': 30, 'monthly_fee': 95.00, 'lease_rev': 90_000}, 'gallery': {'sq_ft': 700, 'commission': 0.30, 'events_yr': 12}, } COGS, LABOR, RENT, OTHER = 0.30, 0.35, 0.15, 0.10 # ~10% EBITDA
Step 02 · A year of foot traffic

What does a typical year at the door look like?

Next, we simulate an entire year of daily visits. We build in the patterns every real venue has: coffee peaks on weekend mornings, the bar fills up Thursday through Saturday night, the makerspace stays small and steady. We also factor in a slow opening ramp — no one is at capacity on day one.

The chart below is the result. Each line is one venue over 12 months, smoothed so the shape is easy to read. It's not a prediction of one specific Tuesday — it's a reasonable picture of a normal year.

§2 Simulated Traffic Data — Cell 6 executed · 1.2s
In [6]:
for venue, color in [('coffee_shop', C_COFFEE), ('bar', C_BAR), ('makerspace', C_MAKER)]: df = all_traffic[all_traffic.venue == venue] ax.plot(df.date, df.visitors.rolling(7).mean(), color=color, linewidth=1.8) ax.set_title('Daily Visitors · 7-day rolling avg')
Out[6]:
300 200 100 0 Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec coffee bar makerspace Daily Visitors — 7-day rolling average
Step 03 · From a flyer to a regular

How a stranger becomes a customer.

Not everyone who sees an ad walks in. Not everyone who walks in buys something. Not everyone who buys something comes back. The chart below tracks that dropout, step by step — what marketers call a funnel.

Read it top to bottom: 500,000 people see an ad on their phone; 75,000 click through or visit the website; 19,600 actually walk in the door; and by the end of the year, about 4,939 have become repeat visitors. Each drop-off is a lever the operator can pull — a better sign out front, a free first drink, a reason to return next week.

Visitor Funnel · Year 1 projection 0.99% end-to-end conversion
AwarenessAd Impressions
500,000
InterestWebsite / Social
75,000
↓ 85%
VisitFoot Traffic
19,600
↓ 74%
EngagementDwell > 30 min
11,760
↓ 40%
ConversionPurchase / Signup
8,232
↓ 30%
RetentionReturn within 90 days
4,939
↓ 40%
Step 04 · Turning visits into dollars

One million dollars of coffee and drinks, roughly.

Multiply visits by average ticket and you get direct revenue: about $1,035,920 in Year 1, split across the four venues. That's the money changing hands at the register.

But the full economic picture is bigger. When the coffee shop buys beans from a local roaster, and the roaster pays a driver, and the driver buys lunch — that chain of spending is called the multiplier effect. The U.S. government publishes official multipliers for exactly this purpose (they call it RIMS-II). Applied to our direct revenue, the total economic output comes out to $1.86M, supporting roughly 27 jobs and returning $52,719 in sales tax to the city in the first year alone.

§5 Economic Impact Model — Cell 14 executed · 0.1s
In [14]:
rev = compute_revenue(all_traffic, SPACE_CONFIG) impact = rims_ii_multiplier(rev, jobs_mult=1.4, output_mult=1.8) impact.to_frame().style.format({'Value': '{:,.0f}'})
Out[14]:
Revenue StreamAnnualShare
Coffee Shop$262,08025%
Bar & Beverage$337,04033%
Events & Private Rentals$336,00032%
Makerspace (lease)$90,0009%
Art Gallery (commission)$10,8001%
Direct Revenue$1,035,920100%
× 1.8 output multiplier (RIMS-II)$1,864,656
Direct FTE · supported (×1.4)19 · 26.6
Sales tax to City of Denver$52,719
Step 05 · What about year five?

Three futures, not one hopeful line.

Most business plans show a single line going up and to the right. Grant committees have seen a thousand of those, and they don't trust any of them. So instead we show three futures side by side: a cautious one (slow growth), a realistic one (the base case), and an optimistic one.

Even in the cautious version, the space clears the grant's requirements. That's the point of showing the range — the pitch doesn't depend on everything going right.

§6 Five-Year Proforma — Cell 17 executed · 0.4s
Out[17]:
$1.6M $1.3M $1.1M $0.9M Y1 Y2 Y3 Y4 Y5 Aggressive · $6.6M Base · $5.2M Conservative · $4.8M Revenue Projection · 5-year proforma cumulative totals annotated

Why the grant cleared — committees don't reward the biggest number. They reward the number they can defend. Every assumption was written down, every multiplier was borrowed from a federal source, and the downside case was presented alongside the upside. The one-page summary fit on a single sheet; the supporting math ran fifty more.

Part 2 · the customer view

Who's actually coming back?

Part 1 gave the grant committee the big numbers. Part 2 is for the people running the place once it opens. Instead of totals — 75,000 visitors, a million in revenue — we zoom in on individual customers. The goal: know your regulars the way a neighborhood bartender does, but at scale and with data.

Step 06 · Meeting the customers

How a walk-in becomes a name in the system.

The trick to all of this is a moment most people don't notice: the free WiFi sign-in. When a customer joins the WiFi, they give an email address. That email is the thread that ties everything together — the same email might show up on a bar tab two weeks later, or on a membership signup two months later.

To show how the analysis would work once the space was open, I simulated 5,000 fictional customers, each with a realistic mix of where they heard about the place, which venue they prefer, and how engaged they are. The table below is a peek at that roster.

user_ltv_analysis_clean.ipynb — §1 User Journeys executed · 0.3s
In [3]:
n_users = 5000 users = pd.DataFrame({ 'user_id': range(1, n_users + 1), 'acquisition_source': np.random.choice(SOURCES, n_users, p=WEIGHTS), 'home_zip': np.random.choice(ZIPS, n_users, p=ZIP_W), 'engagement_score': np.random.beta(2, 5, n_users), # right-skewed 'venue_affinity': np.random.choice(['coffee', 'bar', 'both'], n_users, p=[.45, .25, .30]), })
Out[3]:
user_idsourcezipengagementaffinity
1social_ad802020.24coffee
2organic802050.61both
3referral802060.08bar
4search_ad802040.33coffee
5event802110.77both
5000organic802030.19coffee
Step 07 · From first visit to paying member

Most people come back. Almost none sign up.

Same idea as the funnel in Part 1 — but this time we're watching individual customers instead of crowds. The good news: 88% of first-time visitors come back at least once. That's a healthy sign that the space itself works.

The harder news: only about 1% actually become paying members. That sounds small, and it is — but it's also the most valuable step on the whole chart, because (as we'll see next) members are worth many times more than casual visitors. This funnel is the operator's to-do list.

User Funnel · simulated N=5,000 0.88% Acquired → Member
AcquiredWiFi signup
5,000
100%
Activated2+ visits
4,398
↓ 12%
Engaged5+ visits
2,598
↓ 41%
Multi-venuecoffee + bar
1,550
↓ 40%
Event attendeeticketed event
231
↓ 85%
Convertedpaying member
44
↓ 81%
Step 08 · What a customer is “worth”

A casual visitor: $60. A member: $450+.

“Customer lifetime value” is a fancy term for a simple idea: how much money does one customer spend with us over the course of a year? Add up their coffees, their drinks, their event tickets, and their membership fees. That's their value.

The chart below is the most important picture in the entire project. A person who just drops in a few times is worth about $60 a year. A person who signs up for a membership is worth $450 to $550+ — roughly seven and a half times more. The entire strategy for running the space boils down to one sentence: move people from the short bar on the left to the taller bars on the right.

Average LTV by membership tier Simulated Year-1 cohort · n = 5,000
LTV decomposed into spend and membership revenue. Y-axis in dollars.
Avg spend Membership rev Non-member (spend only)
Non-member4,956 users5.2 avg visits
Coffee Regular20 users16.2 avg visits
Bar Member9 users16.2 avg visits
All-Access15 users15.8 avg visits
Non-member → Coffee Regular 6.0× LTV lift · $60 → $361
Step 09 · Three findings that changed the plan

What the data actually said.

Once the customer view was running, three patterns jumped out immediately. Each one was specific enough to change how the team spent time and money in week one.

Insight 01 · cross-venue
$92 vs $16

Multi-venue users (coffee and bar) have 5.7× the LTV of single-venue users. The membership ladder is designed to pull single-venue visitors across.

Insight 02 · acquisition
Events > Ads

Event attendees and organic walk-ins convert at roughly the same rate as paid — but their LTV is $3–5 higher. Community events aren't marketing spend; they're the best marketing spend.

Insight 03 · retention
Visit #3

Users who make it to their third visit become 6× more likely to convert. The first-90-days playbook is designed around pushing people past that threshold.

Step 10 · One customer, start to finish

From one Instagram ad to $477 a year.

All the charts up to this point are averages. To make it real, here's what one specific customer's year actually looked like, pulled from their record in the system. She saw an Instagram ad in January, came in for a latte, came back a week later with a laptop, wandered over to an art opening at the bar, and eventually bought a coffee-shop membership on Valentine's Day.

By the end of the year she'd spent $477. One person. One story. Multiply by a few thousand customers and you have a business.

§9 Sample User Journey — Cell 21 executed · 0.0s
Out[21]:
user_id = 2847 · Coffee Regular MemberAcquisition: Instagram ad · Home ZIP 80205
Jan 15First visit · bought latte$6
Jan 22Return · worked 3 hrs · 2 drinks$14
Feb 01Art opening event · wine at bar$28
Feb 08Coffee visit with friend$12
Feb 14Joined Coffee Regular membership$15/mo
Feb–Dec28 additional visits · avg $9 each$252
+ prorated11 months of membership$165
Year-1 LTV$477
Part 3 · how it works underneath

The plumbing, in one picture.

None of this analysis is possible without one small trick: recognizing that the person on the WiFi, the person at the register, and the person on the membership list are often the same person. The diagram below shows how those three systems get stitched together with a single shared email address — and how the stitched data then flows into the two analyses you just read.

Community Hub · Data Pipeline Signal capture → identity → model
CAPTURE UNIFY MODEL USE WiFi captive portal email + ZIP · device ID dwell time · visit freq POS · Toast receipt email capture avg ticket · product mix Membership signup tier · join date renewal state Door counter · Placer.ai (opt.) Segment · identity graph email as stable ID device_id ↔ user_id receipts ↔ visits unified user record 1 user = N visits + M receipts tier · LTV · recency economic_impact_demo traffic sim · funnel · proforma RIMS-II multipliers → grant report, annual user_ltv_analysis_clean cohorts · LTV · attribution tier assignment model → ops dashboard, live Grant reporting quarterly PDF export Ops dashboard daily KPI refresh Marketing attribution LTV by source · CAC Membership triggers offer on visit #3
Purple WiFi Toast POS Segment Python · Pandas Jupyter Matplotlib
Outcomes

What shipped.

$2M
Grant secured
First-pass approval
75.5k
Y1 visitors modeled
4 venues combined
7.5×
Member LTV gap
$60 → $450+
2
Notebooks shipped
Still in production

The one-sentence takeaway — we stopped counting foot traffic and started tracking people. Who came in, how often they returned, what they spent, whether they stuck around. Once you can see the difference between a $60 customer and a $450 customer, the whole job becomes obvious: help more of the first group turn into the second.

What I learned — the instinct in grant work is to make the numbers look as big as possible. The discipline is the opposite: show the downside, cite your sources, and let the real number speak. The same discipline that convinces a committee also keeps the business honest with itself six months later.

What's next

From projection to the real thing.