跳到主要内容

第12章:Python 数据分析

12.1 数据分析概述

数据分析是指通过收集、处理、分析数据,提取有价值的信息和洞察的过程。Python 凭借其丰富的库生态系统,成为数据分析的热门工具。

数据分析的基本步骤

  1. 数据收集:获取原始数据
  2. 数据清洗:处理缺失值、异常值等
  3. 数据探索:了解数据的基本特征
  4. 数据预处理:转换数据格式、特征工程等
  5. 数据分析:使用统计方法、机器学习算法等
  6. 数据可视化:展示分析结果
  7. 结果解释:得出结论并提出建议

常用的数据分析库

描述用途
NumPy数值计算库数组操作、数学运算
Pandas数据处理库数据清洗、转换、分析
Matplotlib数据可视化库绘制图表
Seaborn统计数据可视化库高级图表、统计图形
Scikit-learn机器学习库机器学习算法
SciPy科学计算库统计分析、优化等

12.2 NumPy 库

NumPy(Numerical Python)是 Python 中用于科学计算的核心库,提供了高效的多维数组操作。

安装 NumPy

pip install numpy

NumPy 数组

import numpy as np

# 创建一维数组
arr1 = np.array([1, 2, 3, 4, 5])
print(arr1)
print(type(arr1))
print(arr1.shape)

# 创建二维数组
arr2 = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(arr2)
print(arr2.shape)

# 创建特殊数组
zeros = np.zeros((2, 3)) # 全零数组
print(zeros)

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

full = np.full((2, 3), 5) # 填充指定值
print(full)

identity = np.eye(3) # 单位矩阵
print(identity)

range_arr = np.arange(0, 10, 2) # 范围数组
print(range_arr)

linspace = np.linspace(0, 10, 5) # 等间隔数组
print(linspace)

# 随机数组
random = np.random.random((2, 3)) # 0-1 随机数
print(random)

randint = np.random.randint(0, 10, (2, 3)) # 随机整数
print(randint)

数组操作

import numpy as np

# 数组索引和切片
arr = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(arr[0, 0]) # 单个元素
print(arr[0, :]) # 第一行
print(arr[:, 0]) # 第一列
print(arr[0:2, 0:2]) # 子数组

# 数组运算
arr1 = np.array([1, 2, 3])
arr2 = np.array([4, 5, 6])

print(arr1 + arr2) # 加法
print(arr1 - arr2) # 减法
print(arr1 * arr2) # 乘法(元素级)
print(arr1 / arr2) # 除法
print(arr1 ** 2) # 平方

# 广播
arr3 = np.array([[1, 2, 3], [4, 5, 6]])
scalar = 2
print(arr3 * scalar) # 广播标量

# 聚合函数
arr = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(np.sum(arr)) # 总和
print(np.mean(arr)) # 平均值
print(np.min(arr)) # 最小值
print(np.max(arr)) # 最大值
print(np.std(arr)) # 标准差
print(np.argmax(arr)) # 最大值索引

# 轴操作
print(np.sum(arr, axis=0)) # 按列求和
print(np.sum(arr, axis=1)) # 按行求和

# 数组形状操作
arr = np.array([[1, 2, 3], [4, 5, 6]])
print(arr.shape) # (2, 3)

# 重塑
reshaped = arr.reshape(3, 2)
print(reshaped)

# 扁平化
flattened = arr.flatten()
print(flattened)

# 转置
transposed = arr.T
print(transposed)

线性代数

import numpy as np

# 矩阵乘法
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
print(np.dot(A, B)) # 矩阵乘法
print(A @ B) # 矩阵乘法(Python 3.5+)

# 逆矩阵
inv_A = np.linalg.inv(A)
print(inv_A)

# 行列式
det_A = np.linalg.det(A)
print(det_A)

# 特征值和特征向量
eigenvalues, eigenvectors = np.linalg.eig(A)
print(eigenvalues)
print(eigenvectors)

# 求解线性方程组
# Ax = b
A = np.array([[2, 3], [1, -2]])
b = np.array([8, -3])
x = np.linalg.solve(A, b)
print(x)

12.3 Pandas 库

Pandas 是 Python 中用于数据处理和分析的强大库,提供了 DataFrame 和 Series 两种主要数据结构。

安装 Pandas

pip install pandas

Series

Series 是一维标签数组,类似于带标签的 NumPy 数组。

import pandas as pd

# 创建 Series
s1 = pd.Series([1, 2, 3, 4, 5])
print(s1)

