Measures of Concentration in BigMart Sales

Up to this point in our exploratory data analysis, we have examined location, variability, shape, and heterogeneity across the primary features of the BigMart Sales dataset. We now address a dimension of fundamental importance in economic, commercial, and management analytics: concentration.

The core premise is straightforward: knowing average sales per product or store is insufficient; we must also understand how total revenue is distributed across categories, store types, or individual outlets. Two scenarios can share identical total sales and averages yet exhibit completely different internal structures: in one, sales may be evenly distributed across all units, whereas in the other, a tiny fraction of categories or branches generates the vast majority of revenue.

From an EDA perspective, concentration measures and visual diagnostics answer critical business questions:

In this chapter, we develop descriptive metrics and graphical representations to evaluate sales concentration in BigMart Sales, culminating in a concentration mini-dashboard.

Environment Setup

We will work with the following BigMart dataset. For this section, we will use Python alongside pandas, numpy, plotly, and matplotlib.

import numpy as np
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go

DATA_PATH = "../../data/bigmart_sales.csv"  
df = pd.read_csv(DATA_PATH)

df.head()
Item_Identifier Item_Weight Item_Fat_Content Item_Visibility Item_Type Item_MRP Outlet_Identifier Outlet_Establishment_Year Outlet_Size Outlet_Location_Type Outlet_Type Item_Outlet_Sales
0 FDA15 9.30 Low Fat 0.016047 Dairy 249.8092 OUT049 1999 Medium Tier 1 Supermarket Type1 3735.1380
1 DRC01 5.92 Regular 0.019278 Soft Drinks 48.2692 OUT018 2009 Medium Tier 3 Supermarket Type2 443.4228
2 FDN15 17.50 Low Fat 0.016760 Meat 141.6180 OUT049 1999 Medium Tier 1 Supermarket Type1 2097.2700
3 FDX07 19.20 Regular 0.000000 Fruits and Vegetables 182.0950 OUT010 1998 NaN Tier 3 Grocery Store 732.3800
4 NCD19 8.93 Low Fat 0.000000 Household 53.8614 OUT013 1987 High Tier 3 Supermarket Type1 994.7052

As in previous sections, we ensure relevant categorical fields are standardized before aggregation.

# Basic standardization of categorical values in BigMart
if "Item_Fat_Content" in df.columns:
    df["Item_Fat_Content"] = (
        df["Item_Fat_Content"]
        .replace({
            "LF": "Low Fat",
            "low fat": "Low Fat",
            "reg": "Regular"
        })
    )

candidate_cols = [
    "Item_Identifier",
    "Item_Type",
    "Outlet_Identifier",
    "Outlet_Type",
    "Outlet_Location_Type",
    "Outlet_Size",
    "Item_Outlet_Sales",
]

existing = [c for c in candidate_cols if c in df.columns]
df[existing].head()
Item_Identifier Item_Type Outlet_Identifier Outlet_Type Outlet_Location_Type Outlet_Size Item_Outlet_Sales
0 FDA15 Dairy OUT049 Supermarket Type1 Tier 1 Medium 3735.1380
1 DRC01 Soft Drinks OUT018 Supermarket Type2 Tier 3 Medium 443.4228
2 FDN15 Meat OUT049 Supermarket Type1 Tier 1 Medium 2097.2700
3 FDX07 Fruits and Vegetables OUT010 Grocery Store Tier 3 NaN 732.3800
4 NCD19 Household OUT013 Supermarket Type1 Tier 3 High 994.7052

Conceptual Foundations

Relative Share

Let \(x_i\) be the total sales generated by category or unit \(i\). Its relative share relative to total sales is defined as:

\[ p_i = \frac{x_i}{\sum_{j=1}^{n} x_j}. \]

Expressed as a percentage:

\[ p_i^{(\%)} = 100 \cdot p_i. \]

Cumulative Share

Sorting units in descending order by contribution, the cumulative share up to position \(k\) is given by:

\[ P_k = \sum_{i=1}^{k} p_{(i)}, \]

where \(p_{(i)}\) represents the share of the \(i\)-th ranked unit.

This metric forms the basis of Pareto analysis, determining how many categories account for a targeted percentage of revenue.

Concentration Curve

A concentration curve compares observed cumulative revenue against an idealized uniform distribution. When a few units account for most sales, the cumulative curve rises steeply at first before flattening, departing significantly from the diagonal line of equal distribution.

