logo Netzschleuder network catalog, repository, and centrifuge

History of newsgroups_words

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 by Tiago de Paula Peixoto 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/newsgroups_words/__init__.py b/entries/newsgroups_words/__init__.py
index f3557c9..62bb6ef 100644
--- a/entries/newsgroups_words/__init__.py
+++ b/entries/newsgroups_words/__init__.py
@@ -32,7 +32,7 @@ tags = ['Text', 'Online', 'Unweighted', 'Metadata']
 url = 'https://cs.nyu.edu/~roweis/data.html'
 citation = [('S. Roweis, "Data for MATLAB hackers: 20 Newsgroups (20news_w100.mat)."', 'https://web.archive.org/web/2016/https://cs.nyu.edu/~roweis/data.html'),
             ('K. Lang, "NewsWeeder: Learning to Filter Netnews." Machine Learning Proceedings 1995, 331-339 (1995)', 'https://doi.org/10.1016/B978-1-55860-377-6.50048-7')]
-icon_hash = None
+date_added = '2026-10-25'
 upstream_license = None
 upstream_prefix = 'https://web.archive.org/web/20160306205635id_/http://cs.nyu.edu/~roweis/data'
 files = [('20news_w100.mat', None, None)]
[entries] Prefix the properties in the descriptions with (Node), (Edge) or (Graph) by Tiago de Paula Peixoto 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/newsgroups_words/__init__.py b/entries/newsgroups_words/__init__.py
index a81b7ec..f3557c9 100644
--- a/entries/newsgroups_words/__init__.py
+++ b/entries/newsgroups_words/__init__.py
@@ -24,8 +24,10 @@ description = """A bipartite network of Usenet newsgroup postings and the words
 
 The network has the following properties:
 
-- Nodes: `is_word`, whether the node is a word (otherwise a posting); `name`, the word (empty for postings).
-- Posting nodes: `index`, the position of the posting in the source data (starting from 0); `newsgroup`, the top-level newsgroup hierarchy of the posting ("comp.\\*", "rec.\\*", "sci.\\*" or "talk.\\*")."""
+- (Node) `is_word`: whether the node is a word (otherwise a posting).
+- (Node) `name`: the word (empty for postings).
+- (Node) `index` (postings): the position of the posting in the source data (starting from 0).
+- (Node) `newsgroup` (postings): the top-level newsgroup hierarchy of the posting ("comp.\\*", "rec.\\*", "sci.\\*" or "talk.\\*")."""
 tags = ['Text', 'Online', 'Unweighted', 'Metadata']
 url = 'https://cs.nyu.edu/~roweis/data.html'
 citation = [('S. Roweis, "Data for MATLAB hackers: 20 Newsgroups (20news_w100.mat)."', 'https://web.archive.org/web/2016/https://cs.nyu.edu/~roweis/data.html'),
[entries] Remove unneeded code from the entries by Tiago de Paula Peixoto 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/newsgroups_words/__init__.py b/entries/newsgroups_words/__init__.py
index 854f80d..a81b7ec 100644
--- a/entries/newsgroups_words/__init__.py
+++ b/entries/newsgroups_words/__init__.py
@@ -43,7 +43,6 @@ def fetch_upstream(force=False):
 @coerce_props()
 @annotate()
 def parse(alts=None):
-    global files
     name = __name__.split(".")[-1]
     for fnames, alt, fmt in files:
         if alts is not None and alt not in alts:
[entries] New entry: newsgroups_words by Tiago de Paula Peixoto ef05fa5
diff --git a/entries/newsgroups_words/__init__.py b/entries/newsgroups_words/__init__.py
new file mode 100644
index 0000000..854f80d
--- /dev/null
+++ b/entries/newsgroups_words/__init__.py
@@ -0,0 +1,81 @@
+#! /usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2020-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 *
+import scipy.io
+
+title = "20 Newsgroups word occurrences (1995)"
+description = """A bipartite network of Usenet newsgroup postings and the words they contain, from a reduced version of the "20 Newsgroups" collection prepared by S. Roweis. The 20 Newsgroups collection, assembled by K. Lang, consists of about 20,000 postings to 20 Usenet newsgroups. In this reduced version, the vocabulary is restricted to 100 selected words, and the data contain 16,242 postings, each containing at least one of them; each posting is connected to each of these words it contains (occurrence only, without counts). Each posting is labelled by the top-level hierarchy of its newsgroup (comp.\\*, rec.\\*, sci.\\* or talk.\\*). The data were obtained from the file "20news_w100.mat" formerly distributed on S. Roweis' data page, retrieved from the Internet Archive copy.
+
+The network has the following properties:
+
+- Nodes: `is_word`, whether the node is a word (otherwise a posting); `name`, the word (empty for postings).
+- Posting nodes: `index`, the position of the posting in the source data (starting from 0); `newsgroup`, the top-level newsgroup hierarchy of the posting ("comp.\\*", "rec.\\*", "sci.\\*" or "talk.\\*")."""
+tags = ['Text', 'Online', 'Unweighted', 'Metadata']
+url = 'https://cs.nyu.edu/~roweis/data.html'
+citation = [('S. Roweis, "Data for MATLAB hackers: 20 Newsgroups (20news_w100.mat)."', 'https://web.archive.org/web/2016/https://cs.nyu.edu/~roweis/data.html'),
+            ('K. Lang, "NewsWeeder: Learning to Filter Netnews." Machine Learning Proceedings 1995, 331-339 (1995)', 'https://doi.org/10.1016/B978-1-55860-377-6.50048-7')]
+icon_hash = None
+upstream_license = None
+upstream_prefix = 'https://web.archive.org/web/20160306205635id_/http://cs.nyu.edu/~roweis/data'
+files = [('20news_w100.mat', None, None)]
+
+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 fnames, alt, fmt in files:
+        if alts is not None and alt not in alts:
+            continue
+        if isinstance(fnames, str):
+            fnames = [fnames]
+        with ExitStack() as stack:
+            fs = [stack.enter_context(open_upstream_file(name, fn, "rb", seekable=True))
+                  for fn in fnames]
+
+            # MATLAB file: 'documents' (sparse, words x postings, 0/1),
+            # 'wordlist', 'newsgroups' (labels 1-4) and 'groupnames'
+            m = scipy.io.loadmat(fs[0])
+            words = [str(w[0]) for w in m["wordlist"][0]]
+            gnames = [str(x[0]) for x in m["groupnames"][0]]
+            labels = m["newsgroups"][0].astype("int")
+            A = m["documents"].tocoo()
+            ND = A.shape[1]
+            NW = A.shape[0]
+
+            g = Graph(directed=False)
+            g.add_vertex(ND + NW)
+            g.vp.is_word = g.new_vp("bool")
+            g.vp.name = g.new_vp("string")
+            g.vp.index = g.new_vp("int")
+            g.vp.newsgroup = g.new_vp("string")
+            g.vp.is_word.a[ND:] = True
+            g.vp.index.a[:ND] = numpy.arange(ND)
+            for i, w in enumerate(words):
+                g.vp.name[ND + i] = w
+            for d in range(ND):
+                g.vp.newsgroup[d] = gnames[labels[d] - 1]
+            idx = numpy.lexsort((A.row, A.col))
+            g.add_edge_list(numpy.array([A.col[idx], ND + A.row[idx]]).T)
+        yield alt, g