# 带索引的 Series
s2 = pd.Series([1, 2, 3, 4, 5], index=['a', 'b', 'c', 'd', 'e'])
print(s2)

# 从字典创建 Series
data = {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}
s3 = pd.Series(data)
print(s3)

# 访问元素
print(s2['a'])
print(s2[0])
print(s2[['a', 'c', 'e']])

# 布尔索引
print(s2[s2 > 3])

# 基本运算
print(s2 + 1)
print(s2 * 2)
print(s2.mean())
print(s2.sum())
print(s2.max())
print(s2.min())

DataFrame

DataFrame 是二维标签数据结构,类似于表格。

import pandas as pd

# 创建 DataFrame
data = {
'name': ['John', 'Alice', 'Bob', 'Charlie', 'David'],
'age': [30, 25, 35, 40, 28],
'city': ['New York', 'London', 'Paris', 'Tokyo', 'Sydney'],
'salary': [50000, 60000, 70000, 80000, 55000]
}
df = pd.DataFrame(data)
print(df)

# 查看数据
print(df.head()) # 前 5 行
print(df.tail()) # 后 5 行
print(df.info()) # 数据信息
print(df.describe()) # 统计信息

# 访问列
print(df['name'])
print(df[['name', 'age']])

# 访问行
print(df.loc[0]) # 按标签访问
print(df.iloc[0]) # 按位置访问
print(df.loc[0:2, ['name', 'age']]) # 切片

# 布尔索引
print(df[df['age'] > 30])
print(df[(df['age'] > 30) & (df['salary'] > 60000)])

# 添加列
df['bonus'] = df['salary'] * 0.1
print(df)

# 修改列
df['salary'] = df['salary'] + 1000
print(df)

# 删除列
df = df.drop('bonus', axis=1)
print(df)

# 排序
df_sorted = df.sort_values('age', ascending=False)
print(df_sorted)

# 分组
grouped = df.groupby('city')
print(grouped.mean())
print(grouped.sum())

数据读取和写入

import pandas as pd

# 读取 CSV 文件
df = pd.read_csv('data.csv')
print(df)

# 写入 CSV 文件
df.to_csv('output.csv', index=False)

# 读取 Excel 文件
df = pd.read_excel('data.xlsx')
print(df)

# 写入 Excel 文件
df.to_excel('output.xlsx', index=False)

# 读取 JSON 文件
df = pd.read_json('data.json')
print(df)

# 写入 JSON 文件
df.to_json('output.json')

# 读取 SQL 数据库
import sqlite3

conn = sqlite3.connect('example.db')
df = pd.read_sql('SELECT * FROM users', conn)
print(df)

# 写入 SQL 数据库
df.to_sql('users', conn, if_exists='replace', index=False)

数据清洗

import pandas as pd
import numpy as np

# 创建含有缺失值的数据
data = {
'name': ['John', 'Alice', 'Bob', 'Charlie', 'David'],
'age': [30, np.nan, 35, 40, 28],
'city': ['New York', 'London', np.nan, 'Tokyo', 'Sydney'],
'salary': [50000, 60000, 70000, np.nan, 55000]
}
df = pd.DataFrame(data)
print(df)

# 检查缺失值
print(df.isnull())
print(df.isnull().sum())

# 处理缺失值
# 删除含有缺失值的行
df_dropna = df.dropna()
print(df_dropna)

# 填充缺失值
df_fillna = df.fillna({
'age': df['age'].mean(),
'city': 'Unknown',
'salary': df['salary'].median()
})
print(df_fillna)

# 处理重复值
df_duplicates = pd.DataFrame([
['John', 30, 'New York', 50000],
['John', 30, 'New York', 50000],
['Alice', 25, 'London', 60000]
], columns=['name', 'age', 'city', 'salary'])
print(df_duplicates)

# 检查重复值
print(df_duplicates.duplicated())

# 删除重复值
df_unique = df_duplicates.drop_duplicates()
print(df_unique)

# 数据类型转换
df['age'] = df['age'].astype('int64', errors='ignore')
print(df.dtypes)

# 字符串处理
df['city'] = df['city'].str.upper()
print(df)

# 日期处理
df['date'] = pd.date_range('2023-01-01', periods=5)
print(df)
print(df['date'].dt.year)
print(df['date'].dt.month)
print(df['date'].dt.day)

12.4 Matplotlib 库

Matplotlib 是 Python 中用于数据可视化的库,提供了各种图表类型。

安装 Matplotlib

pip install matplotlib

基本图表

