AB
AiBoss站
教程

用 Python 与 pandas 搭建一个简单的数据分析器

教程

用 Python 与 pandas 搭建一个简单的数据分析器

从一份 CSV 开始,用 pandas 完成加载、探查、统计、筛选、分组、相关性与可视化,并把零散代码整理成可复用的分析函数。适合刚学会 Python 基础、想跑通第一条数据分析流程的读者。

数据分析听起来门槛很高,容易让人联想到庞大的数据库、复杂的数学推导和机器学习算法。但很多真正有用的分析任务,起点其实很朴素:读入一份数据,围绕它提出几个问题,然后用 Python 把答案算出来。本教程就用一份虚构的学生成绩数据,带你走完这条链路——加载 CSV、查看数据结构、计算统计量、按条件筛选、按类别分组、计算相关性、画一张图,最后把这些步骤收拢成可复用的函数。学完之后,这套骨架可以直接套用到其他结构相似的 CSV 数据集上。

适合的读者:已经会用 Python 变量、基本数据类型、导入库并调用函数、能对列表做简单操作,但还没有系统做过数据分析的人。不需要统计学背景,也不需要机器学习经验。文中所有数字都来自一份人为构造的小样本,观察到的规律只用于演示方法,不能当作关于真实学生的结论。

准备工作

需要的软件

  • Python:Python 3 或兼容的 Python 安装。
  • pandas:负责读取、筛选、分组与统计。
  • Matplotlib:负责画图。
  • 代码编辑器或 IDE:例如 VS Code、PyCharm,或者任意文本编辑器,只要能打开终端即可。

在终端里执行下面这条命令,一次装好两个库:

pip install pandas matplotlib

安装完成后,用一段极短的脚本确认导入正常,并打印 pandas 的版本号:

import pandas as pd
import matplotlib.pyplot as plt

print(pd.__version__)

终端里能打印出版本号,说明环境就绪。如果这一步报 ModuleNotFoundError,通常意味着 pip 装到了另一个 Python 解释器里,检查一下当前终端使用的 Python 与 pip 是否来自同一套环境。

准备一个工作目录

新建一个文件夹,用来同时存放脚本文件和数据集文件。后面脚本里用相对路径 "students.csv" 读取数据,因此脚本运行时的工作目录必须与 CSV 所在目录一致,否则会报找不到文件。

操作步骤

第一步:创建数据集

新建一个名为 students.csv 的文本文件,写入下面的内容。这份数据共 12 条记录,覆盖 9 到 12 四个年级,每行代表一名学生,每列代表一个属性:

  • name:学生名字
  • grade:所在年级
  • study_hours:学习时长(小时)
  • attendance:出勤率(百分比)
  • score:测验成绩,0 到 100 分
name,grade,study_hours,attendance,score
Ava,9,4.5,95,82
Liam,9,6.0,98,91
Sophia,10,3.5,89,76
Noah,10,7.0,99,94
Emma,11,5.5,93,88
Oliver,11,8.0,97,96
Mia,12,4.0,91,79
Ethan,12,6.5,96,90
Isabella,9,7.5,100,97
Lucas,10,2.5,85,68
Amelia,11,6.0,94,92
James,12,5.0,90,84

第一行是表头,必须与后面代码里引用的列名完全一致,包括大小写和下划线。例如 Ava 是 9 年级学生,学习 4.5 小时,出勤率 95%,成绩 82 分。有了这些记录,就可以回答诸如「学习时长与成绩之间是否存在关联」「各年级平均分差多少」这类问题。

第二步:用 pandas 加载数据

在脚本开头导入 pandas,然后调用 read_csv():

import pandas as pd

data = pd.read_csv("students.csv")
print(data)

read_csv() 会把 CSV 文件读成一个 DataFrame。DataFrame 是 pandas 的核心数据结构,可以理解成一张带行列标签、可编程操作的表。执行 print(data) 后,终端会按行列形式输出全部 12 条记录。

第三步:先探查,再分析

动手算任何指标之前,先弄清楚手里这份数据长什么样,能避免大量低级错误。

查看前几行。head() 默认返回前 5 行,也可以传入行数:

print(data.head())
print(data.head(3))

查看数据规模。shape 返回一个元组,依次是行数和列数:

print(data.shape)