This visual representation serves as a conceptual foundation for advanced metrics like Lorenz curves and Gini indices.

Sales Concentration by Product Category

We begin by aggregating total sales across product types.

sales_by_item_type = (
    df.groupby("Item_Type", dropna=False)["Item_Outlet_Sales"]
      .sum()
      .sort_values(ascending=False)
      .reset_index()
)

sales_by_item_type["share"] = (
    sales_by_item_type["Item_Outlet_Sales"] /
    sales_by_item_type["Item_Outlet_Sales"].sum()
)

sales_by_item_type["cum_share"] = sales_by_item_type["share"].cumsum()
sales_by_item_type["share_pct"] = 100 * sales_by_item_type["share"]
sales_by_item_type["cum_share_pct"] = 100 * sales_by_item_type["cum_share"]

sales_by_item_type.head(10)
Item_Type Item_Outlet_Sales share cum_share share_pct cum_share_pct
0 Fruits and Vegetables 2.820060e+06 0.151688 0.151688 15.168849 15.168849
1 Snack Foods 2.732786e+06 0.146994 0.298683 14.699412 29.868261
2 Household 2.055494e+06 0.110563 0.409246 11.056317 40.924578
3 Frozen Foods 1.825735e+06 0.098205 0.507450 9.820464 50.745042
4 Dairy 1.522594e+06 0.081899 0.589349 8.189897 58.934939
5 Canned 1.444151e+06 0.077680 0.667029 7.767962 66.702901
6 Baking Goods 1.265525e+06 0.068071 0.735100 6.807148 73.510048
7 Health and Hygiene 1.045200e+06 0.056220 0.791321 5.622038 79.132086
8 Meat 9.175656e+05 0.049355 0.840676 4.935503 84.067590
9 Soft Drinks 8.928977e+05 0.048028 0.888704 4.802817 88.870407

Contribution Ranking

Ranking categories by total sales highlights their relative weight in overall enterprise revenue.

fig = px.bar(
    sales_by_item_type,
    x="Item_Type",
    y="share_pct",
    title="Sales Percentage Share by Product Type",
    labels={"share_pct": "Share (%)", "Item_Type": "Product Type"}
)
fig.update_layout(xaxis_tickangle=-45)
fig.show()

This visualization clarifies which product lines anchor total revenue and which represent minor contributors.

Pareto Chart

A Pareto chart combines individual contribution bars with a cumulative percentage line.

pareto_df = sales_by_item_type.copy()

fig = go.Figure()
fig.add_bar(
    x=pareto_df["Item_Type"],
    y=pareto_df["share_pct"],
    name="Individual Share (%)"
)
fig.add_scatter(
    x=pareto_df["Item_Type"],
    y=pareto_df["cum_share_pct"],
    mode="lines+markers",
    name="Cumulative Share (%)",
    yaxis="y2"
)

fig.update_layout(
    title="Pareto Chart of Sales by Product Type",
    xaxis=dict(title="Product Type"),
    yaxis=dict(title="Individual Share (%)"),
    yaxis2=dict(
        title="Cumulative Share (%)",
        overlaying="y",
        side="right",
        range=[0, 105]
    ),
    legend=dict(orientation="h"),
)
fig.update_xaxes(tickangle=-45)
fig.show()

Interpretation

When the cumulative line climbs rapidly across the first few categories, high concentration is present.

We quantify concentration using three standard threshold questions:

  • How many categories generate 50% of total sales?
  • How many categories generate 80% of total sales?
  • What proportion of sales is concentrated within the top three categories?
def categories_to_reach_threshold(table, threshold=0.8):
    idx = np.argmax(table["cum_share"].to_numpy() >= threshold)
    return idx + 1

n50 = categories_to_reach_threshold(sales_by_item_type, 0.50)
n80 = categories_to_reach_threshold(sales_by_item_type, 0.80)

top3_share = sales_by_item_type["share"].head(3).sum() * 100

summary_concentration_item = pd.DataFrame({
    "Indicator": [
        "Categories needed to reach 50% of sales",
        "Categories needed to reach 80% of sales",
        "Cumulative share of top 3 categories (%)"
    ],
    "Value": [n50, n80, round(top3_share, 2)]
})

