A tool for picking where to send a neuroscience/psychology manuscript, ranked by the things that actually drive the decision: impact, time from submission to publication, acceptance rate, and fit.
Live: https://torwager.github.io/neurojournalrecommender/
(served from docs/ on main via GitHub Pages)
Two sibling pages, cross-linked by the nav at the top of each. The broad
cross-publisher page is the default (index.html).
docs/index.html — "Neuroscience & psychology journals" (default) — 638
journals across all publishers, from OpenAlex: everything with substantial output
in neuroscience/psychology/psychiatry/cognitive science, plus every venue CANlab
has published in and the named general-medical journals (JAMA, NEJM). Comparable
impact metric, abstract-driven content match, and a Tor-Wager-frequency filter.
docs/nature.html — "Nature journals" — 247 Nature Portfolio journals from
the manuscript-transfer PDF. Has acceptance rate and turnaround times (publisher-
reported) that OpenAlex lacks, a hand-curated CANlab relevance filter, and the
same abstract-driven content match.
| Dimension | Nature page | Broad page |
|---|---|---|
| Impact | publisher JIF | OpenAlex 2-yr citedness (comparable) |
| Time to publication | ✅ | — (OpenAlex has none) |
| Acceptance rate | ✅ (bands) | — (OpenAlex has none) |
| Fit / content match | ✅ | ✅ abstract → TF-IDF topic match |
| CANlab (Wager) publication count | ✅ | ✅ column + filter |
Not the Clarivate Journal Impact Factor — it's proprietary and not freely available in bulk, and publisher-reported JIFs are a grab-bag across different years and definitions. The best uniform, comparable alternative is OpenAlex's 2-year mean citedness: citations in a year to a journal's previous-two-years articles, divided by those articles, computed identically for every one of ~250k journals from one open citation graph. It's not the JIF (runs a little lower) but it is comparable across journals and publishers, which is what matters here. The broad page labels the axis honestly as "Impact (OpenAlex 2-yr citedness)".
Other freely-queryable options considered: Scimago SJR / CiteScore (Scopus-based, downloadable but a different model and licence), and h-index (OpenAlex ships it — it's on the broad page as a secondary column, but it rewards age/size and isn't a rate).
data/raw/ source PDF + its text extraction
data/processed/ journals_nature.* — 246 journals, straight from the PDF
nature_neuroscience.json — scraped NN metrics
nature_topics.json — OpenAlex topic profiles for Nature journals
journals_annotated.* — Nature set + topics/CANlab/NN row
wager_pubs.json — Tor Wager per-journal publication counts
journals_openalex.json — ~640 journals fetched from OpenAlex
journals_broad.* — + relevance, area, Wager counts
scripts/ parsers, fetchers, annotators, templates, build scripts
docs/ index.html (broad, default), nature.html — Pages serves these
Rebuild everything (the fetch_*/enrich_* steps hit the network; rest are offline):
python3 scripts/fetch_wager.py # Wager per-journal pub counts (shared)
# Broad page (the default, index.html)
python3 scripts/fetch_openalex.py # ~640 journals incl. Wager venues (network)
python3 scripts/annotate_openalex.py
python3 scripts/build_broad.py # -> docs/index.html
# Nature page (nature.html)
python3 scripts/fetch_nature_neuro.py # scrape Nature Neuroscience metrics (network)
python3 scripts/parse_nature_transfer.py data/raw/nature_transfer_list.txt data/processed/journals_nature
python3 scripts/annotate_journals.py # run once to create names…
python3 scripts/enrich_nature_topics.py # …then match to OpenAlex for topics (network)
python3 scripts/annotate_journals.py # …and again to fold topic profiles in
python3 scripts/build_site.py # -> docs/nature.htmlThe build scripts inline the data into each page, so both HTML files are fully self-contained — no fetch, no CDN, and the content match runs client-side. They work from the filesystem and from GitHub Pages alike.
Source: a Nature Portfolio manuscript-transfer listing (generated from Nature Neuroscience, subject terms Perception and Psychology), captured 2026-07-23.
Extraction was cross-checked three ways: the parser finds exactly 246 journals, the page itself says "Displaying 246 journals", and all 246 names are unique.
Per journal: name, impact factor, acceptance-rate band, median days to first
decision, median days to acceptance, access model, APC, plus derived flags
(transformative, review_journal, canlab_relevant, canlab_topic).
Coverage is uneven because the publisher leaves fields blank:
| Field | Missing |
|---|---|
| Impact factor | 76 / 246 |
| Acceptance rate | 83 / 246 |
| Days to first decision | 56 / 246 |
| Days to acceptance | 55 / 246 |
It is the journal the transfer list was generated from, so it cannot appear in
its own list — every other Nature research title is present. It's added back with
real metrics scraped by fetch_nature_neuro.py from nature.com/neuro/journal-impact
(JIF 20.3, 9 days to first decision, 399 to acceptance; the page publishes no
acceptance rate).
Each dimension is converted to a percentile rank among the journals that report it, then combined as a weighted average. Percentiles rather than raw values because impact factor is heavily right-skewed and days-to-acceptance is not, so averaging the raw numbers would let impact factor dominate arbitrarily.
Journals missing a metric score a neutral 0.5 on that dimension instead of sinking to the bottom, and the table marks them as unreported. Acceptance rate is published as a band (e.g. 25–29%); the score uses the band midpoint. Weights are user-set and default to 40 / 30 / 30 across impact, speed, and odds.
Both pages share the same controls:
- Weight sliders for each dimension; the score is their weighted average.
- Axis dropdowns — plot any metric against any other, including content match and weighted score. Log scaling and ticks follow the metric.
- Colour dropdown — viridis, defaulting to weighted score. The bright end always marks the desirable direction, so it inverts for days and APC (low = bright). Grey means unreported; point size always tracks the weighted score.
- Review-journal filter — all / exclude / review-only (default all).
- Content match — the abstract box; see below.
The Nature page adds a hand-curated CANlab topic filter and its publisher-only speed/acceptance columns. The broad page adds the on-topic filter, an area filter, and the Wager (CANlab) publication-count column and filter.
Paste an abstract; each journal gets a 0–100 score from TF-IDF cosine similarity between your text and the journal's OpenAlex topic profile (its most-published topics, weighted). Distinctive words (schizophrenia, nociception, fMRI) count for more than common ones via inverse document frequency. It runs entirely in the browser — nothing is uploaded. Entering an abstract auto-sets the content-match weight high and puts content match on the Y axis.
Content match is scored on a linear 0–max scale, not percentile-ranked like the other dimensions: for a given abstract most journals have ~zero overlap, so percentile-ranking would compress a genuine top match down to the level of a marginal one and the weighted score wouldn't move. Linear scaling lets a strong match actually drive the ranking. It's a lexical topic match, not a quality or fit judgement.
- The two pages use different impact metrics (publisher JIF vs OpenAlex citedness) and can't be pooled on one axis. They're deliberately separate.
- OpenAlex lacks acceptance rates and turnaround times, so the broad page can't offer the speed/odds dimensions the Nature page has.
- Content match is lexical, built from topic labels, not a learned embedding — good at topic overlap, blind to methodological nuance.
- Nature-page topic profiles are mostly name-derived. OpenAlex switched to a free
daily budget (~$0.10/day) which this build exhausted, so 231 of 247 Nature journals
fall back to a profile synthesised from the journal name + CANlab area rather than
real OpenAlex topics (16 were cached).
enrich_nature_topics.pyupgrades them to real profiles automatically on a rerun after the quota resets (midnight UTC). The broad page's profiles are all real OpenAlex. - The broad set is top-by-output, so a few generalist medical venues surface; the on-topic filter (on by default) handles most of that.
- The Nature set is one publisher (Nature Portfolio / BMC / Springer) and its metrics are publisher-self-reported.
Extract and clean a first journal dataset; stand up a working prototype.✅Axis/colour controls, CANlab relevance filter, extraction audit.✅Broaden coverage beyond Nature Portfolio (OpenAlex, 576 journals, comparable impact metric).✅Add fit — abstract-driven content match (both pages).✅Publish — repo + GitHub Pages on✅docs/.Broad cross-publisher page as default; CANlab venues + Wager publication filter.✅- Refinements: reconcile the two datasets (match on ISSN so a journal shows both JIF and OpenAlex metrics); embedding-based content match; per-journal detail pages; save/share a weighting.