这份数据的结果是 (12, 5),即 12 行、5 列。

查看列名。后续代码要按列名取值,先确认拼写:

print(data.columns)

查看各列类型。info() 会列出每列的非空数量与数据类型,帮助你判断某列是整数、小数还是文本:

data.info()

这一步在真实数据上尤其重要:如果某个数值列被读成了文本类型,后面的 mean()、max() 都会失败或给出意外结果。

第四步:计算基础统计量

pandas 把常用统计公式都封装好了,不需要手写。

平均分。对某一列调用 mean() 即可:

average_score = data["score"].mean()
print(f"Average score: {average_score:.2f}")

其中 :.2f 是格式化占位符,表示保留两位小数。

最高分与最低分。分别用 max() 和 min():

highest_score = data["score"].max()
lowest_score = data["score"].min()
print(f"Highest score: {highest_score}")
print(f"Lowest score: {lowest_score}")

中位数。把成绩排序后位于中间的那个值:

median_score = data["score"].median()
print(f"Median score: {median_score}")

一次性拿到完整统计摘要。describe() 会输出计数、均值、标准差、最小值、25 分位、中位数、75 分位和最大值:

print(data["score"].describe())

去掉列名直接对 DataFrame 调用,则会对所有数值列分别生成摘要:

print(data.describe())

面对一份陌生的数据集,describe() 往往是最先敲下的命令之一。

第五步:按条件筛选记录

筛选是 pandas 最常用的能力之一:给出一列布尔条件,pandas 只保留条件为真的行。

找出 90 分及以上的学生:

high_scores = data[data["score"] >= 90]
print(high_scores)

表达式 data["score"] >= 90 会为每一行生成一个 True 或 False,外层再用它去索引原表。

找出学习时长超过 5 小时的学生:

frequent_studiers = data[data["study_hours"] > 5]
print(frequent_studiers)

组合多个条件。例如同时要求学习时长超过 5 小时、出勤率高于 95%:

students = data[
    (data["study_hours"] > 5) & (data["attendance"] > 95)
]
print(students)

组合条件时,与用 &,或用 |,并且每个条件都要用括号包起来。漏掉括号会触发运算符优先级问题,导致报错或结果不符合预期。

第六步:定位具体记录

有时你要的不是最大值本身,而是「哪一行取到了这个最大值」。这时用 idxmax() 拿到行索引,再用 loc 取出整行:

top_student_index = data["score"].idxmax()
top_student = data.loc[top_student_index]
print(top_student)

同样的方法可以找出学习时长最长的那名学生:

most_studious_index = data["study_hours"].idxmax()
most_studious_student = data.loc[most_studious_index]
print(most_studious_student)

这一步的意义在于把统计结果和原始记录对应起来,便于核对与解释。

第七步:按类别分组

当数据里存在类别列时,分组统计非常有用。这份数据里的类别就是年级。

各年级平均分:

average_by_grade = data.groupby("grade")["score"].mean()
print(average_by_grade)

链式调用 round(2) 可以让输出更易读:

average_by_grade = (
    data.groupby("grade")["score"]
    .mean()
    .round(2)
)
print(average_by_grade)

一次计算多个统计量。用 agg() 传入统计函数名列表:

grade_summary = data.groupby("grade")["score"].agg(
    ["mean", "min", "max", "count"]
)
print(grade_summary)

输出会按年级给出平均分、最低分、最高分和人数。

第八步:分析学习时长与成绩的关系

用 corr() 计算两列的相关系数:

correlation = data["study_hours"].corr(data["score"])
print(f"Correlation: {correlation:.2f}")

相关系数的取值范围大致在 -1 到 1 之间:越接近 1,说明正向线性关系越强;越接近 -1,说明负向线性关系越强;接近 0 则说明几乎没有线性关系。

需要特别强调:相关不等于因果。即使这份数据里学习时长与成绩呈现相关,也不能据此断定「多学习直接导致了更高分数」。可能存在其他因素同时影响两者,也可能只是样本太小造成的巧合。

第九步:画图

数字适合精确核对,图形适合快速发现模式。

散点图:学习时长与成绩。

import matplotlib.pyplot as plt

plt.scatter(data["study_hours"], data["score"])
plt.xlabel("Study Hours")
plt.ylabel("Test Score")
plt.title("Study Hours vs. Test Scores")
plt.show()

