解决Python的Surprise库在划分训练集和测试集时遇到的ValueError: User 7746 is not part of the trainset.问题

使用Surprise库对协同过滤推荐算法进行测试

问题描述

在使用Python的Surprise库(scikit-surprise)搭建一个简单的TopN推荐系统对协同过滤推荐算法进行测试,计算分类预测准确度如召回率(Recall)、精确率(Precision)和覆盖率(Coverage)时,使用train_test_split()方法对数据集进行的划分,遇到该库的Trainset模块报错为:ValueError: User 7746 is not part of the trainset.这个问题是我使用Jester笑话数据集进行测试时遇到的,同时我还使用了MovieLens1M和MovieLens100K数据集,但都无此问题。

解决过程

在研究完这个库数据划分源码之后,分析发现问题确实出在数据集中。

    def split(self, data):
        """Generator function to iterate over trainsets and testsets.

        Args:
            data(:obj:`Dataset<surprise.dataset.Dataset>`): The data containing
                ratings that will be divided into trainsets and testsets.

        Yields:
            tuple of (trainset, testset)
        """

        test_size, train_size = self.validate_train_test_sizes(
            self.test_size, self.train_size, len(data.raw_ratings)
        )
        rng = get_rng(self.random_state)

        for _ in range(self.n_splits):

            if self.shuffle:
                permutation = rng.permutation(len(data.raw_ratings))
            else:
                permutation = np.arange(len(data.raw_ratings))

            raw_trainset = [data.raw_ratings[i] for i in permutation[:test_size]]
            raw_testset = [
                data.raw_ratings[i]
                for i in permutation[test_size : (test_size + train_size)]
            ]

            trainset = data.construct_trainset(raw_trainset)
            testset = data.construct_testset(raw_testset)

            yield trainset, testset

要使程序计算覆盖率时能够运行下去,划分之后的训练集中必须包含全部用户,观察MoviLens数据集可以发现每个用户几乎都有30条以上的评分记录所以在numpy.random.permutation(len(data.raw_ratings))生成的随机序列中把所有用户都抽中几条评分记录划分进训练集的概率非常大,然后我根据报错ValueError: User 7746 is not part of the trainset.jester_ratings.dat数据集中查看用户7746的评分记录,发现才3条,那么很有可能这名用户连一条记录都没被抽中划分进训练集中,把这名用户及其评分记录删除,发现报错的就是其它用户了,这些用户同样是评分记录非常少的。

解决方案

使用pandas读取jester_ratings.dat,然后使用groupby().count()功能统计每一位用户的评分记录数量,将评分记录数量少于20条的用户剔除,把剔除后的数据重新保存为数据集以便后面使用。

from surprise import Dataset, Reader
from surprise.model_selection import train_test_split
import pandas as pd
import csv

if __name__ == '__main__':
	data = pd.read_csv('jester_ratings.dat', header = None, encoding ='utf - 8', delimiter = "\t\t", quoting = csv.QUOTE_NONE, engine = 'python')
	# print(data.head())
	df = data.iloc[:, 0].groupby(data.iloc[:, 0]).count()
	# print(df.index)
	for index in df.index:
		if df[index] < 20:
			# 从data中删除这个用户
			data = data[data.iloc[:, 0] != index]
	data.to_csv("jester_ratings.csv", index=False)
	jester_data = Dataset.load_from_df(data, Reader(line_format='user item rating', rating_scale=(-10, 10)))
	train, test = train_test_split(jester_data, test_size=0.2, random_state=42)
	print(train.n_items, train.n_users)

这样就能正常运行了。运行完后发现Jester数据集计算覆盖率似乎意义不大,计算出来覆盖率几乎都是100%。

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值