{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Union / Concat Tables\n", "\n", "The _concat_ function mimics the functionality of UNION in SQL queries and replaces the Merge functionality in Excel. It is a powerful function for dataframes in Pandas, that is mostly missing from Excel. \n", "\n", "For this example, we will transform a table with game scores into a calculated standing table.\n", "\n", "[![Open In Studio Lab](https://studiolab.sagemaker.aws/studiolab.svg)](https://studiolab.sagemaker.aws/import/github/aiola-lab/from-excel-to-pandas/blob/master/notebooks/03.04_concat_tables.ipynb)" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import pandas as pd" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Loading raw table data\n", "\n", "For this example, we will take an example of score table from Premier League in the UK, and calculate the standing table of that league using the league rules for points and rankings. \n", "\n" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "url = 'https://www.worldfootball.net/schedule/eng-premier-league-2020-2021-spieltag/1/'\n", "\n", "import requests\n", "from io import StringIO\n", "\n", "response = requests.get(url)\n", "response" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "scores = (\n", " pd\n", " .read_html(\n", " StringIO(\n", " response.text\n", " )\n", " )\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As you can see in the [web site](https://www.worldfootball.net/schedule/eng-premier-league-2020-2021-spieltag/1/), the format of the table is simple, and we can parse it with:\n", "* Take the second (index of 1) HTML table on the web site\n", "* Keep only the columns 2, 4, 5\n", "* Rename the columns to reflect the names of the **home** and **away** teams and the score\n", "* Parse the score with the first (index of 0) character is the number of goals of the **home** team\n", "* and the third (index of 2) character is the number of goals of the **away** team" ] }, { "cell_type": "code", "execution_count": 70, "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", "
HomeAwayScorehome_goalsaway_goals
0Fulham FCArsenal FC0:3 (0:1)03
1Crystal PalaceSouthampton FC1:0 (1:0)10
2Liverpool FCLeeds United4:3 (3:2)43
3West Ham UnitedNewcastle United0:2 (0:0)02
4West Bromwich AlbionLeicester City0:3 (0:0)03
5Tottenham HotspurEverton FC0:1 (0:0)01
6Sheffield UnitedWolverhampton Wanderers0:2 (0:2)02
7Brighton & Hove AlbionChelsea FC1:3 (0:1)13
8Burnley FCManchester United0:1 (0:0)01
9Manchester CityAston Villa2:0 (0:0)20
\n", "
" ], "text/plain": [ " Home Away Score home_goals \\\n", "0 Fulham FC Arsenal FC 0:3 (0:1) 0 \n", "1 Crystal Palace Southampton FC 1:0 (1:0) 1 \n", "2 Liverpool FC Leeds United 4:3 (3:2) 4 \n", "3 West Ham United Newcastle United 0:2 (0:0) 0 \n", "4 West Bromwich Albion Leicester City 0:3 (0:0) 0 \n", "5 Tottenham Hotspur Everton FC 0:1 (0:0) 0 \n", "6 Sheffield United Wolverhampton Wanderers 0:2 (0:2) 0 \n", "7 Brighton & Hove Albion Chelsea FC 1:3 (0:1) 1 \n", "8 Burnley FC Manchester United 0:1 (0:0) 0 \n", "9 Manchester City Aston Villa 2:0 (0:0) 2 \n", "\n", " away_goals \n", "0 3 \n", "1 0 \n", "2 3 \n", "3 2 \n", "4 3 \n", "5 1 \n", "6 2 \n", "7 3 \n", "8 1 \n", "9 0 " ] }, "execution_count": 70, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(\n", " scores[1]\n", " [[2,4,5]]\n", " .rename(columns={\n", " 2: 'Home',\n", " 4: 'Away',\n", " 5: 'Score'\n", " })\n", " .assign(home_goals = lambda r: r.Score.str[0])\n", " .assign(away_goals = lambda r: r.Score.str[2])\n", ")\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we have the scores of the first round with the hosting team (`Home`), the visiting team (`Away`) and the number of goals that each team scored. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Concatinate multuple tables\n", "\n", "We want to concatinate the scores of multiple rounds. Using a for-loop we will load the results of each round and add them to a list of rounds." ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [], "source": [ "round_tables = []\n", "for round in range(1,5):\n", " url = f'https://www.worldfootball.net/schedule/eng-premier-league-2020-2021-spieltag/{round}/'\n", " response = requests.get(url)\n", " round_table = (\n", " pd\n", " .read_html(\n", " StringIO(\n", " response.text\n", " )\n", " )\n", " [1]\n", " [[2,4,5]]\n", " .rename(columns={\n", " 2: 'Home',\n", " 4: 'Away',\n", " 5: 'Score'\n", " })\n", " .assign(home_goals = lambda r: r.Score.str[0].astype(int))\n", " .assign(away_goals = lambda r: r.Score.str[2].astype(int))\n", " )\n", " round_tables.append(round_table)\n", " " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Concatinate the rounds scores together\n", "\n", "In Excel it is not easy to stitch multiple tables together into a single larger table. Nevertheless, with Pandas it is easy, as we can see in the following example:\n", "* Concatinate the list of round scores\n", "* Calculate the number of points of the home team (3 for a win, 1 for a draw)\n", "* Calculate the number of points of the away team" ] }, { "cell_type": "code", "execution_count": 37, "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", " \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", " \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", " \n", " \n", " \n", " \n", " \n", " \n", "
HomeAwayScorehome_goalsaway_goalshome_pointsaway_points
0Fulham FCArsenal FC0:3 (0:1)0303
1Crystal PalaceSouthampton FC1:0 (1:0)1030
2Liverpool FCLeeds United4:3 (3:2)4330
3West Ham UnitedNewcastle United0:2 (0:0)0203
4West Bromwich AlbionLeicester City0:3 (0:0)0303
5Tottenham HotspurEverton FC0:1 (0:0)0103
6Sheffield UnitedWolverhampton Wanderers0:2 (0:2)0203
7Brighton & Hove AlbionChelsea FC1:3 (0:1)1303
8Burnley FCManchester United0:1 (0:0)0103
9Manchester CityAston Villa2:0 (0:0)2030
0Everton FCWest Bromwich Albion5:2 (2:1)5230
1Leeds UnitedFulham FC4:3 (2:1)4330
2Manchester UnitedCrystal Palace1:3 (0:1)1303
3Arsenal FCWest Ham United2:1 (1:1)2130
4Southampton FCTottenham Hotspur2:5 (1:1)2503
5Newcastle UnitedBrighton & Hove Albion0:3 (0:2)0303
6Chelsea FCLiverpool FC0:2 (0:0)0203
7Leicester CityBurnley FC4:2 (1:1)4230
8Aston VillaSheffield United1:0 (0:0)1030
9Wolverhampton WanderersManchester City1:3 (0:2)1303
0Brighton & Hove AlbionManchester United2:3 (1:1)2303
1Crystal PalaceEverton FC1:2 (1:2)1203
2West Bromwich AlbionChelsea FC3:3 (3:0)3311
3Burnley FCSouthampton FC0:1 (0:1)0103
4Sheffield UnitedLeeds United0:1 (0:0)0103
5Tottenham HotspurNewcastle United1:1 (1:0)1111
6Manchester CityLeicester City2:5 (1:1)2503
7West Ham UnitedWolverhampton Wanderers4:0 (1:0)4030
8Fulham FCAston Villa0:3 (0:2)0303
9Liverpool FCArsenal FC3:1 (2:1)3130
0Chelsea FCCrystal Palace4:0 (0:0)4030
1Everton FCBrighton & Hove Albion4:2 (2:1)4230
2Leeds UnitedManchester City1:1 (0:1)1111
3Newcastle UnitedBurnley FC3:1 (1:0)3130
4Leicester CityWest Ham United0:3 (0:2)0303
5Southampton FCWest Bromwich Albion2:0 (1:0)2030
6Arsenal FCSheffield United2:1 (0:0)2130
7Wolverhampton WanderersFulham FC1:0 (0:0)1030
8Manchester UnitedTottenham Hotspur1:6 (1:4)1603
9Aston VillaLiverpool FC7:2 (4:1)7230
\n", "
" ], "text/plain": [ " Home Away Score home_goals \\\n", "0 Fulham FC Arsenal FC 0:3 (0:1) 0 \n", "1 Crystal Palace Southampton FC 1:0 (1:0) 1 \n", "2 Liverpool FC Leeds United 4:3 (3:2) 4 \n", "3 West Ham United Newcastle United 0:2 (0:0) 0 \n", "4 West Bromwich Albion Leicester City 0:3 (0:0) 0 \n", "5 Tottenham Hotspur Everton FC 0:1 (0:0) 0 \n", "6 Sheffield United Wolverhampton Wanderers 0:2 (0:2) 0 \n", "7 Brighton & Hove Albion Chelsea FC 1:3 (0:1) 1 \n", "8 Burnley FC Manchester United 0:1 (0:0) 0 \n", "9 Manchester City Aston Villa 2:0 (0:0) 2 \n", "0 Everton FC West Bromwich Albion 5:2 (2:1) 5 \n", "1 Leeds United Fulham FC 4:3 (2:1) 4 \n", "2 Manchester United Crystal Palace 1:3 (0:1) 1 \n", "3 Arsenal FC West Ham United 2:1 (1:1) 2 \n", "4 Southampton FC Tottenham Hotspur 2:5 (1:1) 2 \n", "5 Newcastle United Brighton & Hove Albion 0:3 (0:2) 0 \n", "6 Chelsea FC Liverpool FC 0:2 (0:0) 0 \n", "7 Leicester City Burnley FC 4:2 (1:1) 4 \n", "8 Aston Villa Sheffield United 1:0 (0:0) 1 \n", "9 Wolverhampton Wanderers Manchester City 1:3 (0:2) 1 \n", "0 Brighton & Hove Albion Manchester United 2:3 (1:1) 2 \n", "1 Crystal Palace Everton FC 1:2 (1:2) 1 \n", "2 West Bromwich Albion Chelsea FC 3:3 (3:0) 3 \n", "3 Burnley FC Southampton FC 0:1 (0:1) 0 \n", "4 Sheffield United Leeds United 0:1 (0:0) 0 \n", "5 Tottenham Hotspur Newcastle United 1:1 (1:0) 1 \n", "6 Manchester City Leicester City 2:5 (1:1) 2 \n", "7 West Ham United Wolverhampton Wanderers 4:0 (1:0) 4 \n", "8 Fulham FC Aston Villa 0:3 (0:2) 0 \n", "9 Liverpool FC Arsenal FC 3:1 (2:1) 3 \n", "0 Chelsea FC Crystal Palace 4:0 (0:0) 4 \n", "1 Everton FC Brighton & Hove Albion 4:2 (2:1) 4 \n", "2 Leeds United Manchester City 1:1 (0:1) 1 \n", "3 Newcastle United Burnley FC 3:1 (1:0) 3 \n", "4 Leicester City West Ham United 0:3 (0:2) 0 \n", "5 Southampton FC West Bromwich Albion 2:0 (1:0) 2 \n", "6 Arsenal FC Sheffield United 2:1 (0:0) 2 \n", "7 Wolverhampton Wanderers Fulham FC 1:0 (0:0) 1 \n", "8 Manchester United Tottenham Hotspur 1:6 (1:4) 1 \n", "9 Aston Villa Liverpool FC 7:2 (4:1) 7 \n", "\n", " away_goals home_points away_points \n", "0 3 0 3 \n", "1 0 3 0 \n", "2 3 3 0 \n", "3 2 0 3 \n", "4 3 0 3 \n", "5 1 0 3 \n", "6 2 0 3 \n", "7 3 0 3 \n", "8 1 0 3 \n", "9 0 3 0 \n", "0 2 3 0 \n", "1 3 3 0 \n", "2 3 0 3 \n", "3 1 3 0 \n", "4 5 0 3 \n", "5 3 0 3 \n", "6 2 0 3 \n", "7 2 3 0 \n", "8 0 3 0 \n", "9 3 0 3 \n", "0 3 0 3 \n", "1 2 0 3 \n", "2 3 1 1 \n", "3 1 0 3 \n", "4 1 0 3 \n", "5 1 1 1 \n", "6 5 0 3 \n", "7 0 3 0 \n", "8 3 0 3 \n", "9 1 3 0 \n", "0 0 3 0 \n", "1 2 3 0 \n", "2 1 1 1 \n", "3 1 3 0 \n", "4 3 0 3 \n", "5 0 3 0 \n", "6 1 3 0 \n", "7 0 3 0 \n", "8 6 0 3 \n", "9 2 3 0 " ] }, "execution_count": 37, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import numpy as np\n", "\n", "scores_table = (\n", " pd\n", " .concat(round_tables)\n", " .assign(home_points = lambda r : np.select(\n", " [\n", " r.home_goals > r.away_goals, \n", " r.home_goals == r.away_goals, \n", " ], \n", " [\n", " 3, \n", " 1\n", " ], \n", " default=0\n", " )\n", " )\n", " .assign(away_points = lambda r : np.select(\n", " [\n", " r.home_goals < r.away_goals, \n", " r.home_goals == r.away_goals, \n", " ], \n", " [\n", " 3, \n", " 1\n", " ], \n", " default=0\n", " )\n", " )\n", "\n", ")\n", "\n", "scores_table" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Building Standing Table\n", "\n", "We will `concat` again on the socres and calculate the standing table based on these scores.\n", "\n", "* We will concatinate the following two tables\n", "* First table, we will calculate the games, points, and goals for the **home** teams\n", "* Second table, we will repeat the process for the **away** teams\n", "* Next, we will _sum_ the values from the two tables group-by each team\n", "* Calculate the goal difference that is used as tie breaker in case of equal points\n", "* Sort by the number of points and the goal difference" ] }, { "cell_type": "code", "execution_count": 69, "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", " \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", "
gamesgoalsgoals_againstpointsdiff
Team
Everton FC4125127
Aston Villa411497
Leicester City412795
Arsenal FC48593
Liverpool FC4111190
Tottenham Hotspur412577
Chelsea FC410674
Leeds United49871
Manchester City48771
Newcastle United46571
West Ham United48464
Southampton FC4566-1
Crystal Palace4576-2
Wolverhampton Wanderers4476-3
Manchester United46116-5
Brighton & Hove Albion48103-2
West Bromwich Albion45131-8
Sheffield United4160-5
Burnley FC4390-6
Fulham FC43110-8
\n", "
" ], "text/plain": [ " games goals goals_against points diff\n", "Team \n", "Everton FC 4 12 5 12 7\n", "Aston Villa 4 11 4 9 7\n", "Leicester City 4 12 7 9 5\n", "Arsenal FC 4 8 5 9 3\n", "Liverpool FC 4 11 11 9 0\n", "Tottenham Hotspur 4 12 5 7 7\n", "Chelsea FC 4 10 6 7 4\n", "Leeds United 4 9 8 7 1\n", "Manchester City 4 8 7 7 1\n", "Newcastle United 4 6 5 7 1\n", "West Ham United 4 8 4 6 4\n", "Southampton FC 4 5 6 6 -1\n", "Crystal Palace 4 5 7 6 -2\n", "Wolverhampton Wanderers 4 4 7 6 -3\n", "Manchester United 4 6 11 6 -5\n", "Brighton & Hove Albion 4 8 10 3 -2\n", "West Bromwich Albion 4 5 13 1 -8\n", "Sheffield United 4 1 6 0 -5\n", "Burnley FC 4 3 9 0 -6\n", "Fulham FC 4 3 11 0 -8" ] }, "execution_count": 69, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(\n", " pd\n", " .concat(\n", " [\n", " scores_table\n", " .rename(columns={'Home':'Team'})\n", " .groupby('Team')\n", " .agg(\n", " games = ('home_goals', 'count'), \n", " goals = ('home_goals', 'sum'), \n", " goals_against = ('away_goals', 'sum'), \n", " points = ('home_points', 'sum')\n", " )\n", " ,\n", " scores_table\n", " .rename(columns={'Away':'Team'})\n", " .groupby('Team')\n", " .agg(\n", " games = ('away_goals', 'count'), \n", " goals = ('away_goals', 'sum'), \n", " goals_against = ('home_goals', 'sum'), \n", " points = ('away_points', 'sum')\n", " ) \n", " ]\n", " )\n", " .groupby('Team')\n", " .sum()\n", " .assign(diff = lambda r : r.goals - r.goals_against)\n", " .sort_values(by=['points','diff'], ascending=False)\n", ")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "interpreter": { "hash": "aee8b7b246df8f9039afb4144a1f6fd8d2ca17a180786b69acc140d282b71a49" }, "kernelspec": { "display_name": "Python 3.9.2 64-bit", "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.9.2" }, "orig_nbformat": 2 }, "nbformat": 4, "nbformat_minor": 2 }