import matplotlib.pyplot as plt
import numpy as np

# 折线图
x = np.linspace(0, 10, 100)
y = np.sin(x)

plt.figure(figsize=(10, 6))
plt.plot(x, y, label='sin(x)')
plt.title('Sin Wave')
plt.xlabel('X')
plt.ylabel('Y')
plt.grid(True)
plt.legend()
plt.savefig('sin_wave.png')
plt.show()

# 散点图
x = np.random.randn(100)
y = np.random.randn(100)

plt.figure(figsize=(10, 6))
plt.scatter(x, y, c='red', alpha=0.5)
plt.title('Scatter Plot')
plt.xlabel('X')
plt.ylabel('Y')
plt.grid(True)
plt.savefig('scatter_plot.png')
plt.show()

# 柱状图
categories = ['A', 'B', 'C', 'D', 'E']
values = [10, 20, 15, 25, 30]

plt.figure(figsize=(10, 6))
plt.bar(categories, values, color='blue')
plt.title('Bar Chart')
plt.xlabel('Category')
plt.ylabel('Value')
plt.grid(axis='y')
plt.savefig('bar_chart.png')
plt.show()

# 直方图
data = np.random.randn(1000)

plt.figure(figsize=(10, 6))
plt.hist(data, bins=30, alpha=0.7, color='green')
plt.title('Histogram')
plt.xlabel('Value')
plt.ylabel('Frequency')
plt.grid(axis='y')
plt.savefig('histogram.png')
plt.show()

# 饼图
labels = ['A', 'B', 'C', 'D']
sizes = [15, 30, 45, 10]
colors = ['gold', 'yellowgreen', 'lightcoral', 'lightskyblue']

plt.figure(figsize=(8, 8))
plt.pie(sizes, labels=labels, colors=colors, autopct='%1.1f%%', startangle=90)
plt.title('Pie Chart')
plt.axis('equal') # 确保饼图是圆形
plt.savefig('pie_chart.png')
plt.show()

子图

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 10, 100)
y1 = np.sin(x)
y2 = np.cos(x)
y3 = np.tan(x)
y4 = np.exp(x)

# 创建 2x2 子图
fig, axes = plt.subplots(2, 2, figsize=(12, 10))

# 第一个子图
axes[0, 0].plot(x, y1)
axes[0, 0].set_title('Sin Wave')
axes[0, 0].set_xlabel('X')
axes[0, 0].set_ylabel('Y')
axes[0, 0].grid(True)

# 第二个子图
axes[0, 1].plot(x, y2)
axes[0, 1].set_title('Cos Wave')
axes[0, 1].set_xlabel('X')
axes[0, 1].set_ylabel('Y')
axes[0, 1].grid(True)

# 第三个子图
axes[1, 0].plot(x, y3)
axes[1, 0].set_title('Tan Wave')
axes[1, 0].set_xlabel('X')
axes[1, 0].set_ylabel('Y')
axes[1, 0].grid(True)

# 第四个子图
axes[1, 1].plot(x, y4)
axes[1, 1].set_title('Exponential Function')
axes[1, 1].set_xlabel('X')
axes[1, 1].set_ylabel('Y')
axes[1, 1].grid(True)

plt.tight_layout() # 调整布局
plt.savefig('subplots.png')
plt.show()

高级图表

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

# 热力图
data = np.random.rand(10, 10)

plt.figure(figsize=(10, 8))
heatmap = plt.imshow(data, cmap='viridis')
plt.colorbar(heatmap)
plt.title('Heatmap')
plt.savefig('heatmap.png')
plt.show()

# 箱线图
data = [np.random.randn(100) for _ in range(4)]

plt.figure(figsize=(10, 6))
plt.boxplot(data, labels=['A', 'B', 'C', 'D'])
plt.title('Box Plot')
plt.xlabel('Category')
plt.ylabel('Value')
plt.grid(True)
plt.savefig('box_plot.png')
plt.show()

# 面积图
dates = pd.date_range('2023-01-01', periods=30)
data = pd.DataFrame({
'A': np.random.randn(30).cumsum(),
'B': np.random.randn(30).cumsum(),
'C': np.random.randn(30).cumsum()
}, index=dates)

plt.figure(figsize=(12, 6))
plt.stackplot(data.index, data['A'], data['B'], data['C'], labels=['A', 'B', 'C'])
plt.title('Area Plot')
plt.xlabel('Date')
plt.ylabel('Value')
plt.legend(loc='upper left')
plt.grid(True)
plt.savefig('area_plot.png')
plt.show()

