Skip to main content
Back to problems
#3230
Medium Database

Customer purchasing behavior analysis

Database
36.7% acceptance
Mar 31, 2026
9
4

No description available.

Solution

Pandas
Time O(1)
Space O(1)
LeetCode
solution.pandas
# Table: Transactions
#
# +------------------+---------+
# | Column Name      | Type    |
# +------------------+---------+
# | transaction_id   | int     |
# | customer_id      | int     |
# | product_id       | int     |
# | transaction_date | date    |
# | amount           | decimal |
# +------------------+---------+
# transaction_id is the unique identifier for this table.
# Each row of this table contains information about a transaction, including the customer ID, product ID, date, and amount spent.
#
# Table: Products
#
# +-------------+---------+
# | Column Name | Type    |
# +-------------+---------+
# | product_id  | int     |
# | category    | varchar |
# | price       | decimal |
# +-------------+---------+
# product_id is the unique identifier for this table.
# Each row of this table contains information about a product, including its category and price.
#
# Write a solution to analyze customer purchasing behavior. For each customer, calculate:
#
# The total amount spent.
#
# The number of transactions.
#
# The number of unique product categories purchased.
#
# The average amount spent.
#
# The most frequently purchased product category (if there is a tie, choose the one with the most recent transaction).
#
# A loyalty score defined as: (Number of transactions * 10) + (Total amount spent / 100).
#
# Round total_amount, avg_transaction_amount, and loyalty_score to 2 decimal places.
#
# Return the result table ordered by loyalty_score in descending order, then by customer_id in ascending order.
#
# The query result format is in the following example.
#
# Example 1:
# Input:
# Transactions table:
# +----------------+-------------+------------+------------------+--------+
# | transaction_id | customer_id | product_id | transaction_date | amount |
# +----------------+-------------+------------+------------------+--------+
# | 1              | 101         | 1          | 2023-01-01       | 100.00 |
# | 2              | 101         | 2          | 2023-01-15       | 150.00 |
# | 3              | 102         | 1          | 2023-01-01       | 100.00 |
# | 4              | 102         | 3          | 2023-01-22       | 200.00 |
# | 5              | 101         | 3          | 2023-02-10       | 200.00 |
# +----------------+-------------+------------+------------------+--------+
# Products table:
# +------------+----------+--------+
# | product_id | category | price  |
# +------------+----------+--------+
# | 1          | A        | 100.00 |
# | 2          | B        | 150.00 |
# | 3          | C        | 200.00 |
# +------------+----------+--------+
# Output:
# +-------------+--------------+-------------------+-------------------+------------------------+--------------+---------------+
# | customer_id | total_amount | transaction_count | unique_categories | avg_transaction_amount | top_category | loyalty_score |
# +-------------+--------------+-------------------+-------------------+------------------------+--------------+---------------+
# | 101         | 450.00       | 3                 | 3                 | 150.00                 | C            | 34.50         |
# | 102         | 300.00       | 2                 | 2                 | 150.00                 | C            | 23.00         |
# +-------------+--------------+-------------------+-------------------+------------------------+--------------+---------------+
# Explanation:
# For customer 101:
# Total amount spent: 100.00 + 150.00 + 200.00 = 450.00
# Number of transactions: 3
# Unique categories: A, B, C (3 categories)
# Average transaction amount: 450.00 / 3 = 150.00
# Top category: C (Customer 101 made 1 purchase each in categories A, B, and C. Since the count is the same for all categories, we choose the most recent transaction, which is category C on 2023-02-10)
# Loyalty score: (3 * 10) + (450.00 / 100) = 34.50
# For customer 102:
# Total amount spent: 100.00 + 200.00 = 300.00
# Number of transactions: 2
# Unique categories: A, C (2 categories)
# Average transaction amount: 300.00 / 2 = 150.00
# Top category: C (Customer 102 made 1 purchase each in categories A and C. Since the count is the same for both categories, we choose the most recent transaction, which is category C on 2023-01-22)
# Loyalty score: (2 * 10) + (300.00 / 100) = 23.00
# Note: The output is ordered by loyalty_score in descending order, then by customer_id in ascending order.

import pandas as pd
from decimal import Decimal, ROUND_HALF_UP


def _rhu(x):
  return float(Decimal(str(x)).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP))


def analyze_customer_behavior(
  transactions: pd.DataFrame, products: pd.DataFrame
) -> pd.DataFrame:
  merged = transactions.merge(products, on="product_id")
  total_amount = (
    merged.groupby("customer_id")["amount"].sum().apply(_rhu).rename("total_amount")
  )
  transaction_count = (
    merged.groupby("customer_id")["transaction_id"]
    .count()
    .rename("transaction_count")
  )
  unique_categories = (
    merged.groupby("customer_id")["category"].nunique().rename("unique_categories")
  )
  avg_amount = (
    merged.groupby("customer_id")["amount"]
    .mean()
    .apply(_rhu)
    .rename("avg_transaction_amount")
  )

  # Top category: most frequent, tie-break by most recent transaction date
  cat_counts = (
    merged.groupby(["customer_id", "category"])
    .agg(cnt=("transaction_id", "count"), max_date=("transaction_date", "max"))
    .reset_index()
  )
  cat_counts.sort_values(
    ["customer_id", "cnt", "max_date"], ascending=[True, False, False], inplace=True
  )
  top_category = (
    cat_counts.groupby("customer_id").first()["category"].rename("top_category")
  )

  result = pd.concat(
    [total_amount, transaction_count, unique_categories, avg_amount, top_category],
    axis=1,
  ).reset_index()
  result["loyalty_score"] = result.apply(
    lambda r: float(
      Decimal(
        str(round(r["transaction_count"] * 10 + r["total_amount"] / 100, 10))
      ).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
    ),
    axis=1,
  )
  return result.sort_values(["loyalty_score", "customer_id"], ascending=[False, True])