Skip to contents

R-CMD-check License: MIT

bibnets is an R package for importing, constructing, and exporting bibliometric networks. It reads scholarly export formats from Scopus, Web of Science, OpenAlex, BibTeX, RIS, Lens.org, Dimensions, and Crossref; converts multi-valued fields such as authors, references, keywords, countries, and affiliations into sparse incidence matrices; returns edge lists for co-authorship, co-citation, bibliographic coupling, keyword co-occurrence, direct citation, historiograph, and custom co-occurrence analyses; and exports to igraph, tidygraph, cograph, Gephi CSV, GraphML, and sparse matrix formats.

Beyond construction, bibnets supports similarity and dissimilarity normalizations (association strength, cosine, Jaccard, inclusion, equivalence, and raw counts); counting methods including fractional, paper, and position-aware authorship credit (harmonic, arithmetic, geometric, adaptive geometric, golden-ratio, first-author, last-author, first-last, custom); attention-style position weights (lead, last, proximity, circular) for author, keyword, country, and institution networks; temporal network construction with fixed, sliding, and cumulative time windows; disparity-filter backbone extraction for multiscale weighted networks; Garfield-style historiograph construction over the most locally cited documents; local citation scoring; and threshold and top-n pruning utilities.

Main Features

  • Dedicated network builders plus a generic builder: author_network(), document_network(), reference_network(), keyword_network(), source_network(), institution_network(), country_network(), historiograph(), and conetwork().
  • Counting methods for full, fractional, paper-level, strength, and position-aware authorship weighting, including harmonic, arithmetic, geometric, adaptive geometric, golden-ratio, first-author, last-author, first-last, and custom position-weighted schemes.
  • Attention-style position weights through attention = "lead", "last", "proximity", or "circular" for author, keyword, country, and institution networks.
  • Similarity measures: none, association strength, cosine, Jaccard, inclusion, and equivalence.
  • Readers for scholarly exports: Scopus, Web of Science, OpenAlex nested data, OpenAlex flat CSV, BibTeX, RIS, Lens.org, Dimensions, Crossref, and generic CSV files.
  • Custom columns and separators: every builder accepts an entity-named column argument (authors, keywords, references, journal, countries, affiliations) plus sep, references_sep, and strip_quotes, so a plain data frame with its own column names and delimiter works in one call — no reader and no pre-splitting required.
  • Network reduction and export: backbone(), prune(), filter_top(), to_gephi(), to_graphml(), to_igraph(), to_tbl_graph(), to_matrix(), and to_cograph().
  • Temporal and historical analysis: temporal_network(), local_citations(), and historiograph().
  • Author-name parsing: parse_names() reorders and splits author names (Last, First, SURNAME Initials, or First Last) into first / last / particle / suffix components — useful for normalizing labels before building author networks.
  • Standard output: all network builders return a bibnets_network edge list with from, to, weight, and count columns.

Install

From CRAN:

install.packages("bibnets")

Development version from GitHub:

# install.packages("remotes")
remotes::install_github("mohsaqr/bibnets")

Quick Start

library(bibnets)

# Build straight from your own data frame — name the column and delimiter:
authors <- author_network(my_papers, authors = "Author Names", sep = ",")

# Or read a scholarly export (format auto-detected), then build:
data    <- read_biblio("scopus.csv")
authors <- author_network(data, type = "collaboration")

# Standard edge list: from, to, weight, count
head(authors)
summary(authors)

The edge list separates two quantities:

  • count: the raw binary co-occurrence count for the pair.
  • weight: the analysis weight after counting and optional similarity normalization.

With similarity = "none" and counting = "full", weight and count are usually the same. Once fractional counting or similarity normalization is used, they intentionally diverge.

Reading Data

Use read_biblio() when you have a file, a vector of files, or a directory:

data <- read_biblio("scopus_export.csv")
data <- read_biblio(c("wos_1.txt", "wos_2.txt"))
data <- read_biblio("exports/")

Use a format-specific reader when the source is already known:

scopus <- read_scopus("scopus.csv")
wos    <- read_wos("savedrecs.txt")
oa     <- read_openalex_csv("openalex_works.csv")
dim    <- read_dimensions("dimensions.csv")
lens   <- read_lens("lens.csv")

For custom CSV files, map each source column onto a standard field by name (authors, keywords, references, countries, affiliations, journal):

data <- read_biblio(
  "my_data.csv",
  id       = "paper_id",
  authors  = "Authors",
  keywords = "Keywords",
  sep      = ";"
)