12.5 Seaborn 库

Seaborn 是基于 Matplotlib 的统计数据可视化库,提供了更高级的图表类型。

安装 Seaborn

pip install seaborn

常用图表

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

# 设置风格
sns.set(style="whitegrid")

# 加载示例数据
tips = sns.load_dataset("tips")
iris = sns.load_dataset("iris")

# 散点图(带回归线)
plt.figure(figsize=(10, 6))
sns.regplot(x="total_bill", y="tip", data=tips)
plt.title('Scatter Plot with Regression Line')
plt.savefig('regplot.png')
plt.show()

# 分类散点图
plt.figure(figsize=(10, 6))
sns.swarmplot(x="day", y="total_bill", data=tips)
plt.title('Swarm Plot')
plt.savefig('swarmplot.png')
plt.show()

# 箱线图
plt.figure(figsize=(10, 6))
sns.boxplot(x="day", y="total_bill", data=tips)
plt.title('Box Plot')
plt.savefig('boxplot.png')
plt.show()

# 小提琴图
plt.figure(figsize=(10, 6))
sns.violinplot(x="day", y="total_bill", data=tips)
plt.title('Violin Plot')
plt.savefig('violinplot.png')
plt.show()

# 热力图(相关性矩阵)
corr = iris.corr()
plt.figure(figsize=(10, 8))
sns.heatmap(corr, annot=True, cmap='coolwarm')
plt.title('Correlation Heatmap')
plt.savefig('heatmap_seaborn.png')
plt.show()

# 配对图
plt.figure(figsize=(12, 10))
sns.pairplot(iris, hue="species")
plt.title('Pair Plot')
plt.savefig('pairplot.png')
plt.show()

# 联合分布图
plt.figure(figsize=(10, 6))
sns.jointplot(x="total_bill", y="tip", data=tips)
plt.savefig('jointplot.png')
plt.show()

12.6 综合示例

示例1:销售数据分析

import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

# 生成示例数据
dates = pd.date_range('2023-01-01', periods=365)
sales = pd.DataFrame({
'date': dates,
'sales': np.random.randn(365).cumsum() + 100,
'category': np.random.choice(['A', 'B', 'C', 'D'], 365)
})

# 数据探索
print(sales.head())
print(sales.info())
print(sales.describe())

# 按类别分组
category_sales = sales.groupby('category')['sales'].sum()
print(category_sales)

# 时间序列分析
plt.figure(figsize=(12, 6))
sns.lineplot(x='date', y='sales', data=sales)
plt.title('Sales Trend')
plt.xlabel('Date')
plt.ylabel('Sales')
plt.grid(True)
plt.savefig('sales_trend.png')
plt.show()

# 类别销售分析
plt.figure(figsize=(10, 6))
sns.barplot(x=category_sales.index, y=category_sales.values)
plt.title('Sales by Category')
plt.xlabel('Category')
plt.ylabel('Total Sales')
plt.grid(axis='y')
plt.savefig('sales_by_category.png')
plt.show()

# 月度销售分析
sales['month'] = sales['date'].dt.month
monthly_sales = sales.groupby('month')['sales'].mean()

plt.figure(figsize=(12, 6))
sns.barplot(x=monthly_sales.index, y=monthly_sales.values)
plt.title('Monthly Average Sales')
plt.xlabel('Month')
plt.ylabel('Average Sales')
plt.grid(axis='y')
plt.savefig('monthly_sales.png')
plt.show()

示例2: iris 数据集分析

import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.datasets import load_iris

# 加载数据
iris = load_iris()
df = pd.DataFrame(data=iris.data, columns=iris.feature_names)
df['species'] = iris.target
df['species_name'] = df['species'].map({0: 'setosa', 1: 'versicolor', 2: 'virginica'})

# 数据探索
print(df.head())
print(df.info())
print(df.describe())

# 相关性分析
corr = df.corr()
plt.figure(figsize=(10, 8))
sns.heatmap(corr, annot=True, cmap='coolwarm')
plt.title('Correlation Heatmap')
plt.savefig('iris_correlation.png')
plt.show()

# 配对图
plt.figure(figsize=(12, 10))
sns.pairplot(df, hue='species_name')
plt.title('Pair Plot')
plt.savefig('iris_pairplot.png')
plt.show()

# 箱线图
plt.figure(figsize=(12, 6))
sns.boxplot(data=df.drop(['species', 'species_name'], axis=1))
plt.title('Box Plot of Features')
plt.savefig('iris_boxplot.png')
plt.show()

