引言:为什么Python是数据分析的首选工具
在当今数据驱动的世界中,Python已经成为数据分析领域的绝对王者。根据2023年Kaggle开发者调查,超过90%的数据科学家将Python作为主要编程语言。Python之所以如此受欢迎,是因为它拥有丰富的库生态系统、简洁的语法以及强大的社区支持。
想象一下,你是一家电商公司的数据分析师,每天需要处理数百万条用户行为数据。使用Python,你可以轻松地从数据库中提取数据、清洗脏数据、进行复杂的统计分析,并生成直观的可视化图表。这一切都可以在同一个环境中完成,无需在多个工具之间切换。
Python在数据分析中的优势主要体现在以下几个方面:
- 易学性:即使是编程新手也能快速上手
- 丰富的库:Pandas、NumPy、Matplotlib等库提供了强大的功能
- 可扩展性:可以处理从几KB到几TB的数据
- 集成性:可以与机器学习、深度学习框架无缝对接
基础篇:搭建Python数据分析环境
1. 安装和配置
要开始Python数据分析之旅,首先需要搭建合适的开发环境。推荐使用Anaconda发行版,它预装了所有必要的库和工具。
# 下载并安装Anaconda
# 访问 https://www.anaconda.com/products/distribution 下载适合你操作系统的版本
# 安装完成后,打开终端或命令提示符,创建新的conda环境
conda create -n data_analysis python=3.9
# 激活新环境
conda activate data_analysis
# 安装核心数据分析库
conda install pandas numpy matplotlib seaborn scikit-learn
2. Jupyter Notebook入门
Jupyter Notebook是数据分析师的瑞士军刀,它允许你以交互式的方式编写和运行代码。
# 创建你的第一个Jupyter Notebook
# 在终端输入以下命令启动Jupyter
jupyter notebook
# 在Notebook中尝试以下代码
import pandas as pd
import numpy as np
# 创建一个简单的DataFrame
data = {
'姓名': ['张三', '李四', '王五'],
'年龄': [25, 30, 35],
'薪资': [8000, 12000, 15000]
}
df = pd.DataFrame(data)
print(df)
输出结果:
姓名 年龄 薪资
0 张三 25 8000
1 李四 30 12000
2 王五 35 15000
核心篇:Pandas数据处理实战
1. 数据读取与写入
Pandas支持多种数据格式的读写操作,这是数据分析的第一步。
import pandas as pd
# 读取CSV文件
df_csv = pd.read_csv('sales_data.csv')
# 读取Excel文件
df_excel = pd.read_excel('customer_info.xlsx', sheet_name='Sheet1')
# 读取SQL数据库(需要安装sqlalchemy)
from sqlalchemy import create_engine
engine = create_engine('mysql+pymysql://username:password@localhost/dbname')
df_sql = pd.read_sql('SELECT * FROM orders', engine)
# 将数据写入不同格式
df_csv.to_csv('processed_data.csv', index=False)
df_excel.to_excel('report.xlsx', sheet_name='Summary')
2. 数据清洗与预处理
真实世界的数据往往是脏乱的,数据清洗是分析前的必要步骤。
# 示例:处理销售数据中的常见问题
import pandas as pd
import numpy as np
# 创建包含问题的示例数据
data = {
'订单ID': [1, 2, 3, 4, 5, 6],
'产品名称': ['手机', '电脑', '平板', '手机', '电脑', None],
'销售日期': ['2023-01-01', '2023-01-02', '2023-01-03', '2023-01-01', '2023-01-02', '2023-01-03'],
'销售额': [2999, 5999, 1999, 2999, 5999, 999],
'客户地区': ['北京', '上海', '广州', '北京', None, '深圳']
}
df = pd.DataFrame(data)
# 1. 处理缺失值
print("原始数据:")
print(df)
# 检查缺失值
print("\n缺失值统计:")
print(df.isnull().sum())
# 填充缺失值
df['产品名称'].fillna('未知产品', inplace=True)
df['客户地区'].fillna('未知地区', inplace=True)
# 2. 处理重复值
df = df.drop_duplicates()
# 3. 数据类型转换
df['销售日期'] = pd.to_datetime(df['销售日期'])
# 4. 异常值处理(使用IQR方法)
Q1 = df['销售额'].quantile(0.25)
Q3 = df['销售额'].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
# 过滤异常值
df = df[(df['销售额'] >= lower_bound) & (df['销售额'] <= upper_bound)]
print("\n清洗后的数据:")
print(df)
3. 数据筛选与查询
Pandas提供了强大的数据筛选功能,类似于SQL查询。
# 继续使用上面的df
# 基本筛选
high_sales = df[df['销售额'] > 3000] # 销售额大于3000的记录
# 多条件筛选
beijing_high = df[(df['客户地区'] == '北京') & (df['销售额'] > 2500)]
# 使用query方法
result = df.query('销售额 > 2500 and 客户地区 in ["北京", "上海"]')
# 使用isin进行集合筛选
regions = ['北京', '上海']
regional_sales = df[df['客户地区'].isin(regions)]
# 字符串筛选
phone_sales = df[df['产品名称'].str.contains('手机')]
# 日期筛选(假设df['销售日期']已经是datetime类型)
january_sales = df[df['销售日期'].dt.month == 1]
4. 数据分组与聚合
分组聚合是数据分析中最常用的操作之一。
# 创建更复杂的销售数据示例
np.random.seed(42)
dates = pd.date_range('2023-01-01', '2023-12-31', freq='D')
products = ['手机', '电脑', '平板', '耳机']
regions = ['北京', '上海', '广州', '深圳', '杭州']
data = {
'日期': np.random.choice(dates, 500),
'产品': np.random.choice(products, 500),
'地区': np.random.choice(regions, 500),
'销售额': np.random.randint(1000, 10000, 500),
'数量': np.random.randint(1, 10, 500)
}
sales_df = pd.DataFrame(data)
# 1. 基本分组聚合
# 按产品分组,计算每个产品的总销售额和平均销售额
product_stats = sales_df.groupby('产品').agg({
'销售额': ['sum', 'mean', 'count'],
'数量': 'sum'
}).round(2)
print("按产品分组统计:")
print(product_stats)
# 2. 多级分组
# 按地区和产品分组
region_product_stats = sales_df.groupby(['地区', '产品']).agg({
'销售额': 'sum',
'数量': 'mean'
}).round(2)
print("\n按地区和产品分组统计:")
print(region_product_stats)
# 3. 使用transform进行分组填充
# 计算每个产品的平均销售额,并填充到每行
sales_df['产品平均销售额'] = sales_df.groupby('产品')['销售额'].transform('mean')
# 4. 使用apply进行复杂分组操作
def top_n_products(group, n=3):
return group.nlargest(n, '销售额')
top_products = sales_df.groupby('地区').apply(top_n_products, n=2)
print("\n各地区销售额前2名的产品:")
print(top_products)
进阶篇:数据可视化与探索性分析
1. Matplotlib基础绘图
Matplotlib是Python最基础的绘图库,提供了丰富的绘图功能。
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
# 创建示例数据
np.random.seed(42)
dates = pd.date_range('2023-01-01', periods=12, freq='M')
sales = np.random.randint(50000, 150000, 12)
profit = sales * np.random.uniform(0.1, 0.3, 12)
monthly_data = pd.DataFrame({
'月份': dates,
'销售额': sales,
'利润': profit
})
# 1. 基本折线图
plt.figure(figsize=(12, 6))
plt.plot(monthly_data['月份'], monthly_data['销售额'],
marker='o', linewidth=2, label='销售额')
plt.plot(monthly_data['月份'], monthly_data['利润'],
marker='s', linewidth=2, label='利润')
plt.title('2023年月度销售与利润趋势', fontsize=16, fontweight='bold')
plt.xlabel('月份', fontsize=12)
plt.ylabel('金额(元)', fontsize=12)
plt.legend()
plt.grid(True, alpha=0.3)
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()
# 2. 柱状图
plt.figure(figsize=(10, 6))
products = ['手机', '电脑', '平板', '耳机']
product_sales = [450000, 380000, 250000, 180000]
bars = plt.bar(products, product_sales, color=['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4'])
plt.title('2023年各产品销售额', fontsize=16, fontweight='bold')
plt.xlabel('产品', fontsize=12)
plt.ylabel('销售额(元)', fontsize=12)
# 在柱子上显示数值
for bar in bars:
height = bar.get_height()
plt.text(bar.get_x() + bar.get_width()/2., height,
f'{height:,}', ha='center', va='bottom', fontsize=11)
plt.tight_layout()
plt.show()
# 3. 散点图(探索相关性)
plt.figure(figsize=(8, 6))
np.random.seed(42)
x = np.random.normal(100, 15, 100) # 广告投入
y = x * 1.2 + np.random.normal(0, 10, 100) # 销售额(正相关)
plt.scatter(x, y, alpha=0.6, color='#3498db', s=50)
plt.title('广告投入 vs 销售额', fontsize=16, fontweight='bold')
plt.xlabel('广告投入(万元)', fontsize=12)
plt.ylabel('销售额(万元)', fontsize=12)
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
2. Seaborn高级可视化
Seaborn基于Matplotlib,提供了更美观的统计图形。
import seaborn as sns
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
# 设置Seaborn样式
sns.set_style("whitegrid")
# 创建示例数据
np.random.seed(42)
data = {
'年龄': np.random.randint(20, 60, 200),
'薪资': np.random.normal(15000, 5000, 200),
'部门': np.random.choice(['技术', '销售', '市场', '人事'], 200),
'绩效评分': np.random.uniform(3, 5, 200)
}
df = pd.DataFrame(data)
# 1. 箱线图(显示分布和异常值)
plt.figure(figsize=(10, 6))
sns.boxplot(x='部门', y='薪资', data=df, palette='Set2')
plt.title('各部门薪资分布', fontsize=16, fontweight='bold')
plt.xlabel('部门', fontsize=12)
plt.ylabel('薪资(元)', fontsize=12)
plt.tight_layout()
plt.show()
# 2. 小提琴图(结合箱线图和核密度估计)
plt.figure(figsize=(10, 6))
sns.violinplot(x='部门', y='绩效评分', data=df, palette='Set3', inner='quartile')
plt.title('各部门绩效评分分布', fontsize=16, fontweight='bold')
plt.xlabel('部门', fontsize=12)
plt.ylabel('绩效评分', fontsize=12)
plt.tight_layout()
plt.show()
# 3. 热力图(相关性矩阵)
plt.figure(figsize=(8, 6))
correlation_matrix = df[['年龄', '薪资', '绩效评分']].corr()
sns.heatmap(correlation_matrix, annot=True, cmap='coolwarm', center=0,
square=True, linewidths=0.5, cbar_kws={"shrink": 0.8})
plt.title('变量相关性热力图', fontsize=16, fontweight='bold')
plt.tight_layout()
plt.show()
# 4. Pairplot(多变量关系)
# 注意:这个图会显示所有数值变量之间的两两关系
# sns.pairplot(df, hue='部门', palette='husl')
# plt.show()
高级篇:时间序列分析
1. 时间序列基础操作
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# 创建时间序列数据
np.random.seed(42)
dates = pd.date_range('2020-01-01', '2023-12-31', freq='D')
values = np.random.normal(100, 15, len(dates)).cumsum() # 随机游走
ts = pd.Series(values, index=dates)
print("时间序列前5行:")
print(ts.head())
# 1. 重采样(Resampling)
# 将日数据转换为月数据
monthly_mean = ts.resample('M').mean()
monthly_sum = ts.resample('M').sum()
print("\n月度平均值:")
print(monthly_mean.head())
# 2. 滚动窗口计算
rolling_7d = ts.rolling(window='7D').mean() # 7天移动平均
rolling_30d = ts.rolling(window='30D').mean() # 30天移动平均
# 3. 时间序列分解
from statsmodels.tsa.seasonal import seasonal_decompose
# 创建具有趋势和季节性的数据
np.random.seed(42)
dates = pd.date_range('2020-01-01', periods=365, freq='D')
trend = np.linspace(100, 200, 365)
seasonal = 20 * np.sin(2 * np.pi * np.arange(365) / 30) # 30天周期
noise = np.random.normal(0, 5, 365)
values = trend + seasonal + noise
ts_decompose = pd.Series(values, index=dates)
# 进行分解(假设是月度数据)
decomposition = seasonal_decompose(ts_decompose, model='additive', period=30)
# 绘制分解结果
fig, (ax1, ax2, ax3, ax4) = plt.subplots(4, 1, figsize=(12, 10))
ts_decompose.plot(ax=ax1, title='原始数据')
decomposition.trend.plot(ax=ax2, title='趋势')
decomposition.seasonal.plot(ax=ax3, title='季节性')
decomposition.resid.plot(ax=ax4, title='残差')
plt.tight_layout()
plt.show()
2. 时间序列预测基础
from statsmodels.tsa.arima.model import ARIMA
from statsmodels.tsa.stattools import acf, pacf
from statsmodels.graphics.tsaplots import plot_acf, plot_pacf
# 使用之前创建的时间序列数据
# 注意:实际应用中需要确保数据是平稳的
# 1. 绘制ACF和PACF图确定ARIMA参数
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 8))
plot_acf(ts_decompose, ax=ax1, lags=40)
plot_pacf(ts_decompose, ax=ax2, lags=40)
plt.tight_layout()
plt.show()
# 2. ARIMA模型预测
# 分割数据
train_size = int(len(ts_decompose) * 0.8)
train, test = ts_decompose[:train_size], ts_decompose[train_size:]
# 拟合ARIMA模型(参数需要根据ACF/PACF图确定)
model = ARIMA(train, order=(2, 1, 2)) # (p, d, q)
results = model.fit()
print("\nARIMA模型摘要:")
print(results.summary())
# 预测
forecast = results.forecast(steps=len(test))
forecast_index = test.index
# 绘制结果
plt.figure(figsize=(12, 6))
plt.plot(train.index, train, label='训练数据', color='blue')
plt.plot(test.index, test, label='实际数据', color='green')
plt.plot(forecast_index, forecast, label='预测数据', color='red', linestyle='--')
plt.title('ARIMA模型预测结果', fontsize=16, fontweight='bold')
plt.xlabel('日期', fontsize=12)
plt.ylabel('数值', fontsize=12)
plt.legend()
plt.tight_layout()
plt.show()
实战篇:电商销售数据分析完整案例
1. 数据加载与初步探索
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from datetime import datetime, timedelta
# 创建模拟的电商销售数据
np.random.seed(42)
n_orders = 1000
# 生成订单数据
order_data = {
'订单ID': [f'ORD{i:05d}' for i in range(1, n_orders + 1)],
'客户ID': np.random.randint(1000, 2000, n_orders),
'产品ID': np.random.choice(['P001', 'P002', 'P003', 'P004', 'P005'], n_orders),
'下单时间': [datetime(2023, 1, 1) + timedelta(days=np.random.randint(0, 365),
hours=np.random.randint(0, 24),
minutes=np.random.randint(0, 60))
for _ in range(n_orders)],
'数量': np.random.randint(1, 5, n_orders),
'单价': np.random.choice([299, 499, 799, 999, 1299], n_orders),
'客户地区': np.random.choice(['北京', '上海', '广州', '深圳', '杭州', '成都'], n_orders),
'支付方式': np.random.choice(['支付宝', '微信支付', '信用卡', '储蓄卡'], n_orders)
}
orders = pd.DataFrame(order_data)
# 计算订单金额
orders['订单金额'] = orders['数量'] * orders['单价']
# 添加产品类别信息
product_categories = {
'P001': '手机',
'P002': '电脑',
'P003': '平板',
'P004': '耳机',
'P005': '配件'
}
orders['产品类别'] = orders['产品ID'].map(product_categories)
print("数据概览:")
print(orders.head())
print(f"\n数据形状:{orders.shape}")
print(f"\n数据类型:\n{orders.dtypes}")
2. 数据清洗与特征工程
# 1. 检查缺失值
print("缺失值统计:")
print(orders.isnull().sum())
# 2. 检查重复值
print(f"\n重复订单数:{orders.duplicated(subset=['订单ID']).sum()}")
# 3. 数据类型转换
orders['下单时间'] = pd.to_datetime(orders['下单时间'])
# 4. 特征工程
# 提取时间特征
orders['下单日期'] = orders['下单时间'].dt.date
orders['下单月份'] = orders['下单时间'].dt.month
orders['下单星期'] = orders['下单时间'].dt.dayofweek # 0=周一, 6=周日
orders['下单小时'] = orders['下单时间'].dt.hour
# 计算折扣(模拟)
np.random.seed(42)
orders['折扣率'] = np.random.uniform(0.8, 1.0, len(orders))
orders['实际支付金额'] = (orders['订单金额'] * orders['折扣率']).round(2)
# 计算利润(假设成本是售价的60%)
orders['成本'] = (orders['单价'] * 0.6 * orders['数量']).round(2)
orders['利润'] = (orders['实际支付金额'] - orders['成本']).round(2)
print("\n添加特征后的数据:")
print(orders[['订单ID', '下单月份', '下单星期', '折扣率', '实际支付金额', '利润']].head())
3. 多维度分析
# 1. 整体销售情况分析
print("=== 整体销售情况 ===")
total_sales = orders['实际支付金额'].sum()
total_profit = orders['利润'].sum()
avg_order_value = orders['实际支付金额'].mean()
total_orders = len(orders)
print(f"总销售额:{total_sales:,.2f} 元")
print(f"总利润:{total_profit:,.2f} 元")
print(f"平均订单金额:{avg_order_value:,.2f} 元")
print(f"总订单数:{total_orders}")
# 2. 按月份分析销售趋势
monthly_sales = orders.groupby('下单月份').agg({
'实际支付金额': 'sum',
'利润': 'sum',
'订单ID': 'count'
}).round(2)
monthly_sales.columns = ['销售额', '利润', '订单数']
print("\n=== 月度销售情况 ===")
print(monthly_sales)
# 3. 按产品类别分析
category_analysis = orders.groupby('产品类别').agg({
'实际支付金额': ['sum', 'mean'],
'利润': 'sum',
'数量': 'sum',
'订单ID': 'count'
}).round(2)
category_analysis.columns = ['销售额', '平均订单金额', '总利润', '总销量', '订单数']
category_analysis = category_analysis.sort_values('销售额', ascending=False)
print("\n=== 产品类别分析 ===")
print(category_analysis)
# 4. 按地区分析
region_analysis = orders.groupby('客户地区').agg({
'实际支付金额': 'sum',
'利润': 'sum',
'订单ID': 'count'
}).round(2)
region_analysis.columns = ['销售额', '利润', '订单数']
region_analysis = region_analysis.sort_values('销售额', ascending=False)
print("\n=== 地区分析 ===")
print(region_analysis)
# 5. 支付方式分析
payment_analysis = orders.groupby('支付方式').agg({
'实际支付金额': 'sum',
'订单ID': 'count'
}).round(2)
payment_analysis.columns = ['销售额', '订单数']
payment_analysis = payment_analysis.sort_values('销售额', ascending=False)
print("\n=== 支付方式分析 ===")
print(payment_analysis)
4. 可视化分析
# 设置中文字体(根据系统调整)
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号
# 1. 月度销售趋势图
plt.figure(figsize=(12, 6))
plt.plot(monthly_sales.index, monthly_sales['销售额'],
marker='o', linewidth=2, color='#2E86AB', label='销售额')
plt.plot(monthly_sales.index, monthly_sales['利润'],
marker='s', linewidth=2, color='#A23B72', label='利润')
plt.title('2023年月度销售与利润趋势', fontsize=16, fontweight='bold')
plt.xlabel('月份', fontsize=12)
plt.ylabel('金额(元)', fontsize=12)
plt.legend()
plt.grid(True, alpha=0.3)
plt.xticks(range(1, 13))
plt.tight_layout()
plt.show()
# 2. 产品类别销售占比
plt.figure(figsize=(10, 8))
category_sales = category_analysis['销售额']
colors = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7']
plt.pie(category_sales.values, labels=category_sales.index,
autopct='%1.1f%%', colors=colors, startangle=90)
plt.title('各产品类别销售额占比', fontsize=16, fontweight='bold')
plt.axis('equal')
plt.tight_layout()
plt.show()
# 3. 地区销售对比
plt.figure(figsize=(10, 6))
bars = plt.bar(region_analysis.index, region_analysis['销售额'],
color=['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7', '#FFA07A'])
plt.title('各地区销售额对比', fontsize=16, fontweight='bold')
plt.xlabel('地区', fontsize=12)
plt.ylabel('销售额(元)', fontsize=12)
plt.xticks(rotation=45)
# 在柱子上显示数值
for bar in bars:
height = bar.get_height()
plt.text(bar.get_x() + bar.get_width()/2., height,
f'{height:,.0f}', ha='center', va='bottom', fontsize=10)
plt.tight_layout()
plt.show()
# 4. 小时销售热力图
hourly_sales = orders.groupby(['下单星期', '下单小时'])['实际支付金额'].sum().unstack()
plt.figure(figsize=(12, 6))
sns.heatmap(hourly_sales, cmap='YlOrRd', annot=False, fmt='.0f')
plt.title('星期-小时销售热力图', fontsize=16, fontweight='bold')
plt.xlabel('小时', fontsize=12)
plt.ylabel('星期(0=周一)', fontsize=12)
plt.tight_layout()
plt.show()
5. 高级分析:RFM模型
# RFM模型分析(最近购买时间、购买频率、消费金额)
from datetime import datetime
# 计算参考日期(假设今天是2024-01-01)
reference_date = datetime(2024, 1, 1)
# 计算每个客户的RFM指标
rfm = orders.groupby('客户ID').agg({
'下单时间': lambda x: (reference_date - x.max()).days, # Recency
'订单ID': 'count', # Frequency
'实际支付金额': 'sum' # Monetary
}).round(2)
rfm.columns = ['Recency', 'Frequency', 'Monetary']
# RFM评分(1-5分)
rfm['R_Score'] = pd.qcut(rfm['Recency'], 5, labels=[5, 4, 3, 2, 1]) # 越小越好
rfm['F_Score'] = pd.qcut(rfm['Frequency'].rank(method='first'), 5, labels=[1, 2, 3, 4, 5])
rfm['M_Score'] = pd.qcut(rfm['Monetary'], 5, labels=[1, 2, 3, 4, 5])
# 合并RFM分数
rfm['RFM_Score'] = rfm['R_Score'].astype(str) + rfm['F_Score'].astype(str) + rfm['M_Score'].astype(str)
# 客户分群
def segment_customer(row):
score = int(row['RFM_Score'])
if score >= 555:
return 'VIP客户'
elif score >= 444:
return '高价值客户'
elif score >= 333:
return '潜力客户'
elif score >= 222:
return '一般客户'
else:
return '流失风险客户'
rfm['客户分群'] = rfm.apply(segment_customer, axis=1)
print("\n=== RFM模型分析结果 ===")
print(rfm.head(10))
# 客户分群统计
print("\n客户分群统计:")
print(rfm['客户分群'].value_counts())
# 各分群的平均值
print("\n各分群平均值:")
print(rfm.groupby('客户分群')[['Recency', 'Frequency', 'Monetary']].mean().round(2))
6. 预测分析:销售预测
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestRegressor
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score
# 准备数据:按天汇总销售
daily_sales = orders.groupby('下单日期').agg({
'实际支付金额': 'sum',
'订单ID': 'count'
}).reset_index()
daily_sales.columns = ['日期', '销售额', '订单数']
# 添加时间特征
daily_sales['日期'] = pd.to_datetime(daily_sales['日期'])
daily_sales['星期'] = daily_sales['日期'].dt.dayofweek
daily_sales['月份'] = daily_sales['日期'].dt.month
daily_sales['是否周末'] = daily_sales['星期'].isin([5, 6]).astype(int)
# 添加滞后特征(前1天、前7天的销售额)
daily_sales['lag_1'] = daily_sales['销售额'].shift(1)
daily_sales['lag_7'] = daily_sales['销售额'].shift(7)
# 移除包含NaN的行
daily_sales = daily_sales.dropna()
# 准备特征和目标变量
features = ['星期', '月份', '是否周末', 'lag_1', 'lag_7']
X = daily_sales[features]
y = daily_sales['销售额']
# 分割数据
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 训练随机森林模型
rf_model = RandomForestRegressor(n_estimators=100, random_state=42)
rf_model.fit(X_train, y_train)
# 预测
y_pred = rf_model.predict(X_test)
# 评估模型
mae = mean_absolute_error(y_test, y_pred)
mse = mean_squared_error(y_test, y_pred)
r2 = r2_score(y_test, y_pred)
print("\n=== 销售预测模型评估 ===")
print(f"平均绝对误差(MAE):{mae:,.2f}")
print(f"均方误差(MSE):{mse:,.2f}")
print(f"R²分数:{r2:.4f}")
# 特征重要性
feature_importance = pd.DataFrame({
'特征': features,
'重要性': rf_model.feature_importances_
}).sort_values('重要性', ascending=False)
print("\n特征重要性:")
print(feature_importance)
# 可视化预测结果
plt.figure(figsize=(12, 6))
plt.plot(range(len(y_test)), y_test.values, label='实际值', color='blue', alpha=0.7)
plt.plot(range(len(y_pred)), y_pred, label='预测值', color='red', linestyle='--')
plt.title('销售预测:实际值 vs 预测值', fontsize=16, fontweight='bold')
plt.xlabel('样本索引', fontsize=12)
plt.ylabel('销售额', fontsize=12)
plt.legend()
plt.tight_layout()
plt.show()
总结与最佳实践
1. 性能优化技巧
# 1. 使用向量化操作替代循环
import numpy as np
import pandas as pd
# 不推荐的做法(使用循环)
def slow_calculation(df):
result = []
for i in range(len(df)):
if df.iloc[i]['销售额'] > 5000:
result.append('高')
else:
result.append('低')
return result
# 推荐的做法(使用向量化)
def fast_calculation(df):
return np.where(df['销售额'] > 5000, '高', '低')
# 2. 使用适当的数据类型
def optimize_memory(df):
# 将object类型转换为category如果唯一值较少
for col in df.select_dtypes(include=['object']).columns:
if df[col].nunique() / len(df) < 0.5:
df[col] = df[col].astype('category')
# 将数值类型转换为更小的类型
for col in df.select_dtypes(include=['int']).columns:
df[col] = pd.to_numeric(df[col], downcast='integer')
for col in df.select_dtypes(include=['float']).columns:
df[col] = pd.to_numeric(df[col], downcast='float')
return df
# 3. 使用chunksize处理大文件
def process_large_file(file_path, chunk_size=10000):
chunks = pd.read_csv(file_path, chunksize=chunk_size)
results = []
for chunk in chunks:
# 对每个chunk进行处理
processed = chunk.groupby('类别')['销售额'].sum()
results.append(processed)
# 合并结果
final_result = pd.concat(results).groupby(level=0).sum()
return final_result
2. 代码组织与可维护性
# 使用函数封装重复逻辑
def load_data(file_path, file_type='csv'):
"""加载数据文件"""
if file_type == 'csv':
return pd.read_csv(file_path)
elif file_type == 'excel':
return pd.read_excel(file_path)
else:
raise ValueError("不支持的文件类型")
def clean_data(df, fillna_values=None, drop_duplicates=True):
"""数据清洗"""
if fillna_values:
df = df.fillna(fillna_values)
if drop_duplicates:
df = df.drop_duplicates()
return df
def analyze_sales(df, group_by=None, agg_func='sum'):
"""销售分析"""
if group_by:
return df.groupby(group_by)['销售额'].agg(agg_func)
else:
return df['销售额'].agg(agg_func)
# 使用类封装复杂分析
class SalesAnalyzer:
def __init__(self, data):
self.data = data
self.results = {}
def monthly_trend(self):
"""月度趋势分析"""
self.results['monthly'] = self.data.groupby('月份')['销售额'].sum()
return self.results['monthly']
def category_performance(self):
"""品类表现分析"""
self.results['category'] = self.data.groupby('产品类别').agg({
'销售额': 'sum',
'利润': 'sum'
})
return self.results['category']
def generate_report(self):
"""生成完整报告"""
report = {
'总销售额': self.data['销售额'].sum(),
'总利润': self.data['利润'].sum(),
'月度趋势': self.monthly_trend().to_dict(),
'品类表现': self.category_performance().to_dict()
}
return report
3. 常见问题与解决方案
问题1:内存不足
# 解决方案:分块处理或使用Dask
import dask.dataframe as dd
# 使用Dask处理大数据
ddf = dd.read_csv('large_file.csv')
result = ddf.groupby('类别').销售额.sum().compute()
问题2:数据类型混乱
# 解决方案:统一数据类型
df['日期'] = pd.to_datetime(df['日期'], errors='coerce')
df['数值'] = pd.to_numeric(df['数值'], errors='coerce')
问题3:中文显示乱码
# 解决方案:设置字体
plt.rcParams['font.sans-serif'] = ['SimHei', 'Arial Unicode MS', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False
结语
Python数据分析是一个不断学习和实践的过程。从基础的Pandas操作到高级的时间序列分析和机器学习预测,每一步都需要扎实的理论基础和丰富的实践经验。
记住以下关键点:
- 数据质量是基础:花足够的时间在数据清洗和预处理上
- 理解业务:技术服务于业务,理解业务需求才能做出有价值的分析
- 持续学习:数据分析领域发展迅速,保持学习新技术和方法
- 代码规范:编写可读、可维护的代码,便于团队协作
- 可视化沟通:用图表讲好数据故事,让分析结果更容易被理解
通过本指南的学习,你应该能够独立完成从数据获取、清洗、分析到可视化的完整流程,并能够根据业务需求进行深入的探索性分析和预测建模。记住,最好的学习方式是实践,尝试用真实的数据来应用这些技术,你会发现数据分析的无限可能。
