Python 数据分析入门

从基础语法到 Pandas、可视化、Git 与 Marimo

笔记本风格 · 边学边练 · 12 章完整路径
01

变量

通俗理解

变量就像贴了标签的收纳盒。你可以在盒子里放东西(赋值),通过标签找到里面的东西(使用变量),盒子里的东西还可以随时更换(重新赋值)。

📓 关于代码单元格

本网站的代码块模拟了笔记本工具(如 Marimo、Jupyter)的单元格样式。代码写在"单元格"里,点击运行或按 Shift+Enter 就能执行,结果直接显示在单元格下方。In [1] 表示第 1 个执行的单元格。在第10章你将学到 Marimo 这款现代化笔记本工具。

🔍 逐行解析

  • name = "小明"等号是"赋值",把右边的值放进左边的变量。"小明" 是字符串。
  • age = 25整数类型 int,存放整数。
  • height = 1.75浮点类型 float,存放小数。
  • is_student = True布尔类型 bool,只有 TrueFalse,首字母大写。
  • print(name)print() 是内置函数,把内容显示在屏幕上。

✏️ 小测验

1. 正确的变量赋值是?

变量名不能以数字开头;== 是比较不是赋值。

2. age = 25 的数据类型是?

没有小数点的数字是 int。

3. Python 布尔值是?

True/False 是关键字,不加引号。
02

函数

通俗理解

函数就像食谱。给它食材(参数),按固定步骤操作(函数体),最后给你一道菜(返回值)。食谱可以反复使用。

🔍 逐行解析

  • def greet(...):def 定义函数,函数名后有括号和冒号。
  • greeting="你好"默认参数,不传就用默认值。
  • (缩进)message = ...函数体必须缩进 4 个空格。
  • f"{greeting},{name}!"f-string:字符串前加 f,花括号里放变量。
  • return messagereturn 把结果返回给调用者。

✏️ 小测验

1. 定义函数的关键字是?

def 是 define 的缩写。

2. 返回结果用哪个关键字?

return 返回值;print 只是显示。

3. def f(x, y=0) 中 y 是?

有默认值的参数叫默认参数。
03

循环

通俗理解

循环就像跑步机——重复同一个动作直到停止条件。for 是"固定次数重复",while 是"条件满足时重复"。

🔍 逐行解析

  • for fruit in fruits:让 fruit 依次等于列表中每个元素。
  • range(1, 4)生成 1,2,3(不包含4),左闭右开。
  • while count < 3:条件为 True 就继续,False 就停止。
  • count = count + 1必须改变条件,否则死循环。

✏️ 小测验

1. range(1,4) 循环几次?

生成 1,2,3 共 3 个数字。

2. while 何时停止?

条件为 False 时停止循环。

3. ["苹果","香蕉"] 有几个元素?

逗号分隔的值就是元素。
04

条件判断与数据结构

通俗理解

条件判断就像路口的红绿灯——根据不同情况走不同的路。数据结构则是不同类型的收纳盒:列表是一排抽屉,字典是带标签的文件柜。

📋 本章你将学会

  • if / elif / else 条件判断
  • 列表(list)的常用操作:索引、切片、追加
  • 字典(dict)的键值对操作
  • 字符串常用方法

🔍 比较与逻辑运算符

运算符含义示例
==等于x == 5
!=不等于x != 0
> < >= <=大小比较age >= 18
and并且(两个都为True)x > 0 and x < 10
or或者(任一为True)x == 1 or x == 2
not取反not done

💡 列表 vs 字典怎么选?

  • 有顺序、用编号访问 → 列表 ["苹果", "香蕉"]
  • 有名称、按键访问 → 字典 {"name": "小明"}
  • Pandas 的每一列就是一个有序列表,每行就是一个字典

✏️ 小测验

1. fruits = ["a","b","c"]fruits[0] 是?

Python 索引从 0 开始。

2. 字典通过什么取值?

dict["key"] 按键名取值。

3. " hi ".strip() 的结果是?

strip() 去掉首尾空白。
05

API 调用

通俗理解

API 就像餐厅服务员。你不用自己去厨房做菜,告诉服务员要点什么(请求),服务员把菜端给你(返回数据)。requests 库就是沟通工具。

