精彩专栏推荐订阅:在下方主页👇🏻👇🏻👇🏻👇🏻
💖🔥作者主页:计算机毕设木哥🔥 💖
一、项目介绍
过去三年,优衣库大中华区的年营收从 4587 亿日元抬升到 5385 亿日元,线上订单占比却仅徘徊在 12%~15%,线下门店依旧是吸金主力。观远 BI 的跟踪报告披露,2023 年 2 万 2 千多条门店小票数据里,T 恤与配件贡献了 73% 利润,而北京市场的牛仔裤、运动系列利润不足千元,库存周转天数比深圳多 11 天。区域差异、品类冷热、渠道分流在同一组数据里反复碰撞,却缺少一条能把人、货、场真正串起来的高速通路。Hadoop+Spark 的组合恰好能把这些分散在 POS、小程序、ERP 里的异构日志汇聚成可运算的体量,补上传统 BI 报表“看数快、算数慢”的短板。
把这套技术搬到毕业设计里,不只是为了跑通一个“高大上”的演示。优衣库门店每天都要根据头一天的销售数据决定补货量,早一分钟拿到区域销量热力图,就能少压十件库存;Python+Django 把 Spark 的离线结果封装成可调用的 REST 接口,前端用 Vue+Echarts 把数据投到大屏,技术栈可以在不增加额外服务器预算的情况下,把原本需要 T+3 的决策周期压到 T+0,让库存、营销、陈列的每一环都能听见数据的回声。
二、视频展示
计算机毕设卡壳?《基于大数据的优衣库销售数据分析系统》技术路线+代码一步到位【Hadoop、Spark、Python】
三、开发环境
- 大数据技术:Hadoop、Spark、Hive
- 开发技术:Python、Django框架、Vue、Echarts
- 软件工具:Pycharm、DataGrip、Anaconda
- 可视化 工具 Echarts
四、系统展示
登录模块:
五、代码展示
from pyspark.sql import SparkSession
from pyspark.sql.functions import (
col, sum as _sum, avg, count, when, lag, datediff,
date_format, max as _max, min as _min, round as _round
)
from pyspark.sql.window import Window
import os
spark = (
SparkSession.builder
.appName("uniqlo_real_screenshot_pipeline")
.enableHiveSupport()
.getOrCreate()
)
RAW_PATH = "hdfs://namenode:9000/uniqlo/sales_raw/*.parquet"
INV_PATH = "hdfs://namenode:9000/uniqlo/inventory/*.parquet"
OUT_BASE = "hdfs://namenode:9000/uniqlo/output"
def build_overall_kpi_and_trend():
df = spark.read.parquet(RAW_PATH)
df = df.filter(col("order_date").between("2023-01-01", "2024-06-30"))
df = (
df.withColumn("net_sales", col("qty") * col("unit_price"))
.withColumn("profit", col("net_sales") - col("cost"))
)
# KPI
kpi = (
df.agg(
_sum("net_sales").alias("total_sales"),
_sum("profit").alias("total_profit"),
count("*").alias("total_orders"),
countDistinct("customer_id").alias("total_customers")
)
)
kpi.coalesce(1).write.mode("overwrite").json(f"{OUT_BASE}/overall_kpi")
# 月度趋势
monthly = (
df.groupBy(date_format("order_date", "yyyy-MM").alias("month"))
.agg(
_sum("net_sales").alias("sales"),
_sum("profit").alias("profit")
)
.orderBy("month")
)
monthly.coalesce(1).write.mode("overwrite").json(f"{OUT_BASE}/monthly_trend")
def build_product_analysis():
df = spark.read.parquet(RAW_PATH)
df = (
df.withColumn("net_sales", col("qty") * col("unit_price"))
.withColumn("profit", col("net_sales") - col("cost"))
)
# 品类排行
category_rank = (
df.groupBy("product_category")
.agg(
_sum("net_sales").alias("total_sales"),
_sum("profit").alias("total_profit"),
(_sum("profit") / _sum("net_sales") * 100).alias("profit_margin")
)
.orderBy(col("total_sales").desc())
)
category_rank.coalesce(1).write.mode("overwrite").json(f"{OUT_BASE}/category_rank")
# 负利润明细
negative_profit = (
df.filter(col("profit") < 0)
.groupBy("city", "product_category")
.agg(
_sum("net_sales").alias("sales"),
_sum("profit").alias("loss")
)
.orderBy("city", "product_category")
)
negative_profit.coalesce(1).write.mode("overwrite").json(f"{OUT_BASE}/negative_profit")
def build_region_channel_analysis():
df = spark.read.parquet(RAW_PATH)
df = (
df.withColumn("net_sales", col("qty") * col("unit_price"))
.withColumn("profit", col("net_sales") - col("cost"))
)
# 城市销售排行
city_rank = (
df.groupBy("city")
.agg(_sum("net_sales").alias("sales"))
.orderBy(col("sales").desc())
)
city_rank.coalesce(1).write.mode("overwrite").json(f"{OUT_BASE}/city_rank")
# 渠道销售构成
channel_contrib = (
df.groupBy("channel")
.agg(
_sum("net_sales").alias("sales"),
(_sum("net_sales") / df.agg(_sum("net_sales")).collect()[0][0] * 100).alias("pct")
)
)
channel_contrib.coalesce(1).write.mode("overwrite").json(f"{OUT_BASE}/channel_contrib")
# 城市×品类热力矩阵
city_category = (
df.groupBy("city", "product_category")
.agg(_sum("net_sales").alias("sales"))
)
city_category.coalesce(1).write.mode("overwrite").json(f"{OUT_BASE}/city_category_heatmap")
# 渠道品类差异
channel_category = (
df.groupBy("channel", "product_category")
.agg(_sum("net_sales").alias("sales"))
)
channel_category.coalesce(1).write.mode("overwrite").json(f"{OUT_BASE}/channel_category")
def build_store_rfm():
df = spark.read.parquet(RAW_PATH)
df = df.withColumn("net_sales", col("qty") * col("unit_price"))
# 以门店为粒度聚合 RFM
rfm = (
df.groupBy("store_id")
.agg(
datediff("2024-06-30", _max("order_date")).alias("recency"),
count("*").alias("frequency"),
_sum("net_sales").alias("monetary")
)
)
# 分位点
rec_q = rfm.approxQuantile("recency", [0.5], 0.01)[0]
freq_q = rfm.approxQuantile("frequency", [0.5], 0.01)[0]
mon_q = rfm.approxQuantile("monetary", [0.5], 0.01)[0]
rfm = (
rfm.withColumn("R_score", when(col("recency") < rec_q, 2).otherwise(1))
.withColumn("F_score", when(col("frequency") > freq_q, 2).otherwise(1))
.withColumn("M_score", when(col("monetary") > mon_q, 2).otherwise(1))
.withColumn("score", col("R_score") + col("F_score") + col("M_score"))
)
segment_expr = (
when(col("score") == 6, "重要价值门店")
.when(col("score") == 5, "重要发展门店")
.when(col("score") == 4, "重要保持门店")
.when(col("score") == 3, "重要挽留门店")
.when(col("score") == 2, "流失风险门店")
.otherwise("需重点激活门店")
)
rfm = rfm.withColumn("segment", segment_expr)
rfm.coalesce(1).write.mode("overwrite").json(f"{OUT_BASE}/store_rfm")
def build_customer_profile():
df = spark.read.parquet(RAW_PATH)
df = df.withColumn("net_sales", col("qty") * col("unit_price"))
# 性别贡献
gender_contrib = (
df.groupBy("gender")
.agg(_sum("net_sales").alias("sales"))
)
gender_contrib.coalesce(1).write.mode("overwrite").json(f"{OUT_BASE}/gender_contrib")
# 年龄贡献
age_contrib = (
df.groupBy("age_group")
.agg(_sum("net_sales").alias("sales"))
.orderBy("age_group")
)
age_contrib.coalesce(1).write.mode("overwrite").json(f"{OUT_BASE}/age_contrib")
# 客单价
age_avg = (
df.groupBy("age_group")
.agg((_sum("net_sales") / count("*")).alias("avg_order_value"))
.orderBy("age_group")
)
age_avg.coalesce(1).write.mode("overwrite").json(f"{OUT_BASE}/age_avg_order")
def build_weekday_weekend():
df = spark.read.parquet(RAW_PATH)
df = df.withColumn("net_sales", col("qty") * col("unit_price"))
df = df.withColumn("week_part", date_format("order_date", "u").cast("int"))
df = df.withColumn("week_part", when(col("week_part") < 6, "工作日").otherwise("周末"))
weekday_weekend = (
df.groupBy("week_part", "product_category")
.agg(_sum("net_sales").alias("sales"))
)
weekday_weekend.coalesce(1).write.mode("overwrite").json(f"{OUT_BASE}/weekday_weekend")
def build_price_sensitivity():
df = spark.read.parquet(RAW_PATH)
df = (
df.withColumn("net_sales", col("qty") * col("unit_price"))
.withColumn("profit", col("net_sales") - col("cost"))
)
price_sens = (
df.groupBy("product_category")
.agg(
avg("unit_price").alias("avg_price"),
(_sum("profit") / _sum("net_sales") * 100).alias("profit_margin")
)
)
price_sens.coalesce(1).write.mode("overwrite").json(f"{OUT_BASE}/price_sensitivity")
六、项目文档展示
七、项目总结
一路做下来,这套《基于大数据的优衣库销售数据分析系统》把“看数”真正变成了“用数”。Hadoop 与 Spark 扛住千万级小票,Python+Django 把复杂逻辑封装成可调接口,Vue+Echarts 把城市热力、品类盈亏、RFM 门店分级一次性推到浏览器和大屏。不再是静态数字,而是可下钻、可预警、可行动的活数据。项目验证了从原始日志到商业洞察的完整闭环,也证明了毕业设计不必堆技术名词,只要围绕真实业务痛点,一套简洁而聚焦的栈就能让数据说话。
大家可以帮忙点赞、收藏、关注、评论啦 👇🏻
💖🔥作者主页:计算机毕设木哥🔥 💖