【Kaggle】练习赛《预测贴纸的销量》(下)

news2025/1/13 2:49:07

前言

上篇利用各地区的GDP数据还填充目标标签的缺失值;中篇顺着这个思路,利用这个原理来预测未来的销量,具体方法思路:先一一对国家、产品和商店进行汇总,然后对未来三年的每日销售额进行预测,然后再进行分解,得到每个国家、产品和商店的销售额;本篇利用机器学习的常规思路进行预测未来的销量。
分别采用了不同的方案,对时间序列的特点,根据上篇EDA的特性,增加了周期性的内容,如何正弦,余弦特征列等。

加载库

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

加载数据

train = pd.read_csv("/kaggle/input/playground-series-s5e1/train.csv")
test = pd.read_csv("/kaggle/input/playground-series-s5e1/test.csv")

方案一

# 复制一份原始数据
train_data=train.copy()
test_data=test.copy()
# 查看缺失值情况
train_data.isna().sum().sort_values(ascending=False)

num_sold 8871
id 0
date 0
country 0
store 0
product 0
dtype: int64

# 直接删除
train_data = train_data.dropna()
EDA
train_data['date'] = pd.to_datetime(train_data['date'])
test_data['date'] = pd.to_datetime(test_data['date'])

train_data['Year'] = train_data['date'].dt.year
train_data['Month'] = train_data['date'].dt.month
train_data['Day'] = train_data['date'].dt.day

test_data['Year'] = test_data['date'].dt.year
test_data['Month'] = test_data['date'].dt.month
test_data['Day'] = test_data['date'].dt.day

简单的处理一下年月日,根据上篇EDA基本特征。

# 删除 date
train_data.drop('date',axis=1,inplace=True)
test_data.drop('date',axis=1,inplace=True)
分离出分类和数据特征
train_data = train_data.drop('id', axis = 1)
num_cols = list(train_data.select_dtypes(exclude=['object']).columns.difference(['num_sold']))
cat_cols = list(train_data.select_dtypes(include=['object']).columns)

test_data = test_data.drop('id', axis = 1)
num_cols_test = list(test_data.select_dtypes(exclude=['object']).columns.difference(['id']))
cat_cols_test = list(test_data.select_dtypes(include=['object']).columns)
对分类特征进行编码
from sklearn.preprocessing import LabelEncoder
# Initialize LabelEncoder
label_encoders = {col: LabelEncoder() for col in cat_cols}

# Apply LabelEncoder to each categorical column
for col in cat_cols:
    train_data[col] = label_encoders[col].fit_transform(train_data[col])
    test_data[col] = label_encoders[col].transform(test_data[col])
对分离X、y ,并分隔训练集和验证集
from sklearn.model_selection import train_test_split
X = train_data.drop(['num_sold'], axis=1)
y = train_data['num_sold']


# Split datainto training set and test set
X_train, X_valid, y_train, y_valid = train_test_split(X, y, test_size=0.2, random_state=42)
引入xgbboost的回归算法库,定义评估函数
from xgboost import XGBRegressor
from sklearn.metrics import mean_squared_error, mean_absolute_percentage_error
# Define MAPE metric
def mape(y_true, y_pred):
    return mean_absolute_percentage_error(y_true, y_pred)
def mse(y_true, y_pred):
    return mean_squared_error(y_true, y_pred)
用xgbboost的回归算法建模
xgb = XGBRegressor(random_state=42)
xgb.fit(X_train, y_train)
预测和评估
# 预测和评估
y_pred = xgb.predict(X_valid)

score = mape(y_valid, y_pred)
me=mse(y_valid, y_pred)
score,me

(0.30150839272752505, 9599.83371487707)

方案二

查看标签的偏度和峰度
train_data['num_sold'].skew(),train_data['num_sold'].kurtosis()

(1.4153734524983919, 2.61233506292136)

发现数据分布偏度较大

画图查看数据分布
sns.histplot(data=y, kde=True, stat="density");

 0

从图形看出,呈现明显偏态情况,因此对数据进行对数处理,再看图形

sns.histplot(data=np.log(y), kde=True, stat="density");

01

经过对数处理后,偏态情况明显改善,因此以对数后的值作为标签进行建模

对数处理后
y=np.log1p(y)
X_train, X_valid, y_train, y_valid = train_test_split(X, y, test_size=0.2, random_state=42)

