cboettig commited on
Commit
02e2c8a
1 Parent(s): ee759b5
Files changed (1) hide show
  1. explore.ipynb +327 -0
explore.ipynb ADDED
@@ -0,0 +1,327 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "code",
5
+ "execution_count": 6,
6
+ "id": "f7e6298c-d886-432a-a1b7-c3fee914c24f",
7
+ "metadata": {},
8
+ "outputs": [],
9
+ "source": [
10
+ "# boilerplate setup\n",
11
+ "import leafmap.maplibregl as leafmap\n",
12
+ "import ibis\n",
13
+ "from ibis import _\n",
14
+ "\n",
15
+ "conn = ibis.duckdb.connect()\n",
16
+ "ca_parquet = \"https://data.source.coop/cboettig/ca30x30/ca_areas.parquet\"\n",
17
+ "# or use local copy:\n",
18
+ "ca_parquet = \"/home/rstudio/source.coop/cboettig/ca30x30/ca_areas.parquet\"\n"
19
+ ]
20
+ },
21
+ {
22
+ "cell_type": "code",
23
+ "execution_count": 104,
24
+ "id": "bd9c2561-334a-40d6-adf0-ce20a991a9a7",
25
+ "metadata": {},
26
+ "outputs": [
27
+ {
28
+ "data": {
29
+ "application/vnd.jupyter.widget-view+json": {
30
+ "model_id": "f46fb7de69784a4e89763c5432d06a40",
31
+ "version_major": 2,
32
+ "version_minor": 1
33
+ },
34
+ "text/plain": [
35
+ "Map(height='600px', map_options={'bearing': 0, 'center': (0, 20), 'pitch': 0, 'style': 'https://basemaps.carto…"
36
+ ]
37
+ },
38
+ "execution_count": 104,
39
+ "metadata": {},
40
+ "output_type": "execute_result"
41
+ }
42
+ ],
43
+ "source": [
44
+ "# Visualize the full data to show overlap:\n",
45
+ "import leafmap.maplibregl as leafmap\n",
46
+ "ca_pmtiles = \"https://data.source.coop/cboettig/ca30x30/ca_areas.pmtiles\"\n",
47
+ "style = {\n",
48
+ " \"version\": 8,\n",
49
+ " \"sources\": {\n",
50
+ " \"ca\": {\n",
51
+ " \"type\": \"vector\",\n",
52
+ " \"url\": \"pmtiles://\" + ca_pmtiles,\n",
53
+ " }},\n",
54
+ " \"layers\": [{\n",
55
+ " \"id\": \"layer1\",\n",
56
+ " \"source\": \"ca\",\n",
57
+ " \"source-layer\": \"CA_Cons_Areas_parentlyr_Merge_ecofix\",\n",
58
+ " \"type\": \"fill\",\n",
59
+ " \"filter\": [\"<\",[\"get\", \"reGAP\"], 3,],\n",
60
+ " \"paint\": {\n",
61
+ " \"fill-color\": {\n",
62
+ " 'property': 'Release_Year',\n",
63
+ " 'type': 'categorical',\n",
64
+ " 'stops': [[2023, \"#FF000080\"], # note RGBA code includes alpha\n",
65
+ " [2024, \"#0000FF80\"]]\n",
66
+ " },\n",
67
+ " \"fill-opacity\": 0.5 # ignored? \n",
68
+ " }\n",
69
+ " }]}\n",
70
+ "\n",
71
+ "m = leafmap.Map(style=\"positron\")\n",
72
+ "m.add_pmtiles(ca_pmtiles, style = style)\n",
73
+ "m.to_html(\"ca30x30_gap12.html\") # save a copy\n",
74
+ "m\n"
75
+ ]
76
+ },
77
+ {
78
+ "cell_type": "code",
79
+ "execution_count": 158,
80
+ "id": "3ceed4c7-b7a2-436e-bca1-c57e7133cdf6",
81
+ "metadata": {},
82
+ "outputs": [],
83
+ "source": [
84
+ "# Make things faster. Let's zoom in on a subset of the data. \n",
85
+ "\n",
86
+ "from ibis.interactive import *\n",
87
+ "\n",
88
+ "ca_parquet = \"/home/rstudio/source.coop/cboettig/ca30x30/ca_areas.parquet\"\n",
89
+ "con = ibis.duckdb.connect()\n",
90
+ "\n",
91
+ "# Ugly code for a cookie cutter zoom\n",
92
+ "# Turns out we could have just used filter(_.UNIT_NAME == \"Angeles National Forest\")\n",
93
+ "import geopandas as gpd\n",
94
+ "from shapely.geometry import box\n",
95
+ "min_lon, min_lat, max_lon, max_lat = -118.4, 34., -117.545715, 34.495239\n",
96
+ "bbox = box(min_lon, min_lat, max_lon, max_lat)\n",
97
+ "gdf = gpd.GeoDataFrame({'geometry': [bbox]}, crs=\"EPSG:4326\")\n",
98
+ "cookie_cutter = ibis.literal(gdf.geometry.iloc[0])\n",
99
+ "\n",
100
+ "angeles_forest = (\n",
101
+ " con.read_parquet(ca_parquet)\n",
102
+ " .cast({\"SHAPE\": \"geometry\"})\n",
103
+ " .mutate(geom = _.SHAPE.convert(\"epsg:3310\",\"epsg:4326\"))\n",
104
+ " .filter( _.geom.intersects(cookie_cutter))\n",
105
+ " .filter(_.UNIT_NAME == \"Angeles National Forest\") \n",
106
+ " .select(\"reGAP\", \"UNIT_NAME\", \"MNG_AGNCY\", \"Release_Year\", \"OBJECTID\", \"geom\")\n",
107
+ ")\n",
108
+ "\n"
109
+ ]
110
+ },
111
+ {
112
+ "cell_type": "code",
113
+ "execution_count": 159,
114
+ "id": "3ed21429-a75e-4b52-b6eb-d41c8f051883",
115
+ "metadata": {},
116
+ "outputs": [],
117
+ "source": [
118
+ "# split 2023 & 2024\n",
119
+ "allgap_2023 = angeles_forest.filter(_.Release_Year == 2023)\n",
120
+ "allgap_2024 = angeles_forest.filter(_.Release_Year == 2024)"
121
+ ]
122
+ },
123
+ {
124
+ "cell_type": "code",
125
+ "execution_count": 160,
126
+ "id": "7bfac88a-822a-4e4e-883e-b7e51544abd1",
127
+ "metadata": {},
128
+ "outputs": [],
129
+ "source": [
130
+ "# split but with reGAP < 3\n",
131
+ "la_2023 = allgap_2023.filter(_.reGAP < 3)\n",
132
+ "la_2024 = allgap_2024.filter(_.reGAP < 3)\n",
133
+ "\n",
134
+ "# THIS IS ALMOST WHAT WE WANT, BUT for some issues\n",
135
+ "# All reGAP 1,2 polygons in 2024 that don't intersect 2023\n",
136
+ "la_intersects = la_2024.anti_join(la_2023, _.geom.intersects(la_2023.geom))\n"
137
+ ]
138
+ },
139
+ {
140
+ "cell_type": "code",
141
+ "execution_count": 164,
142
+ "id": "ddae1f9e-988a-4aed-bc71-f1c40d2bb0ea",
143
+ "metadata": {
144
+ "scrolled": true
145
+ },
146
+ "outputs": [
147
+ {
148
+ "data": {
149
+ "application/vnd.jupyter.widget-view+json": {
150
+ "model_id": "52b3b9c8cb1b4bbf8fe16aebef4d2e25",
151
+ "version_major": 2,
152
+ "version_minor": 1
153
+ },
154
+ "text/plain": [
155
+ "Map(height='600px', map_options={'bearing': 0, 'center': (0, 20), 'pitch': 0, 'style': 'https://basemaps.carto…"
156
+ ]
157
+ },
158
+ "execution_count": 164,
159
+ "metadata": {},
160
+ "output_type": "execute_result"
161
+ }
162
+ ],
163
+ "source": [
164
+ "import leafmap.maplibregl as leafmap\n",
165
+ "#la_gdf = la_2024.execute()\n",
166
+ "\n",
167
+ "\n",
168
+ "release_year = {'property': 'Release_Year', 'type': 'categorical','stops': [[2023, \"#FF000080\"], [2024, \"#0000FF80\"]]},\n",
169
+ "gap = {\n",
170
+ " 'property': 'reGAP',\n",
171
+ " 'type': 'categorical',\n",
172
+ " 'stops': [\n",
173
+ " [1, \"#26633d\"],\n",
174
+ " [2, \"#879647\"],\n",
175
+ " [3, \"#BBBBBB\"],\n",
176
+ " [4, \"#F8F8F8\"]]\n",
177
+ " }\n",
178
+ "\n",
179
+ "paint = {\"fill-color\": gap, \"fill-opacity\": 0.5}\n",
180
+ "\n",
181
+ "m = leafmap.Map(style=\"positron\")\n",
182
+ "m.add_gdf(allgap_2023.execute(),layer_type=\"fill\", name = \"allgap_2023\", paint=paint)\n",
183
+ "m.add_gdf(allgap_2024.execute(),layer_type=\"fill\", name = \"allgap_2024\", paint=paint)\n",
184
+ "#m.add_gdf(la_2023.execute(),layer_type=\"fill\", name = \"2023\", paint=paint)\n",
185
+ "#m.add_gdf(la_2024.execute(),layer_type=\"fill\", name = \"2024\", paint=paint)\n",
186
+ "m.add_gdf(la_intersects.execute(),layer_type=\"fill\", name = \"anti_intersects\", paint=paint)\n",
187
+ "#m.add_gdf(intersects.execute(), layer_type=\"fill\", name = \"overlapping\", paint=paint)\n",
188
+ "m.add_layer_control()\n",
189
+ "m"
190
+ ]
191
+ },
192
+ {
193
+ "cell_type": "code",
194
+ "execution_count": 170,
195
+ "id": "6832038f-0342-495f-8cbf-9a8f3cc83aac",
196
+ "metadata": {},
197
+ "outputs": [
198
+ {
199
+ "data": {
200
+ "text/html": [
201
+ "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\">┏━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━┓\n",
202
+ "┃<span style=\"font-weight: bold\"> OBJECTID_right </span>┃<span style=\"font-weight: bold\"> geom </span>┃<span style=\"font-weight: bold\"> reGAP </span>┃\n",
203
+ "┡━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━┩\n",
204
+ "│ <span style=\"color: #7f7f7f; text-decoration-color: #7f7f7f\">int64</span> │ <span style=\"color: #7f7f7f; text-decoration-color: #7f7f7f\">geospatial:geometry</span> │ <span style=\"color: #7f7f7f; text-decoration-color: #7f7f7f\">int16</span> │\n",
205
+ "├────────────────┼──────────────────────────────────────────────────────────────────────────────────┼───────┤\n",
206
+ "│ <span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">21105</span> │ <span style=\"font-weight: bold\">&lt;</span><span style=\"color: #ff00ff; text-decoration-color: #ff00ff; font-weight: bold\">MULTIPOLYGON</span><span style=\"color: #000000; text-decoration-color: #000000\"> Z </span><span style=\"color: #000000; text-decoration-color: #000000; font-weight: bold\">(((</span><span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">-118.017</span><span style=\"color: #000000; text-decoration-color: #000000\"> </span><span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">34.499</span><span style=\"color: #000000; text-decoration-color: #000000\"> </span><span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">0</span><span style=\"color: #000000; text-decoration-color: #000000\">, </span><span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">-118.019</span><span style=\"color: #000000; text-decoration-color: #000000\"> </span><span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">34.499</span><span style=\"color: #000000; text-decoration-color: #000000\"> </span><span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">0</span><span style=\"color: #000000; text-decoration-color: #000000\">, </span><span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">-118.019</span><span style=\"color: #000000; text-decoration-color: #000000\"> </span><span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">34.499</span><span style=\"color: #000000; text-decoration-color: #000000\"> </span><span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">0</span><span style=\"color: #000000; text-decoration-color: #000000\">, </span><span style=\"color: #808000; text-decoration-color: #808000\">...</span><span style=\"font-weight: bold\">&gt;</span> │ <span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">2</span> │\n",
207
+ "│ <span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">22618</span> │ <span style=\"font-weight: bold\">&lt;</span><span style=\"color: #ff00ff; text-decoration-color: #ff00ff; font-weight: bold\">MULTIPOLYGON</span><span style=\"color: #000000; text-decoration-color: #000000\"> Z </span><span style=\"color: #000000; text-decoration-color: #000000; font-weight: bold\">(((</span><span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">-118.297</span><span style=\"color: #000000; text-decoration-color: #000000\"> </span><span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">34.433</span><span style=\"color: #000000; text-decoration-color: #000000\"> </span><span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">0</span><span style=\"color: #000000; text-decoration-color: #000000\">, </span><span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">-118.298</span><span style=\"color: #000000; text-decoration-color: #000000\"> </span><span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">34.433</span><span style=\"color: #000000; text-decoration-color: #000000\"> </span><span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">0</span><span style=\"color: #000000; text-decoration-color: #000000\">, </span><span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">-118.303</span><span style=\"color: #000000; text-decoration-color: #000000\"> </span><span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">34.433</span><span style=\"color: #000000; text-decoration-color: #000000\"> </span><span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">0</span><span style=\"color: #000000; text-decoration-color: #000000\">, </span><span style=\"color: #808000; text-decoration-color: #808000\">...</span><span style=\"font-weight: bold\">&gt;</span> │ <span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">2</span> │\n",
208
+ "└────────────────┴──────────────────────────────────────────────────────────────────────────────────┴───────┘\n",
209
+ "</pre>\n"
210
+ ],
211
+ "text/plain": [
212
+ "┏━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━┓\n",
213
+ "┃\u001b[1m \u001b[0m\u001b[1mOBJECTID_right\u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1mgeom\u001b[0m\u001b[1m \u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1mreGAP\u001b[0m\u001b[1m \u001b[0m┃\n",
214
+ "┡━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━┩\n",
215
+ "│ \u001b[2mint64\u001b[0m │ \u001b[2mgeospatial:geometry\u001b[0m │ \u001b[2mint16\u001b[0m │\n",
216
+ "├────────────────┼──────────────────────────────────────────────────────────────────────────────────┼───────┤\n",
217
+ "│ \u001b[1;36m21105\u001b[0m │ \u001b[1m<\u001b[0m\u001b[1;95mMULTIPOLYGON\u001b[0m\u001b[39m Z \u001b[0m\u001b[1;39m(\u001b[0m\u001b[1;39m(\u001b[0m\u001b[1;39m(\u001b[0m\u001b[1;36m-118.017\u001b[0m\u001b[39m \u001b[0m\u001b[1;36m34.499\u001b[0m\u001b[39m \u001b[0m\u001b[1;36m0\u001b[0m\u001b[39m, \u001b[0m\u001b[1;36m-118.019\u001b[0m\u001b[39m \u001b[0m\u001b[1;36m34.499\u001b[0m\u001b[39m \u001b[0m\u001b[1;36m0\u001b[0m\u001b[39m, \u001b[0m\u001b[1;36m-118.019\u001b[0m\u001b[39m \u001b[0m\u001b[1;36m34.499\u001b[0m\u001b[39m \u001b[0m\u001b[1;36m0\u001b[0m\u001b[39m, \u001b[0m\u001b[33m...\u001b[0m\u001b[1m>\u001b[0m │ \u001b[1;36m2\u001b[0m │\n",
218
+ "│ \u001b[1;36m22618\u001b[0m │ \u001b[1m<\u001b[0m\u001b[1;95mMULTIPOLYGON\u001b[0m\u001b[39m Z \u001b[0m\u001b[1;39m(\u001b[0m\u001b[1;39m(\u001b[0m\u001b[1;39m(\u001b[0m\u001b[1;36m-118.297\u001b[0m\u001b[39m \u001b[0m\u001b[1;36m34.433\u001b[0m\u001b[39m \u001b[0m\u001b[1;36m0\u001b[0m\u001b[39m, \u001b[0m\u001b[1;36m-118.298\u001b[0m\u001b[39m \u001b[0m\u001b[1;36m34.433\u001b[0m\u001b[39m \u001b[0m\u001b[1;36m0\u001b[0m\u001b[39m, \u001b[0m\u001b[1;36m-118.303\u001b[0m\u001b[39m \u001b[0m\u001b[1;36m34.433\u001b[0m\u001b[39m \u001b[0m\u001b[1;36m0\u001b[0m\u001b[39m, \u001b[0m\u001b[33m...\u001b[0m\u001b[1m>\u001b[0m │ \u001b[1;36m2\u001b[0m │\n",
219
+ "└────────────────┴──────────────────────────────────────────────────────────────────────────────────┴───────┘"
220
+ ]
221
+ },
222
+ "execution_count": 170,
223
+ "metadata": {},
224
+ "output_type": "execute_result"
225
+ }
226
+ ],
227
+ "source": [
228
+ "## It intersects these, even though it looks like it shouldn't\n",
229
+ "\n",
230
+ "hits = (la_2024\n",
231
+ " .filter(_.OBJECTID == 111913) # appears to be the new stuff\n",
232
+ " .join(la_2023, _.geom.intersects(la_2023.geom), how=\"left\")\n",
233
+ " .select(_.OBJECTID_right, _.geom_right, _.reGAP)\n",
234
+ " .rename(geom = \"geom_right\")\n",
235
+ ")\n",
236
+ "hits\n",
237
+ "\n"
238
+ ]
239
+ },
240
+ {
241
+ "cell_type": "code",
242
+ "execution_count": 162,
243
+ "id": "b41b0af6-635c-4ffb-9f19-342284f18e62",
244
+ "metadata": {},
245
+ "outputs": [
246
+ {
247
+ "data": {
248
+ "application/vnd.jupyter.widget-view+json": {
249
+ "model_id": "64cd02336fac4d9884e2c7ed66472146",
250
+ "version_major": 2,
251
+ "version_minor": 0
252
+ },
253
+ "text/plain": [
254
+ "FloatProgress(value=0.0, layout=Layout(width='auto'), style=ProgressStyle(bar_color='black'))"
255
+ ]
256
+ },
257
+ "metadata": {},
258
+ "output_type": "display_data"
259
+ },
260
+ {
261
+ "data": {
262
+ "application/vnd.jupyter.widget-view+json": {
263
+ "model_id": "9729a29cd0f345fd9a9bc46dec111744",
264
+ "version_major": 2,
265
+ "version_minor": 1
266
+ },
267
+ "text/plain": [
268
+ "Map(height='600px', map_options={'bearing': 0, 'center': (0, 20), 'pitch': 0, 'style': 'https://basemaps.carto…"
269
+ ]
270
+ },
271
+ "execution_count": 162,
272
+ "metadata": {},
273
+ "output_type": "execute_result"
274
+ }
275
+ ],
276
+ "source": [
277
+ "import leafmap.maplibregl as leafmap\n",
278
+ "import ibis\n",
279
+ "from ibis import _\n",
280
+ "## Minimal look\n",
281
+ "areas = (\n",
282
+ " con.read_parquet(\"https://data.source.coop/cboettig/ca30x30/ca_areas.parquet\")\n",
283
+ " .cast({\"SHAPE\": \"geometry\"})\n",
284
+ " .rename(geom = \"SHAPE\")\n",
285
+ " .select(\"Release_Year\", \"reGAP\", \"OBJECTID\", \"geom\")\n",
286
+ " .mutate(geom = _.geom.convert(\"epsg:3310\",\"epsg:4326\"))\n",
287
+ " \n",
288
+ ")\n",
289
+ "\n",
290
+ "#areas.filter(_.Release_Year join(la_2023, _.geom.intersects(la_2023.geom), how=\"left\")\n",
291
+ "\n",
292
+ "\n",
293
+ "new = areas.filter(_.OBJECTID == 111913)\n",
294
+ "old = areas.filter(_.OBJECTID == 21105)\n",
295
+ "# old.geom.difference(new.geom)\n",
296
+ "\n",
297
+ "\n",
298
+ "m = leafmap.Map(style=\"positron\")\n",
299
+ "#m.add_gdf(old.execute(),layer_type=\"fill\", name = \"2023\", paint=paint)\n",
300
+ "m.add_gdf(new.execute(),layer_type=\"fill\", name = \"2024\", paint=paint)\n",
301
+ "m.add_layer_control()\n",
302
+ "m"
303
+ ]
304
+ }
305
+ ],
306
+ "metadata": {
307
+ "kernelspec": {
308
+ "display_name": "Python 3 (ipykernel)",
309
+ "language": "python",
310
+ "name": "python3"
311
+ },
312
+ "language_info": {
313
+ "codemirror_mode": {
314
+ "name": "ipython",
315
+ "version": 3
316
+ },
317
+ "file_extension": ".py",
318
+ "mimetype": "text/x-python",
319
+ "name": "python",
320
+ "nbconvert_exporter": "python",
321
+ "pygments_lexer": "ipython3",
322
+ "version": "3.10.12"
323
+ }
324
+ },
325
+ "nbformat": 4,
326
+ "nbformat_minor": 5
327
+ }