{"cells":[{"metadata":{},"cell_type":"markdown","source":"## 1. Introduction\n\nThis is an Exploratory Data Analysis (EDA) Kernel for Lyft Motion Prediction for Autonomous Vehicles competition dataset. \n\n![](https://self-driving.lyft.com/wp-content/uploads/2020/06/blog_3.jpg)\n\n\nWe start with the analysis preparation, which requires, for this competition, to install and load several packages for load and manage l5kit dataset.  \nWe follow with data exploration, reviewing the agents, the scenes, the frames and following with inspection of the animated scenes."},{"metadata":{},"cell_type":"markdown","source":"## 2. Analysis preparation"},{"metadata":{},"cell_type":"markdown","source":"### 2.1. Install & load packages\n\nWe will have to install l5kit to access the data.\n\n<img src=\"https://www.l5kit.org/_images/pipeline.png\" width=800></img>\n"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"_kg_hide-input":true,"_kg_hide-output":true},"cell_type":"code","source":"!pip install --upgrade pip\n!pip install pymap3d==2.1.0\n!pip install -U l5kit","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"import os\nimport numpy as np\nimport pandas as pd\nfrom l5kit.data import ChunkedDataset, LocalDataManager\nfrom l5kit.dataset import EgoDataset, AgentDataset\nfrom l5kit.rasterization import build_rasterizer\nfrom l5kit.configs import load_config_data\nfrom l5kit.visualization import draw_trajectory, TARGET_POINTS_COLOR\nfrom l5kit.geometry import transform_points\nfrom l5kit.data import PERCEPTION_LABELS\nfrom tqdm import tqdm\nfrom collections import Counter\nimport matplotlib.pyplot as plt\nimport seaborn as sns \nfrom matplotlib import animation, rc\nfrom matplotlib.ticker import MultipleLocator\nfrom IPython.display import display, clear_output\nimport PIL\nfrom IPython.display import HTML\n\nrc('animation', html='jshtml')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### 2.2. Configuration\n\nWe set the local dataset configurations before accessing it.\nWe set the path for the l5kit data folder by setting the environment variable `L5KIT_DATA_FOLDER` and we load the lyft configuration files from a `yaml` file from an external dataset. "},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true,"_kg_hide-input":true,"_kg_hide-output":true},"cell_type":"code","source":"os.environ[\"L5KIT_DATA_FOLDER\"] = \"/kaggle/input/lyft-motion-prediction-autonomous-vehicles\"\ncfg = load_config_data(\"/kaggle/input/lyft-config-files/visualisation_config.yaml\")\nprint(cfg)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## 3. Load data\n\nWe load, using the l5kit local data manager, the dataset. L5kit uses [zarr](https://zarr.readthedocs.io/en/stable/) data format; here the arrays are divided into chunks and compressed."},{"metadata":{"_kg_hide-input":true,"_kg_hide-output":true,"trusted":true},"cell_type":"code","source":"# local data manager\ndm = LocalDataManager()\n# set dataset path\ndataset_path = dm.require(cfg[\"val_data_loader\"][\"key\"])\n# load the dataset; this is a zarr format, chunked dataset\nchunked_dataset = ChunkedDataset(dataset_path)\n# open the dataset\nchunked_dataset.open()\nprint(chunked_dataset)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## 4. Data exploration\n\n\n### 4.1. Explore the dataset"},{"metadata":{},"cell_type":"markdown","source":"We load and show entities in the dataset. \n\n### 4.1.1. Agents\n\nWe start with the agents."},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"agents = chunked_dataset.agents\nagents_df = pd.DataFrame(agents)\nagents_df.columns = [\"data\"]; features = ['centroid', 'extent', 'yaw', 'velocity', 'track_id', 'label_probabilities']\n\nfor i, feature in enumerate(features):\n    agents_df[feature] = agents_df['data'].apply(lambda x: x[i])\nagents_df.drop(columns=[\"data\"],inplace=True)\nprint(f\"agents dataset: {agents_df.shape}\")\nagents_df.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"The fields in the agents dataset are the following:  \n\n* centroid - the agent position (in plane - two dimmensions);   \n* extent - the agent dimmensions (three dimmensions, let's called length, width, height);  \n* yaw - the agent oscilation/twist about the vertical plane;   \n* velocity - the speed of the agent - in euclidian space;   \n* track_id - index of track associated to the agent;   \n* label_probabilities - gives the probability for the agent to belong to one of 17 different agent type; we will explore these labels in a moment;    \n\n\nLet's look to the distribution of few of these values. "},{"metadata":{},"cell_type":"markdown","source":"#### Centroid distribution"},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"agents_df['cx'] = agents_df['centroid'].apply(lambda x: x[0])\nagents_df['cy'] = agents_df['centroid'].apply(lambda x: x[1])","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"fig, ax = plt.subplots(1,1,figsize=(8,8))\nplt.scatter(agents_df['cx'], agents_df['cy'], marker='+')\nplt.xlabel('x', fontsize=11); plt.ylabel('y', fontsize=11)\nplt.title(\"Centroids distribution\")\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"#### Extent distribution"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"agents_df['ex'] = agents_df['extent'].apply(lambda x: x[0])\nagents_df['ey'] = agents_df['extent'].apply(lambda x: x[1])\nagents_df['ez'] = agents_df['extent'].apply(lambda x: x[2])","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"sns.set_style('whitegrid')\n\nfig, ax = plt.subplots(1,3,figsize=(16,5))\nplt.subplot(1,3,1)\nplt.scatter(agents_df['ex'], agents_df['ey'], marker='+')\nplt.xlabel('ex', fontsize=11); plt.ylabel('ey', fontsize=11)\nplt.title(\"Extent: ex-ey\")\nplt.subplot(1,3,2)\nplt.scatter(agents_df['ey'], agents_df['ez'], marker='+', color=\"red\")\nplt.xlabel('ey', fontsize=11); plt.ylabel('ez', fontsize=11)\nplt.title(\"Extent: ey-ez\")\nplt.subplot(1,3,3)\nplt.scatter(agents_df['ez'], agents_df['ex'], marker='+', color=\"green\")\nplt.xlabel('ez', fontsize=11); plt.ylabel('ex', fontsize=11)\nplt.title(\"Extent: ez-ex\")\nplt.show();","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"#### Yaw\n\nLet's see yaw distribution."},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"fig, ax = plt.subplots(1,1,figsize=(8,8))\nsns.distplot(agents_df['yaw'],color=\"magenta\")\nplt.title(\"Yaw distribution\")\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"#### Velocity\n\nLet's look to velocity distribution."},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"agents_df['vx'] = agents_df['velocity'].apply(lambda x: x[0])\nagents_df['vy'] = agents_df['velocity'].apply(lambda x: x[1])","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"fig, ax = plt.subplots(1,1,figsize=(8,8))\nplt.title(\"Velocity distribution\")\nplt.scatter(agents_df['vx'], agents_df['vy'], marker='.', color=\"red\")\nplt.xlabel('vx', fontsize=11); plt.ylabel('vy', fontsize=11)\nplt.show();","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"#### track id\n"},{"metadata":{"trusted":true},"cell_type":"code","source":"print(\"Number of tracks: \", agents_df.track_id.nunique())\nprint(\"Entries per track id (first 10): \\n\", agents_df.track_id.value_counts()[0:10])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Let's look to the distribution of the label probabilities."},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"probabilities = agents[\"label_probabilities\"]\nlabels_indexes = np.argmax(probabilities, axis=1)\ncounts = []\nfor idx_label, label in enumerate(PERCEPTION_LABELS):\n    counts.append(np.sum(labels_indexes == idx_label))\n\nagents_df = pd.DataFrame()\nfor count, label in zip(counts, PERCEPTION_LABELS):\n    agents_df = agents_df.append(pd.DataFrame({'label':label, 'count':count},index=[0]))\nagents_df = agents_df.reset_index().drop(columns=['index'], axis=1)","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"print(f\"agents probabilities dataset: {agents_df.shape}\")\nagents_df  ","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"There are 4 different active agents present in the dataset, as following:  \n* PERCEPTION_LABEL_UNKNOWN - majority;  \n* PERCEPTION_LABEL_CAR;  \n* PERCEPTION_LABEL_CYCLIST;  \n* PERCEPTION_LABEL_PEDESTRIAN.\n\n\nLet's look to their distribution:"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"f, ax = plt.subplots(1,1, figsize=(10,4))\nplt.scatter(agents_df['label'], agents_df['count']+1, marker='*')\nplt.xticks(rotation=90, size=8)\nplt.xlabel('Perception label')\nplt.ylabel(f'Agents count')\nplt.title(\"Agents perception label values count distribution\")\nplt.grid(True)\nax.set(yscale=\"log\")\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### 4.1.2. Scenes\n\nLet's look now to the scenes."},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"scenes = chunked_dataset.scenes\nscenes_df = pd.DataFrame(scenes)\nscenes_df.columns = [\"data\"]; features = ['frame_index_interval', 'host', 'start_time', 'end_time']\nfor i, feature in enumerate(features):\n    scenes_df[feature] = scenes_df['data'].apply(lambda x: x[i])\nscenes_df.drop(columns=[\"data\"],inplace=True)\nprint(f\"scenes dataset: {scenes_df.shape}\")\nscenes_df.head()","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"f, ax = plt.subplots(1,1, figsize=(6,4))\nsns.countplot(scenes_df.host)\nplt.xlabel('Host')\nplt.ylabel(f'Count')\nplt.title(\"Scenes host count distribution\")\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Let's show the scenes frame index succesion, on the same graph with the host."},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"scenes_df['frame_index_start'] = scenes_df['frame_index_interval'].apply(lambda x: x[0])\nscenes_df['frame_index_end'] = scenes_df['frame_index_interval'].apply(lambda x: x[1])\nscenes_df.head()","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"f, ax = plt.subplots(1,1, figsize=(8,8))\nspacing = 498\nminorLocator = MultipleLocator(spacing)\nax.yaxis.set_minor_locator(minorLocator)\nax.xaxis.set_minor_locator(minorLocator)\nplt.xlabel('Start frame index')\nplt.ylabel(f'End frame index')\nplt.grid(which = 'minor')\nplt.title(\"Frames scenes start and end index (grouped per host)\")\nsns.scatterplot(scenes_df['frame_index_start'], scenes_df['frame_index_end'], marker='|',  hue=scenes_df['host'])\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### 4.1.3. Frames\n\nWe are now looking to the frames.\n"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"frames_df = pd.DataFrame(chunked_dataset.frames)\nframes_df.columns = [\"data\"]; features = ['timestamp', 'agent_index_interval', 'traffic_light_faces_index_interval', \n                                          'ego_translation','ego_rotation']\nfor i, feature in enumerate(features):\n    frames_df[feature] = frames_df['data'].apply(lambda x: x[i])\nframes_df.drop(columns=[\"data\"],inplace=True)\nprint(f\"frames dataset: {frames_df.shape}\")\nframes_df.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"The frames are described by:  \n* timestamp;  \n* agent index interval;   \n* traffic light faces index interval;   \n* ego translation;   \n* ego rotation;   \n\n\nLet's look to ego translations.\n\n#### Ego translations"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"frames_df['dx'] = frames_df['ego_translation'].apply(lambda x: x[0])\nframes_df['dy'] = frames_df['ego_translation'].apply(lambda x: x[1])\nframes_df['dz'] = frames_df['ego_translation'].apply(lambda x: x[2])","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"sns.set_style('whitegrid')\nplt.figure()\n\nfig, ax = plt.subplots(1,3,figsize=(16,5))\n\nplt.subplot(1,3,1)\nplt.scatter(frames_df['dx'], frames_df['dy'], marker='+')\nplt.xlabel('dx', fontsize=11); plt.ylabel('dy', fontsize=11)\nplt.title(\"Translations: dx-dy\")\nplt.subplot(1,3,2)\nplt.scatter(frames_df['dy'], frames_df['dz'], marker='+', color=\"red\")\nplt.xlabel('dy', fontsize=11); plt.ylabel('dz', fontsize=11)\nplt.title(\"Translations: dy-dz\")\nplt.subplot(1,3,3)\nplt.scatter(frames_df['dz'], frames_df['dx'], marker='+', color=\"green\")\nplt.xlabel('dz', fontsize=11); plt.ylabel('dx', fontsize=11)\nplt.title(\"Translations: dz-dx\")\n\nfig.suptitle(\"Ego translations in 2D planes of the 3 components (dx,dy,dz)\", size=14)\nplt.show();","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"fig, ax = plt.subplots(1,3,figsize=(16,5))\ncolors = ['magenta', 'orange', 'darkblue']; labels= [\"dx\", \"dy\", \"dz\"]\nfor i in range(0,3):\n    df = frames_df['ego_translation'].apply(lambda x: x[i])\n    plt.subplot(1,3,i + 1)\n    sns.distplot(df, hist=False, color = colors[ i ])\n    plt.xlabel(labels[i])\nfig.suptitle(\"Ego translations distribution\", size=14)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"#### Ego rotations\n\nLet's also plot Ego rotations components distributions. The rotation matrix is 3 x 3."},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"fig, ax = plt.subplots(3,3,figsize=(16,16))\ncolors = ['red', 'blue', 'green', 'magenta', 'orange', 'darkblue', 'black', 'cyan', 'darkgreen']\nfor i in range(0,3):\n    for j in range(0,3):\n        df = frames_df['ego_rotation'].apply(lambda x: x[i][j])\n        plt.subplot(3,3,i * 3 + j + 1)\n        sns.distplot(df, hist=False, color = colors[ i * 3 + j  ])\n        plt.xlabel(f'r[ {i + 1} ][ {j + 1} ]')\nfig.suptitle(\"Ego rotation angles distribution\", size=14)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"#### Trafic lights faces index interval"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"frames_df['tlfii0'] = frames_df['traffic_light_faces_index_interval'].apply(lambda x: x[0])\nframes_df['tlfii1'] = frames_df['traffic_light_faces_index_interval'].apply(lambda x: x[1])\nsns.set_style('whitegrid')\nplt.figure()\nfig, ax = plt.subplots(1,1,figsize=(8,8))\nplt.scatter(frames_df['tlfii0'], frames_df['tlfii1'], marker='+')\nplt.xlabel('Trafic lights faces index interval [0]', fontsize=11); plt.ylabel('Trafic lights faces index interval [1]', fontsize=11)\nplt.title(\"Trafic lights faces index interval\")\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"#### Agents index interval"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"fig, ax = plt.subplots(1, 2, figsize=(12, 5))\ncolors = ['cyan', 'darkgreen']\nfor i in range(0,2):\n    df = frames_df['agent_index_interval'].apply(lambda x: x[i])\n    plt.subplot(1, 2, i + 1)\n    sns.distplot(df, hist=False, color = colors[ i ])\n    plt.xlabel(f'agent index interval [ {i} ]')\nfig.suptitle(\"Agent index interval\", size=14)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### 4.2. Visualize animated scene\n\n\nWe define two utility functions:   \n* one function to display animated scenes, using animation from matplotlib;  \n* one function to assemble the animation; this function receives as parameters the scene index and the map type (either `semantic` or `satellite`).  "},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"def show_scene_animated(images):\n\n    def animate(i):\n        im.set_data(images[i])\n \n    fig, ax = plt.subplots()\n    im = ax.imshow(images[0])\n    \n    return animation.FuncAnimation(fig, animate, frames=len(images), interval=60)\n\ndef prepare_scene_for_animation(scene_index=20,map_type=\"py_semantic\",show_trajectory=False):\n    cfg[\"raster_params\"][\"map_type\"] = map_type\n    rast = build_rasterizer(cfg, dm)\n    dataset = EgoDataset(cfg, chunked_dataset, rast)\n    scene_idx = scene_index\n    indexes = dataset.get_scene_indices(scene_idx)\n    images = []\n\n    for idx in indexes:\n\n        data = dataset[idx]\n        im = data[\"image\"].transpose(1, 2, 0)\n        im = dataset.rasterizer.to_rgb(im)\n        target_positions_pixels = transform_points(data[\"target_positions\"] + data[\"centroid\"][:2], data[\"world_to_image\"])\n        center_in_pixels = np.asarray(cfg[\"raster_params\"][\"ego_center\"]) * cfg[\"raster_params\"][\"raster_size\"]\n        if show_trajectory:\n            draw_trajectory(on_image=im, positions=target_positions_pixels, rgb_color=TARGET_POINTS_COLOR, radius=1, yaws=data[\"target_yaws\"])\n        clear_output(wait=True)\n        images.append(PIL.Image.fromarray(im[::-1]))    \n    return images","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Let's visualize two different scenes, using semantic map type."},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"semantic_images_animation = show_scene_animated(prepare_scene_for_animation(10,\"py_semantic\"))","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"HTML(semantic_images_animation.to_jshtml())","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"semantic_images_animation = show_scene_animated(prepare_scene_for_animation(20,\"py_semantic\"))","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"HTML(semantic_images_animation.to_jshtml())","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"semantic_images_animation = show_scene_animated(prepare_scene_for_animation(30,\"py_semantic\"))","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"HTML(semantic_images_animation.to_jshtml())","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"semantic_images_animation = show_scene_animated(prepare_scene_for_animation(40,\"py_semantic\"))","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"HTML(semantic_images_animation.to_jshtml())","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Let's look now to the same scenes, but using satellite map type."},{"metadata":{"trusted":true},"cell_type":"code","source":"satellite_images_animation = show_scene_animated(prepare_scene_for_animation(10, \"py_satellite\"))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"HTML(satellite_images_animation.to_jshtml())","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"satellite_images_animation = show_scene_animated(prepare_scene_for_animation(20, \"py_satellite\"))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"HTML(satellite_images_animation.to_jshtml())","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"satellite_images_animation = show_scene_animated(prepare_scene_for_animation(30, \"py_satellite\"))","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"HTML(satellite_images_animation.to_jshtml())","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"satellite_images_animation = show_scene_animated(prepare_scene_for_animation(40, \"py_satellite\"))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"HTML(satellite_images_animation.to_jshtml())","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## 5. References\n\n[1] Lyft Understanding the data and EDA, https://www.kaggle.com/nxrprime/lyft-understanding-the-data-and-eda  \n[2] Lyft Scenes Visualizations, https://www.kaggle.com/jpbremer/lyft-scene-visualisations/  \n[3] Lyft l5kit, https://github.com/lyft/l5kit  \n[4] Lyft l5kit data visualization, https://github.com/lyft/l5kit/blob/master/examples/visualisation/visualise_data.ipynb  \n[5] Lyft l5kit agents motion prediction, https://github.com/lyft/l5kit/blob/master/examples/agent_motion_prediction/agent_motion_prediction.ipynb   "}],"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat":4,"nbformat_minor":4}