第4章 数据科学三剑客:NumPy基础、Pandas数据处理、Matplotlib数据可视化入门

说实话,刚入行那会儿,我总觉得数据科学离自己很远。直到第一次接手一个用户行为分析项目,面对几十万行CSV文件,Excel直接卡死,我才意识到——嗯,Python的数据科学三剑客,是绕不过去的坎。

这三样东西,说白了就是:NumPy管计算、Pandas管表格、Matplotlib管画图。今天咱们就一个一个来过一遍。

4.1 NumPy基础:高性能数组计算

NumPy,全称是Numerical Python。它最核心的东西就是ndarray,也就是N维数组对象。我习惯叫它“张量”,虽然严格来说不太一样,但意思差不多。

4.1.1 创建数组

创建数组的方式有很多,我挑几个最常用的说说。

import numpy as np

# 从列表创建
arr1 = np.array([1, 2, 3, 4, 5])

# 全零数组
arr2 = np.zeros((3, 4))

# 全一数组
arr3 = np.ones((2, 3))

# 等差数列
arr4 = np.arange(0, 10, 2)  # [0, 2, 4, 6, 8]

# 均匀分布
arr5 = np.linspace(0, 1, 5)  # [0. , 0.25, 0.5, 0.75, 1. ]
我的小习惯: 创建测试数据时,我特别喜欢用np.random.randn()生成正态分布数据。比如模拟用户年龄分布,用它特别方便。

4.1.2 数组运算

NumPy的向量化运算,是它最大的亮点。你想想看,如果用Python原生列表做加法,得写个for循环。但NumPy直接一行搞定。

a = np.array([1, 2, 3])
b = np.array([4, 5, 6])

# 对应元素相加
c = a + b  # [5, 7, 9]

# 标量运算
d = a * 2  # [2, 4, 6]

# 矩阵乘法
e = a.dot(b)  # 32

为什么会这么快?因为底层是C语言实现的,而且利用了CPU的SIMD指令集。我在项目中遇到过一个问题:用Python列表处理100万条数据,跑了快10秒;换成NumPy,0.1秒就搞定了。差距就是这么大。

4.1.3 索引与切片

NumPy的索引和Python列表类似,但多了个“花式索引”的概念。

arr = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])

# 普通索引
print(arr[0, 1])  # 2

# 切片
print(arr[:2, 1:])  # [[2, 3], [5, 6]]

# 花式索引
indices = [0, 2]
print(arr[indices])  # [[1, 2, 3], [7, 8, 9]]
注意: 切片返回的是视图,不是副本。修改切片会影响原数组。我曾经因为这个bug排查了半天,后来养成了习惯——需要独立数据时,一定用.copy()

4.2 Pandas数据处理:表格操作的瑞士军刀

Pandas,我个人觉得它是Python数据分析的灵魂。它的两个核心数据结构:Series(一维)和DataFrame(二维)。

4.2.1 创建DataFrame

实际项目中,数据大多从文件读取。但为了演示,咱们先手动创建一个。

import pandas as pd

data = {
    '姓名': ['张三', '李四', '王五'],
    '年龄': [25, 30, 28],
    '城市': ['北京', '上海', '广州']
}
df = pd.DataFrame(data)
print(df)

输出结果:

姓名年龄城市
张三25北京
李四30上海
王五28广州

4.2.2 数据读取与写入

这是最常用的功能。我建议你记住这几个函数:

  • pd.read_csv() — 读取CSV文件
  • pd.read_excel() — 读取Excel文件
  • pd.read_json() — 读取JSON文件
  • df.to_csv() — 保存为CSV
# 读取CSV
df = pd.read_csv('user_data.csv', encoding='utf-8')

# 查看前5行
print(df.head())

# 查看基本信息
print(df.info())

# 查看统计摘要
print(df.describe())
避坑指南: 我曾经遇到过CSV文件编码问题,中文全变成乱码。后来我统一用encoding='utf-8-sig',完美解决。另外,大文件读取时记得加chunksize参数,不然内存容易爆。

4.2.3 数据清洗

真实数据,永远不干净。我总结了一套“清洗三板斧”:

  1. 处理缺失值df.dropna()df.fillna()
  2. 处理重复值df.drop_duplicates()
  3. 处理异常值:结合条件筛选
# 查看缺失值
print(df.isnull().sum())

# 填充缺失值
df['年龄'].fillna(df['年龄'].mean(), inplace=True)

# 删除重复行
df.drop_duplicates(inplace=True)

# 筛选年龄在0-120之间的数据
df = df[(df['年龄'] > 0) & (df['年龄'] < 120)]