不直接用log函数,而采用np.log1p函数,即加1后求对数 ,更好避免 0 求对数,同理,评估和预测时,是需要返回的,即使用np.expm1的方式。

建模、预测、评估
xgb = XGBRegressor(random_state=42)
xgb.fit(X_train, y_train)
 # 预测和评估
y_pred = xgb.predict(X_valid)

# score = mape(y_valid, y_pred) # 注意这里不对的,一定要返回原来的值,=否则不能比较了
# me=mse(y_valid, y_pred)
# (0.012581281054301561, 0.00724846629002101)

score = mape(np.expm1(y_valid), np.expm1(y_pred))
me=mse(np.expm1(y_valid), np.expm1(y_pred))
score,me

(0.06777639649910845, 8359.441860251725)

效果提升明显

评测试结果

方案MAPE 得分处理方式
方案一0.3015年月日分离
方案二0.0678方案一基础上将标签对数处理

方案三

train_data=train.copy()
test_data=test.copy()
train_data = train_data.dropna()
常规年月日处理
train_data['date'] = pd.to_datetime(train_data['date'])
test_data['date'] = pd.to_datetime(test_data['date'])

train_data['year'] = train_data['date'].dt.year
train_data['month'] = train_data['date'].dt.month
train_data['day'] = train_data['date'].dt.day

test_data['year'] = test_data['date'].dt.year
test_data['month'] = test_data['date'].dt.month
test_data['day'] = test_data['date'].dt.day
增加GDP权重比率

这里增加的方式与前面的方式不一样,直接用API调用的方式,灵活方便

# Function to fetch GDP per capita
import requests
def get_gdp_per_capita(country, year):
    alpha3 = {
        'Canada': 'CAN', 'Finland': 'FIN', 'Italy': 'ITA',
        'Kenya': 'KEN', 'Norway': 'NOR', 'Singapore': 'SGP'
    }
    url = f"https://api.worldbank.org/v2/country/{alpha3[country]}/indicator/NY.GDP.PCAP.CD?date={year}&format=json"
    response = requests.get(url).json()
    try:
        return response[1][0]['value']
    except (IndexError, TypeError):
        return None

countries = ['Canada', 'Finland', 'Italy', 'Kenya', 'Norway', 'Singapore']
years = range(2010, 2020)
gdp_data = {}

for country in countries:
    for year in years:
        gdp_data[(country, year)] = get_gdp_per_capita(country, year)

# Add GDP feature to train and test DataFrames
def add_gdp_feature(df):
    df['date'] = pd.to_datetime(df['date'])
    df['year'] = df['date'].dt.year  # Extract year from the date
    df['gdp'] = df.apply(lambda row: gdp_data.get((row['country'], row['year']), None), axis=1)
    return df

# Apply to train and test datasets
train_data = add_gdp_feature(train_data)
test_data = add_gdp_feature(test_data)

填充训练集和测试GDP数据

from sklearn.preprocessing import LabelEncoder
# Initialize LabelEncoder
label_encoders = {col: LabelEncoder() for col in cat_cols}

# Apply LabelEncoder to each categorical column
for col in cat_cols:
    train_data[col] = label_encoders[col].fit_transform(train_data[col])
    test_data[col] = label_encoders[col].transform(test_data[col])
train_date=train_data.pop('date')
test_date=test_data.pop('date')
train_data = train_data.drop('id', axis = 1)
num_cols = list(train_data.select_dtypes(exclude=['object']).columns.difference(['num_sold']))
cat_cols = list(train_data.select_dtypes(include=['object']).columns)

test_data = test_data.drop('id', axis = 1)
num_cols_test = list(test_data.select_dtypes(exclude=['object']).columns.difference(['id']))
cat_cols_test = list(test_data.select_dtypes(include=['object']).columns)
查看数据
train_data.head()
-countrystoreproductnum_soldyearmonthdaygdp
1001973.020101147560.666601
2002906.020101147560.666601
3003423.020101147560.666601
4004491.020101147560.666601
5020300.020101147560.666601
X = train_data.drop(['num_sold'], axis=1)
y = np.log1p(train_data['num_sold'])
xgb = XGBRegressor(random_state=42)
xgb.fit(X_train, y_train)
 # 预测和评估
y_pred = xgb.predict(X_valid)
score = mape(np.expm1(y_valid), np.expm1(y_pred))
me=mse(np.expm1(y_valid), np.expm1(y_pred))
score,me