🌐 API 调用流程

  • 发送请求requests.get(url)
  • 检查状态status_code 为 200 表示成功
  • 解析数据response.json() 转成字典
  • 提取信息data["键名"]
代码使用标准 requests 库,在 Marimo/Jupyter 中也是这样写。输出为预计算结果。

🔍 逐行解析

  • import requests导入库,使用前需 pip install requests
  • requests.get(url)发送 GET 请求,返回 Response 对象。
  • response.status_code200 成功,404 未找到,500 服务器错误。
  • response.json()把 JSON 转成 Python 字典。
  • data["body"][:50]切片取前 50 个字符。

✏️ 小测验

1. 发送 GET 请求用?

requests.get() 是最常用的 GET 请求方法。

2. 解析 JSON 用?

.json() 把 JSON 字符串解析为字典。

3. 状态码 200 表示?

200 表示成功。
06

数据读取与 DataFrame

通俗理解

Pandas 是 Python 数据分析的"瑞士军刀"。DataFrame 就像一张 Excel 表格——有行有列,每列有名字,每行有编号。read_csv 就是把文件读成这样的表格。

📦 安装与导入

$ pip install pandas

在 Marimo/Jupyter 中,导入 pandas 的惯例写法:

📂 文件路径:绝对路径 vs 相对路径

读取文件前需要告诉 Python 文件在哪里。路径有两种写法:

  • 相对路径:相对于当前工作目录,如 "data/iris.csv". 表示当前目录,.. 表示上一级目录。推荐使用,换台电脑也能运行。
  • 绝对路径:从根目录开始的完整路径,如 "/home/user/project/data/iris.csv"(Windows 为 "C:\\Users\\...\\iris.csv")。只在当前电脑有效。
在 Marimo/Jupyter 中,用 import os; os.getcwd() 查看当前工作目录,确保相对路径的起点正确。

