Skip to main content

Competitor Store Location Analysis

This tutorial will:

  • Explore 2 store chains in Sacramento, CA
  • Create drive-time catchments around each store
  • Join catchments with census data (population, income)
  • Determine which stores best reach target populations with minimal competition
Using AI prompts as a starting point

This tutorial shows the prompts we use to build this analysis.

Since AI is non-deterministic, prompts may not always work identically. You might need to manually adjust prompts or code.

Consider prompts as starting points, not final answers.

Getting Data

We'll use these online datasets for this analysis:

  • Starbucks point locations as our target stores. Available on Kaggle
  • McDonald's point locations as our competitor stores. Available on Kaggle
  • Census Data to get population and average income of the area around each store.

Starbucks & McDonald's locations

Ask the AI to write a UDF loading data from the Kaggle cURL API:

Downloading Starbucks data from Kaggle

Open this specific dataset and return points as a dataframe based on this Kaggle curl request:

#!/bin/bash
curl -L -o ~/Downloads/store-locations.zip\
https://www.kaggle.com/api/v1/datasets/download/starbucks/store-locations

To simplify this tutorial, we've pre-downloaded the data:

  • Starbucks: s3://fused-sample/demo_data/catchment_analysis/starbucks_location.pq
  • McDonald's: s3://fused-sample/demo_data/catchment_analysis/mcdonalds_location.pq

Census Data

Get Census data from the official US Census website.

For simplicity, we've pre-downloaded Census Block Groups (BG) data:

  • Sacramento, CA Census Block Groups: s3://fused-sample/demo_data/catchment_analysis/acs_bg_ca_2022_sacramento_geoparquet.parquet

Exploring Data in Fused

Create 1 UDF per dataset to explore them as individual layers on the Map View.

Starbucks

Create a new UDF and ask the AI:

can you open this starbucks location file (s3://fused-sample/demo_data/catchment_analysis/starbucks_sacramento.pq) and return as geodataframe 

McDonald's

Create another UDF and ask the AI:

can you open this macdonalds location file (s3://fused-sample/demo_data/catchment_analysis/mcdonalds_sacramento.pq) and return as geodataframe 

Census Data

Create another UDF and ask the AI:

Can you open this file using geopandas as return df s3://fused-sample/demo_data/catchment_analysis/acs_bg_ca_2022_sacramento_geoparquet.parquet

After renaming each UDF and changing store colors in the Visualization Tab:

Visualization JSONs

You can copy paste these JSONs into the Visualization Tab to have similar colored stores:

Starbucks

{
"vectorLayer": {
"@@type": "GeoJsonLayer",
"stroked": true,
"filled": false,
"pickable": true,
"lineWidthMinPixels": 1,
"pointRadiusMinPixels": 1,
"getLineColor": {
"@@function": "colorContinuous",
"attr": "value",
"domain": [
0,
10
],
"steps": 20,
"colors": "Mint",
"nullColor": [
2,
250,
1
]
},
"getFillColor": [
208,
208,
208,
40
]
}
}

McDonalds

{
"vectorLayer": {
"@@type": "GeoJsonLayer",
"stroked": true,
"filled": false,
"pickable": true,
"lineWidthMinPixels": 1,
"pointRadiusMinPixels": 1,
"getLineColor": {
"@@function": "colorContinuous",
"attr": "value",
"domain": [
0,
10
],
"steps": 20,
"colors": "Burg",
"nullColor": [
250,
100,
1
]
},
"getFillColor": [
208,
208,
208,
40
]
}
}

Stores and Census Block Groups in Sacramento

Creating isochrones

The Fused AI Assistant uses other UDFs as inspiration to adapt existing code for new use cases.

We'll use the Get Isochrone UDF as AI inspiration. This UDF:

  • Uses the Valhalla API to create drive-time isochrones around points
  • Returns a GeoDataFrame with isochrone polygons

Perfect for our Starbucks analysis! We need:

Since this API is slow, we'll break down the problem:

  1. Use Get_Isochrone UDF for 3 points first
  2. Validate isochrones look correct
  3. Manually increase API calls

Steps:

  • Open the Get_Isochrone UDF in Workbench (no copy needed)
  • Go to your starbucks_location UDF chat
  • Include @Get_Isochrone in your AI request for context

Ask the AI (in the chat for your starbucks_location UDF):

Use @Get_Isochrone to create 15-minute driving isochrones around each starbucks location. 

Test this out with just 3 points first so we make sure this is working correctly at first

You'll see 3 isochrones on the map:

You can manually increase the API calls if desired. For simplicity, we provide the output file:

s3://fused-sample/demo_data/catchment_analysis/starbucks_isochrones_15min.pq

Joining catchments & census data

Next, we'll:

  • Take each store's 15-minute catchment polygon and find intersecting block groups
  • From intersecting BGs, compute catchment metrics:
    • Population sum inside catchment
    • Median income
    • Block group count (number of intersecting BGs)

In a new UDF, ask the AI:

Write a UDF that intersects each store’s 15-minute catchment polygon (from catchment_udf) with Sacramento block groups.  
For each store, use gpd.overlay to compute:
- Total population (sum across intersecting block groups)
- Median household income (median of intersecting block groups)
- Block group count (distinct GEOIDs touched)
Aggregate results per store into a clean summary table and return that.

The block groups for sacramento are here: `s3://fused-sample/demo_data/catchment_analysis/sacramento_block_groups.pq`

The starbucks catchments are here: `'s3://fused-sample/demo_data/catchment_analysis/starbucks_isochrones_15min.pq'`

use 4326 projection for all
Return the catchment geometries so I can still visualize the gdf on a map

Competitor Analysis

In the same UDF, ask the AI for competitor analysis:

Enhance the catchment UDF to measure competition:  
- Load McDonald’s store locations from
`s3://fused-sample/demo_data/catchment_analysis/mcdonalds_sacramento.pq`.
- For each Starbucks 15-minute catchment polygon, count how many McDonald’s points fall inside (point-in-polygon).
- Add this as a new column `Competition` to the per-store summary table.
How many mcdonalds (competitor to starbucks in this case) are present in each catchment?

Track how many of our own stores are in each catchment:

Enhance the catchment UDF to measure cannibalization:  
- Load Starbucks store locations from
`s3://fused-sample/demo_data/catchment_analysis/starbucks_sacramento.pq`.
- For each Starbucks 15-minute catchment polygon, count how many *other* Starbucks points fall inside (exclude the store itself).
- Add this as a new column `Cannibalization` to the per-store summary table.

Ranking stores

Rank stores based on:

  • Highest catchment population
  • Highest median income
  • Lowest competition
  • Lowest cannibalization (i.e. number of our own stores)

So let's ask the AI:

Based on all the info here, give me a ranking of which stores I should invest in the most. I care about starbucks stores that have:
- The highest income
- Highest population
- Least amount of competitors
- Least amount of cannibilization

Create a ranking formula for Starbucks stores based on these criteria

Ask the AI to plot a chart. When your UDF returns a dataframe, AI chat suggests chart creation:

AI suggesting to create a chart from the dataframe

This visualizes the results:

Starbucks location ranking showing population, income, competition and cannibalization metrics

The AI ranking shows top stores have:

  • Highest income
  • Lowest population

Since all AI output is Python code, you can examine and fine-tune the ranking logic!

Next Steps

Improvement ideas:

  • Fine-tune ranking, drive times, etc.
  • Add more data sources
  • Scale to entire US using fused.submit()