(0.06931576130020592, 8393.297674737376)

test_pred = xgb.predict(test_data)
submission = pd.DataFrame({'id': test['id'], 'num_sold': np.expm1(test_pred)})
print(submission.head())
submission.to_csv('submission_xgb_log.csv', index=False)
-idnum_sold
0230130134.386810
1230131759.479980
2230132680.182922
3230133350.549652
4230134413.237976

方案四

增加星期几的信息
train_data['weekday']=train_date.dt.weekday
test_data['weekday'] = test_date.dt.weekday
X = train_data.drop(['num_sold'], axis=1)
y = np.log1p(train_data['num_sold'])

X_train, X_valid, y_train, y_valid = train_test_split(X, y, test_size=0.2, random_state=42)
xgb = XGBRegressor(random_state=42)
xgb.fit(X_train, y_train)
 # 预测和评估
y_pred = xgb.predict(X_valid)
score = mape(np.expm1(y_valid), np.expm1(y_pred))

(0.045671587590535107, 3451.7557869198386)

test_pred = xgb.predict(test_data)
submission = pd.DataFrame({'id': test['id'], 'num_sold': np.expm1(test_pred)})
print(submission.head())
submission.to_csv('submission_xgb_log11.csv', index=False)
-idnum_sold
0230130162.664078
1230131892.149536
2230132819.040649
3230133412.940216
4230134496.703644

方案五

train_data=train.copy()
test_data=test.copy()
train_data = train_data.dropna()
def eda(df):
    df['date'] = pd.to_datetime(df['date'])
    df['year'] = df['date'].dt.year
    df['day'] = df['date'].dt.day
    df['month'] = df['date'].dt.month
    df['quarter'] = df['date'].dt.quarter
    df['month_name'] = df['date'].dt.month_name()
    df['day_of_week'] = df['date'].dt.day_name()
    df['week'] = df['date'].dt.isocalendar().week
    df['month_sin'] = np.sin(2 * np.pi * df['month'] / 12) 
    df['month_cos'] = np.cos(2 * np.pi * df['month'] / 12)
    df['quarter_sin'] = np.sin(2 * np.pi * df['quarter'] / 4)
    df['quarter_cos'] = np.cos(2 * np.pi * df['quarter'] / 4)
    df['day_sin'] = np.sin(2 * np.pi * df['day'] / 31)  
    df['day_cos'] = np.cos(2 * np.pi * df['day'] / 31)
    df['group'] = (df['year'] - 2020) * 48 + df['month'] * 4 + df['day'] // 7
    df.drop('date', axis=1, inplace=True)
    df['cos_year'] = np.cos(df['year'] * (2 * np.pi) / 100)
    df['sin_year'] = np.sin(df['year'] * (2 * np.pi) / 100)
    # why using sin/cos? to tell model that after Dec we have Jan, of we dont do this it will
    # consider 1 to 12 and then 12 to 1 wont be considered. same applies on week day also
    # this is universal funnction whenever we have date.
    dummy_prefixes = ['country', 'store', 'product','month_name','day_of_week']
    df = pd.get_dummies(df, columns=dummy_prefixes, drop_first=True)

    return df

train_data = eda(train_data)
test_data = eda(test_data)
train_data = train_data.drop('id',axis=1)
test_data = test_data.drop('id',axis=1)
X = train_data.drop(['num_sold'], axis=1)
y = np.log1p(train_data['num_sold'])

X_train, X_valid, y_train, y_valid = train_test_split(X, y, test_size=0.2, random_state=42)
xgb = XGBRegressor(random_state=42)
xgb.fit(X_train, y_train)
# 预测和评估
y_pred = xgb.predict(X_valid)
score = mape(np.expm1(y_valid), np.expm1(y_pred))
me=mse(np.expm1(y_valid), np.expm1(y_pred))
score,me

(0.04549245301889767, 3233.53332460358)

test_pred = xgb.predict(test_data)
submission = pd.DataFrame({'id': test['id'], 'num_sold': np.expm1(test_pred)})
print(submission.head())
submission.to_csv('submission_xgb_log_up.csv', index=False)
-idnum_sold
0230130144.742188
1230131869.433594
2230132759.550293
3230133403.059479
4230134456.856323