🔍 常用读取参数

  • pd.read_csv("f.csv")读取逗号分隔文件
  • sep="\t"指定分隔符(如制表符)
  • header=None文件没有列名时使用
  • skiprows=2跳过前2行
  • pd.read_excel("f.xlsx")读取 Excel 文件(需 pip install openpyxl
分析中间结果或最终结果都可以用 to_csv() 保存,方便分享或下次继续分析。读取用 read_csv(),写入用 to_csv(),方向相反。

✏️ 小测验

1. pandas 的惯例导入别名是?

社区惯例是 import pandas as pd

2. 查看 DataFrame 前5行用?

.head() 默认显示前5行,可传参数指定数量。

3. df.shape 返回什么?

.shape 返回元组 (行数, 列数)。
07

数据筛选与变换

通俗理解

数据筛选就像在 Excel 中用筛选器——只看满足条件的行。数据变换则是添加计算列排序,让数据变成你需要的样子。

isin() 适合"值在这几个之中"的筛选场景,比写一长串 | 条件更清晰。

💡 方法链(Chaining)

Pandas 支持把多个操作连起来写,像流水线一样:

用括号包裹可以换行写,更清晰。

df["新列"] = ... 直接修改原表;df.assign(新列=...) 返回副本不改动原表,在方法链中更安全。

🔍 缺失值与类型转换速查

  • df.isnull().sum()统计每列缺失值数量
  • df.dropna()删除含缺失值的行(subset=["列"] 指定列)
  • df["列"].fillna(值)填充缺失值(可用均值、中位数等)
  • df["列"].astype(float)转换数据类型(如字符串转数字)
  • np.nan表示缺失值(Not a Number)

✏️ 小测验

1. 选择多列应该用?

多列用列表 df[["a","b"]],注意双层方括号。

2. 多条件"且"用什么符号?

Pandas 中用单个 & 表示"且",| 表示"或"。

3. 降序排列用?

ascending=False 表示降序。
08

分组聚合与合并

通俗理解

分组聚合就像按班级统计平均分——先把学生按班级分组(groupby),再对每组计算统计量(mean/count/sum)。合并则是把两张表按共同列拼起来,像 Excel 的 VLOOKUP。

🔍 merge 的 how 参数

  • how="inner"只保留两表都有的匹配(默认)
  • how="left"保留左表所有行,右表无匹配填 NaN
  • how="right"保留右表所有行
  • how="outer"保留两表所有行

📐 什么是整洁数据?

Pandas 和大多数可视化工具都要求数据是"整洁的"。整洁数据有三条原则:

  • 每行是一个观测(一个实体的一组测量值)
  • 每列是一个变量(一个测量特征)
  • 每个单元格是一个值(不混合多个信息)

不整洁的数据常见问题:年份作为列名(宽格式)、一个单元格里塞了多个值。用 meltpivot 可以在宽格式和长格式之间转换。

长格式中 yearsales 各是一列,可以直接用于分组、绘图。这是 Pandas 最推荐的数据形状。

🔍 数据重塑速查

  • df.melt(id_vars, var_name, value_name)宽转长:多列合并成"键列+值列"
  • df.pivot(index, columns, values)长转宽:一列的值展开成多列
  • .reset_index()把索引变回普通列(groupby/pivot 后常用)

✏️ 小测验

1. groupby 后计算平均值用?

Pandas 用 .mean() 计算平均值。

2. 统计某列各值出现次数用?

.value_counts() 是最常用的计数方法。

3. 两表合并用?

pd.merge() 按共同列合并两张表。
09

数据可视化

通俗理解

数据可视化就是把数字变成图——条形图比表格更直观地看出大小,折线图展示趋势,散点图发现关系。选对图表类型,数据自己会"说话"。

📊 如何选择图表类型?

你想展示什么推荐图表示例场景
比较不同类别的大小条形图/柱状图各产品销售额对比
展示随时间变化的趋势折线图月度收入变化
发现两个变量的关系散点图身高与体重的关系
展示数据分布直方图学生成绩分布
展示部分占整体比例饼图市场份额
在 Python 中,常用 matplotlibseabornaltair 画图。下面用交互式图表展示每种图表的效果。
各鸢尾花品种的平均花瓣长度
平均花瓣长度 (cm)0123456setosaversicolorvirginica1.464.265.55
2023年各月销售额趋势(万元)
销售额(万元)0501001502002503001月2月3月4月5月6月7月8月9月10月11月12月120135128152168185195188210225240268
花萼长度与花瓣长度的关系
花瓣长度 (cm)花萼长度 (cm)0123456012345678setosaversicolorvirginica
花萼长度分布
频数花萼长度区间 (cm)0102030404.3-4.74.7-5.15.1-5.55.5-5.95.9-6.36.3-6.76.7-7.17.1-7.57.5-7.952837292117832

💡 可视化最佳实践

  • always add title and axis labels:每张图都要有标题和轴标签
  • 从0开始柱状图:柱状图Y轴从0开始,避免误导
  • 少用3D图和饼图:3D图难读,多类别饼图难分辨
  • 颜色有意义:用颜色区分类别或数值大小,不是为了好看
  • 保存高清图plt.savefig("fig.png", dpi=300)

✏️ 小测验

1. 展示月度销售趋势用什么图?

趋势用折线图最直观。

2. 发现两个变量关系用?

散点图展示两个数值变量的关系。

3. Python 最基础的绘图库是?

matplotlib 是 Python 最基础的绘图库,seaborn/altair 基于它构建。
10

Marimo 工作流

通俗理解

Marimo 是一款响应式 Python 笔记本——改一个单元格,所有依赖它的单元格自动重新运行,就像 Excel 改了一个数字,引用它的公式自动更新。笔记本以纯 Python 存储,天生 Git 友好,还能一键变成应用。

🚀 为什么选择 Marimo?

Marimo 解决了 Jupyter 的几个经典痛点:隐藏状态导致结果不一致、.ipynb 是 JSON 难用 Git 管理、无法轻松分享为应用。

  • 响应式:运行一个单元格,自动运行所有依赖它的单元格
  • 纯 Python 存储.py 文件,Git diff 清晰可见
  • 可复现:无隐藏状态,删除单元格即删除其变量
  • 可部署:一键作为脚本执行或 Web 应用分享
  • 内置交互:滑块、下拉框、表格等 UI 元素,无需 ipywidgets
  • 内置 SQL:原生支持 SQL 单元格

📦 安装 Marimo

# 使用 pip 安装 $ pip install marimo # 运行入门教程 $ marimo tutorial intro # 创建新笔记本 $ marimo edit notebook.py # 也可以用 uv(更快) $ uv add marimo && uv run marimo edit
Marimo 笔记本保存为 .py 文件,而不是 .ipynb。可以直接用任何编辑器打开,也能用 python notebook.py 作为脚本执行。

⚡ 响应式 = 自动依赖追踪

Marimo 静态分析每个单元格的定义(它创建了什么变量)和引用(它读取了什么变量),构建依赖图(DAG)。当一个单元格运行时,所有引用其变量的单元格自动重新运行。

这和 Jupyter 最大的不同:Jupyter 需要你手动按顺序运行单元格;Marimo 根据变量依赖关系自动决定执行顺序,类似于电子表格。

🔍 关键规则

  • 运行单元格1Marimo 自动运行单元格2和3,因为它们依赖 df 和平均分
  • 删除单元格1df 从内存中删除,单元格2和3自动标记为过时
  • 全局变量名唯一每个全局变量只能由一个单元格定义,避免冲突
  • 下划线前缀 = 局部变量_temp 只在当前单元格可见,不触发其他单元格重跑

🎛️ 无需 ipywidgets

Marimo 内置了丰富的 UI 组件,交互即响应——拖动滑块,引用其值的单元格自动更新。

📋 常用 UI 组件

组件代码用途
滑块mo.ui.slider()选择数值范围
下拉框mo.ui.dropdown()选择单个选项
复选框mo.ui.checkbox()布尔开关
文本输入mo.ui.text()输入文字
表格mo.ui.table()可交互筛选的数据表
数字输入mo.ui.number()输入精确数值
特性JupyterMarimo
文件格式.ipynb(JSON).py(纯 Python)
执行模型手动按顺序运行响应式自动运行依赖单元格
隐藏状态容易产生不一致无隐藏状态,删除即清除
Git 友好差(JSON diff 难读)好(纯 Python diff 清晰)
UI 交互需安装 ipywidgets内置组件
部署为应用需额外工具marimo run 一键部署
作为脚本执行需 jupytext 转换python file.py 直接运行
SQL 支持需 %%sql 魔法命令原生 SQL 单元格
快捷键 / 命令功能
Ctrl+Enter运行当前单元格
Shift+Enter运行单元格并跳到下一个
Ctrl+B在下方插入单元格
Ctrl+/注释/取消注释
marimo edit启动编辑器
marimo run file.py作为 Web 应用运行
python file.py作为普通脚本执行
marimo export html file.py导出为静态 HTML
  • 一个全局变量只在一个单元格定义:这是 Marimo 的强制规则,也是好的编程习惯
  • 用下划线前缀创建临时变量_temp_df 不会触发其他单元格重跑
  • 不要跨单元格修改变量:在定义它的单元格内完成修改,或创建新变量
  • mo.md() 写 Markdown:Marimo 没有单独的 Markdown 单元格,用 mo.md() 在代码单元格中写笔记
  • 大型计算用 Lazy 模式:设置中开启 Lazy,单元格只标记为过时而非自动运行
  • 用函数封装临时逻辑:避免全局变量过多
Marimo 不追踪变量的原地修改(如 list.append()df["col"] = ...)。如果需要修改数据,在定义它的单元格内修改,或创建新变量。

✏️ 小测验

1. Marimo 笔记本保存为什么格式?

Marimo 保存为 .py 文件,Git 友好,可直接当脚本运行。

2. 运行一个单元格时,Marimo 会自动做什么?

Marimo 根据变量依赖图自动运行引用了该单元格变量的其他单元格。

3. 如何让变量只在当前单元格可见?

下划线前缀(如 _x)的变量是单元格局部变量,不会触发其他单元格重跑。
11

Git 版本控制

通俗理解

Git 就像代码的"时光机"——记录每次修改,随时能回到任意版本。GitHub 则是"云盘",把代码备份到网上,还能和别人协作。

📦 为什么需要版本控制?

  • 回溯历史:改坏了能回到上一个能跑的版本
  • 备份同步:代码在 GitHub 上,换电脑也不怕
  • 多人协作:各改各的,Git 自动合并
  • 实验自由:开分支随便试,不影响主线代码
概念类比说明
Repository(仓库)项目文件夹被 Git 管理的项目目录
Commit(提交)存档点一次快照,记录"谁在什么时候改了什么"
Push(推送)上传到云盘把本地提交传到 GitHub
Pull(拉取)从云盘下载获取别人的最新修改
Branch(分支)平行宇宙在分支上实验,不影响主线
# 1. 初始化仓库(在项目文件夹中执行一次) $ git init # 2. 查看当前状态(哪些文件改了) $ git status # 3. 添加文件到暂存区 $ git add notebook.ipynb # 添加单个文件 $ git add . # 添加所有文件 # 4. 提交(创建存档点) $ git commit -m "添加数据分析笔记本" # 5. 关联远程仓库(首次) $ git remote add origin https://github.com/用户名/仓库名.git # 6. 推送到 GitHub $ git push -u origin main # 之后修改代码的日常流程: $ git add . $ git commit -m "更新可视化图表" $ git push
# 下载别人的项目到本地 $ git clone https://github.com/用户名/仓库名.git # 进入项目目录 $ cd 仓库名 # 获取最新更新 $ git pull

🔧 .gitignore 文件

在项目根目录创建 .gitignore,告诉 Git 哪些文件不要备份:

💡 Git + Marimo 工作流

  • Marimo 笔记本是纯 .py 文件,Git diff 能清晰看到代码变更
  • 大文件(数据集)放 .gitignore,用云盘或数据平台分享
  • 每次完成一个分析步骤就 commit 一次,写清楚说明
  • 在 GitHub 上 .py 文件直接可读,无需额外渲染插件

⚠️ 什么是合并冲突?

当两个人修改了同一个文件的同一行,Git 无法自动决定保留哪个版本,就会产生"合并冲突"(merge conflict)。用 git pull 拉取别人的更改时可能遇到。

Git 会在冲突文件中插入特殊标记:

🔧 解决冲突的步骤

  • 1. 打开冲突文件找到 <<<<<<< 标记的位置
  • 2. 决定保留哪个版本保留你的、保留别人的、或手动合并两者
  • 3. 删除所有冲突标记删掉 <<<<<<< HEAD=======>>>>>>>> 三行
  • 4. 保存文件确认代码能正常运行
  • 5. 重新提交git add .git commit -m "解决合并冲突"git push
不要在有冲突标记的情况下提交!确保删掉所有 <<<<<<<=======>>>>>>>> 后再 commit。

💡 避免冲突的最佳实践

  • 每次开始工作前先 git pull,获取最新版本
  • 频繁小步提交,不要攒一大堆改动再 push
  • 和协作者沟通,避免同时编辑同一个文件的同一区域
  • 不同功能用不同分支(git branch),完成后再合并

✏️ 小测验

1. 提交代码的命令顺序是?

先 add 暂存,再 commit 提交,最后 push 推送。

2. 下载 GitHub 项目用?

git clone 首次下载项目;git pull 获取后续更新。

3. .gitignore 的作用是?

.gitignore 告诉 Git 哪些文件不需要版本控制(如大数据文件、缓存)。
12

综合实战

实战场景

从 API 获取数据 → 用 Pandas 整理 → 分组统计 → 可视化展示。一个完整的数据分析流程,把前面学的全部串起来!

各用户文章数量统计
文章数0246810用户1用户2用户3用户4用户5用户6用户7用户8用户9用户1010101010101010101010

🗺️ 概念对应表

代码用到的知识
import requests, pandas as pd第2章 函数/导入库 + 第5章 API
requests.get(url)第5章 API 调用
pd.DataFrame(posts)第6章 DataFrame
df.groupby("userId").agg(...)第8章 分组聚合
df["title"].str.len()第4章 字符串方法 + 第7章 添加列
.plot(kind="bar")第9章 数据可视化
for ... in ...第3章 循环

🚀 下一步

恭喜你完成全部 12 章!你已经掌握了数据分析的核心技能。继续探索:

  • marimo edit 创建你的第一个响应式笔记本
  • 学习 numpy 做数值计算
  • 尝试 altairplotly 做交互式图表
  • 在 GitHub 上创建你的第一个数据分析项目
在自己电脑上运行前,安装所需库:pip install pandas requests matplotlib marimo