跳转到内容

【限时特惠】小白玩转Python数据分析_哔哩哔哩_bilibili

00:00 00:00:59

  • 质量不错的数据集可以收藏起来
    • 后续找分析技巧后再探索
  • 科学配套资料已整理很多开源数据集供后续练手
  • 爬虫是一种常见获取数据的方法

    1. 发送请求获取网页源代码
    2. 解析网页源代码,提取想要的内容
    3. 这些数据作为后续分析的原料
  • 爬虫玩得好,牢饭吃不早”:便捷低成本的数据获取手段
  • 红线不能踩
    • 不要爬取公民隐私数据
    • 不要爬取侵权版权保护内容
    • 不要爬取国家事务、国防建设、尖端科学技术领域的计算机系统
  • 本课聚焦数据分析,不深入讲解爬虫技术
  • 在课程后面的更多获取数据章节,可以学习增加数据获取的方法
  • 最后要了解的:通过公开API获取数据
  • API全称 Application Programming Interface应用程序编程接口
    • 中文中直接叫API
  • API定义了两个程序之间的服务合约
    • 即双方如何使用请求和响应来进行通讯
[客户端 发送请求] <--> [服务端 发送响应]
  • 当爬虫发送请求后(如请求**/home**页面的内容)
    • 服务端发送响应
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=adge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Home</title>
</head>
<body>
...
</body>
</html>
  • 爬虫方式:发送请求后获取**/home页面的网页源代码**
    • 示例响应:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Home</title>
</head>
<body>
...
</body>
</html>
- **网页源代码**用于**浏览器渲染**,非直接信息
- 需额外**解析源代码**提取特定数据
  • API方式:直接请求**/data** API端口
    • 示例响应:
[
{
"title": "A Light in the Attic",
"price": "91.77"
},
{
"title": "Tipping the Velvet",
"price": "53.74"
},
{
"title": "Soumission",
"price": "50.30"
}
]
- 按对方**服务合约**发送请求,即可**直接获得想要的数据**
- **无需解析源代码**
  • 当网站提供公开API时,通过API获取数据比爬虫更高效
    • 直接获得结构化数据(如JSON),无需解析源代码
  • 如果没有API,再用其他方法
    • 课程后面更多获取数据章节会更新如何通过API获取数据,掌握多重技能
  • 成功获取数据后,一般存储为某种格式
    • 以供后续读取或修改
  • 数据集文件以供后续读取或修改
    • 即使网上下载的数据集,也可能有不同格式供选择,如CSVJSON
  • 常见数据格式进行讲解
  • 获得想分析的数据文件后,下一步是用代码读取它
    • 不同文件格式有不同的读取方法
    • 读取前了解文件格式很重要
  • 常见文件格式示例:TXTMP3PDFJPEG
    • 通过文件后缀来辨别格式
  • 通过文件后缀分辨文件格式
    • TXT格式一般保存为**.txt**结尾的文件
  • 文件名后缀只是文件名的一部分
    • 更改后缀不影响实际文件格式
      • 示例:将TXT文件改名为data.mp3
      • 操作系统根据后缀匹配打开程序,用音乐播放器打开导致无法正常打开
  • 文件后缀影响:电脑选择用什么软件打开它
    • 不改变文件内容,因此实际格式不变
data.txt (TXT图标)
改名
data.mp3 (音乐图标) → 用播放器打开失败
  • 不仅看文件后缀,更重要是文件内容遵循的格式规则
    • JSON是程序员非常喜欢的数据格式
      • 全称 JavaScript Object NotationJavaScript对象表示法
      • JavaScript语法有些关联,但无需了解JavaScript
      • Python字典 {}列表 [] 非常类似
      • 被无数主流编程语言支持
  • 用途:用来存储或交换信息
    • API获取数据时,很多时候以JSON格式反馈
[
{
"title": "A Light in the Attic",
"price": "$51.77"
},
{
"title": "Tipping the Velvet",
"price": "$53.74"
},
{
"title": "Soumission",
"price": "$50.30"
}
]
+-----------------+ OK +-----------------+
| [Screen with |------------->| [Server Rack] |
| List of Data] | +-----------------+|
+-----------------+ |
^ |
| |
+----------------------------------+
↓ JSON Data
  • API响应(如**/data**端口)很多时候以JSON格式反馈
    • 原因:占用体积小,且容易转换成程序语言结构(如Python)
  • JSON两种数据结构对象数组
    • 对象{}):
{
"id": "1",
"type": "article",
"title": "working with JSON data",
"created": "2099-12-18T14:56:29.000Z"
}
- 以**大括号**开头结尾,内含**键值对**(键:值,用逗号分隔)
- **对应Python字典**
- **数组**(**[]**):
[
{
"title": "A Light in the Attic",
"price": "51.77"
},
{
"title": "Tipping the Velvet",
"price": "53.74"
}
]
- 以**中括号**开头结尾,内含**一个个值**(用逗号分隔)
- **对应Python列表**
  • JSON与Python字典区别:Python字典可用整数等不可变类型作键,但JSON只能用字符串作键
    • 字符串必须用双引号包围,单引号不行
{
"id": "1",
"type": "article",
"title": "working with JSON data",
"created": "2099-12-18T14:56:29.000Z"
}
  • 规则适用于嵌套结构:对象或数组内嵌套的对象,其键也必须遵循相同规则
  • JSON支持的值类型: - 字符串:用双引号包围,如 "star" - 数字:整数或浮点数,如 3151.77 - 布尔值truefalse(注意与Python布尔值区别) - 数组:如 ["hi", 7] - 对象:如 {"age": 25} - 空值null
  • JSON支持6种基本数据类型
    • 字符串:用双引号包围,如 "star"
    • 数字:整数或小数,如 3151.77
    • 布尔值:小写 truefalse
      • JavaScript本质导致小写,与Python True/False不同
    • 数组[] 内可嵌套,支持复杂结构,如 ["hi", 7]
    • 对象{} 内键值对,值可为对象,支持嵌套,如 {"age": 25}
    • 空值null,相当于Python None,表示值不存在
  • 示例:
{
"title": "A Light in the Attic",
"price": [51.77, 52],
"sale": false,
"attributes": { "discount": null }
}
  • 辨识有效JSON格式
    • 真JSON遵循严格语法:双引号字符串、小写布尔、正确嵌套
    • 假JSON示例(无效):缺少引号或重复键
{
"性别": "",
"性别": "",
"性别": "",
"性别": "",
"性别": ""
}
  • 第一个错误示例
{
"性别": "",
"性别": "",
"性别": "",
"性别": "",
"性别": ""
}
- **不是真JSON**:**对象的键不能重复**
- 因为要靠**键去提取值**,重复键不知道提取哪个
  • 第二个错误示例
{
"学号": "001",
"性别": "",
"班级": 5,
"身高": 1.66,
"已毕业": False
}
- **不是真JSON**:
- **对象的键**(如学号、性别)**必须是双引号包围的字符串**
- 无双引号包围不行
- **布尔值**必须**小写开头**(如**false**)
- 与**Python布尔值**(**True/False**)不同
  • 与Python布尔值区别:JSON用true/false(小写),不同于Python的True/False
{
"学号": "001",
"性别": "",
"班级": 5,
"身高": 1.66,
"已毕业": False
}
  • ```json

    [null, null, null]

- **中括号**开头结尾**数组**
- 内部**三个null**空值),符合JSON支持类型
- ```json
{
"键": 1,
"键": [true, 3]
}
3.14, [null, null, true]
- 检查内部各值:**第一个值为对象**
  • 真JSON示例验证过程:
    • 对象键值对:键均为双引号包围的字符串,无问题
    • 值检查:
      • 第一个值数字(支持)
      • 第二个值数组(内含布尔值 false整数 3,均为支持类型)
    • 外层数组其他元素:
      • 浮点数 3.14(支持)
      • 数组(内含两个字符串,支持)
      • 布尔值(支持)
  • 整个复杂嵌套结构符合JSON规则(实际分析数据不会如此混乱)

