From 78d5aa4a2be8e65e23fa611119673a6a5a978a04 Mon Sep 17 00:00:00 2001 From: Jean-Marie Burel Date: Wed, 20 Oct 2021 15:08:37 +0100 Subject: [PATCH] add notebook showing how to interact between IDR and OXO --- oxo.ipynb | 865 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 865 insertions(+) create mode 100644 oxo.ipynb diff --git a/oxo.ipynb b/oxo.ipynb new file mode 100644 index 00000000..9b55a8b7 --- /dev/null +++ b/oxo.ipynb @@ -0,0 +1,865 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "molecular-excess", + "metadata": {}, + "source": [ + "# Cross resources example\n", + "\n", + "This notebook search for a term in IDR and the Ontology Cross reference service (OXO) https://www.ebi.ac.uk/spot/oxo/index.\n", + "We use the API of both resources to find data and terms." + ] + }, + { + "cell_type": "markdown", + "id": "abroad-steal", + "metadata": {}, + "source": [ + "### Libraries used" + ] + }, + { + "cell_type": "code", + "execution_count": 70, + "id": "superb-discretion", + "metadata": {}, + "outputs": [], + "source": [ + "import requests\n", + "import csv\n", + "import os\n", + "import pandas as pd\n", + "from tempfile import NamedTemporaryFile" + ] + }, + { + "cell_type": "markdown", + "id": "positive-credits", + "metadata": {}, + "source": [ + "### Set up to interact with IDR" + ] + }, + { + "cell_type": "code", + "execution_count": 71, + "id": "diagnostic-safety", + "metadata": {}, + "outputs": [], + "source": [ + "INDEX_PAGE = \"https://idr.openmicroscopy.org/webclient/?experimenter=-1\"\n", + "\n", + "# create http session\n", + "with requests.Session() as session:\n", + " request = requests.Request('GET', INDEX_PAGE)\n", + " prepped = session.prepare_request(request)\n", + " response = session.send(prepped)\n", + " if response.status_code != 200:\n", + " response.raise_for_status()" + ] + }, + { + "cell_type": "markdown", + "id": "narrow-stake", + "metadata": {}, + "source": [ + "### Set up base URLS so can use shorter variable names later on" + ] + }, + { + "cell_type": "code", + "execution_count": 72, + "id": "bottom-patent", + "metadata": {}, + "outputs": [], + "source": [ + "URL = \"https://idr.openmicroscopy.org/mapr/api/{key}/?value={value}&case_sensitive=false&orphaned=true\"\n", + "SCREENS_PROJECTS_URL = \"https://idr.openmicroscopy.org/mapr/api/{key}/?value={value}\"\n", + "PLATES_URL = \"https://idr.openmicroscopy.org/mapr/api/{key}/plates/?value={value}&id={screen_id}\"\n", + "DATASETS_URL = \"https://idr.openmicroscopy.org/mapr/api/{key}/datasets/?value={value}&id={project_id}\"\n", + "IMAGES_URL = \"https://idr.openmicroscopy.org/mapr/api/{key}/images/?value={value}&node={parent_type}&id={parent_id}\"\n", + "ATTRIBUTES_URL = \"https://idr.openmicroscopy.org/webclient/api/annotations/?type=map&image={image_id}\"\n", + "\n", + "URL_OXO = \"https://www.ebi.ac.uk/spot/oxo/api/search\"" + ] + }, + { + "cell_type": "markdown", + "id": "paperback-float", + "metadata": {}, + "source": [ + "## Context" + ] + }, + { + "cell_type": "code", + "execution_count": 73, + "id": "interim-project", + "metadata": {}, + "outputs": [], + "source": [ + "TERM = \"HGNC_5009\"\n", + "TYPE = \"gene\"\n", + "\n", + "KEYS = {\"phenotype\":\n", + " (\"Phenotype\",\n", + " \"Phenotype Term Name\",\n", + " \"Phenotype Term Accession\",\n", + " \"Phenotype Term Accession URL\")\n", + "}" + ] + }, + { + "cell_type": "markdown", + "id": "buried-client", + "metadata": {}, + "source": [ + "## Search for studies in IDR\n", + "Using the specified TERM, search for studies with that specific term.\n", + "No result should be found" + ] + }, + { + "cell_type": "code", + "execution_count": 74, + "id": "serial-sampling", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'maps': [], 'screens': [], 'projects': []}\n" + ] + } + ], + "source": [ + "query = {'key': TYPE, 'value': TERM}\n", + "url = URL.format(**query)\n", + "json = session.get(url).json()\n", + "print(json)" + ] + }, + { + "cell_type": "markdown", + "id": "sorted-terminal", + "metadata": {}, + "source": [ + "## Search for mapping in OXO\n", + "We use the Ontoloty Cross Reference service https://www.ebi.ac.uk/spot/oxo/index to find associated terms.\n", + "We search using a mapping distance of 2." + ] + }, + { + "cell_type": "code", + "execution_count": 75, + "id": "liked-tobacco", + "metadata": {}, + "outputs": [], + "source": [ + "# Prepare the query\n", + "headers = {\n", + " 'Accept': 'application/json',\n", + " 'Content-Type': 'application/json'\n", + "}\n", + "\n", + "data = {\n", + " \"ids\" : [ TERM.replace(\"_\", \":\") ], \"distance\" : 2\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 76, + "id": "informational-thickness", + "metadata": {}, + "outputs": [], + "source": [ + "response = requests.post(URL_OXO, headers=headers, json=data)" + ] + }, + { + "cell_type": "code", + "execution_count": 77, + "id": "loved-impossible", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
curielabelsourcePrefixestargetPrefixdistance
0OMIM:600698[Orphanet]OMIM2
1Genatlas:HMGA2[Orphanet]Genatlas2
2Reactome:P52926[Orphanet]Reactome2
3Orphanet:248487high mobility group AT-hook 2[Orphanet]Orphanet1
4Ensembl:ENSG00000149948[Orphanet]Ensembl2
\n", + "
" + ], + "text/plain": [ + " curie label sourcePrefixes \\\n", + "0 OMIM:600698 [Orphanet] \n", + "1 Genatlas:HMGA2 [Orphanet] \n", + "2 Reactome:P52926 [Orphanet] \n", + "3 Orphanet:248487 high mobility group AT-hook 2 [Orphanet] \n", + "4 Ensembl:ENSG00000149948 [Orphanet] \n", + "\n", + " targetPrefix distance \n", + "0 OMIM 2 \n", + "1 Genatlas 2 \n", + "2 Reactome 2 \n", + "3 Orphanet 1 \n", + "4 Ensembl 2 " + ] + }, + "execution_count": 77, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "resp_json = response.json()\n", + "df_oxo = pd.DataFrame.from_records(resp_json[\"_embedded\"][\"searchResults\"][0][\"mappingResponseList\"])\n", + "df_oxo" + ] + }, + { + "cell_type": "markdown", + "id": "blank-commons", + "metadata": {}, + "source": [ + "### Parse the mapping from OXO\n", + "Parse the result of the mapping queries, the terms found will then be used to find studies in IDR" + ] + }, + { + "cell_type": "code", + "execution_count": 78, + "id": "effective-intermediate", + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['600698', 'HMGA2', 'P52926', '248487', 'ENSG00000149948']\n" + ] + } + ], + "source": [ + "queries = resp_json[\"_embedded\"][\"searchResults\"]\n", + "terms = []\n", + "for i in range(len(queries)):\n", + " mappings = queries[i][\"mappingResponseList\"]\n", + " for j in range(len(mappings)):\n", + " curie = mappings[j][\"curie\"]\n", + " terms.append(curie.split(\":\")[1])\n", + "print(terms)" + ] + }, + { + "cell_type": "markdown", + "id": "downtown-constitutional", + "metadata": {}, + "source": [ + "### Search for the terms in IDR\n", + "We search for the terms returned by OXO.\n", + "This time several studies are found and we keep the studies only returned once. We then query IDR for phenotypes associated with the genes." + ] + }, + { + "cell_type": "code", + "execution_count": 79, + "id": "whole-concentrate", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[1202, 2751]\n" + ] + } + ], + "source": [ + "ids = {}\n", + "genes = {}\n", + "for v in terms:\n", + " qs1 = {'key': TYPE, 'value': v}\n", + " url1 = URL.format(**qs1)\n", + " json = session.get(url1).json()\n", + " for m in json['maps']:\n", + " qs2 = {'key': TYPE, 'value': v, 'compound_id': m['id']}\n", + " url2 = SCREENS_PROJECTS_URL.format(**qs2)\n", + " json = session.get(url2).json()\n", + " for s in json['screens']:\n", + " id = s['id']\n", + " ids[id] = ids.get(id, 0) + 1\n", + " for p in json['projects']:\n", + " id = p['id']\n", + " ids[id] = ids.get(id, 0) + 1\n", + "\n", + "to_keep = []\n", + "for id in ids:\n", + " if ids[id] == 1:\n", + " to_keep.append(id)\n", + "\n", + "print(to_keep)" + ] + }, + { + "cell_type": "markdown", + "id": "returning-chase", + "metadata": {}, + "source": [ + "### Helper method\n", + "Parse the output of the json and save it into the CVS file." + ] + }, + { + "cell_type": "code", + "execution_count": 80, + "id": "israeli-syntax", + "metadata": {}, + "outputs": [], + "source": [ + "def parse_annotation(writer, json_data, name, data_type):\n", + " screen_name = \"-\"\n", + " plate_name = \"-\"\n", + " project_name = \"-\"\n", + " dataset_name = \"-\"\n", + " if data_type == 'datasets':\n", + " project_name = name\n", + " else:\n", + " screen_name = name\n", + " \n", + " for p in json_data[data_type]:\n", + " parent_id = p['id']\n", + " if data_type == 'datasets':\n", + " dataset_name = p['name']\n", + " else:\n", + " plate_name = p['name']\n", + " qs3 = {'key': TYPE, 'value': gene,\n", + " 'parent_type': data_type[:-1], 'parent_id': parent_id}\n", + " url3 = IMAGES_URL.format(**qs3)\n", + " for i in session.get(url3).json()['images']:\n", + "\n", + " image_id = i['id']\n", + " url4 = ATTRIBUTES_URL.format(**{'image_id': image_id})\n", + " for a in session.get(url4).json()['annotations']:\n", + " ontologies = [] # for ontology terms for a phenotype\n", + " row = {}\n", + " for v in a['values']:\n", + " if str(v[0]) in KEYS['phenotype']:\n", + " if str(v[0]) in ['Phenotype']: # has phenotype\n", + " row[str(v[0])] = v[1] # so create row\n", + " \n", + " # if there are ontology mappings for the\n", + " # phenotype, add them to the ontologies list\n", + " ontList = ['Phenotype Term Name',\n", + " 'Phenotype Term Accession',\n", + " 'Phenotype Term Accession URL']\n", + "\n", + " if str(v[0]) in ontList:\n", + " ontologies.extend([str(v[0]), str(v[1])])\n", + " if row:\n", + " if (len(ontologies) > 0): # 1+ ontology mapping\n", + " row.update({'Gene': gene,\n", + " 'Screen': screen_name,\n", + " 'Plate': plate_name,\n", + " 'Image': image_id,\n", + " 'Project' : project_name,\n", + " 'Dataset': dataset_name})\n", + " # we have the start of a row now\n", + " # but we want to print out as many rows\n", + " # as there are ontology mappings\n", + " # so if there is mapping to 1 ontology term\n", + " # print 1 row, if there are 2 ontology terms\n", + " # print 2 rows etc\n", + " numberOfRows = len(ontologies)/6\n", + " # this is 3 pairs of ontology values per\n", + " # mapping, add the ontology mappings and print\n", + " n = 1\n", + " while (n <= numberOfRows):\n", + " row.update({ontologies[0]: ontologies[1],\n", + " ontologies[2]: ontologies[3],\n", + " ontologies[4]: ontologies[5]})\n", + " # remove that set of ontology mappings\n", + " ontologies = ontologies[6:]\n", + " writer.writerow(row)\n", + " n = n + 1" + ] + }, + { + "cell_type": "code", + "execution_count": 81, + "id": "destroyed-florist", + "metadata": {}, + "outputs": [], + "source": [ + "home = os.path.expanduser(\"~\")\n", + "csvfile = NamedTemporaryFile(\"w\", delete=False, newline='', dir=home, suffix=\".csv\")\n", + "\n", + "try:\n", + " fieldnames = [\n", + " 'Gene', 'Screen', 'Plate', 'Project', 'Dataset', 'Image',\n", + " 'Phenotype', 'Phenotype Term Name', 'Phenotype Term Accession',\n", + " 'Phenotype Term Accession URL']\n", + " writer = csv.DictWriter(csvfile, fieldnames=fieldnames)\n", + " writer.writeheader()\n", + " \n", + " for v in terms:\n", + " qs1 = {'key': TYPE, 'value': v}\n", + " url1 = URL.format(**qs1)\n", + " json = session.get(url1).json()\n", + " for m in json['maps']:\n", + " qs2 = {'key': TYPE, 'value': v, 'compound_id': m['id']}\n", + " url2 = SCREENS_PROJECTS_URL.format(**qs2)\n", + " json = session.get(url2).json()\n", + " for s in json['screens']:\n", + " id = s['id']\n", + " if id in to_keep:\n", + " gene = s['extra']['value']\n", + " qs3 = {'key': TYPE, 'value': gene, 'screen_id': s['id']}\n", + " url3 = PLATES_URL.format(**qs3)\n", + " parse_annotation(writer, session.get(url3).json(), s['name'], 'plates')\n", + "finally:\n", + " csvfile.close()" + ] + }, + { + "cell_type": "markdown", + "id": "communist-queens", + "metadata": {}, + "source": [ + "### Explore the data\n", + "Read the generated CSV file into a dataframe. " + ] + }, + { + "cell_type": "code", + "execution_count": 82, + "id": "featured-driving", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
GeneScreenPlateProjectDatasetImagePhenotypePhenotype Term NamePhenotype Term AccessionPhenotype Term Accession URL
0HMGA2idr0093-mueller-perturbation/screenA (9)19--12608479Increased or decreased total RNA production ratetranscription, DNA-templatedGO_0006351http://purl.obolibrary.org/obo/GO_0006351
1HMGA2idr0093-mueller-perturbation/screenA (9)19--12608485Increased or decreased total RNA production ratetranscription, DNA-templatedGO_0006351http://purl.obolibrary.org/obo/GO_0006351
2HMGA2idr0093-mueller-perturbation/screenA (9)19--12608484Increased or decreased total RNA production ratetranscription, DNA-templatedGO_0006351http://purl.obolibrary.org/obo/GO_0006351
3HMGA2idr0093-mueller-perturbation/screenA (9)19--12608481Increased or decreased total RNA production ratetranscription, DNA-templatedGO_0006351http://purl.obolibrary.org/obo/GO_0006351
4HMGA2idr0093-mueller-perturbation/screenA (9)19--12608483Increased or decreased total RNA production ratetranscription, DNA-templatedGO_0006351http://purl.obolibrary.org/obo/GO_0006351
5HMGA2idr0093-mueller-perturbation/screenA (9)19--12608482Increased or decreased total RNA production ratetranscription, DNA-templatedGO_0006351http://purl.obolibrary.org/obo/GO_0006351
6HMGA2idr0093-mueller-perturbation/screenA (9)19--12608486Increased or decreased total RNA production ratetranscription, DNA-templatedGO_0006351http://purl.obolibrary.org/obo/GO_0006351
7HMGA2idr0093-mueller-perturbation/screenA (9)19--12608480Increased or decreased total RNA production ratetranscription, DNA-templatedGO_0006351http://purl.obolibrary.org/obo/GO_0006351
8HMGA2idr0093-mueller-perturbation/screenA (9)19--12608478Increased or decreased total RNA production ratetranscription, DNA-templatedGO_0006351http://purl.obolibrary.org/obo/GO_0006351
\n", + "
" + ], + "text/plain": [ + " Gene Screen Plate Project Dataset \\\n", + "0 HMGA2 idr0093-mueller-perturbation/screenA (9) 19 - - \n", + "1 HMGA2 idr0093-mueller-perturbation/screenA (9) 19 - - \n", + "2 HMGA2 idr0093-mueller-perturbation/screenA (9) 19 - - \n", + "3 HMGA2 idr0093-mueller-perturbation/screenA (9) 19 - - \n", + "4 HMGA2 idr0093-mueller-perturbation/screenA (9) 19 - - \n", + "5 HMGA2 idr0093-mueller-perturbation/screenA (9) 19 - - \n", + "6 HMGA2 idr0093-mueller-perturbation/screenA (9) 19 - - \n", + "7 HMGA2 idr0093-mueller-perturbation/screenA (9) 19 - - \n", + "8 HMGA2 idr0093-mueller-perturbation/screenA (9) 19 - - \n", + "\n", + " Image Phenotype \\\n", + "0 12608479 Increased or decreased total RNA production rate \n", + "1 12608485 Increased or decreased total RNA production rate \n", + "2 12608484 Increased or decreased total RNA production rate \n", + "3 12608481 Increased or decreased total RNA production rate \n", + "4 12608483 Increased or decreased total RNA production rate \n", + "5 12608482 Increased or decreased total RNA production rate \n", + "6 12608486 Increased or decreased total RNA production rate \n", + "7 12608480 Increased or decreased total RNA production rate \n", + "8 12608478 Increased or decreased total RNA production rate \n", + "\n", + " Phenotype Term Name Phenotype Term Accession \\\n", + "0 transcription, DNA-templated GO_0006351 \n", + "1 transcription, DNA-templated GO_0006351 \n", + "2 transcription, DNA-templated GO_0006351 \n", + "3 transcription, DNA-templated GO_0006351 \n", + "4 transcription, DNA-templated GO_0006351 \n", + "5 transcription, DNA-templated GO_0006351 \n", + "6 transcription, DNA-templated GO_0006351 \n", + "7 transcription, DNA-templated GO_0006351 \n", + "8 transcription, DNA-templated GO_0006351 \n", + "\n", + " Phenotype Term Accession URL \n", + "0 http://purl.obolibrary.org/obo/GO_0006351 \n", + "1 http://purl.obolibrary.org/obo/GO_0006351 \n", + "2 http://purl.obolibrary.org/obo/GO_0006351 \n", + "3 http://purl.obolibrary.org/obo/GO_0006351 \n", + "4 http://purl.obolibrary.org/obo/GO_0006351 \n", + "5 http://purl.obolibrary.org/obo/GO_0006351 \n", + "6 http://purl.obolibrary.org/obo/GO_0006351 \n", + "7 http://purl.obolibrary.org/obo/GO_0006351 \n", + "8 http://purl.obolibrary.org/obo/GO_0006351 " + ] + }, + "execution_count": 82, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df = pd.read_csv(csvfile.name)\n", + "df = df.sort_values(by=['Gene'])\n", + "df" + ] + }, + { + "cell_type": "markdown", + "id": "offensive-paste", + "metadata": {}, + "source": [ + "### Search in Oxo using the Phenotype Term Accession " + ] + }, + { + "cell_type": "code", + "execution_count": 83, + "id": "drawn-atlanta", + "metadata": {}, + "outputs": [], + "source": [ + "term = df[\"Phenotype Term Accession\"][0]\n", + "term = term.replace(\"_\", \":\")\n", + "data = {\n", + " \"ids\" : [ term ], \"distance\" : 2\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 84, + "id": "flexible-vegetation", + "metadata": {}, + "outputs": [], + "source": [ + "response = requests.post(URL_OXO, headers=headers, json=data)" + ] + }, + { + "cell_type": "code", + "execution_count": 85, + "id": "liked-pastor", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
curielabelsourcePrefixestargetPrefixdistance
0OMP:0007106transcription phenotype[OMP]OMP1
1Wikipedia:Transcription_(genetics)[PLANP, OGSF, CMPO, OMP, BAO, GO, FYPO, WBPhen...Wikipedia1
\n", + "
" + ], + "text/plain": [ + " curie label \\\n", + "0 OMP:0007106 transcription phenotype \n", + "1 Wikipedia:Transcription_(genetics) \n", + "\n", + " sourcePrefixes targetPrefix distance \n", + "0 [OMP] OMP 1 \n", + "1 [PLANP, OGSF, CMPO, OMP, BAO, GO, FYPO, WBPhen... Wikipedia 1 " + ] + }, + "execution_count": 85, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "resp_json = response.json()\n", + "df_oxo = pd.DataFrame.from_records(resp_json[\"_embedded\"][\"searchResults\"][0][\"mappingResponseList\"])\n", + "df_oxo" + ] + }, + { + "cell_type": "markdown", + "id": "bibliographic-taste", + "metadata": {}, + "source": [ + "### License (BSD 2-Clause)\n", + "\n", + "Copyright (C) 2021 University of Dundee. All Rights Reserved.\n", + "\n", + "Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n", + "\n", + "Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.10" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}