Bootstrap

淘宝用户行为分析大数据可视化

1. 项目概述

此项目旨在分析淘宝用户的行为数据,通过可视化技术展示关键统计数据,帮助了解用户的行为模式和偏好。项目包括数据处理、数据可视化以及后端数据服务。

获取项目代码
🚀 点击此处获取完整项目代码

运行效果展示图
运行效果展示图

视频演示

大数据可视化项目运行效果演示

2. 技术栈

  • 前端: HTML, CSS, JavaScript, ECharts
  • 后端: Flask
  • 数据处理: Python, pandas
  • 数据库: MySQL
  • 缓存: Flask-Caching

3. 目录结构

taobao-user-behavior-analysis
├── static
│   ├── css
│   │   └── layer.css
│   ├── js
│   │   ├── chart.js
│   │   └── dataService.js
├── templates
│   └── index.html
├── data_processing.py
├── app.py
└── data
    ├── user_data.csv
    ├── daily_behavior.csv
    ├── hour_behavior.csv
    ├── summary_data.csv
    ├── top_click_items.csv
    └── top_purchase_items.csv

4. 数据处理流程

  • 数据读取:

    • 从名为 user_data.csv 的文件中读取用户行为数据。这些数据包括用户ID、商品ID、行为类型(点击、购买、收藏等)、行为发生时间等字段。
  • 数据清洗:

    • 清除空值和重复值: 对数据进行清洗操作,去除空值和重复的数据记录,确保数据质量。
    • 处理时间戳: 将时间戳字段转换为可识别的日期时间格式,以便后续的时间分组统计。
    • 时间范围处理: 根据业务需求,确保数据时间范围合理,可能需要过滤掉异常时间点或不合理的时间数据。
  • 特征工程:

    • 行为统计: 根据用户行为类型(点击、购买、收藏等),计算各种行为的统计数据,如每个用户的总点击次数、总购买次数、总收藏次数等。
    • 独热编码处理: 将分类数据(如行为类型)转换为独热编码形式,以便后续的机器学习模型或分析使用。
  • 数据库存储:

    • 将经过清洗和特征工程处理后的数据保存到 MySQL 数据库中。数据库中的表结构应该能够容纳用户行为数据,并支持快速查询和统计。
    • 每日和每小时的行为统计数据文件生成: 将数据按照日期和小时进行分组,生成每日和每小时的行为统计数据文件。这些文件可以用于后续的数据分析、报表生成或者用作机器学习模型的训练数据。

通过以上数据处理流程,可以保证从原始数据到最终存储的过程中数据质量和处理效率的高标准。这些步骤不仅保证了数据的准确性和完整性,还为后续的数据分析和应用提供了基础。

5. 前端部分

5.1 HTML (index.html)

  • 功能:

    • 定义页面的整体结构和布局。
    • 包含头部导航栏、数据概览表格和多个图表容器。
    • 页面内的按钮和导航元素用于提供用户交互功能,如刷新数据和切换视图。
  • 具体内容:

    • 头部: 包含页面标题和导航栏,便于用户在不同数据视图之间进行切换。导航栏中的按钮可以触发不同的前端功能,如刷新数据和切换到每日或分时统计视图。
    • 数据概览表格: 显示关键统计数据的表格,如用户数、商品数、类别数、点击数、购买数、购物车数和收藏数。这些数据可以帮助用户快速了解整体情况。
    • 图表容器: 定义用于嵌入和展示 ECharts 图表的 <div> 容器。每个图表容器对应一个特定的数据视图,例如每日用户行为统计、分时用户行为统计、点击Top10商品和购买Top10商品。

5.2 CSS (layer.css)

  • 功能:

    • 设置页面的全局样式和布局样式,确保页面的一致性和美观性。
    • 使用媒体查询实现响应式设计,确保页面在不同设备和屏幕尺寸下都能正常显示和使用。
  • 具体内容:

    • 全局样式: 定义基本的字体、颜色和其他全局样式设置,确保页面的整体视觉效果一致。
    • 布局样式: 配置头部、导航栏、数据表格和图表容器的布局,使页面结构清晰、内容易于阅读。
    • 媒体查询: 根据不同的屏幕尺寸调整布局和样式,确保在移动设备和桌面设备上都有良好的用户体验。例如,在小屏幕设备上,图表容器可能会从横向排列改为纵向排列,以适应屏幕宽度。

5.3 JavaScript (chart.js)

  • 功能:

    • 负责从后端获取数据,并将数据传递给图表组件。
    • 初始化并配置 ECharts 图表,包括设置图表的动画效果和事件监听,以增强用户的交互体验。
  • 具体内容:

    • 获取数据: 使用 AJAX 请求从后端 API 获取图表所需的数据。确保数据请求的稳定性和正确性,并处理可能的错误情况。
    • 初始化图表: 使用 ECharts 初始化图表实例,配置图表的基本选项,包括标题、图例、坐标轴和数据系列。
    • 渲染图表: 将获取的数据填充到图表中,并在页面上渲染图表。确保图表能够动态更新和显示最新的数据。
    • 事件监听: 为图表添加交互事件监听器,如点击事件和悬停事件。通过这些事件可以实现更丰富的交互功能,如点击图表元素显示详细信息或跳转到相关页面。

通过以上 HTML、CSS 和 JavaScript 的协调工作,前端部分能够实现良好的用户界面和交互体验,动态展示数据分析结果,并支持用户进行多种操作和查看不同的数据视图。

6. 后端部分

