FranKGraphBench 0.0.1a0__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. FranKGraphBench-0.0.1a0.dist-info/LICENSE +201 -0
  2. FranKGraphBench-0.0.1a0.dist-info/METADATA +189 -0
  3. FranKGraphBench-0.0.1a0.dist-info/RECORD +70 -0
  4. FranKGraphBench-0.0.1a0.dist-info/WHEEL +6 -0
  5. FranKGraphBench-0.0.1a0.dist-info/entry_points.txt +3 -0
  6. FranKGraphBench-0.0.1a0.dist-info/top_level.txt +2 -0
  7. data_integration/__init__.py +47 -0
  8. data_integration/__main__.py +4 -0
  9. data_integration/dataset.py +270 -0
  10. data_integration/dataset2class.py +34 -0
  11. data_integration/datasets/__init__.py +0 -0
  12. data_integration/datasets/amazon.py +140 -0
  13. data_integration/datasets/book-crossing.py +98 -0
  14. data_integration/datasets/douban-movie.py +75 -0
  15. data_integration/datasets/lastfm.py +215 -0
  16. data_integration/datasets/movielens.py +375 -0
  17. data_integration/datasets/steam.py +98 -0
  18. data_integration/datasets/worker.py +31 -0
  19. data_integration/datasets/yelp.py +156 -0
  20. framework/__init__.py +17 -0
  21. framework/__main__.py +4 -0
  22. framework/dataloader/__init__.py +0 -0
  23. framework/dataloader/dataloader.py +52 -0
  24. framework/dataloader/dataset/__init__.py +0 -0
  25. framework/dataloader/dataset/dataset.py +38 -0
  26. framework/dataloader/edge_splitter/__init__.py +0 -0
  27. framework/dataloader/edge_splitter/edge_splitter.py +163 -0
  28. framework/dataloader/graph/__init__.py +0 -0
  29. framework/dataloader/graph/graph.py +314 -0
  30. framework/dataloader/graph/node.py +80 -0
  31. framework/dataloader/preprocess/__init__.py +0 -0
  32. framework/dataloader/preprocess/methods.py +47 -0
  33. framework/evaluator/__init__.py +0 -0
  34. framework/evaluator/metric.py +39 -0
  35. framework/evaluator/metric2class.py +10 -0
  36. framework/evaluator/metrics/__init__.py +0 -0
  37. framework/evaluator/metrics/map.py +33 -0
  38. framework/evaluator/metrics/ndcg.py +49 -0
  39. framework/experiment.py +120 -0
  40. framework/recommender/__init__.py +0 -0
  41. framework/recommender/model2class.py +46 -0
  42. framework/recommender/models/__init__.py +0 -0
  43. framework/recommender/models/complEx/__init__.py +0 -0
  44. framework/recommender/models/complEx/model.py +193 -0
  45. framework/recommender/models/deep_walk_based/__init__.py +0 -0
  46. framework/recommender/models/deep_walk_based/model.py +149 -0
  47. framework/recommender/models/distMult/__init__.py +0 -0
  48. framework/recommender/models/distMult/model.py +187 -0
  49. framework/recommender/models/ePHEN/__init__.py +0 -0
  50. framework/recommender/models/ePHEN/model.py +159 -0
  51. framework/recommender/models/rESCAL/__init__.py +0 -0
  52. framework/recommender/models/rESCAL/model.py +184 -0
  53. framework/recommender/models/rotatE/__init__.py +0 -0
  54. framework/recommender/models/rotatE/model.py +195 -0
  55. framework/recommender/models/transD/__init__.py +0 -0
  56. framework/recommender/models/transD/model.py +190 -0
  57. framework/recommender/models/transE/__init__.py +0 -0
  58. framework/recommender/models/transE/model.py +194 -0
  59. framework/recommender/models/transH/__init__.py +0 -0
  60. framework/recommender/models/transH/model.py +193 -0
  61. framework/recommender/models/transR/__init__.py +0 -0
  62. framework/recommender/models/transR/model.py +190 -0
  63. framework/recommender/models/tuckER/__init__.py +0 -0
  64. framework/recommender/models/tuckER/model.py +199 -0
  65. framework/recommender/recommender.py +32 -0
  66. framework/recommender/utils/__init__.py +0 -0
  67. framework/recommender/utils/walker.py +185 -0
  68. framework/reporter/__init__.py +0 -0
  69. framework/reporter/report.py +101 -0
  70. framework/utils.py +10 -0
