Source code for compresso_recsys.datasets.movielens20m

from __future__ import annotations

import zipfile
from urllib.request import urlretrieve
from typing import Iterable

import pandas as pd

from .base import RecSysDataset


[docs] class MovieLens20M(RecSysDataset): name = "movielens20m" default_text_fields = ("title", "genres", "description") url = "https://files.grouplens.org/datasets/movielens/ml-20m.zip" text_descriptions_url = ( "https://raw.githubusercontent.com/recombee/beeformer/main/" "_datasets/ml20m/item_text_descriptions.feather" ) def __init__( self, data_dir: str = "data", *, metadata_text_fields: Iterable[str] | None = None, min_entity_text_words: int = 0, ) -> None: self.metadata_text_fields = tuple(metadata_text_fields or self.default_text_fields) self.min_entity_text_words = int(min_entity_text_words) super().__init__(data_dir=data_dir) def download(self) -> None: zip_path = self.root / "ml-20m.zip" if not zip_path.exists(): urlretrieve(self.url, zip_path) extract_dir = self.root / "ml-20m" if not extract_dir.exists(): with zipfile.ZipFile(zip_path, "r") as zf: zf.extractall(self.root) descriptions_path = self.root / "item_text_descriptions.feather" if not descriptions_path.exists(): urlretrieve(self.text_descriptions_url, descriptions_path) def prepare(self) -> None: self.download() ratings_path = self.root / "ml-20m" / "ratings.csv" movies_path = self.root / "ml-20m" / "movies.csv" if not ratings_path.exists(): raise FileNotFoundError(f"Missing ratings file: {ratings_path}") ratings = pd.read_csv(ratings_path) ratings = ratings.rename( columns={ "userId": "user_id", "movieId": "item_id", "rating": "value", "timestamp": "timestamp", } ) ratings["user_id"] = ratings["user_id"].astype(str) ratings["item_id"] = ratings["item_id"].astype(str) self._interactions = ratings[["user_id", "item_id", "value", "timestamp"]].copy() if movies_path.exists(): movies = pd.read_csv(movies_path) movies = movies.rename(columns={"movieId": "item_id", "title": "title", "genres": "genres"}) movies["item_id"] = movies["item_id"].astype(str) descriptions_path = self.root / "item_text_descriptions.feather" if descriptions_path.exists(): descriptions = pd.read_feather(descriptions_path) descriptions = descriptions.rename( columns={"movieId": "item_id", "llama31_description": "description"} ) descriptions["item_id"] = descriptions["item_id"].astype(str) movies = movies.merge(descriptions[["item_id", "description"]], on="item_id", how="left") keep = [c for c in ["item_id", "title", "genres", "description"] if c in movies.columns] self._item_metadata = self.add_entity_text( movies[keep], fields=self.metadata_text_fields, min_words=self.min_entity_text_words, ) else: self._item_metadata = self.add_entity_text( pd.DataFrame(columns=["item_id", "title", "genres", "description"]), fields=self.metadata_text_fields, min_words=self.min_entity_text_words, ) self._interactions = self.restrict_interactions_to_metadata_items(self._interactions, self._item_metadata)