{
"cells": [
{
"cell_type": "markdown",
"id": "7b1f3038-fbf6-424e-bfc1-14f23e5559d1",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": ""
},
"tags": []
},
"source": [
"## Xarray engine: overview"
]
},
{
"cell_type": "markdown",
"id": "17eec2d0-2841-4f3f-8786-4e5d8d12a83b",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": ""
},
"tags": []
},
"source": [
"Earthkit-data comes with its own Xarray engine called \"earthkit\" to perform conversions between GRIB and Xarray data."
]
},
{
"cell_type": "markdown",
"id": "34202724-dcba-479c-b3ee-643326a73e0b",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": ""
},
"tags": []
},
"source": [
"To start with, we get the example data will use in this notebook and read it into a GRIB fieldlist."
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "6c4a57a5-8693-4886-957e-669c31fa9cd3",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": ""
},
"tags": []
},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "8b2a598b3f264e2aa75a0cddab1650d2",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"pl.grib: 0%| | 0.00/48.8k [00:00, ?B/s]"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"import earthkit.data as ekd\n",
"ds_fl = ekd.from_source(\"sample\", \"pl.grib\")"
]
},
{
"cell_type": "markdown",
"id": "17281c38-a27c-4e93-9303-098871b3c3fa",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": ""
},
"tags": []
},
"source": [
"### Creating Xarray"
]
},