# 小提琴图
plt.figure(figsize=(12, 6))
sns.violinplot(x='species_name', y='sepal length (cm)', data=df)
plt.title('Sepal Length by Species')
plt.savefig('iris_violinplot.png')
plt.show()

示例3:房价预测数据探索

import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

# 加载数据(使用波士顿房价数据集)
from sklearn.datasets import load_boston
boston = load_boston()
df = pd.DataFrame(data=boston.data, columns=boston.feature_names)
df['PRICE'] = boston.target

# 数据探索
print(df.head())
print(df.info())
print(df.describe())

# 相关性分析
corr = df.corr()
plt.figure(figsize=(12, 10))
sns.heatmap(corr, annot=True, cmap='coolwarm')
plt.title('Correlation Heatmap')
plt.savefig('boston_correlation.png')
plt.show()

# 房价分布
plt.figure(figsize=(10, 6))
sns.histplot(df['PRICE'], bins=30)
plt.title('Price Distribution')
plt.xlabel('Price')
plt.ylabel('Frequency')
plt.savefig('price_distribution.png')
plt.show()

# 特征与房价的关系
plt.figure(figsize=(12, 6))
sns.scatterplot(x='RM', y='PRICE', data=df)
plt.title('Price vs. Number of Rooms')
plt.xlabel('Number of Rooms')
plt.ylabel('Price')
plt.savefig('price_vs_rooms.png')
plt.show()

plt.figure(figsize=(12, 6))
sns.scatterplot(x='LSTAT', y='PRICE', data=df)
plt.title('Price vs. Lower Status Population')
plt.xlabel('Lower Status Population (%)')
plt.ylabel('Price')
plt.savefig('price_vs_lstat.png')
plt.show()

12.7 常见问题和解决方案

问题1:数据导入错误

# 错误示例
import pandas as pd

df = pd.read_csv('data.csv') # 文件不存在或格式错误

# 解决方案
import pandas as pd

try:
df = pd.read_csv('data.csv')
print("Data loaded successfully")
except FileNotFoundError:
print("File not found")
except pd.errors.EmptyDataError:
print("File is empty")
except pd.errors.ParserError:
print("File format error")
except Exception as e:
print(f"Error: {e}")

问题2:内存不足

# 解决方案:分块读取大文件
import pandas as pd

# 分块读取
chunks = []
for chunk in pd.read_csv('large_file.csv', chunksize=10000):
# 处理每个块
chunks.append(chunk)

# 合并所有块
df = pd.concat(chunks)

# 或者使用 dask 处理更大的文件
# pip install dask
import dask.dataframe as dd
df = dd.read_csv('very_large_file.csv')
result = df.groupby('column').mean().compute()

问题3:图表显示问题

# 解决方案
import matplotlib.pyplot as plt

# 确保图表显示
plt.figure()
plt.plot([1, 2, 3], [4, 5, 6])
plt.title('Test Plot')
plt.savefig('test_plot.png') # 保存图表
plt.show() # 显示图表

# 在 Jupyter Notebook 中
# %matplotlib inline

问题4:中文显示问题

# 解决方案
import matplotlib.pyplot as plt

# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei'] # 指定中文字体
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示问题

plt.figure()
plt.plot([1, 2, 3], [4, 5, 6])
plt.title('测试图表') # 中文标题
plt.xlabel('X轴') # 中文标签
plt.ylabel('Y轴') # 中文标签
plt.show()

12.8 练习

  1. NumPy 练习:创建 NumPy 数组,进行各种数学运算
  2. Pandas 练习:读取 CSV 文件,进行数据清洗和分析
  3. Matplotlib 练习:创建各种类型的图表
  4. Seaborn 练习:使用 Seaborn 创建统计图表
  5. 综合练习:分析一个真实的数据集,包括数据清洗、探索、可视化
  6. 挑战练习:使用机器学习算法对数据进行预测分析

12.9 小结

本章我们学习了:

  • 数据分析的基本概念和步骤
  • NumPy 库的使用(数组操作、数学运算)
  • Pandas 库的使用(DataFrame、数据处理、数据清洗)
  • Matplotlib 库的使用(各种图表类型)
  • Seaborn 库的使用(统计数据可视化)
  • 综合示例

现在你已经掌握了 Python 的数据分析技能,可以开始应用这些知识来解决实际问题了!

12.10 进一步学习资源

通过不断练习和实践,你将能够熟练掌握 Python 数据分析技能,为解决实际问题提供有力支持。