查看全课大纲(第 33 / 35 节)
- 1.特征工程简介
- 2.特征工程的处理流程
- 3.数据与特征概述
- 4.数据统计、相似度与数据探索
- 5.数据探索工具 Pandas_profiling
- 6.工具应用:《泰坦尼克》分析报告
- 7.构造一个微型链家租房数据集用于演示
- 8.数据预处理综述
- 9.缺失值处理实操
- 10.异常值处理实操
- 11.连续值特征构建
- 12.类别型数据特征构建
- 13.特征幅度缩放
- 14.特征离散化
- 15.特征编码:让类别数据参与建模
- 16.特征变换
- 17.时间特征
- 18.特征衍生
- 19.自动化特征工具
- 20.特征选择综述
- 21.特征选择方法
- 22.过滤式特征选择方法实操
- 23.包裹式特征选择方法
- 24.嵌入式特征选择
- 25.混序法与混合法特征选择
- 26.特征降维综述
- 27.案例实操:特征降维
- 28.特征监控概述与指标
- 29.Iris 数据集特征漂移检测案例
- 30.特征监控实战:用分布对比检测数据漂移
- 31.实时特征监控
- 32.电商销量预测项目背景介绍
- 33.数据预处理和特征工程(一)
- 34.数据预处理和特征工程(二)
- 35.数据建模
数据预处理和特征工程(一)
约 21 分钟
小象实战讲义 · 特征工程精讲与实践
在上一章,我们系统学习了特征监控的理论与工具。本章我们将进入一个完整的实战项目——Kaggle上的“Predict Future Sales”电商销量预测比赛。本节是项目实战的第一部分,我们将聚焦于数据预处理和基础特征工程的完整流程。学完本节,你将掌握如何从一个原始、多文件的电商数据集出发,通过数据清洗、异常值处理、类别特征编码、时间特征构建以及统计特征衍生,为后续的建模准备好高质量的输入特征。
💡 核心导读
- 项目背景与数据概览:了解Kaggle销量预测比赛的任务目标,认识原始数据(商品、类别、销售、商店信息)的结构与挑战。
- 数据清洗与预处理:针对销售数据,进行基于业务规则的异常值剔除、重复ID规整,并过滤出测试集相关的商品与商店。
- 类别特征编码与信息抽取:对商品类别、名称等字符串字段进行拆分、清洗,并使用标签编码(LabelEncoder)将其转换为模型可处理的数值形式。
- 时间特征与基础统计特征构建:从销售日期中提取星期、月份、年份等信息,并构建日期块编号;同时进行初步的聚合统计,如商店-星期销量占比、月度销售聚合等。
项目背景与数据加载
本项目基于Kaggle的“Predict Future Sales”比赛。任务目标是:利用历史销售数据,预测俄罗斯一家大型软件公司旗下商店在未来一个月(2015年11月)的商品月销量(item_cnt_month)。原始数据为俄文,我们使用一份英文翻译版本进行讲解。
首先,我们导入必要的工具库,并进行pandas的基础显示设置。
import pandas as pd
import numpy as np
from itertools import product
from sklearn.cluster import KMeans
from sklearn.preprocessing import LabelEncoder
import warnings
# pandas显示设置
pd.set_option('display.max_rows', 400)
pd.set_option('display.max_columns', 160)
pd.set_option('display.max_colwidth', 40)
warnings.filterwarnings("ignore")
print("工具库导入与设置完成。")工具库导入与设置完成。接下来,我们加载核心数据文件。为了演示清晰,我们使用构造的小样本数据来模拟原始数据(原始测试集约3MB,销售训练集约90MB)。
import pandas as pd
import numpy as np
# 1. 加载测试集(构造样例)
test_data = {
'ID': [0, 1, 2, 3, 4],
'shop_id': [5, 5, 9, 9, 5],
'item_id': [5037, 5320, 5233, 6001, 6002]
}
test = pd.DataFrame(test_data)
print("测试集数据(样例):")
print(test)
print(f"\n测试集形状: {test.shape}")
print("任务:预测每个 (shop_id, item_id) 组合在未来一个月的 item_cnt_month(月销量)。")
# 2. 加载商品类别数据(构造样例)
categories_data = {
'category_id': [40, 55, 16, 71, 83],
'category_name': ['PC - Headsets / Headphones', 'Games - PS4', 'Cinema - Blu-ray', 'Gifts - Figures', 'Music - MP3']
}
categories = pd.DataFrame(categories_data)
print("\n商品类别数据(样例):")
print(categories)
print("\n观察:category_name 字段包含主类目和子类目,由连接符 '-' 分隔。")测试集数据(样例):
ID shop_id item_id
0 0 5 5037
1 1 5 5320
2 2 9 5233
3 3 9 6001
4 4 5 6002
测试集形状: (5, 3)
任务:预测每个 (shop_id, item_id) 组合在未来一个月的 item_cnt_month(月销量)。
商品类别数据(样例):
category_id category_name
0 40 PC - Headsets / Headphones
1 55 Games - PS4
2 16 Cinema - Blu-ray
3 71 Gifts - Figures
4 83 Music - MP3
观察:category_name 字段包含主类目和子类目,由连接符 '-' 分隔。类别特征的处理与编码
在动手之前,先回答一个关键问题:为什么这里用标签编码而不是独热编码?因为我们最终选用的模型是树模型——树在做分裂时只关心「是否小于某个阈值」,序号编码的大小关系不会对它产生负面影响,可以直接使用。如果改用线性模型(注意本任务是回归问题,不能用逻辑回归,而是线性回归、多项式回归这类),通常需要独热编码;但独热的代价是把类别平铺成大量 0/1 列,数据维度急剧升高且非常稀疏。另外,LightGBM 等工具虽然支持直接指定类别型列,但当类别数非常多时,直接指定的建模效率可能很低、甚至训练很慢,所以实践中更常见的做法还是自己先做序号编码。
拆分与编码商品类别
观察到category_name字段的结构后,我们将其拆分为group_name(主类目)和子类目(此处保留原始完整名称),并对两者进行标签编码。
import pandas as pd
# 商品类别数据(与本节前文一致)
categories_data = {
'category_id': [40, 55, 16, 71, 83],
'category_name': ['PC - Headsets / Headphones', 'Games - PS4', 'Cinema - Blu-ray', 'Gifts - Figures', 'Music - MP3']
}
categories = pd.DataFrame(categories_data)
import pandas as pd
from sklearn.preprocessing import LabelEncoder
# 使用上一步构造的 categories DataFrame
# 拆分主类目(group_name)
categories['group_name'] = categories['category_name'].str.split(' - ').str[0]
print("拆分出主类目:")
print(categories[['category_id', 'category_name', 'group_name']])
# 初始化标签编码器
le = LabelEncoder()
# 对主类目进行编码
categories['group_id'] = le.fit_transform(categories['group_name'].values)
print("\n对主类目进行标签编码后:")
print(categories[['category_id', 'group_name', 'group_id']])拆分出主类目:
category_id category_name group_name
0 40 PC - Headsets / Headphones PC
1 55 Games - PS4 Games
2 16 Cinema - Blu-ray Cinema
3 71 Gifts - Figures Gifts
4 83 Music - MP3 Music
对主类目进行标签编码后:
category_id group_name group_id
0 40 PC 4
1 55 Games 1
2 16 Cinema 0
3 71 Gifts 2
4 83 Music 3商品信息清洗与特征抽取
加载商品信息表,对商品名称进行清洗(小写化、去除标点、停用词),并抽取名称的前N个字符作为可能代表商品系列的前缀特征,同样进行编码。
import pandas as pd
# 商品类别数据(与本节前文一致)
categories_data = {
'category_id': [40, 55, 16, 71, 83],
'category_name': ['PC - Headsets / Headphones', 'Games - PS4', 'Cinema - Blu-ray', 'Gifts - Figures', 'Music - MP3']
}
categories = pd.DataFrame(categories_data)
from sklearn.preprocessing import LabelEncoder
categories['group_name'] = categories['category_name'].str.split(' - ').str[0]
categories['group_id'] = LabelEncoder().fit_transform(categories['group_name'].values)
import pandas as pd
from sklearn.preprocessing import LabelEncoder
# 构造商品信息样例数据
items_data = {
'item_id': [2173, 2174, 3470, 3471, 3472],
'item_name': [
'Grand Theft Auto V (PS4)',
'Grand Theft Auto V (XBOX ONE)',
'AKG K 44 Headphones',
'Sony MDR-ZX110 Headphones',
'Star Wars Battlefront (PS4)'
],
'category_id': [55, 55, 40, 40, 55]
}
items = pd.DataFrame(items_data)
print("原始商品信息:")
print(items)
# 商品名称清洗
items['item_name_clean'] = items['item_name'].str.lower()
items['item_name_clean'] = items['item_name_clean'].str.replace(r'[^\w\s]', ' ', regex=True) # 去标点
# 简单模拟去除常见停用词
stop_words = ['the', 'a', 'an', 'and', 'or', 'for', 'of', 'on', 'in', 'to', 'with']
for w in stop_words:
items['item_name_clean'] = items['item_name_clean'].str.replace(rf'\b{w}\b', ' ', regex=True)
items['item_name_clean'] = items['item_name_clean'].str.replace(r'\s+', ' ', regex=True).str.strip()
print("\n清洗后的商品名称:")
print(items[['item_id', 'item_name', 'item_name_clean']])
# 抽取名称前缀(去除空格)
items['item_name_no_space'] = items['item_name_clean'].str.replace(' ', '')
items['item_name_first4'] = items['item_name_no_space'].str[:4]
items['item_name_first6'] = items['item_name_no_space'].str[:6]
# 对前缀特征进行标签编码
le = LabelEncoder()
items['item_name_first4_enc'] = le.fit_transform(items['item_name_first4'].values)
items['item_name_first6_enc'] = le.fit_transform(items['item_name_first6'].values)
print("\n抽取前缀并编码后:")
print(items[['item_id', 'item_name_first4', 'item_name_first4_enc', 'item_name_first6', 'item_name_first6_enc']])
# 将类别信息合并到商品表 (模拟join操作)
# 这里我们简单地将之前 categories 表的 group_id 根据 category_id 合并过来
items = pd.merge(items, categories[['category_id', 'group_id']], on='category_id', how='left')
print("\n合并类别信息后的商品表(部分列):")
print(items[['item_id', 'item_name', 'category_id', 'group_id']])原始商品信息:
item_id item_name category_id
0 2173 Grand Theft Auto V (PS4) 55
1 2174 Grand Theft Auto V (XBOX ONE) 55
2 3470 AKG K 44 Headphones 40
3 3471 Sony MDR-ZX110 Headphones 40
4 3472 Star Wars Battlefront (PS4) 55
清洗后的商品名称:
item_id item_name item_name_clean
0 2173 Grand Theft Auto V (PS4) grand theft auto v ps4
1 2174 Grand Theft Auto V (XBOX ONE) grand theft auto v xbox one
2 3470 AKG K 44 Headphones akg k 44 headphones
3 3471 Sony MDR-ZX110 Headphones sony mdr zx110 headphones
4 3472 Star Wars Battlefront (PS4) star wars battlefront ps4
抽取前缀并编码后:
item_id item_name_first4 item_name_first4_enc item_name_first6 \
0 2173 gran 1 grandt
1 2174 gran 1 grandt
2 3470 akgk 0 akgk44
3 3471 sony 2 sonymd
4 3472 star 3 starwa
item_name_first6_enc
0 1
1 1
2 0
3 2
4 3
合并类别信息后的商品表(部分列):
item_id item_name category_id group_id
0 2173 Grand Theft Auto V (PS4) 55 1
1 2174 Grand Theft Auto V (XBOX ONE) 55 1
2 3470 AKG K 44 Headphones 40 4
3 3471 Sony MDR-ZX110 Headphones 40 4
4 3472 Star Wars Battlefront (PS4) 55 1销售数据清洗与预处理
销售数据是核心,但往往包含异常值。我们首先进行基于业务规则的清洗。
import pandas as pd
# 构造销售训练数据样例
sales_raw_data = {
'date': ['02.01.2013', '03.01.2013', '04.01.2013', '05.01.2013', '10.01.2013'],
'date_block_num': [0, 0, 0, 0, 0],
'shop_id': [25, 25, 25, 25, 25],
'item_id': [2173, 2174, 3470, 3471, 9999], # 9999为测试集未出现的商品
'item_price': [1499.0, 1499.0, 799.0, 399.0, -100.0], # 包含一个异常价格
'item_cnt_day': [1, 2, 1, 5, 2000] # 包含一个异常销量
}
sales_raw = pd.DataFrame(sales_raw_data)
print("原始销售数据(样例,含异常值):")
print(sales_raw)
# 方法1:使用 .query() 进行异常值过滤(原代码方法)
sales_clean = sales_raw.query('0 < item_price < 50000 and 0 < item_cnt_day < 1001')
print("\n使用.query()过滤异常值后:")
print(sales_clean)
# 方法2:使用布尔索引实现相同的过滤(等效方法)
sales_bool_index = sales_raw[
(sales_raw['item_price'] > 0) &
(sales_raw['item_price'] < 50000) &
(sales_raw['item_cnt_day'] > 0) &
(sales_raw['item_cnt_day'] < 1001)
]
print("\n使用布尔索引过滤后的数据:")
print(sales_bool_index)
print("两种方法结果一致:", sales_clean.equals(sales_bool_index))
# 过滤掉测试集中未出现的商店(本例中shop_id 25在测试集样例中未出现,仅为演示逻辑)
test_shop_ids = [5, 9] # 来自之前构造的测试集
sales_clean = sales_clean[sales_clean['shop_id'].isin(test_shop_ids)]
print("\n过滤掉测试集未出现的商店后(本例中shop_id 25被过滤):")
print(sales_clean)补充说明两点数据规整的口径(本样例数据量太小,不再单独演示):
- 重复记录去重(原课执行的规整操作):原始商品表中存在商品名称与商品 ID 重复出现的记录,用
pandas的duplicated()做去重规整。 - 商品维度过滤(原课未采用的可选简化):如果只想保留测试集中出现过的商品,可以类比商店过滤用
sales['item_id'].isin(test_item_ids)滤掉非测试集商品。但原课并未这样做——全部商品的历史销量被保留,用于后续月度网格构建与 lag 类特征的计算,这样新品/测试商品的滞后特征才有历史可循;是否按商品过滤属于工程取舍,需结合内存与建模目标决定。
原始销售数据(样例,含异常值):
date date_block_num shop_id item_id item_price item_cnt_day
0 02.01.2013 0 25 2173 1499.0 1
1 03.01.2013 0 25 2174 1499.0 2
2 04.01.2013 0 25 3470 799.0 1
3 05.01.2013 0 25 3471 399.0 5
4 10.01.2013 0 25 9999 -100.0 2000
使用.query()过滤异常值后:
date date_block_num shop_id item_id item_price item_cnt_day
0 02.01.2013 0 25 2173 1499.0 1
1 03.01.2013 0 25 2174 1499.0 2
2 04.01.2013 0 25 3470 799.0 1
3 05.01.2013 0 25 3471 399.0 5
使用布尔索引过滤后的数据:
date date_block_num shop_id item_id item_price item_cnt_day
0 02.01.2013 0 25 2173 1499.0 1
1 03.01.2013 0 25 2174 1499.0 2
2 04.01.2013 0 25 3470 799.0 1
3 05.01.2013 0 25 3471 399.0 5
两种方法结果一致: True
过滤掉测试集未出现的商店后(本例中shop_id 25被过滤):
Empty DataFrame
Columns: [date, date_block_num, shop_id, item_id, item_price, item_cnt_day]
Index: []时间特征与基础统计特征构建
清洗后的销售数据需要转换日期格式,并从中提取丰富的特征。
import pandas as pd
import numpy as np
# 重新构造一份清洗后的销售数据用于特征构建演示
sales_data = {
'date': ['02.01.2013', '03.01.2013', '04.01.2013', '05.01.2013', '02.02.2013', '03.02.2013'],
'date_block_num': [0, 0, 0, 0, 1, 1],
'shop_id': [5, 5, 5, 9, 5, 9],
'item_id': [5037, 5320, 5037, 5233, 5320, 6001],
'item_price': [1499.0, 899.0, 1499.0, 1999.0, 899.0, 2499.0],
'item_cnt_day': [1, 2, 1, 1, 3, 2]
}
sales = pd.DataFrame(sales_data)
print("用于特征构建的销售数据(清洗后样例):")
print(sales)
# 1. 日期转换与基本时间特征提取
sales['date'] = pd.to_datetime(sales['date'], format='%d.%m.%Y')
sales['weekday'] = sales['date'].dt.dayofweek # 星期几 (0=周一)
sales['month'] = sales['date'].dt.month
sales['year'] = sales['date'].dt.year - 2013 # 相对于2013年的年份偏移
print("\n添加基本时间特征后:")
print(sales[['date', 'weekday', 'month', 'year']].head())
# 2. 计算每日收入
sales['revenue'] = sales['item_cnt_day'] * sales['item_price']
print("\n计算每日收入后:")
print(sales[['item_cnt_day', 'item_price', 'revenue']].head())
# 3. 构建“日期块编号” - 这是本项目的核心时间索引
# date_block_num = (year * 12) + month - 1
# 我们的数据中已提供此列,此处验证其计算逻辑
sales['date_block_num_calc'] = sales['year'] * 12 + sales['month'] - 1
print("\n验证 date_block_num 计算逻辑(应与原列一致):")
print(sales[['date_block_num', 'year', 'month', 'date_block_num_calc']].head())
# 4. 聚合统计:按商店和星期几统计销量占比
# 计算每个商店每个星期几的总销量
shop_weekday_sales = sales.groupby(['shop_id', 'weekday'], as_index=False)['item_cnt_day'].sum()
shop_total_sales = sales.groupby('shop_id', as_index=False)['item_cnt_day'].sum().rename(columns={'item_cnt_day': 'shop_total'})
# 合并计算占比
weekday_ratio = pd.merge(shop_weekday_sales, shop_total_sales, on='shop_id', how='left')
weekday_ratio['day_quality'] = weekday_ratio['item_cnt_day'] / weekday_ratio['shop_total']
print("\n商店-星期销量占比特征:")
print(weekday_ratio)
# 5. 核心聚合:按 date_block_num, shop_id, item_id 聚合为月度数据
sales_monthly = sales.groupby(['date_block_num', 'shop_id', 'item_id'], as_index=False).agg({
'item_cnt_day': 'sum',
'revenue': 'sum'
}).rename(columns={'item_cnt_day': 'item_cnt_month'})
print("\n聚合后的月度销售数据(核心训练单元):")
print(sales_monthly)用于特征构建的销售数据(清洗后样例):
date date_block_num shop_id item_id item_price item_cnt_day
0 02.01.2013 0 5 5037 1499.0 1
1 03.01.2013 0 5 5320 899.0 2
2 04.01.2013 0 5 5037 1499.0 1
3 05.01.2013 0 9 5233 1999.0 1
4 02.02.2013 1 5 5320 899.0 3
5 03.02.2013 1 9 6001 2499.0 2
添加基本时间特征后:
date weekday month year
0 2013-01-02 2 1 0
1 2013-01-03 3 1 0
2 2013-01-04 4 1 0
3 2013-01-05 5 1 0
4 2013-02-02 5 2 0
计算每日收入后:
item_cnt_day item_price revenue
0 1 1499.0 1499.0
1 2 899.0 1798.0
2 1 1499.0 1499.0
3 1 1999.0 1999.0
4 3 899.0 2697.0
验证 date_block_num 计算逻辑(应与原列一致):
date_block_num year month date_block_num_calc
0 0 0 1 0
1 0 0 1 0
2 0 0 1 0
3 0 0 1 0
4 1 0 2 1
商店-星期销量占比特征:
shop_id weekday item_cnt_day shop_total day_quality
0 5 2 1 7 0.142857
1 5 3 2 7 0.285714
2 5 4 1 7 0.142857
3 5 5 3 7 0.428571
4 9 5 1 3 0.333333
5 9 6 2 3 0.666667
聚合后的月度销售数据(核心训练单元):
date_block_num shop_id item_id item_cnt_month revenue
0 0 5 5037 2 2998.0
1 0 5 5320 2 1798.0
2 0 9 5233 1 1999.0
3 1 5 5320 3 2697.0
4 1 9 6001 2 4998.0📝 动手练一练
- 概念辨析:在清洗销售数据时,我们使用了
.query('0 < item_price < 50000 and 0 < item_cnt_day < 1001')来过滤异常值。请简述这种基于业务规则设定阈值的方法,与基于统计方法(如3σ原则)或模型方法(如孤立森林)进行异常值检测的优缺点分别是什么?
👉 点击查看参考答案
- 业务规则法(本节课所用):
- 优点:直观、可解释性强,紧密结合业务常识(如价格不可能为负或天价)。计算简单高效。
- 缺点:阈值依赖人工经验,可能不够精确;对于隐藏的、不符合常识但分布异常的“软”异常点可能失效。
- 统计方法(如3σ原则):
- 优点:基于数据自身分布,无需过多先验知识;对服从近似正态分布的数据效果较好。
- 缺点:对非正态分布数据效果差;对多变量间的复杂异常关系捕捉能力弱;同样可能误杀正常点。
- 模型方法(如孤立森林):
- 优点:无需假设数据分布,能捕捉多变量间复杂的异常模式;适用于高维数据。
- 缺点:计算成本较高;模型本身有参数需要调整;可解释性相对较差。
在实际工业项目中,通常建议结合使用:先用业务规则剔除“硬”异常(明显错误),再使用统计或模型方法挖掘“软”异常。
- 动手实践:在“时间特征与基础统计特征构建”部分的代码中,我们计算了
weekday_ratio(商店-星期销量占比)。请尝试修改代码,计算每个商店(shop_id)在所有日期内的总销售额(revenue总和),并计算每个商品(item_id)在该商店总销售额中的贡献占比。
👉 点击查看参考答案
import pandas as pd
# 商品类别数据(与本节前文一致)
categories_data = {
'category_id': [40, 55, 16, 71, 83],
'category_name': ['PC - Headsets / Headphones', 'Games - PS4', 'Cinema - Blu-ray', 'Gifts - Figures', 'Music - MP3']
}
categories = pd.DataFrame(categories_data)
import pandas as pd
# 清洗后的销售数据(与上一块一致,含已计算的 revenue 列)
sales_data = {
'date': ['02.01.2013', '03.01.2013', '04.01.2013', '05.01.2013', '02.02.2013', '03.02.2013'],
'date_block_num': [0, 0, 0, 0, 1, 1],
'shop_id': [5, 5, 5, 9, 5, 9],
'item_id': [5037, 5320, 5037, 5233, 5320, 6001],
'item_price': [1499.0, 899.0, 1499.0, 1999.0, 899.0, 2499.0],
'item_cnt_day': [1, 2, 1, 1, 3, 2]
}
sales = pd.DataFrame(sales_data)
sales['revenue'] = sales['item_cnt_day'] * sales['item_price']
import pandas as pd
# 使用之前构造的 sales DataFrame(包含 revenue 列)
# 计算每个商店的总销售额
shop_total_revenue = sales.groupby('shop_id', as_index=False)['revenue'].sum().rename(columns={'revenue': 'shop_total_rev'})
# 计算每个商店-商品组合的销售额
shop_item_revenue = sales.groupby(['shop_id', 'item_id'], as_index=False)['revenue'].sum().rename(columns={'revenue': 'item_rev_in_shop'})
# 合并计算贡献占比
contribution = pd.merge(shop_item_revenue, shop_total_revenue, on='shop_id', how='left')
contribution['rev_contribution_ratio'] = contribution['item_rev_in_shop'] / contribution['shop_total_rev']
print("商品在商店销售额中的贡献占比:")
print(contribution.sort_values(['shop_id', 'rev_contribution_ratio'], ascending=[True, False]))运行结果示例(取决于具体数据):
商品在商店销售额中的贡献占比:
shop_id item_id item_rev_in_shop shop_total_rev rev_contribution_ratio
1 5 5320 4495.0 7493.0 0.599893
0 5 5037 2998.0 7493.0 0.400107
3 9 6001 4998.0 6997.0 0.714306
2 9 5233 1999.0 6997.0 0.285694此特征可以反映商品在特定商店中的重要性,是一个有价值的衍生特征。
本章小结
本节我们正式开启了电商销量预测项目的实战。我们从理解比赛任务和数据文件开始,逐步完成了数据预处理和基础特征工程的关键步骤:
- 数据加载与观察:认识了测试集、商品类别、商品信息和销售数据等多表结构,明确了预测目标。
- 类别特征处理:对结构化的类别名称进行拆分,对非结构化的商品名称进行清洗和前缀抽取,并统一使用
LabelEncoder转换为数值型特征,为树模型做好准备。 - 销售数据清洗:基于业务规则(价格、销量范围)过滤异常值,并筛选出与测试集相关的商店,聚焦核心预测目标。
- 时间与统计特征构建:从日期字段中提取了星期、月份、年份等基础时间特征,构建了核心的
date_block_num(月份块编号),并进行了初步的聚合统计,生成了商店-星期销量占比、月度销量与收入等特征。
这些步骤构成了特征工程工作流的坚实起点。处理后的数据已经从一个原始的、多文件的、包含异常和字符串的状态,转变为一个更干净、更具信息量的结构化表格。
📋 行动清单
- 在你的环境中,尝试运行本节提供的自包含代码块,理解每一步数据转换的输出结果。
- 思考:如果商品名称不是英文,而是中文或其他语言,清洗和抽取前缀的策略需要如何调整?
- 预习:在下一节,我们将学习如何构建更复杂的滞后特征(lag features)、价格相关特征以及基于聚类的商店特征。
—— 小象教研组