
{
"cell_type": "raw",
"id": "4658ddfd-8652-4c24-a9d3-0cfd5edeb789",
"metadata": {
"editable": true,
"raw_mimetype": "text/restructuredtext",
"slideshow": {
"slide_type": ""
},
"tags": []
},
"source": [
"To convert a GRIB fieldlist to Xarray we need to use :py:meth:`~data.readers.grib.index.GribFieldList.to_xarray`."
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "7bb7962d-e399-4d5b-bc73-78c619a4304d",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": ""
},
"tags": []
},
"outputs": [
{
"data": {
"text/html": [
"
\n",
"
<xarray.Dataset> Size: 176kB\n",
"Dimensions: (forecast_reference_time: 4, step: 2, level: 2,\n",
" latitude: 19, longitude: 36)\n",
"Coordinates:\n",
" * forecast_reference_time (forecast_reference_time) datetime64[ns] 32B 202...\n",
" * step (step) timedelta64[ns] 16B 00:00:00 06:00:00\n",
" * level (level) int64 16B 500 700\n",
" * latitude (latitude) float64 152B 90.0 80.0 ... -80.0 -90.0\n",
" * longitude (longitude) float64 288B 0.0 10.0 ... 340.0 350.0\n",
"Data variables:\n",
" r (forecast_reference_time, step, level, latitude, longitude) float64 88kB ...\n",
" t (forecast_reference_time, step, level, latitude, longitude) float64 88kB ...\n",
"Attributes:\n",
" class: od\n",
" stream: oper\n",
" levtype: pl\n",
" type: fc\n",
" expver: 0001\n",
" date: 20240603\n",
" time: 0\n",
" domain: g\n",
" number: 0\n",
" Conventions: CF-1.8\n",
" institution: ECMWF- forecast_reference_time: 4
- step: 2
- level: 2
- latitude: 19
- longitude: 36
forecast_reference_time
(forecast_reference_time)
datetime64[ns]
2024-06-03 ... 2024-06-04T12:00:00
- standard_name :
- forecast_reference_time
- long_name :
- initial time of forecast
array(['2024-06-03T00:00:00.000000000', '2024-06-03T12:00:00.000000000',\n",
" '2024-06-04T00:00:00.000000000', '2024-06-04T12:00:00.000000000'],\n",
" dtype='datetime64[ns]')step
(step)
timedelta64[ns]
00:00:00 06:00:00
array([ 0, 21600000000000], dtype='timedelta64[ns]')
level
(level)
int64
500 700
- units :
- hPa
- positive :
- down
- stored_direction :
- decreasing
- standard_name :
- air_pressure
- long_name :
- pressure
latitude
(latitude)
float64
90.0 80.0 70.0 ... -80.0 -90.0
- units :
- degrees_north
- standard_name :
- latitude
- long_name :
- latitude
array([ 90., 80., 70., 60., 50., 40., 30., 20., 10., 0., -10., -20.,\n",
" -30., -40., -50., -60., -70., -80., -90.])longitude
(longitude)
float64
0.0 10.0 20.0 ... 330.0 340.0 350.0
- units :
- degrees_east
- standard_name :
- longitude
- long_name :
- longitude
array([ 0., 10., 20., 30., 40., 50., 60., 70., 80., 90., 100., 110.,\n",
" 120., 130., 140., 150., 160., 170., 180., 190., 200., 210., 220., 230.,\n",
" 240., 250., 260., 270., 280., 290., 300., 310., 320., 330., 340., 350.])
r
(forecast_reference_time, step, level, latitude, longitude)
float64
...
- param :
- r
- standard_name :
- relative_humidity
- long_name :
- Relative humidity
- paramId :
- 157
- units :
- %
- _earthkit :
- {'message': b"GRIB\\x00\\x00l\\x01\\x00\\x004\\x80b\\x9a\\xff\\x80\\x9dd\\x01\\xf4\\x18\\x06\\x03\\x00\\x00\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x01\\t\\x04\\x010001\\x00\\x00\\x00\\x00\\x00 \\x00\\xff\\x00\\x00$\\x00\\x13\\x01_\\x90\\x00\\x00\\x00\\x80\\x81_\\x90\\x05W0'\\x10'\\x10\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x0c\\x00\\x80\\x02D\\xb9}n\\x00\\x007777", 'bitsPerValue': 16}
[10944 values with dtype=float64]
t
(forecast_reference_time, step, level, latitude, longitude)
float64
...
- param :
- t
- standard_name :
- air_temperature
- long_name :
- Temperature
- paramId :
- 130
- units :
- K
- _earthkit :
- {'message': b"GRIB\\x00\\x00l\\x01\\x00\\x004\\x80b\\x9a\\xff\\x80\\x82d\\x01\\xf4\\x18\\x06\\x03\\x00\\x00\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x15\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x01\\t\\x04\\x010001\\x00\\x00\\x00\\x00\\x00 \\x00\\xff\\x00\\x00$\\x00\\x13\\x01_\\x90\\x00\\x00\\x00\\x80\\x81_\\x90\\x05W0'\\x10'\\x10\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x0c\\x00\\x80\\x02D\\xb9}n\\x00\\x007777", 'bitsPerValue': 16}
[10944 values with dtype=float64]
PandasIndex
PandasIndex(DatetimeIndex(['2024-06-03 00:00:00', '2024-06-03 12:00:00',\n",
" '2024-06-04 00:00:00', '2024-06-04 12:00:00'],\n",
" dtype='datetime64[ns]', name='forecast_reference_time', freq=None))PandasIndex
PandasIndex(TimedeltaIndex(['0 days 00:00:00', '0 days 06:00:00'], dtype='timedelta64[ns]', name='step', freq=None))
PandasIndex
PandasIndex(Index([500, 700], dtype='int64', name='level'))
PandasIndex
PandasIndex(Index([ 90.0, 80.0, 70.0, 60.0, 50.0, 40.0, 30.0, 20.0, 10.0, 0.0,\n",
" -10.0, -20.0, -30.0, -40.0, -50.0, -60.0, -70.0, -80.0, -90.0],\n",
" dtype='float64', name='latitude'))PandasIndex
PandasIndex(Index([ 0.0, 10.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0, 90.0,\n",
" 100.0, 110.0, 120.0, 130.0, 140.0, 150.0, 160.0, 170.0, 180.0, 190.0,\n",
" 200.0, 210.0, 220.0, 230.0, 240.0, 250.0, 260.0, 270.0, 280.0, 290.0,\n",
" 300.0, 310.0, 320.0, 330.0, 340.0, 350.0],\n",
" dtype='float64', name='longitude'))
- class :
- od
- stream :
- oper
- levtype :
- pl
- type :
- fc
- expver :
- 0001
- date :
- 20240603
- time :
- 0
- domain :
- g
- number :
- 0
- Conventions :
- CF-1.8
- institution :
- ECMWF
"
],
"text/plain": [
" Size: 176kB\n",
"Dimensions: (forecast_reference_time: 4, step: 2, level: 2,\n",
" latitude: 19, longitude: 36)\n",
"Coordinates:\n",
" * forecast_reference_time (forecast_reference_time) datetime64[ns] 32B 202...\n",
" * step (step) timedelta64[ns] 16B 00:00:00 06:00:00\n",
" * level (level) int64 16B 500 700\n",
" * latitude (latitude) float64 152B 90.0 80.0 ... -80.0 -90.0\n",
" * longitude (longitude) float64 288B 0.0 10.0 ... 340.0 350.0\n",
"Data variables:\n",
" r (forecast_reference_time, step, level, latitude, longitude) float64 88kB ...\n",
" t (forecast_reference_time, step, level, latitude, longitude) float64 88kB ...\n",
"Attributes:\n",
" class: od\n",
" stream: oper\n",
" levtype: pl\n",
" type: fc\n",
" expver: 0001\n",
" date: 20240603\n",
" time: 0\n",
" domain: g\n",
" number: 0\n",
" Conventions: CF-1.8\n",
" institution: ECMWF"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"ds = ds_fl.to_xarray()\n",
"ds"
]
},