Introduction

This report examines Chanel’s product pricing and merchandise assortment across several luxury retail categories. Using a dataset of Chanel product prices, the analysis explores differences in average prices, product selection, and price variation across categories and subcategories.

The goal is to better understand how pricing differs throughout Chanel’s merchandise assortment through descriptive statistics and five Python visualizations. The analysis uses product prices converted from euros to US dollars to provide a consistent basis for comparison.

Descriptive Statistics

The dataset contains 1,452 Chanel product records across merchandise categories such as fashion, jewelry, watches, eyewear, fragrance, makeup, and skincare. The original dataset contains 17 variables. An additional variable, price_usd, is created to represent product prices in US dollars.

For this analysis, prices are converted using an assumed exchange rate of 1 euro = 1.1193 US dollars. All pricing visualizations and descriptive statistics use the converted values.

import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
import seaborn as sns

df = pd.read_parquet(
    "/Users/laurenhood/Library/CloudStorage/OneDrive-LoyolaUniversityMaryland/PythonProject/train-00000-of-00001-7d980ff20ade4103.parquet"
)

eur_to_usd = 1.1193
df["price_usd"] = df["price_eur"] * eur_to_usd

print("Dataset shape:", df.shape)
## Dataset shape: (1452, 18)
print()
print("Descriptive Statistics for Product Prices (USD):")
## Descriptive Statistics for Product Prices (USD):
print(df["price_usd"].describe().round(2))
## count      1452.00
## mean       5432.68
## std       12751.13
## min          25.81
## 25%         206.51
## 50%        1445.59
## 75%        5898.52
## max      135265.95
## Name: price_usd, dtype: float64

The descriptive statistics reveal substantial variation in Chanel’s product prices. The average product price is approximately $5,432.68, while the median is approximately $1,445.59. The mean is considerably higher than the median, indicating a right-skewed price distribution in which a smaller number of extremely expensive products raise the overall average.

The minimum product price is approximately $25.81, while the maximum reaches approximately $135,265.95. The standard deviation of approximately $12,751.13 further demonstrates the wide variation in pricing. These differences reflect the diversity of Chanel’s merchandise assortment, which includes relatively accessible beauty products alongside high-priced luxury jewelry and watches.

The first quartile is approximately $206.51, while the third quartile is approximately $5,898.52. This means that half of the products have prices between these two values. Because the distribution contains extreme values, examining the median and quartiles alongside the mean provides a more complete understanding of Chanel’s pricing structure.

Data Visualizations

Average Price by Category

d1 = df.groupby("category1_code")["price_usd"].mean()
d1 = d1.sort_values(ascending=True)

mean_price = d1.mean()

my_colors = []

for price in d1:
    if price > mean_price:
        my_colors.append("#243B53")
    else:
        my_colors.append("#A9C5D9")

fig = plt.figure(figsize=(13, 7))
ax1 = fig.add_subplot(1, 1, 1)

ax1.barh(
    d1.index,
    d1.values,
    color=my_colors
)

ax1.set_title(
    "Average Chanel Product Price by Category",
    fontsize=18,
    fontweight="bold",
    pad=20
)

ax1.set_xlabel("Average Price ($)", fontsize=14)
ax1.set_ylabel("Product Category", fontsize=14)

plt.xticks(fontsize=11);
plt.yticks(fontsize=12);

ax1.axvline(
    mean_price,
    color="black",
    linestyle="--",
    linewidth=1.5
)

for row_counter, value in enumerate(d1.values):
    ax1.text(
        value + d1.max() * 0.015,
        row_counter,
        f"${value:,.0f}",
        fontsize=11,
        va="center"
    )

above = mpatches.Patch(
    color="#243B53",
    label="Above Category Mean"
)

below = mpatches.Patch(
    color="#A9C5D9",
    label="Below Category Mean"
)

ax1.legend(
    handles=[above, below],
    loc="lower right",
    fontsize=11
)