每个点代表一名学生,横轴是学习时长,纵轴是成绩。观察点是否整体呈上升趋势,就能对两者的关系有一个直观印象。

柱状图:各年级平均分。

average_by_grade = data.groupby("grade")["score"].mean()
average_by_grade.plot(kind="bar")
plt.xlabel("Grade")
plt.ylabel("Average Score")
plt.title("Average Score by Grade")
plt.show()

柱状图让不同组之间的均值对比一目了然。

一个完整示例

把前面的步骤合并到一个脚本里,就是一条最小可运行的分析流水线。新建 analyzer.py,与 students.csv 放在同一目录:

import pandas as pd
import matplotlib.pyplot as plt

# 加载数据
data = pd.read_csv("students.csv")

# 基本信息
print("Dataset shape:")
print(data.shape)

print("\nFirst five rows:")
print(data.head())

# 成绩统计
average_score = data["score"].mean()
median_score = data["score"].median()
highest_score = data["score"].max()
lowest_score = data["score"].min()

print("\nScore Statistics")
print(f"Average: {average_score:.2f}")
print(f"Median: {median_score:.2f}")
print(f"Highest: {highest_score}")
print(f"Lowest: {lowest_score}")

# 90 分及以上的学生
high_scores = data[data["score"] >= 90]
print("\nStudents with scores of 90 or higher:")
print(high_scores[["name", "score"]])

# 各年级平均分
average_by_grade = (
    data.groupby("grade")["score"]
    .mean()
    .round(2)
)
print("\nAverage Score by Grade:")
print(average_by_grade)

# 学习时长与成绩的相关性
correlation = data["study_hours"].corr(data["score"])
print(f"\nStudy Hours vs. Score Correlation: {correlation:.2f}")

# 散点图
plt.scatter(data["study_hours"], data["score"])
plt.xlabel("Study Hours")
plt.ylabel("Test Score")
plt.title("Study Hours vs. Test Scores")
plt.show()

在终端里执行:

python analyzer.py

脚本会依次打印数据规模、前五行、成绩统计、高分学生名单、各年级平均分和相关系数,最后弹出一个散点图窗口。关闭图形窗口后程序结束。这就是一个规模虽小但环节完整的数据分析流程。

把分析器改造成可复用的函数

上面的脚本能跑,但每换一份数据就要改一遍代码。更好的做法是把重复操作封装成函数。

思路是:让函数接收一个 DataFrame 作为参数,在函数内部完成统计与打印,最后返回需要的结果。这样主程序只负责读数据和调用函数,逻辑与数据分离。例如可以定义一个 analyze_scores 函数,把成绩的均值、中位数、极值、高分筛选等步骤收进去;再定义一个按年级汇总的函数,把 groupby 那一段收进去。调用时传入不同的 DataFrame,就能复用同一套分析逻辑。

封装时注意两点:一是函数内部不要硬编码文件名,数据从参数进来;二是把「打印给人看」和「返回给程序用」区分开,需要后续处理的中间结果用 return 返回,而不是只打印到终端。

注意事项

  • 样本量很小。这份数据只有 12 条记录,任何统计结果都只具有演示意义,不能推广到真实人群。换用真实数据时,结论的可信度取决于数据本身的规模与采集方式。
  • 相关不等于因果。相关系数只描述线性关联的强弱与方向,不能证明因果方向,也不能排除混杂因素。
  • 组合条件必须加括号。用 & 和 | 连接多个筛选条件时,每个条件都要用括号包住,否则会因运算符优先级得到错误结果。
  • 列名必须完全匹配。CSV 表头与代码中的列名字符串要一致,包括大小写与下划线;拼错会直接抛出 KeyError。
  • 注意数据类型。如果数值列被读成文本,统计函数会失败或给出异常结果,加载后先用 info() 确认类型。
  • 注意工作目录。使用相对路径读取 CSV 时,脚本的运行目录必须与文件所在目录一致,否则会报文件不存在。
  • 缺失值需要单独处理。真实数据常有空值,均值等计算会跳过空值,但计数、分组和绘图的表现可能与你预期不同,分析前应先检查缺失情况。
  • 软件版本与依赖行为可能变化。pandas 与 Matplotlib 的具体版本、默认行为以及安装方式,请以各自官网当前信息为准。