JSON与Python数据差异及解析必要性

Section titled “JSON与Python数据差异及解析必要性”
  • 对JSON已有基本了解,但拿到JSON文件不能直接用Python分析
    • 原因:JSON与Python字典/列表存在细微区别
      • JSON布尔值 true/false(小写)直接放进Python会报错
      • Python布尔值为大写 True/False
{
"questionType": "yes/no",
"asin": "1466736038",
"answerTime": "Mar 8, 2014",
"unixTime": 1394265600,
"question": "Is there a SIM card in it?",
"answerType": "Y",
"answer": "Yes. The Galaxy SIII accommodates a micro SIM card."
}
  • 解决方案:对JSON进行解析+转换 → Python数据
    • Pandas库帮助下特别简单
    • 具体操作下节详解
  • 用Python读取JSON:通过Pandas库,操作非常丝滑
    • import pandas as pd
    • 使用 pd.read_json() 函数读取JSON文件
      • 参数传入文件路径,如 ./cell_phones_survey.json
import pandas as pd
pd.read_json("./cell_phones_survey.json")
  • 文件路径:配套文件中有 cell_phones_survey.json
    • 不熟悉路径可复习Python入门章节
  • 使用pd.read_json(“./cell_phones_survey.json”)直接将JSON文件转为DataFrame
    • 自动完成文件读取JSON解析转成DataFrame的全流程
    • 无Pandas时,每个步骤需单独代码
survey_df = pd.read_json("./cell_phones_survey.json")
  • DataFrame对应关系
    • 原始JSON为数组,数组中每个对象DataFrame的一行
    • 合理性:DataFrame每行表示一个数据实例,JSON对象即数据实例

示例输出(前几行):

questionType asin answerTime unixTime question answerType answer
0 yes/no 1466736038 Mar 8, 2014 1394265600 Is there a SIM card in it? Y Yes. The Galaxy SIII accommodates a micro SIM card.
1 yes/no 1466736038 Jan 29, 2015 1422518400 Is this phone new… Y It is new but I was not able to get it active…

原始JSON示例:

[
{
"questionType": "yes/no",
"asin": "1466736038",
"answerTime": "Mar 8, 2014",
"unixTime": 1394265600,
"question": "Is there a SIM card in it?",
"answerType": "Y",
"answer": "Yes. The Galaxy SIII accommodates a micro SIM card."
},
// 更多对象...
]
  • JSON数组中的每个对象对应 DataFrame一行数据
    • 示例:每项调查结果作为一个数组成员对象
  • 对象内的键值对对应 DataFrame列名数据
    • DataFrame列表示数据实例的各个属性
    • JSON对象键值对作用相同
{
"questionType": "yes/no",
"asin": "1466736038",
"answerTime": "Mar 8, 2014",
"unixTime": 1394265600,
"question": "Is there a SIM card in it?",
"answerType": "Y",
"answer": "Yes. The Galaxy SIII accommodates a micro SIM card."
}
questionType asin answerTime unixTime question answerType answer
yes/no 1466736038 Mar 8, 2014 1394265600 Is there a SIM card…? Y Yes. The Galaxy SIII…
yes/no 1466736038 Jan 29, 2015 1422518400 Is this phone new…? It is new but…
NaN
  • answerType列部分值为 NaN(缺失)
    • 原始JSON中有些对象缺少answerType键
    • JSON不要求所有对象键相同
    • Pandas自动处理:用 NaN 表示数据空缺
  • JSON对象结构(最外层为键值对)示例:github.json
{
"name": "text-rewriter",
"stars": 11,
"forks": 4,
"watchers": 3,
"isFork": false,
"languages": [
{"name": "JavaScript", "size": 21769},
{"name": "HTML", "size": 2096},
{"name": "CSS", "size": 2081}
],
"description": "Webextension to rewrite phrases in pages",
"createdAT": "2015-03-14T22:35:11Z",
"pushedAt": "2022-02-11T14:26:08Z",
"License": null
}
  • 使用 pd.read_json("./github.json") 读取为 DataFrame
