{ "cells": [ { "cell_type": "markdown", "id": "8ee54fa1", "metadata": {}, "source": [ "# CDA Python: Features & Examples\n", "---" ] }, { "cell_type": "markdown", "id": "35217356", "metadata": {}, "source": [ "The following examples illustrate some ```CDA Python``` features while providing insights into the underlying data structure (**Getting started**). To demonstrate those features, we provide a few relevant text queries along with step-by-step explanations on how to translate those into the ```CDA Python``` queries (**Example queries**). Finally, there are a few additional queries intended for the test users to play around with and send feedback to the CDA team (**Test queries**)." ] }, { "cell_type": "code", "execution_count": null, "id": "5245b7b0", "metadata": {}, "outputs": [], "source": [ "from cdapython import Q, columns, unique_terms,query\n", "import cdapython\n", "print(cdapython.__file__)\n", "print(cdapython.__version__)" ] }, { "cell_type": "markdown", "id": "bccff533", "metadata": {}, "source": [ "## Getting started" ] }, { "cell_type": "markdown", "id": "36d1d3ec", "metadata": {}, "source": [ "Print out the list of available fields with ```columns()```:" ] }, { "cell_type": "code", "execution_count": 2, "id": "b83e40e6", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/Users/dboles/Documents/python/working/cda-python/venv/lib/python3.7/site-packages/urllib3/connectionpool.py:1020: InsecureRequestWarning: Unverified HTTPS request is being made to host 'cda.cda-dev.broadinstitute.org'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/1.26.x/advanced-usage.html#ssl-warnings\n", " InsecureRequestWarning,\n" ] }, { "data": { "text/plain": [ "['id',\n", " 'identifier',\n", " 'identifier.system',\n", " 'identifier.value',\n", " 'sex',\n", " 'race',\n", " 'ethnicity',\n", " 'days_to_birth',\n", " 'subject_associated_project',\n", " 'File',\n", " 'File.id',\n", " 'File.identifier',\n", " 'File.identifier.system',\n", " 'File.identifier.value',\n", " 'File.label',\n", " 'File.data_category',\n", " 'File.data_type',\n", " 'File.file_format',\n", " 'File.associated_project',\n", " 'File.drs_uri',\n", " 'File.byte_size',\n", " 'File.checksum',\n", " 'ResearchSubject',\n", " 'ResearchSubject.id',\n", " 'ResearchSubject.identifier',\n", " 'ResearchSubject.identifier.system',\n", " 'ResearchSubject.identifier.value',\n", " 'ResearchSubject.associated_project',\n", " 'ResearchSubject.primary_disease_type',\n", " 'ResearchSubject.primary_disease_site',\n", " 'ResearchSubject.Diagnosis',\n", " 'ResearchSubject.Diagnosis.id',\n", " 'ResearchSubject.Diagnosis.identifier',\n", " 'ResearchSubject.Diagnosis.identifier.system',\n", " 'ResearchSubject.Diagnosis.identifier.value',\n", " 'ResearchSubject.Diagnosis.primary_diagnosis',\n", " 'ResearchSubject.Diagnosis.age_at_diagnosis',\n", " 'ResearchSubject.Diagnosis.morphology',\n", " 'ResearchSubject.Diagnosis.stage',\n", " 'ResearchSubject.Diagnosis.grade',\n", " 'ResearchSubject.Diagnosis.Treatment',\n", " 'ResearchSubject.Diagnosis.Treatment.id',\n", " 'ResearchSubject.Diagnosis.Treatment.identifier',\n", " 'ResearchSubject.Diagnosis.Treatment.identifier.system',\n", " 'ResearchSubject.Diagnosis.Treatment.identifier.value',\n", " 'ResearchSubject.Diagnosis.Treatment.treatment_type',\n", " 'ResearchSubject.Diagnosis.Treatment.treatment_outcome',\n", " 'ResearchSubject.Diagnosis.Treatment.days_to_treatment_start',\n", " 'ResearchSubject.Diagnosis.Treatment.days_treatment_end',\n", " 'ResearchSubject.File',\n", " 'ResearchSubject.File.id',\n", " 'ResearchSubject.File.identifier',\n", " 'ResearchSubject.File.identifier.system',\n", " 'ResearchSubject.File.identifier.value',\n", " 'ResearchSubject.File.label',\n", " 'ResearchSubject.File.data_category',\n", " 'ResearchSubject.File.data_type',\n", " 'ResearchSubject.File.file_format',\n", " 'ResearchSubject.File.associated_project',\n", " 'ResearchSubject.File.drs_uri',\n", " 'ResearchSubject.File.byte_size',\n", " 'ResearchSubject.File.checksum',\n", " 'ResearchSubject.Specimen',\n", " 'ResearchSubject.Specimen.id',\n", " 'ResearchSubject.Specimen.identifier',\n", " 'ResearchSubject.Specimen.identifier.system',\n", " 'ResearchSubject.Specimen.identifier.value',\n", " 'ResearchSubject.Specimen.associated_project',\n", " 'ResearchSubject.Specimen.age_at_collection',\n", " 'ResearchSubject.Specimen.primary_disease_type',\n", " 'ResearchSubject.Specimen.anatomical_site',\n", " 'ResearchSubject.Specimen.source_material_type',\n", " 'ResearchSubject.Specimen.specimen_type',\n", " 'ResearchSubject.Specimen.derived_from_specimen',\n", " 'ResearchSubject.Specimen.derived_from_subject',\n", " 'ResearchSubject.Specimen.File',\n", " 'ResearchSubject.Specimen.File.id',\n", " 'ResearchSubject.Specimen.File.identifier',\n", " 'ResearchSubject.Specimen.File.identifier.system',\n", " 'ResearchSubject.Specimen.File.identifier.value',\n", " 'ResearchSubject.Specimen.File.label',\n", " 'ResearchSubject.Specimen.File.data_category',\n", " 'ResearchSubject.Specimen.File.data_type',\n", " 'ResearchSubject.Specimen.File.file_format',\n", " 'ResearchSubject.Specimen.File.associated_project',\n", " 'ResearchSubject.Specimen.File.drs_uri',\n", " 'ResearchSubject.Specimen.File.byte_size',\n", " 'ResearchSubject.Specimen.File.checksum']" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "columns(verify=False)" ] }, { "cell_type": "markdown", "id": "26229d50", "metadata": {}, "source": [ "All of the above fields are what describes the highest entity in the data structure hierarchy – ```Patient``` entity. The first five fields represent ```Patient``` demographic information, while the ```ResearchSubject``` entity contains details that we are used to seeing within the nodes' ```Case``` record.\n", "\n", "One of the contributions of the CDA is aggregated ```ResearchSubject``` information. This means that all ```ResearchSubject``` records coming from the same subject are now gathered under the Patient entity. As we know, certain specimens are studied in multiple projects (being part of a single data node or multiple nodes) as different ```ResearchSubject``` entries. Those ```ResearchSubject``` entries are collected as a list under the ```ResearchSubject``` entity. One example of this is the patient record with ```id = TCGA-E2-A10A``` which contains two ```ResearchSubject``` entries, one from GDC and the other from PDC.\n", "\n", "Note that the ```ResearchSubject``` entity is a list of records, as many other entities above are. **There are certain considerations that should be made when creating the queries by using the fields that come from lists, but more about that will follow in examples below**.\n", "\n", "The names in the list may look familiar to you, but they may have been renamed or restructured in the CDA. The field name mappings are described in the _CDA Schema Field Mapping_ document that is linked in the _Testing Guide_. A more direct way to explore and understand the fields is to use the ```unique_terms()``` function:" ] }, { "cell_type": "code", "execution_count": null, "id": "47da0214", "metadata": {}, "outputs": [], "source": [ "unique_terms(\"ResearchSubject.Specimen.source_material_type\",limit=10)" ] }, { "cell_type": "markdown", "id": "e96d2c77", "metadata": {}, "source": [ "Additionally, you can specify a particular data node by using the ```system``` argument:" ] }, { "cell_type": "code", "execution_count": null, "id": "f922031e", "metadata": {}, "outputs": [], "source": [ "unique_terms(\"ResearchSubject.Specimen.source_material_type\", system=\"PDC\")" ] }, { "cell_type": "markdown", "id": "1f6396f2", "metadata": {}, "source": [ "Now, let's dive into the querying!\n", "\n", "We can start by getting the record for ```id = TCGA-E2-A10A``` that we mentioned earlier:" ] }, { "cell_type": "code", "execution_count": null, "id": "ac11f401", "metadata": {}, "outputs": [], "source": [ "q = Q('id = \"TCGA-E2-A10A\"') # note the double quotes for the string value\n", "\n", "r = q.run()\n", "\n", "\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "id": "3936e82a", "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "id": "b83e303d", "metadata": {}, "source": [ "We see that we've got a single patient record as a result, which is what we expect.\n", "\n", "Let's see how the result looks like:" ] }, { "cell_type": "code", "execution_count": null, "id": "1aa11a6f", "metadata": { "scrolled": true }, "outputs": [], "source": [ "r[0]" ] }, { "cell_type": "markdown", "id": "e97085f8", "metadata": {}, "source": [ "The record is pretty large, so we'll print out ```identifier``` values for each ```ResearchSubject``` to confirm that we have one ```ResearchSubject``` that comes from GDC, and one that comes from PDC:" ] }, { "cell_type": "code", "execution_count": null, "id": "88625cfa", "metadata": {}, "outputs": [], "source": [ "for research_subject in r[0]['ResearchSubject']:\n", " print(research_subject['identifier'])" ] }, { "cell_type": "markdown", "id": "8b48f8d2", "metadata": {}, "source": [ "The values represent ```ResearchSubject``` IDs and are equivalent to ```case_id``` values in data nodes." ] }, { "cell_type": "markdown", "id": "3a783bd7", "metadata": {}, "source": [ "## Example queries" ] }, { "cell_type": "markdown", "id": "c215820a", "metadata": {}, "source": [ "Now that we can create a query with ```Q()``` function, let's see how we can combine multiple conditions.\n", "\n", "There are three operators available:\n", "* ```And()```\n", "* ```Or()```\n", "* ```From()```\n", "\n", "The following examples show how those operators work in practice." ] }, { "cell_type": "markdown", "id": "77acef39", "metadata": {}, "source": [ "### Query 1\n", "\n", "**Find data for subjects who were diagnosed after the age of 50 and who were investigated as part of the TCGA-OV project.**" ] }, { "cell_type": "code", "execution_count": null, "id": "d6da17a6", "metadata": {}, "outputs": [], "source": [ "q1 = Q('ResearchSubject.Diagnosis.age_at_diagnosis > 50*365')\n", "q2 = Q('ResearchSubject.associated_project = \"TCGA-OV\"')\n", "\n", "q = q1.AND(q2)\n", "r = q.run()\n", "\n", "print(r)" ] }, { "cell_type": "markdown", "id": "3d13b766", "metadata": {}, "source": [ "### Query 2\n", "\n", "**Find data for donors with melanoma (Nevi and Melanomas) diagnosis and who were diagnosed before the age of 30.**" ] }, { "cell_type": "code", "execution_count": null, "id": "c6ce3248", "metadata": {}, "outputs": [], "source": [ "q1 = Q('ResearchSubject.Specimen.primary_disease_type = \"Nevi and Melanomas\"')\n", "q2 = Q('ResearchSubject.Diagnosis.age_at_diagnosis < 30*365')\n", "\n", "q = q1.AND(q2)\n", "r = q.run()\n", "\n", "print(r)" ] }, { "cell_type": "markdown", "id": "7621e34d", "metadata": {}, "source": [ "In addition, we can check how many records come from particular systems by adding one more condition to the query:" ] }, { "cell_type": "code", "execution_count": null, "id": "45ad21fd", "metadata": {}, "outputs": [], "source": [ "q1 = Q('ResearchSubject.Specimen.primary_disease_type = \"Nevi and Melanomas\"')\n", "q2 = Q('ResearchSubject.Diagnosis.age_at_diagnosis < 30*365')\n", "q3 = Q('ResearchSubject.Specimen.identifier.system = \"GDC\"')\n", "\n", "q = q1.AND(q2.AND(q3))\n", "r = q.run()\n", "\n", "print(r)" ] }, { "cell_type": "markdown", "id": "036bc88d", "metadata": {}, "source": [ "By comparing the ```Count``` value of the two results we can see that all the patients returned in the initial query are coming from the GDC.\n", "\n", "To explore the results further, we can fetch the patient JSON objects by iterating through the results:" ] }, { "cell_type": "code", "execution_count": null, "id": "df5deb25", "metadata": {}, "outputs": [], "source": [ "projects = set()\n", "\n", "for patient in r:\n", " research_subjects = patient['ResearchSubject']\n", " for rs in research_subjects:\n", " projects.add(rs['associated_project'])\n", "\n", "print(projects)" ] }, { "cell_type": "markdown", "id": "27df4ffc", "metadata": {}, "source": [ "The output shows the projects where _Nevi and Melanomas_ cases appear." ] }, { "cell_type": "markdown", "id": "ee0955c4", "metadata": {}, "source": [ "### Query 3\n", "\n", "**Identify all samples that meet the following conditions:**\n", "\n", "* **Sample is from primary tumor**\n", "* **Disease is ovarian or breast cancer**\n", "* **Subjects are females under the age of 60 years**" ] }, { "cell_type": "code", "execution_count": null, "id": "e6709c55", "metadata": {}, "outputs": [], "source": [ "tumor_type = Q('ResearchSubject.Specimen.source_material_type = \"Primary Tumor\"')\n", "disease1 = Q('ResearchSubject.primary_disease_site = \"Ovary\"')\n", "disease2 = Q('ResearchSubject.primary_disease_site = \"Breast\"')\n", "demographics1 = Q('sex = \"female\"')\n", "demographics2 = Q('days_to_birth > -60*365') # note that days_to_birth is a negative value\n", "\n", "q1 = tumor_type.AND(demographics1.AND(demographics2))\n", "q2 = disease1.OR(disease2)\n", "q = q1.AND(q2)\n", "\n", "r = q.run()\n", "print(r)" ] }, { "cell_type": "markdown", "id": "22e0e66a", "metadata": {}, "source": [ "In this case, we have a result that contains more than 1000 records which is the default page size. To load the next 1000 records, we can use the ```next_page()``` method:" ] }, { "cell_type": "code", "execution_count": null, "id": "c62c6074", "metadata": {}, "outputs": [], "source": [ "r2 = r.next_page()" ] }, { "cell_type": "code", "execution_count": null, "id": "0376b0d6", "metadata": {}, "outputs": [], "source": [ "print(r2)" ] }, { "cell_type": "markdown", "id": "72b1f71b", "metadata": {}, "source": [ "Alternatively, we can use the ```offset``` argument to specify the record to start from:\n", "\n", "```\n", "...\n", "r = q.run(offset=1000)\n", "print(r)\n", "```" ] }, { "cell_type": "markdown", "id": "bd653b13", "metadata": {}, "source": [ "### Query 4\n", "\n", "**Find data for donors with \"Ovarian Serous Cystadenocarcinoma\" with proteomic and genomic data.**" ] }, { "cell_type": "markdown", "id": "92e0f930", "metadata": {}, "source": [ "**Note that disease type value denoting the same disease groups can be completely different within different systems. This is where CDA features come into play.** We first start by exploring the values available for this particular field in both systems." ] }, { "cell_type": "code", "execution_count": 3, "id": "cd201350", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/Users/dboles/Documents/python/working/cda-python/cdapython/decorators_cache.py:18: ResourceWarning: unclosed \n", " func.cache_clear()\n", "ResourceWarning: Enable tracemalloc to get the object allocation traceback\n" ] }, { "data": { "text/plain": [ "[None,\n", " 'Acinar Cell Neoplasms',\n", " 'Adenomas and Adenocarcinomas',\n", " 'Adnexal and Skin Appendage Neoplasms',\n", " 'Basal Cell Neoplasms',\n", " 'Blood Vessel Tumors',\n", " 'Chronic Myeloproliferative Disorders',\n", " 'Complex Epithelial Neoplasms',\n", " 'Complex Mixed and Stromal Neoplasms',\n", " 'Cystic, Mucinous and Serous Neoplasms']" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "unique_terms('ResearchSubject.primary_disease_type', system=\"GDC\",limit=10,verify=False)" ] }, { "cell_type": "markdown", "id": "b3eecdd5", "metadata": {}, "source": [ "Since “Ovarian Serous Cystadenocarcinoma” doesn’t appear in GDC values we decide to look into the PDC:" ] }, { "cell_type": "code", "execution_count": null, "id": "706f0301", "metadata": {}, "outputs": [], "source": [ "unique_terms('ResearchSubject.primary_disease_type', system=\"PDC\")" ] }, { "cell_type": "markdown", "id": "3a3476ed", "metadata": {}, "source": [ "After examining the output, we see that it does come from the PDC. Hence, if we could first identify the data that has research subjects found within the PDC that have this particular disease type, and then further narrow down the results to include only the portion of the data that is present in GDC, we could get the records that we are looking for." ] }, { "cell_type": "code", "execution_count": 8, "id": "637640e2", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Getting results from database\n", "\n", "Total execution time: 27307 ms\n", "\n", " QueryID: bd084bbd-33bd-4339-b034-b620192922b1\n", " Query: SELECT all_v2.* FROM (SELECT all_v2.* FROM gdc-bq-sample.integration.all_v2 AS all_v2, UNNEST(ResearchSubject) AS _ResearchSubject, UNNEST(_ResearchSubject.identifier) AS _identifier WHERE ((_ResearchSubject.primary_disease_type = 'Ovarian Serous Cystadenocarcinoma') AND (_identifier.system = 'PDC'))) AS all_v2, UNNEST(ResearchSubject) AS _ResearchSubject, UNNEST(_ResearchSubject.identifier) AS _identifier WHERE (_identifier.system = 'GDC')\n", " Offset: 0\n", " Count: 100\n", " Total Row Count: 275\n", " More pages: True\n", " \n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "/Users/dboles/Documents/python/working/cda-python/venv/lib/python3.7/site-packages/ipykernel_launcher.py:6: ResourceWarning: unclosed \n", " \n", "ResourceWarning: Enable tracemalloc to get the object allocation traceback\n" ] } ], "source": [ "q1 = Q('ResearchSubject.primary_disease_type = \"Ovarian Serous Cystadenocarcinoma\"')\n", "q2 = Q('ResearchSubject.identifier.system = \"PDC\"')\n", "q3 = Q('ResearchSubject.identifier.system = \"GDC\"')\n", "\n", "q = q3.FROM(q1.AND(q2))\n", "r = q.run(verify=False)\n", "\n", "print(r)" ] }, { "cell_type": "markdown", "id": "9ea451f4", "metadata": {}, "source": [ "As you can see, this is achieved by utilizing ```From``` operator. The ```From``` operator allows us to create queries from results of other queries. This is particularly useful when working with conditions that involve a single field which can take multiple different values for different items in a list that is being part of, e.g. we need ```ResearchSubject.identifier.system``` to be both “PDC” and “GDC” for a single patient. In such cases, ```And``` operator can’t help because it will return those entries where the field takes both values, which is zero entries." ] }, { "cell_type": "code", "execution_count": 10, "id": "da890a83", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{'table_catalog': 'gdc-bq-sample', 'table_schema': 'cda_mvp', 'table_name': 'v3', 'column_name': 'days_to_birth', 'field_path': 'days_to_birth', 'data_type': 'INT64', 'description': None}\n", "{'table_catalog': 'gdc-bq-sample', 'table_schema': 'cda_mvp', 'table_name': 'v3', 'column_name': 'race', 'field_path': 'race', 'data_type': 'STRING', 'description': None}\n", "{'table_catalog': 'gdc-bq-sample', 'table_schema': 'cda_mvp', 'table_name': 'v3', 'column_name': 'sex', 'field_path': 'sex', 'data_type': 'STRING', 'description': None}\n", "{'table_catalog': 'gdc-bq-sample', 'table_schema': 'cda_mvp', 'table_name': 'v3', 'column_name': 'ethnicity', 'field_path': 'ethnicity', 'data_type': 'STRING', 'description': None}\n", "{'table_catalog': 'gdc-bq-sample', 'table_schema': 'cda_mvp', 'table_name': 'v3', 'column_name': 'id', 'field_path': 'id', 'data_type': 'STRING', 'description': None}\n" ] } ], "source": [ "for i in Q.sql(\"SELECT * FROM `gdc-bq-sample.cda_mvp.INFORMATION_SCHEMA.COLUMN_FIELD_PATHS` WHERE table_name = 'v3' Limit 5\",verify=False):\n", " print(i)" ] }, { "cell_type": "code", "execution_count": null, "id": "231e5519", "metadata": {}, "outputs": [], "source": [ "q1 = query('ResearchSubject.identifier.system = \"GDC\" FROM ResearchSubject.primary_disease_type = \"Ovarian Serous Cystadenocarcinoma\" AND ResearchSubject.identifier.system = \"PDC\"')\n", "result = q1.run(async_call=True)\n", "print(result)\n" ] }, { "cell_type": "markdown", "id": "716356cc", "metadata": {}, "source": [ "## Data extraction and release information" ] }, { "cell_type": "code", "execution_count": null, "id": "0d420653", "metadata": {}, "outputs": [], "source": [ "# If you are interested in the extraction dates or data release versions of GDC, PDC, or IDC that is in a table or view, execute this code\n", "\n", "for i in Q.sql(\"SELECT option_value FROM `gdc-bq-sample.integration.INFORMATION_SCHEMA.TABLE_OPTIONS` WHERE table_name = 'all_v1'\"):\n", " print(i)" ] }, { "cell_type": "markdown", "id": "e1a235eb", "metadata": {}, "source": [ "## Test queries" ] }, { "cell_type": "markdown", "id": "9740d86e", "metadata": {}, "source": [ "Now that we've successfully run and analyzed a few queries, here are a few additional ones you can try out on your own.\n", "\n", "Solutions can be shared with the CDA team as indicated in the _Testing Guide_ document." ] }, { "cell_type": "markdown", "id": "484df50b", "metadata": {}, "source": [ "### Test Query 1\n", "\n", "**Find data from TCGA-BRCA project, with donors over the age of 50 with Stage IIIC cancer.**" ] }, { "cell_type": "code", "execution_count": null, "id": "76e5ff7c", "metadata": {}, "outputs": [], "source": [ "# Solution\n", "\n", "# ...\n", "\n", "# print(r)" ] }, { "cell_type": "markdown", "id": "dfebb4f8", "metadata": {}, "source": [ "### Test Query 2\n", "\n", "**Find data from all patients who have been treated with \"Radiation Therapy, NOS\" and have both genomic and proteomic data.**" ] }, { "cell_type": "code", "execution_count": null, "id": "8fab696d", "metadata": {}, "outputs": [], "source": [ "# Solution\n", "\n", "# ...\n", "\n", "# print(r)" ] }, { "cell_type": "markdown", "id": "cf6e1ba8", "metadata": {}, "source": [ "### Test Query 3\n", "\n", "**Find data from all subjects with lung adenocarcinomas that have both primary and recurrent tumors.**" ] }, { "cell_type": "code", "execution_count": null, "id": "13b82aef", "metadata": {}, "outputs": [], "source": [ "# Solution\n", "\n", "# ...\n", "\n", "# print(r)" ] }, { "cell_type": "code", "execution_count": null, "id": "a84d6fcc", "metadata": {}, "outputs": [], "source": [ "\n", "Q('ResearchSubject.id = \"c5421e34-e5c7-4ba5-aed9-146a5575fd8d\"').run().pretty_print(-1) " ] } ], "metadata": { "kernelspec": { "display_name": "Python 3.10.4 ('venv': venv)", "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.10.4" }, "metadata": { "interpreter": { "hash": "5c867d1980d5b66d2bfc8a5903dcee074b4b68f3917a4f27f8a310cab24e9f1b" } }, "vscode": { "interpreter": { "hash": "4578c8680ee810f847df558484335f5ffb0f004d38a87276387030f59580c508" } } }, "nbformat": 4, "nbformat_minor": 5 }