赞
踩
steam
上一个游戏的种种数据,是如何影响到其最终评分。steam_datasets
数据集进行线性回归分析,并在文章结尾尝试加入非线性优化模型。不是最优
,如有错误,请多多指正。以下是数据集steam_datasets.csv
的一小部分截图,它包含以下几个信息
Column1
:行数game
:游戏名release
:发行日期(1)peak_players
:巅峰玩家数 (2)positive_reviews
:好评(3)negative_reviews
:差评(4)total_reviews
: 总评价数primary_genre
: 游戏分类 (5)detected_technologies
游戏开发平台(6)review_percentage
:玩家对游戏的总体满意程度(7)players_right_now
:当前玩家数(8)24_hour_peak
:24小时玩家巅峰数(9)all_time_peak
:总玩家巅峰数(10)rating
:游戏打分(输出)要求根据上述提供的数据集完成下述要求:
rating
进行线性回归预测pytorch
实现上述代码steam_datasets.csv
并对其进行回归处理之前,我们首先需要对数据进行读取,在对数据进行读取之前,我们可以使用chardet
库(这是一个用于检测文本数据的字符编码的库),对数据集进行编码检测,从而在使用pandas
对数据集读取时指定正确的编码格式。import chardet
with open('./steam_datasets.csv', 'rb') as f:
result = chardet.detect(f.read()) # 编码检测
print(result['encoding']) # 输出编码格式
GB2312
,GBK
,GB18030
,着三个编码格式是兼容的,包含的字符个数:GB2312
<GBK
< GB18030
,这里chardet
库给出的结果是GB2312
,但是使用这个编码格式进行读取会遇到报错,故我们往上选择GBK
进行数据解码。Pandas
是一个开放源码、BSD 许可的库,提供高性能、易于使用的数据结构和数据分析工具。这里我们使用Pandas
对csv
数据集进行读取,并指定编码格式。import pandas as pd
raw_data=pd.read_csv('./steam_datasets.csv',encoding="GBK")
print(raw_data)
raw_data = raw_data.dropna()
raw_data = raw_data.drop(['game','Column1','total_reviews'], axis=1)
raw_data = raw_data.dropna()
peak_players
, positive_reviews
, negative_reviews
, review_percentage
, players_right_now
, 24_hour_peak
, all_time_peak
, rating
这8列都应该是数据类型,我们观察csv
文件可以发现在这些标签中,混杂着非数值类型的日期和字符串,为此我们需要进行删除。
rating
和all_time_peak
出现非法值的情况def to_numeric_if_possible(x):
try:
return pd.to_numeric(x)
except (ValueError, TypeError):
return pd.NA
check_labels=['peak_players', 'positive_reviews', 'negative_reviews', 'review_percentage', 'players_right_now', '24_hour_peak', 'all_time_peak', 'rating']
# 指定列名并应用自定义函数
raw_data[check_labels] = raw_data[check_labels].applymap(to_numeric_if_possible)
raw_data = raw_data.dropna()
release
,primary_genre
,detected_technologies
着三个标签属于非数值,对于非数值类型的标签,我们需要转换为数值类型的标签来进行线性回归。release
是2023/1/26
,属于日期类数据类型,我们可以将日期转换为距离某个特定日期的天数,这样可以作为一个连续变量用于回归。from datetime import datetime
# 尝试将 'release' 列转换为日期,无法解析的设置为 NaT
raw_data['release'] = pd.to_datetime(raw_data['release'], errors='coerce', format='%Y/%m/%d')
# 删除包含 NaT 的行
raw_data = raw_data.dropna(subset=['release'])
# 选择2005-01-01作为参考点
specific_date = datetime.strptime('2005-01-01', '%Y-%m-%d')
raw_data['release'] = pd.to_datetime(raw_data['release']).map(lambda date: (date - specific_date).days)
print(raw_data['release'])
# 计算依次最大最小值是为了检查是否出现非法值,此外根据最大最小值调整参考日期
print(max(raw_data['release']))
print(min(raw_data['release']))
哑编码(独热编码,One-Hot Encoding)
: 独热编码是一种将分类变量转换为一系列二进制列的过程,其中每列对应一个可能的类别值。这些列中的值通常为0或1,表示某个特定类别是否存在。
分解(Factorization)
:分解是将分类数据转换为整数的过程。每个类别被分配一个唯一的整数。这种方法不会产生独热编码那样的稀疏矩阵,但它可能会丢失一些类别间的信息,因为它只保留了一个整数而不是整个类别结构。
标签编码(Label Encoding)
:类别编码是将类别转换为数值的方法primary_genre
观察可以发现,每个游戏分类都已经分配好对应的序号,且继续观察多标签的行数基本上都属于错误数据,都已经被剔除,故这里只需要提取出单分类
的游戏分类对应的数据即可
re
库对括号的数字进行提取,并对其中是否为单数字进行检测import re # 使用正则表达式提取括号中的数字,并将它们转换为逗号分隔的字符串 raw_data['primary_genre'] = raw_data['primary_genre'].apply( lambda x: ','.join(re.findall(r'\((\d+)\)', x))) is_single_digit = raw_data['primary_genre'].apply(lambda x: all(len(str(item)) == 1 for item in x)) # 检查是否全部为单数字 all_single_digit = is_single_digit.all() print(all_single_digit) print(raw_data['primary_genre']) # 将字符串转换为数字,使用之前的函数 raw_data['primary_genre'] = raw_data['primary_genre'].apply(to_numeric_if_possible) raw_data = raw_data.dropna() print(raw_data['primary_genre']) # 输出最大最小值进行确认 print(max(raw_data['primary_genre'])) print(min(raw_data['primary_genre']))
detected_technologies
detected_technologies
由好几种开发工具组成,为此我们使用代码进行统计primary_genre_str = raw_data['detected_technologies'].str.cat(sep=';')
# split 分割文本
entries = [entry for line in primary_genre_str.split("\n") for entry in line.split(";")]
all_categories=set()
for entry in entries:
entry = entry.strip() # 移除前后的空白字符
if '.' in entry:
all_categories.add(entry.split('.')[0])
print(all_categories)
通过统计字符.
前面的字符串,我们得到了所有字符的分类
这里我们对所有标签进行统计,计算总类别数
all_categories=set()
# 遍历每个条目,检查是否以 all_categories_label 中的某个标签开头
for entry in entries:
entry = entry.strip()
for label in all_categories_label:
if entry.startswith(label):
all_categories.add(entry)
break
print("all_categories ", all_categories)
print("Number of all_categories:", len(all_categories))
标签编码
# 进行标签编码
all_categories_dict = {label: idx for idx, label in enumerate(sorted(all_categories))}
print(all_categories_dict)
如下顾名思义
紧接着我们对原数据中的标签进行替换
# 使用正则表达式检查是否为数字
data_as_lists = []
for entry in raw_data['detected_technologies']:
# 分割字符串
split_entries = entry.split('; ')
# 只保留数字部分
numeric_entries = [int(num) for num in split_entries if re.match(r'^\d+$', num)]
data_as_lists.append(numeric_entries)
raw_data['detected_technologies']=data_as_lists
print(raw_data['detected_technologies'])
原始数据detected_technologies
就完成了非字符型的处理的第一步
仔细观察,上述detected_technologies
的数据的每一行是一个长度不定的输入,对于线性模型,在不想增加其输入维度的情况下,这里我采用PCA降维
主成分分析(PCA,Principal Component Analysis)
是一种统计方法,它通过正交变换将一组可能相关的变量转换为一组线性不相关的变量,这组变量称为主成分。PCA的主要目的是降维,即在尽可能保留原始数据信息的前提下,减少数据的特征维度。
from sklearn.decomposition import PCA
import numpy as np
# 找到最长的列表长度
max_length = max(len(lst) for lst in raw_data['detected_technologies'])
# 使用列表推导式和列表的extend方法来填充列表,确保所有列表长度一致
padded_technologies = [x + [0]*(max_length - len(x)) for x in raw_data['detected_technologies']]
technologies_array = np.array(padded_technologies)
# 应用PCA降维到1维
pca = PCA(n_components=1)
technologies_pca = pca.fit_transform(technologies_array)
print(technologies_pca)
raw_data['detected_technologies']=technologies_pca
all_label=['rating','release','peak_players','positive_reviews','negative_reviews','primary_genre','detected_technologies','review_percentage','players_right_now','24_hour_peak','all_time_peak'] # 再次检查每一列的数据类型 for col in all_label: print(f"{col}: {raw_data[col].dtype}") # 确保所有列都是数值类型 for col in all_label: if col=='detected_technologies': continue if raw_data[col].dtype == object: # 尝试将非数值类型转换为数值类型 raw_data[col] = pd.to_numeric(raw_data[col], errors='coerce') # 删除任何仍然包含 NaN 值的行 raw_data = raw_data.dropna(subset=all_label) # 再次检查每一列的数据类型 for col in all_label: print(f"{col}: {raw_data[col].dtype}") print(raw_data)
数据标准化(Standardization)
:数据标准化旨在调整数据的尺度,使每个特征具有相同的数值范围,从而消除特征之间的量纲影响,确保数据在训练过程中被平等对待训练集(Training Set)
-用于训练模型,即通过迭代优化模型的参数(如权重和偏置)来最小化损失函数。(有时候还会进行训练集
和验证集
的划分)测试集(Test Set)
: 用于评估模型在未见过的数据上的表现,即模型的泛化能力。import torch from sklearn.model_selection import train_test_split import torch.nn as nn from sklearn.preprocessing import StandardScaler # 选择除了 'rating' 之外的所有列作为特征 X = raw_data.drop(columns=['rating']).values # 标准化 scaler = StandardScaler() X = scaler.fit_transform(X) y = raw_data['rating'].values # 划分训练集和测试集 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) # 将数据转换为PyTorch张量 X_train_tensor = torch.tensor(X_train, dtype=torch.float32) y_train_tensor = torch.tensor(y_train, dtype=torch.float32).view(-1, 1) X_test_tensor = torch.tensor(X_test, dtype=torch.float32) y_test_tensor = torch.tensor(y_test, dtype=torch.float32).view(-1, 1)
nn
模组,需要重写forward
用于前向传播class LinearRegressionModel(nn.Module):
def __init__(self, input_dim):
super(LinearRegressionModel, self).__init__()
self.linear = nn.Linear(input_dim, 1)
def forward(self, x):
return self.linear(x)
# 实例化模型
input_dim = X_train.shape[1]
model = LinearRegressionModel(input_dim)
均方误差损失(MSELoss)
:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。