I had a table of 24 Indian cities. For each one: how many local businesses are listed on Google Maps, and how many of them have no website. The question was simple: what share of businesses have no website overall?
My first answer was wrong by more than two percentage points, and the reason is a mistake I see in a lot of dashboards. Here is the walk-through.
The data
Counts per city, collected in November 2026 from 73,568 Google Maps listings (one row per city):
import io import pandas as pd csv = """city,listings,no_website Kota,2685,1737 Kanpur,1907,1173 Patna,1984,1144 Bhopal,1656,955 Nagpur,1714,942 Rajkot,1328,720 Vadodara,1954,1053 Faridabad,3200,1642 Surat,3433,1609 Chandigarh,2941,1348 Indore,3251,1435 Coimbatore,3277,1354 Jaipur,3555,1435 Lucknow,3347,1342 Ahmedabad,3678,1388 Gurgaon,5318,1997 Kolkata,3516,1251 Hyderabad,3534,1222 Chennai,3625,1239 Pune,3626,1222 Noida,3497,1092 Delhi,3307,980 Mumbai,3668,1054 Bangalore,3567,947""" df = pd.read_csv(io.StringIO(csv)) df["pct"] = (100 * df.no_website / df.listings).round(1) Mistake 1: averaging the percentages
df.pct.mean() # 43.5 That says 43.5% of businesses have no website. But Kota (2,685 listings) and Gurgaon (5,318 listings) count equally in that mean. Small cities with high percentages pull the number up.
The question was about businesses, not cities, so pool the counts first:
100 * df.no_website.sum() / df.listings.sum() # 41.2 41.2%. Same data, a 2.3-point difference, and the second one is the answer to the question that was asked. Rule of thumb: if the rows have different sizes, never .mean() a ratio column. Sum numerators, sum denominators, divide.
Mistake 2: the same thing, hiding inside groupby
It gets sneakier when you group. Say you want metros vs the rest:
metros = {"Delhi", "Mumbai", "Bangalore", "Chennai", "Kolkata", "Hyderabad", "Pune", "Ahmedabad"} ncr = {"Gurgaon", "Noida", "Faridabad"} def tier(city): if city in metros: return "metro" if city in ncr: return "ncr" return "tier-2" df["tier"] = df.city.map(tier) # wrong: mean of percentages per group df.groupby("tier").pct.mean() # right: pooled ratio per group g = df.groupby("tier")[["listings", "no_website"]].sum() g["pct"] = (100 * g.no_website / g.listings).round(1) print(g) listings no_website pct tier metro 28521 9303 32.6 ncr 12015 4731 39.4 tier-2 33032 16247 49.2 The wrong version says tier-2 is at 51.0% and NCR at 40.0%; the pooled numbers are 49.2% and 39.4%. Aggregate the raw counts, then compute the ratio. groupby(...).pct.mean() is the same bug as before, just in a nicer-looking table.
(The result itself is interesting: in tier-2 cities about half of all listed businesses have no website, against a third in the metros.)
Mistake 3: ranking by the ratio when you care about volume
Which city is the "biggest opportunity" for someone who builds websites? Sorting by pct puts Kota first. Sorting by the count tells a different story:
df.sort_values("no_website", ascending=False).head(5)[["city", "no_website", "pct"]] city no_website pct Gurgaon 1997 37.6 Kota 1737 64.7 Faridabad 1642 51.3 Surat 1609 46.9 Indore 1435 44.1 Gurgaon has a below-average percentage but the most businesses without a site, simply because it has the most listings. Neither ranking is "right". A ratio tells you how easy each pitch might be, a count tells you how many pitches exist. Pick the one that matches the decision, and say which one you used.
A tiny helper so you don't repeat the bug
def pooled_pct(frame, num, den, by=None): """Percentage computed from summed counts, optionally per group.""" if by is None: return round(100 * frame[num].sum() / frame[den].sum(), 1) s = frame.groupby(by)[[num, den]].sum() return (100 * s[num] / s[den]).round(1) pooled_pct(df, "no_website", "listings") # 41.2 pooled_pct(df, "no_website", "listings", "tier") # metro 32.6, ncr 39.4, tier-2 49.2 Takeaways
- A mean of ratios weights every row equally. That is only correct when every row is the same size.
- Sum the parts, then divide, both overall and inside
groupby. - Ratio rankings and count rankings answer different questions. State which one you are showing.
Have you shipped a dashboard with an averaged percentage in it? I have. What made you catch it?
The counts come from the public India city lead pages on Mantis (November 2026). A business can appear in more than one category, so these are listings, not unique businesses. I work on Mantis.