ax1.set_xlim(0, d1.max() * 1.20)
## (0.0, 24795.792815975998)
ax1.grid(axis="x", alpha=0.2)
ax1.set_axisbelow(True)

plt.tight_layout()
plt.show()

The horizontal bar chart compares the average product prices across Chanel’s main merchandise categories. Jewelry and watches have the highest average prices, reflecting the premium pricing of these luxury product categories. Fashion products also occupy a higher price range than beauty-related merchandise, while fragrance, makeup, and skincare have considerably lower average prices.The dashed reference line represents the mean of the individual category averages, making it easier to identify which categories are priced above or below that benchmark.

Price vs. Product Selection

x2 = df.groupby("category2_code").agg(
    count=("price_usd", "count"),
    avg_price=("price_usd", "mean")
).reset_index()

x2 = x2[x2["count"] >= 5]
x2 = x2.reset_index(drop=True)

plt.figure(figsize=(13, 8))

plt.scatter(
    x2["count"],
    x2["avg_price"],
    s=x2["count"] * 8,
    c=x2["count"],
    cmap="Blues",
    marker="o",
    edgecolors="black",
    alpha=0.75
)

plt.title(
    "Chanel Product Subcategories: Price vs. Product Selection",
    fontsize=18,
    fontweight="bold"
)

plt.xlabel("Number of Products", fontsize=14)
plt.ylabel("Average Product Price ($)", fontsize=14)

cbar = plt.colorbar()

cbar.set_label(
    "Number of Products",
    rotation=270,
    fontsize=13,
    labelpad=25
)

plt.xticks(fontsize=11);
plt.yticks(fontsize=11);
plt.grid(alpha=0.2)

plt.tight_layout()
plt.show()

This scatter plot examines the relationship between the number of products offered within Chanel’s subcategories and their average prices. Each bubble represents a product subcategory containing at least five products. The horizontal axis shows the number of products, while the vertical axis represents the average price in US dollars. Bubble size and color also indicate the number of products within each subcategory.

Highest-Priced Subcategories Heat Map

top_categories = (
    df.groupby("category2_code")["price_usd"]
    .mean()
    .sort_values(ascending=False)
    .head(15)
    .index
)

heatmap_df = df[
    df["category2_code"].isin(top_categories)
]

heatmap_data = heatmap_df.pivot_table(
    index="category1_code",
    columns="category2_code",
    values="price_usd",
    aggfunc="mean"
)

plt.figure(figsize=(15, 7))

sns.heatmap(
    heatmap_data,
    cmap="Blues",
    annot=True,
    fmt=".0f",
    linewidths=0.5,
    cbar_kws={"label": "Average Price ($)"}
)

plt.title(
    "Chanel's 15 Highest-Priced Product Subcategories",
    fontsize=18,
    fontweight="bold",
    pad=20
)

plt.xlabel("Product Subcategory", fontsize=14)
plt.ylabel("Product Category", fontsize=14)

plt.xticks(
    rotation=45,
    ha="right",
    fontsize=10
);

plt.yticks(
    rotation=0,
    fontsize=11
);

plt.tight_layout()
plt.show()

The heat map highlights Chanel’s 15 product subcategories with the highest average prices. Darker shades of blue indicate higher average prices, while lighter shades represent comparatively lower prices. Blank cells indicate category and subcategory combinations that are not represented in the dataset.

Jewelry dominates the highest-priced subcategories, specifically collections such as N°5, Camélia, and Sous le Signe du Lion. Watches and fashion are also represented, although their average prices are generally lower than those of the most expensive jewelry collections.

Product Assortment Nested Pie Chart

top_four = (
    df["category1_code"]
    .value_counts()
    .head(4)
    .index
)

pie_df = df[
    df["category1_code"].isin(top_four)
].copy()

pie_data = (
    pie_df.groupby(
        ["category1_code", "category2_code"]
    )
    .size()
    .reset_index(name="count")
)