github_df = pd.read_json("./github.json")
  • 输出示例(3行DataFrame):
owner stars forks watchers isFork languages description createdAT pushedAt License
0 text-rewriter 11 4 3 False JavaScript Webextension to rewrite… 2015-03-14T22:35:11Z 2022-02-11T14:26:08Z None
1 text-rewriter 11 4 3 False HTML Webextension to rewrite… 2015-03-14T22:35:11Z 2022-02-11T14:26:08Z None
2 text-rewriter 11 4 3 False CSS Webextension to rewrite… 2015-03-14T22:35:11Z 2022-02-11T14:26:08Z None
  • 解析规则
    • 每个键名 → DataFrame列名
    • 非数组值(如 stars: 11)在所有行重复填充
    • 数组值(如 languages,长度3)→ Pandas将其元素视为不同数据实例,展开为多行
      • 每个 languages 对象占一行,name 提取为该列值
  • languages属性示例:值为JSON数组,包含多个对象(如JavaScript、HTML、CSS)
    • Pandas自动展开为多行
    • 每行对应一个语言对象
{
"name": "JavaScript",
"size": 21769
},
{
"name": "HTML",
"size": 2096
},
{
"name": "CSS",
"size": 2081
}
  • 展开结果:DataFrame中languages列下三行,其他列(如owner、name、stars等)值被复制三份
    • 原因:表格要求每列行数相同
    • 得到结构规整的表格
owner name stars languages description
pelmers text-rewriter 11 {“name”: “JavaScript”, “size”: 21769} Webextension to rewrite…
pelmers text-rewriter 11 {“name”: “HTML”, “size”: 2096} Webextension to rewrite…
pelmers text-rewriter 11 {“name”: “CSS”, “size”: 2081} Webextension to rewrite…
  • 已学会JSON转DataFrame:下载任意JSON文件后,可读取到Python供分析
  • 悬念:JSON虽程序员喜爱,但数据分析师更喜欢其他格式(下节分解)

Pandas读取JSON的局限与CSV格式介绍

Section titled “Pandas读取JSON的局限与CSV格式介绍”
  • 大JSON文件用Excel打开可能导致卡顿甚至崩溃。
  • JSON对象易转为Python字典,JSON数组易转为Python列表。
  • 在Pandas帮助下,JSON可直接转换为DataFrame,方便后续分析。
  • 数据分析师最喜欢的数据格式是CSV
  • CSV也是纯文本格式
    • 无字符、颜色等花哨特征
    • 内容结构本质上是一张表格
      • 值之间用英文逗号分隔
  • 全称Comma-Separated Values逗号分隔值
  • 第一行大部分情况下是表头(展示列名)

示例CSV内容:

CustomerID,Gender,Age,Annual Income,Spending Score (1-100),Profession,Work Experience,Family Size
1,Male,19,35000,39,Healthcare,1,4
2,Male,21,35000,80,Engineer,1,3
...
  • Excel等表格软件打开,即可见逗号对齐的表格效果
CustomerID Gender Age Annual Income Spending Score (1-100) Profession Work Experience Family Size
1 Male 19 35000 39 Healthcare 1 4
2 Male 21 35000 80 Engineer 1 3
  • CSVComma-Separated Values,逗号分隔值)
    • 首行通常包含属性名(列名),如 CustomerIDGenderAgeAnnual Income ($)Spending Score (1-100)ProfessionWork ExperienceFamily Size
      • 逗号分隔
    • 也可无表头,直接从数据行开始
  • 每行数据独占一行
    • CSV行直接对应 DataFrame行
    • 每行包含相同数量的值
      • 逗号分隔符数量必须一致
      • 行多或少逗号 → 非合格CSV

示例 personal_data.csv(前几行):