Flask 应用 (app.py)

  • API 接口: Flask 应用提供了一个 API 接口,用于处理前端请求。主要的 API 是 /api/chartData,该接口返回图表所需的数据。前端通过调用此接口获取数据并进行展示。

  • 处理前端请求: Flask 应用的主程序处理前端发送的 HTTP 请求。根路径 / 返回一个 HTML 页面,其中包含数据概要信息。API 路径 /api/chartData 处理 GET 请求,返回所需的图表数据。

  • 数据预处理: 在启动 Flask 应用时,如果本地数据文件不存在,会预先处理数据并将结果存储到 CSV 文件中。这些预处理包括按日期、按小时、按商品 ID 分组汇总数据,并生成数据概览。

  • 数据缓存: 为了提高性能,Flask 应用使用 flask_caching 模块缓存 API 接口的响应数据。缓存时间设定为 3600 秒(即 1 小时),在此时间段内重复的请求会直接从缓存中获取数据,而不需要重新处理。

  • 数据读取与分页: 从预处理生成的 CSV 文件中读取数据,并根据请求的分页参数(page 和 page_size)进行分页处理,以减少一次性返回大量数据的压力。

数据服务 (dataService.js)

  • HTTP 请求: dataService.js 负责发起 HTTP 请求,调用 Flask 应用提供的 API 接口 /api/chartData,以获取所需的图表数据。

  • 错误处理: 在发起 HTTP 请求过程中,dataService.js 处理可能出现的网络错误和响应错误。如果请求失败,会在控制台打印错误信息,并返回 null,以确保前端能够处理数据获取失败的情况。

通过以上后端部分的设计和实现,整个应用能够高效地处理前端请求,提供所需的图表数据,并通过缓存机制提高性能,确保用户体验的流畅性。

7. 数据可视化

7.1 ECharts 图表

  • 每日用户行为统计

    • 功能: 展示每日用户在平台上的各类行为的统计数据,包括点击、购买、添加购物车和收藏等行为。
    • 具体内容:
      • 图表类型: 采用折线图或柱状图,便于展示随时间变化的趋势。
      • 数据展示: 每日的行为数据以时间为横轴,行为数量为纵轴进行展示。
      • 交互功能: 支持鼠标悬停显示详细数据,点击某一天的数据点可以显示该天的详细行为信息。
  • 分时用户行为统计

    • 功能: 细化展示用户在不同时间段内的行为数据,帮助了解一天内用户行为的峰谷时段。
    • 具体内容:
      • 图表类型: 采用堆叠柱状图或面积图,以不同颜色区分不同的行为类型。
      • 数据展示: 时间以小时为单位,横轴表示一天中的24个小时,纵轴表示各个时间段的行为数量。
      • 交互功能: 鼠标悬停时显示具体时间段的详细数据,支持放大某个时间段进行更详细的查看。
  • 点击Top10商品

    • 功能: 展示点击次数最多的前10个商品,帮助了解用户最感兴趣的商品。
    • 具体内容:
      • 图表类型: 采用横向条形图,便于比较不同商品的点击次数。
      • 数据展示: 横轴表示商品名称或ID,纵轴表示点击次数。前10个点击次数最多的商品按从高到低排序。
      • 交互功能: 鼠标悬停时显示商品的详细信息,点击某个商品可以跳转到商品详情页面或显示更详细的统计信息。
  • 购买Top10商品

    • 功能: 展示购买次数最多的前10个商品,帮助了解用户购买行为的偏好。
    • 具体内容:
      • 图表类型: 采用横向条形图,便于比较不同商品的购买次数。
      • 数据展示: 横轴表示商品名称或ID,纵轴表示购买次数。前10个购买次数最多的商品按从高到低排序。
      • 交互功能: 鼠标悬停时显示商品的详细信息,点击某个商品可以跳转到商品详情页面或显示更详细的统计信息。

通过这些 ECharts 图表,可以直观、动态地展示淘宝用户的行为数据,帮助分析和理解用户行为模式和偏好,从而为业务决策提供有力支持。图表的交互功能增强了用户体验,使数据的展示更加生动和易于理解。

8. 主要功能模块

  • 数据读取与处理:

    • 文件: data_processing.py
    • 功能: 该模块负责从 CSV 文件中读取原始数据。读取的数据可能包含缺失值和不一致的数据格式,因此模块中包含数据清洗和转换的功能。特征工程部分用于从原始数据中提取有用的特征,以便后续的分析和可视化。
    • 具体步骤:
      • 使用 pandas 库读取 CSV 文件。
      • 处理缺失值,如填充、删除或插值。
      • 数据格式转换,如日期格式统一。
      • 特征工程,如创建新的衍生特征或数据聚合。
  • 数据库操作:

    • 工具: SQLAlchemy
    • 功能: 该模块负责与 MySQL 数据库的交互。使用 SQLAlchemy 创建数据库连接,定义数据模型,并将处理后的数据保存到数据库中。
    • 具体步骤:
      • 创建数据库连接字符串,配置数据库连接参数。
      • 使用 SQLAlchemy 定义 ORM 模型,映射数据库表结构。
      • 将清洗和处理后的数据插入数据库表中。
      • 实现查询功能,以便后端 API 可以获取需要的数据。
  • 后端 API:

    • 框架: Flask
    • 功能: 提供 RESTful API 接口,供前端通过 AJAX 调用以获取图表数据。API 负责从数据库中检索数据,并以 JSON 格式返回给前端。
    • 具体步骤:
      • 定义 Flask 应用程序,配置路由。
      • 实现数据查询 API,处理前端请求。
      • 在 API 中调用数据库操作模块,从数据库检索数据。
      • 将检索到的数据转换为 JSON 格式,并通过 API 返回。
  • 图表渲染:

    • 库: ECharts, chart.js
    • 功能: 负责在前端页面中使用 ECharts 库渲染图表。初始化和配置图表,包括过渡动画和事件监听,以提高用户交互体验。
    • 具体步骤:
      • 在前端页面中引入 ECharts 和 chart.js 库。
      • 使用 AJAX 请求后端 API 获取图表数据。
      • 使用 ECharts 初始化图表,配置图表选项,如图例、轴、数据系列等。
      • 实现图表过渡动画,以增强视觉效果。
      • 监听图表事件,如点击、悬停,提供用户交互功能。