这个0.0454924提交后的成绩如下,成绩远不如中篇的不用模型进行比率计算的结果。
在这里插入图片描述

评测总结果

方案MAPE 得分处理方式
方案一0.3015年月日分离
方案二0.06777方案一基础上将标签对数处理
方案三0.06931方案二基础上增加GDP数据
方案四0.04567方案三基础上增加星期几内容
方案五0.04549重新特征工程

总结

一、对时间次序数据的特征处理,有以下

  1. 第一步一般采用年月日分离处理;
  2. 有些国家,特别在销售产品时,节假日(Holiday)也有一定的效果;
  3. GDP数据直接会影响结果,在方案三处理后的效果不明显;
  4. 在销量上周末的特征表现明显;
  5. 通上篇EDA,发现销量是有规律的周期性变化的,因此可以采用三角函数处理方式,效果明显;
    二、本文只对特征工程展开研究和讨论,并没有在建模和调参上做一些说明,因此在模型上还可以选择 RandomforestLightGBMcatboost等回归算法; 在调参方面可以用之前讲过的Optuna 来优化; 通过这些处理,成绩肯定会有所提升;
    三、详测结果只能代表在验证集的效果,最终的成绩只提交看到,因此,两者还存在着较大的差距的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2275764.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

RT-DETR代码详解(官方pytorch版)——参数配置(1)

前言 RT-DETR虽然是DETR系列,但是它的代码结构和之前的DETR系列代码不一样。 它是通过很多的yaml文件进行参数配置,和之前在train.py的parser argparse.ArgumentParser()去配置所有参数不同,所以刚开始不熟悉代码的时候可能不知道在哪儿修…

细说STM32F407单片机以DMA方式读写外部SRAM的方法

目录 一、工程配置 1、时钟、DEBUG、GPIO、CodeGenerator 2、USART3 3、NVIC 4、 FSMC 5、DMA 2 (1)创建MemToMem类型DMA流 (2)开启DMA流的中断 二、软件设计 1、KEYLED 2、fsmc.h、fsmc.c、dma.h、dma.c 3、main.h…

Proteus-8086调试汇编格式的一点心得

这阵子开始做汇编的微机实验(微机原理与接口技术题解及实验指导,吴宁版本13章),中间出了挺多问题,解决后记录下。 先上电路图 用子电路来仿真发现仿真的时候子电路这块根本没有高低电平输出,只好把子电路拿…

FreeROTS学习 内存管理

内存管理是一个系统基本组成部分,FreeRTOS 中大量使用到了内存管理,比如创建任务、信号量、队列等会自动从堆中申请内存,用户应用层代码也可以 FreeRTOS 提供的内存管理函数来申请和释放内存 FreeRTOS 内存管理简介 FreeRTOS 创建任务、队列…

【西北工业大学主办 | EI检索稳定 | 高H值专家与会报告】2025年航天航空工程与材料技术国际会议(AEMT 2025)

2025 年航天航空工程与材料技术国际会议(AEMT 2025)将于2025年2月28日至3月2日在中国天津召开。本届会议由西北工业大学主办,由北京航空航天大学、北京理工大学作为支持单位加入,AEIC 学术交流中心协办。 AEMT 2025 旨在汇聚来自全…

目标检测跟踪中的Siamese孪生网络与普通卷积网络(VGG、ResNet)有什么区别?

1、什么是Siamese网络? Siamese网络又叫孪生网络,是一种特殊的神经网络架构,由一对(或多对)共享参数的子网络组成,用于学习输入样本之间的相似性或关系。最早在 1994 年由 Bromley 等人提出,最…

网络攻击行为可视化分析系统【数据分析 + 可视化】

一、系统背景 随着信息技术的快速发展,网络已成为现代社会不可或缺的一部分。然而,与此同时,网络攻击手段也日益多样化和复杂化,给企业和个人的信息安全带来了极大的威胁。传统的网络攻击分析方法往往依赖于人工分析和处理大量的…

一个运行在浏览器中的开源Web操作系统Puter本地部署与远程访问

文章目录 前言1.关于Puter2.本地部署Puter3.Puter简单使用4. 安装内网穿透5.配置puter公网地址6. 配置固定公网地址 💡 推荐 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。【点击跳转到网站…

C语言 操作符_位操作符、赋值操作符、单目操作符

