# douyin-analytics **Repository Path**: ghostroot/douyin-analytics ## Basic Information - **Project Name**: douyin-analytics - **Description**: - 项目性质 :FastAPI数据可视化项目 - 数据规模 :173万条抖音用户行为数据 - 技术栈 :前端ECharts+jQuery,后端FastAPI+Pandas - 功能规模 :26个图表,20+API接口,4个分析页面 - 部署特性 :一键部署脚本 - 适用场景 :数据分析可视化学习 - **Primary Language**: Python - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-04-23 - **Last Updated**: 2026-04-24 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 抖音数据分析大屏 —— 零基础到完整项目教程 > **设计原则:零跳跃学习** > > 传统路线的问题:知识点A → 知识点C(发现需要B,回头补B)→ 知识点D(发现需要前置知识X) > > 本路线的目标:知识点A → 知识点B(A的自然延伸)→ 知识点C(B的必然发展)→ 知识点D(C的合理进阶) --- ![echarts](./img/1.png) ![echarts](./img/2.png) ![echarts](./img/3.png) ![echarts](./img/4.png) ## 目录 - [第1章 从问题出发——为什么需要数据分析大屏](#第1章-从问题出发为什么需要数据分析大屏) - [第2章 认识数据——理解我们的原材料](#第2章-认识数据理解我们的原材料) - [第3章 系统架构——从全局看项目](#第3章-系统架构从全局看项目) - [第4章 技术选型——为什么是这些工具](#第4章-技术选型为什么是这些工具) - [第5章 后端基础——搭建数据服务](#第5章-后端基础搭建数据服务) - [第6章 数据分析实战——从数据到洞察](#第6章-数据分析实战从数据到洞察) - [第7章 前端基础——HTML与CSS](#第7章-前端基础html与css) - [第8章 CSS组件化设计](#第8章-css组件化设计) - [第9章 JavaScript组件化架构](#第9章-javascript组件化架构) - [第10章 图表组件开发](#第10章-图表组件开发) - [第11章 页面组装与集成](#第11章-页面组装与集成) - [第12章 部署与运维](#第12章-部署与运维) --- # 第1章 从问题出发——为什么需要数据分析大屏 ## 1.1 什么是数据分析 想象你开了一家奶茶店。每天有几百个顾客来买奶茶,你脑子里会有很多问题: - 今天卖了多少杯? - 哪种口味最受欢迎? - 什么时间段客人最多? - 哪些客人经常来? 如果你只有一个小本子记录每天的订单,翻半天也找不到答案。**数据分析就是把杂乱的数据变成有用的信息的过程**。 在互联网时代,抖音每天产生海量的用户行为数据——谁看了什么视频、看了多久、有没有点赞、在哪个城市……这些数据如果只是一行行存在文件里,就像那本厚厚的订单本,毫无意义。只有通过**分析**和**可视化**,数据才能告诉我们:用户喜欢什么、什么时候最活跃、哪些内容质量高。 ## 1.2 为什么需要可视化 看两组信息,哪个更容易理解? **纯数字版本:** ``` 北京用户数:285,432 上海用户数:243,567 广州用户数:198,234 深圳用户数:176,890 杭州用户数:145,678 ``` **柱状图版本:** 一个简单的柱状图,北京最高,依次递减,一眼就能看出差距。 人类大脑处理图形的速度比处理文字快6万倍。**数据可视化就是把数字变成图形,让人类的大脑能"一眼看懂"**。 ## 1.3 什么是数据大屏 你可能在公司大堂、指挥中心、商场见过那种超大屏幕,上面有各种图表、数字、地图在实时跳动——这就是**数据大屏**。 数据大屏的特点: - **全屏展示**:占满整个屏幕,没有滚动条 - **多图表同时呈现**:一屏展示多个维度的数据 - **暗色主题**:深色背景减少视觉疲劳,适合长时间观看 - **实时或准实时更新**:数据变化时图表自动刷新 ## 1.4 本项目要解决什么问题 本项目是一个**抖音数据分析大屏**,要回答以下核心问题: | 问题 | 对应的分析维度 | |------|---------------| | 平台整体情况如何? | 概览仪表盘(用户数、视频数、点赞数、完播率) | | 用户有什么行为特征? | 用户分析(活跃用户、完播行为、时间模式) | | 内容质量怎么样? | 内容质量(音乐热度、作者影响力、时长分布) | | 用户和内容在空间时间上如何分布? | 时空分布(城市地图、时间趋势、工作日vs周末) | 这四个问题,对应大屏的四个页面。每个页面包含多个图表,从不同角度回答同一个大问题。 ```mermaid graph TD A[抖音数据] --> B[概览仪表盘] A --> C[用户分析] A --> D[内容质量] A --> E[时空分布] B --> B1[KPI卡片] B --> B2[每日点赞趋势] B --> B3[热门音乐排行] B --> B4[作者活跃度] B --> B5[渠道分布] B --> B6[城市分布] B --> B7[小时趋势] B --> B8[一周模式] C --> C1[活跃用户排名] C --> C2[完播行为] C --> C3[城市热力] C --> C4[时间模式] C --> C5[渠道偏好] C --> C6[行为雷达] D --> D1[音乐词云] D --> D2[音乐参与度] D --> D3[作者影响力] D --> D4[时长分布] D --> D5[渠道质量] D --> D6[点赞分布] D --> D7[完播质量] E --> E1[城市地图] E --> E2[城市-音乐桑基图] E --> E3[分钟级趋势] E --> E4[工作日vs周末] E --> E5[时间-城市热力] ``` --- # 第2章 认识数据——理解我们的原材料 ## 2.1 数据从哪里来 本项目的数据来自一个CSV文件:`data/douyin_dataset.csv`,包含约**173万条**用户行为记录。 CSV(Comma-Separated Values,逗号分隔值)是一种最简单的数据存储格式,就像一张Excel表格,每行一条记录,每列一个字段,列与列之间用逗号隔开。 ## 2.2 CSV文件格式解析 打开CSV文件,你会看到类似这样的内容: ```csv uid,user_city,item_id,author_id,item_city,channel,finish,like,music_id,duration_time,real_time,H,date 12345,110000,67890,54321,310000,1,1,0,88888,15,12.3,20,2024-01-15 67890,440100,11111,22222,110000,2,0,1,99999,8,7.5,14,2024-01-15 ``` 第一行是**表头**(字段名),后面的每一行是一条**记录**(一次用户行为)。 ## 2.3 逐字段理解数据集 数据集有14个字段,我们逐个理解: | 字段名 | 含义 | 数据类型 | 示例值 | 解读 | |--------|------|---------|--------|------| | `uid` | 用户ID | 整数 | 12345 | 唯一标识一个用户 | | `user_city` | 用户所在城市编码 | 整数 | 110000 | 110000=北京,440100=广州 | | `item_id` | 视频ID | 整数 | 67890 | 唯一标识一个视频 | | `author_id` | 视频作者ID | 整数 | 54321 | 唯一标识一个作者 | | `item_city` | 视频发布城市编码 | 整数 | 310000 | 视频作者所在城市 | | `channel` | 渠道编号 | 整数 | 1 | 视频所属的内容渠道 | | `finish` | 是否完播 | 0或1 | 1 | 1=看完整个视频,0=没看完 | | `like` | 是否点赞 | 0或1 | 0 | 1=点了赞,0=没点赞 | | `music_id` | 背景音乐ID | 整数 | 88888 | 视频使用的背景音乐 | | `duration_time` | 视频时长(秒) | 整数 | 15 | 视频总时长15秒 | | `real_time` | 实际观看时长(秒) | 浮点数 | 12.3 | 用户实际看了12.3秒 | | `H` | 观看小时 | 整数 | 20 | 晚上8点观看 | | `date` | 观看日期 | 字符串 | 2024-01-15 | 2024年1月15日 | ### 关键理解:每条记录代表什么? 每条记录代表**一个用户观看一个视频的行为**。它回答了: - **谁**看了?(uid) - **谁**发布的?(author_id) - 看了**什么**?(item_id) - 在**哪里**看的?(user_city) - 视频来自**哪里**?(item_city) - 通过什么**渠道**看到的?(channel) - **看完**了吗?(finish) - **点赞**了吗?(like) - 什么**音乐**?(music_id) - 视频多**长**?(duration_time) - 实际看了多**久**?(real_time) - 什么**时间**看的?(H、date) ## 2.4 数据能告诉我们什么 有了这些字段,我们可以回答很多问题: ```mermaid graph LR subgraph 原始字段 A1[uid] A2[user_city] A3[item_id] A4[author_id] A5[channel] A6[finish] A7[like] A8[music_id] A9[duration_time] A10[H] A11[date] end subgraph 可回答的问题 B1[有多少独立用户?] B2[哪些城市用户最多?] B3[哪些作者最活跃?] B4[哪个渠道最受欢迎?] B5[完播率是多少?] B6[总点赞数是多少?] B7[哪首音乐最火?] B8[视频时长分布?] B9[几点最活跃?] B10[每日趋势?] end A1 --> B1 A2 --> B2 A4 --> B3 A5 --> B4 A6 --> B5 A7 --> B6 A8 --> B7 A9 --> B8 A10 --> B9 A11 --> B10 ``` **关键洞察**:数据分析的本质就是**对原始字段进行聚合运算**——计数(count)、求和(sum)、平均(mean)、分组(groupby)。 --- # 第3章 系统架构——从全局看项目 ## 3.1 什么是前后端分离 在理解项目架构之前,先理解一个核心概念:**前后端分离**。 想象一家餐厅: - **后厨**(后端):负责处理食材,做出菜品。顾客看不到后厨的运作。 - **前厅**(前端):负责展示菜品,让顾客看到和选择。顾客只和前厅打交道。 - **服务员**(API接口):前厅告诉服务员要什么菜,服务员去后厨取,再端给前厅。 对应到我们的项目: - **后端**:Python程序,读取CSV数据,计算分析结果 - **前端**:网页,展示图表和数字 - **API接口**:后端提供的URL地址,前端通过它获取数据 ```mermaid sequenceDiagram participant 浏览器 as 前端(浏览器) participant API as 后端API(FastAPI) participant CSV as 数据文件(CSV) 浏览器->>API: 1. 请求 /api/overview API->>CSV: 2. 读取 douyin_dataset.csv CSV-->>API: 3. 返回原始数据 API->>API: 4. Pandas聚合计算 API-->>浏览器: 5. 返回JSON结果 浏览器->>浏览器: 6. ECharts渲染图表 ``` ## 3.2 本项目的架构设计 本项目采用经典的**三层架构**: ```mermaid graph TB subgraph 展示层["展示层(前端)"] direction TB HTML[HTML 页面结构] CSS[CSS 样式与布局] JS[JavaScript 交互逻辑] ECharts[ECharts 图表渲染] end subgraph 接口层["接口层(API)"] direction TB FastAPI[FastAPI 路由] CORS[CORS 跨域配置] Response[统一响应包装] end subgraph 数据层["数据层(后端)"] direction TB Pandas[Pandas 数据处理] CSV[CSV 文件存储] end 展示层 -->|AJAX请求| 接口层 接口层 -->|读取数据| 数据层 数据层 -->|JSON响应| 接口层 接口层 -->|JSON数据| 展示层 ``` ### 架构的关键决策 | 决策 | 选择 | 原因 | |------|------|------| | 数据存储 | CSV文件 | 无需数据库,简单直接 | | 数据处理 | 每次请求实时读取 | 数据不变,无需缓存 | | 前后端通信 | RESTful API + JSON | 标准化,前端易消费 | | 页面模式 | SPA单页应用 | 切换流畅,无页面刷新 | ## 3.3 数据流转全过程 从用户打开网页到看到图表,数据经历了完整的流转: ```mermaid flowchart TD A[用户打开浏览器] --> B[访问 http://localhost:8080] B --> C[FastAPI重定向到 /static/index.html] C --> D[浏览器加载HTML] D --> E[加载CSS样式文件] D --> F[加载JavaScript文件] E --> G[页面渲染完成] F --> G G --> H[PageNavigation初始化] H --> I[激活概览页面] I --> J[创建图表组件实例] J --> K[各组件发起AJAX请求] K --> L[FastAPI处理请求] L --> M[Pandas读取CSV] M --> N[Pandas聚合计算] N --> O[返回JSON数据] O --> P[前端接收数据] P --> Q[ECharts渲染图表] Q --> R[用户看到完整大屏] style A fill:#4d96ff,color:#fff style R fill:#6bcb77,color:#fff style K fill:#ffd93d,color:#333 style O fill:#ffd93d,color:#333 ``` ## 3.4 目录结构详解 ``` douyin-analytics/ ├── src/ # 后端代码目录 │ ├── __init__.py # Python包标识文件(空文件) │ └── main.py # 后端唯一入口:所有API路由 ├── data/ # 数据目录 │ └── douyin_dataset.csv # 数据源文件(173万条记录) ├── static/ # 前端静态资源目录 │ ├── index.html # SPA唯一入口HTML │ ├── css/ # 样式文件 │ │ ├── base.css # CSS变量、全局重置、动画 │ │ ├── layout.css # Grid/Flexbox布局 │ │ ├── components/ # 组件样式 │ │ │ ├── nav.css # 导航栏组件 │ │ │ └── loading.css # 加载动画组件 │ │ └── pages/ # 页面样式 │ │ ├── overview.css # 概览页 │ │ ├── user-analysis.css │ │ ├── content-quality.css │ │ └── spatiotemporal.css │ ├── js/ # JavaScript文件 │ │ ├── jquery.min.js # jQuery 4.0.0 │ │ ├── echarts.min.js # ECharts 6 │ │ ├── lib/ # 公共工具库 │ │ │ ├── page-navigation.js # 页面导航管理器 │ │ │ ├── loading-manager.js # 加载动画管理器 │ │ │ ├── resize-manager.js # 尺寸管理器 │ │ │ └── echarts-config.js # ECharts主题配置 │ │ ├── overview/ # 概览页图表 │ │ ├── user-analysis/ # 用户分析页图表 │ │ ├── content-quality/ # 内容质量页图表 │ │ └── spatiotemporal/ # 时空分布页图表 │ └── geo/ # 地图数据 │ └── china_full.json # 中国地图GeoJSON ├── douyin-analytics.service # systemd服务文件 ├── requirements.txt # Python依赖清单 ├── install.sh # 安装脚本 └── manage.sh # 管理脚本 ``` **目录设计原则**: - **按功能分层**:CSS分为base/layout/components/pages四层 - **按页面分模块**:JS图表按页面分目录,每个图表一个文件 - **公共代码抽取**:lib/存放所有页面共享的工具类 --- # 第4章 技术选型——为什么是这些工具 ## 4.1 Python与FastAPI **为什么用Python?** - 数据分析领域最流行的语言 - Pandas库让数据处理变得简单 - 语法简洁,适合初学者 **为什么用FastAPI而不是Flask/Django?** - 性能极高(基于异步ASGI) - 自动生成API文档 - 代码量少,一个文件就能写完所有接口 - 原生支持异步,适合数据密集型应用 ## 4.2 Pandas数据处理 Pandas是Python的数据分析库,核心概念是**DataFrame**——你可以把它想象成一张Excel表格: ```python # 读取CSV → 得到DataFrame(就像打开Excel) df = pd.read_csv('douyin_dataset.csv') # DataFrame的操作就像Excel的公式: df['uid'].nunique() # = COUNTA(去重uid列) df['like'].sum() # = SUM(like列) df.groupby('user_city').size() # = 数据透视表按城市计数 ``` ## 4.3 HTML/CSS/JavaScript 这是前端的三板斧: | 技术 | 作用 | 类比 | |------|------|------| | HTML | 页面结构 | 房子的骨架(墙、门、窗) | | CSS | 页面样式 | 房子的装修(颜色、大小、位置) | | JavaScript | 页面交互 | 房子的电器(开关、遥控) | ## 4.4 ECharts可视化 ECharts是百度开源的图表库,提供几十种图表类型。选择它的原因: - 中文文档完善 - 图表类型丰富(折线图、柱状图、饼图、地图、雷达图……) - 主题可定制 - 性能优秀,能处理大数据量 ## 4.5 jQuery与AJAX **为什么用jQuery?** - 简化DOM操作和AJAX请求 - 代码更简洁 - 本项目DOM操作不多,jQuery足够 **什么是AJAX?** AJAX(Asynchronous JavaScript And XML)让网页**不刷新就能从服务器获取数据**。就像你在餐厅坐着,服务员把菜端到你桌上,你不需要站起来去厨房。 ```mermaid sequenceDiagram participant 页面 as 网页 participant 服务器 as 后端服务器 页面->>页面: 用户点击"用户分析" 页面->>服务器: AJAX请求 /api/active_users Note over 页面: 页面不刷新,等待数据 服务器-->>页面: 返回JSON数据 页面->>页面: 用数据渲染图表 ``` --- # 第5章 后端基础——搭建数据服务 ## 5.1 FastAPI入门 FastAPI应用的最小骨架: ```python from fastapi import FastAPI app = FastAPI() @app.get("/") async def home(): return {"message": "Hello World"} ``` 就这么简单!`@app.get("/")`定义了一个路由——当用户访问根路径时,执行`home()`函数,返回JSON数据。 ## 5.2 第一个API:健康检查 健康检查是最简单的API,用于确认服务是否在运行: ```python @app.get("/health") async def health(): return {"message": "ok"} ``` **执行流程**: 1. 浏览器请求 `GET /health` 2. FastAPI匹配到这个路由 3. 执行`health()`函数 4. 返回 `{"message": "ok"}` ## 5.3 数据读取函数 所有API都需要读取CSV数据,所以抽取一个公共函数: ```python from pathlib import Path import pandas as pd def get_data(): src_dir = Path(__file__).parent data_file = src_dir.parent / "data" / "douyin_dataset.csv" df = pd.read_csv(data_file) return df ``` **逐行解析**: - `Path(__file__).parent`:获取当前文件(main.py)所在的目录(src/) - `src_dir.parent`:获取src的父目录(项目根目录) - `/ "data" / "douyin_dataset.csv"`:拼接路径到数据文件 - `pd.read_csv()`:Pandas读取CSV文件,返回DataFrame ## 5.4 统一响应格式 为了让前端处理更简单,所有API返回统一格式: ```python def success_response(data=None): return {"code": 200, "data": data} ``` 前端只需要检查: ```javascript if (response.code === 200 && response.data) { // 使用 response.data } ``` ## 5.5 CORS跨域配置 **什么是跨域问题?** 浏览器有一个安全策略:网页只能请求同源(同协议、同域名、同端口)的API。如果前端在`localhost:8080`,API也在`localhost:8080`,就没有跨域问题。但如果前端和后端在不同端口,就需要CORS配置。 ```python app.add_middleware( CORSMiddleware, allow_origins=["*"], # 允许所有来源 allow_credentials=True, # 允许携带凭证 allow_methods=["*"], # 允许所有HTTP方法 allow_headers=["*"], # 允许所有HTTP头部 ) ``` ## 5.6 静态文件服务 前端文件(HTML、CSS、JS)需要通过HTTP访问,FastAPI提供了静态文件挂载: ```python from starlette.staticfiles import StaticFiles app.mount("/static", StaticFiles(directory=BASE_DIR / "static"), name="static") ``` 这样,`static/index.html`就可以通过`/static/index.html`访问。 ## 5.7 首页重定向 用户访问根路径`/`时,自动跳转到静态首页: ```python from starlette.responses import RedirectResponse @app.get("/") async def home(): return RedirectResponse(url="/static/index.html") ``` ## 5.8 后端完整骨架 把以上所有部分组合起来,就是后端的完整骨架: ```mermaid graph TD A[FastAPI应用实例] --> B[CORS中间件] A --> C[静态文件挂载] A --> D[根路由重定向] A --> E[健康检查API] A --> F[数据读取函数] A --> G[统一响应函数] A --> H[业务API接口群] H --> H1["/api/overview"] H --> H2["/api/likes_by_date"] H --> H3["/api/user_city_distribution"] H --> H4["...更多接口"] F --> I["douyin_dataset.csv"] ``` --- # 第6章 数据分析实战——从数据到洞察 本章是整个教程的核心——我们从一个具体的分析问题出发,一步步写出后端API。 ## 6.1 Pandas基础操作 在写API之前,先掌握5个最常用的Pandas操作: | 操作 | 代码 | 含义 | 类比Excel | |------|------|------|-----------| | 读取 | `pd.read_csv()` | 读取CSV文件 | 打开文件 | | 计数 | `len(df)` | 总行数 | 看行号 | | 去重计数 | `df['uid'].nunique()` | 不重复的值有多少个 | 删除重复后计数 | | 求和 | `df['like'].sum()` | 某列求和 | SUM函数 | | 均值 | `df['finish'].mean()` | 某列平均值 | AVERAGE函数 | | 分组计数 | `df.groupby('city').size()` | 按某列分组后计数 | 数据透视表 | | 分组聚合 | `df.groupby('city').agg({...})` | 按某列分组后多种运算 | 数据透视表+公式 | ## 6.2 最简单的分析:24小时发布分布 **问题**:一天24个小时,每个小时有多少条视频被观看? **思路**:按`H`字段(0-23)分组,统计每组的记录数。 ```python @app.get("/api/hourly_distribution") async def hourly_distribution(): df = get_data() # 按小时分组计数 hourly = df.groupby('H').size() # 确保所有小时都有值(缺失的补0) hours = list(range(24)) counts = [int(hourly.get(h, 0)) for h in hours] return success_response({"hours": hours, "counts": counts}) ``` **执行流程**: ```mermaid flowchart LR A[原始数据] --> B["groupby('H')"] B --> C[".size()"] C --> D[补全0-23小时] D --> E[返回JSON] style A fill:#ff6b6b,color:#fff style E fill:#6bcb77,color:#fff ``` **为什么需要补0?** 如果凌晨3点没有人看视频,`groupby`结果中就没有3这个键。但前端需要24个数据点来画图,所以必须补0。 ## 6.3 聚合分析:概览KPI **问题**:平台的核心指标是什么? **思路**:4个KPI分别用不同的Pandas操作。 ```python @app.get("/api/overview") async def overview(): df = get_data() total_items = len(df) # 总记录数 total_users = df['uid'].nunique() # 去重用户数 total_likes = df['like'].sum() # 点赞总和 finish_rate = df['finish'].mean() * 100 # 完播率均值×100 return { "code": 200, "data": { "total_items": int(total_items), "total_videos": int(total_items), "total_users": int(total_users), "total_likes": int(total_likes), "finish_rate": round(finish_rate, 2) } } ``` **每个KPI的Pandas操作对应**: ```mermaid graph LR subgraph DataFrame DF[173万行数据] end DF -->|len| KPI1[总记录数: 1730000] DF -->|nunique| KPI2[独立用户数: 523891] DF -->|sum| KPI3[总点赞数: 842156] DF -->|mean × 100| KPI4[完播率: 45.67%] ``` ## 6.4 分组分析:城市分布 **问题**:哪些城市的用户最多? **思路**:按`user_city`分组,统计每组的记录数,取Top10。 ```python @app.get("/api/user_city_distribution") async def user_city_distribution(): df = get_data() # 按城市分组 → 计数 → 降序排列 → 取前10 city_data = df.groupby('user_city').size().sort_values(ascending=False).head(10) cities = [] counts = [] for city, count in city_data.items(): cities.append(str(city)) counts.append(int(count)) return success_response({"cities": cities, "counts": counts}) ``` **Pandas操作链**: ```mermaid flowchart LR A[DataFrame] --> B["groupby('user_city')"] B --> C[".size()"] C --> D[".sort_values(ascending=False)"] D --> E[".head(10)"] E --> F[Top10城市数据] style A fill:#ff6b6b,color:#fff style F fill:#6bcb77,color:#fff ``` ## 6.5 时间序列分析:每日点赞趋势 **问题**:每天的点赞数如何变化? **思路**:将`date`列转为日期类型,按日期分组,对`like`列求和。 ```python @app.get("/api/likes_by_date") async def likes_by_date(): df = get_data() # 关键:指定format参数加速解析! df['date'] = pd.to_datetime(df['date'], format='%Y-%m-%d') likes_by_date = df.groupby('date')['like'].sum().sort_index() dates = [] likes = [] for date, like in likes_by_date.items(): dates.append(date.strftime("%Y-%m-%d")) likes.append(int(like)) return success_response({"dates": dates, "likes": likes}) ``` **性能注意**:`pd.to_datetime`不指定`format`时,Pandas会逐行猜测日期格式,173万行数据会非常慢。指定`format='%Y-%m-%d'`后,解析速度提升10-100倍。 ## 6.6 关联分析:城市-音乐关联 **问题**:不同城市的用户喜欢什么音乐? **思路**:按城市和音乐组合分组,统计每组的记录数,取每个城市的Top5音乐。 ```python @app.get("/api/city_music_association") async def city_music_association(): df = get_data() # 取用户最多的Top10城市 top_cities = df.groupby('user_city').size().nlargest(10).index.tolist() # 筛选Top10城市的数据 df_top = df[df['user_city'].isin(top_cities)] # 按城市和音乐分组计数 grouped = df_top.groupby(['user_city', 'music_id']).size().reset_index(name='count') # 每个城市取Top5音乐 associations = [] music_ids_set = set() for city in top_cities: city_data = grouped[grouped['user_city'] == city].nlargest(5, 'count') for _, row in city_data.iterrows(): associations.append({ "city": str(row['user_city']), "music_id": str(row['music_id']), "count": int(row['count']) }) music_ids_set.add(str(row['music_id'])) return success_response({ "cities": [str(c) for c in top_cities], "music_ids": list(music_ids_set), "associations": associations }) ``` **分析流程**: ```mermaid flowchart TD A[173万条数据] --> B[取Top10城市] B --> C[筛选Top10城市数据] C --> D["groupby(city, music_id)"] D --> E[每个城市取Top5音乐] E --> F[构建关联数据] style A fill:#ff6b6b,color:#fff style F fill:#6bcb77,color:#fff ``` ## 6.7 深度分析:渠道质量评估 **问题**:不同渠道的内容质量如何? **思路**:按渠道分组,同时计算多个指标——视频数、平均点赞、完播率、平均时长。 ```python @app.get("/api/channel_quality_analysis") async def channel_quality_analysis(): df = get_data() # 使用agg一次性计算多个指标 channel_stats = df.groupby('channel').agg({ 'item_id': 'count', # 视频数 'like': 'mean', # 平均点赞 'finish': 'mean', # 完播率 'duration_time': 'mean' # 平均时长 }).reset_index() channels = channel_stats['channel'].tolist() counts = channel_stats['item_id'].tolist() avg_likes = channel_stats['like'].tolist() finish_rates = channel_stats['finish'].tolist() avg_durations = channel_stats['duration_time'].tolist() return success_response({ "channels": channels, "counts": [int(c) for c in counts], "avg_likes": [round(float(l), 4) for l in avg_likes], "finish_rates": [round(float(f), 4) for f in finish_rates], "avg_durations": [round(float(d), 2) for d in avg_durations] }) ``` **agg的威力**:一次分组,多个指标,避免重复遍历数据。 ## 6.8 所有API接口总览 | 接口 | Pandas核心操作 | 返回数据 | |------|---------------|---------| | `/api/overview` | len, nunique, sum, mean | 4个KPI数值 | | `/api/likes_by_date` | groupby+sum | 日期-点赞数对 | | `/api/user_city_distribution` | groupby+size+head | Top10城市 | | `/api/item_city_distribution` | groupby+size+head | Top10视频城市 | | `/api/channel_distribution` | groupby+size | 渠道占比 | | `/api/duration_distribution` | pd.cut+groupby | 时长区间分布 | | `/api/hourly_distribution` | groupby+size | 24小时分布 | | `/api/music_popularity` | groupby+size+head | Top20音乐 | | `/api/music_engagement` | groupby+agg | 音乐使用+点赞 | | `/api/author_productivity` | groupby+nunique+head | Top20作者 | | `/api/author_influence` | groupby+agg | 作者多维指标 | | `/api/minute_trend` | pd.cut+groupby | 144个时段 | | `/api/weekday_pattern` | groupby+size | 周一至周日 | | `/api/active_users` | groupby+size+head | Top50用户 | | `/api/user_completion_behavior` | groupby+agg | 用户完播行为 | | `/api/city_music_association` | groupby+nlargest | 城市-音乐关联 | | `/api/channel_quality_analysis` | groupby+agg | 渠道多维质量 | | `/api/city_analysis_deep` | groupby+agg | 城市深度分析 | | `/api/channel_quality_deep` | groupby+agg | 渠道深度分析 | --- # 第7章 前端基础——HTML与CSS ## 7.1 HTML文档结构 HTML是网页的骨架。我们的`index.html`结构如下: ```html 抖音数据分析大屏
...
...
``` **关键理解**: - ``:不显示在页面上,定义元信息和引入资源 - ``:页面可见内容 - `
`:每个页面是一个section,通过CSS控制显示/隐藏 - ` ``` **注册流程**: ```mermaid sequenceDiagram participant HTML as index.html participant Nav as PageNavigation participant Page as 页面模块 HTML->>Nav: PageNavigation.getInstance() HTML->>Nav: register('overview-section', {onActivate, onDeactivate}) HTML->>Nav: register('user-analysis-section', {...}) HTML->>Nav: register('content-quality-section', {...}) HTML->>Nav: register('spatiotemporal-section', {...}) HTML->>Nav: navigateTo('overview-section', false) Nav->>Page: OverviewPage.init() Note over Page: 创建8个图表,发起8个AJAX请求 ``` ## 11.6 导航栏点击事件绑定 导航栏的HTML结构: ```html ``` **data-page属性**:每个导航项通过`data-page`属性关联到对应的页面section ID。 点击事件绑定: ```javascript // 在PageNavigation构造函数中绑定 document.querySelectorAll('.nav-bar-item').forEach(item => { item.addEventListener('click', () => { const pageId = item.dataset.page; // 读取data-page属性 this.navigateTo(pageId); }); }); ``` **导航切换完整流程**: ```mermaid flowchart TD A[用户点击导航项] --> B[读取data-page属性] B --> C["PageNavigation.navigateTo(pageId)"] C --> D["当前页面.onDeactivate()"] D --> E["销毁当前页所有图表"] E --> F["移除所有页面active类"] F --> G["目标页面添加active类"] G --> H["目标页面.onActivate()"] H --> I["创建目标页所有图表"] I --> J["各图表并行发起AJAX"] J --> K["数据返回后渲染图表"] K --> L["延迟50ms触发ResizeManager.refreshAll()"] style A fill:#4d96ff,color:#fff style K fill:#6bcb77,color:#fff ``` ## 11.7 JS文件加载顺序 在`index.html`中,所有JS文件按依赖顺序加载: ```html ``` **加载顺序的依赖关系**: ```mermaid flowchart TD subgraph 第1层["第1层:基础库"] jQuery[jquery.min.js] ECharts[echarts.min.js] end subgraph 第2层["第2层:工具库"] EC[echarts-config.js] LM[loading-manager.js] RM[resize-manager.js] PN[page-navigation.js] end subgraph 第3层["第3层:图表组件"] CK[chart-kpi.js] CT[chart-time-trend.js] CM[chart-music-popularity.js] OM[overview/main.js] UA[...user-analysis/...] CQ[...content-quality/...] ST[...spatiotemporal/...] end subgraph 第4层["第4层:初始化"] INIT["$(document).ready()"] end jQuery --> EC jQuery --> LM ECharts --> EC ECharts --> RM LM --> PN EC --> CK EC --> CT CK --> OM CT --> OM OM --> INIT style jQuery fill:#ff6b6b,color:#fff style ECharts fill:#ff6b6b,color:#fff style INIT fill:#6bcb77,color:#fff ``` ## 11.8 完整集成流程 从用户打开浏览器到看到完整大屏的全过程: ```mermaid sequenceDiagram participant User as 用户 participant Browser as 浏览器 participant Server as FastAPI服务器 participant CSV as CSV文件 User->>Browser: 输入 http://localhost:8080 Browser->>Server: GET / Server-->>Browser: 302重定向到 /static/index.html Browser->>Server: GET /static/index.html Server-->>Browser: HTML文件 par 并行加载资源 Browser->>Server: GET /static/css/base.css Browser->>Server: GET /static/css/layout.css Browser->>Server: GET /static/js/jquery.min.js Browser->>Server: GET /static/js/echarts.min.js end Server-->>Browser: CSS和JS文件 Note over Browser: 按顺序执行JS文件 Browser->>Browser: PageNavigation初始化 Browser->>Browser: 注册4个页面 Browser->>Browser: navigateTo('overview-section') Browser->>Browser: OverviewPage.init() par 8个AJAX请求并行 Browser->>Server: GET /api/overview Browser->>Server: GET /api/likes_by_date Browser->>Server: GET /api/music_popularity Browser->>Server: GET /api/author_productivity Browser->>Server: GET /api/channel_distribution Browser->>Server: GET /api/user_city_distribution Browser->>Server: GET /api/hourly_distribution Browser->>Server: GET /api/weekday_pattern end Server->>CSV: 每个请求读取CSV CSV-->>Server: DataFrame Server->>Server: Pandas聚合计算 Server-->>Browser: 返回8组JSON数据 Browser->>Browser: ECharts渲染8个图表 Browser-->>User: 完整大屏展示 ``` --- # 第12章 部署与运维 ## 12.1 从开发到生产 开发环境和生产环境的区别: | 对比项 | 开发环境 | 生产环境 | |--------|---------|---------| | 运行方式 | 手动uvicorn命令 | systemd服务自动管理 | | 进程管理 | 前台运行,关闭终端即停止 | 后台守护,崩溃自动重启 | | 日志 | 控制台输出 | journalctl系统日志 | | 端口 | 8080 | 8080 | | 工作目录 | 项目源码目录 | /opt/douyin-analytics | ## 12.2 安装脚本详解 `install.sh`是一键部署脚本,需要root权限执行: ```bash #!/bin/bash # 1. 创建安装目录 mkdir -p /opt/douyin-analytics # 2. 复制项目文件 cp -r src/ /opt/douyin-analytics/ cp -r data/ /opt/douyin-analytics/ cp -r static/ /opt/douyin-analytics/ # 3. 创建Python虚拟环境 cd /opt/douyin-analytics python3 -m venv .venv # 4. 安装依赖 source .venv/bin/activate pip install -r requirements.txt # 5. 设置文件权限 chown -R www-data:www-data /opt/douyin-analytics # 6. 安装systemd服务 cp douyin-analytics.service /etc/systemd/system/ systemctl daemon-reload systemctl enable douyin-analytics systemctl start douyin-analytics ``` **安装流程**: ```mermaid flowchart TD A[执行 install.sh] --> B[创建 /opt/douyin-analytics] B --> C[复制项目文件] C --> D[创建Python虚拟环境] D --> E[pip安装依赖] E --> F[设置www-data权限] F --> G[安装systemd服务] G --> H[启用并启动服务] H --> I[部署完成] style A fill:#4d96ff,color:#fff style I fill:#6bcb77,color:#fff ``` ## 12.3 systemd服务配置 `douyin-analytics.service`定义了服务运行参数: ```ini [Unit] Description=抖音数据分析大屏 After=network.target [Service] Type=simple User=www-data Group=www-data WorkingDirectory=/opt/douyin-analytics ExecStart=/opt/douyin-analytics/.venv/bin/uvicorn src.main:app --host 0.0.0.0 --port 8080 --workers 4 Restart=always RestartSec=5 [Install] WantedBy=multi-user.target ``` **关键配置解析**: | 配置项 | 值 | 含义 | |--------|-----|------| | `User` | www-data | 以低权限用户运行,安全 | | `WorkingDirectory` | /opt/douyin-analytics | 工作目录,影响相对路径 | | `ExecStart` | uvicorn命令 | 启动命令,使用虚拟环境中的uvicorn | | `--workers 4` | 4个工作进程 | 充分利用多核CPU | | `Restart=always` | 总是重启 | 进程崩溃后自动重启 | | `RestartSec=5` | 5秒后重启 | 避免频繁重启 | ## 12.4 服务管理 ### 使用systemctl ```bash # 启动服务 sudo systemctl start douyin-analytics # 停止服务 sudo systemctl stop douyin-analytics # 重启服务 sudo systemctl restart douyin-analytics # 查看状态 sudo systemctl status douyin-analytics # 查看日志 sudo journalctl -u douyin-analytics -f ``` ### 使用manage.sh ```bash sudo ./manage.sh start # 启动 sudo ./manage.sh stop # 停止 sudo ./manage.sh restart # 重启 sudo ./manage.sh status # 状态 sudo ./manage.sh log # 查看日志 sudo ./manage.sh install # 安装/更新Python依赖 ``` ## 12.5 验证部署 部署完成后,通过以下步骤验证: ```mermaid flowchart TD A[部署完成] --> B[访问 http://host:8080/health] B --> C{返回 ok?} C -->|否| D[检查服务状态
systemctl status] D --> E[检查日志
journalctl -u] E --> F[修复问题后重启] C -->|是| G[访问 http://host:8080] G --> H{页面正常?} H -->|否| I[检查浏览器Console错误] I --> J[检查Network面板API请求] H -->|是| K[切换4个页面] K --> L{所有图表正常?} L -->|否| M[检查对应API返回数据] L -->|是| N[✅ 部署成功] style N fill:#6bcb77,color:#fff ``` ## 12.6 常见问题排查 | 问题 | 可能原因 | 排查方法 | |------|---------|---------| | 页面无法访问 | 服务未启动 | `systemctl status douyin-analytics` | | API返回500 | CSV文件路径错误 | 检查`/opt/douyin-analytics/data/`下是否有CSV | | 图表不显示 | JS加载失败 | 浏览器Console查看错误 | | 图表显示空白 | API数据格式错误 | Network面板查看API响应 | | 页面切换卡顿 | 图表未销毁 | Console查看是否有内存泄漏警告 | | 窗口缩放图表不适应 | ResizeManager未注册 | 检查图表组件是否调用了`register()` | ## 12.7 性能优化建议 1. **数据缓存**:CSV每次请求都重新读取,可使用`functools.lru_cache`缓存DataFrame 2. **日期解析优化**:`pd.to_datetime`始终指定`format`参数 3. **前端资源压缩**:生产环境可压缩CSS和JS文件 4. **CDN加速**:jQuery和ECharts可从CDN加载 5. **Nginx反向代理**:生产环境建议在uvicorn前加Nginx,处理静态文件和负载均衡 --- # 附录A:全项目知识图谱 ```mermaid graph TB subgraph 后端 Python[Python语言] --> FastAPI[FastAPI框架] FastAPI --> Route[路由装饰器] FastAPI --> CORS[CORS中间件] FastAPI --> Static[静态文件挂载] Python --> Pandas[Pandas库] Pandas --> ReadCSV[read_csv] Pandas --> GroupBy[groupby] Pandas --> Agg[agg聚合] Pandas --> Cut[pd.cut分桶] end subgraph 前端基础 HTML[HTML结构] --> Section[section页面] HTML --> Div[div容器] CSS[CSS样式] --> Variables[CSS变量] CSS --> Grid[CSS Grid] CSS --> Flex[Flexbox] CSS --> Animation[动画] JS[JavaScript] --> Class[类语法] JS --> Ajax[AJAX请求] JS --> Singleton[单例模式] end subgraph 前端组件 Class --> Chart[图表组件类] Chart --> Constructor[constructor] Chart --> LoadData[_loadData] Chart --> Render[_renderChart] Chart --> Destroy[destroy] Singleton --> PageNav[PageNavigation] Singleton --> ResizeMgr[ResizeManager] Loading[LoadingManager] --> Show[show] Loading --> Hide[hide] EChartsConfig[echarts-config] --> InitChart[initChart] EChartsConfig --> BarOption[getBarChartOption] EChartsConfig --> LineOption[getLineChartOption] EChartsConfig --> PieOption[getPieChartOption] end subgraph 图表类型 BarOption --> BarChart[柱状图] LineOption --> LineChart[折线图] PieOption --> PieChart[饼图] EChartsConfig --> RadarOption[getRadarChartOption] RadarOption --> RadarChart[雷达图] end subgraph 部署 Linux[Linux系统] --> Systemd[systemd服务] Systemd --> Service[.service文件] Bash[Bash脚本] --> Install[install.sh] Bash --> Manage[manage.sh] end Route --> Ajax GroupBy --> LoadData Agg --> LoadData Grid --> Section Variables --> CSS Constructor --> Loading LoadData --> Ajax Render --> InitChart Destroy --> ResizeMgr style Python fill:#ff6b6b,color:#fff style CSS fill:#ffd93d,color:#333 style JS fill:#4d96ff,color:#fff style EChartsConfig fill:#00d4ff,color:#000 ``` # 附录B:零跳跃学习路线图 ```mermaid flowchart LR A["第1章
为什么需要
数据分析大屏"] --> B["第2章
认识数据
理解原材料"] B --> C["第3章
系统架构
从全局看项目"] C --> D["第4章
技术选型
为什么是这些工具"] D --> E["第5章
后端基础
搭建数据服务"] E --> F["第6章
数据分析实战
从数据到洞察"] F --> G["第7章
前端基础
HTML与CSS"] G --> H["第8章
CSS组件化
设计"] H --> I["第9章
JS组件化
架构"] I --> J["第10章
图表组件
开发"] J --> K["第11章
页面组装
与集成"] K --> L["第12章
部署与
运维"] style A fill:#ff6b6b,color:#fff style L fill:#6bcb77,color:#fff ``` **每章的前置知识依赖**: | 章节 | 前置知识 | 来自哪章 | |------|---------|---------| | 第1章 | 无 | — | | 第2章 | 知道什么是数据分析 | 第1章 | | 第3章 | 理解数据字段 | 第2章 | | 第4章 | 理解前后端分离 | 第3章 | | 第5章 | 知道FastAPI是什么 | 第4章 | | 第6章 | 会用FastAPI写路由 | 第5章 | | 第7章 | 理解前后端如何通信 | 第3章 | | 第8章 | 会写基础CSS | 第7章 | | 第9章 | 理解CSS组件化 | 第8章 | | 第10章 | 理解JS组件化架构 | 第9章 | | 第11章 | 会开发图表组件 | 第10章 | | 第12章 | 完成整个项目 | 第11章 | --- > 🎉 **恭喜你完成了整个教程!** 从"什么是数据分析"到"部署一个完整的数据大屏",你走过了零跳跃的学习路线,每一步都是上一步的自然延伸。