{ "cells": [ { "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { "inputWidgets": {}, "nuid": "5dbca100-ca8d-4cb3-be39-72d2aeb85077", "showTitle": false, "title": "" } }, "source": [ "# Kepler visualizations" ] }, { "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { "inputWidgets": {}, "nuid": "a2712c52-9c84-427e-bff8-814b9fa35d86", "showTitle": false, "title": "" } }, "source": [ "You can use the `%%mosaic_kepler` magic function to visualise data using [Kepler.gl](https://kepler.gl/).\n", "\n", "The mosaic_kepler magic function accepts four parameters:\n", "\n", "1) `dataset`: Can be a Spark dataset or a string representing a table/view name\n", "\n", "2) `column_name`: The column that needs to be plotted, can be either a geometry column (`WKT`, `WKB` or Mosaic internal format) or a column containing a spatial grid index ID\n", "\n", "3) `feature_type`: The type of data to be plotted. Valid values are `geometry` (if SRID=4326), `geometry()` (where `` is the SRID used by the geometry column), `geometry(bng)`, `geometry(osgb36)`, `bng` and `h3`\n", "\n", "4) `limit`: The maximum number of objects to plot. The default limit is `1000`\n", "\n", "Usage:\n", "```\n", "%%mosaic_kepler\n", "dataset column_name feature_type [limit]\n", "```\n", "\n", "This magic function is only available in python. It can be used from notebooks with other default languages by storing the intermediate result in a temporary view, and then adding a python cell that uses the `mosaic_kepler` with the temporary view created from another language." ] }, { "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { "inputWidgets": {}, "nuid": "573fa05d-6dfd-414e-84c4-6f3618540b60", "showTitle": false, "title": "" } }, "source": [ "## Examples" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "application/vnd.databricks.v1+cell": { "inputWidgets": {}, "nuid": "c73e5956-dec2-4206-be2d-99bca42d2c93", "showTitle": false, "title": "" } }, "outputs": [ { "data": { "text/html": [ "\n", "
Python interpreter will be restarted.\n", "Python interpreter will be restarted.\n", "
" ] }, "metadata": { "application/vnd.databricks.v1+output": { "addedWidgets": {}, "arguments": {}, "data": "
Python interpreter will be restarted.\nPython interpreter will be restarted.\n
", "datasetInfos": [], "metadata": {}, "removedWidgets": [], "type": "html" } }, "output_type": "display_data" } ], "source": [ "%pip install databricks-mosaic --quiet" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "application/vnd.databricks.v1+cell": { "inputWidgets": {}, "nuid": "a18dcbe7-1a01-45f2-a961-dd0f0284cf9c", "showTitle": false, "title": "" } }, "outputs": [ { "data": { "text/html": [ "\n", "
" ] }, "metadata": { "application/vnd.databricks.v1+output": { "addedWidgets": {}, "arguments": {}, "data": "
", "datasetInfos": [], "metadata": {}, "removedWidgets": [], "type": "html" } }, "output_type": "display_data" } ], "source": [ "from pyspark.sql.functions import *\n", "import mosaic as mos\n", "mos.enable_mosaic(spark, dbutils)" ] }, { "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { "inputWidgets": {}, "nuid": "2a997c83-5aff-4463-8c64-a1dc7a55d7d3", "showTitle": false, "title": "" } }, "source": [ "### Download example shapes" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "application/vnd.databricks.v1+cell": { "inputWidgets": {}, "nuid": "93072805-dd95-4518-9099-47ca7e85b62d", "showTitle": false, "title": "" } }, "outputs": [ { "data": { "text/html": [ "\n", "
" ] }, "metadata": { "application/vnd.databricks.v1+output": { "addedWidgets": {}, "arguments": {}, "data": "
", "datasetInfos": [], "metadata": {}, "removedWidgets": [], "type": "html" } }, "output_type": "display_data" } ], "source": [ "import requests\n", "\n", "req = requests.get('https://data.cityofnewyork.us/api/geospatial/d3c5-ddgc?method=export&format=GeoJSON')\n", "with open('/dbfs/tmp/nyc_taxi_zones.geojson', 'wb') as f:\n", " f.write(req.content)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "application/vnd.databricks.v1+cell": { "inputWidgets": {}, "nuid": "eb739b60-a368-4dc9-9cf7-2f4346ffb06a", "showTitle": false, "title": "" } }, "outputs": [ { "data": { "text/html": [ "\n", "
+-----------------+--------------------+--------------------+--------------------+--------------------+--------------------+\n", " type| properties| geom_json| geom_internal| geom_wkt| geom_wkb|\n", "+-----------------+--------------------+--------------------+--------------------+--------------------+--------------------+\n", "FeatureCollection|{EWR, 1, 1, 0.000...|{"coordinates":[[...|{6, 4326, [[[-74....|MULTIPOLYGON (((-...|[01 06 00 00 00 0...|\n", "+-----------------+--------------------+--------------------+--------------------+--------------------+--------------------+\n", "\n", "
" ] }, "metadata": { "application/vnd.databricks.v1+output": { "addedWidgets": {}, "arguments": {}, "data": "
+-----------------+--------------------+--------------------+--------------------+--------------------+--------------------+\n| type| properties| geom_json| geom_internal| geom_wkt| geom_wkb|\n+-----------------+--------------------+--------------------+--------------------+--------------------+--------------------+\n|FeatureCollection|{EWR, 1, 1, 0.000...|{"coordinates":[[...|{6, 4326, [[[-74....|MULTIPOLYGON (((-...|[01 06 00 00 00 0...|\n+-----------------+--------------------+--------------------+--------------------+--------------------+--------------------+\n\n
", "datasetInfos": [], "metadata": {}, "removedWidgets": [], "type": "html" } }, "output_type": "display_data" } ], "source": [ "neighbourhoods = (\n", " spark.read\n", " .option(\"multiline\", \"true\")\n", " .format(\"json\")\n", " .load(\"dbfs:/tmp/nyc_taxi_zones.geojson\")\n", " \n", " # Extract geoJSON values for shapes\n", " .select(\"type\", explode(col(\"features\")).alias(\"feature\"))\n", " .select(\"type\", col(\"feature.properties\").alias(\"properties\"), to_json(col(\"feature.geometry\")).alias(\"geom_json\"))\n", " \n", " # Mosaic internal representation\n", " .withColumn(\"geom_internal\", mos.st_geomfromgeojson(\"geom_json\"))\n", " \n", " # WKT representation\n", " .withColumn(\"geom_wkt\", mos.st_aswkt(col(\"geom_internal\")))\n", " \n", " # WKB representation\n", " .withColumn(\"geom_wkb\", mos.st_aswkb(col(\"geom_internal\")))\n", " \n", " # Limit to only 1 shape\n", " .limit(1)\n", ")\n", "\n", "neighbourhoods.show()" ] }, { "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { "inputWidgets": {}, "nuid": "0c74d4f1-dbe6-4117-b752-4761b6a5d692", "showTitle": false, "title": "" } }, "source": [ "### Plot geometries from Spark dataset" ] }, { "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { "inputWidgets": {}, "nuid": "4109bf9f-479e-47f1-aea5-fb881136c49e", "showTitle": false, "title": "" } }, "source": [ "#### Internal geometry type" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "application/vnd.databricks.v1+cell": { "inputWidgets": {}, "nuid": "99be3b7a-3678-44bc-8d4a-ac35346cd724", "showTitle": false, "title": "" } }, "outputs": [ { "data": { "text/html": [ "\n", "
User Guide: https://docs.kepler.gl/docs/keplergl-jupyter\n", "
" ] }, "metadata": { "application/vnd.databricks.v1+output": { "addedWidgets": {}, "arguments": {}, "data": "
User Guide: https://docs.kepler.gl/docs/keplergl-jupyter\n
", "datasetInfos": [], "metadata": {}, "removedWidgets": [], "type": "html" } }, "output_type": "display_data" }, { "data": { "text/html": [ "" ] }, "metadata": { "application/vnd.databricks.v1+output": { "addedWidgets": {}, "arguments": {}, "data": "", "datasetInfos": [], "metadata": {}, "removedWidgets": [], "textData": null, "type": "htmlSandbox" } }, "output_type": "display_data" }, { "data": { "text/html": [ "Kepler.gl
" ] }, "metadata": { "application/vnd.databricks.v1+output": { "addedWidgets": {}, "arguments": {}, "data": "Kepler.gl
", "datasetInfos": [], "metadata": {}, "removedWidgets": [], "textData": null, "type": "htmlSandbox" } }, "output_type": "display_data" } ], "source": [ "%%mosaic_kepler\n", "neighbourhoods \"geom_internal\" \"geometry\"" ] }, { "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { "inputWidgets": {}, "nuid": "34b3328f-2966-4b6f-859f-325eeec04619", "showTitle": false, "title": "" } }, "source": [ "![mosaic kepler map example geometry](../images/kepler-1.png)" ] }, { "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { "inputWidgets": {}, "nuid": "f6d1c289-b007-4e40-b354-4c59fef9c2c4", "showTitle": false, "title": "" } }, "source": [ "#### WKT geometry type" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "application/vnd.databricks.v1+cell": { "inputWidgets": {}, "nuid": "84ee5934-ed9c-4ee8-affd-dd347f375eec", "showTitle": false, "title": "" } }, "outputs": [ { "data": { "text/html": [ "\n", "
User Guide: https://docs.kepler.gl/docs/keplergl-jupyter\n", "
" ] }, "metadata": { "application/vnd.databricks.v1+output": { "addedWidgets": {}, "arguments": {}, "data": "
User Guide: https://docs.kepler.gl/docs/keplergl-jupyter\n
", "datasetInfos": [], "metadata": {}, "removedWidgets": [], "type": "html" } }, "output_type": "display_data" }, { "data": { "text/html": [ "" ] }, "metadata": { "application/vnd.databricks.v1+output": { "addedWidgets": {}, "arguments": {}, "data": "", "datasetInfos": [], "metadata": {}, "removedWidgets": [], "textData": null, "type": "htmlSandbox" } }, "output_type": "display_data" }, { "data": { "text/html": [ "Kepler.gl
" ] }, "metadata": { "application/vnd.databricks.v1+output": { "addedWidgets": {}, "arguments": {}, "data": "Kepler.gl
", "datasetInfos": [], "metadata": {}, "removedWidgets": [], "textData": null, "type": "htmlSandbox" } }, "output_type": "display_data" } ], "source": [ "%%mosaic_kepler\n", "neighbourhoods \"geom_wkt\" \"geometry\"" ] }, { "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { "inputWidgets": {}, "nuid": "b81a56b6-3d7c-4c87-91b2-fecc2f77333c", "showTitle": false, "title": "" } }, "source": [ "![mosaic kepler map example geometry](../images/kepler-1.png)" ] }, { "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { "inputWidgets": {}, "nuid": "2acdd70b-decd-4654-8927-9f821e728864", "showTitle": false, "title": "" } }, "source": [ "#### WKB geometry type" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "application/vnd.databricks.v1+cell": { "inputWidgets": {}, "nuid": "0ddd1cad-dabf-4a9d-9e89-4406e2fda6e4", "showTitle": false, "title": "" } }, "outputs": [ { "data": { "text/html": [ "\n", "
User Guide: https://docs.kepler.gl/docs/keplergl-jupyter\n", "
" ] }, "metadata": { "application/vnd.databricks.v1+output": { "addedWidgets": {}, "arguments": {}, "data": "
User Guide: https://docs.kepler.gl/docs/keplergl-jupyter\n
", "datasetInfos": [], "metadata": {}, "removedWidgets": [], "type": "html" } }, "output_type": "display_data" }, { "data": { "text/html": [ "" ] }, "metadata": { "application/vnd.databricks.v1+output": { "addedWidgets": {}, "arguments": {}, "data": "", "datasetInfos": [], "metadata": {}, "removedWidgets": [], "textData": null, "type": "htmlSandbox" } }, "output_type": "display_data" }, { "data": { "text/html": [ "Kepler.gl
" ] }, "metadata": { "application/vnd.databricks.v1+output": { "addedWidgets": {}, "arguments": {}, "data": "Kepler.gl
", "datasetInfos": [], "metadata": {}, "removedWidgets": [], "textData": null, "type": "htmlSandbox" } }, "output_type": "display_data" } ], "source": [ "%%mosaic_kepler\n", "neighbourhoods \"geom_wkb\" \"geometry\"" ] }, { "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { "inputWidgets": {}, "nuid": "0f0deddf-3408-470d-8e9a-a6d066d23697", "showTitle": false, "title": "" } }, "source": [ "![mosaic kepler map example geometry](../images/kepler-1.png)" ] }, { "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { "inputWidgets": {}, "nuid": "590cb32e-2b3f-4ad7-b609-ffef9ae17967", "showTitle": false, "title": "" } }, "source": [ "### Plot geometries from table/view" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "application/vnd.databricks.v1+cell": { "inputWidgets": {}, "nuid": "782fcc2e-d868-4819-bb01-9c4ea4bafb36", "showTitle": false, "title": "" } }, "outputs": [ { "data": { "text/html": [ "\n", "
" ] }, "metadata": { "application/vnd.databricks.v1+output": { "addedWidgets": {}, "arguments": {}, "data": "
", "datasetInfos": [], "metadata": {}, "removedWidgets": [], "type": "html" } }, "output_type": "display_data" } ], "source": [ "neighbourhoods.createOrReplaceTempView(\"temp_view_neighbourhoods\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "application/vnd.databricks.v1+cell": { "inputWidgets": {}, "nuid": "fe84fa59-998d-4b9a-82c5-f280393ae244", "showTitle": false, "title": "" } }, "outputs": [ { "data": { "text/html": [ "\n", "
User Guide: https://docs.kepler.gl/docs/keplergl-jupyter\n", "
" ] }, "metadata": { "application/vnd.databricks.v1+output": { "addedWidgets": {}, "arguments": {}, "data": "
User Guide: https://docs.kepler.gl/docs/keplergl-jupyter\n
", "datasetInfos": [], "metadata": {}, "removedWidgets": [], "type": "html" } }, "output_type": "display_data" }, { "data": { "text/html": [ "" ] }, "metadata": { "application/vnd.databricks.v1+output": { "addedWidgets": {}, "arguments": {}, "data": "", "datasetInfos": [], "metadata": {}, "removedWidgets": [], "textData": null, "type": "htmlSandbox" } }, "output_type": "display_data" }, { "data": { "text/html": [ "Kepler.gl
" ] }, "metadata": { "application/vnd.databricks.v1+output": { "addedWidgets": {}, "arguments": {}, "data": "Kepler.gl
", "datasetInfos": [], "metadata": {}, "removedWidgets": [], "textData": null, "type": "htmlSandbox" } }, "output_type": "display_data" } ], "source": [ "%%mosaic_kepler\n", "\"temp_view_neighbourhoods\" \"geom_wkt\" \"geometry\"" ] }, { "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { "inputWidgets": {}, "nuid": "9d7dd0c3-64c5-43bb-aecd-287635929970", "showTitle": false, "title": "" } }, "source": [ "![mosaic kepler map example geometry](../images/kepler-1.png)" ] }, { "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { "inputWidgets": {}, "nuid": "d34979d6-45e5-4a6f-a12a-52e46cb1e946", "showTitle": false, "title": "" } }, "source": [ "### Plot H3 indexes" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "application/vnd.databricks.v1+cell": { "inputWidgets": {}, "nuid": "86cdd4f3-34c5-4365-8108-303ca63d97af", "showTitle": false, "title": "" } }, "outputs": [ { "data": { "text/html": [ "\n", "
+-------+------------------+--------------------+\n", "is_core| index_id| wkb|\n", "+-------+------------------+--------------------+\n", " true|617733150781997055|[01 03 00 00 00 0...|\n", " true|617733150856445951|[01 03 00 00 00 0...|\n", " true|617733150856970239|[01 03 00 00 00 0...|\n", " true|617733150784094207|[01 03 00 00 00 0...|\n", " true|617733150843600895|[01 03 00 00 00 0...|\n", " true|617733150843863039|[01 03 00 00 00 0...|\n", " true|617733150844125183|[01 03 00 00 00 0...|\n", " true|617733150784880639|[01 03 00 00 00 0...|\n", " true|617733150844387327|[01 03 00 00 00 0...|\n", " true|617733150844649471|[01 03 00 00 00 0...|\n", " true|617733150785404927|[01 03 00 00 00 0...|\n", " true|617733150844911615|[01 03 00 00 00 0...|\n", " true|617733150785667071|[01 03 00 00 00 0...|\n", " true|617733150845173759|[01 03 00 00 00 0...|\n", " true|617733150785929215|[01 03 00 00 00 0...|\n", " true|617733150786453503|[01 03 00 00 00 0...|\n", " true|617733150846222335|[01 03 00 00 00 0...|\n", " true|617733150847270911|[01 03 00 00 00 0...|\n", " true|617733150847795199|[01 03 00 00 00 0...|\n", " true|617733150848057343|[01 03 00 00 00 0...|\n", "+-------+------------------+--------------------+\n", "only showing top 20 rows\n", "\n", "
" ] }, "metadata": { "application/vnd.databricks.v1+output": { "addedWidgets": {}, "arguments": {}, "data": "
+-------+------------------+--------------------+\n|is_core| index_id| wkb|\n+-------+------------------+--------------------+\n| true|617733150781997055|[01 03 00 00 00 0...|\n| true|617733150856445951|[01 03 00 00 00 0...|\n| true|617733150856970239|[01 03 00 00 00 0...|\n| true|617733150784094207|[01 03 00 00 00 0...|\n| true|617733150843600895|[01 03 00 00 00 0...|\n| true|617733150843863039|[01 03 00 00 00 0...|\n| true|617733150844125183|[01 03 00 00 00 0...|\n| true|617733150784880639|[01 03 00 00 00 0...|\n| true|617733150844387327|[01 03 00 00 00 0...|\n| true|617733150844649471|[01 03 00 00 00 0...|\n| true|617733150785404927|[01 03 00 00 00 0...|\n| true|617733150844911615|[01 03 00 00 00 0...|\n| true|617733150785667071|[01 03 00 00 00 0...|\n| true|617733150845173759|[01 03 00 00 00 0...|\n| true|617733150785929215|[01 03 00 00 00 0...|\n| true|617733150786453503|[01 03 00 00 00 0...|\n| true|617733150846222335|[01 03 00 00 00 0...|\n| true|617733150847270911|[01 03 00 00 00 0...|\n| true|617733150847795199|[01 03 00 00 00 0...|\n| true|617733150848057343|[01 03 00 00 00 0...|\n+-------+------------------+--------------------+\nonly showing top 20 rows\n\n
", "datasetInfos": [], "metadata": {}, "removedWidgets": [], "type": "html" } }, "output_type": "display_data" } ], "source": [ "neighbourhood_chips = (neighbourhoods\n", " .limit(1)\n", " .select(mos.grid_tessellateexplode(\"geom_internal\", lit(9)))\n", " .select(\"index.*\")\n", " )\n", "\n", "neighbourhood_chips.show()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "application/vnd.databricks.v1+cell": { "inputWidgets": {}, "nuid": "84f6d1d7-b270-498b-b1fe-fe6d17e0e713", "showTitle": false, "title": "" } }, "outputs": [ { "data": { "text/html": [ "\n", "
User Guide: https://docs.kepler.gl/docs/keplergl-jupyter\n", "
" ] }, "metadata": { "application/vnd.databricks.v1+output": { "addedWidgets": {}, "arguments": {}, "data": "
User Guide: https://docs.kepler.gl/docs/keplergl-jupyter\n
", "datasetInfos": [], "metadata": {}, "removedWidgets": [], "type": "html" } }, "output_type": "display_data" }, { "data": { "text/html": [ "" ] }, "metadata": { "application/vnd.databricks.v1+output": { "addedWidgets": {}, "arguments": {}, "data": "", "datasetInfos": [], "metadata": {}, "removedWidgets": [], "textData": null, "type": "htmlSandbox" } }, "output_type": "display_data" }, { "data": { "text/html": [ "Kepler.gl
" ] }, "metadata": { "application/vnd.databricks.v1+output": { "addedWidgets": {}, "arguments": {}, "data": "Kepler.gl
", "datasetInfos": [], "metadata": {}, "removedWidgets": [], "textData": null, "type": "htmlSandbox" } }, "output_type": "display_data" } ], "source": [ "%%mosaic_kepler\n", "neighbourhood_chips \"index_id\" \"h3\"" ] }, { "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { "inputWidgets": {}, "nuid": "a9d921cb-5994-42a9-9518-626486cd1227", "showTitle": false, "title": "" } }, "source": [ "![mosaic kepler map example H3 indexes](../images/kepler-2.png)" ] }, { "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { "inputWidgets": {}, "nuid": "64cb0e7f-be2e-48f9-bea8-35a147e3f60c", "showTitle": false, "title": "" } }, "source": [ "### Plot H3 chips" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "application/vnd.databricks.v1+cell": { "inputWidgets": {}, "nuid": "dbe03c2b-a2c2-4b17-a44c-16891e81d3d0", "showTitle": false, "title": "" } }, "outputs": [ { "data": { "text/html": [ "\n", "
User Guide: https://docs.kepler.gl/docs/keplergl-jupyter\n", "
" ] }, "metadata": { "application/vnd.databricks.v1+output": { "addedWidgets": {}, "arguments": {}, "data": "
User Guide: https://docs.kepler.gl/docs/keplergl-jupyter\n
", "datasetInfos": [], "metadata": {}, "removedWidgets": [], "type": "html" } }, "output_type": "display_data" }, { "data": { "text/html": [ "" ] }, "metadata": { "application/vnd.databricks.v1+output": { "addedWidgets": {}, "arguments": {}, "data": "", "datasetInfos": [], "metadata": {}, "removedWidgets": [], "textData": null, "type": "htmlSandbox" } }, "output_type": "display_data" }, { "data": { "text/html": [ "Kepler.gl
" ] }, "metadata": { "application/vnd.databricks.v1+output": { "addedWidgets": {}, "arguments": {}, "data": "Kepler.gl
", "datasetInfos": [], "metadata": {}, "removedWidgets": [], "textData": null, "type": "htmlSandbox" } }, "output_type": "display_data" } ], "source": [ "%%mosaic_kepler\n", "neighbourhood_chips \"wkb\" \"geometry\"" ] }, { "cell_type": "markdown", "metadata": { "application/vnd.databricks.v1+cell": { "inputWidgets": {}, "nuid": "6c8244f4-4c03-4d12-9d06-d5174ef94626", "showTitle": false, "title": "" } }, "source": [ "![mosaic kepler map example h3 chips](../images/kepler-3.png)" ] } ], "metadata": { "application/vnd.databricks.v1+notebook": { "dashboards": [], "language": "python", "notebookMetadata": { "pythonIndentUnit": 2 }, "notebookName": "kepler", "notebookOrigID": 2874007245243191, "widgets": {} }, "language_info": { "name": "python" } }, "nbformat": 4, "nbformat_minor": 0 }