summary_concentration_item
Indicator Value
0 Categories needed to reach 50% of sales 4.00
1 Categories needed to reach 80% of sales 9.00
2 Cumulative share of top 3 categories (%) 40.92

Sales Concentration by Outlet

Evaluating sales concentration across individual stores reveals whether revenue is broadly distributed or heavily reliant on a few dominant outlets.

sales_by_outlet = (
    df.groupby("Outlet_Identifier", dropna=False)["Item_Outlet_Sales"]
      .sum()
      .sort_values(ascending=False)
      .reset_index()
)

sales_by_outlet["share"] = (
    sales_by_outlet["Item_Outlet_Sales"] /
    sales_by_outlet["Item_Outlet_Sales"].sum()
)
sales_by_outlet["cum_share"] = sales_by_outlet["share"].cumsum()
sales_by_outlet["share_pct"] = 100 * sales_by_outlet["share"]
sales_by_outlet["cum_share_pct"] = 100 * sales_by_outlet["cum_share"]

sales_by_outlet
Outlet_Identifier Item_Outlet_Sales share cum_share share_pct cum_share_pct
0 OUT027 3.453926e+06 0.185784 0.185784 18.578359 18.578359
1 OUT035 2.268123e+06 0.122000 0.307784 12.200030 30.778389
2 OUT049 2.183970e+06 0.117474 0.425258 11.747378 42.525768
3 OUT017 2.167465e+06 0.116586 0.541844 11.658602 54.184370
4 OUT013 2.142664e+06 0.115252 0.657096 11.525196 65.709565
5 OUT046 2.118395e+06 0.113947 0.771042 11.394658 77.104223
6 OUT045 2.036725e+06 0.109554 0.880596 10.955364 88.059587
7 OUT018 1.851823e+06 0.099608 0.980204 9.960789 98.020377
8 OUT010 1.883402e+05 0.010131 0.990334 1.013065 99.033442
9 OUT019 1.796941e+05 0.009666 1.000000 0.966558 100.000000
fig = go.Figure()
fig.add_bar(
    x=sales_by_outlet["Outlet_Identifier"],
    y=sales_by_outlet["share_pct"],
    name="Individual Share (%)"
)
fig.add_scatter(
    x=sales_by_outlet["Outlet_Identifier"],
    y=sales_by_outlet["cum_share_pct"],
    mode="lines+markers",
    name="Cumulative Share (%)",
    yaxis="y2"
)
fig.update_layout(
    title="Sales Concentration by Outlet",
    xaxis=dict(title="Outlet"),
    yaxis=dict(title="Individual Share (%)"),
    yaxis2=dict(
        title="Cumulative Share (%)",
        overlaying="y",
        side="right",
        range=[0, 105]
    ),
    legend=dict(orientation="h")
)
fig.show()

Analytical Commentary

Unlike category-level analysis, store-level concentration involves fewer entities, highlighting operational dependencies when a minority of outlets generates a major share of total revenue.

Concentration by Outlet Type

Aggregating sales across commercial store formats (Outlet_Type) provides strategic insights into revenue concentration across store models.

sales_by_outlet_type = (
    df.groupby("Outlet_Type", dropna=False)["Item_Outlet_Sales"]
      .sum()
      .sort_values(ascending=False)
      .reset_index()
)

sales_by_outlet_type["share"] = (
    sales_by_outlet_type["Item_Outlet_Sales"] /
    sales_by_outlet_type["Item_Outlet_Sales"].sum()
)
sales_by_outlet_type["cum_share"] = sales_by_outlet_type["share"].cumsum()
sales_by_outlet_type["share_pct"] = 100 * sales_by_outlet_type["share"]
sales_by_outlet_type["cum_share_pct"] = 100 * sales_by_outlet_type["cum_share"]

sales_by_outlet_type
Outlet_Type Item_Outlet_Sales share cum_share share_pct cum_share_pct
0 Supermarket Type1 1.291734e+07 0.694812 0.694812 69.481228 69.481228
1 Supermarket Type3 3.453926e+06 0.185784 0.880596 18.578359 88.059587
2 Supermarket Type2 1.851823e+06 0.099608 0.980204 9.960789 98.020377
3 Grocery Store 3.680343e+05 0.019796 1.000000 1.979623 100.000000
fig = px.bar(
    sales_by_outlet_type,
    x="Outlet_Type",
    y="share_pct",
    title="Sales Share by Outlet Type",
    labels={"share_pct": "Share (%)", "Outlet_Type": "Outlet Type"}
)
fig.update_layout(xaxis_tickangle=-20)
fig.show()

