We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
build_graph in MovieLens class 
## ❓ Questions and Help

Dear Quan (Andy) Gan,

i have a question about the method build_graph in the MovieLens class.
I see that the dataset is processed and you consider all the User and Movie features in generating the node features of the graph. In particular, you assign id of the node with this scheme: 

from [  0 : Length(Users)  ]  ->  Features of User (encoded) 
from [ Length(Users) : Length(Users)  + Length(Movies) ] -> Features of Movies (encoded) 

The problem comes out when you consider the Title of the movies because in the code there is the following (line 158): 

        for i, tw in enumerate(tqdm.tqdm(title_words)):
            g.ndata['title'][i, [vocab_invmap[w] for w in tw]] = 1

which would mean that the encoded title would be assigned to the nodes related to the Users
[0 : Len(Movies)]
Maybe i didn't understand the logic behind this, is this correct ? or am i missing something? 

Thank you,
Best regards
Francesco







--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `examples/pytorch/recommendation/rec/datasets/movielens.py`
Content:
```
1 import pandas as pd
2 import dgl
3 import os
4 import torch
5 import numpy as np
6 import scipy.sparse as sp
7 import time
8 from functools import partial
9 from .. import randomwalk
10 import stanfordnlp
11 import re
12 import tqdm
13 import string
14 
15 class MovieLens(object):
16     def __init__(self, directory):
17         '''
18         directory: path to movielens directory which should have the three
19                    files:
20                    users.dat
21                    movies.dat
22                    ratings.dat
23         '''
24         self.directory = directory
25 
26         users = []
27         movies = []
28         ratings = []
29 
30         # read users
31         with open(os.path.join(directory, 'users.dat')) as f:
32             for l in f:
33                 id_, gender, age, occupation, zip_ = l.strip().split('::')
34                 users.append({
35                     'id': int(id_),
36                     'gender': gender,
37                     'age': age,
38                     'occupation': occupation,
39                     'zip': zip_,
40                     })
41         self.users = pd.DataFrame(users).set_index('id').astype('category')
42 
43         # read movies
44         with open(os.path.join(directory, 'movies.dat'), encoding='latin1') as f:
45             for l in f:
46                 id_, title, genres = l.strip().split('::')
47                 genres_set = set(genres.split('|'))
48 
49                 # extract year
50                 assert re.match(r'.*\([0-9]{4}\)$', title)
51                 year = title[-5:-1]
52                 title = title[:-6].strip()
53 
54                 data = {'id': int(id_), 'title': title, 'year': year}
55                 for g in genres_set:
56                     data[g] = True
57                 movies.append(data)
58         self.movies = (
59                 pd.DataFrame(movies)
60                 .set_index('id')
61                 .fillna(False)
62                 .astype({'year': 'category'}))
63         self.genres = self.movies.columns[self.movies.dtypes == bool]
64 
65         # read ratings
66         with open(os.path.join(directory, 'ratings.dat')) as f:
67             for l in f:
68                 user_id, movie_id, rating, timestamp = [int(_) for _ in l.split('::')]
69                 ratings.append({
70                     'user_id': user_id,
71                     'movie_id': movie_id,
72                     'rating': rating,
73                     'timestamp': timestamp,
74                     })
75         ratings = pd.DataFrame(ratings)
76         movie_count = ratings['movie_id'].value_counts()
77         movie_count.name = 'movie_count'
78         ratings = ratings.join(movie_count, on='movie_id')
79         self.ratings = ratings
80 
81         # drop users and movies which do not exist in ratings
82         self.users = self.users[self.users.index.isin(self.ratings['user_id'])]
83         self.movies = self.movies[self.movies.index.isin(self.ratings['movie_id'])]
84 
85         self.data_split()
86         self.build_graph()
87 
88     def split_user(self, df, filter_counts=False):
89         df_new = df.copy()
90         df_new['prob'] = 0
91 
92         if filter_counts:
93             df_new_sub = (df_new['movie_count'] >= 10).nonzero()[0]
94         else:
95             df_new_sub = df_new['train'].nonzero()[0]
96         prob = np.linspace(0, 1, df_new_sub.shape[0], endpoint=False)
97         np.random.shuffle(prob)
98         df_new['prob'].iloc[df_new_sub] = prob
99         return df_new
100 
101     def data_split(self):
102         self.ratings = self.ratings.groupby('user_id', group_keys=False).apply(
103                 partial(self.split_user, filter_counts=True))
104         self.ratings['train'] = self.ratings['prob'] <= 0.8
105         self.ratings['valid'] = (self.ratings['prob'] > 0.8) & (self.ratings['prob'] <= 0.9)
106         self.ratings['test'] = self.ratings['prob'] > 0.9
107         self.ratings.drop(['prob'], axis=1, inplace=True)
108 
109     def build_graph(self):
110         user_ids = list(self.users.index)
111         movie_ids = list(self.movies.index)
112         user_ids_invmap = {id_: i for i, id_ in enumerate(user_ids)}
113         movie_ids_invmap = {id_: i for i, id_ in enumerate(movie_ids)}
114         self.user_ids = user_ids
115         self.movie_ids = movie_ids
116         self.user_ids_invmap = user_ids_invmap
117         self.movie_ids_invmap = movie_ids_invmap
118 
119         g = dgl.DGLGraph()
120         g.add_nodes(len(user_ids) + len(movie_ids))
121 
122         # user features
123         for user_column in self.users.columns:
124             udata = torch.zeros(g.number_of_nodes(), dtype=torch.int64)
125             # 0 for padding
126             udata[:len(user_ids)] = \
127                     torch.LongTensor(self.users[user_column].cat.codes.values.astype('int64') + 1)
128             g.ndata[user_column] = udata
129 
130         # movie genre
131         movie_genres = torch.from_numpy(self.movies[self.genres].values.astype('float32'))
132         g.ndata['genre'] = torch.zeros(g.number_of_nodes(), len(self.genres))
133         g.ndata['genre'][len(user_ids):len(user_ids) + len(movie_ids)] = movie_genres
134 
135         # movie year
136         g.ndata['year'] = torch.zeros(g.number_of_nodes(), dtype=torch.int64)
137         # 0 for padding
138         g.ndata['year'][len(user_ids):len(user_ids) + len(movie_ids)] = \
139                 torch.LongTensor(self.movies['year'].cat.codes.values.astype('int64') + 1)
140 
141         # movie title
142         nlp = stanfordnlp.Pipeline(use_gpu=False, processors='tokenize,lemma')
143         vocab = set()
144         title_words = []
145         for t in tqdm.tqdm(self.movies['title'].values):
146             doc = nlp(t)
147             words = set()
148             for s in doc.sentences:
149                 words.update(w.lemma.lower() for w in s.words
150                              if not re.fullmatch(r'['+string.punctuation+']+', w.lemma))
151             vocab.update(words)
152             title_words.append(words)
153         vocab = list(vocab)
154         vocab_invmap = {w: i for i, w in enumerate(vocab)}
155         # bag-of-words
156         g.ndata['title'] = torch.zeros(g.number_of_nodes(), len(vocab))
157         for i, tw in enumerate(tqdm.tqdm(title_words)):
158             g.ndata['title'][i, [vocab_invmap[w] for w in tw]] = 1
159         self.vocab = vocab
160         self.vocab_invmap = vocab_invmap
161 
162         rating_user_vertices = [user_ids_invmap[id_] for id_ in self.ratings['user_id'].values]
163         rating_movie_vertices = [movie_ids_invmap[id_] + len(user_ids)
164                                  for id_ in self.ratings['movie_id'].values]
165         self.rating_user_vertices = rating_user_vertices
166         self.rating_movie_vertices = rating_movie_vertices
167 
168         g.add_edges(
169                 rating_user_vertices,
170                 rating_movie_vertices,
171                 data={'inv': torch.zeros(self.ratings.shape[0], dtype=torch.uint8)})
172         g.add_edges(
173                 rating_movie_vertices,
174                 rating_user_vertices,
175                 data={'inv': torch.ones(self.ratings.shape[0], dtype=torch.uint8)})
176         self.g = g
177 
178     def generate_mask(self):
179         while True:
180             ratings = self.ratings.groupby('user_id', group_keys=False).apply(self.split_user)
181             prior_prob = ratings['prob'].values
182             for i in range(5):
183                 train_mask = (prior_prob >= 0.2 * i) & (prior_prob < 0.2 * (i + 1))
184                 prior_mask = ~train_mask
185                 train_mask &= ratings['train'].values
186                 prior_mask &= ratings['train'].values
187                 yield prior_mask, train_mask
188 
189     def refresh_mask(self):
190         if not hasattr(self, 'masks'):
191             self.masks = self.generate_mask()
192         prior_mask, train_mask = next(self.masks)
193 
194         valid_tensor = torch.from_numpy(self.ratings['valid'].values.astype('uint8'))
195         test_tensor = torch.from_numpy(self.ratings['test'].values.astype('uint8'))
196         train_tensor = torch.from_numpy(train_mask.astype('uint8'))
197         prior_tensor = torch.from_numpy(prior_mask.astype('uint8'))
198         edge_data = {
199                 'prior': prior_tensor,
200                 'valid': valid_tensor,
201                 'test': test_tensor,
202                 'train': train_tensor,
203                 }
204 
205         self.g.edges[self.rating_user_vertices, self.rating_movie_vertices].data.update(edge_data)
206         self.g.edges[self.rating_movie_vertices, self.rating_user_vertices].data.update(edge_data)
207
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/examples/pytorch/recommendation/rec/datasets/movielens.py b/examples/pytorch/recommendation/rec/datasets/movielens.py
--- a/examples/pytorch/recommendation/rec/datasets/movielens.py
+++ b/examples/pytorch/recommendation/rec/datasets/movielens.py
@@ -155,7 +155,7 @@
         # bag-of-words
         g.ndata['title'] = torch.zeros(g.number_of_nodes(), len(vocab))
         for i, tw in enumerate(tqdm.tqdm(title_words)):
-            g.ndata['title'][i, [vocab_invmap[w] for w in tw]] = 1
+            g.ndata['title'][len(user_ids) + i, [vocab_invmap[w] for w in tw]] = 1
         self.vocab = vocab
         self.vocab_invmap = vocab_invmap

{"golden_diff": "diff --git a/examples/pytorch/recommendation/rec/datasets/movielens.py b/examples/pytorch/recommendation/rec/datasets/movielens.py\n--- a/examples/pytorch/recommendation/rec/datasets/movielens.py\n+++ b/examples/pytorch/recommendation/rec/datasets/movielens.py\n@@ -155,7 +155,7 @@\n         # bag-of-words\n         g.ndata['title'] = torch.zeros(g.number_of_nodes(), len(vocab))\n         for i, tw in enumerate(tqdm.tqdm(title_words)):\n-            g.ndata['title'][i, [vocab_invmap[w] for w in tw]] = 1\n+            g.ndata['title'][len(user_ids) + i, [vocab_invmap[w] for w in tw]] = 1\n         self.vocab = vocab\n         self.vocab_invmap = vocab_invmap\n", "issue": "build_graph in MovieLens class \n## \u2753 Questions and Help\r\n\r\nDear Quan (Andy) Gan,\r\n\r\ni have a question about the method build_graph in the MovieLens class.\r\nI see that the dataset is processed and you consider all the User and Movie features in generating the node features of the graph. In particular, you assign id of the node with this scheme: \r\n\r\nfrom [  0 : Length(Users)  ]  ->  Features of User (encoded) \r\nfrom [ Length(Users) : Length(Users)  + Length(Movies) ] -> Features of Movies (encoded) \r\n\r\nThe problem comes out when you consider the Title of the movies because in the code there is the following (line 158): \r\n\r\n        for i, tw in enumerate(tqdm.tqdm(title_words)):\r\n            g.ndata['title'][i, [vocab_invmap[w] for w in tw]] = 1\r\n\r\nwhich would mean that the encoded title would be assigned to the nodes related to the Users\r\n[0 : Len(Movies)]\r\nMaybe i didn't understand the logic behind this, is this correct ? or am i missing something? \r\n\r\nThank you,\r\nBest regards\r\nFrancesco\r\n\r\n\r\n\r\n\r\n\r\n\n", "before_files": [{"content": "import pandas as pd\nimport dgl\nimport os\nimport torch\nimport numpy as np\nimport scipy.sparse as sp\nimport time\nfrom functools import partial\nfrom .. import randomwalk\nimport stanfordnlp\nimport re\nimport tqdm\nimport string\n\nclass MovieLens(object):\n    def __init__(self, directory):\n        '''\n        directory: path to movielens directory which should have the three\n                   files:\n                   users.dat\n                   movies.dat\n                   ratings.dat\n        '''\n        self.directory = directory\n\n        users = []\n        movies = []\n        ratings = []\n\n        # read users\n        with open(os.path.join(directory, 'users.dat')) as f:\n            for l in f:\n                id_, gender, age, occupation, zip_ = l.strip().split('::')\n                users.append({\n                    'id': int(id_),\n                    'gender': gender,\n                    'age': age,\n                    'occupation': occupation,\n                    'zip': zip_,\n                    })\n        self.users = pd.DataFrame(users).set_index('id').astype('category')\n\n        # read movies\n        with open(os.path.join(directory, 'movies.dat'), encoding='latin1') as f:\n            for l in f:\n                id_, title, genres = l.strip().split('::')\n                genres_set = set(genres.split('|'))\n\n                # extract year\n                assert re.match(r'.*\\([0-9]{4}\\)$', title)\n                year = title[-5:-1]\n                title = title[:-6].strip()\n\n                data = {'id': int(id_), 'title': title, 'year': year}\n                for g in genres_set:\n                    data[g] = True\n                movies.append(data)\n        self.movies = (\n                pd.DataFrame(movies)\n                .set_index('id')\n                .fillna(False)\n                .astype({'year': 'category'}))\n        self.genres = self.movies.columns[self.movies.dtypes == bool]\n\n        # read ratings\n        with open(os.path.join(directory, 'ratings.dat')) as f:\n            for l in f:\n                user_id, movie_id, rating, timestamp = [int(_) for _ in l.split('::')]\n                ratings.append({\n                    'user_id': user_id,\n                    'movie_id': movie_id,\n                    'rating': rating,\n                    'timestamp': timestamp,\n                    })\n        ratings = pd.DataFrame(ratings)\n        movie_count = ratings['movie_id'].value_counts()\n        movie_count.name = 'movie_count'\n        ratings = ratings.join(movie_count, on='movie_id')\n        self.ratings = ratings\n\n        # drop users and movies which do not exist in ratings\n        self.users = self.users[self.users.index.isin(self.ratings['user_id'])]\n        self.movies = self.movies[self.movies.index.isin(self.ratings['movie_id'])]\n\n        self.data_split()\n        self.build_graph()\n\n    def split_user(self, df, filter_counts=False):\n        df_new = df.copy()\n        df_new['prob'] = 0\n\n        if filter_counts:\n            df_new_sub = (df_new['movie_count'] >= 10).nonzero()[0]\n        else:\n            df_new_sub = df_new['train'].nonzero()[0]\n        prob = np.linspace(0, 1, df_new_sub.shape[0], endpoint=False)\n        np.random.shuffle(prob)\n        df_new['prob'].iloc[df_new_sub] = prob\n        return df_new\n\n    def data_split(self):\n        self.ratings = self.ratings.groupby('user_id', group_keys=False).apply(\n                partial(self.split_user, filter_counts=True))\n        self.ratings['train'] = self.ratings['prob'] <= 0.8\n        self.ratings['valid'] = (self.ratings['prob'] > 0.8) & (self.ratings['prob'] <= 0.9)\n        self.ratings['test'] = self.ratings['prob'] > 0.9\n        self.ratings.drop(['prob'], axis=1, inplace=True)\n\n    def build_graph(self):\n        user_ids = list(self.users.index)\n        movie_ids = list(self.movies.index)\n        user_ids_invmap = {id_: i for i, id_ in enumerate(user_ids)}\n        movie_ids_invmap = {id_: i for i, id_ in enumerate(movie_ids)}\n        self.user_ids = user_ids\n        self.movie_ids = movie_ids\n        self.user_ids_invmap = user_ids_invmap\n        self.movie_ids_invmap = movie_ids_invmap\n\n        g = dgl.DGLGraph()\n        g.add_nodes(len(user_ids) + len(movie_ids))\n\n        # user features\n        for user_column in self.users.columns:\n            udata = torch.zeros(g.number_of_nodes(), dtype=torch.int64)\n            # 0 for padding\n            udata[:len(user_ids)] = \\\n                    torch.LongTensor(self.users[user_column].cat.codes.values.astype('int64') + 1)\n            g.ndata[user_column] = udata\n\n        # movie genre\n        movie_genres = torch.from_numpy(self.movies[self.genres].values.astype('float32'))\n        g.ndata['genre'] = torch.zeros(g.number_of_nodes(), len(self.genres))\n        g.ndata['genre'][len(user_ids):len(user_ids) + len(movie_ids)] = movie_genres\n\n        # movie year\n        g.ndata['year'] = torch.zeros(g.number_of_nodes(), dtype=torch.int64)\n        # 0 for padding\n        g.ndata['year'][len(user_ids):len(user_ids) + len(movie_ids)] = \\\n                torch.LongTensor(self.movies['year'].cat.codes.values.astype('int64') + 1)\n\n        # movie title\n        nlp = stanfordnlp.Pipeline(use_gpu=False, processors='tokenize,lemma')\n        vocab = set()\n        title_words = []\n        for t in tqdm.tqdm(self.movies['title'].values):\n            doc = nlp(t)\n            words = set()\n            for s in doc.sentences:\n                words.update(w.lemma.lower() for w in s.words\n                             if not re.fullmatch(r'['+string.punctuation+']+', w.lemma))\n            vocab.update(words)\n            title_words.append(words)\n        vocab = list(vocab)\n        vocab_invmap = {w: i for i, w in enumerate(vocab)}\n        # bag-of-words\n        g.ndata['title'] = torch.zeros(g.number_of_nodes(), len(vocab))\n        for i, tw in enumerate(tqdm.tqdm(title_words)):\n            g.ndata['title'][i, [vocab_invmap[w] for w in tw]] = 1\n        self.vocab = vocab\n        self.vocab_invmap = vocab_invmap\n\n        rating_user_vertices = [user_ids_invmap[id_] for id_ in self.ratings['user_id'].values]\n        rating_movie_vertices = [movie_ids_invmap[id_] + len(user_ids)\n                                 for id_ in self.ratings['movie_id'].values]\n        self.rating_user_vertices = rating_user_vertices\n        self.rating_movie_vertices = rating_movie_vertices\n\n        g.add_edges(\n                rating_user_vertices,\n                rating_movie_vertices,\n                data={'inv': torch.zeros(self.ratings.shape[0], dtype=torch.uint8)})\n        g.add_edges(\n                rating_movie_vertices,\n                rating_user_vertices,\n                data={'inv': torch.ones(self.ratings.shape[0], dtype=torch.uint8)})\n        self.g = g\n\n    def generate_mask(self):\n        while True:\n            ratings = self.ratings.groupby('user_id', group_keys=False).apply(self.split_user)\n            prior_prob = ratings['prob'].values\n            for i in range(5):\n                train_mask = (prior_prob >= 0.2 * i) & (prior_prob < 0.2 * (i + 1))\n                prior_mask = ~train_mask\n                train_mask &= ratings['train'].values\n                prior_mask &= ratings['train'].values\n                yield prior_mask, train_mask\n\n    def refresh_mask(self):\n        if not hasattr(self, 'masks'):\n            self.masks = self.generate_mask()\n        prior_mask, train_mask = next(self.masks)\n\n        valid_tensor = torch.from_numpy(self.ratings['valid'].values.astype('uint8'))\n        test_tensor = torch.from_numpy(self.ratings['test'].values.astype('uint8'))\n        train_tensor = torch.from_numpy(train_mask.astype('uint8'))\n        prior_tensor = torch.from_numpy(prior_mask.astype('uint8'))\n        edge_data = {\n                'prior': prior_tensor,\n                'valid': valid_tensor,\n                'test': test_tensor,\n                'train': train_tensor,\n                }\n\n        self.g.edges[self.rating_user_vertices, self.rating_movie_vertices].data.update(edge_data)\n        self.g.edges[self.rating_movie_vertices, self.rating_user_vertices].data.update(edge_data)\n", "path": "examples/pytorch/recommendation/rec/datasets/movielens.py"}], "after_files": [{"content": "import pandas as pd\nimport dgl\nimport os\nimport torch\nimport numpy as np\nimport scipy.sparse as sp\nimport time\nfrom functools import partial\nfrom .. import randomwalk\nimport stanfordnlp\nimport re\nimport tqdm\nimport string\n\nclass MovieLens(object):\n    def __init__(self, directory):\n        '''\n        directory: path to movielens directory which should have the three\n                   files:\n                   users.dat\n                   movies.dat\n                   ratings.dat\n        '''\n        self.directory = directory\n\n        users = []\n        movies = []\n        ratings = []\n\n        # read users\n        with open(os.path.join(directory, 'users.dat')) as f:\n            for l in f:\n                id_, gender, age, occupation, zip_ = l.strip().split('::')\n                users.append({\n                    'id': int(id_),\n                    'gender': gender,\n                    'age': age,\n                    'occupation': occupation,\n                    'zip': zip_,\n                    })\n        self.users = pd.DataFrame(users).set_index('id').astype('category')\n\n        # read movies\n        with open(os.path.join(directory, 'movies.dat'), encoding='latin1') as f:\n            for l in f:\n                id_, title, genres = l.strip().split('::')\n                genres_set = set(genres.split('|'))\n\n                # extract year\n                assert re.match(r'.*\\([0-9]{4}\\)$', title)\n                year = title[-5:-1]\n                title = title[:-6].strip()\n\n                data = {'id': int(id_), 'title': title, 'year': year}\n                for g in genres_set:\n                    data[g] = True\n                movies.append(data)\n        self.movies = (\n                pd.DataFrame(movies)\n                .set_index('id')\n                .fillna(False)\n                .astype({'year': 'category'}))\n        self.genres = self.movies.columns[self.movies.dtypes == bool]\n\n        # read ratings\n        with open(os.path.join(directory, 'ratings.dat')) as f:\n            for l in f:\n                user_id, movie_id, rating, timestamp = [int(_) for _ in l.split('::')]\n                ratings.append({\n                    'user_id': user_id,\n                    'movie_id': movie_id,\n                    'rating': rating,\n                    'timestamp': timestamp,\n                    })\n        ratings = pd.DataFrame(ratings)\n        movie_count = ratings['movie_id'].value_counts()\n        movie_count.name = 'movie_count'\n        ratings = ratings.join(movie_count, on='movie_id')\n        self.ratings = ratings\n\n        # drop users and movies which do not exist in ratings\n        self.users = self.users[self.users.index.isin(self.ratings['user_id'])]\n        self.movies = self.movies[self.movies.index.isin(self.ratings['movie_id'])]\n\n        self.data_split()\n        self.build_graph()\n\n    def split_user(self, df, filter_counts=False):\n        df_new = df.copy()\n        df_new['prob'] = 0\n\n        if filter_counts:\n            df_new_sub = (df_new['movie_count'] >= 10).nonzero()[0]\n        else:\n            df_new_sub = df_new['train'].nonzero()[0]\n        prob = np.linspace(0, 1, df_new_sub.shape[0], endpoint=False)\n        np.random.shuffle(prob)\n        df_new['prob'].iloc[df_new_sub] = prob\n        return df_new\n\n    def data_split(self):\n        self.ratings = self.ratings.groupby('user_id', group_keys=False).apply(\n                partial(self.split_user, filter_counts=True))\n        self.ratings['train'] = self.ratings['prob'] <= 0.8\n        self.ratings['valid'] = (self.ratings['prob'] > 0.8) & (self.ratings['prob'] <= 0.9)\n        self.ratings['test'] = self.ratings['prob'] > 0.9\n        self.ratings.drop(['prob'], axis=1, inplace=True)\n\n    def build_graph(self):\n        user_ids = list(self.users.index)\n        movie_ids = list(self.movies.index)\n        user_ids_invmap = {id_: i for i, id_ in enumerate(user_ids)}\n        movie_ids_invmap = {id_: i for i, id_ in enumerate(movie_ids)}\n        self.user_ids = user_ids\n        self.movie_ids = movie_ids\n        self.user_ids_invmap = user_ids_invmap\n        self.movie_ids_invmap = movie_ids_invmap\n\n        g = dgl.DGLGraph()\n        g.add_nodes(len(user_ids) + len(movie_ids))\n\n        # user features\n        for user_column in self.users.columns:\n            udata = torch.zeros(g.number_of_nodes(), dtype=torch.int64)\n            # 0 for padding\n            udata[:len(user_ids)] = \\\n                    torch.LongTensor(self.users[user_column].cat.codes.values.astype('int64') + 1)\n            g.ndata[user_column] = udata\n\n        # movie genre\n        movie_genres = torch.from_numpy(self.movies[self.genres].values.astype('float32'))\n        g.ndata['genre'] = torch.zeros(g.number_of_nodes(), len(self.genres))\n        g.ndata['genre'][len(user_ids):len(user_ids) + len(movie_ids)] = movie_genres\n\n        # movie year\n        g.ndata['year'] = torch.zeros(g.number_of_nodes(), dtype=torch.int64)\n        # 0 for padding\n        g.ndata['year'][len(user_ids):len(user_ids) + len(movie_ids)] = \\\n                torch.LongTensor(self.movies['year'].cat.codes.values.astype('int64') + 1)\n\n        # movie title\n        nlp = stanfordnlp.Pipeline(use_gpu=False, processors='tokenize,lemma')\n        vocab = set()\n        title_words = []\n        for t in tqdm.tqdm(self.movies['title'].values):\n            doc = nlp(t)\n            words = set()\n            for s in doc.sentences:\n                words.update(w.lemma.lower() for w in s.words\n                             if not re.fullmatch(r'['+string.punctuation+']+', w.lemma))\n            vocab.update(words)\n            title_words.append(words)\n        vocab = list(vocab)\n        vocab_invmap = {w: i for i, w in enumerate(vocab)}\n        # bag-of-words\n        g.ndata['title'] = torch.zeros(g.number_of_nodes(), len(vocab))\n        for i, tw in enumerate(tqdm.tqdm(title_words)):\n            g.ndata['title'][len(user_ids) + i, [vocab_invmap[w] for w in tw]] = 1\n        self.vocab = vocab\n        self.vocab_invmap = vocab_invmap\n\n        rating_user_vertices = [user_ids_invmap[id_] for id_ in self.ratings['user_id'].values]\n        rating_movie_vertices = [movie_ids_invmap[id_] + len(user_ids)\n                                 for id_ in self.ratings['movie_id'].values]\n        self.rating_user_vertices = rating_user_vertices\n        self.rating_movie_vertices = rating_movie_vertices\n\n        g.add_edges(\n                rating_user_vertices,\n                rating_movie_vertices,\n                data={'inv': torch.zeros(self.ratings.shape[0], dtype=torch.uint8)})\n        g.add_edges(\n                rating_movie_vertices,\n                rating_user_vertices,\n                data={'inv': torch.ones(self.ratings.shape[0], dtype=torch.uint8)})\n        self.g = g\n\n    def generate_mask(self):\n        while True:\n            ratings = self.ratings.groupby('user_id', group_keys=False).apply(self.split_user)\n            prior_prob = ratings['prob'].values\n            for i in range(5):\n                train_mask = (prior_prob >= 0.2 * i) & (prior_prob < 0.2 * (i + 1))\n                prior_mask = ~train_mask\n                train_mask &= ratings['train'].values\n                prior_mask &= ratings['train'].values\n                yield prior_mask, train_mask\n\n    def refresh_mask(self):\n        if not hasattr(self, 'masks'):\n            self.masks = self.generate_mask()\n        prior_mask, train_mask = next(self.masks)\n\n        valid_tensor = torch.from_numpy(self.ratings['valid'].values.astype('uint8'))\n        test_tensor = torch.from_numpy(self.ratings['test'].values.astype('uint8'))\n        train_tensor = torch.from_numpy(train_mask.astype('uint8'))\n        prior_tensor = torch.from_numpy(prior_mask.astype('uint8'))\n        edge_data = {\n                'prior': prior_tensor,\n                'valid': valid_tensor,\n                'test': test_tensor,\n                'train': train_tensor,\n                }\n\n        self.g.edges[self.rating_user_vertices, self.rating_movie_vertices].data.update(edge_data)\n        self.g.edges[self.rating_movie_vertices, self.rating_user_vertices].data.update(edge_data)\n", "path": "examples/pytorch/recommendation/rec/datasets/movielens.py"}]}