Python数据分析与可视化
1. 数据分析概述
作为Java开发者,您可能已经使用过Java的数据处理库,如Apache Commons Math或Java Stream API。Python在数据分析领域有着显著优势,拥有丰富的库和工具,使数据处理、分析和可视化变得简单高效。
数据分析标准流程
flowchart TD
A[数据获取] -->|导入数据| B[数据清洗]
B -->|处理缺失值和异常值| C[探索性数据分析]
C -->|统计分析| D[数据可视化]
D -->|图表展示| E[特征工程]
E -->|创建新特征| F[建模与分析]
F -->|应用算法| G[结果解释]
G -->|业务洞察| H[报告与决策]
style A fill:#f9f,stroke:#333,stroke-width:2px
style B fill:#bbf,stroke:#333,stroke-width:2px
style C fill:#dfd,stroke:#333,stroke-width:2px
style D fill:#fdd,stroke:#333,stroke-width:2px
style E fill:#ddf,stroke:#333,stroke-width:2px
style F fill:#ffd,stroke:#333,stroke-width:2px
style G fill:#dff,stroke:#333,stroke-width:2px
style H fill:#fdf,stroke:#333,stroke-width:2px
Python数据科学生态系统
- NumPy:科学计算的基础库,提供多维数组对象和相关操作
- pandas:基于NumPy的数据分析工具,提供DataFrame对象
- Matplotlib:经典的可视化库,提供类似MATLAB的绘图API
- Seaborn:基于Matplotlib的高级可视化库,提供更美观的图表
- SciPy:科学计算库,提供统计、优化、积分等功能
- scikit-learn:机器学习库,提供各种算法和工具
2. NumPy基础
NumPy是Python科学计算的基础库,提供了多维数组对象、各种派生对象(如掩码数组和矩阵)以及用于数组快速操作的各种API。
安装NumPy
pip install numpy
创建数组
import numpy as np
# 从列表创建数组
arr1 = np.array([1, 2, 3, 4, 5])
print(arr1) # 输出: [1 2 3 4 5]
# 创建二维数组
arr2 = np.array([[1, 2, 3], [4, 5, 6]])
print(arr2)
# 输出:
# [[1 2 3]
# [4 5 6]]
# 创建特殊数组
zeros = np.zeros((3, 4)) # 3x4的全0数组
ones = np.ones((2, 3)) # 2x3的全1数组
identity = np.eye(3) # 3x3的单位矩阵
random_arr = np.random.random((2, 2)) # 2x2的随机数组
# 创建等间隔数组
linear = np.linspace(0, 10, 5) # 0到10之间的5个等间隔数
print(linear) # 输出: [ 0. 2.5 5. 7.5 10. ]
arange = np.arange(0, 10, 2) # 0到10之间步长为2的数组
print(arange) # 输出: [0 2 4 6 8]
数组属性和方法
arr = np.array([[1, 2, 3], [4, 5, 6]])
# 数组属性
print(arr.shape) # 输出: (2, 3) - 数组形状
print(arr.ndim) # 输出: 2 - 数组维度
print(arr.size) # 输出: 6 - 数组元素总数
print(arr.dtype) # 输出: int64 - 数组元素类型
# 数组变形
reshaped = arr.reshape(3, 2) # 将2x3数组变形为3x2
print(reshaped)
# 输出:
# [[1 2]
# [3 4]
# [5 6]]
flattened = arr.flatten() # 将多维数组展平为一维
print(flattened) # 输出: [1 2 3 4 5 6]
# 数组转置
transposed = arr.T
print(transposed)
# 输出:
# [[1 4]
# [2 5]
# [3 6]]
数组索引和切片
arr = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])
# 基本索引
print(arr[0, 0]) # 输出: 1 - 第一行第一列
print(arr[1, 2]) # 输出: 7 - 第二行第三列
# 切片
print(arr[0:2, 1:3])
# 输出:
# [[2 3]
# [6 7]]
# 高级索引
print(arr[[0, 2], [1, 3]]) # 输出: [2 12] - (0,1)和(2,3)位置的元素
# 布尔索引
mask = arr > 5
print(arr[mask]) # 输出: [ 6 7 8 9 10 11 12] - 所有大于5的元素
数组操作
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
# 数组运算
print(a + b) # 输出: [5 7 9] - 元素级加法
print(a - b) # 输出: [-3 -3 -3] - 元素级减法
print(a * b) # 输出: [4 10 18] - 元素级乘法
print(a / b) # 输出: [0.25 0.4 0.5 ] - 元素级除法
print(a ** 2) # 输出: [1 4 9] - 元素级平方
# 矩阵运算
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
print(np.dot(A, B)) # 矩阵乘法
# 输出:
# [[19 22]
# [43 50]]
print(np.linalg.inv(A)) # 矩阵求逆
# 输出:
# [[-2. 1. ]
# [ 1.5 -0.5]]
# 统计函数
arr = np.array([1, 2, 3, 4, 5])
print(np.sum(arr)) # 输出: 15 - 求和
print(np.mean(arr)) # 输出: 3.0 - 平均值
print(np.std(arr)) # 输出: 1.4142135623730951 - 标准差
print(np.min(arr)) # 输出: 1 - 最小值
print(np.max(arr)) # 输出: 5 - 最大值
print(np.median(arr)) # 输出: 3.0 - 中位数
广播机制
NumPy的广播机制允许对不同形状的数组进行算术运算:
# 标量与数组运算
arr = np.array([1, 2, 3, 4])
print(arr * 2) # 输出: [2 4 6 8]
# 不同形状数组的运算
a = np.array([[1, 2, 3], [4, 5, 6]]) # 形状(2, 3)
b = np.array([10, 20, 30]) # 形状(3,)
print(a + b)
# 输出:
# [[11 22 33]
# [14 25 36]]
3. pandas基础
pandas是基于NumPy的数据分析工具,提供了DataFrame等数据结构和数据分析功能。
安装pandas
pip install pandas
Series对象
Series是带有标签的一维数组:
import pandas as pd
# 从列表创建Series
s = pd.Series([1, 3, 5, 7, 9])
print(s)
# 输出:
# 0 1
# 1 3
# 2 5
# 3 7
# 4 9
# dtype: int64
# 指定索引
s = pd.Series([1, 3, 5, 7, 9], index=['a', 'b', 'c', 'd', 'e'])
print(s)
# 输出:
# a 1
# b 3
# c 5
# d 7
# e 9
# dtype: int64
# 从字典创建Series
data = {'a': 1, 'b': 3, 'c': 5, 'd': 7, 'e': 9}
s = pd.Series(data)
print(s)
# 输出与上面相同
# 访问元素
print(s['a']) # 输出: 1 - 通过标签访问
print(s[0]) # 输出: 1 - 通过位置访问
print(s[['a', 'c', 'e']]) # 访问多个元素
# 输出:
# a 1
# c 5
# e 9
# dtype: int64
DataFrame对象
DataFrame是带有标签的二维表格:
# 从字典创建DataFrame
data = {
'Name': ['John', 'Anna', 'Peter', 'Linda'],
'Age': [28, 34, 29, 42],
'City': ['New York', 'Paris', 'Berlin', 'London']
}
df = pd.DataFrame(data)
print(df)
# 输出:
# Name Age City
# 0 John 28 New York
# 1 Anna 34 Paris
# 2 Peter 29 Berlin
# 3 Linda 42 London
# 指定索引
df = pd.DataFrame(data, index=['p1', 'p2', 'p3', 'p4'])
print(df)
# 输出:
# Name Age City
# p1 John 28 New York
# p2 Anna 34 Paris
# p3 Peter 29 Berlin
# p4 Linda 42 London
# 从CSV文件创建DataFrame
# df = pd.read_csv('data.csv')
# 从Excel文件创建DataFrame
# df = pd.read_excel('data.xlsx')
DataFrame基本操作
# 查看数据
print(df.head(2)) # 前2行
print(df.tail(2)) # 后2行
print(df.info()) # 数据信息
print(df.describe()) # 数值列的统计摘要
# 访问数据
print(df['Name']) # 访问单列
print(df[['Name', 'Age']]) # 访问多列
# loc - 基于标签的索引
print(df.loc['p1']) # 访问单行
print(df.loc['p1':'p3']) # 访问多行
print(df.loc['p1', 'Name']) # 访问特定元素
print(df.loc['p1':'p3', ['Name', 'City']]) # 访问子DataFrame
# iloc - 基于位置的索引
print(df.iloc[0]) # 访问第一行
print(df.iloc[0:3]) # 访问前三行
print(df.iloc[0, 0]) # 访问第一行第一列
print(df.iloc[0:3, [0, 2]]) # 访问子DataFrame
# 条件过滤
print(df[df['Age'] > 30]) # 年龄大于30的行
print(df[(df['Age'] > 30) & (df['City'] == 'London')]) # 组合条件
数据处理
# 添加列
df['Country'] = ['USA', 'France', 'Germany', 'UK']
print(df)
# 修改值
df.loc['p1', 'Age'] = 29
print(df)
# 删除列
df_copy = df.copy()
df_copy = df_copy.drop('Country', axis=1)
print(df_copy)
# 删除行
df_copy = df.copy()
df_copy = df_copy.drop('p1', axis=0)
print(df_copy)
# 处理缺失值
df_missing = pd.DataFrame({
'A': [1, 2, None, 4],
'B': [5, None, 7, 8],
'C': [9, 10, 11, None]
})
print(df_missing.isnull()) # 检查缺失值
print(df_missing.fillna(0)) # 填充缺失值
print(df_missing.dropna()) # 删除包含缺失值的行
print(df_missing.dropna(axis=1)) # 删除包含缺失值的列
数据聚合和分组
# 创建示例数据
data = {
'Category': ['A', 'B', 'A', 'B', 'A', 'B'],
'Value1': [10, 20, 30, 40, 50, 60],
'Value2': [100, 200, 300, 400, 500, 600]
}
df = pd.DataFrame(data)
print(df)
# 分组聚合
grouped = df.groupby('Category')
print(grouped.sum()) # 每个类别的总和
print(grouped.mean()) # 每个类别的平均值
print(grouped.count()) # 每个类别的计数
# 多种聚合
print(grouped.agg(['sum', 'mean', 'count']))
# 自定义聚合
print(grouped.agg({
'Value1': ['sum', 'mean'],
'Value2': ['min', 'max']
}))
数据合并和连接
# 创建示例数据
df1 = pd.DataFrame({
'ID': [1, 2, 3, 4],
'Name': ['John', 'Anna', 'Peter', 'Linda'],
'Age': [28, 34, 29, 42]
})
df2 = pd.DataFrame({
'ID': [1, 2, 3, 5],
'City': ['New York', 'Paris', 'Berlin', 'Rome'],
'Salary': [50000, 60000, 55000, 65000]
})
# 合并(merge) - 类似SQL连接
merged = pd.merge(df1, df2, on='ID')
print(merged)
# 不同类型的合并
left_merge = pd.merge(df1, df2, on='ID', how='left')
right_merge = pd.merge(df1, df2, on='ID', how='right')
outer_merge = pd.merge(df1, df2, on='ID', how='outer')
# 连接(concat) - 按行或列拼接
concat_rows = pd.concat([df1, df1]) # 按行连接
concat_cols = pd.concat([df1, df2], axis=1) # 按列连接
# 连接(join) - 基于索引的合并
df1_indexed = df1.set_index('ID')
df2_indexed = df2.set_index('ID')
joined = df1_indexed.join(df2_indexed)
print(joined)
数据透视表
# 创建示例数据
data = {
'Date': pd.date_range(start='2023-01-01', periods=6),
'Product': ['A', 'B', 'A', 'B', 'A', 'B'],
'Region': ['East', 'East', 'West', 'West', 'East', 'West'],
'Sales': [100, 200, 150, 250, 120, 220]
}
df = pd.DataFrame(data)
print(df)
# 创建透视表
pivot = pd.pivot_table(
df,
values='Sales',
index='Product',
columns='Region',
aggfunc='sum'
)
print(pivot)
# 多级索引透视表
pivot_multi = pd.pivot_table(
df,
values='Sales',
index=['Product', pd.Grouper(key='Date', freq='M')],
columns='Region',
aggfunc='sum'
)
print(pivot_multi)
4. Matplotlib基础
Matplotlib是Python最流行的可视化库,提供了类似MATLAB的绘图API。
安装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, 'b-', label='sin(x)') # 'b-'表示蓝色实线
# 添加标题和标签
plt.title('Sine Wave')
plt.xlabel('x')
plt.ylabel('sin(x)')
# 添加图例
plt.legend()
# 添加网格
plt.grid(True)
# 显示图形
plt.show()
多子图
# 创建数据
x = np.linspace(0, 10, 100)
y1 = np.sin(x)
y2 = np.cos(x)
y3 = np.tan(x)
y4 = x**2
# 创建2x2的子图
fig, axes = plt.subplots(2, 2, figsize=(12, 10))
# 在每个子图上绘制不同的图形
axes[0, 0].plot(x, y1)
axes[0, 0].set_title('Sine')
axes[0, 1].plot(x, y2)
axes[0, 1].set_title('Cosine')
axes[1, 0].plot(x, y3)
axes[1, 0].set_title('Tangent')
axes[1, 1].plot(x, y4)
axes[1, 1].set_title('Square')
# 调整子图之间的间距
plt.tight_layout()
# 显示图形
plt.show()
常见图表类型
线图
x = np.linspace(0, 10, 100)
y1 = np.sin(x)
y2 = np.cos(x)
plt.figure(figsize=(10, 6))
plt.plot(x, y1, 'b-', label='sin(x)')
plt.plot(x, y2, 'r--', label='cos(x)')
plt.title('Sine and Cosine Waves')
plt.xlabel('x')
plt.ylabel('y')
plt.legend()
plt.grid(True)
plt.show()
散点图
# 创建随机数据
np.random.seed(42)
x = np.random.rand(50)
y = np.random.rand(50)
colors = np.random.rand(50)
sizes = 1000 * np.random.rand(50)
plt.figure(figsize=(10, 6))
plt.scatter(x, y, c=colors, s=sizes, alpha=0.5, cmap='viridis')
plt.colorbar() # 添加颜色条
plt.title('Scatter Plot')
plt.xlabel('x')
plt.ylabel('y')
plt.show()
柱状图
categories = ['A', 'B', 'C', 'D', 'E']
values = [25, 40, 30, 55, 15]
plt.figure(figsize=(10, 6))
plt.bar(categories, values, color='skyblue')
plt.title('Bar Chart')
plt.xlabel('Category')
plt.ylabel('Value')
plt.show()
直方图
# 创建正态分布数据
data = np.random.normal(0, 1, 1000)
plt.figure(figsize=(10, 6))
plt.hist(data, bins=30, alpha=0.7, color='skyblue', edgecolor='black')
plt.title('Histogram')
plt.xlabel('Value')
plt.ylabel('Frequency')
plt.grid(True, alpha=0.3)
plt.show()
饼图
labels = ['A', 'B', 'C', 'D', 'E']
sizes = [15, 30, 25, 10, 20]
explode = (0, 0.1, 0, 0, 0) # 突出第二个扇形
plt.figure(figsize=(10, 6))
plt.pie(sizes, explode=explode, labels=labels, autopct='%1.1f%%',
shadow=True, startangle=90)
plt.axis('equal') # 保证饼图是圆的
plt.title('Pie Chart')
plt.show()
箱线图
# 创建多组数据
data = [np.random.normal(0, std, 100) for std in range(1, 6)]
plt.figure(figsize=(10, 6))
plt.boxplot(data, patch_artist=True)
plt.title('Box Plot')
plt.xlabel('Group')
plt.ylabel('Value')
plt.grid(True, alpha=0.3)
plt.show()
热图
import seaborn as sns
# 创建相关矩阵
corr_matrix = np.corrcoef(np.random.randn(10, 5))
plt.figure(figsize=(10, 8))
sns.heatmap(corr_matrix, annot=True, cmap='coolwarm', vmin=-1, vmax=1)
plt.title('Correlation Matrix')
plt.show()
自定义图表样式
# 设置全局样式
plt.style.use('seaborn-darkgrid')
# 创建数据
x = np.linspace(0, 10, 100)
y = np.sin(x)
# 创建图形和坐标轴
fig, ax = plt.subplots(figsize=(10, 6))
# 绘制数据
ax.plot(x, y, linewidth=2, color='#ff7f0e', linestyle='-', marker='o',
markersize=5, markevery=10, label='sin(x)')
# 设置标题和标签
ax.set_title('Customized Sine Wave', fontsize=16, fontweight='bold')
ax.set_xlabel('x', fontsize=12)
ax.set_ylabel('sin(x)', fontsize=12)
# 设置坐标轴范围
ax.set_xlim(0, 10)
ax.set_ylim(-1.5, 1.5)
# 添加网格
ax.grid(True, linestyle='--', alpha=0.7)
# 添加图例
ax.legend(fontsize=12)
# 添加水平线和垂直线
ax.axhline(y=0, color='k', linestyle='-', alpha=0.3)
ax.axvline(x=5, color='r', linestyle='--', alpha=0.3)
# 添加文本注释
ax.text(5, 0.5, 'Important Point', fontsize=12,
ha='center', va='center', bbox=dict(facecolor='white', alpha=0.5))
# 添加箭头注释
ax.annotate('Local Maximum', xy=(1.5, 1), xytext=(2.5, 1.3),
arrowprops=dict(facecolor='black', shrink=0.05))
# 保存图形
# plt.savefig('custom_plot.png', dpi=300, bbox_inches='tight')
# 显示图形
plt.show()
5. Seaborn基础
Seaborn是基于Matplotlib的高级可视化库,提供了更美观的图表和更简洁的API。
安装Seaborn
pip install seaborn
基本使用
import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
# 设置样式
sns.set_theme(style="whitegrid")
# 加载示例数据集
tips = sns.load_dataset("tips")
print(tips.head())
常见图表类型
关系图
# 散点图
plt.figure(figsize=(10, 6))
sns.scatterplot(x="total_bill", y="tip", hue="time", size="size", data=tips)
plt.title("Relationship between Total Bill and Tip")
plt.show()
# 线图
plt.figure(figsize=(10, 6))
sns.lineplot(x="total_bill", y="tip", hue="time", data=tips)
plt.title("Relationship between Total Bill and Tip")
plt.show()
# 回归图
plt.figure(figsize=(10, 6))
sns.regplot(x="total_bill", y="tip", data=tips)
plt.title("Regression between Total Bill and Tip")
plt.show()
# 成对关系图
plt.figure(figsize=(12, 10))
sns.pairplot(tips, hue="time")
plt.suptitle("Pairwise Relationships", y=1.02)
plt.show()
分布图
# 直方图
plt.figure(figsize=(10, 6))
sns.histplot(data=tips, x="total_bill", kde=True)
plt.title("Distribution of Total Bill")
plt.show()
# 核密度估计图
plt.figure(figsize=(10, 6))
sns.kdeplot(data=tips, x="total_bill", hue="time", fill=True)
plt.title("Density of Total Bill by Time")
plt.show()
# 箱线图
plt.figure(figsize=(10, 6))
sns.boxplot(x="day", y="total_bill", data=tips)
plt.title("Total Bill by Day")
plt.show()
# 小提琴图
plt.figure(figsize=(10, 6))
sns.violinplot(x="day", y="total_bill", hue="sex", data=tips, split=True)
plt.title("Total Bill by Day and Sex")
plt.show()
分类图
# 条形图
plt.figure(figsize=(10, 6))
sns.barplot(x="day", y="total_bill", data=tips)
plt.title("Average Total Bill by Day")
plt.show()
# 计数图
plt.figure(figsize=(10, 6))
sns.countplot(x="day", hue="sex", data=tips)
plt.title("Count of Bills by Day and Sex")
plt.show()
# 点图
plt.figure(figsize=(10, 6))
sns.pointplot(x="day", y="total_bill", hue="time", data=tips)
plt.title("Average Total Bill by Day and Time")
plt.show()
矩阵图
# 热图
corr = tips.corr()
plt.figure(figsize=(10, 8))
sns.heatmap(corr, annot=True, cmap="coolwarm")
plt.title("Correlation Matrix")
plt.show()
# 聚类图
plt.figure(figsize=(12, 10))
sns.clustermap(corr, annot=True, cmap="coolwarm")
plt.title("Clustered Correlation Matrix")
plt.show()
高级自定义
# 创建FacetGrid进行多图绘制
g = sns.FacetGrid(tips, col="time", row="sex", height=4, aspect=1.5)
g.map_dataframe(sns.scatterplot, x="total_bill", y="tip")
g.add_legend()
plt.show()
# 使用catplot创建分类图
g = sns.catplot(
data=tips, kind="bar",
x="day", y="total_bill", hue="sex",
palette="dark", alpha=.6, height=6, aspect=1.5
)
g.set_axis_labels("Day", "Total Bill")
g.legend.set_title("Sex")
plt.show()
# 使用jointplot创建联合分布图
plt.figure(figsize=(10, 8))
sns.jointplot(
data=tips,
x="total_bill", y="tip",
kind="reg", truncate=False,
xlim=(0, 60), ylim=(0, 12),
color="m", height=8
)
plt.show()
6. 数据分析案例
让我们通过一个完整的案例来整合NumPy、pandas和可视化库的使用。
案例:销售数据分析
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
# 设置样式
plt.style.use('seaborn-whitegrid')
sns.set_palette("deep")
# 创建模拟销售数据
np.random.seed(42)
# 日期范围
dates = pd.date_range(start='2023-01-01', end='2023-12-31', freq='D')
# 产品类别
categories = ['Electronics', 'Clothing', 'Food', 'Books', 'Home']
# 创建数据框
data = []
for date in dates:
for category in categories:
# 添加季节性和周末效应
seasonal_factor = 1.0 + 0.3 * np.sin(2 * np.pi * date.dayofyear / 365)
weekend_factor = 1.5 if date.dayofweek >= 5 else 1.0
# 基础销售量
base_sales = np.random.normal(loc=100, scale=20)
# 应用因子
sales = base_sales * seasonal_factor * weekend_factor
# 添加一些随机促销效应
if np.random.random() < 0.05: # 5%的概率有促销
sales *= np.random.uniform(1.5, 2.5)
# 添加到数据列表
data.append({
'Date': date,
'Category': category,
'Sales': max(0, sales), # 确保销售额非负
'Weekday': date.day_name(),
'Month': date.month_name(),
'Quarter': f"Q{(date.month-1)//3+1}"
})
# 创建DataFrame
sales_df = pd.DataFrame(data)
print(sales_df.head())
# 1. 数据探索
print("\n基本统计信息:")
print(sales_df.describe())
print("\n按类别分组的销售统计:")
category_stats = sales_df.groupby('Category')['Sales'].agg(['count', 'mean', 'std', 'min', 'max'])
print(category_stats)
print("\n按月份分组的销售统计:")
monthly_stats = sales_df.groupby('Month')['Sales'].mean().reindex([
'January', 'February', 'March', 'April', 'May', 'June',
'July', 'August', 'September', 'October', 'November', 'December'
])
print(monthly_stats)
# 2. 可视化分析
# 设置图形大小
plt.figure(figsize=(15, 10))
# 2.1 总销售趋势
plt.subplot(2, 2, 1)
daily_sales = sales_df.groupby('Date')['Sales'].sum()
plt.plot(daily_sales.index, daily_sales.values)
plt.title('Daily Total Sales')
plt.xlabel('Date')
plt.ylabel('Sales')
plt.xticks(rotation=45)
plt.tight_layout()
# 2.2 类别销售比较
plt.subplot(2, 2, 2)
category_sales = sales_df.groupby('Category')['Sales'].sum()
sns.barplot(x=category_sales.index, y=category_sales.values)
plt.title('Total Sales by Category')
plt.xlabel('Category')
plt.ylabel('Sales')
plt.xticks(rotation=45)
plt.tight_layout()
# 2.3 月度销售趋势
plt.subplot(2, 2, 3)
monthly_sales = sales_df.groupby('Month')['Sales'].sum()
monthly_sales = monthly_sales.reindex([
'January', 'February', 'March', 'April', 'May', 'June',
'July', 'August', 'September', 'October', 'November', 'December'
])
sns.barplot(x=monthly_sales.index, y=monthly_sales.values)
plt.title('Total Sales by Month')
plt.xlabel('Month')
plt.ylabel('Sales')
plt.xticks(rotation=45)
plt.tight_layout()
# 2.4 星期几销售分布
plt.subplot(2, 2, 4)
weekday_sales = sales_df.groupby('Weekday')['Sales'].sum()
weekday_sales = weekday_sales.reindex([
'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'
])
sns.barplot(x=weekday_sales.index, y=weekday_sales.values)
plt.title('Total Sales by Weekday')
plt.xlabel('Weekday')
plt.ylabel('Sales')
plt.xticks(rotation=45)
plt.tight_layout()
plt.tight_layout(pad=3.0)
plt.show()
# 3. 高级分析
# 3.1 热图:月份和类别的销售关系
plt.figure(figsize=(12, 8))
month_category_sales = sales_df.pivot_table(
index='Month',
columns='Category',
values='Sales',
aggfunc='sum'
)
month_category_sales = month_category_sales.reindex([
'January', 'February', 'March', 'April', 'May', 'June',
'July', 'August', 'September', 'October', 'November', 'December'
])
sns.heatmap(month_category_sales, annot=True, fmt='.0f', cmap='YlGnBu')
plt.title('Sales Heatmap: Month vs Category')
plt.tight_layout()
plt.show()
# 3.2 箱线图:各类别销售分布
plt.figure(figsize=(12, 6))
sns.boxplot(x='Category', y='Sales', data=sales_df)
plt.title('Sales Distribution by Category')
plt.xlabel('Category')
plt.ylabel('Sales')
plt.tight_layout()
plt.show()
# 3.3 小提琴图:季度销售分布
plt.figure(figsize=(12, 6))
sns.violinplot(x='Quarter', y='Sales', data=sales_df, inner='quartile')
plt.title('Sales Distribution by Quarter')
plt.xlabel('Quarter')
plt.ylabel('Sales')
plt.tight_layout()
plt.show()
# 3.4 成对关系图:数值变量之间的关系
# 为了演示,添加一些额外的数值特征
sales_df['UnitPrice'] = np.random.uniform(10, 100, size=len(sales_df))
sales_df['Quantity'] = sales_df['Sales'] / sales_df['UnitPrice']
plt.figure(figsize=(10, 8))
sns.pairplot(sales_df[['Sales', 'UnitPrice', 'Quantity', 'Date']],
kind='scatter', diag_kind='kde')
plt.suptitle('Relationships Between Numerical Variables', y=1.02)
plt.tight_layout()
plt.show()
# 4. 时间序列分析
# 按日期和类别分组的销售趋势
plt.figure(figsize=(15, 10))
for category in categories:
category_data = sales_df[sales_df['Category'] == category]
daily_sales = category_data.groupby('Date')['Sales'].sum()
plt.plot(daily_sales.index, daily_sales.values, label=category)
plt.title('Daily Sales Trend by Category')
plt.xlabel('Date')
plt.ylabel('Sales')
plt.legend()
plt.grid(True)
plt.tight_layout()
plt.show()
# 5. 结论和见解
# (这部分通常是基于分析结果撰写的文本,这里只是示例)
print("\n分析结论:")
print("1. 销售额在周末显著高于工作日")
print("2. 电子产品类别的销售额最高,但也有最大的波动")
print("3. 销售额在第四季度达到峰值,可能是由于假日购物季")
print("4. 所有类别都显示出明显的季节性模式")
7. 实用数据分析技巧
数据清洗
# 处理缺失值
def handle_missing_values(df):
# 检查缺失值
print("缺失值统计:")
print(df.isnull().sum())
# 填充数值列的缺失值
numeric_cols = df.select_dtypes(include=['number']).columns
for col in numeric_cols:
df[col].fillna(df[col].median(), inplace=True)
# 填充分类列的缺失值
categorical_cols = df.select_dtypes(include=['object']).columns
for col in categorical_cols:
df[col].fillna(df[col].mode()[0], inplace=True)
return df
# 处理异常值
def handle_outliers(df, columns, method='iqr'):
for col in columns:
if method == 'iqr':
# IQR方法
Q1 = df[col].quantile(0.25)
Q3 = df[col].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
# 替换异常值为边界值
df[col] = np.where(df[col] < lower_bound, lower_bound, df[col])
df[col] = np.where(df[col] > upper_bound, upper_bound, df[col])
elif method == 'zscore':
# Z-score方法
from scipy import stats
z_scores = stats.zscore(df[col])
abs_z_scores = np.abs(z_scores)
filtered_entries = (abs_z_scores < 3) # 保留z-score小于3的值
df[col] = np.where(abs_z_scores >= 3, df[col].median(), df[col])
return df
# 特征工程
def feature_engineering(df):
# 日期特征
if 'Date' in df.columns and pd.api.types.is_datetime64_any_dtype(df['Date']):
df['Year'] = df['Date'].dt.year
df['Month'] = df['Date'].dt.month
df['Day'] = df['Date'].dt.day
df['DayOfWeek'] = df['Date'].dt.dayofweek
df['IsWeekend'] = df['DayOfWeek'].isin([5, 6]).astype(int)
# 分类特征编码
categorical_cols = df.select_dtypes(include=['object']).columns
for col in categorical_cols:
df[f'{col}_encoded'] = df[col].astype('category').cat.codes
return df
数据分析流程
def analyze_dataset(df, target_column=None):
"""
对数据集进行全面分析
参数:
df: pandas DataFrame, 要分析的数据集
target_column: str, 目标变量列名(如果有)
"""
print("="*50)
print("数据集基本信息")
print("="*50)
# 1. 基本信息
print(f"数据集形状: {df.shape}")
print(f"数据集列: {df.columns.tolist()}")
print("\n数据类型:")
print(df.dtypes)
# 2. 缺失值分析
print("\n缺失值统计:")
missing = df.isnull().sum()
missing_percent = (missing / len(df)) * 100
missing_info = pd.DataFrame({
'缺失值数量': missing,
'缺失值百分比': missing_percent
})
print(missing_info[missing_info['缺失值数量'] > 0])
# 3. 描述性统计
print("\n数值列描述性统计:")
print(df.describe().T)
print("\n分类列描述性统计:")
categorical_cols = df.select_dtypes(include=['object']).columns
for col in categorical_cols:
print(f"\n{col} 值计数:")
print(df[col].value_counts().head())
# 4. 相关性分析
if len(df.select_dtypes(include=['number']).columns) > 1:
print("\n相关性矩阵:")
corr = df.select_dtypes(include=['number']).corr()
print(corr)
# 可视化相关性矩阵
plt.figure(figsize=(12, 10))
sns.heatmap(corr, annot=True, cmap='coolwarm', vmin=-1, vmax=1)
plt.title('Correlation Matrix')
plt.show()
# 5. 目标变量分析(如果提供)
if target_column and target_column in df.columns:
print(f"\n目标变量 '{target_column}' 分析:")
if pd.api.types.is_numeric_dtype(df[target_column]):
print("\n目标变量统计:")
print(df[target_column].describe())
# 目标变量分布
plt.figure(figsize=(12, 6))
plt.subplot(1, 2, 1)
sns.histplot(df[target_column], kde=True)
plt.title(f'{target_column} Distribution')
plt.subplot(1, 2, 2)
sns.boxplot(y=df[target_column])
plt.title(f'{target_column} Box Plot')
plt.tight_layout()
plt.show()
# 与其他数值变量的关系
numeric_cols = df.select_dtypes(include=['number']).columns
numeric_cols = [col for col in numeric_cols if col != target_column]
if len(numeric_cols) > 0:
plt.figure(figsize=(15, len(numeric_cols) * 4))
for i, col in enumerate(numeric_cols, 1):
plt.subplot(len(numeric_cols), 1, i)
sns.scatterplot(x=col, y=target_column, data=df)
plt.title(f'{col} vs {target_column}')
plt.tight_layout()
plt.show()
else: # 分类目标变量
print("\n目标变量计数:")
print(df[target_column].value_counts())
# 目标变量分布
plt.figure(figsize=(10, 6))
sns.countplot(x=target_column, data=df)
plt.title(f'{target_column} Distribution')
plt.xticks(rotation=45)
plt.show()
print("="*50)
print("分析完成")
print("="*50)
8. 练习:数据分析项目
练习1:股票数据分析
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from datetime import datetime, timedelta
# 设置样式
plt.style.use('seaborn-whitegrid')
sns.set_palette("deep")
# 创建模拟股票数据
def generate_stock_data(tickers, start_date, end_date):
date_range = pd.date_range(start=start_date, end=end_date, freq='B') # 工作日
data = []
for ticker in tickers:
# 初始价格
price = np.random.uniform(50, 200)
# 波动参数
volatility = np.random.uniform(0.01, 0.03)
drift = np.random.uniform(0.0001, 0.0005)
for date in date_range:
# 生成价格变动
daily_return = np.random.normal(drift, volatility)
price *= (1 + daily_return)
# 生成交易量
volume = int(np.random.normal(1000000, 300000))
volume = max(100000, volume) # 确保交易量为正
# 计算开盘价、最高价、最低价
open_price = price * np.random.uniform(0.99, 1.01)
high_price = price * np.random.uniform(1.0, 1.03)
low_price = price * np.random.uniform(0.97, 1.0)
data.append({
'Date': date,
'Ticker': ticker,
'Open': open_price,
'High': high_price,
'Low': low_price,
'Close': price,
'Volume': volume
})
return pd.DataFrame(data)
# 生成数据
tickers = ['AAPL', 'MSFT', 'GOOGL', 'AMZN', 'FB']
start_date = '2022-01-01'
end_date = '2022-12-31'
stock_df = generate_stock_data(tickers, start_date, end_date)
# 数据分析任务
# 1. 计算每只股票的日收益率
stock_df['DailyReturn'] = stock_df.groupby('Ticker')['Close'].pct_change()
# 2. 计算每只股票的累积收益率
stock_df['CumulativeReturn'] = stock_df.groupby('Ticker')['Close'].apply(
lambda x: x / x.iloc[0] - 1
)
# 3. 计算每只股票的波动率(标准差)
volatility = stock_df.groupby('Ticker')['DailyReturn'].std() * np.sqrt(252) # 年化
print("年化波动率:")
print(volatility)
# 4. 可视化股价走势
plt.figure(figsize=(15, 8))
for ticker in tickers:
ticker_data = stock_df[stock_df['Ticker'] == ticker]
plt.plot(ticker_data['Date'], ticker_data['Close'], label=ticker)
plt.title('Stock Price Trends')
plt.xlabel('Date')
plt.ylabel('Price')
plt.legend()
plt.grid(True)
plt.show()
# 5. 可视化累积收益率
plt.figure(figsize=(15, 8))
for ticker in tickers:
ticker_data = stock_df[stock_df['Ticker'] == ticker]
plt.plot(ticker_data['Date'], ticker_data['CumulativeReturn'], label=ticker)
plt.title('Cumulative Returns')
plt.xlabel('Date')
plt.ylabel('Return')
plt.legend()
plt.grid(True)
plt.show()
# 6. 相关性分析
pivot_close = stock_df.pivot_table(index='Date', columns='Ticker', values='Close')
returns = pivot_close.pct_change().dropna()
plt.figure(figsize=(12, 10))
sns.heatmap(returns.corr(), annot=True, cmap='coolwarm', vmin=-1, vmax=1)
plt.title('Correlation of Stock Returns')
plt.show()
# 7. 交易量分析
plt.figure(figsize=(15, 8))
sns.boxplot(x='Ticker', y='Volume', data=stock_df)
plt.title('Trading Volume by Stock')
plt.ylabel('Volume')
plt.yscale('log') # 对数刻度更容易比较
plt.show()
# 8. 风险与收益分析
mean_returns = returns.mean() * 252 # 年化收益率
std_returns = returns.std() * np.sqrt(252) # 年化标准差
plt.figure(figsize=(12, 8))
plt.scatter(std_returns, mean_returns, s=100)
# 添加标签
for i, ticker in enumerate(tickers):
plt.annotate(ticker, (std_returns[i], mean_returns[i]),
xytext=(5, 5), textcoords='offset points')
plt.xlabel('Risk (Annual Standard Deviation)')
plt.ylabel('Expected Return (Annual)')
plt.title('Risk vs. Return')
plt.grid(True)
plt.show()
9. 今日总结
- NumPy是Python科学计算的基础库,提供了多维数组对象和各种数学函数
- pandas是强大的数据分析工具,提供了DataFrame等数据结构和数据处理功能
- Matplotlib是Python的基础可视化库,提供了类似MATLAB的绘图API
- Seaborn是基于Matplotlib的高级可视化库,提供了更美观的图表和更简洁的API
- 数据分析通常包括数据清洗、探索性分析、可视化和高级分析等步骤
- Python的数据科学生态系统非常丰富,可以满足各种数据分析需求
10. 明日预告
明天我们将学习Python的测试与调试技术,包括单元测试、集成测试、调试工具和性能分析等内容。这些技能对于确保代码质量和提高开发效率至关重要。