CustomerID,Gender,Age,Annual Income ($),Spending Score (1-100),Profession,Work Experience,Family Size
1,Male,19,395000,39,Healthcare,1,3
2,Male,21,350000,81,Engineer,3,3
3,Female,20,86000,6,Engineer,1,1
4,Female,23,59000,77,Lawyer,8,2
5,Female,31,38000,40,Entertainment,2,6
  • 疑问:如果数据值本身包含英文逗号,如何处理?(待解)
  • CSV(Comma-Separated Values,逗号分隔值)是数据分析师最喜欢的数据格式
    • 原因:非常规整的二维结构,一眼看出对应DataFrame表格的样子
  • CSV规则
    • 第一行通常为列名(表头),后续每行一个数据实例
    • 字段间用逗号分隔
      • 示例:CustomerID,Gender,Age,Annual Income ($,),Spending Score (1-100),Profession,Work Experience,Family Size
    • 含逗号的值用双引号包围(完整包围,避免逗号被当作分隔符)
      • 示例:'Healthcare,Social Work'"Healthcare,Social Work"
    • 空值处理:两个逗号相邻(如,,
      • 表示中间字段值不存在

示例 personal_data.csv(前几行):

CustomerID,Gender,Age,Annual Income ($,),Spending Score (1-100),Profession,Work Experience,Family Size
1,Male,19,15000,39,"Healthcare,Social Work",1,4
2,Male,21,35000,81,Engineer,3,3
3,Female,20,86000,6,Engineer,1,1
  • CSV vs JSON
    • CSV:规整二维,直观对应表格
    • JSON:灵活嵌套,难直观看出表格结构
    • JSON适合通用编程CSV适合数据分析
  • CSV、JSON、TXT均为纯文本格式
    • 粗体、下划线、字号等格式特征
    • 只关注文本内容,有效减少体积读取干扰
  • Word、Excel格式不属于纯文本
  • 用代码分析数据时,优先选择源数据文件
    • 纯文本CSVJSON
    • 数据库SQLite
  • 仍可用Excel软件 读取、修改、导出 CSV
  • 多数操作系统将Excel设为CSV默认打开软件

示例CSV文件(students.csv):

姓名,考试1,考试2,考试3
陈,85,95,92
小陈,91,92,94
小李,91,92,94
小王,86,81,89
小张,79,89,95
小赵,96,91,91
小周,81,89,92
  • 实际数据分析常见1G+大CSV文件
    • Excel打开易卡顿崩溃:不仅展示数据,还加载格式、功能等
    • 体积对比:CSV已比Excel小,但数据量大仍需注意
  • 查看方式
    • 代码编辑器纯文本编辑器打开
    • 更好方法:用Pandas pd.read_csv() 读取转DataFrame
      • df.head() 等方法灵活查看几行
      • 避免全部加载海量数据

示例代码:

import pandas as pd
df = pd.read_csv("amazon.csv")
df.head()

输出示例(前3行):

product_id product_name category discounted_price actual_price discount_percent rating rating_count
0 B07JVH4H41 Wayona Nylon… Computers… 199 1,099 81% 4.2 24,269
1 B09NSFVPVG Ambrane… Computers… 199 349 43% 4.0 43,894
2 B09BMSWKCT Source Fast… Computers… 199 1,899 90% 3.9 7,928
  • 通过Pandas库读取CSV文件,转为DataFrame供后续分析
    • 操作丝滑,课程文件有 fifa_players.csv
      • 记录16万条国际足球球员信息
      • 文件体积91MB左右
  • Excel打开体验:加载很久
    • 暗示Pandas读取更高效
# 示例列:player_id, name, age, nationality 等

[CSV → Pandas → DataFrame]

CSV文件 (90.9 MB) ──► Pandas ──► DataFrame表格
fifa_players.csv │
│ 16万球员数据
  • 大CSV文件处理实际演示:16万条数据文件
    • Excel打开差点崩溃
    • 纯文本编辑器打开也要等老长时间
import pandas as pd
df = pd.read_csv("amazon.csv")
df.head()
  • 读取效果:几秒钟成功转为DataFrame
    • 每行一条数据实例
    • 各个属性值对齐
    • 示例前几行:product_id、product_name、category、discounted_price 等列
  • 代码处理数据比软件有速度优势
    • 示例:fifa_players.csv(91MB,16万球员数据)用Excel加载很慢
import pandas as pd
df = pd.read_csv("./fifa_players.csv")
df.head()
  • 默认行为:将CSV第一行作为列名(表头)
    • 示例输出列:player_id, player_url, fifa_version
    • 前几行数据:
player_id player_url fifa_version short_name long_name
0 15823 /player/158023/lionel-messi/150002 15 L. Messi Lionel Andrés Messi Cuccittini
1 2081 /player/20801/cristiano-ronaldo-dos-santos 15 Cristiano Cristiano Ronaldo dos Santos Aveiro
  • 无表头文件:设置 header=None
    • 第一行视为数据行,列名用0开始数字代替
df = pd.read_csv("./fifa_players.csv", header=None)
  • 注意:即使有表头,默认不直接用player_id作为列名,而是用位置数字(原因:不完全依赖位置)
  • 真实Netflix影视剧数据集进行上手实践,重点数据整理
    • 下载Jupyter Notebook(包含数据集介绍)及两个数据文件
      • 与之前项目不同,此次练习连接数据
    • 新建单独文件夹,将Notebook和数据文件放到一起保持结构整洁
  • 下一步:运行这个Jupyter Notebook(打开Notebook)
  • 整理不同流派影视作品(如喜剧片动作片科幻片
  • 计算不同演员出演作品的平均IMDB评分
    • IMDB:国外评分网站,类似豆瓣,用户对影视作品打分
  • 应用场景:准备拍喜剧片的导演可查看哪位演员的喜剧片平均评分最高
  • 记录2022年7月美国地区Netflix所有电视剧及电影数据
  • 两个数据表
    • titles.csv:影视作品信息
      • id:作品ID
      • title:标题
      • show_type:类型(电视剧、电影)
      • description:简短描述
      • release_year:发布年份
      • age_certification:年龄认证
    • credits.csv:超过7万名导演及演员信息
      • 名字、作品ID、人物名、演员类型(导演/演员)
  • 练习数据整理,得到下一步分析的数据
  • 数据集关于Netflix电影电视剧(截止2022年7月,覆盖2008-2022年全球Netflix作品)
    • 用于分析Netflix影视评分演员评分
  • 两个CSV文件
    • titles.csv:影视作品信息
      • id:影视作品ID
      • title:名称
      • show_type:类型(电影/电视剧)
      • description:简介
      • release_year:发布年份
      • age_certification:年龄认证
      • runtime:运行时长(电影单集或电视剧单集)
      • genres:类型
      • production_countries:制作国家/地区
      • seasons:季数
      • imdb_score:IMDB评分
      • imdb_votes:IMDB投票数
      • tmdb_popularity:TMDB流行度
      • tmdb_score:TMDB评分
    • credits.csv:演员信息
      • 演员姓名
      • 参演影视作品ID(与titles.csv的id对应
  • 始终以分析目标为导向
    • 决定缺失值处理:观察值缺某个变量时,保留还是删除该行
    • 决定数据连接方式左连接右连接内连接外连接还是全连接
  • 本项目核心变量
    • 作品所属流派
    • 演员参演作品的IMDB评分
  • titles.csv 完整列说明(补充)
    • id:作品ID
    • title:标题
    • type:类型
    • show_type:类型(电视剧、电影)
    • description:描述
    • release_year:发布年份
    • age_certification:年龄认证
    • runtime:运行时长
    • genres流派
    • production_countries:制作国家
    • seasons:季数
    • imdb_score:IMDB评分
    • imdb_votes:投票数
    • imdb_popularity:流行度
  • credits.csv 列说明
    • 演员名
    • 角色名
    • 作品ID
    • 人数
    • 性别
    • 演出类型(导演/演员)
  • 项目目标:评估Netflix电影评分数据集,发现问题,下一节针对性清理
    • 评估与清理可同步进行:发现问题立即清理,更省事
import pandas as pd
original_titles = pd.read_csv("titles.csv")
original_credits = pd.read_csv("credits.csv")
original_titles.head()
cleaned_titles = original_titles.copy()
cleaned_credits = original_credits.copy()
列名 含义
imdb_id IMDB ID
imdb_votes IMDB投票数
imdb_popularity IMDB热度
tmdb_score TMDB评分
  • 完成清理后得到cleaned_titlescleaned_credits两个DataFrame
  • 原数据中每个单元格包含多个流派值(如[‘drama’, ‘crime’]),易于阅读
  • 使用apply(lambda x: x.split())拆分为列表形式
cleaned_titles.genres = cleaned_titles.genres.apply(lambda x: x.split())
cleaned_titles.genres.value_counts()
- 示例输出:`['drama', 'crime'] 1`
id title type description release_year age_certification runtime genres production_countries seasons
tm86810 Taxi Driver MOVIE A mentally unstable… 1976-01-01 R 113 drama US 1.0
tm86810 Taxi Driver MOVIE A mentally unstable… 1976-01-01 R 114 crime US 1.0
tm56888 Deliverance MOVIE Intent on seeing the… 1972-01-01 R 109 drama US 1.0
tm56888 Deliverance MOVIE Intent on seeing the… 1972-01-01 R 109 action US 1.0
tm56888 Deliverance MOVIE Intent on seeing the… 1972-01-01 R 109 thriller US 1.0
person_id id name character role
3748 tm86810 Robert De Niro Travis Bickle ACTOR
7064 tm86810 Jodie Foster Iris Steensman ACTOR
17064 tm86810 Albert Brooks Tunn ACTOR
39849 tm86810 Harvey Keitel Matthew “Sport” Higgins ACTOR
44633 tm86810 Cybill Shepherd Betsy ACTOR
  • genres 已展开:每个流派(如drama、crime、action)单独成行
    • 示例:Taxi Driver 分为 drama 和 crime 两行
    • 目的:便于后续按 genres 值分组
  • 数据形状:16878 行 × 15 列
  • 与原始数据相似,已清理脏数据
  • 数据形状:77931 行 × 5 列
    • person_id:演员ID
    • id:作品ID
    • name:演员名
    • character:角色
    • role:角色类型(ACTOR/DIRECTOR)
  • 计算不同演员出演作品的平均 IMDB 得分,按流派分析高分演员
    • cleaned_titles:有作品信息(含 IMDB 分),缺演员信息
    • cleaned_credits:有演员信息及作品 ID,缺具体作品评分
    • 需连接两表:通过 id 匹配获取演员-作品-评分关联
  • 无法直接获知影视作品流派演员对应,需要将cleaned_titlescleaned_credits进行merge连接
  • 连接键:两个DataFrame均有id列,表示影视作品ID
  • 连接方式抉择(inner/outer/right/left):
    • id仅在cleaned_titles存在(无credits),无法获取演员信息
    • id仅在cleaned_credits存在(无titles),缺少作品信息
  • cleaned_creditscleaned_titles 都有 id 列,通过 id 列进行内连接 inner
    • 只保留两个DataFrame都有的 id,保证流派信息与演员信息同时存在
  • 使用 pd.merge() 函数连接两个DataFrame
    • 示例代码:
credits_with_titles = pd.merge(cleaned_credits, cleaned_titles, on="id", how="inner")
  • 连接后可得知每个演员参演过的影视作品具体信息
  • 使用left mergecredits_with_titles = pd.merge(credits, titles, on="id", how="left")
    • 包含演员信息(person_id, name, character, role)+ 作品信息(title, description, genres, imdb_score等)
credits_with_titles.head()
person_id id name character role title description release_year age_certification runtime genres production_countries seasons imdb_id imdb_score imdb_votes imdb_popularity
3745 tm86810 Robert De Niro Travis Bickle ACTOR Taxi Driver A mentally unstable… 1976-01-01 R 114 drama US NaN 00075214 8.2 808582.0 49.965
3745 tm86810 Robert De Niro Travis Bickle ACTOR Taxi Driver A mentally unstable… 1976-01-01 R 114 crime US NaN 00075214 8.2 808582.0 49.965
14558 tm86810 Jodie Foster Iris Elsernmore ACTOR Taxi Driver A mentally unstable… 1976-01-01 R 114 drama US NaN 00075214 8.2 808582.0 49.965
14558 tm86810 Jodie Foster Iris Elsernmore ACTOR Taxi Driver A mentally unstable… 1976-01-01 R 114 crime US NaN 00075214 8.2 808582.0 49.965
7064 tm86810 Albert Brooks Albert Brooks DIRECTOR Taxi Driver A mentally unstable… 1976-01-01 R 114 drama US NaN 00075214 8.2 808582.0 49.965
  • 问题:role包含ACTOR(演员)和DIRECTOR(导演)
  • 目标:仅分析演员作品IMDB评分,排除导演
  • 方法:使用query筛选role == 'ACTOR'
  • 使用query过滤role == ‘ACTOR’,创建actor_with_titles DataFrame
    • 排除导演数据,仅保留演员信息
    • 后续整理使用actor_with_titles,而非包含导演的credits_with_titles
  • 目标:挖掘各流派IMDB评分高的作品演员
    • genres(流派)和name(演员)分组
    • 示例:查看喜剧片中出演过喜剧片的演员
credits_with_titles.query("role == 'ACTOR'")
actor_with_titles = credits_with_titles.query("role == 'ACTOR'")
actor_with_titles = credits_with_titles.query('role == "ACTOR"')
  • 筛选后仅保留ACTOR行,排除DIRECTOR
  • 目标:计算参演喜剧片演员的所有喜剧作品平均IMDB评分
    • 对演员按流派分组统计平均imdb_score
  • 均可代表演员,但person_id更准确
    • name问题:易拼错重名
    • person_id优势:唯一标识,如身份证号不会重复
      • 精准定位具体演员,确保分组准确性
  • 执行分组操作:使用groupby(['genres', 'person_id'])
    • 顺序重要:先按genres(流派)分组,再按person_id(演员ID)
      • 原因:找出流派内评分高的演员
actor_with_titles.groupby(['genres', 'person_id'])
grouped_genres_and_persons = actor_with_titles.groupby(['genres', 'person_id'])
  • 运行groupby后得到按流派和演员ID分组的结果
  • actor_with_titlesgenresperson_id分组
    • 代码:
groupby_genres_and_person_id = actor_with_titles.groupby(['genres', 'person_id'])
  • 提取imdb_score列并调用mean(),计算各流派中每个演员出演作品的平均IMDB评分
    • 代码:
imdb_score_groupby_genres_and_person_id = groupby_genres_and_person_id["imdb_score"].mean()
  • Series类型(仅提取一列变量)
  • 层次化索引(MultiIndex):genres + person_id
  • 示例输出:
genres person_id
0 action 10007 0.866667
10008 7.000000
10013 7.300000
western 99735 6.500000
99873 7.300000
Name: imdb_score, Length: 168881, dtype: float64
  • 长度:168881条记录
  • 对**grouped_genres_and_personsimdb_score进行分组聚合**,计算平均值 - 外层索引:genres(流派) - 内层索引:person_id(演员ID)
  • 得到多层索引的Series
grouped_genres_and_persons['imdb_score'].mean()
imdb_score_grouped_genres_and_persons = imdb_score_grouped_genres_and_persons.reset_index()
  • 结果DataFrame结构:
genres person_id imdb_score
ACTION 1000 6.866667
ACTION 10007 7.000000
WESTERN 983735 6.500000
  • 含义:每行表示某演员(person_id)在特定流派的所有作品平均IMDB评分
    • 可直接查询任意演员在某流派的平均作品得分
  • 数据规模:168561 rows × 3 columns
  • 至此已整理出目标数据,可进入下一步分析
  • 目标:在已有结果基础上,继续整理数据,找出每个流派演员作品平均IMDB评分最高的值及其对应演员
    • 目的:直接查看各流派口碑最好(平均IMDB分最高)的演员名字
    • 提供更多数据整理练习机会
  • 步骤:先将Series转为DataFrame
df_imdb_score_groupby_genres_and_person_id_df = imdb_score_groupby_genres_and_person_id.reset_index()
- 结果:**3列DataFrame**(genres, person\_id, imdb\_score),**16981行**
  • 示例数据

    | genres | person_id | imdb_score |

    |———|———–|————|

    | action | 1000 | 6.866667 |

    | action | 100017 | 7.000000 |

    | western | 981735 | 6.000000 |

    | western | 981735 | 7.300000 |

  • 下一步:对该DataFrame再次分组,提取imdb_score最大值

    • 示例:找出动作片流派中最高平均评分的演员
  • 目标:找出各流派中平均IMDB评分最高的演员。
  • 代码:
genres_max_scores = imdb_score_groupby_genres_and_person_id_df.groupby("genres")["imdb_score"].max()
  • 结果:得到各流派的最高平均IMDB评分,但不知由哪位演员获得
  • genres_max_scores与原imdb_score_groupby_genres_and_person_id_df 合并,以确定最高评分演员
  • 使用inner内连接:确保只保留匹配的记录。
  • 代码:
genres_max_scores = genres_max_scores.reset_index()
genres_max_scores.columns = ["genres", "max_score"]
merged = pd.merge(imdb_score_groupby_genres_and_person_id_df, genres_max_scores, how="inner", left_on=["genres", "imdb_score"], right_on=["genres", "max_score"])
  • 先重置索引,再合并,通过genresimdb_score进行匹配。
  • genres_max_scores的列名改为genresmax_score,避免歧义。
  • 查看合并后的DataFrame,确认是否正确匹配。
  • 聚合函数特点:分组后调用max()等,会对所有选中列应用相同操作
    • 若同时选person_idimdb_scoreperson_id也会求最大值
    • person_id最大值无意义,仅需imdb_score最大值对应的演员ID
  • 先聚合imdb_score得到各流派最高分,再通过合并匹配回person_id
    • 确保找到最高IMDB评分确切对应的演员ID
# 错误方式:聚合会同时处理person_id
# df.groupby('genres')'person_id', 'imdb_score'.max() # person_id无意义
# 正确:仅聚合imdb_score,后续合并
df.groupby('genres')['imdb_score'].max()
  • 连接键genres + imdb_score
    • genres_max_scores仅含各流派最高平均分
    • imdb_score_groupby_genres_and_person_id_df所有平均分
    • 仅匹配最高分记录,找出对应演员
  • how省略:默认内连接,仅保留匹配项
pd.merge(imdb_score_groupby_genres_and_person_id_df, genres_max_scores,
left_on=['genres', 'imdb_score'], right_on='genres')

最高评分演员合并结果与名字获取

Section titled “最高评分演员合并结果与名字获取”
  • 连接后发现最高平均分对应的演员不一定只有一个
    • 示例:action流派中多个演员平均imdb_score = 9.3
genres person_id imdb_score
action 12790 9.3
action 21533 9.3
action 33830 9.3
action 8601 9.3
war 62647 8.9
western 23311 8.9
- **总行数**:136 rows × 3 columns
  • 使用person_idcleaned_credits连接,获取对应name
    • cleaned_credits包含person_idname
    • 先提取**[‘person_id’, ‘name’]**两列
    • 删除重复值:同一演员多部作品避免多次出现
    • 代码示例:
genres_max_score_with_person_id = pd.merge(
genres_max_score, person_id_df,
left_on='person_id', right_on='person_id',
how='left', suffixes=('_genre', '_person')
)
genres_max_score_with_person_id'person_id', 'name'
  • actor_with_titles提取person_idname
    • 删除重复行,确保唯一对应
  • 代码示例:
person_name_map = actor_with_titles'person_id', 'name'.drop_duplicates()
  • 使用person_id连接person_name_map与之前最高平均IMDB评分数据
    • 获取各流派最高评分演员姓名
  • 两个数据文件(credits和titles)支持完整匹配
  • 新建文件夹:将notebook及两个数据文件统一存放
    • 保持文件结构整洁
  • 打开notebook:运行前先阅读分析目标
    • 理解此次数据分析目的
  • 评估与清洗同步:发现问题立即清理,比上个项目分开步骤更省事
  • 数据分析无标准答案,可按自己想法发挥
  • 清理后DataFramecleaned_titlescleaned_credits
  • 计算不同演员出演作品的平均IMDB评分,挖掘各流派高评分演员(如喜剧片中高分演员)
  • cleaned_titles:含影视作品信息(流派、评分),无演员信息
  • cleaned_credits:含演员信息(person_id、name、role),有影视作品id无流派信息
  • 需要merge两个DataFrame获取完整信息
    • 共同键:id(影视作品ID)
    • 连接方式选择:inner/outer/right/left
cleaned_credits 示例:
person_id id name character role
0 2768 tt0084110 Robert De Niro Travis Bickle ACTOR
cleaned_titles 示例:
age_certification runtime genres ... imdb_score
0 R 114 drama ... 8.2
  • 使用内连接inner:保证流派信息演员信息同时存在
    • 只保留两个DataFrame都有的id
  • id仅在cleaned_titles存在(无credits),无法获取演员信息
  • id仅在cleaned_credits存在(无titles),缺少作品信息
  • 使用pd.merge()函数连接cleaned_creditscleaned_titles
    • 连接键:id
    • 连接方式:inner
  • 代码示例:
credits_with_titles = pd.merge(cleaned_credits, cleaned_titles, on="id", how="inner")
  • 连接后可得知每个演员参演过的影视作品具体信息

自从OpenAI发布ChatGPT后,每个人都有了一个24小时求助,知识面广的助手。

  • 发送编程问题前先设置背景、角色和要求,提高回答质量
    • 背景信息:声明“我是Python初学者”,避免AI假设已有高级知识
    • 角色植入:指定“担任我的Python编程老师”
    • 贯穿要求:“给出详细易懂回答,并配合简单例子讲解”
  • 示例提示:
我是一个Python初学者请你现在开始担任我的Python编程老师在这之后我会问你一系列Python相关问题请你给出详细且易懂的回答并且尽可能地配合简单的例子进行讲解你明白了的话请回复好的’。
  • 适用范围:ChatGPT、文心一言、通义千问等大厂AI助手
  • 目的:让AI对话围绕解决Python问题,提供针对性指导
  • 遇到代码报错:复制报错信息给AI询问原因及解决办法
    • 示例:使用math打印pi时报错
AttributeError: partially initialized module 'math' has no attribute 'pi' (most likely due to a circular import)
  • 常见原因循环导入,如文件名与内置模块冲突(math.py覆盖内置math模块)

    • Python不允许循环导入(模块A导入B,B再导入A)
  • 解决步骤

    1. 检查文件名是否与内置模块相同
  • AI优势:快速定位根源,给出解决办法及预防建议,节省调试时间

用法3: 找bug(输出不符合预期)

Section titled “用法3: 找bug(输出不符合预期)”
  • 代码无报错但结果不对:告诉AI预期目的预期输出,再贴实际代码实际结果
    • 格式:
我写了以下代码, 目的是<预期的结果>
<代码>
但是运行出来后, <不符合预期的结果>, 这是为什么? 如何解决?
  • 关键:AI需知道想要达到的目的,否则无法诊断
    • 示例:询问“为什么列表元素排序失败”(需提供上下文)
  • 提示模板:我写了以下代码,目的是<预期结果>,但是运行出来后,<不符合预期的结果>,这是为什么?如何解决? - AI会指出问题根源并给出修改代码
# 示例:list排序bug
list1 = [1, 7, 2, 6, 5]
list1.sort() # 原地修改,不返回新列表
print(list1) # 输出:[1, 2, 5, 6, 7] 但误以为未排序
  • 解决方案:用sorted(list1)返回新排序列表
sorted_list = sorted(list1)
print(sorted_list) # [1, 2, 5, 6, 7]
  • 提示模板:请给我N道练习<知识点>的题目,并附上答案 - AI秒出多道题,便于对照答案让AI检查自己代码 - 好处:想练哪个点练哪个点,数量随意

  • 示例:3道Python列表合并与排序练习题

    1. 列表合并排序
list1 = [1, 5, 3, 9]
list2 = [2, 6, 4, 8]
merged_list = sorted(list1 + list2)
# [1, 2, 3, 4, 5, 6, 8, 9]
1. **查找最大值**:
numbers = [12, 45, 6, 78, 34, 99, 23]
max_value = max(numbers) # 99
1. **元素计数**:
my_list = ['apple', 'banana', 'apple', 'orange', 'banana', 'apple']
count = my_list.count('apple') # 3
  • 让AI根据知识点出题,像一对一助教
  • 问题:问AI“以下代码可以如何改进?”,然后粘贴代码
  • 示例代码改进(判断is_happy布尔值)
    • 问题代码
is_happy = True
if is_happy == True:
print('Happy')
else if is_happy == False:
print('Sad')
- **AI建议**:
- 布尔值直接用在条件中,无需\`== True`或`== False\`
- `else if is_happy == False`直接换`else`
- **改进代码**:
is_happy = True
if is_happy:
print('Happy')
else:
print('Sad')
  • 好处:代码更简洁、逻辑更清晰,指导写优雅代码
  • 优势:提供碎片化、即时性帮助,大大缩短调试时间
  • 局限:无法进行体系化、系统性详细教学
  • 数据分析首要难题:数据从哪里来
    • 示例目标:分析中国新能源汽车行业发展,但手头无数据集
  • 用法:让AI搜索数据,提供格式化输出
来源链接:<url>
来源网站介绍:<来源网站介绍>
数据介绍:<数据介绍>
  • 示例结果:
    • 来源链接:IEA
    • 来源网站介绍:**国际能源署(IEA)**提供中国新能源汽车注册数据(2020-2023月度),用于全球能源趋势政策研究
    • 数据介绍:2020-2023年中国新能源汽车月度注册数据,CSV格式,最后更新2023年4月26日,适用于市场分析和趋势预测
  • 关键技巧:开启网页浏览模式,让AI借助搜索引擎获取实时信息
    • 因为AI知识受训练截止时间限制,手头数据集可能不含最新数据
  • 数据分析第二个难题什么值得分析?
    • 已知行业或有明确目标无问题

    • 不熟悉行业/数据集:询问AI行业关键指标计算方法

      • 电商示例

        1. CAC(客户获取成本):总营销费用 / 新客户数
        2. AOV(平均订单价值):总收入 / 总订单数
        3. 购物车转化率:完成购买顾客数 / 开始购物顾客数
        4. CLV(顾客生命周期价值):平均购买频率 × AOV × 客户关系期限
        5. 转化率:(购买次数 / 访问次数) × 100
    • 上传数据集求分析方向

      • 房价数据集示例:面积/朝向/楼层对价格影响、位置因素(主路/优选地区)、装修状态价值、价格分布趋势
  • 数据分析第三个难题数据集哪里不干净?怎么处理?
    • 人工评估可能不完整,遗漏问题影响后续分析
    • 解决方案:让AI检查数据集,找出未发现的清洗问题
  • 用法6: 数据清洗建议 - 询问数据集哪里不干净、如何处理,AI分析缺失值、异常值、重复值 - 示例:房价数据集检查房屋面积、层数等属性 - 交叉验证:结合自身发现与AI建议,补遗漏问题
  • 让AI根据数据集生成结论,补充自身分析
    • 示例:AI建议处理缺失值(Age用中位数填充,Cabin删除或转二元变量)
  • AI生成文本本质持续猜下一个最可能输出,可能一本正经胡说八道(hallucination)
  • 核心目的:培养自身分析和分辨能力
  • 数据相关问题,AI常生成Python代码求解
    • 示例代码:
# Load the house price dataset
house_price_file_path = "../data/house_price.csv"
house_price_data = pd.read_csv(house_price_file_path)
# Initial exploration
house_price_data.describe(include='all')
  • 展开执行验证AI结论
  • 验证AI结论:展开ChatGPT生成的Python代码,检查代码合理性和运行结果是否匹配结论
    • 若代码逻辑正确、结果一致,即确认AI非胡说;同时可从中学习
  • 正确心态:勿视AI为全知全能神,自己是AI老板,需审核检验其工作成果
  • 官方AI插件:Jupyter推出的Jupyter AI,内置于Jupyter NotebookJupyterLab
    • 核心功能
      • AI magic:将Notebook转为可复现的生成AI playground(支持IPython内核环境,如JupyterLab、Notebook、Google Colab、VSCode)
      • 聊天UI:JupyterLab原生聊天界面,作为对话式AI助手
      • 模型支持:AI21、Anthropic、Cohere、Hugging Face、OpenAI、SageMaker等
  • 目的:借助生成AI迅猛发展,提升工具工作效率
# 示例:房屋价格数据集加载与探索
house_price_file_path = '/mnt/data/house_price.csv'
house_price_data = pd.read_csv(house_price_file_path)
house_price_data.describe(include='all')
# describe输出示例(房屋价格数据统计)
price area bedrooms bathrooms
count 5.450000e+02 545.000000 545.000000 545.000000
mean 4.766729e+06 5190.541284 2.865138 1.286284
min 1.750000e+06 1600.000000 1.000000 1.000000
max 7.500000e+07 16200.000000 6.000000 4.000000
  • 将生成式AI集成到Jupyter环境中
    • 支持AI magic命令:在Jupyter Notebook中直接使用生成式AI
    • 聊天UI:在JupyterLab中与AI作为对话助手交互
    • 支持多种模型提供商:AI21、Anthropic、Cohere、Hugging Face、OpenAI、SageMaker等
  • 优势:无需复制粘贴代码到网页版AI,直接在写代码的环境中交互
  • Python版本:3.8 到 3.11
    • conda安装示例:conda install python=3.11
  • JupyterLab 4:jupyter_ai包依赖
    • pip安装:pip install jupyterlab==4.0
    • conda安装:
conda config --add channels conda-forge
conda config --set channel_priority strict
conda install jupyterlab==4.0
  • 查看更多前提:搜索Jupyter AI官网,点击Prerequisites
  • 打开CMD或终端
  • 执行:pip install jupyter_ai
  • 若失败:检查前提条件并升级
  • 安装命令pip install jupyter_ai(JupyterLab 4用v2版,JupyterLab 3用pip install jupyter_ai==1.0
    • 仅magic命令:pip install jupyter_ai_magics
  • AI模型非自带:基于第三方大语言模型(如OpenAI、Anthropic)
  • 支持提供商表格
Provider Provider ID 环境变量 Python包
AI21 ai21 AI21_API_KEY ai21
Anthropic anthropic ANTHROPIC_API_KEY anthropic
Anthropic (chat) anthropic-chat ANTHROPIC_API_KEY anthropic
Bedrock bedrock N/A boto3
Bedrock (chat) bedrock-chat N/A boto3
Cohere cohere COHERE_API_KEY cohere
GPT4All gpt4all N/A gpt4all
Hugging Face Hub huggingface_hub HUGGINGFACE_HUB_TOKEN huggingface_hub, ipywidgets, pillow
OpenAI openai OPENAI_API_KEY openai
OpenAI (chat) openai-chat OPENAI_API_KEY openai
SageMaker sagemaker-endpoint N/A boto3
  • macOS注意:可能需pip3 install jupyter_ai