@@ -0,0 +1,270 @@
1
+ import os
2
+ import queue
3
+ import string
4
+ import pandas as pd
5
+ from string import Template
6
+ from .datasets.worker import Worker
7
+
8
+ from SPARQLWrapper import SPARQLWrapper, JSON
9
+ from tqdm import tqdm
10
+
11
+
12
+ class Dataset:
13
+ def __init__(self, input_path, output_path, n_workers=1):
14
+ r"""
15
+ Base class for Recommender System's datasets
16
+ """
17
+ self.input_path = input_path
18
+ self.output_path = output_path
19
+ self.n_workers = n_workers
20
+
21
+ self.sparql_endpoint = "http://dbpedia.org/sparql"
22
+ self.timeout = 180
23
+
24
+ # Output files
25
+ self.item_filename = os.path.join(self.output_path, "item.csv")
26
+ self.user_filename = os.path.join(self.output_path, "user.csv")
27
+ self.rating_filename = os.path.join(self.output_path, "rating.csv")
28
+ self.social_filename = os.path.join(self.output_path, "social.csv")
29
+ self.map_filename = os.path.join(self.output_path, "map.csv")
30
+ self.enriched_filename = os.path.join(self.output_path, "enriched.csv")
31
+
32
+ # create output path if doesn't exist
33
+ self.has_output_path()
34
+ # Mapping regex special chars
35
+ self._special_chars_map = str.maketrans({x: "" for x in string.punctuation})
36
+
37
+ # The following variables need to be overwritten by the subclasses of Dataset()
38
+ self.dataset_name = ""
39
+ self.item_separator = ""
40
+ self.user_separator = ""
41
+ self.rating_separator = ""
42
+ self.social_separator = ""
43
+ # Chosen fields to be extracted from dataset and mapping between the original column name and
44
+ # their standardized name
45
+ # Example: {"movie_title": "movie_title::string"}
46
+ self.reverse_dict = lambda x: {v: k for k, v in x.items()}
47
+ self.item_fields = {}
48
+ self.user_fields = {}
49
+ self.rating_fields = {}
50
+ self.map_query_template = Template(None)
51
+ self.enrich_query_template = Template(None)
52
+
53
+ def has_output_path(self):
54
+ if not os.path.isdir(self.output_path):
55
+ os.mkdir(self.output_path)
56
+
57
+ def load_item_data(self) -> pd.DataFrame:
58
+ """
59
+ Loads item info of Dataset
60
+ :return: returns pd.Dataframe containing each item info.
61
+ """
62
+ raise NotImplementedError
63
+
64
+ def load_user_data(self) -> pd.DataFrame:
65
+ """
66
+ Loads user info of Dataset
67
+ :return: returns pd.Dataframe containing each user info.
68
+ """
69
+ raise NotImplementedError
70
+
71
+ def load_rating_data(self) -> pd.DataFrame:
72
+ """
73
+ Loads rating interactions of Dataset
74
+ :return: returns pd.Dataframe containing each rating interaction.
75
+ """
76
+ raise NotImplementedError
77
+
78
+ def load_social_data(self) -> pd.DataFrame:
79
+ """
80
+ Loads social links of Dataset
81
+ :return: returns pd.Dataframe containing each social link between users.
82
+ """
83
+ raise NotImplementedError
84
+
85
+ def entity_linking(self, df_item) -> pd.DataFrame:
86
+ """
87
+ Entity link each item to their corresponding DBpedia's URI
88
+ :return: returns pd.Dataframe containing each item_id and their mapped URI
89
+ """
90
+ raise NotImplementedError
91
+
92
+ def enrich(self, df_map):
93
+ raise NotImplementedError
94
+
95
+ def get_map_query(self, *args, **kwargs) -> str:
96
+ """
97
+ Returns query parameters to be substituted in the map query template.
98
+ :returns: returns string query
99
+ """
100
+ raise NotImplementedError
101
+
102
+ def get_enrich_query(self, *args, **kwargs) -> str:
103
+ """
104
+ Returns query parameters to be substituted in the enrich query template.
105
+ :returns: returns string query
106
+ """
107
+ raise NotImplementedError
108
+
109
+ def parallel_queries(self, queue, return_type=JSON):
110
+ """
111
+ Parallel query SPARQL endpoint using Threads
112
+ :arguments:
113
+ queue: queue of tuples indicating the item_id and SPARQL query string
114
+ :returns: list containing tuples with item_id and response result in JSON
115
+ """
116
+ n_iters = queue.qsize()
117
+ pbar = tqdm(total=n_iters)
118
+ pbar.set_description("Requesting SPARQL endpoint for each item")
119
+
120
+ # instantiating each Worker
121
+ workers = []
122
+ for _ in range(self.n_workers):
123
+ worker = Worker(queue, lambda q: self._query(q, return_type), pbar)
124
+ worker.start()
125
+ workers.append(worker)
126
+
127
+ for worker in workers:
128
+ worker.join()
129
+ pbar.close()
130
+
131
+ # combining results
132
+ responses = []
133
+ for worker in workers:
134
+ responses.extend(worker.local_results)
135
+
136
+ return responses
137
+
138
+ def sequential_queries(self, q, return_type=JSON):
139
+ """
140
+ Sequential query SPARQL endpoint
141
+ :arguments:
142
+ queue: queue of tuples indicating the item_id and SPARQL query string
143
+ :returns: list containing tuples with item_id and response result in JSON
144
+ """
145
+ pbar = tqdm(total=q.qsize())
146
+ pbar.set_description("Requesting SPARQL endpoint for each item")
147
+ responses = []
148
+
149
+ while True:
150
+ try:
151
+ idx, query = q.get(block=False)
152
+ response = self._query(query, return_type)
153
+ responses.append((idx, response))
154
+ pbar.update(n=1)
155
+ except queue.Empty:
156
+ break
157
+ except Exception as e:
158
+ print(f"Exception:")
159
+ print(e)
160
+
161
+ return responses
162
+
163
+ def _query(self, query, return_type=JSON) -> dict:
164
+ sparql = SPARQLWrapper(self.sparql_endpoint)
165
+ sparql.setTimeout(self.timeout)
166
+ sparql.setQuery(query)
167
+ sparql.setReturnFormat(return_type)
168
+
169
+ try:
170
+ return sparql.query().convert()
171
+ except Exception as e:
172
+ raise e
173
+
174
+ def convert_item_data(self):
175
+ """
176
+ Converts loaded item data to a processed csv (item.csv).
177
+ """
178
+ try:
179
+ print(f"Creating file: {self.item_filename}.")
180
+ df_item = self.load_item_data()
181
+ print(f"{df_item.shape[0]} items with {df_item.shape[1]} Fields")
182
+ print("Fields: " + ", ".join(df_item.columns))
183
+ df_item.to_csv(self.item_filename, index=False)
184
+ except NotImplementedError:
185
+ print("Override load_item_data() method of your Dataset subclass.")
186
+
187
+ def convert_user_data(self):
188
+ """
189
+ Converts loaded user data to a processed csv (user.csv).
190
+ """
191
+ try:
192
+ print(f"Creating file: {self.user_filename}")
193
+ df_user = self.load_user_data()
194
+ print(f"{df_user.shape[0]} users with {df_user.shape[1]} Fields")
195
+ print("Fields: " + ", ".join(df_user.columns))
196
+ df_user.to_csv(self.user_filename, index=False)
197
+ except NotImplementedError:
198
+ print(f"Override load_user_data() of your Dataset subclass.")
199
+
200
+ def convert_rating_data(self):
201
+ """
202
+ Converts loaded rating data to a processed csv (rating.csv)
203
+ """
204
+ try:
205
+ print(f"Creating file: {self.rating_filename}")
206
+ df_rating = self.load_rating_data()
207
+ print(f"{df_rating.shape[0]} ratings with {df_rating.shape[1]} Fields")
208
+ print("Fields: " + ", ".join(df_rating.columns))
209
+ df_rating.to_csv(self.rating_filename, index=False)
210
+ except NotImplementedError:
211
+ print(f"Override load_rating_data() of your Dataset subclass.")
212
+
213
+ def convert_social_data(self):
214
+ """
215
+ Converts loaded social data to a processed csv (social.csv)
216
+ """
217
+ try:
218
+ print(f"Creating file: {self.social_filename}")
219
+ df_social = self.load_social_data()
220
+ print(f"{df_social.shape[0]} social link between users")
221
+ df_social.to_csv(self.social_filename, index=False)
222
+ except NotImplementedError:
223
+ print(f"Override load_rating_data() of your Dataset subclass.")
224
+
225
+ def map_URIs(self):
226
+ """
227
+ Maps each item to their corresponding DBpedia's URI.
228
+ """
229
+ try:
230
+ df_item = pd.read_csv(self.item_filename)
231
+ df_item.columns = [col.split("::")[0] for col in df_item.columns]
232
+ print(f"Mapping each dataset item with DBpedia: {self.map_filename}")
233
+ df_map = self.entity_linking(df_item)
234
+ df_map.to_csv(self.map_filename, index=False)
235
+
236
+ # print mapping statistics
237
+ n_items = df_map.shape[0]
238
+ n_unmatched = df_map[self.map_fields["URI"]].isna().sum()
239
+ print(
240
+ f"{n_unmatched} items weren't matched, corresponding to {n_unmatched/n_items*100:.2f}% of a total of {n_items}."
241
+ )
242
+
243
+ except NotImplementedError:
244
+ print("Override entity_linking() method of your Dataset subclass.")
245
+
246
+ def enrich_data(self):
247
+ """
248
+ Enrich each mapped item using DBpedia's resources.
249
+ """
250
+
251
+ try:
252
+ df_map = pd.read_csv(self.map_filename)
253
+ print(
254
+ f"Enriching each item with DBpedia resources: {self.enriched_filename}"
255
+ )
256
+ df_enrich = self.enrich(df_map)
257
+ df_enrich.to_csv(self.enriched_filename)
258
+
259
+ self.report_enriching(df_enrich)
260
+
261
+ except NotImplementedError:
262
+ print("Override enrich() method of your Dataset subclass.")
263
+
264
+ def report_enriching(self, df):
265
+ n_rows = df.shape[0]
266
+ for col in df.columns:
267
+ n_rows_with = df[col].notna().sum()
268
+ print(
269
+ f"number of entities with the property {col}: {n_rows_with} ({n_rows_with/n_rows*100:.2f}%)"
270
+ )
@@ -0,0 +1,34 @@
1
+ dataset2class = {
2
+ 'ml-100k': {
3
+ 'submodule': 'datasets.movielens',
4
+ 'class': 'MovieLens100k'
5
+ },
6
+ 'ml-1m': {
7
+ 'submodule': 'datasets.movielens',
8
+ 'class': 'MovieLens1M'
9
+ },
10
+ 'lastfm': {
11
+ 'submodule': 'datasets.lastfm',
12
+ 'class': 'LastFM'
13
+ },
14
+ 'book-crossing': {
15
+ 'submodule': 'datasets.book-crossing',
16
+ 'class': 'BookCrossing'
17
+ },
18
+ 'steam': {
19
+ 'submodule': 'datasets.steam',
20
+ 'class': 'Steam'
21
+ },
22
+ 'yelp': {
23
+ 'submodule': 'datasets.yelp',
24
+ 'class': 'Yelp'
25
+ },
26
+ 'douban-movie': {
27
+ 'submodule': 'datasets.douban-movie',
28
+ 'class': 'DoubanMovie'
29
+ },
30
+ 'amazon-video_games-5': {
31
+ 'submodule': 'datasets.amazon',
32
+ 'class': 'AmazonVideoGames5'
33
+ }
34
+ }
File without changes
@@ -0,0 +1,140 @@
1
+ import os
2
+ import gzip
3
+ import json
4
+ import pandas as pd
5
+ import re
6
+ from collections import defaultdict
7
+ from bs4 import BeautifulSoup
8
+
9
+ from ..dataset import Dataset
10
+
11
+ class Amazon(Dataset):
12
+ """
13
+ General Amazon(Dataset) class for data loading all different Amazon datasets
14
+ """
15
+ def __init__(self, input_path, output_path, n_workers=1):
16
+ super().__init__(input_path, output_path, n_workers)
17
+
18
+ def _parse(self, path):
19
+ g = gzip.open(path, 'rb')
20
+ for l in g:
21
+ yield json.loads(l)
22
+
23
+ def _getDF(self, path):
24
+ i = 0
25
+ df = {}
26
+ for d in self._parse(path):
27
+ df[i] = d
28
+ i += 1
29
+ return pd.DataFrame.from_dict(df, orient='index')
30
+
31
+ class AmazonVideoGames5(Amazon):
32
+ def __init__(self, input_path, output_path, n_workers=1):
33
+ super().__init__(input_path, output_path, n_workers)
34
+ self.dataset_name = "Amazon Video Games 5-core"
35
+
36
+ self.string_list_separator = "::"
37
+
38
+ self.item_fields = {
39
+ "asin": "item_id::string",
40
+ "title": "name::string",
41
+ "feature": "features::string_list",
42
+ "description": "description::string",
43
+ "price": "price::number",
44
+ "also_buy": "also_buy::string_list",
45
+ "also_view": "also_view::string_list",
46
+ "brand": "brand::string",
47
+ "category": "categories::string_list"
48
+ }
49
+
50
+ self.user_fields = {
51
+ "reviewerID": "user_id::string",
52
+ "reviewerName": "name::string",
53
+ }
54
+
55
+ self.rating_fields = {
56
+ "reviewerID": "user_id::string",
57
+ "asin": "item_id::string",
58
+ "reviewText": "text::string",
59
+ "overall": "rating::number",
60
+ "summary": "summary::string",
61
+ "unixReviewTime": "timestamp::number"
62
+ }
63
+
64
+ def load_item_data(self) -> pd.DataFrame():
65
+ filename = os.path.join(self.input_path, "Video_Games_5.json.gz")
66
+ print(filename)
67
+ df = self._getDF(filename)
68
+
69
+ filename = os.path.join(self.input_path, "meta_Video_Games.json.gz")
70
+ print(filename)
71
+ meta_df = self._getDF(filename)
72
+ meta_df = meta_df[meta_df['asin'].isin(df['asin'].unique())].reset_index(drop=True)
73
+
74
+ features = defaultdict(list)
75
+ for index, row in meta_df['feature'].dropna().items():
76
+ for feature in row:
77
+ soup = BeautifulSoup(feature, 'html.parser')
78
+ feature = soup.text
79
+ if "" != feature:
80
+ features[index].append(feature)
81
+ meta_df['feature'] = pd.Series(features).apply(lambda x: self.string_list_separator.join(x))
82
+
83
+ descriptions = defaultdict(list)
84
+ for index, row in meta_df['description'].dropna().items():
85
+ for description in row:
86
+ soup = BeautifulSoup(description, 'html.parser')
87
+ description = soup.text
88
+ if "" != description:
89
+ descriptions[index].append(description)
90
+ meta_df['description'] = pd.Series(descriptions).apply(lambda x: " ".join(x))
91
+
92
+ numeric_const_pattern = '[-+]? (?: (?: \d* \. \d+ ) | (?: \d+ \.? ) )(?: [Ee] [+-]? \d+ ) ?'
93
+ rx = re.compile(numeric_const_pattern, re.VERBOSE)
94
+ meta_df['price'] = meta_df['price'].apply(lambda x: rx.findall(x))
95
+ meta_df['price'] = meta_df['price'].apply(lambda x: x[0] if len(x) > 0 else None)
96
+ meta_df['price'] = meta_df['price'].astype('float64')
97
+
98
+ also_buys = defaultdict(list)
99
+ for index, row in meta_df['also_buy'].dropna().items():
100
+ for also_buy in row:
101
+ also_buys[index].append(also_buy)
102
+ meta_df['also_buy'] = pd.Series(also_buys).apply(lambda x: self.string_list_separator.join(x))
103
+
104
+ also_views = defaultdict(list)
105
+ for index, row in meta_df['also_view'].dropna().items():
106
+ for also_view in row:
107
+ also_views[index].append(also_view)
108
+ meta_df['also_view'] = pd.Series(also_views).apply(lambda x: self.string_list_separator.join(x))
109
+
110
+ categories = defaultdict(list)
111
+ for index, row in meta_df['category'].dropna().items():
112
+ for category in row:
113
+ soup = BeautifulSoup(category, 'html.parser')
114
+ category = soup.text
115
+ if "" != category:
116
+ categories[index].append(category)
117
+ meta_df['category'] = pd.Series(categories).apply(lambda x: self.string_list_separator.join(x))
118
+
119
+ meta_df = meta_df.rename(self.item_fields, axis=1)
120
+ meta_df = meta_df[self.item_fields.values()]
121
+ return meta_df
122
+
123
+ def load_user_data(self) -> pd.DataFrame():
124
+ filename = os.path.join(self.input_path, "Video_Games_5.json.gz")
125
+ print(filename)
126
+ df = self._getDF(filename)
127
+
128
+ df = df.rename(self.user_fields, axis=1)
129
+ df = df[self.user_fields.values()]
130
+ return df
131
+
132
+ def load_rating_data(self) -> pd.DataFrame():
133
+ filename = os.path.join(self.input_path, "Video_Games_5.json.gz")
134
+ print(filename)
135
+ df = self._getDF(filename)
136
+
137
+ df = df.rename(self.rating_fields, axis=1)
138
+ df = df[self.rating_fields.values()]
139
+ return df
140
+
@@ -0,0 +1,98 @@
1
+ import os
2
+ import re
3
+ import queue
4
+ from string import Template
5
+ from ..dataset import Dataset
6
+
7
+ import pandas as pd
8
+ from tqdm import tqdm
9
+ from thefuzz import process
10
+
11
+ class BookCrossing(Dataset):
12
+ def __init__(self, input_path, output_path, n_workers=1):
13
+ super().__init__(input_path, output_path, n_workers)
14
+ self.dataset_name = 'Book-Crossing'
15
+
16
+ self.item_separator = ';'
17
+ self.user_separator = ';'
18
+ self.rating_separator = ';'
19
+
20
+ self.item_fields = ['item_id', 'title', 'author', 'year', 'publisher']
21
+ # self.user_fields = ['user_id', 'age', 'gender', 'occupation']
22
+ self.rating_fields = ['user_id', 'item_id', 'rating']
23
+ # self.map_fields = ['item_id', 'URI']
24
+
25
+ self.map_query_template = Template('''
26
+ PREFIX dct: <http://purl.org/dc/terms/>
27
+ PREFIX dbo: <http://dbpedia.org/ontology/>
28
+ PREFIX dbr: <http://dbpedia.org/resource/>
29
+ PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
30
+ PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
31
+ SELECT DISTINCT ?book WHERE {
32
+ {
33
+ ?book rdf:type dbo:WrittenWork .
34
+ ?book rdfs:label ?label .
35
+ FILTER regex(?label, "$name_regex", "i")
36
+ }
37
+ UNION
38
+ {
39
+ ?book rdf:type dbo:WrittenWork .
40
+ ?tmp dbo:wikiPageRedirects ?book .
41
+ ?tmp rdfs:label ?label .
42
+ FILTER regex(?label, "$name_regex", "i") .
43
+ }
44
+ }
45
+ ''')
46
+
47
+
48
+ def load_item_data(self) -> pd.DataFrame():
49
+ filename = os.path.join(self.input_path, 'BX-Books.csv')
50
+
51
+ df = pd.read_csv(filename, sep=self.item_separator, encoding='CP1252', escapechar='\\')
52
+ df = df.drop(df.columns[-3:], axis=1) # Will not use picture URL related fields
53
+ df.columns = self.item_fields
54
+ return df
55
+
56
+ def entity_linking(self, df_item) -> pd.DataFrame():
57
+ q = queue.Queue()
58
+ for idx, row in df_item[['title', 'item_id']].iterrows():
59
+ query = self.get_map_query(row['title'])
60
+ q.put((row['item_id'], query))
61
+
62
+ if self.n_workers > 1:
63
+ responses = self.parallel_queries(q)
64
+ else:
65
+ responses = self.sequential_queries(q)
66
+
67
+ URI_mapping = {}
68
+ for response in tqdm(responses, desc='Disambiguating query return'):
69
+ candidate_URIs = []
70
+ idx, result = response
71
+ for binding in result['results']['bindings']:
72
+ URI = binding['book']['value']
73
+ candidate_URIs.append(URI)
74
+
75
+ expected_URI = f'http://dbpedia.org/resource/{df_item.loc[df_item.item_id == idx]["title"]}'
76
+ str_matching_result = process.extractOne(expected_URI, candidate_URIs)
77
+
78
+ if str_matching_result is not None:
79
+ URI, _ = str_matching_result
80
+ URI_mapping[idx] = URI
81
+
82
+ df_map = pd.DataFrame({'item_id': df_item['item_id']})
83
+ df_map.set_index('item_id')
84
+ df_map['URI'] = df_map['item_id'].apply(lambda id: URI_mapping.get(id))
85
+
86
+ return df_map
87
+
88
+ def get_map_query(self, book_title) -> str:
89
+ book_title = book_title.translate(self._special_chars_map)
90
+ book_title = book_title.replace(' ', '.*')
91
+ book_title = '^' + book_title
92
+
93
+ params = {'name_regex': book_title}
94
+ query = self.map_query_template.substitute(**params)
95
+ return query
96
+
97
+
98
+
@@ -0,0 +1,75 @@
1
+ import os
2
+ import pandas as pd
3
+
4
+ from ..dataset import Dataset
5
+
6
+ class DoubanMovie(Dataset):
7
+ def __init__(self, input_path, output_path, n_workers=1):
8
+ super().__init__(input_path, output_path, n_workers)
9
+ self.dataset_name = "Douban Movie Short"
10
+
11
+ self.item_fields_extract = ["Movie_Name_EN", "Movie_Name_CN"]
12
+ self.user_fields_extract = ["Username"]
13
+ self.rating_fields_extract = ["ID", "Date", "Star", "Comment", "Like", "Movie_Name_EN", "Username"]
14
+
15
+ self.item_fields = {
16
+ "movie_id": "item_id::string",
17
+ "Movie_Name_EN": "name_EN::string",
18
+ "Movie_Name_CN": "name_CN::string"
19
+ }
20
+
21
+ self.user_fields = {
22
+ "user_id": "user_id::string",
23
+ "Username": "name::string"
24
+ }
25
+
26
+ self.rating_fields = {
27
+ "user_id": "user_id::string",
28
+ "movie_id": "item_id::string",
29
+ "Star": "rating::number",
30
+ "Date": "date::string",
31
+ "Comment": "comment::string",
32
+ "Like": "like_count::number"
33
+ }
34
+
35
+ def load_item_data(self) -> pd.DataFrame():
36
+ filename = os.path.join(self.input_path, "DMSC.csv")
37
+ df = pd.read_csv(filename)
38
+
39
+ df = df[self.item_fields_extract].drop_duplicates().reset_index(drop=True)
40
+ df['movie_id'] = df.index
41
+
42
+ df = df.rename(self.item_fields, axis=1)
43
+ df = df[self.item_fields.values()]
44
+ return df
45
+
46
+ def load_user_data(self) -> pd.DataFrame():
47
+ filename = os.path.join(self.input_path, "DMSC.csv")
48
+ df = pd.read_csv(filename)
49
+
50
+ df = df[self.user_fields_extract].drop_duplicates().reset_index(drop=True)
51
+ df['user_id'] = df.index
52
+
53
+ df = df.rename(self.user_fields, axis=1)
54
+ df = df[self.user_fields.values()]
55
+ return df
56
+
57
+ def load_rating_data(self) -> pd.DataFrame():
58
+ filename = os.path.join(self.input_path, "DMSC.csv")
59
+ df = pd.read_csv(filename)
60
+
61
+ item_df = df[self.item_fields_extract].drop_duplicates().reset_index(drop=True)
62
+ item_df['movie_id'] = item_df.index
63
+
64
+ user_df = df[self.user_fields_extract].drop_duplicates().reset_index(drop=True)
65
+ user_df['user_id'] = user_df.index
66
+
67
+ df = df[self.rating_fields_extract].drop_duplicates().reset_index(drop=True)
68
+
69
+ df = df.join(item_df.set_index('Movie_Name_EN'), on='Movie_Name_EN')
70
+ df = df.join(user_df.set_index('Username'), on='Username')
71
+
72
+ df = df.rename(self.rating_fields, axis=1)
73
+ df = df[self.rating_fields.values()]
74
+ return df
75
+