History of tripadvisor_ratings
The changes to the source code of this entry in the repository, newest first, each with the differences it made to the entry's files. They are also listed on GitLab.
[entries] Add the date when each entry was added, and remove icon_hash beff7c9
The dates are those of the commits that first added the entries' files (2020-08-11 for the entries of the initial commit); renamed entries keep the date of their first name. Changed: none
diff --git a/entries/tripadvisor_ratings/__init__.py b/entries/tripadvisor_ratings/__init__.py
index 2f7cb4a..4192ee7 100644
--- a/entries/tripadvisor_ratings/__init__.py
+++ b/entries/tripadvisor_ratings/__init__.py
@@ -56,7 +56,7 @@ url = ['https://www.cs.virginia.edu/~hw5x/dataset.html',
'https://web.archive.org/web/20140914234636/http://times.cs.uiuc.edu/~wang296/Data/']
citation = [('H. Wang, Y. Lu, C. Zhai, "Latent aspect rating analysis on review text data: a rating regression approach." Proceedings of the 16th ACM SIGKDD International Conference on Knowledge Discovery and Data Mining, 783-792 (2010).', 'https://doi.org/10.1145/1835804.1835903'),
('H. Wang, Y. Lu, C. Zhai, "Latent aspect rating analysis without aspect keyword supervision." Proceedings of the 17th ACM SIGKDD International Conference on Knowledge Discovery and Data Mining, 618-626 (2011).', 'https://doi.org/10.1145/2020408.2020505')]
-icon_hash = None
+date_added = '2026-10-27'
upstream_license = None
# The data are no longer online; they are obtained from the Internet Archive.
archived_urls = {
[entries] Prefix the properties in the descriptions with (Node), (Edge) or (Graph) bc3dbbd
The lists of properties at the end of the descriptions marked edge properties with "Edge" but left node properties unmarked, or grouped them in "Nodes: `a`, ...; `b`, ..." items, so the kind of a property was often unclear. Every item now starts with (Node), (Edge) or (Graph), followed by the property names, a colon and the explanation, one item per property or group of properties; qualifications (kinds of nodes, networks) follow the names in parentheses. Properties that were only named got a short explanation, and a few items that named the wrong kind of property were corrected.
diff --git a/entries/tripadvisor_ratings/__init__.py b/entries/tripadvisor_ratings/__init__.py
index 8d7ca77..2f7cb4a 100644
--- a/entries/tripadvisor_ratings/__init__.py
+++ b/entries/tripadvisor_ratings/__init__.py
@@ -36,21 +36,21 @@ The reviewers are identified by their TripAdvisor screen names. The reviews whos
The networks have the following properties:
-- `name`: the screen name of the reviewer (as given in the data for anonymous reviews), or the name of the hotel.
-- `is_hotel`: whether the node is a hotel (otherwise a reviewer).
-- `anonymous`: whether the node is an anonymous review (see above).
-- `hotel_id`: the TripAdvisor id of the hotel (-1 for reviewers).
-- `geo_id`: the TripAdvisor id of the hotel's location (-1 if unknown, and for reviewers).
-- `location`: the location of the hotel (from its URL, e.g. `Seattle Washington`; empty if unknown, and for reviewers).
-- `hotel_rating`: the average rating of the hotel shown by TripAdvisor (`texts` only; -1 for reviewers).
-- `price`: the (lowest) price of the hotel in US dollars, as shown by TripAdvisor (-1 if unknown, and for reviewers).
-- `address`: the address of the hotel (`json` only; empty if unknown, and for reviewers).
-- Edge `rating`: the overall rating of the review, from 0 to 5 (-1 if not given).
-- Edge `rating_value`, `rating_rooms`, `rating_location`, `rating_cleanliness`, `rating_check_in`, `rating_service`, `rating_business_service`: the ratings of these aspects, from 0 to 5 (-1 if not given); `json` also has `rating_sleep_quality`.
-- Edge `time`: the date of the review, in Unix time (-1 if unknown).
-- Edge `readers`, `helpful`: the number of readers of the review, and of readers who found it helpful (`texts` only; -1 if not given).
-- Edge `review_id`: the TripAdvisor id of the review (`json` only).
-- Edge `author_location`: the location given by the reviewer (`json` only; empty if not given)."""
+- (Node) `name`: the screen name of the reviewer (as given in the data for anonymous reviews), or the name of the hotel.
+- (Node) `is_hotel`: whether the node is a hotel (otherwise a reviewer).
+- (Node) `anonymous`: whether the node is an anonymous review (see above).
+- (Node) `hotel_id`: the TripAdvisor id of the hotel (-1 for reviewers).
+- (Node) `geo_id`: the TripAdvisor id of the hotel's location (-1 if unknown, and for reviewers).
+- (Node) `location`: the location of the hotel (from its URL, e.g. `Seattle Washington`; empty if unknown, and for reviewers).
+- (Node) `hotel_rating`: the average rating of the hotel shown by TripAdvisor (`texts` only; -1 for reviewers).
+- (Node) `price`: the (lowest) price of the hotel in US dollars, as shown by TripAdvisor (-1 if unknown, and for reviewers).
+- (Node) `address`: the address of the hotel (`json` only; empty if unknown, and for reviewers).
+- (Edge) `rating`: the overall rating of the review, from 0 to 5 (-1 if not given).
+- (Edge) `rating_value`, `rating_rooms`, `rating_location`, `rating_cleanliness`, `rating_check_in`, `rating_service`, `rating_business_service`: the ratings of these aspects, from 0 to 5 (-1 if not given); `json` also has `rating_sleep_quality`.
+- (Edge) `time`: the date of the review, in Unix time (-1 if unknown).
+- (Edge) `readers`, `helpful`: the number of readers of the review, and of readers who found it helpful (`texts` only; -1 if not given).
+- (Edge) `review_id`: the TripAdvisor id of the review (`json` only).
+- (Edge) `author_location`: the location given by the reviewer (`json` only; empty if not given)."""
tags = ['Economic', 'Online', 'Preferences', 'Bipartite', 'Weighted', 'Multigraph', 'Timestamps', 'Metadata']
url = ['https://www.cs.virginia.edu/~hw5x/dataset.html',
'https://web.archive.org/web/20140914234636/http://times.cs.uiuc.edu/~wang296/Data/']
[entries] Remove unneeded code from the entries 2c69f19
Unneeded "global files" statements, unused imports and variables, f-strings without placeholders, duplicate keys in foodweb_little_rock (keeping the order and the values used), and the second definition of get_lines() in twitter_higgs. Nothing changes in the networks or descriptions. Changed: none
diff --git a/entries/tripadvisor_ratings/__init__.py b/entries/tripadvisor_ratings/__init__.py
index b45190a..8d7ca77 100644
--- a/entries/tripadvisor_ratings/__init__.py
+++ b/entries/tripadvisor_ratings/__init__.py
@@ -292,7 +292,6 @@ def parse_json(f):
@coerce_props()
@annotate()
def parse(alts=None):
- global files
name = __name__.split(".")[-1]
for fname, alt, fmt in files:
if alts is not None and alt not in alts:
[entries] Move contact, convote_mentions, douban, email_company, epinions, flixster, football_league_results, hyves, lastfm, libimseti, livemocha, reuters, sa_companies, sister_cities, tripadvisor_ratings, wikilens_ratings and wikipedia_discussions_de to their original sources 8f097de
These entries downloaded KONECT's copies of the data. They now download the original data (live, from pinned Wayback Machine snapshots, or by re-extracting from public dumps), gaining the metadata KONECT dropped (names, identifiers, timestamps, attributes), with descriptions and citations rewritten from the original sources and factual errors fixed (years and periods of contact, email_company, douban, flixster, libimseti, sa_companies; the rated objects of epinions; the sign of convote_mentions; songs merged by title alone in lastfm). New networks: the trust network of epinions, the links of wikilens_ratings, the JSON crawl of tripadvisor_ratings, and current versions of sister_cities and wikipedia_discussions_de next to the ones KONECT used. asu_social_networks only loses its mention of KONECT. Changed: upstream contact convote_mentions douban email_company epinions flixster football_league_results hyves lastfm libimseti livemocha reuters sa_companies sister_cities tripadvisor_ratings wikilens_ratings wikipedia_discussions_de
diff --git a/entries/tripadvisor_ratings/__init__.py b/entries/tripadvisor_ratings/__init__.py
index 32d4cea..b45190a 100644
--- a/entries/tripadvisor_ratings/__init__.py
+++ b/entries/tripadvisor_ratings/__init__.py
@@ -16,36 +16,277 @@
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
-
+import calendar
+import json
+import tarfile
+import zipfile
from .. import *
-title = 'TripAdvisor hotel ratings (2002-2009)'
-description = """A bipartite network of users of TripAdvisor and the hotels they rated, from the hotel reviews collected by Wang, Lu and Zhai (2010, 2011), as distributed by KONECT. Each edge connects a user to a hotel, with the overall rating of the review (from 1 to 5 stars) and its date. KONECT removed the ratings of the users named "lass=" (26,590 ratings) and "A TripAdvisor Member" (37,704 ratings), which are probably anonymous ratings or errors. The data identify the users and hotels only by indices.
+title = 'TripAdvisor hotel ratings (2001-2009)'
+description = r"""Bipartite networks of the reviewers of TripAdvisor and the hotels they reviewed, from the hotel reviews crawled from TripAdvisor (www.tripadvisor.com) by Hongning Wang, Yue Lu and ChengXiang Zhai for their research on latent aspect rating analysis (2010, 2011). Each node is a reviewer or a hotel, and an edge connects a reviewer to a hotel they reviewed, with the review's overall rating, its ratings of individual aspects of the hotel (value, rooms, location, cleanliness, check in / front desk, service, business service; each from 0 to 5 stars, or -1 if not given), and its date. A reviewer who reviewed the same hotel several times is connected to it by several edges.
+
+The authors published two crawls, which are two networks:
+
+- `texts`: the parsed reviews of 1,759 hotels (the "Text" data set, 240,060 reviews from 2001 to 2009, 64,295 of them anonymous), with the number of readers and of helpful votes of each review.
+- `json`: the authors' "most comprehensive collection" of hotel reviews, crawled at different times and published in JSON format in 2014, with the TripAdvisor id of each review and the location given by its reviewer. The format varies slightly across hotels (e.g. some hotels have no name, address or price, and some reviews have no reviewer location).
-The network has the following properties:
+The reviewers are identified by their TripAdvisor screen names. The reviews whose author is not given by name, i.e. those of "A TripAdvisor Member" (a reviewer without a public profile), "lass=" (apparently a parsing error of the crawl, for which the author is missing), an empty name, or "Posted by a(n) ... traveler" (reviews imported from partner sites), are anonymous: each of them gets a node of its own (marked by `anonymous`), since they cannot be attributed to a single reviewer. In `texts`, the names of reviewers with non-Latin characters were stored in mangled form (UTF-8 decoded as GBK); they are restored where this is possible without loss, and left as they are otherwise. The hotels' names and locations are taken from their TripAdvisor URLs (the names in `json` where they are given); 36 hotels of `texts` have no URL, and so no name or location. The texts of the reviews (and their titles) are left out.
-- `is_hotel`: whether the node is a hotel (otherwise a user).
-- Edge `rating`: the overall rating, from 1 to 5.
-- Edge `time`: the date of the review, in Unix time."""
-tags = ['Economic', 'Online', 'Preferences', 'Weighted', 'Multigraph', 'Timestamps']
-url = ['http://konect.cc/networks/wang-tripadvisor', 'https://www.cs.virginia.edu/~hw5x/dataset.html']
-citation = [('H. Wang, Y. Lu, C. Zhai, "Latent aspect rating analysis on review text data: a rating regression approach." Proceedings of the 16th ACM SIGKDD International Conference on Knowledge Discovery and Data Mining, 783-792 (2010)', 'https://doi.org/10.1145/1835804.1835903'),
- ('H. Wang, Y. Lu, C. Zhai, "Latent aspect rating analysis without aspect keyword supervision." Proceedings of the 17th ACM SIGKDD International Conference on Knowledge Discovery and Data Mining, 618-626 (2011)', 'https://doi.org/10.1145/2020408.2020505'),
- ('J. Kunegis, "KONECT -- The Koblenz Network Collection." Proceedings of the 22nd International Conference on World Wide Web Companion, 1343-1350 (2013)', 'https://doi.org/10.1145/2487788.2488173')]
+The networks have the following properties:
+
+- `name`: the screen name of the reviewer (as given in the data for anonymous reviews), or the name of the hotel.
+- `is_hotel`: whether the node is a hotel (otherwise a reviewer).
+- `anonymous`: whether the node is an anonymous review (see above).
+- `hotel_id`: the TripAdvisor id of the hotel (-1 for reviewers).
+- `geo_id`: the TripAdvisor id of the hotel's location (-1 if unknown, and for reviewers).
+- `location`: the location of the hotel (from its URL, e.g. `Seattle Washington`; empty if unknown, and for reviewers).
+- `hotel_rating`: the average rating of the hotel shown by TripAdvisor (`texts` only; -1 for reviewers).
+- `price`: the (lowest) price of the hotel in US dollars, as shown by TripAdvisor (-1 if unknown, and for reviewers).
+- `address`: the address of the hotel (`json` only; empty if unknown, and for reviewers).
+- Edge `rating`: the overall rating of the review, from 0 to 5 (-1 if not given).
+- Edge `rating_value`, `rating_rooms`, `rating_location`, `rating_cleanliness`, `rating_check_in`, `rating_service`, `rating_business_service`: the ratings of these aspects, from 0 to 5 (-1 if not given); `json` also has `rating_sleep_quality`.
+- Edge `time`: the date of the review, in Unix time (-1 if unknown).
+- Edge `readers`, `helpful`: the number of readers of the review, and of readers who found it helpful (`texts` only; -1 if not given).
+- Edge `review_id`: the TripAdvisor id of the review (`json` only).
+- Edge `author_location`: the location given by the reviewer (`json` only; empty if not given)."""
+tags = ['Economic', 'Online', 'Preferences', 'Bipartite', 'Weighted', 'Multigraph', 'Timestamps', 'Metadata']
+url = ['https://www.cs.virginia.edu/~hw5x/dataset.html',
+ 'https://web.archive.org/web/20140914234636/http://times.cs.uiuc.edu/~wang296/Data/']
+citation = [('H. Wang, Y. Lu, C. Zhai, "Latent aspect rating analysis on review text data: a rating regression approach." Proceedings of the 16th ACM SIGKDD International Conference on Knowledge Discovery and Data Mining, 783-792 (2010).', 'https://doi.org/10.1145/1835804.1835903'),
+ ('H. Wang, Y. Lu, C. Zhai, "Latent aspect rating analysis without aspect keyword supervision." Proceedings of the 17th ACM SIGKDD International Conference on Knowledge Discovery and Data Mining, 618-626 (2011).', 'https://doi.org/10.1145/2020408.2020505')]
icon_hash = None
upstream_license = None
-upstream_prefix = 'http://konect.cc/files'
-files = [('download.tsv.wang-tripadvisor.tar.bz2', None, 'konect')]
+# The data are no longer online; they are obtained from the Internet Archive.
+archived_urls = {
+ "Review_Texts.zip": "https://web.archive.org/web/20150316061732id_/http://times.cs.uiuc.edu/~wang296/Data/LARA/TripAdvisor/Review_Texts.zip",
+ "TripAdvisorJson.tar.bz2": "https://web.archive.org/web/20221021211605id_/https://www.cs.virginia.edu/~hw5x/Data/LARA/TripAdvisor/TripAdvisorJson.tar.bz2",
+}
+upstream_prefix = None
+files = [("Review_Texts.zip", "texts", None),
+ ("TripAdvisorJson.tar.bz2", "json", None)]
-def postprocess(alt, g):
- g.ep.rating = g.ep.weight.copy("int")
- del g.ep["weight"]
+# Limit on the number of reviews read, for checks (None: all)
+_check_rows = None
def fetch_upstream(force=False):
- return fetch_upstream_files(__name__.split(".")[-1], upstream_prefix, files,
- force)
+ return fetch_archived_files(__name__.split(".")[-1], archived_urls,
+ force=force, tries=6)
+
+# The names of the authors of anonymous reviews
+_ANONYMOUS = {"", "A TripAdvisor Member", "lass="}
+
+def _is_anonymous(author):
+ return author in _ANONYMOUS or (author.startswith("Posted by a") and
+ author.endswith(" traveler"))
+
+def _fix_name(s):
+ "Undo the decoding of UTF-8 as GBK in the author names, if lossless."
+ if s.isascii():
+ return s
+ try:
+ return s.encode("gbk").decode("utf8")
+ except (UnicodeEncodeError, UnicodeDecodeError):
+ return s
+
+# (not calendar.month_name, which depends on the locale)
+_MONTH_NAMES = ["January", "February", "March", "April", "May", "June", "July",
+ "August", "September", "October", "November", "December"]
+_MONTHS = {m: i + 1 for i, m in enumerate(_MONTH_NAMES)}
+_MONTHS.update({m[:3]: i + 1 for i, m in enumerate(_MONTH_NAMES)})
+_MONTHS["Sept"] = 9
+
+def _date(s):
+ "Unix time of a date such as 'Dec 23, 2008' or 'April 1, 2012'."
+ m = re.fullmatch(r"\s*([A-Za-z]+)\.? (\d+), (\d{4})\s*", s)
+ if m is None or m.group(1) not in _MONTHS:
+ return -1
+ return calendar.timegm((int(m.group(3)), _MONTHS[m.group(1)],
+ int(m.group(2)), 0, 0, 0))
+
+def _url_info(url):
+ "Location id, hotel name and location from a TripAdvisor hotel URL."
+ m = re.search(r"-g(\d+)-d\d+-(?:r\d+|Reviews)-(.+)\.html", url)
+ if m is None:
+ return -1, "", ""
+ hname, _, loc = m.group(2).rpartition("-")
+ return int(m.group(1)), hname.replace("_", " "), loc.replace("_", " ")
+
+def _price(s):
+ m = re.match(r"\s*\$?([\d,]+)", s)
+ return int(m.group(1).replace(",", "")) if m else -1
+
+def _rating(s):
+ try:
+ return float(s)
+ except (TypeError, ValueError):
+ return -1
+
+_ASPECTS = {"Value": "value", "Rooms": "rooms", "Location": "location",
+ "Cleanliness": "cleanliness", "Check in / front desk": "check_in",
+ "Service": "service", "Business service": "business_service",
+ "Business service (e.g., internet access)": "business_service",
+ "Sleep Quality": "sleep_quality"}
+
+class _Builder:
+ "Collects the reviewers, hotels and reviews of a network."
+ def __init__(self, aspects, eprops, hprops):
+ self.aspects = aspects
+ self.reviewers = {}
+ self.rnames = []
+ self.ranon = []
+ self.hotels = []
+ self.hprops = {k: [] for k in hprops}
+ self.src, self.tgt = [], []
+ self.eprops = {k: [] for k in ["rating"] +
+ [f"rating_{a}" for a in aspects] + ["time"] + eprops}
+ self.nreviews = 0
+
+ def add_hotel(self, **props):
+ self.hotels.append(props["name"])
+ for k in self.hprops:
+ self.hprops[k].append(props[k])
+ return len(self.hotels) - 1
+
+ def add_review(self, h, author, **props):
+ if _is_anonymous(author):
+ r = len(self.rnames)
+ self.rnames.append(author)
+ self.ranon.append(True)
+ else:
+ r = self.reviewers.get(author)
+ if r is None:
+ r = self.reviewers[author] = len(self.rnames)
+ self.rnames.append(author)
+ self.ranon.append(False)
+ self.src.append(r)
+ self.tgt.append(h)
+ for k, v in self.eprops.items():
+ v.append(props[k])
+ self.nreviews += 1
+ return _check_rows is None or self.nreviews < _check_rows
+
+ def graph(self):
+ nr = len(self.rnames)
+ g = Graph(directed=False)
+ g.add_vertex(nr + len(self.hotels))
+ g.vp.name = g.new_vp("string", vals=self.rnames + self.hotels)
+ g.vp.is_hotel = g.new_vp("bool")
+ g.vp.is_hotel.a[nr:] = True
+ g.vp.anonymous = g.new_vp("bool")
+ g.vp.anonymous.a[:nr] = self.ranon
+ for k, vals in self.hprops.items():
+ if k == "name":
+ continue
+ if isinstance(vals[0] if vals else 0, str):
+ p = g.new_vp("string", vals=[""] * nr + vals)
+ else:
+ t = "double" if k == "hotel_rating" else "int64_t"
+ p = g.new_vp(t, val=-1)
+ p.a[nr:] = vals
+ g.vp[k] = p
+ num = [k for k in self.eprops if k != "author_location"]
+ for k in num:
+ g.ep[k] = g.new_ep("double" if k.startswith("rating") else "int64_t")
+ cols = [numpy.array(self.src, dtype="int64"),
+ numpy.array(self.tgt, dtype="int64") + nr]
+ g.add_edge_list(numpy.column_stack(cols + [numpy.array(self.eprops[k], dtype="float")
+ for k in num]),
+ eprops=[g.ep[k] for k in num])
+ if "author_location" in self.eprops:
+ g.ep.author_location = g.new_ep("string",
+ vals=self.eprops["author_location"])
+ return g
+
+_TEXT_ASPECTS = ["value", "rooms", "location", "cleanliness", "check_in",
+ "service", "business_service"]
+
+def parse_texts(f):
+ """The network of the parsed reviews in the zip file `f`: one file
+ "hotel_<id>.dat" per hotel, with a header ("<Overall Rating>...",
+ "<Avg. Price>...", "<URL>...") and the reviews ("<Author>...",
+ "<Content>...", "<Date>...", ...), separated by empty lines."""
+ b = _Builder(_TEXT_ASPECTS, ["readers", "helpful"],
+ ["name", "hotel_id", "geo_id", "location", "hotel_rating",
+ "price"])
+ with zipfile.ZipFile(f) as zf:
+ for member in zf.namelist():
+ m = re.fullmatch(r"hotel_(\d+)\.dat", os.path.basename(member))
+ if m is None:
+ continue
+ text = zf.read(member).decode("utf8").replace("\r\n", "\n")
+ recs = text.split("\n\n")
+ head = {}
+ for line in recs[0].split("\n"):
+ t = re.match(r"<([^>]*)>(.*)", line)
+ if t:
+ head[t.group(1)] = t.group(2)
+ geo, hname, loc = _url_info(head.get("URL", ""))
+ h = b.add_hotel(name=hname, hotel_id=int(m.group(1)), geo_id=geo,
+ location=loc,
+ hotel_rating=_rating(head.get("Overall Rating")),
+ price=_price(head.get("Avg. Price", "")))
+ for rec in recs[1:]:
+ d = {}
+ for line in rec.split("\n"):
+ t = re.match(r"<([^>]*)>(.*)", line)
+ # lines without a tag continue the content
+ if t:
+ d[t.group(1)] = t.group(2)
+ if "Author" not in d:
+ continue
+ props = {"rating": _rating(d.get("Overall")),
+ "time": _date(d.get("Date", "")),
+ "readers": int(d.get("No. Reader", -1)),
+ "helpful": int(d.get("No. Helpful", -1))}
+ for a in _TEXT_ASPECTS:
+ props[f"rating_{a}"] = -1
+ for k, a in _ASPECTS.items():
+ if a in _TEXT_ASPECTS and k in d:
+ props[f"rating_{a}"] = _rating(d[k])
+ if not b.add_review(h, _fix_name(d["Author"]), **props):
+ return b.graph()
+ return b.graph()
+
+_JSON_ASPECTS = _TEXT_ASPECTS + ["sleep_quality"]
+
+def parse_json(f):
+ """The network of the reviews in the bzip2-compressed tar file `f`: one
+ file "json/<hotel id>.json" per hotel, with "HotelInfo" and "Reviews"."""
+ b = _Builder(_JSON_ASPECTS, ["review_id", "author_location"],
+ ["name", "hotel_id", "geo_id", "location", "price",
+ "address"])
+ with tarfile.open(fileobj=f, mode="r|bz2") as tf:
+ for ti in tf:
+ if not ti.isfile() or not ti.name.endswith(".json"):
+ continue
+ d = json.load(tf.extractfile(ti))
+ info = d.get("HotelInfo", {})
+ geo, hname, loc = _url_info(info.get("HotelURL", ""))
+ addr = re.sub(r"\s+", " ", re.sub(r"<[^>]*>", "",
+ info.get("Address", ""))).strip()
+ hid = info.get("HotelID")
+ if hid is None:
+ hid = os.path.basename(ti.name)[:-5]
+ h = b.add_hotel(name=info.get("Name") or hname, hotel_id=int(hid),
+ geo_id=geo, location=loc,
+ price=_price(info.get("Price", "")), address=addr)
+ for r in d.get("Reviews", []):
+ ratings = r.get("Ratings", {})
+ rid = re.sub(r"\D", "", r.get("ReviewID", ""))
+ props = {"rating": _rating(ratings.get("Overall")),
+ "time": _date(r.get("Date", "")),
+ "review_id": int(rid) if rid else -1,
+ "author_location": r.get("AuthorLocation", "")}
+ for a in _JSON_ASPECTS:
+ props[f"rating_{a}"] = -1
+ for k, x in ratings.items():
+ if k in _ASPECTS:
+ props[f"rating_{_ASPECTS[k]}"] = _rating(x)
+ if not b.add_review(h, r.get("Author", ""), **props):
+ return b.graph()
+ return b.graph()
@cache_network()
@coerce_props()
@@ -56,7 +297,10 @@ def parse(alts=None):
for fname, alt, fmt in files:
if alts is not None and alt not in alts:
continue
- with open_upstream_file(name, fname, "rb") as f:
- g = parse_graph([f], fmt, directed=None)
- postprocess(alt, g)
+ if alt == "texts":
+ with open_upstream_file(name, fname, "rb", seekable=True) as f:
+ g = parse_texts(f)
+ else:
+ with open_upstream_file(name, fname, "rb") as f:
+ g = parse_json(f)
yield alt, g
[entries] New entry: tripadvisor_ratings 58df123
diff --git a/entries/tripadvisor_ratings/__init__.py b/entries/tripadvisor_ratings/__init__.py
new file mode 100644
index 0000000..32d4cea
--- /dev/null
+++ b/entries/tripadvisor_ratings/__init__.py
@@ -0,0 +1,62 @@
+#! /usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2026 Tiago de Paula Peixoto <tiago@skewed.de>
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU Affero General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or (at your option) any
+# later version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Affero General Public License along
+# with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+
+from .. import *
+
+
+title = 'TripAdvisor hotel ratings (2002-2009)'
+description = """A bipartite network of users of TripAdvisor and the hotels they rated, from the hotel reviews collected by Wang, Lu and Zhai (2010, 2011), as distributed by KONECT. Each edge connects a user to a hotel, with the overall rating of the review (from 1 to 5 stars) and its date. KONECT removed the ratings of the users named "lass=" (26,590 ratings) and "A TripAdvisor Member" (37,704 ratings), which are probably anonymous ratings or errors. The data identify the users and hotels only by indices.
+
+The network has the following properties:
+
+- `is_hotel`: whether the node is a hotel (otherwise a user).
+- Edge `rating`: the overall rating, from 1 to 5.
+- Edge `time`: the date of the review, in Unix time."""
+tags = ['Economic', 'Online', 'Preferences', 'Weighted', 'Multigraph', 'Timestamps']
+url = ['http://konect.cc/networks/wang-tripadvisor', 'https://www.cs.virginia.edu/~hw5x/dataset.html']
+citation = [('H. Wang, Y. Lu, C. Zhai, "Latent aspect rating analysis on review text data: a rating regression approach." Proceedings of the 16th ACM SIGKDD International Conference on Knowledge Discovery and Data Mining, 783-792 (2010)', 'https://doi.org/10.1145/1835804.1835903'),
+ ('H. Wang, Y. Lu, C. Zhai, "Latent aspect rating analysis without aspect keyword supervision." Proceedings of the 17th ACM SIGKDD International Conference on Knowledge Discovery and Data Mining, 618-626 (2011)', 'https://doi.org/10.1145/2020408.2020505'),
+ ('J. Kunegis, "KONECT -- The Koblenz Network Collection." Proceedings of the 22nd International Conference on World Wide Web Companion, 1343-1350 (2013)', 'https://doi.org/10.1145/2487788.2488173')]
+icon_hash = None
+upstream_license = None
+upstream_prefix = 'http://konect.cc/files'
+files = [('download.tsv.wang-tripadvisor.tar.bz2', None, 'konect')]
+
+def postprocess(alt, g):
+ g.ep.rating = g.ep.weight.copy("int")
+ del g.ep["weight"]
+
+def fetch_upstream(force=False):
+ return fetch_upstream_files(__name__.split(".")[-1], upstream_prefix, files,
+ force)
+
+@cache_network()
+@coerce_props()
+@annotate()
+def parse(alts=None):
+ global files
+ name = __name__.split(".")[-1]
+ for fname, alt, fmt in files:
+ if alts is not None and alt not in alts:
+ continue
+ with open_upstream_file(name, fname, "rb") as f:
+ g = parse_graph([f], fmt, directed=None)
+ postprocess(alt, g)
+ yield alt, g