Skip to main content

bibliominer-analysis

Bibliometric analysis of a Scopus corpus, as a library: notebook, script, CI pipeline, no interface required.

It is the computation layer behind Bibliominer, a bibliometrics tool made of two web applications: one cleans a Scopus export, the other analyses it. The analysis application calls this package for every number and figure it shows, so a script gets exactly what the screen shows.

pip install bibliominer-analysis

Quick start

from bibliominer_analysis import Corpus, Report

corpus = Corpus.from_csv("corpus_cleaned.csv")
print(corpus.summary())                            # documents, years, sources, authors…
top = corpus.authors_impact(n=10)                  # a pandas DataFrame
corpus.figure("documents-per-year", path="production.png")   # 300 dpi PNG

report = Report()
report.add_table("Actors", "Top 10 authors", top)
report.add_corpus_figure(corpus, "top-authors", n=10)
report.save("report.zip")                          # figures, Excel tables, README

What it takes as input

A cleaned Scopus export (CSV). Cleaning means recovering missing DOIs, naming empty source titles, resolving affiliations down to the city and reconciling the references, and it is done in the Bibliominer cleaning application, where every correction is reviewed by its user. This package starts where cleaning stops.

A raw Scopus export is refused: Corpus.from_csv raises NotCleanedError and says which line is not in the cleaned format. On a raw export the numbers would be wrong without any warning (one author counted under two spellings, no cities, co-citation on free text), so the package does not compute them. The cleaned file is recognised by its numbered authors (1:…; 2:…; 3:…), which only the cleaning export writes.

What it computes

Production & impact documents per year, growth, citations, h-index, and the usual per-author / per-source / per-country breakdowns
Networks co-citation (references and authors), co-word, bibliographic coupling, collaboration
Network analysis degree, betweenness, closeness, PageRank, eigenvector, clustering, and community detection
Journals SCImago quartiles and categories, the reference table ships inside the package, so nothing is fetched at runtime
Figures every figure of the web interface, bars, lines, scatters, networks, world maps, treemap, word cloud, Sankey, dendrogram, density map, in PNG, SVG, PDF or JPG
Reports figures, tables and indicators gathered in one ZIP: a folder per section, PNG at 300 dpi + SVG, Excel tables

Everything the interface shows, in Python

"The interface" below is the Bibliominer analysis web application. It calls this package for every number it displays, so a notebook gets the same tables, the same figures and the same options. You do not need the application to use the package.

Filters. The filter bar of the interface is Corpus.filter, which returns a new corpus, every method then works on it:

corpus = Corpus.from_csv("corpus_cleaned.csv")        # say 2020-2025
recent = corpus.filter(years=(2023, 2025))              # only 2023-2025
articles = recent.filter(doc_types=["Article"], countries=["Morocco"])

Top N. Every ranking takes n (or top_n for networks): the default is what the interface shows, any value works.

recent.authors_impact(n=10)          # top 10 authors by h-index
recent.production_by_country(n=5)    # top 5 countries

Figures. Each figure of the interface has a name, the one its export file carries in the interface, and the interface's settings as defaults:

Corpus.figure_catalog()                                  # every figure, by page
recent.figure("top-authors", n=10, path="top_authors.svg")        # vector
recent.figure("collaboration-map", path="collaboration.png")      # 300 dpi
recent.figure("network-keywords", top_n=80, normalization="association",
              path="keywords.pdf")
spec = recent.figure_spec("documents-per-year")          # editable before drawing

The format follows the file extension (png, svg, pdf, jpg), or pass fmt=. PNG is rendered at 300 dpi by default (dpi= to change it); SVG is vector and stays sharp at any size. Network maps keep every disc apart and lay the separate groups out in rows, so a map with twenty teams stays readable.

style dresses a bar, line or scatter figure as the interface's export dialog does: the form (bar, lollipop, line, area, pie, donut), a title and subtitle written on the figure, the colours (gradient by value, category for one colour per bar, series), and the values on the bars:

recent.figure("top-authors", n=10, path="top_authors.png",
              style={"show_title": True, "subtitle": "Years 2023-2025",
                     "palette": "gradient", "value_labels": True})
