Extending grouprec¶
We especially want researchers: add your method, or just open a port-request
issue with a link to your repo/paper and we'll port it. Students & practitioners
welcome — see good-first-issue. Full recipes are in
CONTRIBUTING.md; the
essentials:
New aggregator¶
from grouprec.aggregators.base import Aggregator, as_score_matrix, top_k_indices, available_mask
class MyAgg(Aggregator):
name = "MyAgg"
def aggregate(self, scores, k, *, exclude=None):
rm = as_score_matrix(scores)
return top_k_indices(rm.mean(axis=0), k, available_mask(rm.shape[1], exclude))
aggregators/__init__.py::_REGISTRY, add a citation to references.py,
add a test vs a reference oracle.
New deep model¶
Implement fit(dataset) + recommend(members, k, *, exclude=None, candidates=None),
set paradigm="profile", keep torch under grouprec/models/. Validate with
evaluate_sampled on CAMRa2011.
New dataset¶
Add a DatasetSpec (license + citation + policy) + a loader. Never bundle data.
New group kind / custom similarity¶
Similarity is pluggable: gr.groups.synthetic(..., metric=...) takes "pearson"/"cosine"/
"jaccard", a precomputed matrix, or a callable f(data) -> (n_users, n_users) (use
this to compute similarity over a feature subset or side-information embeddings).
Group kinds are pluggable the same way: pass kind= a builder
f(sim, size, rng) -> list[int] | None (member indices, or None if it stalls). The public
helpers build_predicate_group and build_outlier_group are the building blocks. Example —
a 2+2 group (a size-4 group made of two similar pairs that are dissimilar across pairs):
import numpy as np, grouprec as gr
data = gr.make_blobs_dataset(n_users=80, n_items=60, n_clusters=4, density=0.5, seed=0)
def two_plus_two(sim, size, rng, hi=0.3, lo=0.0):
n = sim.shape[0]
for _ in range(200):
a = int(rng.integers(n))
far = np.flatnonzero(sim[a] <= lo) # an anchor dissimilar to a
if not far.size:
continue
b = int(rng.choice(far))
pa = [int(x) for x in np.flatnonzero(sim[a] >= hi) if x not in (a, b)][:1] # partner near a
pb = [int(x) for x in np.flatnonzero(sim[b] >= hi) if x not in {a, b, *pa}][:1] # near b
if pa and pb:
return [a, *pa, b, *pb]
return None
groups = gr.groups.synthetic(data, kind=two_plus_two, size=4, n=10, metric="pearson", seed=0)
# groups.metadata["kind"] == "two_plus_two"
New metric¶
gr.eval.register_metric("coverage", lambda rec, gains, relevant, k: len(set(rec[:k])) / k)
gr.eval.register_aggregation("p90", lambda v: float(np.percentile(v, 90)))
Always add the paper to grouprec/references.py so gr.cite("MyMethod") works — a test
(tests/test_citations.py) fails CI if a registered aggregator/model/dataset has no
citation. Citations are auto-collected from the objects used in a run via
gr.collect_citations(...) / gr.Experiment(..., cite=[recommender, dataset]).