
{
"cell_type": "raw",
"id": "30df618b-90f1-41c5-a6eb-da3f451d4c3e",
"metadata": {
"editable": true,
"raw_mimetype": "text/restructuredtext",
"slideshow": {
"slide_type": ""
},
"tags": []
},
"source": [
":py:meth:`~data.readers.grib.index.GribFieldList.to_xarray` has a large number of keyword arguments to control how the Xarray dataset is generated. To simplify the usage we can define :ref:`profiles ` providing custom defaults for most of the keyword arguments. At the moment, there are 2 pre-defined profiles available: \"mars\" (the default) and \"grib\". We can pass them :py:meth:`~data.readers.grib.index.GribFieldList.to_xarray` via the ``profile`` kwarg."
]
},
{
"cell_type": "markdown",
"id": "6f7a1692-f382-4c14-88dc-df28a7522f9f",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": ""
},
"tags": []
},
"source": [
"### Writing back to GRIB"
]
},
{
"cell_type": "markdown",
"id": "f9324c45-127b-4971-92f0-553fa6ac8363",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": ""
},
"tags": []
},
"source": [
"This is an **experimental** feature!\n",
"\n",
"In order to write back the Xarray into a GRIB it has to keep the original variable attributes that the eartkit engine generated. By default, variable attributes are not kept in Xarray computations so we need to set the global Xarray ``keep_attrs`` option to enable it as shown in the following cell: "
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "e8e5d214-bf09-4a8a-a944-26f7e8fc5b8d",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": ""
},
"tags": []
},
"outputs": [],
"source": [
"import xarray as xr\n",
"xr.set_options(keep_attrs=True)\n",
"\n",
"ds = ds_fl.to_xarray()\n",
"ds += 1"
]
},
{
"cell_type": "markdown",
"id": "04d442d9-27f8-4f46-ae35-41a85d765f8d",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": ""
},
"tags": []
},
"source": [
"#### Generating a fieldlist"
]
},
{
"cell_type": "raw",
"id": "80eab6d7-bb06-4534-ab83-4c2e4c49fab6",
"metadata": {
"editable": true,
"raw_mimetype": "text/restructuredtext",
"slideshow": {
"slide_type": ""
},
"tags": []
},
"source": [
"To create GRIB fieldlist we need to call :py:meth:`to_fieldlist()` on the \"earthkit\" accessor. The result is an array fieldlist holding all the data in memory."
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "8c7856fb-c03a-4b27-976c-76724635bea7",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": ""
},
"tags": []
},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" | \n",
" centre | \n",
" shortName | \n",
" typeOfLevel | \n",
" level | \n",
" dataDate | \n",
" dataTime | \n",
" stepRange | \n",
" dataType | \n",
" number | \n",
" gridType | \n",
"
\n",
" \n",
" \n",
" \n",
" | 0 | \n",
" ecmf | \n",
" r | \n",
" isobaricInhPa | \n",
" 500 | \n",
" 20240603 | \n",
" 0 | \n",
" 0 | \n",
" fc | \n",
" 0 | \n",
" regular_ll | \n",
"
\n",
" \n",
" | 1 | \n",
" ecmf | \n",
" r | \n",
" isobaricInhPa | \n",
" 700 | \n",
" 20240603 | \n",
" 0 | \n",
" 0 | \n",
" fc | \n",
" 0 | \n",
" regular_ll | \n",
"
\n",
" \n",
" | 2 | \n",
" ecmf | \n",
" r | \n",
" isobaricInhPa | \n",
" 500 | \n",
" 20240603 | \n",
" 0 | \n",
" 6 | \n",
" fc | \n",
" 0 | \n",
" regular_ll | \n",
"
\n",
" \n",
" | 3 | \n",
" ecmf | \n",
" r | \n",
" isobaricInhPa | \n",
" 700 | \n",
" 20240603 | \n",
" 0 | \n",
" 6 | \n",
" fc | \n",
" 0 | \n",
" regular_ll | \n",
"
\n",
" \n",
" | 4 | \n",
" ecmf | \n",
" r | \n",
" isobaricInhPa | \n",
" 500 | \n",
" 20240603 | \n",
" 1200 | \n",
" 0 | \n",
" fc | \n",
" 0 | \n",
" regular_ll | \n",
"
\n",
" \n",
"
\n",
"
"
],
"text/plain": [
" centre shortName typeOfLevel level dataDate dataTime stepRange \\\n",
"0 ecmf r isobaricInhPa 500 20240603 0 0 \n",
"1 ecmf r isobaricInhPa 700 20240603 0 0 \n",
"2 ecmf r isobaricInhPa 500 20240603 0 6 \n",
"3 ecmf r isobaricInhPa 700 20240603 0 6 \n",
"4 ecmf r isobaricInhPa 500 20240603 1200 0 \n",
"\n",
" dataType number gridType \n",
"0 fc 0 regular_ll \n",
"1 fc 0 regular_ll \n",
"2 fc 0 regular_ll \n",
"3 fc 0 regular_ll \n",
"4 fc 0 regular_ll "
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"ds_fl1 = ds.earthkit.to_fieldlist()\n",
"ds_fl1.head()"
]
},