recent.figure("document-types", path="types.svg", style={"kind": "donut"})

Reports. The interface's Add to report basket is Report:

from bibliominer_analysis import Report

report = Report(filters={"years": "2023-2025"})
report.add_corpus_figure(recent, "top-authors", n=10,       # filed under Actors
                         period="2023-2025")                 # top-authors_2023-2025.png
report.add_table("Actors", "Top 10 authors", recent.authors_impact(10))
report.add_indicators("Impact", "Collaboration", recent.collaboration_indicators())
report.save("report.zip")
report.zip
  README.txt                   each file, its title, and the filters it was computed under
  all_tables.xlsx              every table, one sheet each
  1-corpus/figures/*.png|svg   2-actors/…   3-impact/…   4-concepts/…   5-networks/…
  1-corpus/corpus_tables.xlsx  the section's tables, one sheet each, after a Contents sheet

An entry computed over a period carries it in its file or sheet name (period=), so the same figure over two periods gives two files. Excel files are written by the package itself: no extra dependency.

Interface page → Python call

Page Tables Figures (corpus.figure(name))
Corpus · Overview summary(), main_information(), most_cited_documents(10), document_types() document-types
Production production_by_year(), production_by_country(15) documents-per-year, citations-per-year, production-per-country
Documents document_list(200, sort), most_normalized_documents(20) n/a
Corpus profile price_index(window), price_index_by_year(), reference_age_distribution(), concentration(unit), concentration_summary(), self_citation_summary(), authors_self_citation(15), interdisciplinarity(), subject_areas(), access_status(), access_routes(), access_over_time() price-index, reference-ages, lorenz, self-citation-authors, subject-areas, open-access-over-time
Quality quality_summary(), indicator_readiness(), field_completeness(), table_completeness(), anomalies(), duplicates() n/a
Actors · Authors impact(), authors_impact(20), authorship_groups(), authors_over_time(10) top-authors, authorship-groups, authors-over-time
Institutions institutions_impact(20, level), institutions_over_time(8, level), institutions_by_country(20, level), org_hierarchy(40), affiliation_profile(), documents_by_affiliation_count(), authors_by_affiliation_count(20) top-institutions, top-units, institutions-over-time, units-over-time, documents-by-affiliation-count, collaboration-between-institutions, collaboration-between-units
Sources scimago_coverage(), quartile_distribution(), quartile_over_time(), scimago_sources(), sources_impact(15), sources_over_time(6), most_local_cited_sources(15) scimago-quartiles, quartiles-over-time, source-impact, sources-over-time, local-cited-sources
Cities top_cities(15), collaboration_scale(), cities_impact(20), cities_over_time(6), city_hierarchy(60) cities, collaboration-scale, cities-over-time, city-network
Countries country_map(), corresponding_author_countries(20), countries_impact(20) country-map, collaboration-map, scp-mcp
Impact · Local impact most_local_cited_documents(20), most_local_cited_authors(15), historiograph(25) local-cited-documents, local-cited-authors, historiograph
Spectroscopy reference_spectroscopy() rpys-counts, rpys-deviation
Indicators growth_summary(), rgr_doubling_time(), trend_forecast(5, model), collaboration_indicators(), authorship_pattern(), cai(5), price_law(), cochran_sample_size() rgr, doubling-time, trend-and-projection, authorship-pattern, cai-by-period
Laws lotka(), bradford(), zipf(60) lotka, bradford, bradford-zones, bradford-core-journals, zipf
Concepts · Themes top_keywords(120, kind), word_dynamics(8, kind) word-cloud, keyword-treemap, word-dynamics, co-word-network
Text mining top_terms(field, ngram, n), text_trend(field, ngram), term_network(...) text-terms, text-trend, text-network
Thematic map thematic_map(), trend_topics(25) thematic-map, topics-over-time
Thematic evolution thematic_evolution(n_periods=3) thematic-evolution
Conceptual structure conceptual_structure(method, kind), topic_dendrogram(kind) conceptual-map, dendrogram
Three fields three_fields(left, middle, right, n) three-fields
Networks · Networks network(unit, top_n, min_weight) collaboration-authors, collaboration-institutions, collaboration-countries, co-citation-references, co-citation-coupling, co-citation-cited-authors
Network lab network(unit, top_n, min_weight, normalization, overlay, resolution=…), density_map(unit) network-<unit>, density-<unit>
Citation network citation_graph(unit), citation_balance(unit) citation-network-<unit>
Coupling clusters clustering_by_coupling(100, 3, impact) coupling-map

Drawing a network

Networks are built, annotated and drawn without a browser:

from bibliominer_analysis import Corpus
from bibliominer_analysis.networks.build import co_word
from bibliominer_analysis.networks.analysis import annotate
from bibliominer_analysis.figures.network import render_network

corpus = Corpus.from_csv("corpus_cleaned.csv")

graph = co_word(corpus, top_n=60, min_weight=2)   # the thematic structure
graph = annotate(graph, communities=True)          # clusters + centralities

png = render_network(
    graph,
    title="Co-word network",
    size_by="pagerank",   # or "weight", "betweenness"…
    label_top=15,         # label only the biggest nodes
)
open("co_word.png", "wb").write(png)

render_network returns the image bytes, in png, jpg, svg or pdf. Nothing is written to disk unless you write it.

The map does not move

Two runs on the same data produce the same file, byte for byte. Node positions come from a deterministic MDS on shortest paths, refined with a fixed seed, not from a free force simulation. A force layout is stochastic, and a figure published in an article cannot change between runs. The timestamp matplotlib normally writes into the file is stripped for the same reason.

That also means you can commit a figure and let a diff tell you whether the data changed.

Drawing exactly what the screen showed

Pass the coordinates you already have, and the exported figure is the same map the user was looking at:

from bibliominer_analysis.networks.analysis import layout

coords = layout(graph)                       # compute once…
svg = render_network(graph, coords=coords, fmt="svg")   # …draw many times

Reading the numbers

from bibliominer_analysis.metrics.production import by_year

corpus = Corpus.from_csv("corpus_cleaned.csv")
print(by_year(corpus))

Every metric takes the same Corpus and returns plain Python / pandas objects, nothing to unwrap, nothing to serialise.

Requirements

Python ≥ 3.9. Depends on pandas, numpy, networkx and matplotlib, all pure Python or shipped as wheels, so there is nothing to compile.

Data shipped with the package

The SCImago journal list (data_ref/scimagojr_2025.csv) is third-party data, not covered by the MIT licence: SCImago allows its use for non-commercial purposes as long as it is cited, SCImago (n.d.). SJR - SCImago Journal & Country Rank [Portal]. Retrieved from https://www.scimagojr.com. The terms travel with the file, in data_ref/scimagojr_2025.NOTICE.

License

MIT, the LICENSE file ships with the package. The world map (data_ref/countries-110m.json) keeps its own ISC licence, next to it.

Metadata

Release files for bibliominer-analysis 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for bibliominer-analysis 0.1.0
File Size Uploaded
bibliominer_analysis-0.1.0.tar.gz 3.1 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for bibliominer-analysis 0.1.0
File Interpreter ABI Platform
bibliominer_analysis-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 6.1 MB

Release files / bibliominer_analysis-0.1.0.tar.gz

Download URL bibliominer_analysis-0.1.0.tar.gz
Size 3.1 MB
Tags Source
SHA-256 checksum
How to use checksums
eb19b38f8c4b2cbe0ca51c482abd7e5c290a0260da2d45c490a9e9a79402340b
BLAKE2b-256 checksum
How to use checksums
63bee7941d02718c72b2f6327ae8dd74629568899861d66acd83de5359565d55
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.9.13

Release files / bibliominer_analysis-0.1.0-py3-none-any.whl

Download URL bibliominer_analysis-0.1.0-py3-none-any.whl
Size 3.0 MB
Tags Python 3
SHA-256 checksum
How to use checksums
44e95599346816c91d69f5fc7fb03921cedd8e38cfff8705f895bf6c09b5c6ef
BLAKE2b-256 checksum
How to use checksums
f15894ea6b99d6f0ad522eb84eb73a4b89bdffab123b4aab191e4d5a2e27c442
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.9.13

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page