inner_data = (
    pie_data.groupby("category1_code")["count"]
    .sum()
)

pie_data["category1_code"] = pd.Categorical(
    pie_data["category1_code"],
    categories=inner_data.index,
    ordered=True
)

pie_data = pie_data.sort_values(
    ["category1_code", "category2_code"]
)

inner_colors = [
    "#243B53",
    "#527DA3",
    "#A9C5D9",
    "#D7E5F0"
]

outer_colors = plt.cm.Blues(
    [
        0.3 + 0.65 * i / max(len(pie_data) - 1, 1)
        for i in range(len(pie_data))
    ]
)

fig, ax = plt.subplots(figsize=(12, 9))

ax.pie(
    pie_data["count"],
    radius=1.15,
    colors=outer_colors,
    wedgeprops=dict(
        width=0.3,
        edgecolor="white"
    )
);

ax.pie(
    inner_data,
    radius=0.85,
    labels=inner_data.index,
    colors=inner_colors,
    labeldistance=0.65,
    textprops={"fontsize": 11},
    wedgeprops=dict(
        width=0.4,
        edgecolor="white"
    )
);

ax.set_title(
    "Chanel Product Assortment by Category and Subcategory",
    fontsize=18,
    fontweight="bold",
    pad=25
)

ax.set_aspect("equal")

plt.tight_layout()
plt.show()

The nested pie chart illustrates the distribution of products across Chanel’s four largest merchandise categories by product count. The inner ring represents the main categories, while the outer ring displays the individual subcategories within each category. Larger segments indicate a greater number of products represented in the dataset.

Fashion accounts for a substantial portion of the assortment among these categories, while the remaining categories contribute smaller shares. This is particularly interesting when compared with the pricing visualizations, where jewelry has some of the highest average prices despite representing a smaller portion of the product selection.

Product Price Variation by Category

plt.figure(figsize=(13, 7))

sns.boxplot(
    data=df,
    x="price_usd",
    y="category1_code",
    color="#A9C5D9",
    flierprops={
        "marker": "o",
        "markerfacecolor": "#243B53",
        "markersize": 4,
        "alpha": 0.6
    }
)

plt.title(
    "Chanel Product Price Variation by Category",
    fontsize=18,
    fontweight="bold",
    pad=20
)

plt.xlabel("Product Price ($)", fontsize=14)
plt.ylabel("Product Category", fontsize=14)

plt.xticks(fontsize=11);
plt.yticks(fontsize=11);

plt.grid(
    axis="x",
    alpha=0.2
)

plt.tight_layout()
plt.show()

The box plot compares the distribution of product prices across Chanel’s main merchandise categories. Unlike the earlier bar chart, which focuses on average prices, this visualization illustrates the variation within each category, including the median, interquartile range, and potential outliers.

Jewelry exhibits substantial price variation, with several extremely expensive products extending far beyond the typical price range. Watches also demonstrate a relatively wide distribution, while categories such as fragrance, makeup, and skincare have much narrower price ranges.

Conclusion

This descriptive analysis demonstrates the significant differences in pricing and product assortment across Chanel’s merchandise categories. By examining average prices, product counts, subcategory pricing, assortment composition, and price distributions, the five visualizations provide multiple perspectives on Chanel’s luxury retail strategy.

One of the most important findings is the considerable difference between average and median product prices. The presence of exceptionally expensive jewelry and watches creates a right-skewed distribution, demonstrating how a relatively small number of high-priced products can influence overall pricing statistics.

The visualizations also show why merchandise assortment size and pricing should be analyzed separately. Categories with larger product selections do not necessarily have the highest average prices, and individual subcategories may differ largely from their broader merchandise categories.

From a retail analytics and merchandising perspective, these findings highlight the value of descriptive statistics and data visualization in understanding product positioning and pricing differences. Combining multiple visualization techniques provides a more comprehensive understanding of Chanel’s merchandise assortment than relying on a single statistic or graph.