
{
"cell_type": "markdown",
"id": "8a7c0b90-9b87-4ec8-a44e-90eb4f5ee089",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": ""
},
"tags": []
},
"source": [
"We can see that the GRIB field values changed as expected if we compare the original and resulting fieldlists."
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "5ad32a3e-2f48-49f5-b207-15e89c397fba",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": ""
},
"tags": []
},
"outputs": [
{
"data": {
"text/plain": [
"(254.25649845948692, 255.25649845948692)"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"m_0 = ds_fl.sel(param=\"t\", step=6, level=500)[0].values.mean() \n",
"m_1 = ds_fl1.sel(param=\"t\", step=6, level=500)[0].values.mean()\n",
"m_0, m_1"
]
},
{
"cell_type": "markdown",
"id": "39237f5c-c02d-49d7-88eb-1839875e2ecf",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": ""
},
"tags": []
},
"source": [
"#### Generating a GRIB file"
]
},
{
"cell_type": "raw",
"id": "08f6dbd3-e4e2-447d-a85f-e2948f157ca1",
"metadata": {
"editable": true,
"raw_mimetype": "text/restructuredtext",
"slideshow": {
"slide_type": ""
},
"tags": []
},
"source": [
"Once we have the GRIB fieldlist it can be saved to disk using :py:meth:`~data.readers.grib.index.GribFieldList.save` method."
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "e5b9c5ab-cc08-450a-aa30-691b405b3230",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": ""
},
"tags": []
},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" | \n",
" centre | \n",
" shortName | \n",
" typeOfLevel | \n",
" level | \n",
" dataDate | \n",
" dataTime | \n",
" stepRange | \n",
" dataType | \n",
" number | \n",
" gridType | \n",
"
\n",
" \n",
" \n",
" \n",
" | 0 | \n",
" ecmf | \n",
" r | \n",
" isobaricInhPa | \n",
" 500 | \n",
" 20240603 | \n",
" 0 | \n",
" 0 | \n",
" fc | \n",
" 0 | \n",
" regular_ll | \n",
"
\n",
" \n",
" | 1 | \n",
" ecmf | \n",
" r | \n",
" isobaricInhPa | \n",
" 700 | \n",
" 20240603 | \n",
" 0 | \n",
" 0 | \n",
" fc | \n",
" 0 | \n",
" regular_ll | \n",
"
\n",
" \n",
" | 2 | \n",
" ecmf | \n",
" r | \n",
" isobaricInhPa | \n",
" 500 | \n",
" 20240603 | \n",
" 0 | \n",
" 6 | \n",
" fc | \n",
" 0 | \n",
" regular_ll | \n",
"
\n",
" \n",
" | 3 | \n",
" ecmf | \n",
" r | \n",
" isobaricInhPa | \n",
" 700 | \n",
" 20240603 | \n",
" 0 | \n",
" 6 | \n",
" fc | \n",
" 0 | \n",
" regular_ll | \n",
"
\n",
" \n",
" | 4 | \n",
" ecmf | \n",
" r | \n",
" isobaricInhPa | \n",
" 500 | \n",
" 20240603 | \n",
" 1200 | \n",
" 0 | \n",
" fc | \n",
" 0 | \n",
" regular_ll | \n",
"
\n",
" \n",
"
\n",
"
"
],
"text/plain": [
" centre shortName typeOfLevel level dataDate dataTime stepRange \\\n",
"0 ecmf r isobaricInhPa 500 20240603 0 0 \n",
"1 ecmf r isobaricInhPa 700 20240603 0 0 \n",
"2 ecmf r isobaricInhPa 500 20240603 0 6 \n",
"3 ecmf r isobaricInhPa 700 20240603 0 6 \n",
"4 ecmf r isobaricInhPa 500 20240603 1200 0 \n",
"\n",
" dataType number gridType \n",
"0 fc 0 regular_ll \n",
"1 fc 0 regular_ll \n",
"2 fc 0 regular_ll \n",
"3 fc 0 regular_ll \n",
"4 fc 0 regular_ll "
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"ds_fl1.to_target(\"file\", \"_from_xr_1.grib\")\n",
"ekd.from_source(\"file\", \"_from_xr_1.grib\").head()"
]
},