├── .gitignore ├── LICENSE ├── README.md ├── notebooks ├── authors │ └── hirsch-index.ipynb ├── data_questions │ └── counts_within_country.ipynb ├── getting-started │ ├── README.md │ ├── api-webinar-apr2024 │ │ └── tutorial01.ipynb │ ├── get-random-entity.ipynb │ ├── paging.ipynb │ └── premium.ipynb ├── institutions │ ├── japan_sources.csv │ ├── japan_sources.ipynb │ ├── oa-percentage.ipynb │ ├── uw-collaborators copy.ipynb │ └── uw-collaborators.ipynb └── openalex_works │ └── openalex_works.ipynb ├── requirements.txt ├── resources └── img │ ├── OpenAlex-banner.png │ ├── OpenAlex-entities.png │ ├── OpenAlex-logo.png │ ├── notebooks │ ├── cursor-paging.png │ └── meta-object.png │ └── ui_vs_api.svg └── runtime.txt /.gitignore: -------------------------------------------------------------------------------- 1 | # Jupyter Notebook 2 | .ipynb_checkpoints 3 | 4 | # Environments 5 | .env 6 | .venv 7 | env/ 8 | venv/ 9 | ENV/ 10 | env.bak/ 11 | venv.bak/ 12 | 13 | data/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 OurResearch 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 | 2 | OpenAlex logo 3 | 4 | 5 | # OpenAlex API tutorials 6 | 7 | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/ourresearch/openalex-api-tutorials/main) 8 | [![Open All Collab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/ourresearch/openalex-api-tutorials) 9 | [![Deepnote](https://deepnote.com/buttons/launch-in-deepnote-small.svg)](https://www.deepnote.com/launch?url=https%3A%2F%2Fgithub.com%2Fourresearch%2Fopenalex-api-tutorials/) 10 | 11 | A collection of Jupyter notebooks, each walking you through a common example of bibliometric analysis 12 | using scholarly data from the [OpenAlex API](https://docs.openalex.org/). (:warning: Work In Progress). 13 | 14 | 15 | ## :bulb: What is OpenAlex? 16 | [OpenAlex](https://openalex.org/) is a fully-open index of scholarly works, authors, venues, institutions, and concepts 17 | — along with all the ways they're connected to one another. 18 | It's named after the ancient [Library of Alexandria](https://en.wikipedia.org/wiki/Library_of_Alexandria) 19 | and made by the nonprofit [OurResearch](https://ourresearch.org/). 20 | 21 |
22 | 23 | OpenAlex base model 24 | 25 |
26 | 27 | What makes OpenAlex stand out as a bibliographic data source is its Openness: 28 | * The data is made available under the [CC0 license](https://creativecommons.org/publicdomain/zero/1.0/). 29 | That means it's in the public domain, and free to use in any way you like. 30 | 31 | * The primary way to access the data, is the [API](https://docs.openalex.org/#access). 32 | It is free and requires no authentication. 33 | 34 | 35 | ## :notebook: What are Jupyter notebooks? 36 | Jupyter notebooks are documents that let you combine executable code snippets 37 | with explanatory text, formulas and visualizations. 38 | Weaving both of them together allows to craft a narrative around the *How?* and *Why?* 39 | of one's programming work which makes them especially useful for writing up documentation and tutorials. 40 | But not only that: 41 | you can also dive right in by modifying and re-running code snippets as needed. 42 | Therefore a notebook may serve as a starting point to prototype your own idea! 43 | 44 | 45 | ## :rocket: How do I run the notebooks? 46 | *Note: You can browse through and read the notebooks right here on GitHub. However, the code snippets won't be executable.* 47 | 48 | The easiest way to run Jupyter notebooks is via cloud services like [Binder](https://mybinder.org/), 49 | [Google’s Colaboratory](https://colab.research.google.com/) or [Deepnote](https://deepnote.com/). 50 | They provide you with a free execution environment that you can access directly in your browser - no setup needed. 51 | Just click on one of the badges at the top of this README and it will take you to the selected service. 52 | 53 | Alternatively you can set up a Jupyter server on your computer 54 | (for instructions please refer to the [official Jupyter docs](https://docs.jupyter.org/en/latest/install.html)). 55 | Many IDEs also support running Jupyter notebooks out of the box or via a plugin. If you have one installed, 56 | it may be a good idea to consult its docs or marketplace. 57 | If you go local, though, please remember to install the Python packages specified in the `requirements.txt` file. 58 | 59 | 60 | ## :book: Citation 61 | If you use OpenAlex in your research, please cite this paper: 62 | > Priem, J., Piwowar, H., & Orr, R. (2022). _OpenAlex: A fully-open index of scholarly works, authors, venues, institutions, and concepts._ ArXiv. https://arxiv.org/abs/2205.01833 63 | 64 | and don't forget to [tell us](https://docs.openalex.org/#contact) about your project. We love to hear what you come up with using data from OpenAlex! 65 | -------------------------------------------------------------------------------- /notebooks/data_questions/counts_within_country.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "attachments": {}, 5 | "cell_type": "markdown", 6 | "metadata": {}, 7 | "source": [ 8 | "# Question: How are number of works counted when looking at institutions within a country?" 9 | ] 10 | }, 11 | { 12 | "cell_type": "code", 13 | "execution_count": 13, 14 | "metadata": {}, 15 | "outputs": [], 16 | "source": [ 17 | "import requests\n", 18 | "country_code = 'ES'\n", 19 | "url = f\"https://api.openalex.org/works\"\n", 20 | "params = {\n", 21 | " 'filter': f'institutions.country_code:{country_code}',\n", 22 | " 'group_by': 'institutions.id',\n", 23 | "}\n", 24 | "r = requests.get(url, params=params)" 25 | ] 26 | }, 27 | { 28 | "cell_type": "code", 29 | "execution_count": 14, 30 | "metadata": {}, 31 | "outputs": [], 32 | "source": [ 33 | "counts_by_institutions_from_works_endpoint = r.json()['group_by']" 34 | ] 35 | }, 36 | { 37 | "cell_type": "code", 38 | "execution_count": 16, 39 | "metadata": {}, 40 | "outputs": [ 41 | { 42 | "data": { 43 | "text/plain": [ 44 | "{'key': 'https://openalex.org/I71999127',\n", 45 | " 'key_display_name': 'University of Barcelona',\n", 46 | " 'count': 106711}" 47 | ] 48 | }, 49 | "execution_count": 16, 50 | "metadata": {}, 51 | "output_type": "execute_result" 52 | } 53 | ], 54 | "source": [ 55 | "counts_by_institutions_from_works_endpoint[0]" 56 | ] 57 | }, 58 | { 59 | "cell_type": "code", 60 | "execution_count": 18, 61 | "metadata": {}, 62 | "outputs": [ 63 | { 64 | "name": "stdout", 65 | "output_type": "stream", 66 | "text": [ 67 | "collected 1804 sources (using 74 api calls)\n" 68 | ] 69 | } 70 | ], 71 | "source": [ 72 | "country_code = 'ES'\n", 73 | "# url with a placeholder for page number\n", 74 | "url = f\"https://api.openalex.org/institutions\"\n", 75 | "params = {\n", 76 | " 'filter': f'country_code:{country_code}',\n", 77 | " 'page': 1, # initaliaze `page` param to 1\n", 78 | "}\n", 79 | "\n", 80 | "has_more_pages = True\n", 81 | "fewer_than_10k_results = True\n", 82 | "\n", 83 | "institutions_data_from_institutions_endpoint = []\n", 84 | "\n", 85 | "# loop through pages\n", 86 | "loop_index = 0\n", 87 | "while has_more_pages and fewer_than_10k_results:\n", 88 | " \n", 89 | " page_with_results = requests.get(url, params=params).json()\n", 90 | " \n", 91 | " # loop through partial list of results\n", 92 | " results = page_with_results['results']\n", 93 | " for api_result in results:\n", 94 | " # # Collect the fields we are interested in, for this source\n", 95 | " # source = {field: api_result[field] for field in fields}\n", 96 | " # Append this source to our `japanese_sourcers` list\n", 97 | " institutions_data_from_institutions_endpoint.append(api_result)\n", 98 | "\n", 99 | " # next page\n", 100 | " params['page'] += 1\n", 101 | " \n", 102 | " # end loop when either there are no more results on the requested page \n", 103 | " # or the next request would exceed 10,000 results\n", 104 | " per_page = page_with_results['meta']['per_page']\n", 105 | " has_more_pages = len(results) == per_page\n", 106 | " fewer_than_10k_results = per_page * params['page'] <= 10000\n", 107 | " loop_index += 1\n", 108 | "print(f\"collected {len(institutions_data_from_institutions_endpoint)} sources (using {loop_index+1} api calls)\")" 109 | ] 110 | }, 111 | { 112 | "cell_type": "code", 113 | "execution_count": 24, 114 | "metadata": {}, 115 | "outputs": [], 116 | "source": [ 117 | "keyed_counts = {item['key']: item for item in counts_by_institutions_from_works_endpoint}\n", 118 | "data = []\n", 119 | "for inst in institutions_data_from_institutions_endpoint:\n", 120 | " id = inst['id']\n", 121 | " c = keyed_counts.get(id)\n", 122 | " if c:\n", 123 | " data.append({\n", 124 | " 'id': id,\n", 125 | " 'count1': inst['works_count'],\n", 126 | " 'count2': c['count'],\n", 127 | " })" 128 | ] 129 | }, 130 | { 131 | "cell_type": "code", 132 | "execution_count": 25, 133 | "metadata": {}, 134 | "outputs": [ 135 | { 136 | "data": { 137 | "text/plain": [ 138 | "[{'id': 'https://openalex.org/I71999127', 'count1': 106775, 'count2': 106711},\n", 139 | " {'id': 'https://openalex.org/I121748325', 'count1': 101654, 'count2': 101658},\n", 140 | " {'id': 'https://openalex.org/I123044942', 'count1': 88726, 'count2': 88466},\n", 141 | " {'id': 'https://openalex.org/I16097986', 'count1': 75688, 'count2': 74845},\n", 142 | " {'id': 'https://openalex.org/I173304897', 'count1': 70325, 'count2': 70273},\n", 143 | " {'id': 'https://openalex.org/I63634437', 'count1': 63111, 'count2': 61569},\n", 144 | " {'id': 'https://openalex.org/I79238269', 'count1': 58094, 'count2': 58138},\n", 145 | " {'id': 'https://openalex.org/I169108374', 'count1': 55330, 'count2': 55244},\n", 146 | " {'id': 'https://openalex.org/I9617848', 'count1': 51480, 'count2': 51501},\n", 147 | " {'id': 'https://openalex.org/I200284239', 'count1': 49358, 'count2': 49181},\n", 148 | " {'id': 'https://openalex.org/I255234318', 'count1': 47887, 'count2': 47898},\n", 149 | " {'id': 'https://openalex.org/I88060688', 'count1': 42417, 'count2': 42434},\n", 150 | " {'id': 'https://openalex.org/I60053951', 'count1': 42409, 'count2': 42438},\n", 151 | " {'id': 'https://openalex.org/I80180929', 'count1': 36961, 'count2': 36981},\n", 152 | " {'id': 'https://openalex.org/I165339363', 'count1': 36934, 'count2': 35901},\n", 153 | " {'id': 'https://openalex.org/I184999862', 'count1': 36332, 'count2': 36361},\n", 154 | " {'id': 'https://openalex.org/I82767444', 'count1': 33232, 'count2': 33261},\n", 155 | " {'id': 'https://openalex.org/I134820265', 'count1': 32576, 'count2': 31468},\n", 156 | " {'id': 'https://openalex.org/I6289922', 'count1': 29132, 'count2': 29159},\n", 157 | " {'id': 'https://openalex.org/I130194489', 'count1': 28886, 'count2': 28914},\n", 158 | " {'id': 'https://openalex.org/I170486558', 'count1': 28116, 'count2': 28094},\n", 159 | " {'id': 'https://openalex.org/I79189158', 'count1': 27491, 'count2': 27530},\n", 160 | " {'id': 'https://openalex.org/I50357001', 'count1': 26882, 'count2': 26880},\n", 161 | " {'id': 'https://openalex.org/I108103353', 'count1': 25897, 'count2': 25916},\n", 162 | " {'id': 'https://openalex.org/I189268942', 'count1': 25005, 'count2': 25014},\n", 163 | " {'id': 'https://openalex.org/I4210115097', 'count1': 24678, 'count2': 24696},\n", 164 | " {'id': 'https://openalex.org/I158438070', 'count1': 24397, 'count2': 24402},\n", 165 | " {'id': 'https://openalex.org/I80606768', 'count1': 22496, 'count2': 22516},\n", 166 | " {'id': 'https://openalex.org/I88155538', 'count1': 21926, 'count2': 21929},\n", 167 | " {'id': 'https://openalex.org/I2801357902', 'count1': 21347, 'count2': 21351},\n", 168 | " {'id': 'https://openalex.org/I55952717', 'count1': 20759, 'count2': 20781},\n", 169 | " {'id': 'https://openalex.org/I13134134', 'count1': 20519, 'count2': 19919},\n", 170 | " {'id': 'https://openalex.org/I2800562746', 'count1': 20002, 'count2': 19995},\n", 171 | " {'id': 'https://openalex.org/I53110688', 'count1': 19820, 'count2': 19835},\n", 172 | " {'id': 'https://openalex.org/I2960094004', 'count1': 19486, 'count2': 19479},\n", 173 | " {'id': 'https://openalex.org/I182083151', 'count1': 18710, 'count2': 18728},\n", 174 | " {'id': 'https://openalex.org/I10902133', 'count1': 18113, 'count2': 18134},\n", 175 | " {'id': 'https://openalex.org/I39147953', 'count1': 17024, 'count2': 17029},\n", 176 | " {'id': 'https://openalex.org/I2961216182', 'count1': 16932, 'count2': 16922},\n", 177 | " {'id': 'https://openalex.org/I11019714', 'count1': 16732, 'count2': 16732},\n", 178 | " {'id': 'https://openalex.org/I178450904', 'count1': 15500, 'count2': 15508},\n", 179 | " {'id': 'https://openalex.org/I191420491', 'count1': 15161, 'count2': 15168},\n", 180 | " {'id': 'https://openalex.org/I251424209', 'count1': 15059, 'count2': 15072},\n", 181 | " {'id': 'https://openalex.org/I111262870', 'count1': 14954, 'count2': 14962},\n", 182 | " {'id': 'https://openalex.org/I4210161852', 'count1': 14401, 'count2': 14409},\n", 183 | " {'id': 'https://openalex.org/I119635470', 'count1': 14168, 'count2': 14181},\n", 184 | " {'id': 'https://openalex.org/I50441567', 'count1': 13880, 'count2': 13747},\n", 185 | " {'id': 'https://openalex.org/I52354020', 'count1': 13729, 'count2': 13743},\n", 186 | " {'id': 'https://openalex.org/I4210135003', 'count1': 13702, 'count2': 13709},\n", 187 | " {'id': 'https://openalex.org/I2802050225', 'count1': 13507, 'count2': 13509},\n", 188 | " {'id': 'https://openalex.org/I4210153139', 'count1': 13325, 'count2': 13331},\n", 189 | " {'id': 'https://openalex.org/I4210101691', 'count1': 12792, 'count2': 12798},\n", 190 | " {'id': 'https://openalex.org/I4210127641', 'count1': 12724, 'count2': 12741},\n", 191 | " {'id': 'https://openalex.org/I11932220', 'count1': 11809, 'count2': 11554},\n", 192 | " {'id': 'https://openalex.org/I2801795740', 'count1': 11780, 'count2': 11787},\n", 193 | " {'id': 'https://openalex.org/I175051016', 'count1': 11301, 'count2': 11312},\n", 194 | " {'id': 'https://openalex.org/I4210153460', 'count1': 10731, 'count2': 10740},\n", 195 | " {'id': 'https://openalex.org/I95013407', 'count1': 10652, 'count2': 10655},\n", 196 | " {'id': 'https://openalex.org/I15766328', 'count1': 10543, 'count2': 10557},\n", 197 | " {'id': 'https://openalex.org/I78880903', 'count1': 10080, 'count2': 10089},\n", 198 | " {'id': 'https://openalex.org/I8833935', 'count1': 9528, 'count2': 9527},\n", 199 | " {'id': 'https://openalex.org/I4210130807', 'count1': 9359, 'count2': 9359},\n", 200 | " {'id': 'https://openalex.org/I110594554', 'count1': 9356, 'count2': 9245},\n", 201 | " {'id': 'https://openalex.org/I4210147680', 'count1': 9301, 'count2': 9306},\n", 202 | " {'id': 'https://openalex.org/I4210130874', 'count1': 8808, 'count2': 8817},\n", 203 | " {'id': 'https://openalex.org/I4210129357', 'count1': 8784, 'count2': 8796},\n", 204 | " {'id': 'https://openalex.org/I4210130498', 'count1': 8714, 'count2': 8721},\n", 205 | " {'id': 'https://openalex.org/I4210105637', 'count1': 8234, 'count2': 8239},\n", 206 | " {'id': 'https://openalex.org/I4210118429', 'count1': 7971, 'count2': 7973},\n", 207 | " {'id': 'https://openalex.org/I168974976', 'count1': 7912, 'count2': 7922},\n", 208 | " {'id': 'https://openalex.org/I4210094406', 'count1': 7776, 'count2': 7783},\n", 209 | " {'id': 'https://openalex.org/I4210114530', 'count1': 7635, 'count2': 7634},\n", 210 | " {'id': 'https://openalex.org/I138847295', 'count1': 7576, 'count2': 7582},\n", 211 | " {'id': 'https://openalex.org/I4210150677', 'count1': 7321, 'count2': 7327},\n", 212 | " {'id': 'https://openalex.org/I3123212020', 'count1': 7197, 'count2': 7195},\n", 213 | " {'id': 'https://openalex.org/I46176106', 'count1': 7179, 'count2': 7185},\n", 214 | " {'id': 'https://openalex.org/I4210099858', 'count1': 7050, 'count2': 7054},\n", 215 | " {'id': 'https://openalex.org/I3019010403', 'count1': 7048, 'count2': 7061},\n", 216 | " {'id': 'https://openalex.org/I4210123675', 'count1': 7031, 'count2': 7037},\n", 217 | " {'id': 'https://openalex.org/I4210146061', 'count1': 6833, 'count2': 6833},\n", 218 | " {'id': 'https://openalex.org/I4210120109', 'count1': 6542, 'count2': 6550},\n", 219 | " {'id': 'https://openalex.org/I4210090436', 'count1': 6466, 'count2': 6469},\n", 220 | " {'id': 'https://openalex.org/I4210116170', 'count1': 6465, 'count2': 6464},\n", 221 | " {'id': 'https://openalex.org/I1307323311', 'count1': 6442, 'count2': 6446},\n", 222 | " {'id': 'https://openalex.org/I5593406', 'count1': 6304, 'count2': 6280},\n", 223 | " {'id': 'https://openalex.org/I4210133994', 'count1': 6252, 'count2': 6259},\n", 224 | " {'id': 'https://openalex.org/I4210105802', 'count1': 6159, 'count2': 6159},\n", 225 | " {'id': 'https://openalex.org/I4210113665', 'count1': 5795, 'count2': 5801},\n", 226 | " {'id': 'https://openalex.org/I4210096311', 'count1': 5746, 'count2': 5748},\n", 227 | " {'id': 'https://openalex.org/I4210137412', 'count1': 5685, 'count2': 5691},\n", 228 | " {'id': 'https://openalex.org/I4210159146', 'count1': 5644, 'count2': 5647},\n", 229 | " {'id': 'https://openalex.org/I136040515', 'count1': 5524, 'count2': 5529},\n", 230 | " {'id': 'https://openalex.org/I4210137674', 'count1': 5454, 'count2': 5462},\n", 231 | " {'id': 'https://openalex.org/I4210127649', 'count1': 5345, 'count2': 5347},\n", 232 | " {'id': 'https://openalex.org/I4210151127', 'count1': 5344, 'count2': 5355},\n", 233 | " {'id': 'https://openalex.org/I4210135032', 'count1': 5099, 'count2': 5103},\n", 234 | " {'id': 'https://openalex.org/I2800401438', 'count1': 5026, 'count2': 5020},\n", 235 | " {'id': 'https://openalex.org/I96580804', 'count1': 4985, 'count2': 4989},\n", 236 | " {'id': 'https://openalex.org/I4210107147', 'count1': 4959, 'count2': 4962},\n", 237 | " {'id': 'https://openalex.org/I4210157802', 'count1': 4872, 'count2': 4876},\n", 238 | " {'id': 'https://openalex.org/I2184545', 'count1': 4707, 'count2': 4710},\n", 239 | " {'id': 'https://openalex.org/I4210089289', 'count1': 4610, 'count2': 4615},\n", 240 | " {'id': 'https://openalex.org/I47686490', 'count1': 4605, 'count2': 4610},\n", 241 | " {'id': 'https://openalex.org/I4210085921', 'count1': 4401, 'count2': 4403},\n", 242 | " {'id': 'https://openalex.org/I68763199', 'count1': 4335, 'count2': 4343},\n", 243 | " {'id': 'https://openalex.org/I2799803557', 'count1': 4308, 'count2': 4305},\n", 244 | " {'id': 'https://openalex.org/I4210160192', 'count1': 4181, 'count2': 4180},\n", 245 | " {'id': 'https://openalex.org/I179630473', 'count1': 4178, 'count2': 4180},\n", 246 | " {'id': 'https://openalex.org/I904013037', 'count1': 4044, 'count2': 4054},\n", 247 | " {'id': 'https://openalex.org/I4210100188', 'count1': 4033, 'count2': 4037},\n", 248 | " {'id': 'https://openalex.org/I118091203', 'count1': 4012, 'count2': 4019},\n", 249 | " {'id': 'https://openalex.org/I2802353815', 'count1': 3948, 'count2': 3945},\n", 250 | " {'id': 'https://openalex.org/I4210086614', 'count1': 3931, 'count2': 3935},\n", 251 | " {'id': 'https://openalex.org/I4210095952', 'count1': 3918, 'count2': 3925},\n", 252 | " {'id': 'https://openalex.org/I4210108548', 'count1': 3844, 'count2': 3851},\n", 253 | " {'id': 'https://openalex.org/I3123915005', 'count1': 3842, 'count2': 3849},\n", 254 | " {'id': 'https://openalex.org/I4210129578', 'count1': 3842, 'count2': 3843},\n", 255 | " {'id': 'https://openalex.org/I4210157481', 'count1': 3806, 'count2': 3808},\n", 256 | " {'id': 'https://openalex.org/I4210140856', 'count1': 3772, 'count2': 3773},\n", 257 | " {'id': 'https://openalex.org/I4210106702', 'count1': 3763, 'count2': 3767},\n", 258 | " {'id': 'https://openalex.org/I4210092123', 'count1': 3663, 'count2': 3665},\n", 259 | " {'id': 'https://openalex.org/I4210101151', 'count1': 3658, 'count2': 3660},\n", 260 | " {'id': 'https://openalex.org/I2809107791', 'count1': 3572, 'count2': 3576},\n", 261 | " {'id': 'https://openalex.org/I4210097920', 'count1': 3558, 'count2': 3559},\n", 262 | " {'id': 'https://openalex.org/I105140100', 'count1': 3546, 'count2': 3549},\n", 263 | " {'id': 'https://openalex.org/I4210104045', 'count1': 3545, 'count2': 3548},\n", 264 | " {'id': 'https://openalex.org/I4210126200', 'count1': 3543, 'count2': 3547},\n", 265 | " {'id': 'https://openalex.org/I4210152826', 'count1': 3474, 'count2': 3473},\n", 266 | " {'id': 'https://openalex.org/I4210106572', 'count1': 3343, 'count2': 3343},\n", 267 | " {'id': 'https://openalex.org/I4210087295', 'count1': 3311, 'count2': 3313},\n", 268 | " {'id': 'https://openalex.org/I4210158107', 'count1': 3301, 'count2': 3302},\n", 269 | " {'id': 'https://openalex.org/I4210129022', 'count1': 3290, 'count2': 3290},\n", 270 | " {'id': 'https://openalex.org/I4210135619', 'count1': 3228, 'count2': 3230}]" 271 | ] 272 | }, 273 | "execution_count": 25, 274 | "metadata": {}, 275 | "output_type": "execute_result" 276 | } 277 | ], 278 | "source": [ 279 | "data" 280 | ] 281 | }, 282 | { 283 | "cell_type": "code", 284 | "execution_count": null, 285 | "metadata": {}, 286 | "outputs": [], 287 | "source": [] 288 | } 289 | ], 290 | "metadata": { 291 | "kernelspec": { 292 | "display_name": "venv", 293 | "language": "python", 294 | "name": "python3" 295 | }, 296 | "language_info": { 297 | "codemirror_mode": { 298 | "name": "ipython", 299 | "version": 3 300 | }, 301 | "file_extension": ".py", 302 | "mimetype": "text/x-python", 303 | "name": "python", 304 | "nbconvert_exporter": "python", 305 | "pygments_lexer": "ipython3", 306 | "version": "3.9.12" 307 | }, 308 | "orig_nbformat": 4, 309 | "vscode": { 310 | "interpreter": { 311 | "hash": "271691dbc4cdb85f541c883090ff5a004cbd8b9c207c2cfed84437fce4e65fdb" 312 | } 313 | } 314 | }, 315 | "nbformat": 4, 316 | "nbformat_minor": 2 317 | } 318 | -------------------------------------------------------------------------------- /notebooks/getting-started/README.md: -------------------------------------------------------------------------------- 1 | # Getting started 2 | Notebooks explaining the basics of querying the [OpenAlex API](https://docs.openalex.org/) 3 | -------------------------------------------------------------------------------- /notebooks/getting-started/api-webinar-apr2024/tutorial01.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "
\n", 8 | " \n", 9 | " \"OpenAlex\n", 10 | " \n", 11 | "
" 12 | ] 13 | }, 14 | { 15 | "cell_type": "markdown", 16 | "metadata": {}, 17 | "source": [ 18 | "# OpenAlex API Webinar - Tutorial 01 - Getting data about papers that a university's research has cited\n", 19 | "\n", 20 | "Jason Portenoy\n", 21 | "\n", 22 | "April 25, 2024" 23 | ] 24 | }, 25 | { 26 | "cell_type": "markdown", 27 | "metadata": {}, 28 | "source": [ 29 | "Welcome to the Jupyter Notebook accompanying part 2 of the [OpenAlex](https://openalex.org) webinar on using the API!\n", 30 | "\n", 31 | "Video recording of the webinar: [https://youtu.be/DLKUgbw7FV4](https://youtu.be/DLKUgbw7FV4)\n", 32 | "\n", 33 | "We will be using Python code to get data about a university's research works, and the works referenced by those works.\n", 34 | "\n", 35 | "* The [OpenAlex webinars](https://openalex.org/webinars) page is where you can find information on all webinars including this one, with dates for upcoming webinars, and links to video recordings of previous webinars.\n", 36 | "* If you aren't familiar with Jupyter notebooks, [you can learn more here](https://jupyter.org/try-jupyter/notebooks/?path=notebooks/Intro.ipynb)\n", 37 | "* To learn all about the OpenAlex API: [visit the technical documentation](https://docs.openalex.org)\n", 38 | "\n", 39 | "And of course, if you aren't yet familiar with OpenAlex, you can go to [https://openalex.org](https://openalex.org) right now and start exploring!" 40 | ] 41 | }, 42 | { 43 | "cell_type": "markdown", 44 | "metadata": {}, 45 | "source": [ 46 | "## API Basics\n", 47 | "\n", 48 | "[Part 1 of this OpenAlex API webinar series](https://www.youtube.com/watch?v=ycoHc8flx8U) was a basic introduction to an Application Programming Interface, or API: what it is, how OpenAlex's API works, and why it might be useful.\n", 49 | "\n", 50 | "Now, we dive in, and use Python code to get data from OpenAlex about a university's research!\n", 51 | "\n", 52 | "As a reminder, an API allows a program to interact with the data, instead of you (a person). When a person is using the data, the User Interface (UI) is more appropriate:\n", 53 | "\n", 54 | "![UI vs API](../../../resources/img/ui_vs_api.svg)\n", 55 | "\n", 56 | "The **free version** of the OpenAlex API:\n", 57 | "* Does not require authentication\n", 58 | " * But, add your e-mail address for faster and more consistent responses: “mailto=you@example.com”\n", 59 | "* 100k calls per day\n", 60 | "* 10 calls per second\n", 61 | "* _We raise limits for free to support research projects when possible_\n", 62 | "\n", 63 | "The **premium version** of the OpenAlex API:\n", 64 | "* API key tied to your account\n", 65 | "* API limits raised to meet your needs\n", 66 | "* Additional filters to support hourly data updates\n", 67 | " * `from_created_date`\n", 68 | " * `from_updated_date`" 69 | ] 70 | }, 71 | { 72 | "cell_type": "markdown", 73 | "metadata": {}, 74 | "source": [ 75 | "## Let's dive in!\n", 76 | "\n", 77 | "The OpenAlex API is very **powerful**, but it is also very **easy to use**. There is no authentication required. All your code needs to do is make standard HTTP GET requests.\n", 78 | "\n", 79 | "While there are some [good libraries you can use to access the API](https://docs.openalex.org/how-to-use-the-api/api-overview#client-libraries), we're going to start very simply by making API calls directly. We will import just two small libraries to help us." 80 | ] 81 | }, 82 | { 83 | "cell_type": "code", 84 | "execution_count": 1, 85 | "metadata": {}, 86 | "outputs": [], 87 | "source": [ 88 | "# setup: import libraries\n", 89 | "import requests\n", 90 | "import csv" 91 | ] 92 | }, 93 | { 94 | "cell_type": "code", 95 | "execution_count": 2, 96 | "metadata": {}, 97 | "outputs": [], 98 | "source": [ 99 | "# IMPORTANT: Set your email here in order to use the API's \"polite pool\"\n", 100 | "# See: https://docs.openalex.org/how-to-use-the-api/rate-limits-and-authentication#the-polite-pool\n", 101 | "\n", 102 | "# e.g., mailto=\"youremail@example.com\"\n", 103 | "# Go ahead, fill it out:\n", 104 | "mailto = \"\"" 105 | ] 106 | }, 107 | { 108 | "cell_type": "code", 109 | "execution_count": 3, 110 | "metadata": {}, 111 | "outputs": [ 112 | { 113 | "name": "stdout", 114 | "output_type": "stream", 115 | "text": [ 116 | "Success!\n" 117 | ] 118 | } 119 | ], 120 | "source": [ 121 | "url = \"https://api.openalex.org/works\"\n", 122 | "if not mailto:\n", 123 | " raise ValueError(\"You need to fill in your email address in the `mailto` variable above!\")\n", 124 | "params = {\n", 125 | " \"mailto\": mailto,\n", 126 | " \"filter\": \"authorships.author.id:a5086928770\", # Kyle Demes's author ID\n", 127 | "}\n", 128 | "response = requests.get(url, params=params)\n", 129 | "\n", 130 | "# A \"200\" status code means that the API query was successful\n", 131 | "if response.status_code == 200:\n", 132 | " print(\"Success!\")" 133 | ] 134 | }, 135 | { 136 | "cell_type": "code", 137 | "execution_count": 4, 138 | "metadata": {}, 139 | "outputs": [ 140 | { 141 | "name": "stdout", 142 | "output_type": "stream", 143 | "text": [ 144 | "Number of results: 24\n" 145 | ] 146 | } 147 | ], 148 | "source": [ 149 | "results = response.json()['results']\n", 150 | "print(f\"Number of results: {len(results)}\")" 151 | ] 152 | }, 153 | { 154 | "cell_type": "markdown", 155 | "metadata": {}, 156 | "source": [ 157 | "We've retrieved the papers from the API, using a simple query:\n", 158 | "\n", 159 | "[`https://api.openalex.org/works?filter=authorships.author.id:a5086928770`](https://api.openalex.org/works?filter=authorships.author.id:a5086928770)\n", 160 | "\n", 161 | "You can follow that link in the browser to get the same result. But with the data now accessible by our code, we can save a CSV file with whichever fields we want.\n", 162 | "\n", 163 | "Instructions for how to write CSV files with Python are [here](https://docs.python.org/3/library/csv.html). Following these instructions:" 164 | ] 165 | }, 166 | { 167 | "cell_type": "code", 168 | "execution_count": 5, 169 | "metadata": {}, 170 | "outputs": [], 171 | "source": [ 172 | "# The Python documentation shows how to write data to a CSV file:\n", 173 | "# https://docs.python.org/3/library/csv.html\n", 174 | "with open('kdemes_works.csv', 'w', newline='') as f:\n", 175 | " # initialize the csv writer for this file\n", 176 | " writer = csv.writer(f)\n", 177 | "\n", 178 | " # write a header row at the top\n", 179 | " header = ['id', 'doi', 'publication_year', 'title']\n", 180 | " writer.writerow(header)\n", 181 | "\n", 182 | " # loop through the works and write each row\n", 183 | " for item in results:\n", 184 | " this_id = item['id']\n", 185 | " this_doi = item['doi']\n", 186 | " this_publication_year = item['publication_year']\n", 187 | " this_title = item['title']\n", 188 | " writer.writerow([this_id, this_doi, this_publication_year, this_title])" 189 | ] 190 | }, 191 | { 192 | "cell_type": "markdown", 193 | "metadata": {}, 194 | "source": [ 195 | "### University (Institution)\n", 196 | "\n", 197 | "Next, we'll try something a little more advanced. We're going to get the works from a certain institution, and then retrieve all of the references from those works (the works cited by the university's works).\n", 198 | "\n", 199 | "We'll start by collecting the university's works. We'll limit to just recently published papers so it doesn't take too long, but you could get all of the papers just as easily, if you're willing to wait.\n", 200 | "\n", 201 | "#### Cursor paging\n", 202 | "\n", 203 | "Each API query will only return a limited subset of the overall data, in what is known as a page. We need to make multiple queries to \"page through\" all of the data, collecting the data for each API query. We use a method called [\"cursor paging\"](https://docs.openalex.org/how-to-use-the-api/get-lists-of-entities/paging#cursor-paging) to do this." 204 | ] 205 | }, 206 | { 207 | "cell_type": "code", 208 | "execution_count": 6, 209 | "metadata": {}, 210 | "outputs": [ 211 | { 212 | "name": "stdout", 213 | "output_type": "stream", 214 | "text": [ 215 | "Done paging through results. We made 44 API queries, and retrieved 4259 results.\n" 216 | ] 217 | } 218 | ], 219 | "source": [ 220 | "url = \"https://api.openalex.org/works\"\n", 221 | "if not mailto:\n", 222 | " raise ValueError(\"You need to fill in your email address in the `mailto` variable above!\")\n", 223 | "params = {\n", 224 | " \"mailto\": mailto,\n", 225 | " \"filter\": f\"authorships.institutions.lineage:i129801699,publication_year:>2022\", # University of Tasmania\n", 226 | " \"per-page\": 100,\n", 227 | " \"select\": \"id,doi,publication_year,title,primary_location,authorships,topics\",\n", 228 | "}\n", 229 | "\n", 230 | "# Initialize cursor\n", 231 | "cursor = \"*\"\n", 232 | "\n", 233 | "# Initialize an empty list to store our results as we get them\n", 234 | "all_results = []\n", 235 | "count_api_queries = 0\n", 236 | "\n", 237 | "# Loop through pages\n", 238 | "while cursor:\n", 239 | " params[\"cursor\"] = cursor\n", 240 | " response = requests.get(url, params=params)\n", 241 | " if response.status_code != 200:\n", 242 | " print(\"Oh no! Something went wrong during the live demo! How embarrassing!\")\n", 243 | " break\n", 244 | " this_page_results = response.json()['results']\n", 245 | " for result in this_page_results:\n", 246 | " # Store these results in the list we created before the loop we are currently in\n", 247 | " all_results.append(result)\n", 248 | " count_api_queries += 1\n", 249 | "\n", 250 | " # Update cursor, using the response's `next_cursor` metadata field\n", 251 | " cursor = response.json()['meta']['next_cursor']\n", 252 | "print(f\"Done paging through results. We made {count_api_queries} API queries, and retrieved {len(all_results)} results.\")" 253 | ] 254 | }, 255 | { 256 | "cell_type": "markdown", 257 | "metadata": {}, 258 | "source": [ 259 | "Our next step is to loop through each work collected above, and collect all of the referenced works." 260 | ] 261 | }, 262 | { 263 | "cell_type": "code", 264 | "execution_count": 7, 265 | "metadata": {}, 266 | "outputs": [], 267 | "source": [ 268 | "# Let's make our cursor paging code above into a function, so we can reuse it easily.\n", 269 | "# This code just defines the function. We'll need to call the function later on to get it to actually get it to run.\n", 270 | "def api_query_page_results(url, params):\n", 271 | " # Initialize cursor\n", 272 | " cursor = \"*\"\n", 273 | "\n", 274 | " # Loop through pages\n", 275 | " all_results = []\n", 276 | " while cursor:\n", 277 | " params[\"cursor\"] = cursor\n", 278 | " response = requests.get(url, params=params)\n", 279 | " if response.status_code != 200:\n", 280 | " print(\"Oh no! Something went wrong during the live demo! How embarrassing!\")\n", 281 | " response.raise_for_status()\n", 282 | " this_page_results = response.json()['results']\n", 283 | " for result in this_page_results:\n", 284 | " all_results.append(result)\n", 285 | "\n", 286 | " # Update cursor\n", 287 | " cursor = response.json()['meta']['next_cursor']\n", 288 | " return all_results" 289 | ] 290 | }, 291 | { 292 | "cell_type": "code", 293 | "execution_count": 8, 294 | "metadata": {}, 295 | "outputs": [ 296 | { 297 | "name": "stdout", 298 | "output_type": "stream", 299 | "text": [ 300 | "Done collecting references. We retrieved 9157 works.\n" 301 | ] 302 | } 303 | ], 304 | "source": [ 305 | "# collect all of the works referenced by the works found above\n", 306 | "# This will be a dictionary mapping Citing Paper -> List of Cited Papers\n", 307 | "# We start by initializing an empty dictionary\n", 308 | "all_references = {}\n", 309 | "\n", 310 | "# Let's limit the results to loop through to only n=100, because this is a demo, and we don't want to wait for too long\n", 311 | "works_to_collect = all_results[:100]\n", 312 | "\n", 313 | "# We will keep track of the number of works retrieved from the API\n", 314 | "count_works_retrieved = 0\n", 315 | "\n", 316 | "for work in works_to_collect:\n", 317 | " # Get references for this work (i.e., works that have been cited by this work)\n", 318 | " this_work_id = work['id']\n", 319 | " url = \"https://api.openalex.org/works\"\n", 320 | " if not mailto:\n", 321 | " raise ValueError(\"You need to fill in your email address in the `mailto` variable above!\")\n", 322 | " params = {\n", 323 | " \"mailto\": mailto,\n", 324 | " \"filter\": f\"cited_by:{this_work_id}\",\n", 325 | " \"per-page\": 100,\n", 326 | " \"select\": \"id,doi,publication_year,title,primary_location,authorships,topics\",\n", 327 | " }\n", 328 | " this_work_references = api_query_page_results(url, params=params)\n", 329 | " # put this data into our dictionary:\n", 330 | " # The key for the dictionary is the citing work_id, and the value is the list of referenced Works\n", 331 | " all_references[this_work_id] = this_work_references\n", 332 | " count_works_retrieved += len(this_work_references)\n", 333 | "print(f\"Done collecting references. We retrieved {count_works_retrieved} works.\")" 334 | ] 335 | }, 336 | { 337 | "cell_type": "markdown", 338 | "metadata": {}, 339 | "source": [ 340 | "Now we have collected the referenced papers for each of the university's papers we collected in the first step. The next step is to save the data to a CSV file." 341 | ] 342 | }, 343 | { 344 | "cell_type": "code", 345 | "execution_count": 9, 346 | "metadata": {}, 347 | "outputs": [], 348 | "source": [ 349 | "# Function to shorten the OpenAlex ID to make it better for display\n", 350 | "def make_short_id(long_id):\n", 351 | " short_id = long_id.replace(\"https://openalex.org/\", \"\")\n", 352 | " return short_id" 353 | ] 354 | }, 355 | { 356 | "cell_type": "code", 357 | "execution_count": 10, 358 | "metadata": {}, 359 | "outputs": [], 360 | "source": [ 361 | "# Write each citing -> cited pair of works to a CSV file\n", 362 | "output_filename = \"tasmania_paper_references.csv\"\n", 363 | "with open(output_filename, 'w', newline='') as f:\n", 364 | " # initialize the csv writer for this file\n", 365 | " writer = csv.writer(f)\n", 366 | "\n", 367 | " # write a header row at the top\n", 368 | " header = ['citing_paper_id', 'cited_paper_id']\n", 369 | " writer.writerow(header)\n", 370 | "\n", 371 | " # loop through each citation, writing one row for each citation\n", 372 | " for citing_id, cited_works in all_references.items():\n", 373 | " citing_id_short = make_short_id(citing_id)\n", 374 | " for cited_work in cited_works:\n", 375 | " cited_id_short = make_short_id(cited_work['id'])\n", 376 | " writer.writerow([citing_id_short, cited_id_short])" 377 | ] 378 | }, 379 | { 380 | "cell_type": "code", 381 | "execution_count": 11, 382 | "metadata": {}, 383 | "outputs": [], 384 | "source": [ 385 | "# We can keep track of how many times each work has been cited.\n", 386 | "# One way to do this is to use Python's collections.Counter\n", 387 | "from collections import Counter\n", 388 | "citation_counts = Counter()\n", 389 | "for citing_id, cited_works in all_references.items():\n", 390 | " citation_counts.update([w['id'] for w in cited_works])" 391 | ] 392 | }, 393 | { 394 | "cell_type": "markdown", 395 | "metadata": {}, 396 | "source": [ 397 | "We can also save another CSV file with detailed metadata about each of the referenced papers we found. We will include information about the source (journal), and the topics. But you can build out this code to include any information you like (just make sure you are collecting it from the API when you specify the `select` parameter in your API requests above)." 398 | ] 399 | }, 400 | { 401 | "cell_type": "code", 402 | "execution_count": 12, 403 | "metadata": {}, 404 | "outputs": [], 405 | "source": [ 406 | "output_filename = \"tasmania_references_paper_metadata.csv\"\n", 407 | "seen_work_ids = set()\n", 408 | "with open(output_filename, 'w', newline='') as f:\n", 409 | " # initialize the csv writer for this file\n", 410 | " writer = csv.writer(f)\n", 411 | "\n", 412 | " # write a header row at the top\n", 413 | " header = ['work_id', 'title', 'doi', 'utasmania_citation_count', \n", 414 | " 'source_id', 'source_issn', 'source_display_name', \n", 415 | " 'primary_topic_id', 'primary_topic_display_name']\n", 416 | " writer.writerow(header)\n", 417 | "\n", 418 | " for cited_works in all_references.values():\n", 419 | " for w in cited_works:\n", 420 | " work_id = w['id']\n", 421 | " work_id_short = make_short_id(work_id)\n", 422 | " title = w['title']\n", 423 | " if work_id not in seen_work_ids and title != 'Deleted Work':\n", 424 | " # We will write a row to the CSV file for this work\n", 425 | " doi = w['doi']\n", 426 | " utasmania_citation_count = citation_counts[work_id]\n", 427 | "\n", 428 | " # Get source (journal)\n", 429 | " try:\n", 430 | " source = w['primary_location']['source']\n", 431 | " source_id = source['id']\n", 432 | " source_id_short = make_short_id(source_id)\n", 433 | " source_issn = source['issn_l']\n", 434 | " source_display_name = source['display_name']\n", 435 | " except (KeyError, TypeError):\n", 436 | " source_id = None\n", 437 | " source_issn = None\n", 438 | " source_display_name = None\n", 439 | "\n", 440 | " # Get primary_topic\n", 441 | " try:\n", 442 | " primary_topic = w['topics'][0]\n", 443 | " primary_topic_id = primary_topic['id']\n", 444 | " primary_topic_id_short = make_short_id(primary_topic_id)\n", 445 | " primary_topic_display_name = primary_topic['display_name']\n", 446 | " except (IndexError, KeyError, TypeError):\n", 447 | " primary_topic_id = None\n", 448 | " primary_topic_display_name = None\n", 449 | " \n", 450 | " # Write this work's row to the CSV file\n", 451 | " writer.writerow([work_id_short, title, doi, \n", 452 | " utasmania_citation_count, source_id_short, \n", 453 | " source_issn, source_display_name, \n", 454 | " primary_topic_id_short, primary_topic_display_name])\n", 455 | " \n", 456 | " seen_work_ids.add(work_id)\n" 457 | ] 458 | }, 459 | { 460 | "cell_type": "markdown", 461 | "metadata": {}, 462 | "source": [ 463 | "Now we have two CSV files:\n", 464 | "\n", 465 | "* `tasmania_paper_references.csv` has a two column edge-list of citing work -> cited work\n", 466 | "* `tasmania_references_paper_metadata.csv` has metadata about each cited work\n", 467 | "\n", 468 | "You could open this file in a spreadsheet program like Excel to do additional analysis, or continue working with the data in Python." 469 | ] 470 | } 471 | ], 472 | "metadata": { 473 | "kernelspec": { 474 | "display_name": "venv", 475 | "language": "python", 476 | "name": "python3" 477 | }, 478 | "language_info": { 479 | "codemirror_mode": { 480 | "name": "ipython", 481 | "version": 3 482 | }, 483 | "file_extension": ".py", 484 | "mimetype": "text/x-python", 485 | "name": "python", 486 | "nbconvert_exporter": "python", 487 | "pygments_lexer": "ipython3", 488 | "version": "3.10.9" 489 | } 490 | }, 491 | "nbformat": 4, 492 | "nbformat_minor": 2 493 | } 494 | -------------------------------------------------------------------------------- /notebooks/getting-started/get-random-entity.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "230c97f7-1471-4c29-bf95-decb29c2a2ae", 6 | "metadata": {}, 7 | "source": [ 8 | "
\n", 9 | " \n", 10 | " \"OpenAlex\n", 11 | " \n", 12 | "
" 13 | ] 14 | }, 15 | { 16 | "cell_type": "markdown", 17 | "id": "7796f7f3-4cc9-4951-a9eb-bb2b6d3949cb", 18 | "metadata": {}, 19 | "source": [ 20 | "# 🔀 That's so random!\n", 21 | "\n", 22 | "Want to explore the OpenAlex API in a fun nerdy way? \n", 23 | "\n", 24 | "Simply add \"_/random_\" (-> [docs](https://docs.openalex.org/how-to-use-the-api/get-single-entities#random-entity)) to any of the API endpoints like this:\n", 25 | "\n", 26 | "* for a random author: http://api.openalex.org/authors/random\n", 27 | "* for a random concept: http://api.openalex.org/concepts/random\n", 28 | "* for a random institution: http://api.openalex.org/institutions/random\n", 29 | "* for a random venue: http://api.openalex.org/venues/random\n", 30 | "* for a random work: http://api.openalex.org/works/random\n", 31 | "\n", 32 | "Each time you call one of these URLs you'll get a different entity. \n", 33 | "`random` lets you dive right into the data and get a feel for what attributes are available for an entity type and how they are represented in JSON.\n", 34 | "***" 35 | ] 36 | }, 37 | { 38 | "cell_type": "markdown", 39 | "id": "a3ff3c1f-acae-4c1b-b674-2c23a714d4af", 40 | "metadata": {}, 41 | "source": [ 42 | "Let's see for ourselves and query the OpenAlex API for a random work:" 43 | ] 44 | }, 45 | { 46 | "cell_type": "code", 47 | "execution_count": 1, 48 | "id": "2b325b3d-450b-4c22-a31f-79c4b1d2d52d", 49 | "metadata": {}, 50 | "outputs": [ 51 | { 52 | "name": "stdout", 53 | "output_type": "stream", 54 | "text": [ 55 | "{'abstract_inverted_index': None,\n", 56 | " 'alternate_host_venues': [],\n", 57 | " 'authorships': [{'author': {'display_name': 'Gabriel Germain',\n", 58 | " 'id': 'https://openalex.org/A2794418983',\n", 59 | " 'orcid': None},\n", 60 | " 'author_position': 'first',\n", 61 | " 'institutions': [],\n", 62 | " 'raw_affiliation_string': None}],\n", 63 | " 'biblio': {'first_page': '223',\n", 64 | " 'issue': '384',\n", 65 | " 'last_page': '224',\n", 66 | " 'volume': '81'},\n", 67 | " 'cited_by_api_url': 'https://api.openalex.org/works?filter=cites:W2403480063',\n", 68 | " 'cited_by_count': 0,\n", 69 | " 'concepts': [{'display_name': 'Art',\n", 70 | " 'id': 'https://openalex.org/C142362112',\n", 71 | " 'level': 0,\n", 72 | " 'score': '0.406585',\n", 73 | " 'wikidata': 'https://www.wikidata.org/wiki/Q735'},\n", 74 | " {'display_name': 'Philosophy',\n", 75 | " 'id': 'https://openalex.org/C138885662',\n", 76 | " 'level': 0,\n", 77 | " 'score': '0.363935',\n", 78 | " 'wikidata': 'https://www.wikidata.org/wiki/Q5891'}],\n", 79 | " 'counts_by_year': [],\n", 80 | " 'created_date': '2016-06-24',\n", 81 | " 'display_name': '12. Arrighetti (Graziano). Cosmologia mitica di Omero e '\n", 82 | " 'Esiodo (Extr. des « Studi Classici e Orientali », v. XV)',\n", 83 | " 'doi': None,\n", 84 | " 'host_venue': {'display_name': 'Revue des Études Grecques',\n", 85 | " 'id': None,\n", 86 | " 'is_oa': None,\n", 87 | " 'issn': None,\n", 88 | " 'issn_l': None,\n", 89 | " 'license': None,\n", 90 | " 'publisher': 'Persée - Portail des revues scientifiques en SHS',\n", 91 | " 'type': None,\n", 92 | " 'url': 'https://www.persee.fr/doc/reg_0035-2039_1968_num_81_384_1022_t1_0223_0000_3',\n", 93 | " 'version': None},\n", 94 | " 'id': 'https://openalex.org/W2403480063',\n", 95 | " 'ids': {'mag': '2403480063', 'openalex': 'https://openalex.org/W2403480063'},\n", 96 | " 'is_paratext': None,\n", 97 | " 'is_retracted': False,\n", 98 | " 'mesh': [],\n", 99 | " 'open_access': {'is_oa': True, 'oa_status': None, 'oa_url': None},\n", 100 | " 'publication_date': '1968-01-01',\n", 101 | " 'publication_year': 1968,\n", 102 | " 'referenced_works': [],\n", 103 | " 'related_works': [],\n", 104 | " 'title': '12. Arrighetti (Graziano). Cosmologia mitica di Omero e Esiodo '\n", 105 | " '(Extr. des « Studi Classici e Orientali », v. XV)',\n", 106 | " 'type': None,\n", 107 | " 'updated_date': '2021-11-04'}\n" 108 | ] 109 | } 110 | ], 111 | "source": [ 112 | "RANDOM_WORKS_URL = 'http://api.openalex.org/works/random'\n", 113 | "\n", 114 | "import requests\n", 115 | "response = requests.get(url=RANDOM_WORKS_URL)\n", 116 | "response.raise_for_status()\n", 117 | "random_work = response.json()\n", 118 | "\n", 119 | "import pprint\n", 120 | "pprint.pprint(random_work)" 121 | ] 122 | }, 123 | { 124 | "cell_type": "markdown", 125 | "id": "5e620a43-cfac-489e-8e48-642df57f0990", 126 | "metadata": {}, 127 | "source": [ 128 | "*** \n", 129 | "\n", 130 | "Run the notebook again (or if you are using the URL in your browser hit refresh) and voilà a new one!\n", 131 | "\n", 132 | "Happy exploring! 😎" 133 | ] 134 | } 135 | ], 136 | "metadata": { 137 | "kernelspec": { 138 | "display_name": "Python 3 (ipykernel)", 139 | "language": "python", 140 | "name": "python3" 141 | }, 142 | "language_info": { 143 | "codemirror_mode": { 144 | "name": "ipython", 145 | "version": 3 146 | }, 147 | "file_extension": ".py", 148 | "mimetype": "text/x-python", 149 | "name": "python", 150 | "nbconvert_exporter": "python", 151 | "pygments_lexer": "ipython3", 152 | "version": "3.8.10" 153 | } 154 | }, 155 | "nbformat": 4, 156 | "nbformat_minor": 5 157 | } 158 | -------------------------------------------------------------------------------- /notebooks/getting-started/paging.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "29720234-5a2d-48b7-82ee-eb1574e6275b", 6 | "metadata": {}, 7 | "source": [ 8 | "
\n", 9 | " \n", 10 | " \"OpenAlex\n", 11 | " \n", 12 | "
" 13 | ] 14 | }, 15 | { 16 | "cell_type": "markdown", 17 | "id": "f133f73b-c2df-4f5b-ab14-2ea668d47052", 18 | "metadata": {}, 19 | "source": [ 20 | "# Turn the page\n", 21 | "❓ Let's say we query OpenAlex for a [list of entities](https://docs.openalex.org/how-to-use-the-api/get-lists-of-entities). By default the API only gives us the first 25 results of the list. Why is that ❓\n", 22 | "\n", 23 | ">Just like books split large amounts of text and distribute it onto **pages**, the OpenAlex API does the same with a (potentially massive) list of entities.\n", 24 | "\n", 25 | "It makes the data more manageable for both sides: We get small amounts of data that fit into our computer's memory in a reasonable amount of time, while the OpenAlex API needs to process less data at once and can serve more requests to more users.\n", 26 | "\n", 27 | "\n", 28 | "👉 So, coming back to our question of why only 25 results: That is only the first page with a partial list of results! \n", 29 | "The API even tells us this. Every page includes a **meta section** with the following information:\n", 30 | "\n", 31 | "
\n", 32 | " \"meta\n", 33 | "
\n", 34 | "\n", 35 | "\n", 36 | "In order to get the complete list, we need to \"_leaf through_\" all the pages. But how do we do that? \n", 37 | "There are two techniques the OpenAlex API offers: **_🔢 basic paging_** and **_↪️ cursor paging_**. Let's get to know them!\n", 38 | "\n", 39 | "
\n", 40 | " 💡 Use the Polite Pool
\n", 41 | "While it is always a good idea to use the polite pool, this holds especially true for paging. The polite pool has much faster and more consistent response times, so for multiple requests these gains in response time will aggregate and speed up your application!\n", 42 | "
" 43 | ] 44 | }, 45 | { 46 | "cell_type": "markdown", 47 | "id": "a9d7fae8-4ee2-40fa-9db1-dbd1002811f6", 48 | "metadata": {}, 49 | "source": [ 50 | "
\n", 51 | "\n", 52 | "## 🔢 Basic paging\n", 53 | "[Basic paging](https://docs.openalex.org/how-to-use-the-api/get-lists-of-entities/paging#basic-paging) is the simplest form of paging and works like this: \n", 54 | "* All pages are numbered **from 1 to n** \n", 55 | "*We can determine n by dividing meta's `count` by `per_page` and rounding the result up to the next integer.* \n", 56 | "* To request one of the pages, we add the **`page` parameter** to the URL and put the page number as its value, \n", 57 | "e.g. for requesting page 2, we add https://api.openalex.org/works?filter=author.id:A5048491430&page=2\n", 58 | "\n", 59 | "### Within limits\n", 60 | "
\n", 61 | " ⚠️ While basic paging is easy to use, it only works for the first 10,000 results of any list.
\n", 62 | " If we want to see more than 10,000 results, we'll need to use cursor paging.\n", 63 | "
\n", 64 | "\n", 65 | "### Example\n", 66 | "Let's look at an example, where we want to retrieve a complete list of all publications from an author and print their OpenAlex IDs. \n", 67 | "Given the OpenAlex ID for the author `A5048491430` the URL would be: https://api.openalex.org/works?filter=author.id:A5048491430.\n", 68 | "\n", 69 | "To loop through all pages, we start by setting `page=1` and then repeating:\n", 70 | "* request the specified page by adding the `page` parameter to the URL\n", 71 | "* print all of the OpenAlex IDs from the publications on this page in blocks of five\n", 72 | "* update `page` parameter to `page`+1\n", 73 | "\n", 74 | "until *either* there are no more results on the requested page *or* the next request would exceed 10,000 results." 75 | ] 76 | }, 77 | { 78 | "cell_type": "code", 79 | "execution_count": 1, 80 | "id": "98e2d323-c7e2-4003-9e84-1a70551756c7", 81 | "metadata": {}, 82 | "outputs": [ 83 | { 84 | "name": "stdout", 85 | "output_type": "stream", 86 | "text": [ 87 | "\n", 88 | "https://api.openalex.org/works?filter=author.id:A5048491430&page=1\n", 89 | "W2046766973\tW2741809807\tW2045657963\tW1572136682\tW2066415719\n", 90 | "W2170531319\tW1963524534\tW1553564559\tW2003014790\tW2051771537\n", 91 | "W1987881751\tW2980172586\tW2095083909\tW1528782725\tW2102613218\n", 92 | "W4235038322\tW2014140050\tW2109312864\tW3071882161\tW1501540670\n", 93 | "W2103827239\tW4229010617\tW2133737815\tW4366077396\tW2171848392\n", 94 | "\n", 95 | "https://api.openalex.org/works?filter=author.id:A5048491430&page=2\n", 96 | "W4245410681\tW3021154342\tW2017292130\tW2168771768\tW4213202391\n", 97 | "W4236031980\tW1945323029\tW2103382090\tW2105695765\tW3084168212\n", 98 | "W4211010643\tW1934573562\tW2050143895\tW2065622609\tW2110180658\n", 99 | "W2941875476\tW4237216357\tW4242907897\tW4244183537\tW4247478427\n", 100 | "W4287670050\tW104609242\tW1972136887\tW2005148091\tW2010883332\n", 101 | "\n", 102 | "https://api.openalex.org/works?filter=author.id:A5048491430&page=3\n", 103 | "W2108112433\tW2154768595\tW2255028491\tW2284153834\tW2307679124\n", 104 | "W2398849157\tW2402184614\tW2414739039\tW2613086963\tW2727815292\n", 105 | "W2740744046\tW2949915600\tW2951362513\tW2979437137\tW3084303366\n", 106 | "W3168937413\tW3206844309\tW4221043181\tW4230863633\tW4237614390\n", 107 | "W4240735862\tW4244937397\tW4246220990\tW4252159547\tW4252662598\n", 108 | "\n", 109 | "https://api.openalex.org/works?filter=author.id:A5048491430&page=4\n", 110 | "W4288680697\tW4299928665\tW4301303362\t" 111 | ] 112 | } 113 | ], 114 | "source": [ 115 | "import requests\n", 116 | "\n", 117 | "# url with a placeholder for page number\n", 118 | "example_url_with_page = 'https://api.openalex.org/works?filter=author.id:A5048491430&page={}'\n", 119 | "\n", 120 | "page = 1\n", 121 | "has_more_pages = True\n", 122 | "fewer_than_10k_results = True\n", 123 | "\n", 124 | "# loop through pages\n", 125 | "while has_more_pages and fewer_than_10k_results:\n", 126 | " \n", 127 | " # set page value and request page from OpenAlex\n", 128 | " url = example_url_with_page.format(page)\n", 129 | " print('\\n' + url)\n", 130 | " page_with_results = requests.get(url).json()\n", 131 | " \n", 132 | " # loop through partial list of results\n", 133 | " results = page_with_results['results']\n", 134 | " for i,work in enumerate(results):\n", 135 | " openalex_id = work['id'].replace(\"https://openalex.org/\", \"\")\n", 136 | " print(openalex_id, end='\\t' if (i+1)%5!=0 else '\\n')\n", 137 | "\n", 138 | " # next page\n", 139 | " page += 1\n", 140 | " \n", 141 | " # end loop when either there are no more results on the requested page \n", 142 | " # or the next request would exceed 10,000 results\n", 143 | " per_page = page_with_results['meta']['per_page']\n", 144 | " has_more_pages = len(results) == per_page\n", 145 | " fewer_than_10k_results = per_page * page <= 10000" 146 | ] 147 | }, 148 | { 149 | "cell_type": "markdown", 150 | "id": "8cc5ccb4-a896-48ed-9adc-1df242028b34", 151 | "metadata": {}, 152 | "source": [ 153 | "
\n", 154 | "\n", 155 | "## ↪️ Cursor paging\n", 156 | "[Cursor paging](https://docs.openalex.org/how-to-use-the-api/get-lists-of-entities/paging#cursor-paging) is a bit more complicated than basic paging, but it allows us to access as many records as we like. \n", 157 | "\n", 158 | "\n", 159 | "To use cursor paging,\n", 160 | "* we add the **`cursor` parameter** with a start value of `*` to our first query, \n", 161 | "e.g. https://api.openalex.org/works?filter=author.id:A5048491430&cursor=*\n", 162 | "\n", 163 | "* The response to our query will now include a `next_cursor` value in the response's `meta` section. \n", 164 | "To retrieve the next page, we **copy `meta.next_cursor`** into the cursor field of our URL.\n", 165 | "\n", 166 | "* To get all the results, we keep repeating the second step until `meta.next_cursor` is null.\n", 167 | "\n", 168 | "
\n", 169 | " \"cursor\n", 170 | "
\n", 171 | "\n", 172 | "### With great power comes great responsibility\n", 173 | "Cursor paging is very powerful, since there is no limit on the number of pages you can request. Please use it responsibly!\n", 174 | "
\n", 175 | " 🚫 Don't use cursor paging to download a very large or even the whole dataset\n", 176 | " \n", 180 | "\n", 181 | " Instead, download everything at once, using the data snapshot. It's free, easy, fast, and you get all the results in same format you'd get from the API.\n", 182 | "
\n", 183 | "\n", 184 | "### Example\n", 185 | "Let's look at the same example as before, where we want to retrieve a complete list of all publications from an author and print their OpenAlex IDs. \n", 186 | "\n", 187 | "To loop through all pages, we start by setting `cursor=*` and then repeating:\n", 188 | "* request the specified page by adding the `cursor` parameter to the URL\n", 189 | "* print all of the OpenAlex IDs from the publications on this page in blocks of five\n", 190 | "* update `cursor` parameter to `meta.next_cursor`\n", 191 | "\n", 192 | "until `meta.next_cursor` is null and the list of results is empty." 193 | ] 194 | }, 195 | { 196 | "cell_type": "code", 197 | "execution_count": 2, 198 | "id": "0bd272c4-037b-4587-8b9e-2b8fa0a1b269", 199 | "metadata": {}, 200 | "outputs": [ 201 | { 202 | "name": "stdout", 203 | "output_type": "stream", 204 | "text": [ 205 | "\n", 206 | "https://api.openalex.org/works?filter=author.id:A5048491430&cursor=*\n", 207 | "W2046766973\tW2741809807\tW2045657963\tW1572136682\tW2066415719\n", 208 | "W2170531319\tW1963524534\tW1553564559\tW2003014790\tW2051771537\n", 209 | "W1987881751\tW2980172586\tW2095083909\tW1528782725\tW2102613218\n", 210 | "W4235038322\tW2014140050\tW2109312864\tW3071882161\tW1501540670\n", 211 | "W2103827239\tW4229010617\tW2133737815\tW4366077396\tW2171848392\n", 212 | "\n", 213 | "https://api.openalex.org/works?filter=author.id:A5048491430&cursor=Ils3LCAnaHR0cHM6Ly9vcGVuYWxleC5vcmcvVzIxNzE4NDgzOTInXSI=\n", 214 | "W4245410681\tW3021154342\tW2017292130\tW2168771768\tW4213202391\n", 215 | "W4236031980\tW1945323029\tW2103382090\tW2105695765\tW3084168212\n", 216 | "W4211010643\tW1934573562\tW2050143895\tW2065622609\tW2110180658\n", 217 | "W2941875476\tW4237216357\tW4242907897\tW4244183537\tW4247478427\n", 218 | "W4287670050\tW104609242\tW1972136887\tW2005148091\tW2010883332\n", 219 | "\n", 220 | "https://api.openalex.org/works?filter=author.id:A5048491430&cursor=IlswLCAnaHR0cHM6Ly9vcGVuYWxleC5vcmcvVzIwMTA4ODMzMzInXSI=\n", 221 | "W2108112433\tW2154768595\tW2255028491\tW2284153834\tW2307679124\n", 222 | "W2398849157\tW2402184614\tW2414739039\tW2613086963\tW2727815292\n", 223 | "W2740744046\tW2949915600\tW2951362513\tW2979437137\tW3084303366\n", 224 | "W3168937413\tW3206844309\tW4221043181\tW4230863633\tW4237614390\n", 225 | "W4240735862\tW4244937397\tW4246220990\tW4252159547\tW4252662598\n", 226 | "\n", 227 | "https://api.openalex.org/works?filter=author.id:A5048491430&cursor=IlswLCAnaHR0cHM6Ly9vcGVuYWxleC5vcmcvVzQyNTI2NjI1OTgnXSI=\n", 228 | "W4288680697\tW4299928665\tW4301303362\t\n", 229 | "https://api.openalex.org/works?filter=author.id:A5048491430&cursor=IlswLCAnaHR0cHM6Ly9vcGVuYWxleC5vcmcvVzQzMDEzMDMzNjInXSI=\n" 230 | ] 231 | } 232 | ], 233 | "source": [ 234 | "import requests\n", 235 | "\n", 236 | "# url with a placeholder for cursor\n", 237 | "example_url_with_cursor = 'https://api.openalex.org/works?filter=author.id:A5048491430&cursor={}'\n", 238 | "\n", 239 | "cursor = '*'\n", 240 | "\n", 241 | "# loop through pages\n", 242 | "while cursor:\n", 243 | " \n", 244 | " # set cursor value and request page from OpenAlex\n", 245 | " url = example_url_with_cursor.format(cursor)\n", 246 | " print(\"\\n\" + url)\n", 247 | " page_with_results = requests.get(url).json()\n", 248 | " \n", 249 | " # loop through partial list of results\n", 250 | " results = page_with_results['results']\n", 251 | " for i,work in enumerate(results):\n", 252 | " openalex_id = work['id'].replace(\"https://openalex.org/\", \"\")\n", 253 | " print(openalex_id, end='\\t' if (i+1)%5!=0 else '\\n')\n", 254 | "\n", 255 | " # update cursor to meta.next_cursor\n", 256 | " cursor = page_with_results['meta']['next_cursor']" 257 | ] 258 | }, 259 | { 260 | "cell_type": "markdown", 261 | "id": "b3ea64d9-e5ab-4396-bbf3-41ec9318ff69", 262 | "metadata": {}, 263 | "source": [ 264 | "
\n", 265 | "\n", 266 | "What we covered in this notebook is quite technical and might be a bit for beginners to take in, so\n", 267 | "please don't worry too much, if you need to reread it or need additional clarifying. \n", 268 | "The main concept to take away is that \n", 269 | "* the OpenAlex API distributes result lists into smaller chunks called pages \n", 270 | "* and thus to retrieve a complete result list, we have to manually or programatically \"leaf\" though these pages.\n", 271 | "\n", 272 | "Happy paging! 😎" 273 | ] 274 | }, 275 | { 276 | "cell_type": "markdown", 277 | "id": "25ec853d", 278 | "metadata": {}, 279 | "source": [] 280 | } 281 | ], 282 | "metadata": { 283 | "kernelspec": { 284 | "display_name": "Python 3 (ipykernel)", 285 | "language": "python", 286 | "name": "python3" 287 | }, 288 | "language_info": { 289 | "codemirror_mode": { 290 | "name": "ipython", 291 | "version": 3 292 | }, 293 | "file_extension": ".py", 294 | "mimetype": "text/x-python", 295 | "name": "python", 296 | "nbconvert_exporter": "python", 297 | "pygments_lexer": "ipython3", 298 | "version": "3.10.9" 299 | } 300 | }, 301 | "nbformat": 4, 302 | "nbformat_minor": 5 303 | } 304 | -------------------------------------------------------------------------------- /notebooks/getting-started/premium.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "
\n", 8 | " \n", 9 | " \"OpenAlex\n", 10 | " \n", 11 | "
" 12 | ] 13 | }, 14 | { 15 | "attachments": {}, 16 | "cell_type": "markdown", 17 | "metadata": {}, 18 | "source": [ 19 | "# Getting started with OpenAlex Premium\n", 20 | "\n", 21 | "In this tutorial, we're going to learn how to get started using [OpenAlex Premium](https://openalex.org/pricing). This subscription service provides some features beyond the free services. One of the most important of these features is **faster updates,** allowing you to keep your data fully synced with OpenAlex.\n", 22 | "\n", 23 | "The way we do this is by using the `from_created_date` [(doc)](https://docs.openalex.org/api-entities/works/filter-works#from_created_date) or the `from_updated_date` [(doc)](https://docs.openalex.org/api-entities/works/filter-works#from_updated_date) filters. These filters allow you to get the new works you need to keep your data updated, and they require a Premium API Key to work.\n", 24 | "\n", 25 | "We're going to set up the code to poll the OpenAlex API for newly updated works on a regular basis, once per day." 26 | ] 27 | }, 28 | { 29 | "attachments": {}, 30 | "cell_type": "markdown", 31 | "metadata": {}, 32 | "source": [ 33 | "First, we need to get the API key you received by signing up for OpenAlex Premium. (Don't have a key yet? [Contact us right now to learn more about getting premium!](https://openalex.org/pricing))\n", 34 | "\n", 35 | "We'll store our API key in a variable called `my_api_key`. There are several ways to do this. You could just put it into the code, but since it is sensitive information that we don't want others to see, we're going to get it from an [environment variable, which we'll store in a `.env` file.](https://towardsdatascience.com/the-quick-guide-to-using-environment-variables-in-python-d4ec9291619e)\n", 36 | "\n", 37 | "This is just a text file with the name `.env`, that looks like this:\n", 38 | "```\n", 39 | "API_KEY=\n", 40 | "```\n", 41 | "Replace `` with your OpenAlex Premium API Key.\n" 42 | ] 43 | }, 44 | { 45 | "attachments": {}, 46 | "cell_type": "markdown", 47 | "metadata": {}, 48 | "source": [ 49 | "Now, to set our `my_api_key` variable, we'll set our environment using the [`python-dotenv`](https://pypi.org/project/python-dotenv/) library, then get the variable using the `os.getenv()` function." 50 | ] 51 | }, 52 | { 53 | "cell_type": "code", 54 | "execution_count": 1, 55 | "metadata": {}, 56 | "outputs": [ 57 | { 58 | "name": "stdout", 59 | "output_type": "stream", 60 | "text": [ 61 | "API key is set!\n" 62 | ] 63 | } 64 | ], 65 | "source": [ 66 | "import os\n", 67 | "from dotenv import load_dotenv\n", 68 | "\n", 69 | "load_dotenv('.env')\n", 70 | "my_api_key = os.getenv('API_KEY')\n", 71 | "if my_api_key is None:\n", 72 | " print(\"No API key found!!!\")\n", 73 | "else:\n", 74 | " print(\"API key is set!\")" 75 | ] 76 | }, 77 | { 78 | "attachments": {}, 79 | "cell_type": "markdown", 80 | "metadata": {}, 81 | "source": [ 82 | "Our plan is to get all of the works that have been updated in the last four hours. So let's construct a URL that will request that information from the API:" 83 | ] 84 | }, 85 | { 86 | "cell_type": "code", 87 | "execution_count": 4, 88 | "metadata": {}, 89 | "outputs": [], 90 | "source": [ 91 | "import requests\n", 92 | "from datetime import datetime, timedelta" 93 | ] 94 | }, 95 | { 96 | "cell_type": "markdown", 97 | "metadata": {}, 98 | "source": [ 99 | "To use the API key, we have two options. The first option is to include it in the URL, as an `api_key` parameter:" 100 | ] 101 | }, 102 | { 103 | "cell_type": "code", 104 | "execution_count": 9, 105 | "metadata": {}, 106 | "outputs": [ 107 | { 108 | "name": "stdout", 109 | "output_type": "stream", 110 | "text": [ 111 | "Our formatted date-time string looks like this: 2023-11-07T19:22:46.848349\n", 112 | "Requesting newly updated works, including the API key as a URL query parameter...\n" 113 | ] 114 | }, 115 | { 116 | "name": "stdout", 117 | "output_type": "stream", 118 | "text": [ 119 | "Retrieved 25 works, out of 1967482 works updated since 2023-11-07T19:22:46.848349\n" 120 | ] 121 | } 122 | ], 123 | "source": [ 124 | "four_hours_ago = datetime.utcnow() - timedelta(hours=4)\n", 125 | "four_hours_ago_formatted_string = four_hours_ago.isoformat()\n", 126 | "print(f\"Our formatted date-time string looks like this: {four_hours_ago_formatted_string}\")\n", 127 | "# Construct a URL to requests works from the last four hours, including our API key as a URL query parameter.\n", 128 | "url = f\"https://api.openalex.org/works?filter=from_updated_date:{four_hours_ago_formatted_string}&api_key={my_api_key}\"\n", 129 | "print(f\"Requesting newly updated works, including the API key as a URL query parameter...\")\n", 130 | "r = requests.get(url)\n", 131 | "updated_works = r.json()\n", 132 | "\n", 133 | "count_works_retrieved = len(updated_works['results'])\n", 134 | "count_works_total = updated_works['meta']['count']\n", 135 | "print(f\"Retrieved {count_works_retrieved} works, out of {count_works_total} works updated since {four_hours_ago_formatted_string}\")\n" 136 | ] 137 | }, 138 | { 139 | "cell_type": "markdown", 140 | "metadata": {}, 141 | "source": [ 142 | "Success! We've used our API key to request all of the works updated in the last day. To get all of the data, [you can page through the results](https://docs.openalex.org/how-to-use-the-api/get-lists-of-entities/paging)." 143 | ] 144 | }, 145 | { 146 | "cell_type": "markdown", 147 | "metadata": {}, 148 | "source": [ 149 | "Alternatively, we can include the API key as a request header:" 150 | ] 151 | }, 152 | { 153 | "cell_type": "code", 154 | "execution_count": 10, 155 | "metadata": {}, 156 | "outputs": [ 157 | { 158 | "name": "stdout", 159 | "output_type": "stream", 160 | "text": [ 161 | "Requesting newly updated works, including the API key in the request headers...\n", 162 | "Retrieved 200 works, out of 1965510 works updated since 2023-11-07T19:22:46.848349\n" 163 | ] 164 | } 165 | ], 166 | "source": [ 167 | "# Requests works from the last four hours, including our API key in the request headers\n", 168 | "url = f\"https://api.openalex.org/works?filter=from_updated_date:{four_hours_ago_formatted_string}&per-page=200\"\n", 169 | "headers = {\"api_key\": my_api_key}\n", 170 | "print(f\"Requesting newly updated works, including the API key in the request headers...\")\n", 171 | "r = requests.get(url, headers=headers)\n", 172 | "updated_works = r.json()\n", 173 | "\n", 174 | "count_works_retrieved = len(updated_works['results'])\n", 175 | "count_works_total = updated_works['meta']['count']\n", 176 | "print(f\"Retrieved {count_works_retrieved} works, out of {count_works_total} works updated since {four_hours_ago_formatted_string}\")\n" 177 | ] 178 | }, 179 | { 180 | "cell_type": "markdown", 181 | "metadata": {}, 182 | "source": [ 183 | "You can retrieve up to 200 works per page. Again, to get all of the results, [you can page through the results](https://docs.openalex.org/how-to-use-the-api/get-lists-of-entities/paging)." 184 | ] 185 | }, 186 | { 187 | "attachments": {}, 188 | "cell_type": "markdown", 189 | "metadata": {}, 190 | "source": [ 191 | "Keep in mind that this method will get works that have been updated with *any change at all*, including increases in various counts. If you're only interested in *new* works, you could use the [`from_created_date`](https://docs.openalex.org/api-entities/works/filter-works#from_created_date) filter instead of `from_updated_date`, which will give a much smaller number of works.\n", 192 | "\n", 193 | "You'll need to do two things to keep your data fresh:\n", 194 | "1. Set up a script that does something similar to what we did above, and that runs on schedule once per day (using a [cron job](https://en.wikipedia.org/wiki/Cron), for example).\n", 195 | "2. Update your database with the new data you've grabbed from our API (such as using a SQL script)." 196 | ] 197 | }, 198 | { 199 | "attachments": {}, 200 | "cell_type": "markdown", 201 | "metadata": {}, 202 | "source": [ 203 | "And that's it! Using this method, you can keep your data up to date with regular API requests, instead of waiting for new data snapshots.\n", 204 | "\n", 205 | "Enjoy!" 206 | ] 207 | } 208 | ], 209 | "metadata": { 210 | "kernelspec": { 211 | "display_name": "venv", 212 | "language": "python", 213 | "name": "python3" 214 | }, 215 | "language_info": { 216 | "codemirror_mode": { 217 | "name": "ipython", 218 | "version": 3 219 | }, 220 | "file_extension": ".py", 221 | "mimetype": "text/x-python", 222 | "name": "python", 223 | "nbconvert_exporter": "python", 224 | "pygments_lexer": "ipython3", 225 | "version": "3.10.10" 226 | }, 227 | "orig_nbformat": 4 228 | }, 229 | "nbformat": 4, 230 | "nbformat_minor": 2 231 | } 232 | -------------------------------------------------------------------------------- /notebooks/institutions/japan_sources.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "attachments": {}, 5 | "cell_type": "markdown", 6 | "metadata": {}, 7 | "source": [ 8 | "# What are the publication sources located in Japan?\n", 9 | "\n", 10 | "When it comes to geographic location data of works in OpenAlex, there are generally two ways to look at it. One is the location of the *authors* (or rather, the institutional affiliation of the authors), and the other is the location of the work's *source*. In OpenAlex, [sources are where works are hosted.](https://docs.openalex.org/api-entities/venues) Examples of sources include journals, conferences, and institutional repositories. In this tutorial, we are going to look into the sources located in Japan.\n", 11 | "\n", 12 | "Our questions are:\n", 13 | "1. How many sources of scholarly works are in Japan?\n", 14 | "2. What are the types of these sources? Journals? Repositories? Conferences?\n", 15 | " - For journals, what are the publishers?\n", 16 | " - For repositories, what are the host institutions?\n", 17 | "3. What are the names of these sources?\n", 18 | "4. How many works have Japanese sources? How does this vary over time?\n" 19 | ] 20 | }, 21 | { 22 | "cell_type": "code", 23 | "execution_count": 1, 24 | "metadata": {}, 25 | "outputs": [], 26 | "source": [ 27 | "import requests" 28 | ] 29 | }, 30 | { 31 | "attachments": {}, 32 | "cell_type": "markdown", 33 | "metadata": {}, 34 | "source": [ 35 | "### Question 1: How many sources of scholarly works are in Japan?\n", 36 | "\n", 37 | "Let's start with the first question: How many sources of scholarly works are in Japan?\n", 38 | "\n", 39 | "To do this, we will query the `/sources` API endpoint, and use a **filter** to limit it to sources where the `country_code` is `JP`." 40 | ] 41 | }, 42 | { 43 | "cell_type": "code", 44 | "execution_count": 2, 45 | "metadata": {}, 46 | "outputs": [ 47 | { 48 | "name": "stdout", 49 | "output_type": "stream", 50 | "text": [ 51 | "There are 2265 sources with country_code 'JP' (Japan)\n" 52 | ] 53 | } 54 | ], 55 | "source": [ 56 | "country_code = 'JP'\n", 57 | "url = f\"https://api.openalex.org/sources\"\n", 58 | "params = {\n", 59 | " 'filter': f'country_code:{country_code}',\n", 60 | "}\n", 61 | "r = requests.get(url, params=params)\n", 62 | "num_sources = r.json()['meta']['count']\n", 63 | "print(f\"There are {num_sources} sources with country_code 'JP' (Japan)\")" 64 | ] 65 | }, 66 | { 67 | "attachments": {}, 68 | "cell_type": "markdown", 69 | "metadata": {}, 70 | "source": [ 71 | "### Question 2: What are the types of these sources?\n", 72 | "The next question is: What are the *types* of these sources. Possible types are listed in the API docs on the [Source object](https://docs.openalex.org/api-entities/venues/venue-object#type): `journal`, `repository`, `conference`, `ebook platform`.\n", 73 | "\n", 74 | "To answer this question, we can add a `group_by` to our API query, grouping by the `type` field and counting the number of sources:" 75 | ] 76 | }, 77 | { 78 | "cell_type": "code", 79 | "execution_count": 3, 80 | "metadata": {}, 81 | "outputs": [ 82 | { 83 | "name": "stdout", 84 | "output_type": "stream", 85 | "text": [ 86 | "Number of sources in Japan for each *type* of source:\n", 87 | " \"journal\": 2158 sources\n", 88 | " \"repository\": 47 sources\n", 89 | " \"book series\": 40 sources\n", 90 | " \"conference\": 19 sources\n", 91 | " \"ebook platform\": 1 sources\n", 92 | " \"other\": 0 sources\n" 93 | ] 94 | } 95 | ], 96 | "source": [ 97 | "params = {\n", 98 | " 'filter': f'country_code:{country_code}',\n", 99 | " 'group_by': 'type',\n", 100 | "}\n", 101 | "r = requests.get(url, params=params)\n", 102 | "print(\"Number of sources in Japan for each *type* of source:\")\n", 103 | "for item in r.json()['group_by']:\n", 104 | " print(f' \"{item[\"key\"]}\": {item[\"count\"]} sources')" 105 | ] 106 | }, 107 | { 108 | "attachments": {}, 109 | "cell_type": "markdown", 110 | "metadata": {}, 111 | "source": [ 112 | "Most of the Japanese sources are of type: `journal`, and there are few other types of sources, including repositories, book series, and conferences." 113 | ] 114 | }, 115 | { 116 | "attachments": {}, 117 | "cell_type": "markdown", 118 | "metadata": {}, 119 | "source": [ 120 | "### Question 3: What are the names of these sources?\n", 121 | "The next question is: What are the *names* of these sources?\n", 122 | "\n", 123 | "To answer this, we need the API to give us all 2,162 sources. This means we will need to use the [paging technique](https://docs.openalex.org/how-to-use-the-api/get-lists-of-entities/paging).\n", 124 | "\n", 125 | "We'll adapt the technique from the [paging notebook](../getting-started/paging.ipynb) to collect the names and ISSNs of the sources." 126 | ] 127 | }, 128 | { 129 | "cell_type": "code", 130 | "execution_count": 4, 131 | "metadata": {}, 132 | "outputs": [ 133 | { 134 | "name": "stdout", 135 | "output_type": "stream", 136 | "text": [ 137 | "collected 2265 sources (using 92 api calls)\n" 138 | ] 139 | } 140 | ], 141 | "source": [ 142 | "# page through to get all sources\n", 143 | "# use paging technique from `paging.ipynb`\n", 144 | "# url with a placeholder for page number\n", 145 | "country_code = 'JP'\n", 146 | "url = f\"https://api.openalex.org/sources\"\n", 147 | "params = {\n", 148 | " 'filter': f'country_code:{country_code}',\n", 149 | " 'page': 1, # initaliaze `page` param to 1\n", 150 | "}\n", 151 | "\n", 152 | "has_more_pages = True\n", 153 | "fewer_than_10k_results = True\n", 154 | "\n", 155 | "# We will collect the data in a variable called `japanese_sources`.\n", 156 | "# Initialize this as an empty list, which we will append to\n", 157 | "japanese_sources = []\n", 158 | "\n", 159 | "# loop through pages\n", 160 | "loop_index = 0\n", 161 | "while has_more_pages and fewer_than_10k_results:\n", 162 | " \n", 163 | " page_with_results = requests.get(url, params=params).json()\n", 164 | " \n", 165 | " # loop through partial list of results\n", 166 | " results = page_with_results['results']\n", 167 | " for api_result in results:\n", 168 | " # # Collect the fields we are interested in, for this source\n", 169 | " # source = {field: api_result[field] for field in fields}\n", 170 | " # Append this source to our `japanese_sources` list\n", 171 | " japanese_sources.append(api_result)\n", 172 | "\n", 173 | " # next page\n", 174 | " params['page'] += 1\n", 175 | " \n", 176 | " # end loop when either there are no more results on the requested page \n", 177 | " # or the next request would exceed 10,000 results\n", 178 | " per_page = page_with_results['meta']['per_page']\n", 179 | " has_more_pages = len(results) == per_page\n", 180 | " fewer_than_10k_results = per_page * params['page'] <= 10000\n", 181 | " loop_index += 1\n", 182 | "print(f\"collected {len(japanese_sources)} sources (using {loop_index+1} api calls)\")" 183 | ] 184 | }, 185 | { 186 | "attachments": {}, 187 | "cell_type": "markdown", 188 | "metadata": {}, 189 | "source": [ 190 | "Now would be a good time for us to put our sources into a Pandas dataframe. This is just a way to organize the data, make it more spreadsheet-like, and make it more convenient to work with." 191 | ] 192 | }, 193 | { 194 | "cell_type": "code", 195 | "execution_count": 5, 196 | "metadata": {}, 197 | "outputs": [ 198 | { 199 | "name": "stdout", 200 | "output_type": "stream", 201 | "text": [ 202 | "Dataframe has 2265 rows and 7 columns.\n", 203 | "\n", 204 | "The first five sources are named:\n", 205 | " Journal of the Japan Society of Mechanical Engineers\n", 206 | " Nippon Hoshasen Gijutsu Gakkai Zasshi\n", 207 | " Journal of the Physical Society of Japan\n", 208 | " Nihon rinsho. Japanese journal of clinical medicine\n", 209 | " Bulletin of the Chemical Society of Japan\n" 210 | ] 211 | } 212 | ], 213 | "source": [ 214 | "import pandas as pd\n", 215 | "\n", 216 | "# Each source in our list of `japanese sources` contains a lot of data, some of it complex and nested.\n", 217 | "# So let's limit our dataframe to include only some of the fields.\n", 218 | "\n", 219 | "# Define the fields that we are interested in collecting:\n", 220 | "fields = [\n", 221 | " 'id',\n", 222 | " 'issn_l',\n", 223 | " 'display_name',\n", 224 | " 'host_organization',\n", 225 | " 'works_count',\n", 226 | " 'cited_by_count',\n", 227 | " 'type',\n", 228 | "]\n", 229 | "\n", 230 | "# One way to limit the dataframe to include only our `fields` is to use the `from_records()` method\n", 231 | "# and specify only the columns we want.\n", 232 | "df_sources = pd.DataFrame.from_records(japanese_sources, columns=fields)\n", 233 | "\n", 234 | "num_rows, num_columns = df_sources.shape\n", 235 | "print(f\"Dataframe has {num_rows} rows and {num_columns} columns.\")\n", 236 | "print() # blank line\n", 237 | "print(\"The first five sources are named:\")\n", 238 | "for name in df_sources['display_name'].head(5):\n", 239 | " print(f\" {name}\")" 240 | ] 241 | }, 242 | { 243 | "attachments": {}, 244 | "cell_type": "markdown", 245 | "metadata": {}, 246 | "source": [ 247 | "With Pandas, it is very easy to save the data as a spreadsheet file, in case we want to work with it later." 248 | ] 249 | }, 250 | { 251 | "cell_type": "code", 252 | "execution_count": 6, 253 | "metadata": {}, 254 | "outputs": [], 255 | "source": [ 256 | "df_sources.to_csv(\"japan_sources.csv\")" 257 | ] 258 | }, 259 | { 260 | "attachments": {}, 261 | "cell_type": "markdown", 262 | "metadata": {}, 263 | "source": [ 264 | "At this point, we can go back and answer a question we skipped over when we moved onto question 3: What are the [`host_organizations`](https://docs.openalex.org/api-entities/venues/venue-object#host_organization) for these sources? In the case of a source of type `journal`, a `host_organization` is a Publisher---the company or organization that distributes the works.\n", 265 | "\n", 266 | "The data we have collected from the API contains the field `host_organization`, which is a link that can be fed back into the API to get more information about the organization, such as name, parent companies, and number of works. It would not be hard to collect this data, but for now we will leave it to future work. However, we can use the data we already have to simply *count* the number of different publishers that host japanese sources." 267 | ] 268 | }, 269 | { 270 | "cell_type": "code", 271 | "execution_count": 7, 272 | "metadata": {}, 273 | "outputs": [ 274 | { 275 | "name": "stdout", 276 | "output_type": "stream", 277 | "text": [ 278 | "The Japanese sources are associated with 431 different host organizations (publishers).\n" 279 | ] 280 | } 281 | ], 282 | "source": [ 283 | "num_host_orgs = df_sources['host_organization'].nunique()\n", 284 | "print(f\"The Japanese sources are associated with {num_host_orgs} different host organizations (publishers).\")" 285 | ] 286 | }, 287 | { 288 | "attachments": {}, 289 | "cell_type": "markdown", 290 | "metadata": {}, 291 | "source": [ 292 | "### Question 4: How many works have Japanese sources? How does this vary over time?\n", 293 | "Finally, let's look at how many works there are with Japanese sources, and the trends of these works over time.\n", 294 | "\n", 295 | "First, we'll just look at the number of works with Japanese sources in the OpenAlex dataset." 296 | ] 297 | }, 298 | { 299 | "cell_type": "code", 300 | "execution_count": 8, 301 | "metadata": {}, 302 | "outputs": [ 303 | { 304 | "name": "stdout", 305 | "output_type": "stream", 306 | "text": [ 307 | "There are 3,827,227 works (articles) with Japanese sources.\n" 308 | ] 309 | } 310 | ], 311 | "source": [ 312 | "num_works = df_sources['works_count'].sum()\n", 313 | "print(f\"There are {num_works:,} works (articles) with Japanese sources.\") # putting \":,\" after num_works tells the formatter to use commas as thousands separators" 314 | ] 315 | }, 316 | { 317 | "attachments": {}, 318 | "cell_type": "markdown", 319 | "metadata": {}, 320 | "source": [ 321 | "Next, we'll look at the number of works per year per source. This data is not in our dataframe (we excluded it when we specified only certain `fields`). However, we can go back to our collection of `japanese_sources`, which has the field `counts_by_year`. This field—as we can learn from [the docs](https://docs.openalex.org/api-entities/venues/venue-object#counts_by_year)—contains the source's counts of works by year, for the last ten years, organized as a list of dictionaries." 322 | ] 323 | }, 324 | { 325 | "cell_type": "code", 326 | "execution_count": 9, 327 | "metadata": {}, 328 | "outputs": [ 329 | { 330 | "name": "stdout", 331 | "output_type": "stream", 332 | "text": [ 333 | "Created a dataframe counting works from year 2012 to year 2049.\n" 334 | ] 335 | } 336 | ], 337 | "source": [ 338 | "# Put the data for `counts_by_year` into a Pandas dataframe.\n", 339 | "data = []\n", 340 | "for source in japanese_sources:\n", 341 | " for year_count in source['counts_by_year']:\n", 342 | " data.append({\n", 343 | " 'id': source['id'],\n", 344 | " 'year': int(year_count['year']),\n", 345 | " 'works_count': int(year_count['works_count']),\n", 346 | " 'cited_by_count': int(year_count['cited_by_count']),\n", 347 | " })\n", 348 | "df_counts_by_year = pd.DataFrame(data)\n", 349 | "\n", 350 | "# Each row in the dataframe represents one year of one source.\n", 351 | "# We can group by year and sum the number of works to get the\n", 352 | "# total counts by year.\n", 353 | "\n", 354 | "all_counts_japan = df_counts_by_year.groupby('year')['works_count'].sum()\n", 355 | "print(f\"Created a dataframe counting works from year {all_counts_japan.index.min()} to year {all_counts_japan.index.max()}.\")" 356 | ] 357 | }, 358 | { 359 | "attachments": {}, 360 | "cell_type": "markdown", 361 | "metadata": {}, 362 | "source": [ 363 | "We'll use the [seaborn](https://seaborn.pydata.org/index.html) library to plot graphs of the data. Seaborn is an extension on top of [matplotlib](https://seaborn.pydata.org/index.html), the standard visualization library in Python. It is not the only choice for visualization, but we'll use it now because it is widely-used, and not too difficult to get started with." 364 | ] 365 | }, 366 | { 367 | "cell_type": "code", 368 | "execution_count": 10, 369 | "metadata": {}, 370 | "outputs": [ 371 | { 372 | "data": { 373 | "text/plain": [ 374 | "Text(0.5, 1.0, 'Number of works in Japanese journals')" 375 | ] 376 | }, 377 | "execution_count": 10, 378 | "metadata": {}, 379 | "output_type": "execute_result" 380 | }, 381 | { 382 | "data": { 383 | "image/png": "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", 384 | "text/plain": [ 385 | "
" 386 | ] 387 | }, 388 | "metadata": {}, 389 | "output_type": "display_data" 390 | } 391 | ], 392 | "source": [ 393 | "# Import seaborn\n", 394 | "import seaborn as sns\n", 395 | "\n", 396 | "# Apply the default theme\n", 397 | "sns.set_theme()\n", 398 | "\n", 399 | "# Visualize the data\n", 400 | "g = sns.lineplot(all_counts_japan)\n", 401 | "g.set_ylim(bottom=0)\n", 402 | "g.set_xlim(2012, 2022)\n", 403 | "g.set_ylabel(\"number of works\")\n", 404 | "g.set_title(\"Number of works in Japanese journals\")" 405 | ] 406 | }, 407 | { 408 | "attachments": {}, 409 | "cell_type": "markdown", 410 | "metadata": {}, 411 | "source": [ 412 | "We have shown the number of works with Japanese sources in absolute terms, and that the number has been declining over recent years. But this only tells part of the story. Is this a general trend in the data set, or is it specific to Japanese works? To answer this, we want to look at the *relative* number of works, as a percentage of total number of works published in journals.\n", 413 | "\n", 414 | "We need to get the total number of works by year. One way to do this is to query the `/works` API endpoint, and [group by](https://docs.openalex.org/api-entities/works/group-works) the `publication_year`. To match the data we have about sources in Japan, we'll also limit the data to the last ten years, using the [`from_publication_date` convenience filter](https://docs.openalex.org/api-entities/works/filter-works#from_publication_date)." 415 | ] 416 | }, 417 | { 418 | "cell_type": "code", 419 | "execution_count": 11, 420 | "metadata": {}, 421 | "outputs": [], 422 | "source": [ 423 | "url = f\"https://api.openalex.org/works\"\n", 424 | "filters = [\n", 425 | " 'primary_location.source.type:journal',\n", 426 | " 'from_publication_date:2012-01-01',\n", 427 | "]\n", 428 | "params = {\n", 429 | " 'filter': \",\".join(filters),\n", 430 | " 'group_by': 'publication_year',\n", 431 | "}\n", 432 | "# make the API query\n", 433 | "r = requests.get(url, params=params)\n", 434 | "\n", 435 | "# Get the data into a pandas dataframe\n", 436 | "counts_data = []\n", 437 | "for row in r.json()['group_by']:\n", 438 | " counts_data.append({\n", 439 | " 'year': int(row['key']),\n", 440 | " 'works_count': int(row['count']),\n", 441 | " })\n", 442 | "all_counts_all_countries = pd.DataFrame(counts_data)\n", 443 | "# change the data into a series, with the year as index and the number of works as values.\n", 444 | "# this will match the `all_counts_japan` data\n", 445 | "all_counts_all_countries = all_counts_all_countries.set_index('year')['works_count']" 446 | ] 447 | }, 448 | { 449 | "cell_type": "code", 450 | "execution_count": 12, 451 | "metadata": {}, 452 | "outputs": [ 453 | { 454 | "data": { 455 | "text/plain": [ 456 | "Text(0, 0.5, 'Relative number of works')" 457 | ] 458 | }, 459 | "execution_count": 12, 460 | "metadata": {}, 461 | "output_type": "execute_result" 462 | }, 463 | { 464 | "data": { 465 | "image/png": "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YECtb9rQd/v378d3332H5557rtw5vYmISNzi4+MREREBT09PLFy4sKGbI3ocA0xEREREeoVjgImIiIhIrzABJiIiIiK9wgSYiIiIiPQKb4ITOUEQoFZzmLZYSKUSxkMkGAvxYCzEg7EQB6lUovXQERIfJsAiJ5FIoFTmorhY3dBN0XsGBlJYW5sxHiLAWIgHYyEejIV42NiYQSZjAixmHAJBRERERHqFCTARERER6RUmwERERESkV5gAExEREZFeYQJMRERERHqFCTARERER6RUmwERERESkV5gAExEREZFeYQJMRERERHqFCTARERER6RUmwERERESkV5gAExEREZFeYQJMRERERHqFCTARERER6RWDhm4AVS5DmY8VP5yHjdwYtpamsLU0ga2lCZpbmsDEiOEjIiIiqqkmk0HFx8dj0aJFOHPmDMzMzDBs2DC8/fbbMDIyqnQ9QRAQHR2NzZs3IyMjA+7u7pg7dy68vLw0y2RkZOCrr77CuXPncPnyZRgaGuLMmTPl1ldQUIBVq1Zh165duH//PmxtbREcHIz33nuvVvtVWKTC31ful/uauamhJiG2tTRFc82/mSATERERVaRJZEgKhQKhoaFo164dIiMjkZqaiiVLliA/Px/z5s2rdN3o6GgsX74cs2bNgpubGzZt2oSwsDDs2rULTk5OAIDU1FTExMTA09MTXbp0wdWrV8utS61WY8qUKUhKSsKbb76J1q1bIyUlBYmJibXeN2sLY7w60BX3M/LwQJGHdEU+HijykVtQjOy8ImTnFeHmvaxy160wQbYyha3cBMZGslq3i4iIiKixahIJ8NatW5GTk4MVK1bAysoKAKBSqbBgwQKEh4fD3t6+3PUKCgoQFRWFsLAwjB8/HgDg6+uLoKAgrF27FvPnzwcAuLm54ejRowCAyMjIChPgHTt24Ny5c4iJiUGLFi3qZN+MjQwwqHsbFBertcpz84vwQJGvSYhL/qtZgmzRzPDf3mLTRxLlf/9mgkxERERNVJNIgA8dOoSePXtqkl8ACA4Oxscff4wjR45gxIgR5a53+vRpZGdnIzg4WFNmZGSEwMBA7N+/X1MmlVbvXsHvv/8eQUFBdZb8VqaZiSHamBiijb1Fua9XlCCX/j+vQIWs3CJk5RYh8S4TZCIiItIfTSIBTkhIwMiRI7XK5HI57OzskJCQUOl6AODs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V5gAExEREZFeYQJMRERERHqFCTARERER6RUmwERERESkV5gAExEREZFeYQJMRERERHqFCTARERER6RUmwERERESkV5gAExEREZFeYQJMRERERHqFCTARERER6RUmwERERESkV5gAExEREZFeYQJMRERERHqFCTARERER6RUmwERERESkV5pMAhwfH4/XXnsNXl5e8Pf3R0REBAoLC6tcTxAErF69Gv369YOnpydGjx6Ns2fPllkuNTUV06ZNg7e3N7p3744PPvgA2dnZZZY7ePAgnn/+eXh4eGDQoEHYsWNHXeweEREREdWRJpEAKxQKhIaGoqioCJGRkZgxYwa2b9+OJUuWVLludHQ0li9fjvHjxyMqKgp2dnYICwtDUlKSZpmioiJMnDgRN2/exLJlyzB//nwcPnwY77zzjlZdcXFxePPNN+Hl5YXo6GgEBwfjgw8+QGxsbJ3vMxERERHVjkFDN6AubN26FTk5OVixYgWsrKwAACqVCgsWLEB4eDjs7e3LXa+goABRUVEICwvD+PHjAQC+vr4ICgrC2rVrMX/+fADA3r17cf36dcTExMDZ2RkAIJfLMWHCBJw/fx6enp4AgK+//hqenp74z3/+AwB4+umnkZSUhOXLlyMoKKj+3gAiIiIiqrYm0QN86NAh9OzZU5P8AkBwcDDUajWOHDlS4XqnT59GdnY2goODNWVGRkYIDAzEoUOHtOp3c3PTJL8A4O/vDysrK/z5558AgMLCQpw4caJMovvcc88hPj4ed+7cedLdJCIiIqI60CR6gBMSEjBy5EitMrlcDjs7OyQkJFS6HgCtxBYAXFxcsGHDBuTn58PExAQJCQlllpFIJGjfvr2mjtu3b6OoqKjcukq31bp161rtn6WlKQShVqtSHZJISv7PeDQ8xkI8GAvxYCzEQyqVNHQTqApNIgFWKpWQy+Vlyi0tLaFQKCpdz8jICMbGxlrlcrkcgiBAoVDAxMQESqUSFhYWldZf+v/H21H6d2XtqIpU2iQ66psMxkM8GAvxYCzEg7Egqho/JURERESkV5pEAiyXy5GVlVWmXKFQwNLSstL1CgsLUVBQoFWuVCohkUg068rl8nKnPHu0/tL/P94OpVKp9ToRERERNawmkQA7OzuXGeublZWFtLS0MmNyH18PABITE7XKExIS4ODgABMTkwrrFwQBiYmJmjratGkDQ0PDMstVNM6YiIiIiBpGk0iAAwICcPToUU1vKwDExsZCKpXC39+/wvV8fHxgbm6OPXv2aMqKioqwb98+BAQEaNV/5coV3Lx5U1N27NgxPHz4EH379gVQMntEjx49sHfvXq1txMTEwMXFpdY3wBERERFR3ZIIQuO/V1ShUGDw4MFo3749wsPDkZqaiiVLlmDo0KGYN2+eZrnQ0FCkpKRg//79mrLVq1cjMjISs2bNgqurK7Zs2YLDhw9j165dcHJyAlCSFI8YMQIAMHPmTOTl5SEiIgJubm6IiorS1BUXF4dx48bhpZdeQnBwME6cOIGvvvoKn3/+udZUa0RERETUcJpEAgyUPAp54cKFOHPmDMzMzDBs2DDMmDEDRkZGmmVCQkKQnJyMgwcPaspKH4W8efNmZGRkwN3dHXPnzoW3t7dW/ampqVi0aBEOHz4MAwMDBAYG4v3334e5ubnWcr/99hu++OILJCYmwsHBAZMnT8aoUaPqd+eJiIiIqNqaTAJMRERERFQdTWIMMBERERFRdTEBJiIiIiK9wgSYiIiIiPQKE2AiIiIi0itMgImIiIhIrzABJiIiIiK9wgS4nuzZswdvvPEGAgIC4OXlhWHDhuGHH37A47POff/99xg0aBA8PDzw/PPP4/fff9d6vbCwEBEREXj11Vfh5eUFNzc3ZGRklNne1q1bERYWBn9/f/j4+OCll17CgQMH6nUfGxNdx+NR9+7dg7e3d7WW1QcNEQu1Wo3169cjKCgIXbp0gb+/P955551628fGoiFi8f3332Po0KHw8vJC37598eGHHyI9Pb3e9rGxqKtYnD9/HnPnzkVgYCC6du2KgQMHYtmyZcjNzS2zzdOnT2P06NHw9PRE//79sXr16jLb00e6jgWv3w2DCXA9Wb9+PUxNTTFnzhx8/fXXCAgIwEcffYSVK1dqlvn111/x0UcfITg4GNHR0fDy8sKbb76Js2fPapbJz8/H999/D2NjY/j6+la4vVWrVsHBwQHz589HZGQk3NzcMHXqVOzcubM+d7PR0HU8HrVkyRI0a9asrnep0WqIWMybNw/R0dEICQnBunXr8P7778PS0rK+drHR0HUsfvrpJ3z44Yfo06cPvv76a7z11lv4448/MHXq1PrczUahrmKxZ88e3Lp1CxMnTsTq1asRGhqK7du34/XXX9fa3q1btzBhwgTY2dkhKioKoaGhWL58OdatW6erXRYtXceC1+8GIlC9SE9PL1P24YcfCj4+PoJKpRIEQRAGDhwozJw5U2uZ0aNHCxMnTtQqU6vVgiAIwo4dOwRXV9dy6y6v7LXXXhOGDBlS631oSnQdj1JHjx4VunfvLqxdu7bKZfWFrmNx9OhRoVOnTsKVK1fqaheaDF3HIiwsTBg7dqxW2Q8//CC4uroKKSkpT7QvjV1dxaK8en7++WfB1dVVuHDhgqbso48+Evr37y8UFBRoypYtWyb4+flplekjXceC1++GwR7gemJjY1OmzN3dHdnZ2cjNzUVSUhJu3ryJ4OBgrWWee+45HDt2DIWFhZoyiURS6+3dv3+/Fq1venQdDwAoKirCwoULMW3aNFhZWT1R+5sSXcdi+/bt6N69O9zc3J688U2MrmNRXFxc5vHxFhYWAKD3P73XVSzKq6dTp04AoHU9OHToEJ555hkYGRlp1aVUKnHmzJk62afGStex4PW7YTAB1qFTp07B3t4e5ubmSEhIAAC0b99eaxkXFxcUFRUhKSmpTrbn7Oz8xPU0VfUdj2+//RYymQwvv/xynbS3KavPWJw7dw7Ozs74v//7P/j5+cHT0xMTJkxAYmJinbW/KanPWIwaNQp//fUXYmNjkZ2djevXr2PVqlXo378/HBwc6mwfmoq6isWpU6cAQHM9yM3Nxd27d8tcH5ydnSGRSDTbov+pr1hUthyv3/XLoKEboC/i4uIQExOD9957DwCgUCgAAHK5XGu50r9LX6+t3bt348yZM1pjluh/6jseqampWLlyJVauXAmZTFYHLW666jsWaWlp+PHHH9GhQwd89tlnKCoqwueff44JEyZgz549MDY2roO9aBrqOxZDhw5FXl4eZs2ahaKiIgBAr1698Pnnnz9p05ucuopFRkYGIiMj8cwzz6Bdu3YAgKysrHLrMjIygqmp6RNff5qa+oxFeXj91g0mwDpw7949zJgxAz169MC4cePqfXtXrlzBxx9/jBEjRuDZZ5+t9+01NrqIR0REBPz9/dGzZ896qb+p0EUsBEGASqXC119/DVtbWwAlPTWDBw/G7t27MWrUqHrZbmOji1js27cPS5YswRtvvIFu3bohJSUFy5cvx9tvv41Vq1ZVe3hRU1dXsSgqKsLMmTMBAPPnz6+j1ukXXceC12/dYQJcz5RKJSZNmgQrKytERkZCKi0ZdVJ6B3pWVhbs7Oy0ln/09ZpKTk7GpEmT4Onpif/85z9P2PqmRxfxOHPmDPbu3Yvt27dr1s/LywMA5OTkwNTUFKampnWyP42Zrj4bcrkcLVu21CS/QMnPjy1btsSNGzeedDeaBF3EQhAEfPzxx3jppZe0Zn1wcnLCK6+8giNHjqB37951sTuNWl3FQhAEvP/++zh//jw2b96MFi1aaF4rHXdd2hNcqrCwEHl5eZwh5V+6iMWjeP3WLSbA9Sg/Px/h4eHIysrCtm3/397dhTT1/3EAf1v/SdmDOYklbSv5LSYLyXnhKFy0HiANdRkKWlfZSDI1qwvtouhiNiGJkUppT1gYQhZ5ciop0kWkN2VSppYaGcPKjlIL8/l/Ee3Hfvb7UT7M8rxfIOg5noevH7bz9pzPOSt3v+kAf/f/dHV1efT5dHV1QSaTQaVS/fL2RFFESkoKAgMDUVBQAJlMNv1BzCPeqkd3dzdGRkawe/fuSfO2b9+O6OhoyV/y9eZrQ6PRwOVy/XDe0NDQFPZ+fvFWLURRhCiKCAkJ8Zj+/aagN2/eTGcY88JM1iIvLw/V1dUoKSmZ9Df38/NDUFDQpF7f7u5uTExMsPcU3qvFdzx+ex9vgpslo6OjOHLkCLq6unDp0iUoFAqP+SqVCmvXrkVNTY3HdIfDgY0bN3rcmfszvnz5AovFgpGRERQXF0+601rqvFkPo9GI0tJSjy+LxQIAKCwslPwzT7392jCZTHj16hU+fPjgntbZ2Yne3l6sX79+6gOZB7xZC7lcjsWLF6O1tdVj+vPnzwEAq1evnuIo5oeZrEVxcTGuXbsGm832r21YmzdvRn19vbsX+/u6li9fDr1eP4Mj+/N4uxY8fs8NngGeJadPn0ZDQwOys7Phcrk8Ho6t0+ng6+uL9PR0HD9+HGq1GgaDAQ6HAy0tLbhx44bHuh48eIDBwUE8e/YMANDQ0IAlS5ZAo9FAo9EAANLT09HW1gar1Qqn0wmn0+lePiwsbNbH+7vzZj1WrlzpcVkM+HZpCwDCw8N/+MgbKfH2ayMhIQHXr1/HwYMHcejQIYyMjMBut0OtVmPXrl1eG/fvyJu18PHxQWJiIsrKyrB06VJ3D3BBQQHWrVsn+X75maqFIAjIz89HbGwslEqlx3rUarX7/SclJQWCIODYsWNISkpCR0cHLl++jKysrF/+J3O+8XYtePyeGz4TUn/44izZunWrO/T8U319PZRKJYBvH6VYUlICp9OJ4OBgHD16FCaT6afWdfjwYaSnpwPAfz7jtL29farDmDe8XY9/un37NnJycvDo0SPJB+C5qEVPTw+sViuamprg4+ODyMhInDhxAqtWrZrBkf15vF2L4eFhXLlyBXfv3oXT6URAQAAMBgOysrJYixmqRXZ29r9+gtiZM2cQHx/v/vnx48ew2Wx48eIF5HI59u7dC4vFIvmbEb1dCx6/5wYDMBERERFJCnuAiYiIiEhSGICJiIiISFIYgImIiIhIUhiAiYiIiEhSGICJiIiISFIYgImIiIhIUhiAiYiIiEhSGICJiIiISFIYgImIiIhIUhiAiYiIiEhSGICJiIiISFIYgImI/mBDQ0MYHx+f690gIvqjMAATEf2ixsZGaLVa3L9/f9I8QRCg1Wrx5MkTAEBnZycyMjIQERGB0NBQxMfHo76+3mOZgYEB5OXlISYmBnq9HuHh4Thw4ADa2to8fq+pqQlarRZVVVU4d+4cjEYjNmzYAJfLNXuDJSKah/431ztARPSnMRgMCAoKgiAI2LFjh8c8QRCgVquh1+vx8uVLJCUlQaFQwGKxwM/PD9XV1UhLS8P58+fdy/b09KCurg47d+6EUqlEX18fysvLsW/fPlRVVUGhUHhso6ioCDKZDCkpKRgeHoZMJvPa2ImI5gMGYCKiX+Tj44PY2FhcvXoVnz9/xrJlywAAoiji4cOHSE1NBQBYrVYEBQWhoqICvr6+AIDk5GQkJSXh7Nmz7gCs1WpRW1uLBQv+vigXFxeHqKgo3Lp1C2lpaR7bHxoaQkVFBRYtWuSN4RIRzTtsgSAimoK4uDgMDw+jpqbGPc3hcGB0dBSxsbEYGBhAY2MjoqKi4HK5IIoiRFFEf38/IiMj8fr1a7x79w4A4Ovr6w6/Y2Nj6O/vh5+fH4KDg9Ha2jpp22azmeGXiGgaeAaYiGgK/vrrL4SGhkIQBCQkJAD41v4QFhaGNWvWoKWlBRMTE7Db7bDb7T9cx8ePH6FQKDA+Po7S0lKUlZXh7du3GBsbc//OihUrJi2nVCpnZUxERFLBAExENEVmsxlWqxW9vb0YHh5Gc3MzTp48CQDuJzPs378fRqPxh8ur1WoAwIULF2C327Fnzx5kZmbC398fCxYsQG5uLiYmJiYtx7O/RETTwwBMRDRF0dHRsNlsuHfvHr5+/QqZTIaoqCgAgEqlAgDIZDJs2rTpP9dTW1sLg8GA3Nxcj+mfPn1CQEDA7Ow8EZGEsQeYiGiK5HI5jEYjKisrIQgCIiMjIZfLAQCBgYGIiIhAeXk53r9/P2lZURTd3y9cuHDSmd7q6mp3jzAREc0sngEmIpoGs9mMjIwMAEBmZqbHvFOnTiE5ORkxMTFITEyESqVCX18fmpub0dvbi8rKSgDAli1bUFhYiJycHOj1enR0dEAQBPdZZCIimlkMwERE02AymeDv74/x8XFs27bNY55Go0FFRQUKCgpw584dDAwMQC6XQ6fTeTzaLDU1FYODgxAEAQ6HAzqdDhcvXkR+fr63h0NEJAk+Ez+6w4KIiH7K6OgojEYjTCbTpB5eIiL6PbEHmIhoGurq6iCKIsxm81zvChER/SS2QBARTcHTp0/R3t6OoqIi6HQ6REREzPUuERHRT2IAJiKagps3b6KyshIhISGw2WxzvTtERPQL2ANMRERERJLCHmAiIiIikhQGYCIiIiKSFAZgIiIiIpIUBmAiIiIikhQGYCIiIiKSFAZgIiIiIpIUBmAiIiIikhQGYCIiIiKSlP8DyDu3vqA38BkAAAAASUVORK5CYII=", 466 | "text/plain": [ 467 | "
" 468 | ] 469 | }, 470 | "metadata": {}, 471 | "output_type": "display_data" 472 | } 473 | ], 474 | "source": [ 475 | "relative_japan = all_counts_japan / all_counts_all_countries\n", 476 | "g = sns.lineplot(relative_japan)\n", 477 | "g.set_ylim(bottom=0)\n", 478 | "g.set_xlim(2012, 2022)\n", 479 | "g.set_title(\"Number of works in Japanese journals relative to total number of works in journals\")\n", 480 | "g.set_ylabel(\"Relative number of works\")" 481 | ] 482 | }, 483 | { 484 | "attachments": {}, 485 | "cell_type": "markdown", 486 | "metadata": {}, 487 | "source": [ 488 | "We have shown that the relative number of works with Japanese sources has been declining over the last ten years. One possible explanation for this is that Japanese authors are publishing less in Japanese sources. Our next step could be to look at papers with Japanese *authors* (institutional affiliations in Japan), and see if they are increasingly publishing in non-Japanese sources. Looking at publications by language would also be interesting—this information [is now available in OpenAlex](https://docs.openalex.org/api-entities/works/work-object#language)." 489 | ] 490 | }, 491 | { 492 | "cell_type": "markdown", 493 | "metadata": {}, 494 | "source": [] 495 | } 496 | ], 497 | "metadata": { 498 | "kernelspec": { 499 | "display_name": "venv", 500 | "language": "python", 501 | "name": "python3" 502 | }, 503 | "language_info": { 504 | "codemirror_mode": { 505 | "name": "ipython", 506 | "version": 3 507 | }, 508 | "file_extension": ".py", 509 | "mimetype": "text/x-python", 510 | "name": "python", 511 | "nbconvert_exporter": "python", 512 | "pygments_lexer": "ipython3", 513 | "version": "3.10.9" 514 | }, 515 | "orig_nbformat": 4, 516 | "vscode": { 517 | "interpreter": { 518 | "hash": "271691dbc4cdb85f541c883090ff5a004cbd8b9c207c2cfed84437fce4e65fdb" 519 | } 520 | } 521 | }, 522 | "nbformat": 4, 523 | "nbformat_minor": 2 524 | } 525 | -------------------------------------------------------------------------------- /notebooks/institutions/oa-percentage.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "cd151571-2976-4e81-a1e2-2cf716466271", 6 | "metadata": {}, 7 | "source": [ 8 | "
\n", 9 | " \n", 10 | " \"OpenAlex\n", 11 | " \n", 12 | "
" 13 | ] 14 | }, 15 | { 16 | "cell_type": "markdown", 17 | "id": "e141e5ff-f69d-4563-a556-ce1e746aef14", 18 | "metadata": {}, 19 | "source": [ 20 | "# Monitoring Open Access publications for a given institution\n", 21 | "\n", 22 | "
\n", 23 | " In this notebook we will query the OpenAlex API to answer the question:\n", 24 | "
\n", 25 | " How many of recent journal articles from a given institution are Open Access? And how many aren't?\n", 26 | "
\n", 27 | " To get to the bottom of this, we will use the following API functionalities: \n", 28 | " filtering and \n", 29 | " grouping\n", 30 | "
\n", 31 | "
\n", 32 | "\n", 33 | "Imagine you would like to track the University of Florida's progress in the transition towards Open Access (OA). How could you do that using OpenAlex?\n", 34 | "\n", 35 | "### Steps\n", 36 | "Let's start by dividing the process into smaller, more manageable steps:\n", 37 | "1. First we need to get all recent journal articles from the University of Florida\n", 38 | "2. Next we divide them into open and closed access\n", 39 | "3. Finally we count the publications in each category\n", 40 | "4. Additionally we can put the numbers into a plot to visualize our findings\n", 41 | "\n", 42 | "### Input\n", 43 | "The only input we need is an identifier for the institution and here we opted for its [ROR ID](https://ror.org/). \n", 44 | "If we look up the University of Florida in the ROR registry we find its ROR ID is https://ror.org/02y3ad647:" 45 | ] 46 | }, 47 | { 48 | "cell_type": "code", 49 | "execution_count": null, 50 | "id": "558a64e5-2fea-44af-b0be-35d67eb23553", 51 | "metadata": { 52 | "tags": [] 53 | }, 54 | "outputs": [], 55 | "source": [ 56 | "#input\n", 57 | "ror = 'https://ror.org/02y3ad647'" 58 | ] 59 | }, 60 | { 61 | "cell_type": "markdown", 62 | "id": "f7216c04-3575-4d14-a4ed-5bb66401f5e4", 63 | "metadata": {}, 64 | "source": [ 65 | "All set, so let's dive in!\n", 66 | "\n", 67 | "
\n", 68 | "\n", 69 | "## 1. Get all recent journal articles from the University of Florida\n", 70 | "The first step in querying OpenAlex is always to build the URL to get exactly the data we need. We need to ask two things:\n", 71 | "1. About which entity type (author, concept, institution, venue, work) do we want data? \n", 72 | "* --> Since we want to query for metadata about \"_journal articles_\", the entity type should be `works`.\n", 73 | "\n", 74 | "2. What are the criteria the works need to fulfill to fit our purpose? \n", 75 | "* Here we need to look into the list of available [filters for works](https://docs.openalex.org/api-entities/works/filter-works) and select the appropriate ones. \n", 76 | "* --> We want to query for \"_all recent journal articles from the University of Florida_\", so we will filter for the works that:\n", 77 | " * were published in the last 10 years (=recent): `from_publication_date:2012-08-24`,\n", 78 | " * are specified as articles: `type:article`,\n", 79 | " * have at least one [authorship](https://docs.openalex.org/api-entities/works/work-object#authorships) affiliation with the University of Florida: `institutions.ror:https://ror.org/02y3ad647`,\n", 80 | " * are not [paratext](https://docs.openalex.org/api-entities/works/work-object#is_paratext): `is_paratext:false`\n", 81 | "\n", 82 | "
\n", 83 | "\n", 84 | "Now we need to **put the URL together** from these parts as follows: \n", 85 | "* Starting point is the base URL of the OpenAlex API: `https://api.openalex.org/`\n", 86 | "* We append the entity type to it: `https://api.openalex.org/works`\n", 87 | "* All criteria need to go into the query parameter `filter` that is added after a question mark: `https://api.openalex.org/works?filter=`\n", 88 | "* To construct the filter value we take the criteria we specified and concatenate them using commas as separators: \n", 89 | "`https://api.openalex.org/works?filter=institutions.ror:https://ror.org/02y3ad647,type:article,from_publication_date:2012-08-24,is_paratext:false`\n", 90 | "\n", 91 | "With this URL we can get all recent journal articles from the University of Florida!" 92 | ] 93 | }, 94 | { 95 | "cell_type": "code", 96 | "execution_count": null, 97 | "id": "98345207-4fa0-4799-ba03-5179491adbda", 98 | "metadata": { 99 | "tags": [] 100 | }, 101 | "outputs": [], 102 | "source": [ 103 | "def build_institution_works_url(ror):\n", 104 | " # specify endpoint\n", 105 | " endpoint = 'works'\n", 106 | "\n", 107 | " # build the 'filter' parameter\n", 108 | " filters = (\n", 109 | " f'institutions.ror:{ror}',\n", 110 | " 'is_paratext:false',\n", 111 | " 'type:article', \n", 112 | " 'from_publication_date:2012-08-24'\n", 113 | " )\n", 114 | " \n", 115 | " # put the URL together\n", 116 | " return f'https://api.openalex.org/{endpoint}?filter={\",\".join(filters)}'\n", 117 | "\n", 118 | "filtered_works_url = build_institution_works_url(ror)\n", 119 | "print(f'complete URL with filters:\\n{filtered_works_url}')" 120 | ] 121 | }, 122 | { 123 | "cell_type": "markdown", 124 | "id": "58b725da-3088-4e5b-b69a-2b3c18487956", 125 | "metadata": {}, 126 | "source": [ 127 | "
\n", 128 | "\n", 129 | "## 2. Divide them into open and closed access\n", 130 | "To get the number of open and closed works, we need to find an additional attribute that we can use to divide the retrieved works further into these categories. Fortunately OpenAlex includes information about the access status of a work in its metadata via the nested [OpenAccess object](https://docs.openalex.org/api-entities/works/work-object#the-openaccess-object). It is made up of the three attributes\n", 131 | "* `is_oa` _(Boolean): True if this work is Open Access._\n", 132 | "* `oa_status` _(String): The Open Access (OA) status of this work. Possible values are gold, green, hybrid, bronze, closed._\n", 133 | "* `oa_url` _(String): The best Open Access (OA) URL for this work._\n", 134 | "\n", 135 | "**-->`is_oa` seems to be exactly the criterion we are looking for!**\n", 136 | "\n", 137 | "\n", 138 | "#### Shortcut `group_by`\n", 139 | "So one way to get the number of open and closed works would be to add `is_oa` as an additional filter to our query and query OpenAlex for each value in its range `{true, false}` to get its resulting count of works, e.g.\n", 140 | "* `filter=...,is_oa:true`\n", 141 | "* `filter=...,is_oa:false`\n", 142 | "\n", 143 | "\n", 144 | "But wait! Isn't that exactly what `group_by` does? \n", 145 | "Yes, absolutely, the `group_by` parameter takes one attribute as input, divides the list of results based on the attribute's values and returns each of their counts. What a time saver!\n", 146 | "\n", 147 | "Let's add `group_by=is_oa` as an additional query parameter to the end of our URL:" 148 | ] 149 | }, 150 | { 151 | "cell_type": "code", 152 | "execution_count": null, 153 | "id": "bc0569ad-cd73-460e-8472-27d92c310ff6", 154 | "metadata": {}, 155 | "outputs": [], 156 | "source": [ 157 | "group_by_param = 'group_by=is_oa'\n", 158 | "\n", 159 | "work_groups_url = f'{filtered_works_url}&{group_by_param}'\n", 160 | "print(f'complete URL with group_by:\\n{work_groups_url}')" 161 | ] 162 | }, 163 | { 164 | "cell_type": "markdown", 165 | "id": "bc97ecd0-2605-4702-b48b-c83d9d090a95", 166 | "metadata": {}, 167 | "source": [ 168 | "
\n", 169 | "\n", 170 | "## 3. Count the number of works in each group\n", 171 | "\n", 172 | "After putting together the URL, we can query OpenAlex for the groups of publications and retrieve the following two groups:" 173 | ] 174 | }, 175 | { 176 | "cell_type": "code", 177 | "execution_count": null, 178 | "id": "a7768120-47af-4a3a-bfda-f5cd7fed3046", 179 | "metadata": {}, 180 | "outputs": [], 181 | "source": [ 182 | "import requests, json\n", 183 | "response = requests.get(work_groups_url).json()\n", 184 | "\n", 185 | "work_groups = response['group_by']\n", 186 | "print(json.dumps(work_groups, indent=2))" 187 | ] 188 | }, 189 | { 190 | "cell_type": "markdown", 191 | "id": "8dc253db-a7b9-4cc3-93eb-5ab0d20f471f", 192 | "metadata": {}, 193 | "source": [ 194 | "Each group is made up of its `key` that contains the attribute value for the `group_by` attribute, in our case `is_oa`, and its `count` of entities belonging to the group. Given these data we can already answer our initial question: \n", 195 | "> _How many of recent journal articles from a given institution are Open Access? And how many aren't?_" 196 | ] 197 | }, 198 | { 199 | "cell_type": "code", 200 | "execution_count": null, 201 | "id": "cbe98e13-7716-4967-ac93-a3a7b420c3a8", 202 | "metadata": {}, 203 | "outputs": [], 204 | "source": [ 205 | "def calculate_open_closed_counts(work_groups):\n", 206 | " open_works_count = 0\n", 207 | " closed_works_count = 0\n", 208 | " for index, group in enumerate(work_groups):\n", 209 | " print(f\"--> Group {index+1} includes all works where `is_oa` is {group['key']} and has a count of {group['count']} publications.\")\n", 210 | "\n", 211 | " if group['key']==\"true\":\n", 212 | " open_works_count += group['count']\n", 213 | " else: \n", 214 | " closed_works_count += group['count']\n", 215 | " \n", 216 | " return open_works_count, closed_works_count\n", 217 | "\n", 218 | "open_works_count, closed_works_count = calculate_open_closed_counts(work_groups)\n", 219 | "total_works_count = open_works_count + closed_works_count\n", 220 | "\n", 221 | "if total_works_count > 0:\n", 222 | " print('That makes an OA percentage of %f' % (100 * open_works_count/total_works_count))\n", 223 | "else:\n", 224 | " print('OA percentage can`t be determined, no publications in result')" 225 | ] 226 | }, 227 | { 228 | "cell_type": "markdown", 229 | "id": "ead1c279-ffd5-41bf-bd66-dd1cf9f8b3ce", 230 | "metadata": {}, 231 | "source": [ 232 | "
\n", 233 | "\n", 234 | "## 4. Plot the data (optional)\n", 235 | "Last but not least we can put the data into a visually appealing plot. How about a donut plot?" 236 | ] 237 | }, 238 | { 239 | "cell_type": "code", 240 | "execution_count": null, 241 | "id": "9b1f4de3-3a74-4a30-98a9-597e27778f96", 242 | "metadata": {}, 243 | "outputs": [], 244 | "source": [ 245 | "def create_donut_plot(open_works_count, closed_works_count):\n", 246 | " import matplotlib.pyplot as plt\n", 247 | " plt.rcParams[\"figure.figsize\"] = (8,5.5)\n", 248 | "\n", 249 | " # set labels and their respective values\n", 250 | " groups = ['Open Access', 'Closed Access']\n", 251 | " counts = [open_works_count, closed_works_count]\n", 252 | "\n", 253 | " # some visual settings\n", 254 | " colors = ['#23c552', '#f84f31']\n", 255 | " explode = (0.01, 0.01)\n", 256 | "\n", 257 | " # pie chart\n", 258 | " plt.pie(counts, colors=colors, labels=groups,\n", 259 | " autopct='%1.1f%%', pctdistance=0.85,\n", 260 | " explode=explode, textprops={'fontsize': 14})\n", 261 | "\n", 262 | " # make it a donut (draw circle in the middle)\n", 263 | " centre_circle = plt.Circle((0, 0), 0.70, fc='white')\n", 264 | " fig = plt.gcf()\n", 265 | " fig.gca().add_artist(centre_circle)\n", 266 | " \n", 267 | " # display chart\n", 268 | " plt.show()\n", 269 | "\n", 270 | "# create donut chart from open/closed counts\n", 271 | "create_donut_plot(open_works_count, closed_works_count)" 272 | ] 273 | }, 274 | { 275 | "cell_type": "markdown", 276 | "id": "403a64ee-56e5-41d6-b472-cd63765f7217", 277 | "metadata": {}, 278 | "source": [ 279 | "---\n", 280 | "Feel free to use the notebook and determine the percentage of Open Access works for your institution or tweak the filters to fit your analysis. \n", 281 | "\n", 282 | "Happy exploring! 😎" 283 | ] 284 | } 285 | ], 286 | "metadata": { 287 | "kernelspec": { 288 | "display_name": "Python 3 (ipykernel)", 289 | "language": "python", 290 | "name": "python3" 291 | }, 292 | "language_info": { 293 | "codemirror_mode": { 294 | "name": "ipython", 295 | "version": 3 296 | }, 297 | "file_extension": ".py", 298 | "mimetype": "text/x-python", 299 | "name": "python", 300 | "nbconvert_exporter": "python", 301 | "pygments_lexer": "ipython3", 302 | "version": "3.10.9" 303 | } 304 | }, 305 | "nbformat": 4, 306 | "nbformat_minor": 5 307 | } 308 | -------------------------------------------------------------------------------- /notebooks/openalex_works/openalex_works.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "attachments": {}, 5 | "cell_type": "markdown", 6 | "metadata": {}, 7 | "source": [ 8 | "# What impact has OpenAlex had so far?\n", 9 | "\n", 10 | "## Citation analysis\n", 11 | "\n", 12 | "Let's start by looking at the paper that OpenAlex asks researchers to cite:\n", 13 | "\n", 14 | "> Priem, J., Piwowar, H., & Orr, R. (2022). OpenAlex: A fully-open index of scholarly works, authors, venues, institutions, and concepts. ArXiv. https://arxiv.org/abs/2205.01833" 15 | ] 16 | }, 17 | { 18 | "cell_type": "code", 19 | "execution_count": 3, 20 | "metadata": {}, 21 | "outputs": [], 22 | "source": [ 23 | "import requests\n", 24 | "\n", 25 | "doi = '10.48550/arXiv.2205.01833'\n", 26 | "\n", 27 | "url = f'https://api.openalex.org/works?filter=doi:{doi}'\n", 28 | "r = requests.get(url)\n", 29 | "response_data = r.json()\n", 30 | "openalex_article = response_data['results'][0]" 31 | ] 32 | }, 33 | { 34 | "cell_type": "code", 35 | "execution_count": 4, 36 | "metadata": {}, 37 | "outputs": [ 38 | { 39 | "name": "stdout", 40 | "output_type": "stream", 41 | "text": [ 42 | "Within the OpenAlex data, the OpenAlex paper has 3 (incoming) citations.\n" 43 | ] 44 | } 45 | ], 46 | "source": [ 47 | "print(f\"Within the OpenAlex data, the OpenAlex paper has {openalex_article['cited_by_count']} (incoming) citations.\")" 48 | ] 49 | }, 50 | { 51 | "attachments": {}, 52 | "cell_type": "markdown", 53 | "metadata": {}, 54 | "source": [ 55 | "The number of papers citing OpenAlex seems low. Let's try Semantic Scholar's data for the same article." 56 | ] 57 | }, 58 | { 59 | "cell_type": "code", 60 | "execution_count": 8, 61 | "metadata": {}, 62 | "outputs": [], 63 | "source": [ 64 | "s2_api_endpoint = \"https://api.semanticscholar.org/graph/v1/paper\"\n", 65 | "fields = ['citationCount', 'citations.title', 'citations.year', 'citations.publicationDate', 'citations.citationCount', 'citations.externalIds']\n", 66 | "params = {\n", 67 | " 'fields': \",\".join(fields)\n", 68 | "}\n", 69 | "r = requests.get(f\"{s2_api_endpoint}/DOI:{doi}\", params=params)\n", 70 | "s2_article = r.json()" 71 | ] 72 | }, 73 | { 74 | "cell_type": "code", 75 | "execution_count": 9, 76 | "metadata": {}, 77 | "outputs": [ 78 | { 79 | "data": { 80 | "text/plain": [ 81 | "{'error': 'Paper with id DOI:10.48550/arXiv.2205.01833 not found'}" 82 | ] 83 | }, 84 | "execution_count": 9, 85 | "metadata": {}, 86 | "output_type": "execute_result" 87 | } 88 | ], 89 | "source": [ 90 | "s2_article" 91 | ] 92 | }, 93 | { 94 | "cell_type": "code", 95 | "execution_count": 12, 96 | "metadata": {}, 97 | "outputs": [], 98 | "source": [ 99 | "s2_api_endpoint = \"https://api.semanticscholar.org/graph/v1/paper\"\n", 100 | "fields = ['citationCount', 'citations.title', 'citations.year', 'citations.publicationDate', 'citations.citationCount', 'citations.externalIds']\n", 101 | "params = {\n", 102 | " 'fields': \",\".join(fields)\n", 103 | "}\n", 104 | "arxiv_id = '2205.01833'\n", 105 | "r = requests.get(f\"{s2_api_endpoint}/ARXIV:{arxiv_id}\", params=params)\n", 106 | "s2_article = r.json()" 107 | ] 108 | }, 109 | { 110 | "cell_type": "code", 111 | "execution_count": 14, 112 | "metadata": {}, 113 | "outputs": [ 114 | { 115 | "name": "stdout", 116 | "output_type": "stream", 117 | "text": [ 118 | "Within the Semantic Scholar data, the OpenAlex paper has 16 (incoming) citations.\n" 119 | ] 120 | } 121 | ], 122 | "source": [ 123 | "print(f\"Within the Semantic Scholar data, the OpenAlex paper has {s2_article['citationCount']} (incoming) citations.\")" 124 | ] 125 | }, 126 | { 127 | "cell_type": "code", 128 | "execution_count": 16, 129 | "metadata": {}, 130 | "outputs": [], 131 | "source": [ 132 | "citations_dois = [citing_article['externalIds'].get('DOI') for citing_article in s2_article['citations']]" 133 | ] 134 | }, 135 | { 136 | "cell_type": "code", 137 | "execution_count": 17, 138 | "metadata": {}, 139 | "outputs": [ 140 | { 141 | "data": { 142 | "text/plain": [ 143 | "['10.48550/arXiv.2302.02231',\n", 144 | " '10.48550/arXiv.2301.01502',\n", 145 | " '10.48550/arXiv.2210.14871',\n", 146 | " '10.1109/TVCG.2022.3209422',\n", 147 | " '10.1016/j.cosrev.2022.100531',\n", 148 | " '10.3389/frma.2022.1010504',\n", 149 | " '10.48550/arXiv.2211.04429',\n", 150 | " '10.48550/arXiv.2210.00356',\n", 151 | " '10.1108/jd-04-2022-0083',\n", 152 | " '10.48550/arXiv.2209.09246',\n", 153 | " '10.1007/978-3-031-16802-4_52',\n", 154 | " '10.48550/arXiv.2208.11065',\n", 155 | " '10.1007/s11192-022-04446-y',\n", 156 | " '10.5281/zenodo.6975102',\n", 157 | " '10.1162/qss_a_00222',\n", 158 | " '10.1162/qss_a_00200']" 159 | ] 160 | }, 161 | "execution_count": 17, 162 | "metadata": {}, 163 | "output_type": "execute_result" 164 | } 165 | ], 166 | "source": [ 167 | "citations_dois" 168 | ] 169 | }, 170 | { 171 | "cell_type": "code", 172 | "execution_count": 21, 173 | "metadata": {}, 174 | "outputs": [], 175 | "source": [ 176 | "url = f'https://api.openalex.org/works'\n", 177 | "citing_dois_str = \"|\".join(citations_dois)\n", 178 | "params = {\n", 179 | " 'filter': f\"doi:{citing_dois_str}\"\n", 180 | "}\n", 181 | "r = requests.get(url, params=params)\n", 182 | "response_data = r.json()" 183 | ] 184 | }, 185 | { 186 | "cell_type": "code", 187 | "execution_count": 25, 188 | "metadata": {}, 189 | "outputs": [ 190 | { 191 | "name": "stdout", 192 | "output_type": "stream", 193 | "text": [ 194 | "https://doi.org/10.1162/qss_a_00222 2022-11-07 0\n", 195 | "https://doi.org/10.1162/qss_a_00200 2021-09-01 20\n", 196 | "https://doi.org/10.5281/zenodo.6975102 2022-06-28 0\n", 197 | "https://doi.org/10.1007/s11192-022-04446-y 2022-07-15 19\n", 198 | "https://doi.org/10.48550/arxiv.2208.11065 2022-08-23 0\n", 199 | "https://doi.org/10.1007/978-3-031-16802-4_52 2022-01-01 5\n", 200 | "https://doi.org/10.48550/arxiv.2209.09246 2022-09-19 0\n", 201 | "https://doi.org/10.1109/tvcg.2022.3209422 2022-01-01 21\n", 202 | "https://doi.org/10.1108/jd-04-2022-0083 2022-09-21 27\n", 203 | "https://doi.org/10.48550/arxiv.2210.00356 2022-10-01 0\n", 204 | "https://doi.org/10.48550/arxiv.2210.14871 2022-10-26 0\n", 205 | "https://doi.org/10.48550/arxiv.2211.04429 2022-11-08 0\n", 206 | "https://doi.org/10.3389/frma.2022.1010504 2022-11-10 17\n", 207 | "https://doi.org/10.1016/j.cosrev.2022.100531 2023-02-01 155\n", 208 | "https://doi.org/10.48550/arxiv.2301.01502 2023-01-04 0\n", 209 | "https://doi.org/10.48550/arxiv.2302.02231 2023-02-04 0\n" 210 | ] 211 | } 212 | ], 213 | "source": [ 214 | "for result in response_data['results']:\n", 215 | " print(result['doi'], result['publication_date'], len(result['referenced_works']))" 216 | ] 217 | }, 218 | { 219 | "cell_type": "code", 220 | "execution_count": null, 221 | "metadata": {}, 222 | "outputs": [], 223 | "source": [] 224 | }, 225 | { 226 | "cell_type": "code", 227 | "execution_count": null, 228 | "metadata": {}, 229 | "outputs": [], 230 | "source": [] 231 | }, 232 | { 233 | "cell_type": "code", 234 | "execution_count": 6, 235 | "metadata": {}, 236 | "outputs": [ 237 | { 238 | "ename": "MaxTriesExceededException", 239 | "evalue": "Cannot Fetch from Google Scholar.", 240 | "output_type": "error", 241 | "traceback": [ 242 | "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", 243 | "\u001b[0;31mMaxTriesExceededException\u001b[0m Traceback (most recent call last)", 244 | "Cell \u001b[0;32mIn[6], line 10\u001b[0m\n\u001b[1;32m 7\u001b[0m scholarly\u001b[39m.\u001b[39muse_proxy(pg)\n\u001b[1;32m 9\u001b[0m \u001b[39m# Now search Google Scholar from behind a proxy\u001b[39;00m\n\u001b[0;32m---> 10\u001b[0m search_query \u001b[39m=\u001b[39m scholarly\u001b[39m.\u001b[39;49msearch_pubs(\u001b[39mf\u001b[39;49m\u001b[39m'\u001b[39;49m\u001b[39mdoi:\u001b[39;49m\u001b[39m{\u001b[39;49;00mdoi\u001b[39m}\u001b[39;49;00m\u001b[39m'\u001b[39;49m)\n\u001b[1;32m 11\u001b[0m article \u001b[39m=\u001b[39m \u001b[39mnext\u001b[39m(search_query)\n", 245 | "File \u001b[0;32m~/code/ourresearch/openalex-api-tutorials/venv/lib/python3.9/site-packages/scholarly/_scholarly.py:160\u001b[0m, in \u001b[0;36m_Scholarly.search_pubs\u001b[0;34m(self, query, patents, citations, year_low, year_high, sort_by, include_last_year, start_index)\u001b[0m\n\u001b[1;32m 97\u001b[0m \u001b[39m\u001b[39m\u001b[39m\"\"\"Searches by query and returns a generator of Publication objects\u001b[39;00m\n\u001b[1;32m 98\u001b[0m \n\u001b[1;32m 99\u001b[0m \u001b[39m:param query: terms to be searched\u001b[39;00m\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 155\u001b[0m \n\u001b[1;32m 156\u001b[0m \u001b[39m\"\"\"\u001b[39;00m\n\u001b[1;32m 157\u001b[0m url \u001b[39m=\u001b[39m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_construct_url(_PUBSEARCH\u001b[39m.\u001b[39mformat(requests\u001b[39m.\u001b[39mutils\u001b[39m.\u001b[39mquote(query)), patents\u001b[39m=\u001b[39mpatents,\n\u001b[1;32m 158\u001b[0m citations\u001b[39m=\u001b[39mcitations, year_low\u001b[39m=\u001b[39myear_low, year_high\u001b[39m=\u001b[39myear_high,\n\u001b[1;32m 159\u001b[0m sort_by\u001b[39m=\u001b[39msort_by, include_last_year\u001b[39m=\u001b[39minclude_last_year, start_index\u001b[39m=\u001b[39mstart_index)\n\u001b[0;32m--> 160\u001b[0m \u001b[39mreturn\u001b[39;00m \u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49m__nav\u001b[39m.\u001b[39;49msearch_publications(url)\n", 246 | "File \u001b[0;32m~/code/ourresearch/openalex-api-tutorials/venv/lib/python3.9/site-packages/scholarly/_navigator.py:296\u001b[0m, in \u001b[0;36mNavigator.search_publications\u001b[0;34m(self, url)\u001b[0m\n\u001b[1;32m 288\u001b[0m \u001b[39mdef\u001b[39;00m \u001b[39msearch_publications\u001b[39m(\u001b[39mself\u001b[39m, url: \u001b[39mstr\u001b[39m) \u001b[39m-\u001b[39m\u001b[39m>\u001b[39m _SearchScholarIterator:\n\u001b[1;32m 289\u001b[0m \u001b[39m \u001b[39m\u001b[39m\"\"\"Returns a Publication Generator given a url\u001b[39;00m\n\u001b[1;32m 290\u001b[0m \n\u001b[1;32m 291\u001b[0m \u001b[39m :param url: the url where publications can be found.\u001b[39;00m\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 294\u001b[0m \u001b[39m :rtype: {_SearchScholarIterator}\u001b[39;00m\n\u001b[1;32m 295\u001b[0m \u001b[39m \"\"\"\u001b[39;00m\n\u001b[0;32m--> 296\u001b[0m \u001b[39mreturn\u001b[39;00m _SearchScholarIterator(\u001b[39mself\u001b[39;49m, url)\n", 247 | "File \u001b[0;32m~/code/ourresearch/openalex-api-tutorials/venv/lib/python3.9/site-packages/scholarly/publication_parser.py:53\u001b[0m, in \u001b[0;36m_SearchScholarIterator.__init__\u001b[0;34m(self, nav, url)\u001b[0m\n\u001b[1;32m 51\u001b[0m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_pubtype \u001b[39m=\u001b[39m PublicationSource\u001b[39m.\u001b[39mPUBLICATION_SEARCH_SNIPPET \u001b[39mif\u001b[39;00m \u001b[39m\"\u001b[39m\u001b[39m/scholar?\u001b[39m\u001b[39m\"\u001b[39m \u001b[39min\u001b[39;00m url \u001b[39melse\u001b[39;00m PublicationSource\u001b[39m.\u001b[39mJOURNAL_CITATION_LIST\n\u001b[1;32m 52\u001b[0m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_nav \u001b[39m=\u001b[39m nav\n\u001b[0;32m---> 53\u001b[0m \u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49m_load_url(url)\n\u001b[1;32m 54\u001b[0m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mtotal_results \u001b[39m=\u001b[39m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_get_total_results()\n\u001b[1;32m 55\u001b[0m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mpub_parser \u001b[39m=\u001b[39m PublicationParser(\u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_nav)\n", 248 | "File \u001b[0;32m~/code/ourresearch/openalex-api-tutorials/venv/lib/python3.9/site-packages/scholarly/publication_parser.py:59\u001b[0m, in \u001b[0;36m_SearchScholarIterator._load_url\u001b[0;34m(self, url)\u001b[0m\n\u001b[1;32m 57\u001b[0m \u001b[39mdef\u001b[39;00m \u001b[39m_load_url\u001b[39m(\u001b[39mself\u001b[39m, url: \u001b[39mstr\u001b[39m):\n\u001b[1;32m 58\u001b[0m \u001b[39m# this is temporary until setup json file\u001b[39;00m\n\u001b[0;32m---> 59\u001b[0m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_soup \u001b[39m=\u001b[39m \u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49m_nav\u001b[39m.\u001b[39;49m_get_soup(url)\n\u001b[1;32m 60\u001b[0m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_pos \u001b[39m=\u001b[39m \u001b[39m0\u001b[39m\n\u001b[1;32m 61\u001b[0m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_rows \u001b[39m=\u001b[39m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_soup\u001b[39m.\u001b[39mfind_all(\u001b[39m'\u001b[39m\u001b[39mdiv\u001b[39m\u001b[39m'\u001b[39m, class_\u001b[39m=\u001b[39m\u001b[39m'\u001b[39m\u001b[39mgs_r gs_or gs_scl\u001b[39m\u001b[39m'\u001b[39m) \u001b[39m+\u001b[39m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_soup\u001b[39m.\u001b[39mfind_all(\u001b[39m'\u001b[39m\u001b[39mdiv\u001b[39m\u001b[39m'\u001b[39m, class_\u001b[39m=\u001b[39m\u001b[39m'\u001b[39m\u001b[39mgsc_mpat_ttl\u001b[39m\u001b[39m'\u001b[39m)\n", 249 | "File \u001b[0;32m~/code/ourresearch/openalex-api-tutorials/venv/lib/python3.9/site-packages/scholarly/_navigator.py:239\u001b[0m, in \u001b[0;36mNavigator._get_soup\u001b[0;34m(self, url)\u001b[0m\n\u001b[1;32m 237\u001b[0m \u001b[39mdef\u001b[39;00m \u001b[39m_get_soup\u001b[39m(\u001b[39mself\u001b[39m, url: \u001b[39mstr\u001b[39m) \u001b[39m-\u001b[39m\u001b[39m>\u001b[39m BeautifulSoup:\n\u001b[1;32m 238\u001b[0m \u001b[39m \u001b[39m\u001b[39m\"\"\"Return the BeautifulSoup for a page on scholar.google.com\"\"\"\u001b[39;00m\n\u001b[0;32m--> 239\u001b[0m html \u001b[39m=\u001b[39m \u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49m_get_page(\u001b[39m'\u001b[39;49m\u001b[39mhttps://scholar.google.com\u001b[39;49m\u001b[39m{0}\u001b[39;49;00m\u001b[39m'\u001b[39;49m\u001b[39m.\u001b[39;49mformat(url))\n\u001b[1;32m 240\u001b[0m html \u001b[39m=\u001b[39m html\u001b[39m.\u001b[39mreplace(\u001b[39mu\u001b[39m\u001b[39m'\u001b[39m\u001b[39m\\xa0\u001b[39;00m\u001b[39m'\u001b[39m, \u001b[39mu\u001b[39m\u001b[39m'\u001b[39m\u001b[39m \u001b[39m\u001b[39m'\u001b[39m)\n\u001b[1;32m 241\u001b[0m res \u001b[39m=\u001b[39m BeautifulSoup(html, \u001b[39m'\u001b[39m\u001b[39mhtml.parser\u001b[39m\u001b[39m'\u001b[39m)\n", 250 | "File \u001b[0;32m~/code/ourresearch/openalex-api-tutorials/venv/lib/python3.9/site-packages/scholarly/_navigator.py:190\u001b[0m, in \u001b[0;36mNavigator._get_page\u001b[0;34m(self, pagerequest, premium)\u001b[0m\n\u001b[1;32m 188\u001b[0m \u001b[39mreturn\u001b[39;00m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_get_page(pagerequest, \u001b[39mTrue\u001b[39;00m)\n\u001b[1;32m 189\u001b[0m \u001b[39melse\u001b[39;00m:\n\u001b[0;32m--> 190\u001b[0m \u001b[39mraise\u001b[39;00m MaxTriesExceededException(\u001b[39m\"\u001b[39m\u001b[39mCannot Fetch from Google Scholar.\u001b[39m\u001b[39m\"\u001b[39m)\n", 251 | "\u001b[0;31mMaxTriesExceededException\u001b[0m: Cannot Fetch from Google Scholar." 252 | ] 253 | } 254 | ], 255 | "source": [ 256 | "from scholarly import scholarly, ProxyGenerator\n", 257 | "\n", 258 | "# Set up a ProxyGenerator object to use free proxies\n", 259 | "# This needs to be done only once per session\n", 260 | "pg = ProxyGenerator()\n", 261 | "pg.FreeProxies()\n", 262 | "scholarly.use_proxy(pg)\n", 263 | "\n", 264 | "# Now search Google Scholar from behind a proxy\n", 265 | "search_query = scholarly.search_pubs(f'doi:{doi}')\n", 266 | "article = next(search_query)" 267 | ] 268 | }, 269 | { 270 | "cell_type": "code", 271 | "execution_count": null, 272 | "metadata": {}, 273 | "outputs": [], 274 | "source": [] 275 | }, 276 | { 277 | "cell_type": "code", 278 | "execution_count": null, 279 | "metadata": {}, 280 | "outputs": [], 281 | "source": [] 282 | }, 283 | { 284 | "cell_type": "code", 285 | "execution_count": 1, 286 | "metadata": {}, 287 | "outputs": [ 288 | { 289 | "name": "stdout", 290 | "output_type": "stream", 291 | "text": [ 292 | "complete URL with filters:\n", 293 | "https://api.openalex.org/works?search=openalex\n" 294 | ] 295 | } 296 | ], 297 | "source": [ 298 | "# specify endpoint\n", 299 | "endpoint = 'works'\n", 300 | "\n", 301 | "search_query = 'openalex'\n", 302 | "\n", 303 | "# put the URL together\n", 304 | "url = f'https://api.openalex.org/{endpoint}?search={search_query}'\n", 305 | "print(f'complete URL with filters:\\n{url}')" 306 | ] 307 | }, 308 | { 309 | "cell_type": "code", 310 | "execution_count": 3, 311 | "metadata": {}, 312 | "outputs": [], 313 | "source": [ 314 | "openalex_arxiv_paper = 'W4229010617'\n", 315 | "openalex_arxiv_paper_2 = 'W4288680697'\n", 316 | "url = f'https://api.openalex.org/{endpoint}?filter=cites:{openalex_arxiv_paper}'\n", 317 | "result = requests.get(url).json()" 318 | ] 319 | }, 320 | { 321 | "cell_type": "code", 322 | "execution_count": 5, 323 | "metadata": {}, 324 | "outputs": [ 325 | { 326 | "data": { 327 | "text/plain": [ 328 | "{'count': 3, 'db_response_time_ms': 59, 'page': 1, 'per_page': 25}" 329 | ] 330 | }, 331 | "execution_count": 5, 332 | "metadata": {}, 333 | "output_type": "execute_result" 334 | } 335 | ], 336 | "source": [ 337 | "result['meta']" 338 | ] 339 | }, 340 | { 341 | "cell_type": "code", 342 | "execution_count": 9, 343 | "metadata": {}, 344 | "outputs": [ 345 | { 346 | "data": { 347 | "text/plain": [ 348 | "dict_keys(['container_type', 'source', 'bib', 'filled', 'gsrank', 'pub_url', 'author_id', 'url_scholarbib', 'url_add_sclib', 'num_citations', 'citedby_url', 'url_related_articles', 'eprint_url'])" 349 | ] 350 | }, 351 | "execution_count": 9, 352 | "metadata": {}, 353 | "output_type": "execute_result" 354 | } 355 | ], 356 | "source": [ 357 | "article.keys()" 358 | ] 359 | }, 360 | { 361 | "cell_type": "code", 362 | "execution_count": 10, 363 | "metadata": {}, 364 | "outputs": [ 365 | { 366 | "data": { 367 | "text/plain": [ 368 | "19" 369 | ] 370 | }, 371 | "execution_count": 10, 372 | "metadata": {}, 373 | "output_type": "execute_result" 374 | } 375 | ], 376 | "source": [ 377 | "article['num_citations']" 378 | ] 379 | }, 380 | { 381 | "cell_type": "code", 382 | "execution_count": null, 383 | "metadata": {}, 384 | "outputs": [], 385 | "source": [] 386 | } 387 | ], 388 | "metadata": { 389 | "kernelspec": { 390 | "display_name": "venv", 391 | "language": "python", 392 | "name": "python3" 393 | }, 394 | "language_info": { 395 | "codemirror_mode": { 396 | "name": "ipython", 397 | "version": 3 398 | }, 399 | "file_extension": ".py", 400 | "mimetype": "text/x-python", 401 | "name": "python", 402 | "nbconvert_exporter": "python", 403 | "pygments_lexer": "ipython3", 404 | "version": "3.9.12" 405 | }, 406 | "orig_nbformat": 4, 407 | "vscode": { 408 | "interpreter": { 409 | "hash": "271691dbc4cdb85f541c883090ff5a004cbd8b9c207c2cfed84437fce4e65fdb" 410 | } 411 | } 412 | }, 413 | "nbformat": 4, 414 | "nbformat_minor": 2 415 | } 416 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | requests==2.31.0 2 | pandas==2.1.1 3 | python-dotenv==1.0.0 4 | 5 | # Visualization libraries 6 | matplotlib==3.8.0 7 | plotly==5.17.0 8 | seaborn==0.13.0 9 | 10 | # Static visualization rendering 11 | kaleido==0.2.1 12 | 13 | # Geo 14 | country_converter==1.0.0 -------------------------------------------------------------------------------- /resources/img/OpenAlex-banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ourresearch/openalex-api-tutorials/1988d22c5499d6a1f68d85ef2902b600b555aaa5/resources/img/OpenAlex-banner.png -------------------------------------------------------------------------------- /resources/img/OpenAlex-entities.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ourresearch/openalex-api-tutorials/1988d22c5499d6a1f68d85ef2902b600b555aaa5/resources/img/OpenAlex-entities.png -------------------------------------------------------------------------------- /resources/img/OpenAlex-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ourresearch/openalex-api-tutorials/1988d22c5499d6a1f68d85ef2902b600b555aaa5/resources/img/OpenAlex-logo.png -------------------------------------------------------------------------------- /resources/img/notebooks/cursor-paging.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ourresearch/openalex-api-tutorials/1988d22c5499d6a1f68d85ef2902b600b555aaa5/resources/img/notebooks/cursor-paging.png -------------------------------------------------------------------------------- /resources/img/notebooks/meta-object.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ourresearch/openalex-api-tutorials/1988d22c5499d6a1f68d85ef2902b600b555aaa5/resources/img/notebooks/meta-object.png -------------------------------------------------------------------------------- /runtime.txt: -------------------------------------------------------------------------------- 1 | python-3.9 --------------------------------------------------------------------------------