Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ description: >
Neural network architectures that operate directly on graph-structured data...
topic_id: null # OpenAlex topic delimiting the field
seed_papers: [] # OpenAlex work IDs the crawl starts from
exclude_works: [] # records whose metadata OpenAlex got wrong
crawl:
hop_depth: 2 # references, backwards from the seeds
forward_depth: 1 # works citing the seeds, within topic_id
Expand Down
6 changes: 6 additions & 0 deletions fields/gnn.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ seed_papers:
- W2624431344 # Inductive Representation Learning on Large Graphs (2017)
- W2766453196 # Graph Attention Networks (2017)

# Works dropped at cleaning: OpenAlex records whose metadata is wrong in a way
# the pipeline cannot detect, so they would surface in rankings under a name
# that is not theirs.
exclude_works:
- W2964015378 # cited 2347 times inside the graph, titled as a 2025 vulnerability-detection paper

crawl:
# How many citation hops to expand outward from the seeds.
hop_depth: 2
Expand Down
8 changes: 8 additions & 0 deletions src/graph/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import argparse
import json
import logging
from collections.abc import Iterable
from dataclasses import asdict, dataclass, field
from datetime import date
from pathlib import Path
Expand Down Expand Up @@ -44,6 +45,7 @@ class CleaningReport:

raw_nodes: int = 0
raw_edges: int = 0
excluded_nodes: int = 0
duplicate_nodes: int = 0
duplicate_edges: int = 0
out_of_range_nodes: int = 0
Expand Down Expand Up @@ -104,6 +106,7 @@ def clean(
edges: pd.DataFrame,
*,
date_range: DateRange | None = None,
exclude: Iterable[str] = (),
largest_component_only: bool = True,
) -> tuple[pd.DataFrame, pd.DataFrame, CleaningReport]:
"""Reduce raw crawl tables to a consistent node set and edge set."""
Expand All @@ -112,6 +115,10 @@ def clean(
nodes = nodes.drop_duplicates(subset="id")
report.duplicate_nodes = report.raw_nodes - len(nodes)

excluded = nodes["id"].isin(set(exclude))
report.excluded_nodes = int(excluded.sum())
nodes = nodes[~excluded]

if date_range is not None:
in_range = nodes["publication_year"].map(date_range.contains)
report.out_of_range_nodes = int((~in_range).sum())
Expand Down Expand Up @@ -266,6 +273,7 @@ def build_field(
nodes,
edges,
date_range=config.crawl.date_range,
exclude=config.exclude_works,
largest_component_only=largest_component_only,
)
surviving = set(nodes["id"])
Expand Down
1 change: 1 addition & 0 deletions src/graph/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class FieldConfig(BaseModel):
description: str
topic_id: str | None = None
seed_papers: list[str] = Field(default_factory=list)
exclude_works: list[str] = Field(default_factory=list)
crawl: CrawlConfig

@field_validator("description")
Expand Down
10 changes: 10 additions & 0 deletions tests/test_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,16 @@ def test_edges_past_the_crawl_horizon_are_dropped():
assert list(edges["target"]) == ["B"]


def test_excluded_works_are_dropped_with_their_edges():
nodes, edges = frames([node("A"), node("B"), node("C")], [("A", "B"), ("A", "C")])

nodes, edges, report = clean(nodes, edges, exclude=["B"])

assert report.excluded_nodes == 1
assert sorted(nodes["id"]) == ["A", "C"]
assert list(edges.itertuples(index=False, name=None)) == [("A", "C")]


def test_nodes_outside_the_date_range_are_dropped_with_their_edges():
nodes, edges = frames(
[node("A", year=2015), node("B", year=1970), node("C", year=2016)],
Expand Down
Loading