1.位操作符 & - 按(2进制)位与 | - 按(2进制)位或 ^ - 按(2进制)位异或 只适用于整型 例:实现交换两个变量的值,要求不能新建变量 //3^3 0 -> a^a 0 //011 //011 //000 …

图像处理 | 图像二值化

在图像处理领域,图像二值化是一个重要的操作,它将彩色或灰度图像转换为只有两种颜色(通常是黑白)的图像。二值化广泛应用于文字识别、图像分割、边缘检测等领域,尤其在处理简洁和高对比度的图像时非常有效。本文将深入…

IP 地址与蜜罐技术

基于IP的地址的蜜罐技术是一种主动防御策略,它能够通过在网络上布置的一些看似正常没问题的IP地址来吸引恶意者的注意,将恶意者引导到预先布置好的伪装的目标之中。 如何实现蜜罐技术 当恶意攻击者在网络中四处扫描,寻找可入侵的目标时&…

Web基础之什么是HTTP协议

Q:什么是HTTP协议? 概念:Hyper Text Transfer Protocol,超文本传输协议,规定了浏览器和服务器之间数据传输的规则。 特点: 1.基于TCP协议:面向连接,安全 2.基…

#渗透测试#谷歌扩展学习#编写一个属于自己的谷歌扩展

目录 一、Chrome扩展程序是什么 二、如何自己编写一个简单谷歌扩展 1. 创建项目文件夹 2. 创建 manifest.json 文件 3. 创建 popup.html 文件 4. 创建 popup.js 文件 5. 加载扩展程序到Chrome浏览器 6. 测试扩展程序 三、Chrome插件图标设计技巧 1. 简洁明了 2. 独特…

LayerNorm的思考

文章目录 1. LayerNorm2. 图解3. softmax4. python 代码 1. LayerNorm y x − E [ x ] v a r ( x ) ϵ ∗ γ β \begin{equation} y\frac{x-\mathrm{E}[x]}{\sqrt{\mathrm{var}(x)\epsilon}}*\gamma\beta \end{equation} yvar(x)ϵ ​x−E[x]​∗γβ​​ 2. 图解 矩阵A …

ExplaineR:集成K-means聚类算法的SHAP可解释性分析 | 可视化混淆矩阵、决策曲线、模型评估与各类SHAP图

集成K-means聚类算法的SHAP可解释性分析 加载数据集并训练机器学习模型 SHAP 分析以提取特征对预测的影响 通过混淆矩阵可视化模型性能 决策曲线分析 模型评估(多指标和ROC曲线的目视检查) 带注释阈值的 ROC 曲线 加载 SHAP 结果以进行下游分析 与…

Kafka 会丢消息吗?

目录 01 生产者(Producer) 02 消息代理(Broker) 03 消费者(Consumer) 来源:Kafka 会丢消息吗? Kafka 会丢失信息吗? 许多开发人员普遍认为,Kafka 的设计本身就能保证不会丢失消息。然而,Kafka 架构和配置的细微差别会导致消息的丢失。我们需要了解它如何以及何时…

Open FPV VTX开源之第一次出图

Open FPV VTX开源之第一次出图 1. 源由2. 连线2.1 飞控2.2 调试 3. serial3.1 启动log - uboot3.2 登录版本 - linux3.3 获取有线IP 4. ssh - linux5. PixelPilot出图6. 总结7. 参考资料8. 补充 - 8812AU网卡 1. 源由 在《Open FPV VTX开源之硬件规格及组成》章节中&#xff0…

仓颉笔记——写一个简易的web服务并用浏览器打开

创建一个web服务端,同时创建一个客户端去读取这个服务端。 也满足浏览器打开web的需求。 直接上代码。 import net.http.* import std.time.* import std.sync.* import std.log.LogLevel// 1. 构建 Server 实例 let server ServerBuilder().addr("127.0.0.1&…

Trie树算法

Trie树,也称为前缀树或字典树,是一种特殊的树型数据结构。它用于存储一组字符串,使得查找、插入和删除字符串的操作非常高效。类似这种, 模板: 这是用数组来模拟上图中的树的结构,逻辑上和上图结构一致。 …

03-51单片机定时器和串口通信

一、51单片机定时器 1.定时器介绍 1.1为什么要使用定时器 在前面的学习中,用到了 Delay 函数延时,这里学习定时器以后,就可以通过定时器来完成,当然定时器的功能远不止这些: 51 单片机的定时器既可以定时&#xff…