All readers try to return the same core columns: id, title, year, journal, doi, cited_by_count, abstract, type, authors, references, and keywords. Source-specific extras such as countries, affiliations, index_keywords, and keywords_plus are preserved when available.

Custom Columns and Separators (no reader needed)

When data is already a plain data frame or CSV with its own column names and delimiter, you do not have to coerce it into the standard schema first. Every network builder accepts a column argument named after the entity it builds (authors, keywords, references, journal, countries, affiliations) plus a sep to split a delimited character column. The builder splits, normalizes, and projects in a single call:

papers <- data.frame(
  `Author Names`= c("Smith J, Doe A, Lee K", "Smith J, Lee K",
                    "Doe A, Lee K", "Smith J, Doe A"),
  Tags          = c("ml, ai", "ml, nlp", "ai, nlp", "ml, ai"),
  check.names   = FALSE
)

# Point the builder at the column and give it the delimiter — no renaming.
author_network(papers, authors = "Author Names", sep = ",")
keyword_network(papers, keywords = "Tags", sep = ",")

There is no document-identifier column in that example, and none is needed: the builders use an existing id column when present and otherwise fall back to row numbers (each row is treated as one document). To use a differently-named identifier column, name it with id:

papers$paper_id <- c("A1", "A2", "A3", "A4")
author_network(papers, authors = "Author Names", sep = ",", id = "paper_id")

sep is any literal delimiter, so BibTeX-style " and " or pipe-delimited exports work too:

author_network(bib, authors = "creators", sep = " and ")
country_network(data, countries = "Country List", sep = "|")

The defaults match the standard schema, so calls on data already in that schema need no extra arguments:

Builder Column argument (default)
author_network() authors = "authors"
keyword_network() keywords = "keywords"
reference_network() references = "references"
document_network() references = "references"
source_network() journal = "journal"
country_network() countries = "countries"
institution_network() affiliations = "affiliations"