通过这些功能模块,系统能够高效地处理数据,存储和检索数据,并在前端提供动态、交互性强的数据可视化图表。

9. 代码解析

  • layer.css: 定义了 HTML 元素、布局、图表容器和按钮的样式,确保页面的整体外观。
  • index.html: 使用 HTML 构建页面结构,包含头部、数据表格、图表容器和按钮。
  • chart.js: 包含初始化 ECharts 图表实例、定义图表选项、获取后端数据并渲染图表的逻辑。
  • dataService.js: 提供 fetchData 函数,用于发送 HTTP 请求并处理响应数据。
  • data_processing.py: 负责读取、清洗和处理用户行为数据,并将处理后的数据存入数据库。
  • app.py: 创建 Flask 应用,提供 API 接口以供前端获取图表数据,并配置缓存。

10. 数据接口

  • API 路由: /api/chartData
    • 参数: 无参数

      • 说明: 该 API 不需要额外的请求参数,调用时直接返回所有图表所需的数据。
    • 返回值: 返回 JSON 格式的图表数据

      • 数据结构: 返回的数据包括以下几个部分:

        • 每日用户行为统计daily_user_behavior):包含每一天的用户点击、购买、加入购物车和收藏行为的统计数据。
        • 分时用户行为统计hourly_user_behavior):包含每天每小时的用户点击、购买、加入购物车和收藏行为的统计数据。
        • 点击 Top10 商品top_click_items):包含点击次数最多的前10个商品的详细信息。
        • 购买 Top10 商品top_purchase_items):包含购买次数最多的前10个商品的详细信息。
      • 字段说明

        • 每日用户行为统计
          • date:日期(格式为 YYYY-MM-DD
          • clicks:点击次数
          • purchases:购买次数
          • cart_additions:加入购物车次数
          • favorites:收藏次数
        • 分时用户行为统计
          • hour:小时(格式为 HH
          • clicks:点击次数
          • purchases:购买次数
          • cart_additions:加入购物车次数
          • favorites:收藏次数
        • 点击 Top10 商品
          • item_id:商品ID
          • item_name:商品名称
          • click_count:点击次数
        • 购买 Top10 商品
          • item_id:商品ID
          • item_name:商品名称
          • purchase_count:购买次数

通过这个 API,前端可以轻松获取所需的所有图表数据,并根据需要进行展示和交互。这些数据将用于绘制每日和分时用户行为统计图表,以及点击和购买次数最多的商品排行图表,帮助用户直观地了解淘宝用户的行为模式和偏好。

11. 项目功能描述

项目通过对淘宝用户行为数据的分析,提供以下功能:

  • 展示每日用户的点击、购买、购物车和收藏行为统计
  • 展示分时用户的点击和购买行为统计
  • 展示点击Top10商品和购买Top10商品
  • 提供数据概览,包括用户数、商品数、类别数、点击数、购买数、购物车数和收藏数

12. 代码功能实现

12.1 HTML (index.html)

定义页面结构,包含头部、数据表格和图表容器。

<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>数据可视化</title>
    <!-- 引入 layer.css 样式表 -->
    <link href="../static/css/layer.css" rel="stylesheet">
    <!-- 引入 echarts.min.js 绘图库 -->
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/echarts.min.js"></script>
    <!-- 引入 main.js 脚本文件,type="module" 表示模块化加载 -->
    <script src="../static/js/main.js" type="module"></script>
</head>

<body>
<!-- 页面头部 -->
<header>
    <h1>淘宝用户行为大数据分析</h1>
</header>

<section>
    <!-- 数据表格部分,用于展示数据摘要 -->
    <div class="data-table">
        <div class="data-item">
            {# 使用模板语法 {{ }} 来插入数据 #}
            <p>{{ data_summary.user_count }}</p>
            <p>用户</p>
        </div>

        <div class="data-item">
            <p>{{ data_summary.item_count }}</p>
            <p>商品</p>
        </div>

        <div class="data-item">
            <p>{{ data_summary.category_count }}</p>
            <p>类别</p>
        </div>

        <div class="data-item">
            <p>{{ data_summary.click_count }}</p>
            <p>点击</p>
        </div>

        <div class="data-item">
            <p>{{ data_summary.purchase_count }}</p>
            <p>购买</p>
        </div>

        <div class="data-item">
            <p>{{ data_summary.cart_count }}</p>
            <p>购物车</p>
        </div>

        <div class="data-item">
            <p>{{ data_summary.fav_count }}</p>
            <p>收藏</p>
        </div>
    </div>

    <!-- 图表容器部分 -->
    <div class="charts-container">
        <!-- 每日用户行为统计图表 -->
        <article>
            <div id="dailyChart" style="width: 100%; height: 300px; min-height: 300px;"></div>
        </article>

        <!-- 分时用户行为统计图表 -->
        <article>
            <div id="hourlyChart" style="width: 100%; height: 300px; min-height: 300px;"></div>
        </article>

        <!-- 点击Top10商品图表 -->
        <article>
            <div id="clickChart" style="width: 100%; height: 300px; min-height: 300px;"></div>
        </article>

        <!-- 购买Top10商品图表 -->
        <article>
            <div id="purchaseChart" style="width: 100%; height: 300px; min-height: 300px;"></div>
        </article>
    </div>

    <!-- 按钮区域 -->
    <div class="buttons">
        <button id="overviewBtn">总览</button>
        <button id="timeBtn">时间</button>
        <button id="top10Btn">Top10</button>
    </div>
</section>
</body>

</html>

12.2 CSS (layer.css)

定义全局样式和各部分的样式。

/* 全局样式设置 */
html {
    font-family: sans-serif; /* 设置全局字体为sans-serif */
    height: 100%; /* 设置html元素的高度为100% */
    margin: 0; /* 移除html元素的默认外边距 */
}

body {
    margin: 0; /* 移除body元素的默认外边距 */
    height: 100%; /* 设置body元素的高度为100% */
    background: #111; /* 设置背景颜色为深灰色 */
    color: #fff; /* 设置文本颜色为白色 */
    font-family: 'Arial', sans-serif; /* 设置全局字体为Arial */
    overflow-x: hidden; /* 隐藏水平滚动条 */
    display: flex; /* 使用flex布局 */
    flex-direction: column; /* 设置flex方向为纵向 */
}

/* 头部样式设置 */
header {
    height: 80px; /* 设置头部高度 */
    display: flex; /* 使用flex布局 */
    align-items: center; /* 垂直居中 */
    padding: 0 20px; /* 设置内边距 */
    justify-content: center; /* 水平居中 */
}

/* 数据表格样式设置 */
.data-table {
    display: flex; /* 使用flex布局 */
    justify-content: space-around; /* 子元素等距排列 */
    margin: 10px 0; /* 设置外边距 */
    padding: 2px; /* 缩小内边距 */
    border-radius: 8px; /* 设置圆角 */
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); /* 设置阴影效果 */
    color: #f5f5f5; /* 设置文本颜色 */
    height: 60px; /* 控制整体高度 */
}

.data-table div {
    flex: 1 1 200px; /* 设置flex-grow, flex-shrink 和 flex-basis */
    text-align: center; /* 文本居中对齐 */
    margin: 0 10px; /* 设置外边距 */
    padding: 30px; /* 设置内边距 */
    border: 1px solid #ccc; /* 设置边框 */
}

/* 单个数据项样式设置 */
.data-item {
    text-align: center; /* 文本居中对齐 */
    background-color: #2c2c2e; /* 设置背景颜色 */
    padding: 3px 5px; /* 缩小内边距 */
    border-radius: 8px; /* 设置圆角 */
    transition: transform 0.3s, box-shadow 0.3s; /* 设置变换和阴影的过渡效果 */
    color: #ffffff; /* 设置文本颜色 */
    margin: 2px; /* 设置外边距 */
    flex: 1; /* 使每个元素均分父元素宽度 */
    max-height: 60px; /* 控制每个数据项的最大高度 */
    display: flex; /* 使用flex布局 */
    flex-direction: column; /* 设置flex方向为纵向 */
    justify-content: center; /* 垂直居中 */
}

.data-item:hover {
    transform: translateY(-3px); /* 鼠标悬停时上移 */
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); /* 设置阴影效果 */
}

.data-item p:first-child {
    font-size: 18px; /* 设置字体大小 */
    font-weight: bold; /* 设置字体粗细 */
    color: #3498db; /* 设置数字颜色为蓝色 */
    margin: 0; /* 移除默认的外边距 */
}

.data-item p:last-child {
    font-size: 14px; /* 设置字体大小 */
    color: #a1a1a1; /* 设置文本颜色 */
    margin: 0; /* 移除默认的外边距 */
}

/* 标题样式设置 */
h1 {
    font-size: 3rem; /* 设置字体大小 */
    font-weight: bold; /* 设置字体粗细 */
    margin: 0; /* 移除默认的外边距 */
    text-align: center; /* 文本居中对齐 */
    background: linear-gradient(90deg, #00c6ff, #0072ff); /* 设置背景渐变 */
    -webkit-background-clip: text; /* 使背景渐变应用到文本 */
    -webkit-text-fill-color: transparent; /* 使文本填充颜色透明 */
    animation: gradientAnimation 5s ease infinite; /* 设置渐变动画 */
    text-shadow: 0 4px 8px rgba(0, 0, 0, 0.4); /* 设置文本阴影 */
    letter-spacing: 3px; /* 设置字母间距 */
    padding: 20px 0; /* 设置内边距 */
    position: relative; /* 设置相对定位 */
}

h1::before {
    content: ""; /* 插入一个内容为空的伪元素 */
    position: absolute; /* 绝对定位 */
    left: 0; /* 左边距 */
    right: 0; /* 右边距 */
    bottom: 0; /* 底边距 */
    height: 4px; /* 设置高度 */
    background: linear-gradient(90deg, transparent, #0072ff, transparent); /* 设置背景渐变 */
    animation: glow 2s ease-in-out infinite; /* 设置发光动画 */
    border-radius: 4px; /* 设置圆角 */
}

/* 渐变动画 */
@keyframes gradientAnimation {
    0% {
        background-position: 0% 50%; /* 初始位置 */
    }
    50% {
        background-position: 100% 50%; /* 中间位置 */
    }
    100% {
        background-position: 0% 50%; /* 最终位置 */
    }
}

/* 发光动画 */
@keyframes glow {
    0% {
        opacity: 0; /* 初始透明度 */
        box-shadow: 0 0 10px #0072ff; /* 初始阴影 */
    }
    50% {
        opacity: 1; /* 中间透明度 */
        box-shadow: 0 0 20px #0072ff; /* 中间阴影 */
    }
    100% {
        opacity: 0; /* 最终透明度 */
        box-shadow: 0 0 10px #0072ff; /* 最终阴影 */
    }
}

/* 主体样式设置 */
main {
    display: flex; /* 使用flex布局 */
    flex-grow: 1; /* 使主体内容填满剩余空间 */
    padding: 20px; /* 设置内边距 */
    overflow: auto; /* 允许滚动 */
}

/* 图表容器样式设置 */
.charts-container {
    display: flex; /* 使用flex布局 */
    flex-wrap: wrap; /* 允许换行 */
    padding: 10px; /* 设置内边距 */
    gap: 10px; /* 设置子元素间距 */
}

.charts-container > article {
    width: 48%; /* 设置宽度 */
    background: #222; /* 设置背景颜色 */
    padding: 10px; /* 设置内边距 */
    border-radius: 5px; /* 设置圆角 */
    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); /* 设置阴影效果 */
}

/* 按钮容器样式设置 */
.buttons {
    display: flex; /* 使用flex布局 */
    justify-content: flex-end; /* 子元素靠右对齐 */
    padding: 20px; /* 设置内边距 */
}

/* 按钮样式设置 */
.buttons button {
    margin-left: 10px; /* 设置左外边距 */
    background-color: #333; /* 设置背景颜色 */
    color: #fff; /* 设置文本颜色 */
    border: none; /* 移除默认边框 */
    padding: 10px 20px; /* 设置内边距 */
    border-radius: 5px; /* 设置圆角 */
    cursor: pointer; /* 设置鼠标指针样式 */
}

/* 媒体查询:在屏幕宽度小于768像素时应用以下样式 */
@media (max-width: 768px) {
    .charts-container > article {
        width: 100%; /* 设置宽度为100% */
    }
}

12.3 JavaScript (chart.js)

初始化 ECharts 图表,并从后端获取数据。

import {fetchData} from './dataService.js'; // 引入自定义的数据服务模块,用于获取数据

// 获取图表 DOM 节点
const chartDom0 = document.getElementById('dailyChart'); // 获取每日用户行为统计图表的 DOM 节点
const chartDom1 = document.getElementById('hourlyChart'); // 获取分时用户行为统计图表的 DOM 节点
const chartDom2 = document.getElementById('clickChart'); // 获取点击Top10图表的 DOM 节点
const chartDom3 = document.getElementById('purchaseChart'); // 获取购买Top10图表的 DOM 节点

// 初始化 ECharts 图表实例
let charts = {
    dailyChart: echarts.init(chartDom0), // 初始化每日用户行为统计图表
    hourlyChart: echarts.init(chartDom1), // 初始化分时用户行为统计图表
    clickChart: echarts.init(chartDom2), // 初始化点击Top10图表
    purchaseChart: echarts.init(chartDom3) // 初始化购买Top10图表
};

// 函数用于处理后端返回的数据并渲染图表
function renderCharts(data) {
    console.log("Rendering charts with data:", data); // 打印调试信息,显示接收到的数据

    // 1. 获取数据
    const dailyChartData = data.dailyChartData; // 获取每日用户行为统计数据
    const hourlyChartData = data.hourlyChartData; // 获取分时用户行为统计数据
    const clickChartData = data.clickChartData; // 获取点击Top10数据
    const purchaseChartData = data.purchaseChartData; // 获取购买Top10数据

    // 2. 设置指定的日期范围
    const startDate = '2017-11-24'; // 设置开始日期
    const endDate = '2017-12-03'; // 设置结束日期

    // 3. 找到范围内数据的索引
    const startIndex = dailyChartData.xAxisData.indexOf(startDate); // 找到开始日期在数据中的索引
    const endIndex = dailyChartData.xAxisData.indexOf(endDate) + 1; // 找到结束日期在数据中的索引(加1以包含结束日期)

    // 4. 过滤范围内的数据
    const filteredData = {
        xAxisData: dailyChartData.xAxisData.slice(startIndex, endIndex), // 过滤X轴数据
        clickData: dailyChartData.clickData.slice(startIndex, endIndex), // 过滤点击数据
        purchaseData: dailyChartData.purchaseData.slice(startIndex, endIndex), // 过滤购买数据
        cartData: dailyChartData.cartData.slice(startIndex, endIndex), // 过滤购物车数据
        favData: dailyChartData.favData.slice(startIndex, endIndex) // 过滤收藏数据
    };

    // 5. 更新图表配置
    const option1 = {
        title: {text: '每日用户行为统计'}, // 图表标题
        tooltip: {}, // 提示框
        legend: {data: ['点击', '购买', '购物车', '收藏']}, // 图例
        xAxis: {
            type: 'category',
            data: filteredData.xAxisData,
            axisLabel: {
                interval: 1 // 每隔一个标签显示一次
            }
        },
        yAxis: {type: 'value'}, // Y轴为数值轴
        series: [
            {
                name: '点击',
                data: filteredData.clickData,
                type: 'line',
                animationDuration: 2000, // 动画持续时间
                animationEasing: 'cubicOut', // 动画缓动效果
                animationDelay: (idx) => idx * 100 // 动画延迟
            },
            {
                name: '购买',
                data: filteredData.purchaseData,
                type: 'line',
                animationDuration: 2000,
                animationEasing: 'cubicOut',
                animationDelay: (idx) => idx * 100
            },
            {
                name: '购物车',
                data: filteredData.cartData,
                type: 'line',
                animationDuration: 2000,
                animationEasing: 'cubicOut',
                animationDelay: (idx) => idx * 100
            },
            {
                name: '收藏',
                data: filteredData.favData,
                type: 'line',
                animationDuration: 2000,
                animationEasing: 'cubicOut',
                animationDelay: (idx) => idx * 100
            }
        ]
    };

    const option2 = {
        title: {text: '分时用户行为统计'}, // 图表标题
        tooltip: {}, // 提示框
        legend: {data: ['点击', '购买']}, // 图例
        xAxis: {
            type: 'category',
            data: hourlyChartData.xAxisData,
            axisLabel: {
                interval: 1 // 每隔一个标签显示一次
            }
        },
        yAxis: {type: 'value'}, // Y轴为数值轴
        series: [
            {
                name: '点击',
                data: hourlyChartData.clickData,
                type: 'line',
                animationDuration: 2000, // 动画持续时间
                animationEasing: 'cubicOut', // 动画缓动效果
                animationDelay: (idx) => idx * 100 // 动画延迟
            },
            {
                name: '购买',
                data: hourlyChartData.purchaseData,
                type: 'line',
                animationDuration: 2000,
                animationEasing: 'cubicOut',
                animationDelay: (idx) => idx * 100
            }
        ]
    };

    const option3 = {
        title: {text: '点击Top10'}, // 图表标题
        tooltip: {}, // 提示框
        xAxis: {
            type: 'category',
            data: clickChartData.xAxisData,
            axisLabel: {
                interval: 0 // 每个标签都显示
            }
        },
        yAxis: {type: 'value'}, // Y轴为数值轴
        series: [{
            name: '点击次数',
            data: clickChartData.seriesData,
            type: 'bar',
            itemStyle: {
                color: function (params) {
                    const colors = ['#5470C6', '#91CC75', '#EE6666', '#73C0DE', '#3BA272', '#FC8452', '#9A60B4', '#EA7CCC', '#3E92CC', '#F4A460'];
                    return colors[params.dataIndex % colors.length]; // 根据索引设置不同颜色
                }
            },
            barWidth: '20%', // 柱状图宽度
            animationDuration: 2000, // 动画持续时间
            animationEasing: 'elasticOut', // 动画缓动效果
            animationDelay: (idx) => idx * 100 // 动画延迟
        }]
    };

    const option4 = {
        title: {text: '购买Top10'}, // 图表标题
        tooltip: {}, // 提示框
        xAxis: {
            type: 'category',
            data: purchaseChartData.xAxisData,
            axisLabel: {
                interval: 0 // 每个标签都显示
            }
        },
        yAxis: {type: 'value'}, // Y轴为数值轴
        series: [{
            name: '购买次数',
            data: purchaseChartData.seriesData,
            type: 'bar',
            itemStyle: {
                color: function (params) {
                    const colors = ['#5470C6', '#91CC75', '#EE6666', '#73C0DE', '#3BA272', '#FC8452', '#9A60B4', '#EA7CCC', '#3E92CC', '#F4A460'];
                    return colors[params.dataIndex % colors.length]; // 根据索引设置不同颜色
                }
            },
            barWidth: '20%', // 柱状图宽度
            animationDuration: 2000, // 动画持续时间
            animationEasing: 'elasticOut', // 动画缓动效果
            animationDelay: (idx) => idx * 100 // 动画延迟
        }]
    };

    // 设置图表选项并添加过渡动画
    charts.dailyChart.setOption(option1); // 设置每日用户行为统计图表选项
    charts.hourlyChart.setOption(option2); // 设置分时用户行为统计图表选项
    charts.clickChart.setOption(option3); // 设置点击Top10图表选项
    charts.purchaseChart.setOption(option4); // 设置购买Top10图表选项

    // 添加过渡动画效果
    setTimeout(() => {
        charts.dailyChart.hideLoading(); // 隐藏每日用户行为统计图表的加载动画
        charts.hourlyChart.hideLoading(); // 隐藏分时用户行为统计图表的加载动画
        charts.clickChart.hideLoading(); // 隐藏点击Top10图表的加载动画
        charts.purchaseChart.hideLoading(); // 隐藏购买Top10图表的加载动画
    }, 300); // 设置延迟时间,确保图表渲染完毕后再移除 loading 效果
}

// 重新初始化图表实例以触发加载动画和逐点绘制效果
function reinitializeCharts() {
    Object.keys(charts).forEach(key => {
        charts[key].dispose(); // 销毁当前图表实例
        charts[key] = echarts.init(document.getElementById(key)); // 重新初始化图表实例
    });
}

// 获取后端数据并渲染图表
function updateCharts() {
    fetchData('/api/chartData') // 调用 fetchData 函数获取数据
        .then(data => {
            reinitializeCharts(); // 重新初始化图表实例以触发加载动画和逐点绘制效果
            renderCharts(data); // 调用渲染图表的函数,并传入从后端获取的数据
        })
        .catch(error => {
            console.error('Error fetching chart data:', error); // 捕获并打印获取数据过程中的错误
        });
}

// 添加过渡动画
function animateChart(chartDom, show = true) {
    chartDom.style.transition = 'opacity 0.5s'; // 设置过渡效果
    chartDom.style.opacity = show ? 1 : 0; // 根据 show 参数设置透明度
}

// 防抖处理函数,避免频繁触发函数
function debounce(func, wait) {
    let timeout; // 定义一个定时器变量
    return function (...args) {
        clearTimeout(timeout); // 每次调用时清除上一次的定时器
        timeout = setTimeout(() => func.apply(this, args), wait); // 重新设置定时器
    };
}

// 按钮事件监听器
document.getElementById("overviewBtn").addEventListener("click", debounce(function () {
    showAllCharts(); // 调用显示所有图表的函数
}, 300));

document.getElementById("timeBtn").addEventListener("click", debounce(function () {
    hideAllCharts(); // 调用隐藏所有图表的函数
    setTimeout(() => {
        [chartDom0, chartDom1].forEach(dom => {
            dom.style.display = 'block'; // 显示指定的图表
            animateChart(dom, true); // 触发显示动画
        });
        updateCharts(); // 更新图表
    }, 500); // 设置延迟时间,确保图表隐藏动画完成后再显示新图表
}, 300));

document.getElementById("top10Btn").addEventListener("click", debounce(function () {
    hideAllCharts(); // 调用隐藏所有图表的函数
    setTimeout(() => {
        [chartDom2, chartDom3].forEach(dom => {
            dom.style.display = 'block'; // 显示指定的图表
            animateChart(dom, true); // 触发显示动画
        });
        updateCharts(); // 更新图表
    }, 500); // 设置延迟时间,确保图表隐藏动画完成后再显示新图表
}, 300));

// 隐藏所有图表并显示加载动画
function hideAllCharts() {
    Object.values(charts).forEach(chart => chart.showLoading()); // 显示所有图表的加载动画
    setTimeout(() => {
        [chartDom0, chartDom1, chartDom2, chartDom3].forEach(dom => animateChart(dom, false)); // 触发隐藏动画
    }, 300); // 设置延迟时间,确保 loading 效果显示出来
}

// 显示所有图表并触发动画效果和更新
function showAllCharts() {
    Object.values(charts).forEach(chart => chart.showLoading()); // 显示所有图表的加载动画
    setTimeout(() => {
        [chartDom0, chartDom1, chartDom2, chartDom3].forEach(dom => {
            dom.style.display = 'block'; // 显示所有图表
            animateChart(dom, true); // 触发显示动画
        });
        updateCharts(); // 更新图表
    }, 500); // 设置延迟时间,确保图表隐藏动画完成后再显示新图表
}

12.4 JavaScript (dataService.js)

从后端获取图表数据。

// 函数用于发起HTTP请求并获取数据
async function fetchData(url) {
    try {
        const response = await fetch(url);  // 发起异步请求,等待响应
        if (!response.ok) {  // 如果响应状态不为 200-299,则抛出错误
            throw new Error('Network response was not ok ' + response.statusText);
        }
        const data = await response.json();  // 解析响应体为 JSON 格式
        console.log('Data fetched:', data);  // 打印获取的数据,用于调试和验证
        return data;  // 返回解析后的数据对象
    } catch (error) {
        console.error('There was a problem with the fetch operation:', error);  // 捕获并打印发生的错误
        throw error;  // 抛出错误,传播给调用方处理
    }
}

// 导出函数供其他模块使用
export {fetchData};

12.5 数据处理 (data_processing.py)

读取、处理和存储用户行为数据。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Project: taobao-user-behavior-analysis
Description: This script/module is part of the Taobao user behavior analysis project.
Author: DaPiCaoMin
Date: 2024/6/25
"""
import logging

import pandas as pd
from sqlalchemy import create_engine

# 配置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

# 读取数据
try:
    data = pd.read_csv('data/user_data.csv', header=None,
                       names=['UserID', 'ItemID', 'CategoryID', 'BehaviorType', 'Timestamp'])
    logging.info("数据读取成功。")
except Exception as e:
    logging.error(f"数据读取失败: {e}")
    raise

# 数据清洗
data.dropna(inplace=True)
data.drop_duplicates(inplace=True)

# 转换时间戳
data['Timestamp'] = pd.to_datetime(data['Timestamp'], unit='s', errors='coerce')
data.dropna(subset=['Timestamp'], inplace=True)

# 查看最早和最晚的五个时间戳
logging.info("最早的5个时间戳:\n%s", data['Timestamp'].sort_values().head(5))
logging.info("最晚的5个时间戳:\n%s", data['Timestamp'].sort_values().tail(5))

# 处理异常时间戳,设定合理的时间范围
start_date = pd.Timestamp('2017-07-03')
data = data[data['Timestamp'] >= start_date]

# 数据转换
data['Date'] = data['Timestamp'].dt.date
data['Hour'] = data['Timestamp'].dt.hour
data['Weekday'] = data['Timestamp'].dt.weekday

# 处理无效的用户ID和商品ID
data = data[(data['UserID'] > 0) & (data['ItemID'] > 0)]

# 独热编码处理行为类型
behavior_types = ['pv', 'buy', 'cart', 'fav']
for behavior in behavior_types:
    data[f'Behavior_{behavior}'] = (data['BehaviorType'] == behavior).astype(int)

# 确认列名
logging.info("列名:\n%s", data.columns)

# 特征工程
user_behavior = data.groupby('UserID').agg({
    'Behavior_buy': 'sum',
    'Behavior_fav': 'sum',
    'Behavior_cart': 'sum',
    'Behavior_pv': 'sum'
}).reset_index()

user_behavior.columns = ['UserID', 'BuyCount', 'FavCount', 'CartCount', 'PvCount']

# 添加购买行为比例
user_behavior['BuyRatio'] = user_behavior['BuyCount'] / user_behavior['PvCount']
user_behavior['BuyRatio'].fillna(0, inplace=True)

# 将特征合并回原数据
data = pd.merge(data, user_behavior, on='UserID', how='left')

# 确保所有列的类型可以被SQLAlchemy支持
data = data.convert_dtypes()

# 替换无穷大值和无穷小值
data.replace([float('inf'), float('-inf')], None, inplace=True)

# 数据库连接信息
username = 'root'
password = 'root'
host = 'localhost'
port = 3306

# 创建数据库连接
try:
    engine = create_engine(f'mysql+pymysql://{username}:{password}@{host}:{port}/mysql')
    with engine.connect() as conn:
        conn.execute("CREATE DATABASE IF NOT EXISTS user_data")
    logging.info("数据库创建成功。")
except Exception as e:
    logging.error(f"数据库创建失败: {e}")
    raise

# 连接到新创建的数据库
engine = create_engine(f'mysql+pymysql://{username}:{password}@{host}:{port}/user_data')

# 将数据写入数据库
try:
    with engine.begin() as connection:
        data.to_sql('user_behavior', con=connection, if_exists='replace', index=False)
    logging.info("数据清洗完成,并保存到数据库 user_data 中的表 user_behavior。")
except Exception as e:
    logging.error(f"数据写入失败: {e}")
    raise

12.6 Flask 应用 (app.py)

提供 API 接口,并从数据库读取数据。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Project: taobao-user-behavior-analysis
Description: This script/module is part of the Taobao user behavior analysis project.
Author: DaPiCaoMin
Date: 2024/6/25
"""
import os
import time

import pandas as pd
from flask import Flask, jsonify, render_template, request
from flask_caching import Cache
from flask_cors import CORS
from sqlalchemy import create_engine

# 创建 Flask 应用
app = Flask(__name__)
CORS(app)  # 允许跨域请求

# 配置缓存
cache = Cache(app, config={'CACHE_TYPE': 'simple'})

# 从环境变量中获取数据库连接信息,默认值为 'root'
username = os.getenv('DB_USERNAME', 'root')
password = os.getenv('DB_PASSWORD', 'root')
host = os.getenv('DB_HOST', 'localhost')
port = os.getenv('DB_PORT', 3306)

# 创建数据库引擎
engine = create_engine(f'mysql+pymysql://{username}:{password}@{host}:{port}/user_data')


# 预处理数据并存储到 CSV 文件
def preprocess_and_save_to_csv():
    try:
        # 连接到数据库并读取 'user_behavior' 表的数据
        with engine.connect() as conn:
            data = pd.read_sql('SELECT * FROM user_behavior', conn)

        # 按日期分组并汇总各类行为的数据
        daily_data = data.groupby('Date').agg({
            'Behavior_pv': 'sum',
            'Behavior_buy': 'sum',
            'Behavior_cart': 'sum',
            'Behavior_fav': 'sum'
        }).reset_index()
        daily_data.to_csv('data/daily_behavior.csv', index=False)

        # 按小时分组并汇总浏览和购买行为的数据
        hourly_data = data.groupby('Hour').agg({
            'Behavior_pv': 'sum',
            'Behavior_buy': 'sum'
        }).reset_index()
        hourly_data.to_csv('data/hourly_behavior.csv', index=False)

        # 按商品 ID 分组并汇总点击行为的数据,取点击数前 10 的商品
        top_click_items = data.groupby('ItemID')['Behavior_pv'].sum().reset_index().sort_values(by='Behavior_pv',
                                                                                                ascending=False).head(
            10)
        top_click_items.to_csv('data/top_click_items.csv', index=False)

        # 按商品 ID 分组并汇总购买行为的数据,取购买数前 10 的商品
        top_purchase_items = data.groupby('ItemID')['Behavior_buy'].sum().reset_index().sort_values(by='Behavior_buy',
                                                                                                    ascending=False).head(
            10)
        top_purchase_items.to_csv('data/top_purchase_items.csv', index=False)

        # 汇总数据概览信息
        summary_data = {
            'user_count': data['UserID'].nunique(),
            'item_count': data['ItemID'].nunique(),
            'category_count': data['CategoryID'].nunique(),
            'click_count': data['Behavior_pv'].sum(),
            'purchase_count': data['Behavior_buy'].sum(),
            'cart_count': data['Behavior_cart'].sum(),
            'fav_count': data['Behavior_fav'].sum()
        }
        pd.DataFrame([summary_data]).to_csv('data/summary_data.csv', index=False)

    except Exception as e:
        print(f"Error processing data: {e}")


@app.route('/')
def index():
    # 从 CSV 文件中读取数据概要
    summary_data = pd.read_csv('data/summary_data.csv').iloc[0].to_dict()
    return render_template('index.html', data_summary=summary_data)


@cache.cached(timeout=3600, key_prefix='chart_data')
@app.route('/api/chartData', methods=['GET'])
def chart_data():
    # 获取分页参数,默认值分别为第 1 页,每页 1000 条数据
    page = int(request.args.get('page', 1))
    page_size = int(request.args.get('page_size', 1000))
    offset = (page - 1) * page_size

    # 从 CSV 文件中读取数据
    daily_data = pd.read_csv('data/daily_behavior.csv')
    hourly_data = pd.read_csv('data/hourly_behavior.csv')
    top_click_items = pd.read_csv('data/top_click_items.csv')
    top_purchase_items = pd.read_csv('data/top_purchase_items.csv')

    time.sleep(2)  # 模拟数据处理延迟

    # 分页处理数据
    daily_page = daily_data[offset:offset + page_size]
    hourly_page = hourly_data[offset:offset + page_size]

    # 准备按日期分组的图表数据
    daily_chart_data = {
        'xAxisData': daily_page['Date'].astype(str).tolist(),
        'clickData': daily_page['Behavior_pv'].tolist(),
        'purchaseData': daily_page['Behavior_buy'].tolist(),
        'cartData': daily_page['Behavior_cart'].tolist(),
        'favData': daily_page['Behavior_fav'].tolist()
    }

    # 准备按小时分组的图表数据
    hourly_chart_data = {
        'xAxisData': hourly_page['Hour'].tolist(),
        'clickData': hourly_page['Behavior_pv'].tolist(),
        'purchaseData': hourly_page['Behavior_buy'].tolist()
    }

    # 准备点击前 10 商品的图表数据
    click_chart_data = {
        'xAxisData': top_click_items['ItemID'].tolist(),
        'seriesData': top_click_items['Behavior_pv'].tolist()
    }

    # 准备购买前 10 商品的图表数据
    purchase_chart_data = {
        'xAxisData': top_purchase_items['ItemID'].tolist(),
        'seriesData': top_purchase_items['Behavior_buy'].tolist()
    }

    # 汇总所有图表数据
    result = {
        'dailyChartData': daily_chart_data,
        'hourlyChartData': hourly_chart_data,
        'clickChartData': click_chart_data,
        'purchaseChartData': purchase_chart_data
    }

    # 返回 JSON 格式的响应
    return jsonify(result)


# 启动 Flask 应用
if __name__ == '__main__':
    # 仅在数据文件不存在时进行数据预处理
    if not os.path.exists('data/daily_behavior.csv'):
        preprocess_and_save_to_csv()
    app.run(debug=True)

13. 总结

本项目通过对淘宝用户行为数据的分析与可视化,实现了用户行为模式的展示。项目使用了 Flask 作为后端框架,提供数据接口;使用 ECharts 进行前端数据可视化。通过数据处理和可视化,项目能够帮助用户了解淘宝用户的行为趋势和偏好,为进一步的分析和业务决策提供了数据支持。项目的实现过程涵盖了数据读取、清洗、特征工程、数据库存储、后端 API 设计以及前端图表渲染等多个环节,展示了一个从数据处理到可视化的完整流程。

;