├── requirements.txt ├── TODO.md ├── arxiv.sh ├── LICENSE ├── README.md ├── base.ttl ├── .gitignore ├── arxiv.py └── arxiv.ipynb /requirements.txt: -------------------------------------------------------------------------------- 1 | icecream >= 2.1 2 | matplotlib >= 3.4 3 | numpy >= 1.20 4 | pandas >= 1.4 5 | pytextrank >= 3.1 6 | python-dateutil >= 2.8 7 | spacy >= 3.2 8 | tqdm >= 4.63 9 | typer >= 0.4 10 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | ## PR 2 | * 3 | * "graph database" 4 | 5 | ## OpenAIRE 6 | * 7 | 8 | ## richcontext.scholapi 9 | * 10 | 11 | ## time series, predictive trend lines 12 | * 13 | 14 | ## prophy.science 15 | * 16 | -------------------------------------------------------------------------------- /arxiv.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eux 3 | 4 | LOOKBACK=`date -v-2w +%Y-%m-%d` 5 | 6 | while [[ $# -gt 0 ]]; do 7 | key="$1" 8 | 9 | case $key in 10 | -l|--lookback) 11 | LOOKBACK="$2" 12 | shift # past argument 13 | shift # past value 14 | ;; 15 | esac 16 | done 17 | 18 | # query arXiv 19 | 20 | PHRASES=( "graph algorithms" "graph neural networks" "knowledge graph" ) 21 | TTL_FILE=/opt/derwen/chwedl/trends/arxiv.ttl 22 | 23 | for QUERY in "${PHRASES[@]}" 24 | do 25 | python3 arxiv.py cmd-query --min-date=$LOOKBACK --kg-path $TTL_FILE "$QUERY" 26 | done 27 | 28 | # analyze trends 29 | 30 | CSV_FILE=/opt/derwen/chwedl/trends/arxiv.csv 31 | PNG_FILE=/opt/derwen/chwedl/trends/arxiv.png 32 | 33 | python3 arxiv.py cmd-analyze --kg-path $TTL_FILE --csv-file $CSV_FILE 34 | python3 arxiv.py cmd-visualize --csv-file $CSV_FILE --png-file $PNG_FILE 35 | 36 | # extract phrases 37 | 38 | TODAY=`date +%Y%m%d` 39 | KPA_FILE=/opt/derwen/chwedl/trends/phrases.$TODAY.csv 40 | 41 | python3 arxiv.py cmd-extract --min-date=$LOOKBACK --kg-path $TTL_FILE --kpa-file $KPA_FILE 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021-2022 derwen.ai 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # arxiv-trends 2 | 3 | Analyze trends among articles published on [arXiv](https://arxiv.org/help/api) 4 | 5 | 6 | ## Install 7 | 8 | ```bash 9 | python3 -m venv venv 10 | source venv/bin/activate 11 | 12 | python3 -m pip install -U pip 13 | python3 -m pip install -r requirements.txt 14 | python3 -m spacy download en_core_web_sm 15 | cp base.ttl arxiv.ttl 16 | ``` 17 | 18 | 19 | ## Usage 20 | 21 | ```bash 22 | python3 arxiv.py cmd-query --min-date=2021-01-01 "knowledge graph" 23 | ``` 24 | 25 | ```bash 26 | python3 arxiv.py cmd-analyze 27 | python3 arxiv.py cmd-visualize 28 | ``` 29 | 30 | ```bash 31 | python3 arxiv.py cmd-extract 32 | ``` 33 | 34 | 35 | ## License and Copyright 36 | 37 | Source code for **arxiv-trends** plus its logo, documentation, and 38 | examples have an [MIT license](https://spdx.org/licenses/MIT.html) 39 | which is succinct and simplifies use in commercial applications. 40 | 41 | 42 | ## Kudos 43 | 44 | Kudos to arXiv for use of its open access interoperability; 45 | to Jürgen Müller @ BASF for the original idea; 46 | plus general support from [Derwen, Inc.](https://derwen.ai/); 47 | the [Knowledge Graph Conference](https://www.knowledgegraph.tech/) 48 | and [Connected Data World](https://connected-data.world/). 49 | -------------------------------------------------------------------------------- /base.ttl: -------------------------------------------------------------------------------- 1 | @prefix bibo: . 2 | @prefix dct: . 3 | @prefix derw: . 4 | @prefix foaf: . 5 | @prefix lcsh: . 6 | @prefix madsrdf: . 7 | @prefix skos: . 8 | @prefix wd: . 9 | @prefix xsd: . 10 | 11 | 12 | derw:topic_Graph_Algorithms a derw:Topic ; 13 | skos:broader ; 14 | skos:closeMatch lcsh:sh2002004605, 15 | ; 16 | skos:definition "A family of algorithms that operate on graphs for network analysis, measurement, ranking, partitioning, and other methods that leverage graph theory."@en ; 17 | skos:prefLabel "graph algorithms"@en . 18 | 19 | derw:topic_Graph_Database a derw:Topic ; 20 | skos:broader ; 21 | skos:prefLabel "graph database"@en . 22 | 23 | derw:topic_Graph_Embedding a derw:Topic ; 24 | skos:broader ; 25 | skos:prefLabel "graph embedding"@en . 26 | 27 | derw:topic_Graph_Neural_Networks a derw:Topic ; 28 | skos:broader ; 29 | skos:prefLabel "graph neural networks"@en . 30 | 31 | derw:topic_Knowledge_Graph a derw:Topic ; 32 | skos:altLabel "KG"@en ; 33 | skos:broader ; 34 | skos:closeMatch wd:Q33002955, 35 | ; 36 | skos:definition "A knowledge base that uses a graph-structured data model, representing and annotating interlinked descriptions of entities, with an overlay of semantic metadata."@en ; 37 | skos:prefLabel "knowledge graph"@en . 38 | 39 | derw:Topic a skos:Concept , 40 | madsrdf:Topic , 41 | madsrdf:Authority ; 42 | skos:prefLabel "Topic"@en ; 43 | dct:identifier wd:Q1969448 ; 44 | skos:definition "Subject heading used for classifying content and navigating discovery within it."@en . 45 | 46 | derw:Author a skos:Concept, 47 | foaf:Agent ; 48 | dct:identifier wd:Q482980 ; 49 | skos:definition "An author of a publication."@en ; 50 | skos:prefLabel "Author"@en ; 51 | skos:topConceptOf derw:Derwen_Vocabulary . 52 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | arxiv.csv 2 | arxiv.png 3 | arxiv.ttl 4 | phrases.csv 5 | *~ 6 | 7 | # Byte-compiled / optimized / DLL files 8 | __pycache__/ 9 | *.py[cod] 10 | *$py.class 11 | 12 | # C extensions 13 | *.so 14 | 15 | # Distribution / packaging 16 | .Python 17 | build/ 18 | develop-eggs/ 19 | dist/ 20 | downloads/ 21 | eggs/ 22 | .eggs/ 23 | lib/ 24 | lib64/ 25 | parts/ 26 | sdist/ 27 | var/ 28 | wheels/ 29 | pip-wheel-metadata/ 30 | share/python-wheels/ 31 | *.egg-info/ 32 | .installed.cfg 33 | *.egg 34 | MANIFEST 35 | 36 | # PyInstaller 37 | # Usually these files are written by a python script from a template 38 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 39 | *.manifest 40 | *.spec 41 | 42 | # Installer logs 43 | pip-log.txt 44 | pip-delete-this-directory.txt 45 | 46 | # Unit test / coverage reports 47 | htmlcov/ 48 | .tox/ 49 | .nox/ 50 | .coverage 51 | .coverage.* 52 | .cache 53 | nosetests.xml 54 | coverage.xml 55 | *.cover 56 | *.py,cover 57 | .hypothesis/ 58 | .pytest_cache/ 59 | 60 | # Translations 61 | *.mo 62 | *.pot 63 | 64 | # Django stuff: 65 | *.log 66 | local_settings.py 67 | db.sqlite3 68 | db.sqlite3-journal 69 | 70 | # Flask stuff: 71 | instance/ 72 | .webassets-cache 73 | 74 | # Scrapy stuff: 75 | .scrapy 76 | 77 | # Sphinx documentation 78 | docs/_build/ 79 | 80 | # PyBuilder 81 | target/ 82 | 83 | # Jupyter Notebook 84 | .ipynb_checkpoints 85 | 86 | # IPython 87 | profile_default/ 88 | ipython_config.py 89 | 90 | # pyenv 91 | .python-version 92 | 93 | # pipenv 94 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 95 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 96 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 97 | # install all needed dependencies. 98 | #Pipfile.lock 99 | 100 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 101 | __pypackages__/ 102 | 103 | # Celery stuff 104 | celerybeat-schedule 105 | celerybeat.pid 106 | 107 | # SageMath parsed files 108 | *.sage.py 109 | 110 | # Environments 111 | .env 112 | .venv 113 | env/ 114 | venv/ 115 | ENV/ 116 | env.bak/ 117 | venv.bak/ 118 | 119 | # Spyder project settings 120 | .spyderproject 121 | .spyproject 122 | 123 | # Rope project settings 124 | .ropeproject 125 | 126 | # mkdocs documentation 127 | /site 128 | 129 | # mypy 130 | .mypy_cache/ 131 | .dmypy.json 132 | dmypy.json 133 | 134 | # Pyre type checker 135 | .pyre/ 136 | -------------------------------------------------------------------------------- /arxiv.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # see license https://github.com/DerwenAI/arxiv-trends#license-and-copyright 4 | 5 | """ 6 | arxiv-trends: 7 | 8 | Analyze trends in articles published on *arXiv* using NLP, knowledge graph, and time-series. 9 | """ 10 | 11 | from collections import defaultdict 12 | import itertools 13 | import pathlib 14 | import re 15 | import sys 16 | import typing 17 | import unicodedata 18 | import urllib 19 | import urllib.parse 20 | import urllib.request 21 | import xml.etree.ElementTree as et 22 | 23 | from icecream import ic # type: ignore # pylint: disable=E0401 24 | from tqdm import tqdm 25 | import dateutil.tz 26 | import numpy as np 27 | import matplotlib.pyplot as plt # type: ignore # pylint: disable=E0401 28 | import pandas as pd # type: ignore # pylint: disable=E0401 29 | import rdflib # type: ignore # pylint: disable=E0401 30 | import spacy 31 | import typer 32 | 33 | import pytextrank # type: ignore # pylint: disable=E0401 34 | import kglab # type: ignore # pylint: disable=E0401 35 | 36 | 37 | APP = typer.Typer() 38 | 39 | 40 | ###################################################################### 41 | ## utility functions 42 | 43 | def strip_accents ( 44 | text: str, 45 | ) -> str: 46 | """ 47 | Strip accents from the input string. 48 | 49 | See 50 | 51 | text: 52 | the input string 53 | 54 | returns: 55 | the processed string 56 | """ 57 | text = unicodedata.normalize("NFD", text) 58 | text = text.encode("ascii", "ignore") # type: ignore 59 | text = text.decode("utf-8") # type: ignore 60 | 61 | return str(text) 62 | 63 | 64 | def text_to_id ( 65 | text: str, 66 | ) -> str: 67 | """ 68 | Convert input text to an identifier, suitable for a URI 69 | 70 | text: 71 | raw text for the label of a node in the graph 72 | 73 | returns: 74 | a string usable as a unique symbol in RDF 75 | """ 76 | text = strip_accents(text.lower()) 77 | text = re.sub("[ ]+", "_", text) 78 | text = re.sub("[^0-9a-zA-Z_-]", "", text) 79 | 80 | return text 81 | 82 | 83 | ###################################################################### 84 | ## class definitions 85 | 86 | class Trends: 87 | """ 88 | Analyze trends among papers published on arXiv. 89 | """ 90 | NS = { 91 | "atom": "http://www.w3.org/2005/Atom", 92 | "bibo": "http://purl.org/ontology/bibo/", 93 | "cito": "http://purl.org/spar/cito/", 94 | "dct": "http://purl.org/dc/terms/", 95 | "derw": "https://derwen.ai/ns/v1#", 96 | "foaf": "http://xmlns.com/foaf/0.1/", 97 | "lcsh": "http://id.loc.gov/authorities/subjects/", 98 | "madsrdf": "http://www.loc.gov/mads/rdf/v1#", 99 | "opensearch": "http://a9.com/-/spec/opensearch/1.1/", 100 | "owl": "http://www.w3.org/2002/07/owl#", 101 | "prov": "http://www.w3.org/ns/prov#", 102 | "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#", 103 | "rdfs": "http://www.w3.org/2000/01/rdf-schema#", 104 | "schema": "http://schema.org/", 105 | "sh": "http://www.w3.org/ns/shacl#", 106 | "skos": "http://www.w3.org/2004/02/skos/core#", 107 | "wd": "http://www.wikidata.org/entity/", 108 | "xsd": "http://www.w3.org/2001/XMLSchema#", 109 | } 110 | 111 | API_BASE = "http://export.arxiv.org/api/query?" 112 | 113 | 114 | def __init__ ( 115 | self, 116 | *, 117 | kg_path: str = "arxiv.ttl", 118 | ): 119 | """ 120 | Constructor. 121 | 122 | kg_path: 123 | optional - path to the TTL file for kglab to load/save 124 | """ 125 | self.kg = kglab.KnowledgeGraph(namespaces=self.NS) 126 | self.kg_path = pathlib.Path(kg_path) 127 | self.topics: typing.Dict[str, rdflib.Node] = {} 128 | self.load_kg() 129 | 130 | 131 | def load_kg ( 132 | self, 133 | ) -> None: 134 | """ 135 | Load the previous definitions from a serialized KG and initialize the 136 | topics lookup. 137 | """ 138 | self.topics = {} 139 | self.kg.load_rdf(self.kg_path) 140 | 141 | sparql = """ 142 | SELECT ?entry ?label 143 | WHERE { 144 | ?entry a derw:Topic . 145 | ?entry skos:prefLabel ?label 146 | } 147 | """ 148 | 149 | for node, topic in self.kg.query(sparql): 150 | self.topics[topic.toPython()] = node 151 | 152 | 153 | def save_kg ( 154 | self, 155 | ) -> None: 156 | """ 157 | Serialize the updated KG to a file. 158 | """ 159 | self.kg.save_rdf(self.kg_path) 160 | 161 | 162 | def lookup_author ( 163 | self, 164 | name: str, 165 | ) -> rdflib.URIRef: 166 | """ 167 | Lookup an author by name, creating a node in the KG if it doesn't 168 | already exist. 169 | 170 | name: 171 | raw text for the name of the author 172 | 173 | returns: 174 | author node 175 | """ 176 | uri = self.kg.get_ns("derw") + "author_" + text_to_id(name) 177 | node = rdflib.URIRef(uri) 178 | p = self.kg.get_ns("rdf").type 179 | o = self.kg.get_ns("derw").Author 180 | 181 | if (node, p, o) not in self.kg.rdf_graph(): 182 | self.kg.add(node, p, o) 183 | self.kg.add(node, self.kg.get_ns("foaf").name, rdflib.Literal(name, lang=self.kg.language)) 184 | 185 | return node 186 | 187 | 188 | def parse_entry ( 189 | self, 190 | entry: et.Element, 191 | ) -> typing.Tuple[rdflib.URIRef, str]: 192 | """ 193 | Parse the XML from one entry in an Atom feed, and add it to the KG. 194 | 195 | entry: 196 | XML object for one Atom feed entry 197 | 198 | returns: 199 | node and date of the parsed results 200 | """ 201 | href = entry.find("atom:link[@title='pdf']", self.NS).attrib["href"] # type: ignore 202 | date = entry.find("atom:published", self.NS).text[:10] # type: ignore 203 | title = entry.find("atom:title", self.NS).text # type: ignore 204 | abstract = entry.find("atom:summary", self.NS).text.replace("\n", " ").strip() # type: ignore 205 | 206 | # lookup the specified article in the KG, and create a node if 207 | # it doesn't already exist 208 | node = rdflib.URIRef(href) 209 | p = self.kg.get_ns("rdf").type 210 | o = self.kg.get_ns("bibo").Article 211 | 212 | if (node, p, o) not in self.kg.rdf_graph(): 213 | self.kg.add(node, p, o) 214 | self.kg.add(node, self.kg.get_ns("dct").Date, self.kg.encode_date(date, [dateutil.tz.gettz("UTC")])) 215 | self.kg.add(node, self.kg.get_ns("dct").title, rdflib.Literal(title, lang=self.kg.language, normalize=False)) 216 | self.kg.add(node, self.kg.get_ns("dct").abstract, rdflib.Literal(abstract, lang=self.kg.language)) 217 | 218 | # add author list 219 | for author in entry.findall("atom:author/atom:name", self.NS): 220 | self.kg.add(node, self.kg.get_ns("bibo").authorList, self.lookup_author(author.text)) # type: ignore 221 | 222 | return node, date 223 | 224 | 225 | @classmethod 226 | def format_query ( 227 | cls, 228 | query: str, 229 | start: int, 230 | max_results: int, 231 | ) -> str: 232 | """ 233 | Format a URL to search arXiv via its API, based on the given search 234 | criteria. 235 | 236 | query: 237 | query string 238 | 239 | start: 240 | start index within the results 241 | 242 | max_results: 243 | maximum results to return per API call 244 | 245 | returns: 246 | query URL 247 | """ 248 | params: dict = { 249 | "search_query": "all:" + query, 250 | "start": start, 251 | "max_results": max_results, 252 | "sortBy": "submittedDate", 253 | "sortOrder": "descending", 254 | } 255 | 256 | return urllib.parse.urlencode(params, safe=":") 257 | 258 | 259 | def arxiv_api ( 260 | self, 261 | query: str, 262 | min_date: str, 263 | *, 264 | max_items: int = 1, 265 | page_items: int = 1, 266 | ) -> typing.Iterable: 267 | """ 268 | Access the arXiv API based on the given search criteria, parse the XML 269 | results (Atom feed), then update the KG to represent any new entries. 270 | 271 | query: 272 | query string 273 | 274 | min_date: 275 | minimum date to include in the results 276 | 277 | max_items: 278 | optional - maximum items requested per API call 279 | 280 | page_items: 281 | optional - maximum items requested per page 282 | 283 | yields: 284 | `(date, href)` tuple for each search hit within the criteria 285 | """ 286 | start_index = 0 287 | max_index = max_items 288 | 289 | while (start_index < max_index): 290 | # prepare the API query 291 | url = self.API_BASE + self.format_query(query, start_index, page_items) 292 | handle = urllib.request.urlopen(url) 293 | 294 | xml = handle.read().decode("utf-8") 295 | #print(xml) 296 | 297 | # track the API results paging 298 | root = et.fromstring(xml) 299 | total_results = int(root.findall("opensearch:totalResults", self.NS)[0].text) # type: ignore 300 | start_index = int(root.findall("opensearch:startIndex", self.NS)[0].text) # type: ignore 301 | page_items = int(root.findall("opensearch:itemsPerPage", self.NS)[0].text) # type: ignore 302 | 303 | print("---") 304 | ic(total_results) 305 | ic(start_index) 306 | ic(page_items) 307 | 308 | # parse each entry 309 | for entry in tqdm(root.findall("atom:entry", self.NS), desc="Atom entry"): 310 | node, date = self.parse_entry(entry) 311 | yield date, node 312 | 313 | if date < min_date: 314 | return 315 | 316 | # iterate to the next page of results 317 | max_index = min(max_items, total_results) 318 | start_index += page_items 319 | 320 | return 321 | 322 | 323 | ###################################################################### 324 | ## commands 325 | 326 | @APP.command() 327 | def cmd_query ( 328 | query: str, 329 | *, 330 | kg_path: str = "arxiv.ttl", 331 | min_date: str = "2021-06-15", 332 | max_items: int = 5000, 333 | ) -> None: 334 | """ 335 | Query the arXiv API for the given search, then update the KG. 336 | 337 | kg_path: 338 | optional - path to the TTL file for kglab to load/save 339 | 340 | min_date: 341 | optional - minimum date to include in the results 342 | 343 | max_items: 344 | optional - maximum items requested per API call 345 | """ 346 | trends = Trends(kg_path=kg_path) 347 | 348 | # search parameters 349 | page_items = 100 350 | 351 | # get metadata for the matching articles 352 | hit_iter = trends.arxiv_api( 353 | " AND ".join(query.split(" ")), 354 | min_date, 355 | max_items=max_items, 356 | page_items=page_items, 357 | ) 358 | 359 | for date, node in tqdm(hit_iter, desc="Hits"): 360 | trends.kg.add(node, trends.kg.get_ns("derw").fromQuery, trends.topics[query]) 361 | # TODO: what if query new? 362 | #print(query, date, node) 363 | 364 | # persist the metadata 365 | trends.save_kg() 366 | 367 | 368 | @APP.command() 369 | def cmd_extract ( 370 | kg_path: str = "arxiv.ttl", 371 | min_date: str = "2021-06-15", 372 | kpa_file: str = "phrases.csv", 373 | max_phrase: int = 10, 374 | ) -> None: 375 | """ 376 | Extract the entities from each article. 377 | 378 | kg_path: 379 | optional - path to the TTL file for kglab to load/save 380 | 381 | min_date: 382 | optional - minimum date to include in the results 383 | 384 | kpa_file: 385 | optional - path to the CSV file for extracted phrases 386 | 387 | max_phrase: 388 | optional - maximum number of extracted phrases to represent 389 | """ 390 | trends = Trends(kg_path=kg_path) 391 | 392 | # prepare the NLP pipeline 393 | nlp = spacy.load("en_core_web_sm") 394 | nlp.add_pipe("textrank") 395 | 396 | sparql = f""" 397 | SELECT ?article ?title ?abstract 398 | WHERE {{ 399 | ?article a bibo:Article . 400 | ?article dct:title ?title . 401 | ?article dct:abstract ?abstract . 402 | ?article dct:Date ?date . 403 | FILTER (?date > "{min_date}T00:00:00"^^xsd:dateTime) 404 | }} 405 | """ 406 | 407 | # run the pipeline for each article 408 | for node, title, abstract in tqdm(trends.kg.query(sparql), desc="Abstract query"): 409 | text = title.toPython() + ". " + abstract.toPython() 410 | doc = nlp(text) 411 | df_list = [] 412 | 413 | for phrase in itertools.islice(doc._.phrases, max_phrase): 414 | entity_label = " ".join(phrase.text.replace("\n", " ").strip().split()).lower() 415 | entity_id = text_to_id(entity_label) 416 | 417 | df_list.append({ 418 | "url": str(node), 419 | "rank": round(phrase.rank, 3), 420 | "count": phrase.count, 421 | "id": entity_id, 422 | "label": entity_label, 423 | }) 424 | 425 | # serialize extracted phrases to a CSV file 426 | path = pathlib.Path(kpa_file) 427 | df = pd.DataFrame(df_list) 428 | df.to_csv(path, index=False) 429 | 430 | 431 | @APP.command() 432 | def cmd_analyze ( 433 | kg_path: str = "arxiv.ttl", 434 | csv_file: str = "arxiv.csv", 435 | ) -> None: 436 | """ 437 | Analyze the article trends. 438 | 439 | kg_path: 440 | optional - path to the TTL file for kglab to load/save 441 | 442 | csv_file: 443 | optional - path to the CSV file for trend data 444 | """ 445 | trends = Trends(kg_path=kg_path) 446 | 447 | sparql = """ 448 | SELECT ?article ?date ?label 449 | WHERE { 450 | ?article a bibo:Article . 451 | ?article dct:Date ?date . 452 | ?article derw:fromQuery ?topic . 453 | ?topic skos:prefLabel ?label 454 | } 455 | """ 456 | # run the pipeline for each article 457 | df = pd.DataFrame([ 458 | { 459 | "topic": topic.toPython(), 460 | "date": date.toPython(), 461 | "counts": 0, 462 | } 463 | for article, date, topic in tqdm(trends.kg.query(sparql), desc="Article query") 464 | ]).groupby(["topic", "date"]).count() 465 | 466 | # serialize trend data to a CSV file 467 | path = pathlib.Path(csv_file) 468 | df.to_csv(path) 469 | 470 | 471 | @APP.command() 472 | def cmd_visualize ( 473 | csv_file: str = "arxiv.csv", 474 | png_file: str = "arxiv.png", 475 | start_date: str = "2020-01-01", 476 | ) -> None: 477 | """ 478 | Visualize the article trends. 479 | 480 | csv_file: 481 | optional - path to the CSV file fo trend data 482 | 483 | png_file: 484 | optional - path to the PNG file for the rendered diagram 485 | """ 486 | df = pd.read_csv(csv_file, parse_dates=True, index_col="date") 487 | df_list = [] 488 | 489 | for query in tqdm(sorted(set(df["topic"])), desc="Topic"): 490 | df_sub = df[df["topic"] == query] 491 | df_samp = df_sub.resample("M").sum() 492 | df_list.append(df_samp.rename(columns={ "counts": query })) 493 | 494 | df_full = pd.concat(df_list, axis=1, join="inner").reindex(df_samp.index).fillna(0) 495 | 496 | # drop the earliest row as an outlier 497 | df_full = df_full.iloc[1: , :] 498 | 499 | # drop rows before the start date, if any 500 | nix = set([ 501 | i 502 | for i, row in enumerate(df_full.index) 503 | if str(row) < start_date 504 | ]) 505 | 506 | if len(nix) > 0: 507 | cutoff = max(nix) + 1 508 | df_full = df_full.iloc[cutoff: , :] 509 | 510 | # drop the last row – to let arXiv settle 511 | df_full.drop(df_full.tail(1).index, inplace=True) 512 | 513 | # set up the plot 514 | plot = df_full.plot( 515 | subplots=True, 516 | legend=False, 517 | figsize=(11, 7), 518 | xlabel="date submitted" 519 | ) 520 | 521 | plot[0].set(ylabel="monthly counts") 522 | 523 | summary = list(df.groupby("topic").sum().to_dict()["counts"].items()) 524 | y_max = round(max(df_full.max(axis=1)) + 10.0) 525 | 526 | for index, ax in enumerate(plot): 527 | query, count = summary[index] 528 | ax.set(ylim=(0, y_max), title=f"{query}, total = {count}") 529 | 530 | fig = plot[0].get_figure() 531 | fig.tight_layout() 532 | fig.savefig(png_file) 533 | 534 | 535 | if __name__ == "__main__": 536 | APP() 537 | 538 | # simply reminders... 539 | query_list = [ 540 | "knowledge graph", 541 | "graph database", 542 | "graph algorithm", 543 | "graph neural networks", 544 | "graph embedding", 545 | ] 546 | -------------------------------------------------------------------------------- /arxiv.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "id": "b31597b4-5dd0-4b69-b692-cee73fdf843e", 7 | "metadata": {}, 8 | "outputs": [], 9 | "source": [ 10 | "import pandas as pd\n", 11 | "import matplotlib.pyplot as plt\n", 12 | "\n", 13 | "csv_file = \"arxiv.csv\"\n", 14 | "png_file = \"arxiv.png\"\n", 15 | "start_date = \"2020-06-01\"\n", 16 | "\n", 17 | "df = pd.read_csv(csv_file, parse_dates=True, index_col=\"date\")" 18 | ] 19 | }, 20 | { 21 | "cell_type": "code", 22 | "execution_count": 2, 23 | "id": "38dc6d59-7500-4155-96c4-4a5941472a0a", 24 | "metadata": {}, 25 | "outputs": [], 26 | "source": [ 27 | "df_list = []\n", 28 | "\n", 29 | "for query in sorted(set(df[\"topic\"])):\n", 30 | " df_sub = df[df[\"topic\"] == query]\n", 31 | " df_samp = df_sub.resample(\"M\").sum()\n", 32 | " df_list.append(df_samp.rename(columns={ \"counts\": query }))" 33 | ] 34 | }, 35 | { 36 | "cell_type": "code", 37 | "execution_count": 3, 38 | "id": "fe3edc54-b8bf-4ce3-b844-4fe3ae778bfb", 39 | "metadata": {}, 40 | "outputs": [ 41 | { 42 | "data": { 43 | "text/html": [ 44 | "
\n", 45 | "\n", 58 | "\n", 59 | " \n", 60 | " \n", 61 | " \n", 62 | " \n", 63 | " \n", 64 | " \n", 65 | " \n", 66 | " \n", 67 | " \n", 68 | " \n", 69 | " \n", 70 | " \n", 71 | " \n", 72 | " \n", 73 | " \n", 74 | " \n", 75 | " \n", 76 | " \n", 77 | " \n", 78 | " \n", 79 | " \n", 80 | " \n", 81 | " \n", 82 | " \n", 83 | " \n", 84 | " \n", 85 | " \n", 86 | " \n", 87 | " \n", 88 | " \n", 89 | " \n", 90 | " \n", 91 | " \n", 92 | " \n", 93 | " \n", 94 | " \n", 95 | " \n", 96 | " \n", 97 | " \n", 98 | " \n", 99 | " \n", 100 | " \n", 101 | " \n", 102 | " \n", 103 | " \n", 104 | " \n", 105 | " \n", 106 | " \n", 107 | " \n", 108 | " \n", 109 | " \n", 110 | " \n", 111 | " \n", 112 | " \n", 113 | " \n", 114 | " \n", 115 | " \n", 116 | " \n", 117 | " \n", 118 | " \n", 119 | " \n", 120 | " \n", 121 | " \n", 122 | " \n", 123 | " \n", 124 | " \n", 125 | " \n", 126 | " \n", 127 | " \n", 128 | " \n", 129 | " \n", 130 | " \n", 131 | " \n", 132 | " \n", 133 | " \n", 134 | " \n", 135 | " \n", 136 | " \n", 137 | " \n", 138 | " \n", 139 | " \n", 140 | " \n", 141 | " \n", 142 | " \n", 143 | " \n", 144 | " \n", 145 | " \n", 146 | " \n", 147 | " \n", 148 | " \n", 149 | " \n", 150 | " \n", 151 | " \n", 152 | " \n", 153 | " \n", 154 | " \n", 155 | " \n", 156 | " \n", 157 | " \n", 158 | " \n", 159 | " \n", 160 | " \n", 161 | " \n", 162 | " \n", 163 | " \n", 164 | " \n", 165 | " \n", 166 | " \n", 167 | " \n", 168 | " \n", 169 | " \n", 170 | " \n", 171 | " \n", 172 | " \n", 173 | " \n", 174 | " \n", 175 | " \n", 176 | " \n", 177 | " \n", 178 | " \n", 179 | " \n", 180 | " \n", 181 | " \n", 182 | " \n", 183 | "
graph algorithmsgraph neural networksknowledge graph
date
2020-01-311597159
2020-02-2923011271
2020-03-3120212077
2020-04-302349086
2020-05-3121510376
2020-06-30264179108
2020-07-3127814187
2020-08-3122610560
2020-09-30254144108
2020-10-31254181132
2020-11-3027313688
2020-12-31219150107
2021-01-3118910376
2021-02-2825513792
2021-03-3125116493
2021-04-30212164117
2021-05-31264166119
2021-06-30262230137
\n", 184 | "
" 185 | ], 186 | "text/plain": [ 187 | " graph algorithms graph neural networks knowledge graph\n", 188 | "date \n", 189 | "2020-01-31 159 71 59\n", 190 | "2020-02-29 230 112 71\n", 191 | "2020-03-31 202 120 77\n", 192 | "2020-04-30 234 90 86\n", 193 | "2020-05-31 215 103 76\n", 194 | "2020-06-30 264 179 108\n", 195 | "2020-07-31 278 141 87\n", 196 | "2020-08-31 226 105 60\n", 197 | "2020-09-30 254 144 108\n", 198 | "2020-10-31 254 181 132\n", 199 | "2020-11-30 273 136 88\n", 200 | "2020-12-31 219 150 107\n", 201 | "2021-01-31 189 103 76\n", 202 | "2021-02-28 255 137 92\n", 203 | "2021-03-31 251 164 93\n", 204 | "2021-04-30 212 164 117\n", 205 | "2021-05-31 264 166 119\n", 206 | "2021-06-30 262 230 137" 207 | ] 208 | }, 209 | "execution_count": 3, 210 | "metadata": {}, 211 | "output_type": "execute_result" 212 | } 213 | ], 214 | "source": [ 215 | "df_full = pd.concat(df_list, axis=1, join=\"inner\").reindex(df_samp.index).fillna(0)\n", 216 | "\n", 217 | "# delete min value as an outlier\n", 218 | "df_full = df_full.iloc[1: , :]\n", 219 | "\n", 220 | "# drop last row – to let arXiv settle\n", 221 | "df_full.drop(df_full.tail(1).index, inplace=True)\n", 222 | "\n", 223 | "df_full" 224 | ] 225 | }, 226 | { 227 | "cell_type": "code", 228 | "execution_count": 4, 229 | "id": "93df1c10-2765-447e-83d3-00e3c876f88d", 230 | "metadata": {}, 231 | "outputs": [ 232 | { 233 | "data": { 234 | "text/html": [ 235 | "
\n", 236 | "\n", 249 | "\n", 250 | " \n", 251 | " \n", 252 | " \n", 253 | " \n", 254 | " \n", 255 | " \n", 256 | " \n", 257 | " \n", 258 | " \n", 259 | " \n", 260 | " \n", 261 | " \n", 262 | " \n", 263 | " \n", 264 | " \n", 265 | " \n", 266 | " \n", 267 | " \n", 268 | " \n", 269 | " \n", 270 | " \n", 271 | " \n", 272 | " \n", 273 | " \n", 274 | " \n", 275 | " \n", 276 | " \n", 277 | " \n", 278 | " \n", 279 | " \n", 280 | " \n", 281 | " \n", 282 | " \n", 283 | " \n", 284 | " \n", 285 | " \n", 286 | " \n", 287 | " \n", 288 | " \n", 289 | " \n", 290 | " \n", 291 | " \n", 292 | " \n", 293 | " \n", 294 | " \n", 295 | " \n", 296 | " \n", 297 | " \n", 298 | " \n", 299 | " \n", 300 | " \n", 301 | " \n", 302 | " \n", 303 | " \n", 304 | " \n", 305 | " \n", 306 | " \n", 307 | " \n", 308 | " \n", 309 | " \n", 310 | " \n", 311 | " \n", 312 | " \n", 313 | " \n", 314 | " \n", 315 | " \n", 316 | " \n", 317 | " \n", 318 | " \n", 319 | " \n", 320 | " \n", 321 | " \n", 322 | " \n", 323 | " \n", 324 | " \n", 325 | " \n", 326 | " \n", 327 | " \n", 328 | " \n", 329 | " \n", 330 | " \n", 331 | " \n", 332 | " \n", 333 | " \n", 334 | " \n", 335 | " \n", 336 | " \n", 337 | " \n", 338 | " \n", 339 | " \n", 340 | " \n", 341 | " \n", 342 | " \n", 343 | " \n", 344 | "
graph algorithmsgraph neural networksknowledge graph
date
2020-06-30264179108
2020-07-3127814187
2020-08-3122610560
2020-09-30254144108
2020-10-31254181132
2020-11-3027313688
2020-12-31219150107
2021-01-3118910376
2021-02-2825513792
2021-03-3125116493
2021-04-30212164117
2021-05-31264166119
2021-06-30262230137
\n", 345 | "
" 346 | ], 347 | "text/plain": [ 348 | " graph algorithms graph neural networks knowledge graph\n", 349 | "date \n", 350 | "2020-06-30 264 179 108\n", 351 | "2020-07-31 278 141 87\n", 352 | "2020-08-31 226 105 60\n", 353 | "2020-09-30 254 144 108\n", 354 | "2020-10-31 254 181 132\n", 355 | "2020-11-30 273 136 88\n", 356 | "2020-12-31 219 150 107\n", 357 | "2021-01-31 189 103 76\n", 358 | "2021-02-28 255 137 92\n", 359 | "2021-03-31 251 164 93\n", 360 | "2021-04-30 212 164 117\n", 361 | "2021-05-31 264 166 119\n", 362 | "2021-06-30 262 230 137" 363 | ] 364 | }, 365 | "execution_count": 4, 366 | "metadata": {}, 367 | "output_type": "execute_result" 368 | } 369 | ], 370 | "source": [ 371 | "# remove rows before the start date\n", 372 | "nix = set([\n", 373 | " i\n", 374 | " for i, row in enumerate(df_full.index)\n", 375 | " if str(row) < start_date\n", 376 | "])\n", 377 | "\n", 378 | "cutoff = max(nix) + 1\n", 379 | "df_full = df_full.iloc[cutoff: , :]\n", 380 | "\n", 381 | "df_full" 382 | ] 383 | }, 384 | { 385 | "cell_type": "code", 386 | "execution_count": 5, 387 | "id": "8b19b90a-0c00-4fa4-b117-5362c89f3c71", 388 | "metadata": {}, 389 | "outputs": [ 390 | { 391 | "data": { 392 | "image/png": "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\n", 393 | "text/plain": [ 394 | "
" 395 | ] 396 | }, 397 | "metadata": { 398 | "needs_background": "light" 399 | }, 400 | "output_type": "display_data" 401 | } 402 | ], 403 | "source": [ 404 | "plot = df_full.plot(\n", 405 | " subplots=True,\n", 406 | " legend=False,\n", 407 | " figsize=(11, 7),\n", 408 | " xlabel=\"date submitted\"\n", 409 | ")\n", 410 | "\n", 411 | "plot[0].set(ylabel=\"monthly counts\")\n", 412 | "\n", 413 | "summary = list(df.groupby(\"topic\").sum().to_dict()[\"counts\"].items())\n", 414 | "y_max = round(max(df_full.max(axis=1)) + 10.0)\n", 415 | "\n", 416 | "for index, ax in enumerate(plot):\n", 417 | " query, count = summary[index]\n", 418 | " ax.set(ylim=(0, y_max), title=f\"{query}, total = {count}\")\n", 419 | "\n", 420 | "fig = plot[0].get_figure()\n", 421 | "fig.tight_layout()\n", 422 | "fig.savefig(png_file)" 423 | ] 424 | } 425 | ], 426 | "metadata": { 427 | "kernelspec": { 428 | "display_name": "Python 3 (ipykernel)", 429 | "language": "python", 430 | "name": "python3" 431 | }, 432 | "language_info": { 433 | "codemirror_mode": { 434 | "name": "ipython", 435 | "version": 3 436 | }, 437 | "file_extension": ".py", 438 | "mimetype": "text/x-python", 439 | "name": "python", 440 | "nbconvert_exporter": "python", 441 | "pygments_lexer": "ipython3", 442 | "version": "3.8.10" 443 | } 444 | }, 445 | "nbformat": 4, 446 | "nbformat_minor": 5 447 | } 448 | --------------------------------------------------------------------------------