Simplified Concentration Curve

We construct a concentration curve by plotting cumulative population fraction against cumulative revenue fraction.

def concentration_curve(values):
    values = np.asarray(values, dtype=float)
    values = np.sort(values)
    cum_values = np.cumsum(values)
    cum_values = cum_values / cum_values[-1]
    cum_units = np.arange(1, len(values) + 1) / len(values)
    return cum_units, cum_values

x_units, y_conc = concentration_curve(sales_by_item_type["Item_Outlet_Sales"])

curve_df = pd.DataFrame({
    "Fraction_units": x_units,
    "Fraction_sales": y_conc
})

fig = go.Figure()
fig.add_scatter(
    x=curve_df["Fraction_units"],
    y=curve_df["Fraction_sales"],
    mode="lines",
    name="Concentration Curve"
)
fig.add_scatter(
    x=[0, 1],
    y=[0, 1],
    mode="lines",
    name="Uniform Distribution"
)
fig.update_layout(
    title="Sales Concentration Curve by Product Type",
    xaxis_title="Cumulative Fraction of Categories",
    yaxis_title="Cumulative Fraction of Sales"
)
fig.show()

Reading the Curve

Greater separation between the concentration curve and the 45-degree diagonal indicates higher concentration.

Concentration Summary Indicators

We consolidate key concentration findings across features into a single summary table.

top1_item = sales_by_item_type["share"].iloc[0] * 100
top5_item = sales_by_item_type["share"].head(5).sum() * 100

top1_outlet = sales_by_outlet["share"].iloc[0] * 100
top3_outlet = sales_by_outlet["share"].head(3).sum() * 100

concentration_summary = pd.DataFrame({
    "Domain": [
        "Products",
        "Products",
        "Products",
        "Outlets",
        "Outlets"
    ],
    "Indicator": [
        "Top category share (%)",
        "Top 5 cumulative share (%)",
        "Categories needed for 80% of sales",
        "Top outlet share (%)",
        "Top 3 outlets cumulative share (%)"
    ],
    "Value": [
        round(top1_item, 2),
        round(top5_item, 2),
        n80,
        round(top1_outlet, 2),
        round(top3_outlet, 2)
    ]
})

concentration_summary
Domain Indicator Value
0 Products Top category share (%) 15.17
1 Products Top 5 cumulative share (%) 58.93
2 Products Categories needed for 80% of sales 9.00
3 Outlets Top outlet share (%) 18.58
4 Outlets Top 3 outlets cumulative share (%) 42.53

Concentration Mini-Dashboard

We synthesize revenue concentration metrics into a high-level KPI dashboard.

Primary KPIs

total_sales = df["Item_Outlet_Sales"].sum()
n_item_types = df["Item_Type"].nunique() if "Item_Type" in df.columns else np.nan
n_outlets = df["Outlet_Identifier"].nunique() if "Outlet_Identifier" in df.columns else np.nan

dashboard_kpis = pd.DataFrame({
    "Metric": [
        "Total Sales",
        "Product Types",
        "Outlets",
        "Top 3 Categories Share (%)",
        "Top 3 Outlets Share (%)"
    ],
    "Value": [
        round(total_sales, 2),
        n_item_types,
        n_outlets,
        round(sales_by_item_type["share"].head(3).sum() * 100, 2),
        round(sales_by_outlet["share"].head(3).sum() * 100, 2)
    ]
})

dashboard_kpis
Metric Value
0 Total Sales 18591125.41
1 Product Types 16.00
2 Outlets 10.00
3 Top 3 Categories Share (%) 40.92
4 Top 3 Outlets Share (%) 42.53

Final Discussion

Concentration analysis complements location, dispersion, shape, and heterogeneity by revealing how aggregate totals divide among constituent units. In BigMart Sales, this dimension highlights operational reliance on top product categories and store formats.

Suggested Exercises

  1. Build a Pareto chart for Outlet_Location_Type and compare its concentration against Outlet_Type.
  2. Compute the sales percentage accumulated by the top 20% of product categories.
  3. Repeat concentration calculations using item counts per category instead of total monetary sales, comparing both distributions.
  4. Provide a business summary evaluating whether sales structure is diversified or concentrated.