EDA 代码教程:带你轻松拿捏数据分析!(exploredata analusis)
1. Intro
Hello,各位!今天我们要一起探索数据分析!你是不是经常听到“EDA”这个词,但又觉得很神秘?别担心,这篇教程会手把手带你入门,保证你轻松拿捏!
📌案例背景:我们今天研究的是产品销售数据,看看不同因素如何影响销售额。
🛠 2. 导入必要的库
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
pandas:数据处理神器,负责计算数据。
numpy:数学计算画图的小能手。
seaborn:画出好看的统计图。
matplotlib.pyplot:基础绘图工具,配合seaborn使用会更好用。
👁 3.检查数据格式
通常对于数据分析 我们使用.csv格式的文件进行分析()逗号分隔数据
今天,咱们就.csv文件展开示范哈。💕💕💕
📂 4. 数据加载:如何高效读取多个销量数据文件?
目标
在数据分析的第一步,我们通常需要加载多个数据集。这里我们有两类数据:
产品销量数据(sales data) 📈
天气数据(weather data) ⛅
我们将使用 pandas 的 read_csv() 方法批量读取这些文件,并探索它们的结构。
🛠 代码实现(一件三联领取代码和数据集哦)
销量数据文件列表
sales_files = [ ‘DailySales_R1.csv’, ‘DailySales_R2.csv’, ‘DailySales_R3.csv]
天气数据文件列表
weather_files = [ ‘99322123.csv’,’2004sales.csv’]
批量读取销量数据
sales_data = [pd.read_csv(file) for file in sales_files]
批量读取天气数据
weather_data = [pd.read_csv(file) for file in weather_files]
查看部分数据
sales_sample = sales_data[0].head() weather_sample = weather_data[0].head()
sales_sample, weather_sample
代码解析
1️⃣ 为什么要存成列表?
在 sales_data 和 weather_data 这两个变量里,我们使用了列表推导式(List Comprehension)来存储多个年份的数据集。这样做的好处:
- 代码简洁:用一行代码就能遍历所有文件并读取它们,不需要写多个 pd.read_csv() 语句。
- 可扩展性强:如果未来要增加新的数据文件,我们只需要在 sales_files 或 weather_files 里添加新的文件名,而不需要修改 read_csv() 代码。
2️⃣ 为什么用 pd.read_csv() 读取数据?
pandas 的 read_csv() 是最常见的数据读取函数
✅ 快速高效: pandas 经过优化,读取 CSV 文件的速度比 Python 内置的 open() + csv.reader() 更快。
✅ 自动识别数据类型:可以自动解析日期、字符串、数值等数据类型(虽然默认行为有时候需要调整)。
✅ 易于操作:它返回一个 DataFrame ,可以直接用于数据分析。
❌ 反面例子(不推荐做法,可以用,但是太sb了)
不推荐:逐个手动读取
df1 = pd.read_csv(‘sales_2021.csv’) df2 = pd.read_csv(‘sales_2022.csv’) df3 = pd.read_csv(‘sales_2023.csv’) df4 = pd.read_csv(‘sales_2024.csv’)
sales_data = [df1, df2, df3, df4]
为什么不好?
代码冗长,如果有 10 个年份的数据,需要写 10 行 pd.read_csv() 。
不易维护,如果要添加新的数据年份,必须手动修改代码,而不是简单地扩展文件列表。
3️⃣ head() 重要性
sales_sample = sales_data[0].head() weather_sample = weather_data[0].head()
✅ 快速预览数据: head(5) 只取前 5 行数据,避免直接打印整个数据集,防止终端崩溃。
✅ 检查数据格式:可以快速确认数据是否正确加载,例如:
列名是否符合预期? 是否有缺失值?
数据类型是否正确?
🚀 小挑战:尝试优化数据读取
💡 现在我们已经学会了如何批量读取数据,你能优化 pd.read_csv() 的参数,让它更高效吗?比如:
使用 low_memory=False 解决 DtypeWarning 问题? 用 usecols 只加载需要的列,提高性能?
结合 dtype 指定列的数据类型?
思考:
- 为什么 pd.read_csv() 可能会发出 DtypeWarning ?
- 如果 CSV 文件很大,如何优化读取速度?(评论区@博主哦❤❤❤)
🧹 5. 数据清理:如何正确解析天气数据?
在数据分析过程中,原始数据通常会包含许多编码过的信息,比如:
温度(TMP) 🌡
露点(DEW) 💦
海平面气压(SLP) 🌍
这些变量对销量分析至关重要,因为天气因素可能会影响消费者的购买决策,比如:雨天是否影响商店客流量?。在这一部分,我们会解码这些天气变量,并清理数据,使其更易于分析。
代码
解析关键天气变量
温度 (TMP), 露点 (DEW), 海平面气压 (SLP) 是主要的分析变量
weather_combined[‘temperature’] = weather_combined[‘TMP’].str.split(‘,’, expand=True)[0].a weather_combined[‘dew_point’] = weather_combined[‘DEW’].str.split(‘,’, expand=True)[0].ast weather_combined[‘pressure’] = weather_combined[‘SLP’].str.split(‘,’, expand=True)[0].asty
将日期转换为标准的 datetime 格式
weather_combined[‘date’] = pd.to_datetime(weather_combined[‘DATE’], errors=’coerce’)
删除无效日期或关键天气变量缺失的行
weather_combined_cleaned = weather_combined.dropna(subset=[‘date’, ‘temperature’, ‘dew_poi
查看清理后的数据
weather_combined_cleaned.head()
🤔 代码解析
1️⃣ 为什么要拆分 TMP、DEW 和 SLP?
在一些数据集中,天气变量通常以 10,0 这种格式存储,其中:
10 代表测量值(如温度 10°C)。
0 代表质量控制标识符,不需要使用。所以我们使用:
.str.split(‘,’, expand=True)[0]
✅ 分割字符串,只提取第一部分(真正的数值)。
✅ 去除不必要的额外信息,确保数据格式一致。
❌ 反面例子
weather_combined[‘temperature’] = weather_combined[‘TMP’]
直接读取 TMP 列,会包含额外的标识信息,导致数据格式不对,可能会引发 ValueError 。
2️⃣ 为什么 astype(float) / 10 ?
在某些天气数据集中,温度、露点和气压通常被放大了 10 倍,例如:
温度 125 实际上表示 12.5°C 。 露点 85 代表 8.5°C 。
气压 10120 代表 1012.0 hPa 。
✅ astype(float) / 10 确保数值正确缩放,使数据符合实际单位。
❌ 反面例子
weather_combined[‘temperature’] = weather_combined[‘TMP’].astype(float)
忘记 / 10 ,可能导致温度从 12.5°C 变成 125°C ,直接变成了三味真火!
3️⃣ 为什么转换日期格式?
weather_combined[‘date’] = pd.to_datetime(weather_combined[‘DATE’], errors=’coerce’)
✅ pd.to_datetime() 确保时间格式一致,方便后续分析和合并数据。
✅ errors=’coerce’ 防止错误格式的日期引发崩溃,任何无法解析的日期都会变成 NaT (缺失值)。
❌ 反面例子
weather_combined[‘date’] = weather_combined[‘DATE’]
为什么不好?
可能导致 date 仍然是字符串,无法进行日期计算。 不能用于时间序列分析或数据合并。
4️⃣ 为什么删除缺失数据?
weather_combined_cleaned = weather_combined.dropna(subset=[‘date’, ‘temperature’, ‘dew_poi
✅ 确保数据完整,防止后续分析出现错误。
✅ 减少无意义的缺失值行,提高模型的准确性。
❌ 反面例子
weather_combined_cleaned = weather_combined
如果不删除 NaN ,后续的统计分析或机器学习模型可能会报错。
总结
在这一步,我们: ✅ 解析了天气变量(温度、露点、气压),转换成标准单位。
✅ 将日期格式标准化,便于分析。
✅ 清理了缺失值,保证数据完整性。
💡 思考:
- 你能找到天气变量与销量之间的关系吗?
- 除了温度、露点和气压,还有哪些天气因素可能影响销量?
🚀 继续加油,下一步我们将合并销量数据和天气数据,看看天气究竟会如何影响销售趋势!📊
🔗 6. 数据合并:销量数据与天气数据如何对齐?
目标
在数据分析中,我们通常需要合并不同来源的数据,比如:
销量数据 📈:包含每天的产品销售情况。
天气数据 🌦:记录当天的气象条件。
但这些数据可能没有完美匹配的时间戳(例如,一个数据是每天更新,另一个可能是每小时记录),所以我们需要使用“最近匹配”策略来对齐数据。
代码实现
合并所有销量数据到一个 DataFrame
sales_combined = pd.concat(sales_data, ignore_index=True)
将销量数据中的 ‘Report Date’ 转换为 datetime 格式,以便正确对齐
sales_combined[‘report_date’] = pd.to_datetime(sales_combined[‘Report Date’], errors=’coer
删除无效或缺失的日期,确保数据质量
sales_cleaned = sales_combined.dropna(subset=[‘report_date’])
按时间对齐并合并销量数据与天气数据
merged_data = pd.merge_asof( sales_cleaned.sort_values(‘report_date’), # 按日期排序销量数据 weather_combined_cleaned.sort_values(‘date’), # 按日期排序天气数据 left_on=’report_date’, # 以销量数据的 report_date 作为左键 right_on=’date’, # 以天气数据的 date 作为右键
direction=’nearest’ # 采用最近时间匹配方式
)
查看合并后的数据
merged_data.head()
🤔 代码解析
1️⃣ 为什么要用 pd.concat() ?
在 sales_data 变量中,我们存储了多个年份的销量数据(如 2021、2022、2023…)。
sales_combined = pd.concat(sales_data, ignore_index=True)
✅ 合并多个 DataFrame,形成一个完整的销量数据集。
✅ ignore_index=True 重置索引,防止多个 DataFrame 合并后索引混乱。
❌ 反面例子
sales_combined = sales_data[0] # 仅使用第一个年份的数据
只使用 2021 年的数据,忽略了其他年份的销量数据,导致分析结果不完整。
2️⃣ 为什么要转换日期格式?
sales_combined[‘report_date’] = pd.to_datetime(sales_combined[‘Report Date’], errors=’coer
✅ pd.to_datetime() 标准化日期格式,确保不同格式的日期可以正确解析。
✅ errors=’coerce’ 自动处理无效日期,如果某个值不能解析成日期,就会变成 NaT (缺失值)。
❌ 反面例子
sales_combined[‘report_date’] = sales_combined[‘Report Date’]
可能导致 report_date 仍然是字符串,无法用于时间序列分析或数据合并。
3️⃣ 为什么要删除无效的日期?
sales_cleaned = sales_combined.dropna(subset=[‘report_date’])
✅ 确保数据完整,避免时间戳为空的记录干扰分析。
✅ 让 report_date 这一列成为一个可靠的时间索引,避免合并时出现问题。
❌ 反面例子
sales_cleaned = sales_combined
如果不删除 NaN 值, merge_asof() 可能会失败或导致不正确的匹配。
4️⃣ 为什么用 pd.merge_asof() ?
在合并销量数据和天气数据时,我们遇到了一个挑战:
销量数据 可能是每天一条记录 📊
天气数据 可能是每小时一条记录 🌦
所以,我们不能用普通的 merge() ,因为它要求完全匹配的时间戳。我们使用 merge_asof() 来匹配最近的时间戳:
merged_data = pd.merge_asof( sales_cleaned.sort_values(‘report_date’), weather_combined_cleaned.sort_values(‘date’), left_on=’report_date’,
right_on=’date’, direction=’nearest’
)
✅ 自动匹配最近的天气数据,确保销量数据和天气信息合理对应。
✅ 适用于不同时间粒度的数据集(如“日数据”对“小时数据”)。
❌ 反面例子
merged_data = pd.merge(sales_cleaned, weather_combined_cleaned, on=’report_date’)
merge() 只能匹配完全相同的时间戳,如果 report_date 和 date 不完全一致,数据可能会丢失。
总结
在这一步,我们: ✅ 合并了多个年份的销量数据,形成完整的数据集。
✅ 清理了日期格式,确保数据的一致性。
✅ 使用 merge_asof() 进行“最近匹配”,对齐销量数据和天气数据。
💡 思考:
- 你能找到天气和销量之间的关系吗?
- 你觉得天气对哪些产品的销量影响最大?(比如冷饮、雨具等)
📊 7. 数据探索与可视化:销量与天气的关系
目标
现在我们已经成功合并了销量数据和天气数据,接下来要探索它们的特征:
销量的基本统计特征(如总销量、平均销量等)。销量随时间的变化趋势 📈。
天气变量随时间的变化趋势 🌡。
销量与天气之间的相关性 🤝。
代码实现
计算销量和天气变量的统计信息
summary_stats = merged_data[[‘Total Sales’, ‘Avg Sales per Store’, ‘temperature’, ‘dew_poi
绘制销量随时间的变化趋势
plt.figure(figsize=(12, 6))
plt.plot(merged_data[‘report_date’], merged_data[‘Total Sales’], color=’blue’, alpha=0.6) plt.title(‘ 销量随时间变化’, fontsize=14)
plt.xlabel(‘日期’, fontsize=12) plt.ylabel(‘总销量’, fontsize=12) plt.show()
绘制温度随时间的变化趋势
plt.figure(figsize=(12, 6))
plt.plot(merged_data[‘report_date’], merged_data[‘temperature’], color=’red’, alpha=0.6) plt.title(‘温度随时间变化’, fontsize=14)
plt.xlabel(‘日期’, fontsize=12)
plt.ylabel(‘温度 (°C)’, fontsize=12) plt.show()
计算销量与天气变量的相关性
correlation_matrix = merged_data[[‘Total Sales’, ‘Avg Sales per Store’, ‘temperature’, ‘de
返回统计摘要和相关性矩阵
summary_stats, correlation_matrix
代码解析
1️⃣ 为什么要用 describe() ?
summary_stats = merged_data[[‘Total Sales’, ‘Avg Sales per Store’, ‘temperature’, ‘dew_poi
✅ describe() 计算数据的基本统计信息,如:
均值(mean):销量的平均水平 标准差(std):销量的波动程度
最小值(min)/最大值(max):销量的范围
❌ 反面例子
summary_stats = merged_data.mean()
mean() 只会计算均值,不会提供标准差、最小值、最大值等关键信息。
2️⃣ 为什么用 plt.plot() 来绘制时间序列?
plt.plot(merged_data[‘report_date’], merged_data[‘Total Sales’], color=’blue’, alpha=0.6)
✅ plt.plot() 适用于连续数据(时间序列),可以清楚地显示销量随时间的变化趋势。
✅ color=’blue’ 使销量曲线更容易区分。
✅ alpha=0.6 使曲线半透明,增强可视化效果。
❌ 反面例子
plt.bar(merged_data[‘report_date’], merged_data[‘Total Sales’], color=’blue’)
柱状图(bar)适用于离散数据,而时间序列数据是连续的,应该用折线图(line plot)。
3️⃣ 为什么要分析温度随时间的变化?
plt.plot(merged_data[‘report_date’], merged_data[‘temperature’], color=’red’, alpha=0.6)
✅ 直观地查看气温的季节性变化(如夏季温度高、冬季温度低)。
✅ 结合销量数据,看看销量是否受气温影响(如冷饮销量是否在高温时上升)。
❌ 反面例子
plt.scatter(merged_data[‘report_date’], merged_data[‘temperature’], color=’red’)
散点图(scatter plot)适用于观察离散点分布,但不能清晰展示趋势变化。
4️⃣ 为什么要计算相关性?
correlation_matrix = merged_data[[‘Total Sales’, ‘Avg Sales per Store’, ‘temperature’, ‘de
✅ corr() 计算变量之间的线性相关性:
正相关(值接近 1):两个变量同时增加或减少,例如气温上升时冷饮销量上升。
负相关(值接近 -1):一个变量增加时,另一个变量减少,例如降水量增加时户外用品销量下降。
无相关(值接近 0):两个变量之间没有明显的线性关系。
❌ 反面例子
correlation_matrix = merged_data[[‘Total Sales’, ‘temperature’]].cov()
cov() 计算鞋方差,但协方差的数值大小依赖于变量的单位,不如相关系数(correlation coefficient)直观。
💡 思考:
你能找到天气与销量之间的关联吗?
哪些天气变量对销量影响最大?(温度?气压?露点?)
🚀 继续加油!下一步我们将深入分析销量的模式,看看如何挖掘更多商业价值!📊
🔥 8. 特征工程:让数据更“聪明”!
目标
在这一部分,我们要对数据进行特征工程(Feature Engineering),让它更适合建模:
- 清理异常值,确保数据质量。
- 提取时间特征 ⏳,如星期几、小时等。
- 转换时间特征,使用正弦/余弦(sin/cos)编码,让时间信息更加平滑。(可以参考我时间序列的文章)
🛠 代码实现
这可根据作业要求改 但是作业要求官网还没发我
过滤异常天气数据,确保合理范围
weather_cleaned = merged_data[ (merged_data[‘temperature’] < 50) & # 温度不超过 50°C (merged_data[‘dew_point’] < 50) & # 露点不超过 50°C
(merged_data[‘pressure’] < 1500) # 气压不超过 1500 hPa
]
提取时间特征:星期几和小时
weather_cleaned[‘day_of_week’] = weather_cleaned[‘report_date’].dt.dayofweek # 星期一=0,星
weather_cleaned[‘hour_of_day’] = weather_cleaned[‘report_date’].dt.hour # 24 小时制
使用正弦和余弦变换时间特征
weather_cleaned[‘day_sin’] = np.sin(2 np.pi weather_cleaned[‘day_of_week’] / 7) weather_cleaned[‘day_cos’] = np.cos(2 np.pi weather_cleaned[‘day_of_week’] / 7) weather_cleaned[‘hour_sin’] = np.sin(2 np.pi weather_cleaned[‘hour_of_day’] / 24) weather_cleaned[‘hour_cos’] = np.cos(2 np.pi weather_cleaned[‘hour_of_day’] / 24)
选择用于建模的特征
model_data = weather_cleaned[[ ‘Total Sales’, # 销量
‘Avg Sales per Store’, # 平均单店销量
‘temperature’, # 温度 ‘dew_point’, # 露点 ‘pressure’, # 气压
‘day_sin’, ‘day_cos’, # 周期性特征(星期几)
‘hour_sin’, ‘hour_cos’ # 周期性特征(小时)
]]
查看数据描述统计信息
model_data_description = model_data.describe() print(model_data_description)
🤔 代码解析
1️⃣ 为什么要清理异常天气数据?
weather_cleaned = merged_data[ (merged_data[‘temperature’] < 50) & (merged_data[‘dew_point’] < 50) & (merged_data[‘pressure’] < 1500)
]
✅ 去除异常值,确保数据合理性:
temperature < 50 避免极端温度,如 100°C 这种不可能的情况。
dew_point < 50 露点不应过高,否则数据可能有误。
pressure < 1500 气压不应异常偏高。
❌ 反面例子
weather_cleaned = merged_data
如果不筛选异常数据,极端值可能会影响模型的准确性,比如异常高温导致销量预测错误。
2️⃣ 为什么要提取“星期几”和“小时”信息?
weather_cleaned[‘day_of_week’] = weather_cleaned[‘report_date’].dt.dayofweek weather_cleaned[‘hour_of_day’] = weather_cleaned[‘report_date’].dt.hour
✅ 时间特征对销量影响很大:
不同星期的销量模式可能不同(周末销量可能更高)。 不同时间段的销量不同(早晨 vs. 晚上)。
❌ 反面例子
weather_cleaned[‘day_of_week’] = weather_cleaned[‘report_date’]
直接使用日期作为特征,模型无法理解日期的周期性变化。
3️⃣ 为什么要用 sin/cos 变换时间特征?
weather_cleaned[‘day_sin’] = np.sin(2 np.pi weather_cleaned[‘day_of_week’] / 7) weather_cleaned[‘day_cos’] = np.cos(2 np.pi weather_cleaned[‘day_of_week’] / 7)
weather_cleaned[‘hour_sin’] = np.sin(2 np.pi weather_cleaned[‘hour_of_day’] / 24) weather_cleaned[‘hour_cos’] = np.cos(2 np.pi weather_cleaned[‘hour_of_day’] / 24)
✅ 时间是周期性的! 星期一和星期日虽然数值上相差 6,但它们应该是“相邻”的。
✅ sin/cos 变换让模型更容易理解时间特征,避免突变(如 0 → 6 跳跃)。
❌ 反面例子
weather_cleaned[‘day_of_week_encoded’] = weather_cleaned[‘day_of_week’]
这样会让模型认为星期一(0)和星期六(6)相差很远,但它们在实际数据模式中可能很相似。
4️⃣ 为什么要选择这些特征?
model_data = weather_cleaned[[
‘Total Sales’, ‘Avg Sales per Store’, ‘temperature’, ‘dew_point’, ‘pressure’, ‘day_sin’, ‘day_cos’, ‘hour_sin’, ‘hour_cos’
]]
✅ 销量相关特征:
Total Sales 代表销量目标变量。
Avg Sales per Store 代表单店销量情况。
✅ 天气相关特征:
temperature 可能影响某些商品销量(如冷饮)。 dew_point 可能影响舒适度,从而影响购买行为。 pressure 可能与天气变化有关。
✅ 时间相关特征:
day_sin, day_cos 让模型理解“星期几”的周期性影响。
hour_sin, hour_cos 让模型理解“一天中的不同时刻”对销量的影响。
❌ 反面例子
model_data = weather_cleaned[[‘Total Sales’, ‘temperature’, ‘pressure’]]
缺少时间特征,模型无法识别一天中销量的模式。 少考虑露点等其他变量,可能影响预测准确性。
总结
在这一步,我们: ✅ 清理了异常值,确保数据质量。
✅ 提取了时间特征,让模型理解销量的时间模式。
✅ 用 sin/cos 变换,让时间特征更平滑,帮助机器学习模型更好地学习周期性信息。
💡 思考:
- 你能发现哪些时间模式?周末销量是否更高?
- 天气特征对销量的影响是怎样的?
🚀 继续加油!下一步我们将进入模型训练和分析阶段,看看天气和时间如何影响销量!📊
🤖 9. 线性回归:预测销量与天气的关系
目标
在这一步,我们将使用 线性回归(Linear Regression) 来预测销量:
- 数据清理 🧹:去除 NaN 值,确保数据完整。
- 拆分数据集 📊:将数据划分为训练集和测试集。
- 训练模型 🏋️♂️:用 天气特征和时间特征 来预测销量。
- 模型评估 📉:使用 MAE、MSE 和 R² 评估模型表现。
代码实现
from sklearn.model_selection import train_test_split from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score
创建示例数据
data = {
‘total_sales’: [500, 600, 700, 800, np.nan, 900],
‘temperature’: [10, 12, 14, 13, 15, 11],
‘dew_point’: [5, 6, 7, 6, 7, 5],
‘pressure’: [1015, 1013, 1014, 1012, 1016, 1013],
‘day_sin’: [0.5, 0.6, 0.7, 0.5, 0.8, 0.6],
‘day_cos’: [0.5, 0.4, 0.3, 0.5, 0.2, 0.4],
‘hour_sin’: [0.2, 0.4, 0.6, 0.8, 0.9, 0.1],
‘hour_cos’: [0.8, 0.6, 0.4, 0.2, 0.1, 0.9],
}
model_data = pd.DataFrame(data)
处理 NaN 值
nan_rows = model_data[model_data[‘total_sales’].isna()] # 查找 NaN 行 model_data_cleaned = model_data.dropna(subset=[‘total_sales’]) # 删除 NaN 行 nan_count_after = model_data_cleaned[‘total_sales’].isna().sum() # 重新检查 NaN 计数
划分特征 (X) 和目标变量 (y)
X = model_data_cleaned[[‘temperature’, ‘dew_point’, ‘pressure’, ‘day_sin’, ‘day_cos’, ‘hou y = model_data_cleaned[‘total_sales’]
划分训练集和测试集(70% 训练,30% 测试)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
训练线性回归模型
linear_model = LinearRegression() linear_model.fit(X_train, y_train)
预测测试数据
y_pred = linear_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) # R² 分数
mae, mse, r2, nan_rows, nan_count_after
代码解析
1️⃣ 为什么要去除 NaN 值?
nan_rows = model_data[model_data[‘total_sales’].isna()] model_data_cleaned = model_data.dropna(subset=[‘total_sales’])
✅ 机器学习模型不能处理 NaN 值,必须先删除或填充。
✅ 这里我们删除了 total_sales 为空的行,确保目标变量完整。
❌ 反面例子
model_data_cleaned = model_data
如果 total_sales 里有 NaN,模型训练时会报错( ValueError: Input contains NaN )。
2️⃣ 为什么要拆分训练集和测试集?
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
✅ 训练集用于训练模型,测试集用于评估模型的泛化能力。
✅ test_size=0.2 表示 80% 的数据用于训练,20% 用于测试。
✅ random_state=42 保证每次运行的拆分结果相同,方便复现。
❌ 反面例子
X_train, X_test, y_train, y_test = train_test_split(X, y)
没有 test_size ,默认会使用 75% 训练,25% 测试,不利于控制比例。没有 random_state ,每次拆分都不同,导致结果不可复现。
3️⃣ 为什么用线性回归模型?
linear_model = LinearRegression() linear_model.fit(X_train, y_train)
✅ 线性回归适用于分析销量与天气变量之间的线性关系。
✅ 适合初步建模,易于解释。
❌ 反面例子
linear_model = None
不训练模型,就无法进行预测,整个分析流程失效。
4️⃣ 如何评估模型?
mae = mean_absolute_error(y_test, y_pred) mse = mean_squared_error(y_test, y_pred) r2 = r2_score(y_test, y_pred)
✅ MAE(Mean Absolute Error,平均绝对误差):
衡量预测值与真实值的平均绝对偏差,单位与 total_sales 一致。
✅ MSE(Mean Squared Error,均方误差):
对误差进行平方,让较大的误差更加突出。
✅ R²(决定系数):
衡量模型解释目标变量的能力:
接近 1:模型表现好。
接近 0:模型几乎没用。
❌ 反面例子
accuracy = (y_pred == y_test).mean()
accuracy 只适用于分类问题,而这里是回归问题。
总结
在这一步,我们: ✅ 去除了 NaN 值,确保数据完整。
✅ 划分了训练集和测试集,避免数据泄漏。
✅ 训练了线性回归模型,分析天气和时间特征对销量的影响。
✅ 计算了模型性能指标,评估其准确性。
💡 思考:
- 你的模型的 R² 值是多少? 这个模型能很好地预测销量吗?
- 你认为哪些特征最重要? 是否可以添加更多特征来提高预测能力?
🚀 继续加油!下一步,我们将尝试不同的模型,比如 决策树回归 或 随机森林,看看能否提高销量预测的准确性! 📊
🚀 10. 多项式回归 & 时间序列分析:探索更强预测能力!
目标
在本节中,我们将尝试更复杂的回归方法,并探索时间序列模式:
- 多项式回归 🏋️♂️:添加交互特征,提高模型预测能力。
- 时间序列分析 ⏳:分析销量的长期趋势,并进行平稳性检测。
代码实现
🔹 10.1 多项式回归
引入必要的库
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import PolynomialFeatures, StandardScaler
创建多项式回归流水线
pipeline = Pipeline([
(‘scaler’, StandardScaler()), # 归一化数据
(‘poly’, PolynomialFeatures(degree=2, include_bias=False)), # 生成交互特征
(‘model’, LinearRegression()) # 线性回归
])
训练模型
pipeline.fit(X_train, y_train)
预测测试集
y_pred_refined = pipeline.predict(X_test)
评估模型
mae_refined = mean_absolute_error(y_test, y_pred_refined) mse_refined = mean_squared_error(y_test, y_pred_refined) r2_refined = r2_score(y_test, y_pred_refined)
mae_refined, mse_refined, r2_refined
🤔 多项式回归解析
1️⃣ 为什么要使用多项式特征?
(‘poly’, PolynomialFeatures(degree=2, include_bias=False))
✅ 自动生成特征交互项,比如:
temperature * dew_point
temperature² (非线性关系)
✅ 适用于非线性数据集,比如销量与天气的关系。
❌ 反面例子
(‘model’, LinearRegression()) # 仅使用线性关系
如果销量与天气的关系是非线性的,普通线性回归可能效果不好。
2️⃣ 为什么要标准化数据?
(‘scaler’, StandardScaler())
✅ 确保特征缩放一致,避免数值范围大的变量影响模型训练。
❌ 反面例子
pipeline = Pipeline([
(‘poly’, PolynomialFeatures(degree=2, include_bias=False)), (‘model’, LinearRegression())
])
没有标准化,可能导致某些变量主导模型,影响回归效果。
3️⃣ 如何评估改进后的模型?
mae_refined = mean_absolute_error(y_test, y_pred_refined) mse_refined = mean_squared_error(y_test, y_pred_refined) r2_refined = r2_score(y_test, y_pred_refined)
✅ 对比 r² 分数,如果 r²_refined > r² ,说明多项式回归比普通线性回归更好。
❌ 反面例子
print(y_test - y_pred_refined)
直接看误差没有定量衡量标准,难以比较模型优劣。
🔹 10.2 时间序列分析
from statsmodels.tsa.seasonal import seasonal_decompose from statsmodels.tsa.stattools import adfuller
处理时间格式
time_series_data = merged_data[[‘Total Sales’, ‘temperature’, ‘report_date’]].copy() merged_data[‘report_date’] = pd.to_datetime(time_series_data[‘report_date’])
按日期聚合销量
daily_sales = time_series_data.groupby(time_series_data[‘report_date’].dt.date)[‘Total Sal
平稳性检验(ADF 检验)
adf_test = adfuller(daily_sales, autolag=’AIC’) adf_statistic, adf_p_value = adf_test[0], adf_test[1]
绘制销量趋势
plt.figure(figsize=(12, 6))
plt.plot(daily_sales, label=’Daily Sales Volume’, color=’blue’) plt.title(‘📊 每日销量趋势’, fontsize=14)
plt.xlabel(‘日期’, fontsize=12) plt.ylabel(‘销量’, fontsize=12) plt.legend()
plt.grid(True) plt.show()
adf_statistic, adf_p_value
时间序列分析解析
1️⃣ 为什么要做时间序列分解?
daily_sales = time_series_data.groupby(time_series_data[‘report_date’].dt.date)[‘Total Sal
✅ 聚合每日销量,观察销量随时间的变化趋势。
✅ 识别季节性波动(如周末销量是否更高)。
❌ 反面例子
daily_sales = time_series_data[‘Total Sales’]
没有按日期聚合,数据可能仍然是小时级别,不适合分析长期趋势。
2️⃣ 为什么要做 ADF(Augmented Dickey-Fuller)检验?
adf_test = adfuller(daily_sales, autolag=’AIC’) adf_statistic, adf_p_value = adf_test[0], adf_test[1]
✅ 检查时间序列是否平稳:
p 值 < 0.05,数据是平稳的,可以直接建模。
p 值 > 0.05,数据是非平稳的,需要差分(Differencing)。
❌ 反面例子
daily_sales.plot()
只画图无法判断数据是否平稳,必须用统计检验。
3️⃣ 如何解释时间序列图?
plt.plot(daily_sales, label=’Daily Sales Volume’, color=’blue’)
✅ 可以发现哪些模式?
是否有上升趋势 📈?
是否存在周期性变化 ⏳(如周末销量更高)?
❌ 反面例子
plt.bar(daily_sales.index, daily_sales.values)
时间序列适合折线图,而柱状图不适用于连续数据。
总结
在本节中,我们: ✅ 使用了多项式回归,提高销量预测能力。
✅ 分析了时间序列模式,研究销量随时间的变化。
✅ 使用 ADF 检验检查了数据的平稳性,为未来建模做准备。
💡 思考:
- 你的销量数据是否有明显的季节性变化?
- 你的模型 r² 是否比普通线性回归更高?
本期文章就到这里了,如果想知道我们应该怎么用这些代码进行更深入的有效的分析,请关注我的下一篇文章💕