A few related controls:

  • id — the document-identifier column (the rows of the works × entities matrix). id = NULL (default) uses an existing id column if present and otherwise numbers the rows; id = "paper_id" points at any column. Two entities are linked when they share the same id (the same document).
  • references_sep — coupling builders (author_network, source_network, country_network, institution_network) split the references column with its own delimiter (default ";"), independent of sep. Reference strings often contain internal commas ("Smith J, 2020, Journal"), so the two delimiters are kept separate.
  • strip_quotes (default TRUE) — surrounding quote characters ("Alice", or the CSV doubled form ""Alice"") are removed so a quoted label and its bare form collapse to one node. Internal apostrophes (O'Brien) are left alone. Set strip_quotes = FALSE to keep the quotes.
  • Wrong-delimiter safety net — if sep does not actually split the column and the values still contain a structural delimiter (";", "|", or a tab), the builder warns instead of silently treating each whole cell as one entity. The check is quiet for commas and " and ", which appear inside valid single labels.

The field = argument of keyword_network() is deprecated in favor of keywords =; the old argument still works (with a warning).

See the “Custom columns and separators” section of vignette("reading-data", package = "bibnets") for the full treatment.

Network Builders

Co-authorship

edges <- author_network(data, type = "collaboration")

Two authors are linked when they appear on the same paper. Use counting to change how each paper contributes:

author_network(data, type = "collaboration", counting = "full")
author_network(data, type = "collaboration", counting = "fractional")
author_network(data, type = "collaboration", counting = "harmonic")
author_network(data, type = "collaboration", counting = "first_last")

data can be a reader result or a plain data frame — point the builder straight at your own columns, no reader needed:

author_network(my_df, authors = "Author Names", id = "paper_id", sep = ",")

Use attention instead of counting when the goal is position-based weighting independent of the standard counting families. The same option is available on keyword_network(), country_network(), and institution_network():

author_network(data, attention = "lead")       # first author dominant
author_network(data, attention = "last")       # last author dominant
author_network(data, attention = "proximity")  # middle authors weighted most
author_network(data, attention = "circular")   # first and last upweighted

attention and counting are mutually exclusive: when attention is non-NULL, the network is built directly from positional weights and the type/counting arguments are ignored.

Reference Co-citation

refs <- reference_network(data, type = "co_citation", min_occur = 2)

Two references are linked when they are cited together by the same paper. This is a column-mode projection of the papers x references matrix.

Bibliographic Coupling and Direct Citation

coupling <- document_network(data, type = "coupling", similarity = "cosine")
direct   <- document_network(data, type = "citation")

Coupling links two papers when they cite the same references. Direct citation returns directed within-corpus citation edges from citing paper to cited paper.

Keywords, Sources, Countries, and Institutions

keyword_network(data, keywords = "keywords")
source_network(data, type = "coupling", min_occur = 2)
country_network(data, type = "collaboration", counting = "fractional")
institution_network(data, type = "collaboration", counting = "fractional")

Entity labels are trimmed and uppercased before matrix construction so that minor casing differences do not create separate nodes.

Generic Co-networks

conetwork() is useful when a dedicated wrapper is not needed:

conetwork(data, "keywords")
conetwork(data, "authors", by = "keywords")
conetwork(data, "journal", by = "references", similarity = "cosine")

With one field, entities are linked when they co-occur in the same paper. With by, entities are linked through shared values of another field.

Counting and Normalization

Counting controls how each paper contributes to edge weights. Similarity normalization controls how pair-level totals are rescaled after projection.

Method Main use Interpretation
"full" all networks Each observed co-occurrence contributes 1.
"fractional" all networks Contribution is scaled by list size so large teams or long reference lists do not dominate.
"paper" co-occurrence networks Each paper contributes a fixed total amount spread across pairs.
"strength" coupling networks Uses reference-frequency weighting and row-list-size normalization for coupling-strength weighting.
"harmonic" author collaboration Authorship credit decreases by harmonic rank and sums to 1 per paper.
"arithmetic" author collaboration Authorship credit decreases linearly by position.
"geometric" author collaboration Authorship credit decays geometrically by position.
"adaptive_geometric" author collaboration Geometric decay adapts to the number of authors.
"golden" author collaboration Geometric decay based on the golden ratio.
"first" / "last" author collaboration Only the first or last author receives credit.
"first_last" author collaboration First and last authors are upweighted.
"position_weighted" author collaboration User-supplied position weights.

Similarity options:

keyword_network(data, similarity = "association")
keyword_network(data, similarity = "cosine")
keyword_network(data, similarity = "jaccard")
keyword_network(data, similarity = "inclusion")
keyword_network(data, similarity = "equivalence")

Association strength is often useful for co-occurrence data because it compares the observed co-occurrence against the product of the two marginal frequencies. Cosine normalization is often easier to interpret for coupling because it scales shared references by the geometric mean of the two reference-list lengths.

Network Reduction

edges <- author_network(data, "collaboration")

strong_edges <- prune(edges, threshold = 3)
local_top    <- prune(edges, top_n = 5)
top_nodes    <- filter_top(edges, n = 50)
backbone_net <- backbone(edges, alpha = 0.05)

Use:

  • prune(threshold = x) for an absolute edge-weight cutoff.
  • prune(top_n = k) to retain locally strong edges for each node.
  • filter_top(n = k) to keep only the most connected nodes.
  • backbone(alpha = x) to apply the disparity filter for multiscale weighted networks.

Temporal Networks and Historiographs

temporal_network(data, keyword_network, window = 3)
temporal_network(data, author_network, "collaboration",
                 window = 2, strategy = "sliding")

lcs <- local_citations(data)
h   <- historiograph(data, n = 30)

temporal_network() supports fixed, sliding, and cumulative windows. If a window cannot be built, it warns with the window label instead of silently dismissing the failure.

local_citations() counts within-corpus citations. historiograph() then builds a directed network among the top locally cited documents.

Export

to_matrix(edges)
to_gephi(edges)
to_graphml(edges, file = "network.graphml")

if (requireNamespace("igraph", quietly = TRUE)) {
  g <- to_igraph(edges)
}

Optional converters are guarded by requireNamespace(), so packages such as igraph, tidygraph, and cograph are not required unless their output formats are requested.

Author Name Parsing

The same author can appear under different spellings across sources ("Saqr, Mohammed", "SAQR M", "Mohammed Saqr"). parse_names() reorders and splits names so they can be normalized to a single label before building a network — node identity is fixed at build time, so merging spellings afterward is not possible.

parse_names(c("Saqr, Mohammed", "WANG Y", "Mohammed Saqr"))

# Normalize an authors list-column before author_network()
data$authors <- lapply(data$authors, parse_names, format = "last_initials")

It recognizes three conventions — Last, First (comma), SURNAME Initials (Scopus/bibnets), and First Last — and returns the parsed components in a "parts" attribute. It is a standalone utility: no reader or builder calls it.

Vignettes

Three vignettes ship with the package:

License

MIT