MovieLens数据集预处理避坑指南:用Pandas处理‘::’分隔符、编码分类变量与异常值检测的完整流程
MovieLens数据集预处理实战从原始数据到推荐系统输入的完整避坑手册当你第一次从GroupLens官网下载MovieLens数据集时可能会被那些以.dat为扩展名的文件弄得一头雾水。这些文件使用::作为分隔符包含各种需要清洗和转换的数据字段。本文将带你一步步解决这些实际问题从数据加载、清洗到特征工程最终得到可以直接输入推荐算法的高质量数据。1. 数据加载与初始探索1.1 正确读取特殊格式的dat文件MovieLens数据集使用::作为字段分隔符这在使用pandas的read_csv函数时会遇到第一个坑import pandas as pd # 用户数据 users pd.read_csv(users.dat, sep::, enginepython, # 必须指定enginepython来处理非常规分隔符 headerNone, names[user_id, gender, age, occupation, zip_code]) # 电影数据 - 注意编码问题 movies pd.read_csv(movies.dat, sep::, enginepython, encodingISO-8859-1, # 处理特殊字符 headerNone, names[movie_id, title, genres]) # 评分数据 ratings pd.read_csv(ratings.dat, sep::, enginepython, headerNone, names[user_id, movie_id, rating, timestamp])提示当遇到ParserError: Error tokenizing data错误时首先检查分隔符是否正确其次考虑是否需要指定enginepython。1.2 数据概览与基本统计加载数据后我们需要快速了解数据的结构和质量print(用户数据形状:, users.shape) print(电影数据形状:, movies.shape) print(评分数据形状:, ratings.shape) # 各表基本信息 users.info() movies.info() ratings.info() # 描述性统计 print(users.describe()) print(movies.describe()) print(ratings.describe())典型输出可能显示用户表6,040个用户包含性别、年龄、职业和邮编电影表3,883部电影包含标题和类型评分表约1,000,000条评分记录2. 数据清洗与转换2.1 处理电影标题中的年份信息电影标题通常包含括号中的年份我们需要将其分离为单独的字段import re # 提取年份并创建新列 movies[year] movies[title].str.extract(r\((\d{4})\)$) movies[clean_title] movies[title].str.replace(r\(\d{4}\)$, , regexTrue).str.strip() # 检查结果 print(movies[[title, clean_title, year]].head())2.2 分类变量的编码处理性别和职业等分类变量需要转换为数值形式供模型使用from sklearn.preprocessing import LabelEncoder # 性别编码 gender_encoder LabelEncoder() users[gender_encoded] gender_encoder.fit_transform(users[gender]) # 职业编码假设已有职业映射字典 occupation_map { 0: other, 1: academic, 2: artist, # ...其他职业映射 } users[occupation_name] users[occupation].map(occupation_map) # 创建职业的one-hot编码 occupation_dummies pd.get_dummies(users[occupation_name], prefixoccupation) users pd.concat([users, occupation_dummies], axis1)2.3 处理多值类型的电影类型字段电影类型是以|分隔的多值字段需要转换为更适合分析的格式# 将类型拆分为单独列 genres_expanded movies[genres].str.get_dummies(sep|) # 合并回原数据框 movies pd.concat([movies, genres_expanded], axis1) # 统计各类型电影数量 genre_counts genres_expanded.sum().sort_values(ascendingFalse) print(genre_counts)3. 数据质量检查与异常值处理3.1 缺失值检测# 检查各表缺失值 print(用户表缺失值:\n, users.isnull().sum()) print(电影表缺失值:\n, movies.isnull().sum()) print(评分表缺失值:\n, ratings.isnull().sum()) # 处理电影表中可能缺失的年份 movies[year] movies[year].fillna(0).astype(int) # 0表示年份未知3.2 异常评分检测使用统计方法和可视化工具识别异常评分import seaborn as sns import matplotlib.pyplot as plt # 评分分布统计 print(ratings[rating].describe()) # 评分分布直方图 plt.figure(figsize(10, 6)) sns.histplot(ratings[rating], bins10, kdeTrue) plt.title(Rating Distribution) plt.show() # 箱线图检测异常值 plt.figure(figsize(8, 6)) sns.boxplot(xratings[rating]) plt.title(Rating Boxplot) plt.show()3.3 用户行为异常检测检查用户评分行为的异常模式# 计算每个用户的评分数量 user_rating_counts ratings[user_id].value_counts() # 找出评分数量异常多或异常少的用户 print(评分最多的用户:\n, user_rating_counts.head()) print(评分最少的用户:\n, user_rating_counts.tail()) # 可视化用户评分数量分布 plt.figure(figsize(10, 6)) sns.histplot(user_rating_counts, bins50) plt.title(Distribution of Number of Ratings per User) plt.xlabel(Number of Ratings) plt.ylabel(User Count) plt.show()4. 特征工程与数据增强4.1 时间特征提取从时间戳中提取有用的时间特征import datetime # 将时间戳转换为datetime ratings[datetime] pd.to_datetime(ratings[timestamp], units) # 提取各种时间特征 ratings[year] ratings[datetime].dt.year ratings[month] ratings[datetime].dt.month ratings[day] ratings[datetime].dt.day ratings[weekday] ratings[datetime].dt.weekday ratings[hour] ratings[datetime].dt.hour # 检查时间分布 print(ratings[[year, month, day, weekday, hour]].describe())4.2 用户画像构建结合用户信息和评分行为构建更丰富的用户特征# 计算用户平均评分 user_stats ratings.groupby(user_id)[rating].agg([mean, count, std]) user_stats.columns [avg_rating, rating_count, rating_std] # 合并用户统计信息 users users.merge(user_stats, left_onuser_id, right_indexTrue, howleft) # 计算用户评分偏离度 global_avg ratings[rating].mean() users[rating_deviation] users[avg_rating] - global_avg # 检查结果 print(users.head())4.3 电影特征增强基于评分数据增强电影特征# 计算电影评分统计 movie_stats ratings.groupby(movie_id)[rating].agg([mean, count, std]) movie_stats.columns [avg_rating, rating_count, rating_std] # 合并电影统计信息 movies movies.merge(movie_stats, left_onmovie_id, right_indexTrue, howleft) # 计算电影热度评分数量的标准化值 movies[popularity] (movies[rating_count] - movies[rating_count].min()) / \ (movies[rating_count].max() - movies[rating_count].min()) # 检查结果 print(movies.sort_values(rating_count, ascendingFalse).head())5. 数据准备与保存5.1 创建合并的数据集为推荐系统准备一个合并后的数据集# 合并用户和评分数据 user_ratings pd.merge(ratings, users, onuser_id) # 合并电影信息 full_data pd.merge(user_ratings, movies, onmovie_id) # 选择需要的列 final_data full_data[[user_id, movie_id, clean_title, year, gender, age, occupation_name, genres, rating, datetime]] print(final_data.head())5.2 保存处理后的数据将处理后的数据保存为更适合后续分析的格式# 保存为CSV final_data.to_csv(movielens_processed.csv, indexFalse) # 保存为Parquet格式更高效 final_data.to_parquet(movielens_processed.parquet, indexFalse) # 保存为HDF5适合大型数据集 final_data.to_hdf(movielens_processed.h5, keydata, modew)5.3 创建推荐系统输入格式根据不同的推荐算法准备特定格式的输入# 为协同过滤准备用户-物品-评分矩阵 rating_matrix ratings.pivot(indexuser_id, columnsmovie_id, valuesrating) # 为内容推荐准备电影特征矩阵 movie_features movies.set_index(movie_id)[[year] list(genres_expanded.columns)] # 为用户特征准备数据 user_features users.set_index(user_id)[[age, gender_encoded] [col for col in users.columns if occupation_ in col]]在实际项目中我发现正确处理电影标题中的年份和类型字段是最容易出错的部分。特别是当使用正则表达式提取年份时需要确保模式能够覆盖所有情况。另外将用户职业转换为one-hot编码时要注意内存使用情况对于大型数据集可能需要更高效的编码方式。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2586847.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!