
{
"cell_type": "raw",
"id": "bb3a8f0a-d1ea-461b-ae4e-8b3fa312226e",
"metadata": {
"editable": true,
"raw_mimetype": "text/restructuredtext",
"slideshow": {
"slide_type": ""
},
"tags": []
},
"source": [
"It is also possible to directly write the Xarray into a GRIB file when calling :py:meth:`~data.utils.xarray.engine.XarrayEarthkit.to_grib` on the ``earthkit`` accessor. This will be a more memory efficient way to write the data to disk than generating a fieldlist first."
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "d12b5452-822e-4d96-8ddd-0e9f7b7c982c",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": ""
},
"tags": []
},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" | \n",
" centre | \n",
" shortName | \n",
" typeOfLevel | \n",
" level | \n",
" dataDate | \n",
" dataTime | \n",
" stepRange | \n",
" dataType | \n",
" number | \n",
" gridType | \n",
"
\n",
" \n",
" \n",
" \n",
" | 0 | \n",
" ecmf | \n",
" r | \n",
" isobaricInhPa | \n",
" 500 | \n",
" 20240603 | \n",
" 0 | \n",
" 0 | \n",
" fc | \n",
" 0 | \n",
" regular_ll | \n",
"
\n",
" \n",
" | 1 | \n",
" ecmf | \n",
" r | \n",
" isobaricInhPa | \n",
" 700 | \n",
" 20240603 | \n",
" 0 | \n",
" 0 | \n",
" fc | \n",
" 0 | \n",
" regular_ll | \n",
"
\n",
" \n",
" | 2 | \n",
" ecmf | \n",
" r | \n",
" isobaricInhPa | \n",
" 500 | \n",
" 20240603 | \n",
" 0 | \n",
" 6 | \n",
" fc | \n",
" 0 | \n",
" regular_ll | \n",
"
\n",
" \n",
" | 3 | \n",
" ecmf | \n",
" r | \n",
" isobaricInhPa | \n",
" 700 | \n",
" 20240603 | \n",
" 0 | \n",
" 6 | \n",
" fc | \n",
" 0 | \n",
" regular_ll | \n",
"
\n",
" \n",
" | 4 | \n",
" ecmf | \n",
" r | \n",
" isobaricInhPa | \n",
" 500 | \n",
" 20240603 | \n",
" 1200 | \n",
" 0 | \n",
" fc | \n",
" 0 | \n",
" regular_ll | \n",
"
\n",
" \n",
"
\n",
"
"
],
"text/plain": [
" centre shortName typeOfLevel level dataDate dataTime stepRange \\\n",
"0 ecmf r isobaricInhPa 500 20240603 0 0 \n",
"1 ecmf r isobaricInhPa 700 20240603 0 0 \n",
"2 ecmf r isobaricInhPa 500 20240603 0 6 \n",
"3 ecmf r isobaricInhPa 700 20240603 0 6 \n",
"4 ecmf r isobaricInhPa 500 20240603 1200 0 \n",
"\n",
" dataType number gridType \n",
"0 fc 0 regular_ll \n",
"1 fc 0 regular_ll \n",
"2 fc 0 regular_ll \n",
"3 fc 0 regular_ll \n",
"4 fc 0 regular_ll "
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"ds.earthkit.to_grib(\"_from_xr_2.grib\")\n",
"ekd.from_source(\"file\", \"_from_xr_2.grib\").head()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "3ee920a2-24ec-4462-975f-d2b1f7a7b293",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": ""
},
"tags": []
},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "dev",
"language": "python",
"name": "dev"
},
"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.11.12"
}
},
"nbformat": 4,
"nbformat_minor": 5
}