4.2.4 分组与聚合

分组聚合是数据分析的核心操作。我经常用它来做用户分层分析。

# 按城市分组,计算平均年龄
result = df.groupby('城市')['年龄'].mean()

# 多维度聚合
result = df.groupby('城市').agg({
    '年龄': ['mean', 'max', 'min'],
    '姓名': 'count'
})

4.3 Matplotlib数据可视化入门

数据可视化,说白了就是“一图胜千言”。Matplotlib是Python最经典的绘图库,虽然现在有很多新库(比如Seaborn、Plotly),但Matplotlib依然是基础。

4.3.1 基本绘图流程

我习惯用pyplot模块,它模仿了MATLAB的绘图风格。

import matplotlib.pyplot as plt

# 准备数据
x = [1, 2, 3, 4, 5]
y = [2, 4, 6, 8, 10]

# 创建画布
plt.figure(figsize=(8, 5))

# 绘制折线图
plt.plot(x, y, marker='o', linestyle='-', color='b')

# 添加标签和标题
plt.xlabel('X轴')
plt.ylabel('Y轴')
plt.title('折线图示例')

# 显示网格
plt.grid(True)

# 显示图形
plt.show()

4.3.2 常用图表类型

我整理了一个表格,方便你快速查阅:

图表类型函数适用场景
折线图plt.plot()趋势变化
柱状图plt.bar()类别对比
散点图plt.scatter()相关性分析
直方图plt.hist()数据分布
饼图plt.pie()占比展示
# 柱状图示例
categories = ['A', 'B', 'C', 'D']
values = [23, 45, 56, 78]
plt.bar(categories, values, color=['red', 'blue', 'green', 'orange'])
plt.title('柱状图示例')
plt.show()

# 散点图示例
import numpy as np
x = np.random.randn(100)
y = np.random.randn(100)
plt.scatter(x, y, alpha=0.6)
plt.title('散点图示例')
plt.show()

4.3.3 子图与布局

有时候需要在一张图里展示多个图表。我常用subplots来实现。

# 创建2行2列的子图
fig, axes = plt.subplots(2, 2, figsize=(10, 8))

# 在第一个子图绘制
axes[0, 0].plot([1, 2, 3], [1, 4, 9])
axes[0, 0].set_title('子图1')

# 在第二个子图绘制
axes[0, 1].bar(['A', 'B'], [3, 7])
axes[0, 1].set_title('子图2')

# 调整布局
plt.tight_layout()
plt.show()
我的经验: 做报告时,我习惯把多个相关图表放在一个画布里。比如用户增长趋势和付费转化率放在一起,一眼就能看出关联性。另外,颜色搭配也很重要,我一般用plt.cm里的配色方案,比自己瞎选好看多了。

4.4 三剑客协同实战

最后,咱们来个综合案例。假设我们要分析一份销售数据:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

# 1. 生成模拟数据
np.random.seed(42)
dates = pd.date_range('2024-01-01', periods=100, freq='D')
sales = np.random.randint(100, 500, size=100)
cities = np.random.choice(['北京', '上海', '广州', '深圳'], size=100)

df = pd.DataFrame({
    '日期': dates,
    '销售额': sales,
    '城市': cities
})

# 2. 数据清洗
df.dropna(inplace=True)

# 3. 数据分析:按城市统计
city_stats = df.groupby('城市')['销售额'].agg(['mean', 'sum', 'count'])
print(city_stats)

# 4. 数据可视化
plt.figure(figsize=(12, 5))

# 折线图:每日销售额趋势
plt.subplot(1, 2, 1)
plt.plot(df['日期'], df['销售额'], alpha=0.7)
plt.title('每日销售额趋势')
plt.xticks(rotation=45)

# 柱状图:各城市销售额对比
plt.subplot(1, 2, 2)
city_stats['sum'].plot(kind='bar', color=['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4'])
plt.title('各城市总销售额')
plt.ylabel('销售额')

plt.tight_layout()
plt.show()

你看,从数据生成、清洗、分析到可视化,三剑客配合得天衣无缝。我个人觉得,掌握了这三样东西,你就已经具备了处理80%数据分析场景的能力。

嗯,今天就先聊到这儿。下一章咱们会深入讲讲数据预处理的高级技巧,包括特征工程和文本数据清洗。到时候见。

课后练习: 找一份公开数据集(比如Kaggle上的Titanic数据集),用今天学的内容完成:数据加载、缺失值处理、分组统计、绘制至少两张图表。做完之后,你会发现——原来数据分析也没那么难。