{"cells":[{"metadata":{"papermill":{"duration":0.045074,"end_time":"2021-01-13T22:39:03.980489","exception":false,"start_time":"2021-01-13T22:39:03.935415","status":"completed"},"tags":[]},"cell_type":"markdown","source":"<h1 style=\"text-align: center; font-family: Verdana; font-size: 32px; font-style: normal; font-weight: bold; text-decoration: none; text-transform: none; font-variant: small-caps; letter-spacing: 3px; color: #74d5dd; background-color: #ffffff;\">Human Protein Atlas - Single Cell Classification</h1>\n<h2 style=\"text-align: center; font-family: Verdana; font-size: 24px; font-style: normal; font-weight: bold; text-decoration: underline; text-transform: none; letter-spacing: 2px; color: navy; background-color: #ffffff;\">Exploratory Data Analysis (EDA)</h2>\n<h5 style=\"text-align: center; font-family: Verdana; font-size: 12px; font-style: normal; font-weight: bold; text-decoration: None; text-transform: none; letter-spacing: 1px; color: black; background-color: #ffffff;\">CREATED BY: DARIEN SCHETTLER</h5>\n"},{"metadata":{"papermill":{"duration":0.042275,"end_time":"2021-01-13T22:39:04.065992","exception":false,"start_time":"2021-01-13T22:39:04.023717","status":"completed"},"tags":[]},"cell_type":"markdown","source":"<h1 style=\"font-family: Verdana; font-size: 24px; font-style: normal; font-weight: bold; text-decoration: none; text-transform: none; letter-spacing: 3px; color: navy; background-color: #ffffff;\">TABLE OF CONTENTS</h1>\n\n---\n\n<h3 style=\"text-indent: 10vw; font-family: Verdana; font-size: 20px; font-style: normal; font-weight: normal; text-decoration: none; text-transform: none; letter-spacing: 2px; color: navy; background-color: #ffffff;\"><a href=\"#imports\">0&nbsp;&nbsp;&nbsp;&nbsp;IMPORTS</a></h3>\n\n---\n\n<h3 style=\"text-indent: 10vw; font-family: Verdana; font-size: 20px; font-style: normal; font-weight: normal; text-decoration: none; text-transform: none; letter-spacing: 2px; color: navy; background-color: #ffffff;\"><a href=\"#background_information\">1&nbsp;&nbsp;&nbsp;&nbsp;BACKGROUND INFORMATION</a></h3>\n\n---\n\n<h3 style=\"text-indent: 10vw; font-family: Verdana; font-size: 20px; font-style: normal; font-weight: normal; text-decoration: none; text-transform: none; letter-spacing: 2px; color: navy; background-color: #ffffff;\"><a href=\"#setup\">2&nbsp;&nbsp;&nbsp;&nbsp;SETUP</a></h3>\n\n---\n\n<h3 style=\"text-indent: 10vw; font-family: Verdana; font-size: 20px; font-style: normal; font-weight: normal; text-decoration: none; text-transform: none; letter-spacing: 2px; color: navy; background-color: #ffffff;\"><a href=\"#helper_functions\">3&nbsp;&nbsp;&nbsp;&nbsp;HELPER FUNCTIONS</a></h3>\n\n---\n\n<h3 style=\"text-indent: 10vw; font-family: Verdana; font-size: 20px; font-style: normal; font-weight: normal; text-decoration: none; text-transform: none; letter-spacing: 2px; color: navy; background-color: #ffffff;\"><a href=\"#label_ex\">4&nbsp;&nbsp;&nbsp;&nbsp;LABEL EXPLORATION</a></h3>\n\n---\n\n<h3 style=\"text-indent: 10vw; font-family: Verdana; font-size: 20px; font-style: normal; font-weight: normal; text-decoration: none; text-transform: none; letter-spacing: 2px; color: navy; background-color: #ffffff;\"><a href=\"#image_ex\">5&nbsp;&nbsp;&nbsp;&nbsp;IMAGE EXPLORATION</a></h3>\n\n---\n\n<h3 style=\"text-indent: 10vw; font-family: Verdana; font-size: 20px; font-style: normal; font-weight: normal; text-decoration: none; text-transform: none; letter-spacing: 2px; color: navy; background-color: #ffffff;\"><a href=\"#segmentation_ex\">6&nbsp;&nbsp;&nbsp;&nbsp;SEGMENTATION EXPLORATION</a></h3>\n\n---\n\n<h3 style=\"text-indent: 10vw; font-family: Verdana; font-size: 20px; font-style: normal; font-weight: normal; text-decoration: none; text-transform: none; letter-spacing: 2px; color: navy; background-color: #ffffff;\"><a href=\"#swlice\">7&nbsp;&nbsp;&nbsp;&nbsp;SINGLE WEAK-LABEL INDIVIDUAL CELL EXPLPORATION</a></h3>\n\n---\n"},{"metadata":{"papermill":{"duration":0.043617,"end_time":"2021-01-13T22:39:04.153416","exception":false,"start_time":"2021-01-13T22:39:04.109799","status":"completed"},"tags":[]},"cell_type":"markdown","source":"<h1 style=\"text-align: font-family: Verdana; font-size: 24px; font-style: normal; font-weight: bold; text-decoration: none; text-transform: none; letter-spacing: 3px; background-color: #ffffff; color: navy;\" id=\"imports\">0&nbsp;&nbsp;IMPORTS</h1>"},{"metadata":{"_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","execution":{"iopub.execute_input":"2021-01-13T22:39:04.243237Z","iopub.status.busy":"2021-01-13T22:39:04.242538Z","iopub.status.idle":"2021-01-13T22:39:39.252127Z","shell.execute_reply":"2021-01-13T22:39:39.252932Z"},"papermill":{"duration":35.05654,"end_time":"2021-01-13T22:39:39.253146","exception":false,"start_time":"2021-01-13T22:39:04.196606","status":"completed"},"tags":[],"trusted":true},"cell_type":"code","source":"# Cell Segmentator Tool\nprint(\"\\n... INSTALLING AND IMPORTING CELL-PROFILER TOOL (HPACELLSEG) ...\\n\")\ntry:\n    import hpacellseg.cellsegmentator as cellsegmentator\n    from hpacellseg.utils import label_cell\nexcept:\n    !pip install -q \"/kaggle/input/pycocotools/pycocotools-2.0-cp37-cp37m-linux_x86_64.whl\"\n    !pip install -q \"/kaggle/input/hpapytorchzoozip/pytorch_zoo-master\"\n    !pip install -q \"/kaggle/input/hpacellsegmentatormaster/HPA-Cell-Segmentation-master\"\n    import hpacellseg.cellsegmentator as cellsegmentator\n    from hpacellseg.utils import label_cell\n\nprint(\"\\n... OTHER IMPORTS STARTING ...\\n\")\nprint(\"\\n\\tVERSION INFORMATION\")\n\n# Machine Learning and Data Science Imports\nimport tensorflow as tf; print(f\"\\t\\t– TENSORFLOW VERSION: {tf.__version__}\");\nimport pandas as pd; pd.options.mode.chained_assignment = None;\nimport numpy as np; print(f\"\\t\\t– NUMPY VERSION: {np.__version__}\");\nimport torch\n\n# Built In Imports\nfrom collections import Counter\nfrom datetime import datetime\nimport multiprocessing\nfrom glob import glob\nimport imagehash\nimport warnings\nimport requests\nimport imageio\nimport IPython\nimport urllib\nimport zipfile\nimport pickle\nimport random\nimport shutil\nimport string\nimport math\nimport tqdm\nimport time\nimport gzip\nimport ast\nimport io\nimport os\nimport gc\nimport re\n\n# Visualization Imports\nfrom matplotlib.colors import ListedColormap\nimport matplotlib.patches as patches\nimport plotly.graph_objects as go\nimport matplotlib.pyplot as plt\nfrom tqdm.notebook import tqdm\nimport plotly.express as px\nimport seaborn as sns\nfrom PIL import Image\nimport matplotlib; print(f\"\\t\\t– MATPLOTLIB VERSION: {matplotlib.__version__}\");\nimport plotly\nimport PIL\nimport cv2\n\n# Submission Imports\nfrom pycocotools import _mask as coco_mask\nimport typing as t\nimport base64\nimport zlib\n\n# PRESETS\nLBL_NAMES = [\"Nucleoplasm\", \"Nuclear Membrane\", \"Nucleoli\", \"Nucleoli Fibrillar Center\", \"Nuclear Speckles\", \"Nuclear Bodies\", \"Endoplasmic Reticulum\", \"Golgi Apparatus\", \"Intermediate Filaments\", \"Actin Filaments\", \"Microtubules\", \"Mitotic Spindle\", \"Centrosome\", \"Plasma Membrane\", \"Mitochondria\", \"Aggresome\", \"Cytosol\", \"Vesicles\", \"Negative\"]\nINT_2_STR = {x:LBL_NAMES[x] for x in np.arange(19)}\nINT_2_STR_LOWER = {k:v.lower().replace(\" \", \"_\") for k,v in INT_2_STR.items()}\nSTR_2_INT_LOWER = {v:k for k,v in INT_2_STR_LOWER.items()}\nSTR_2_INT = {v:k for k,v in INT_2_STR.items()}\nFIG_FONT = dict(family=\"Helvetica, Arial\", size=14, color=\"#7f7f7f\")\nLABEL_COLORS = [px.colors.label_rgb(px.colors.convert_to_RGB_255(x)) for x in sns.color_palette(\"Spectral\", len(LBL_NAMES))]\nLABEL_COL_MAP = {str(i):x for i,x in enumerate(LABEL_COLORS)}\n\nprint(\"\\n\\n... IMPORTS COMPLETE ...\\n\")\n\n\n# Stop Tensorflow From Eating All The Memory\ngpus = tf.config.experimental.list_physical_devices('GPU')\nif gpus:\n    try:\n        # Currently, memory growth needs to be the same across GPUs\n        for gpu in gpus:\n            tf.config.experimental.set_memory_growth(gpu, True)\n        logical_gpus = tf.config.experimental.list_logical_devices('GPU')\n        print(len(gpus), \"... Physical GPUs,\", len(logical_gpus), \"Logical GPUs ...\\n\")\n    except RuntimeError as e:\n        # Memory growth must be set before GPUs have been initialized\n        print(e)","execution_count":null,"outputs":[]},{"metadata":{"papermill":{"duration":0.042806,"end_time":"2021-01-13T22:39:39.338543","exception":false,"start_time":"2021-01-13T22:39:39.295737","status":"completed"},"tags":[]},"cell_type":"markdown","source":"<h1 style=\"text-align: font-family: Verdana; font-size: 24px; font-style: normal; font-weight: bold; text-decoration: none; text-transform: none; letter-spacing: 3px; color: navy; background-color: #ffffff;\" id=\"background_information\">1&nbsp;&nbsp;BACKGROUND INFORMATION</h1>"},{"metadata":{"papermill":{"duration":0.042139,"end_time":"2021-01-13T22:39:39.423376","exception":false,"start_time":"2021-01-13T22:39:39.381237","status":"completed"},"tags":[]},"cell_type":"markdown","source":"<h3 style=\"text-align: font-family: Verdana; font-size: 20px; font-style: normal; font-weight: normal; text-decoration: none; text-transform: none; letter-spacing: 2px; color: navy; background-color: #ffffff;\">1.1  THE DATA</h3>\n\n---\n\n<b style=\"text-decoration: underline; font-family: Verdana;\">BACKGROUND INFORMATION</b>\n\nIn this competition, we are predicting protein organelle localization labels for each cell in the image. We wish to classify subcellular protein localization patterns of single cells in microscope images. Solving the single-cell image classification challenge will help us determine precise locations for all human proteins in each individual cell in our large collection of open access images. This will contribute to a better understanding of functional differences between otherwise seemingly identical cells. More information about why this task is important can be found [**here**](https://www.kaggle.com/c/hpa-single-cell-image-classification/overview)\n\n<br><br>\n\n***Author's Take:***<br><br>\n&nbsp;&nbsp;&nbsp;&nbsp;**1. Instance Segmentation for All Cells Containing the Protein of Interest**<br>\n&nbsp;&nbsp;&nbsp;&nbsp;**2. Multi-Label Classification of Each Segmented Cell** \n\n<br><br>\n\nThe main difference between this and the previous [HPA competition](https://www.kaggle.com/c/human-protein-atlas-image-classification) is that we are being asked to classify the label(s) of each cell in every image with only weak image-level training data, compared to the classification of image-level labels as done in the previous competition.\n\n\nFor each image in the test set, you must predict a list of instance segmentation masks and their associated detection score (***confidence***): \n\n```python\nImageID,ImageWidth,ImageHeight,PredictionString\nImageAID,ImageAWidth,ImageAHeight,LabelA1 ConfidenceA1 EncodedMaskA1 LabelA2 ConfidenceA2 EncodedMaskA2 ...\nImageBID,ImageBWidth,ImageBHeight,LabelB1 ConfidenceB1 EncodedMaskB1 LabelB2 ConfidenceB2 EncodedMaskB2 …\n```\n\n**Note that a mask MAY have more than one class.**\n**If that is the case, predict separate detections for each class using the same mask.**\n\n```python\nImageID,ImageWidth,ImageHeight,PredictionString\nImageAID,ImageAWidth,ImageAHeight,LabelA1 ConfidenceA1 EncodedMaskA1 LabelA2 ConfidenceA2 EncodedMaskA1 ...\n```\n\n**A sample with real values would be:**\n\n```python\nImageID,ImageWidth,ImageHeight,PredictionString\n721568e01a744247,1118,1600,0 0.637833 eNqLi8xJM7BOTjS08DT2NfI38DfyM/Q3NMAJgJJ+RkBs7JecF5tnAADw+Q9I\n7b018c5e3a20daba,1600,1066,16 0.85117 eNqLiYrLN7DNCjDMMIj0N/Iz9DcwBEIDfyN/QyA2AAsBRfxMPcKTA1MMADVADIo=\n```\n    \nThe binary segmentation masks are run-length encoded (RLE), zlib compressed, and base64 encoded to be used in text format as EncodedMask. Specifically, we use the Coco masks RLE encoding/decoding (see the encode method of COCO’s mask API), the zlib compression/decompression (RFC1950), and vanilla base64 encoding.\n\n\n---\n\n<b style=\"text-decoration: underline; font-family: Verdana;\">DATASET INFORMATION</b>\n\n![4 Channels of Example Image](https://i.ibb.co/s1Lp04V/Screen-Shot-2021-01-27-at-5-14-45-PM.png)\n\nThe dataset is comprised of **`21,806`** training images and **`559`** testing images for a total of **`22,365`** images.\n* The images in this dataset are confocal microscopy images containinng 4 channels (R,G,B,Y).<br>\n&nbsp;&nbsp; - Red (Microtubules)<br>\n&nbsp;&nbsp; - Green (Protein of interest)<br>\n&nbsp;&nbsp; - Blue (Nucleus)<br>\n&nbsp;&nbsp; - Yellow (Endoplasmic reticulum)\n\n* The images are a mix of resolutions (1728x1728, 2048x2048 and 3072x3072).\n* The images are available in both **`.png`** and **`.tfrecord`** format. \n* You are predicting protein organelle localization labels for each cell in the image. Border cells are included when there is enough information to decide on the labels.\n\n\n\n<br>\n\n**There are in total 19 different labels present in the dataset (18 labels for specific locations, and label 18 for negative and unspecific signal).**\n* The dataset is acquired in a highly standardized way using one imaging modality (confocal microscopy). However, the dataset comprises 17 different cell types of highly different morphology, which affect the protein patterns of the different organelles. \n* The labels are represented as integers that map to the following:\n\n> **`0`** - Nucleoplasm <br>\n> **`1`** - Nuclear membrane <br>\n> **`2`** - Nucleoli <br>\n> **`3`** - Nucleoli fibrillar center <br>\n> **`4`** - Nuclear speckles <br>\n> **`5`** - Nuclear bodies <br>\n> **`6`** - Endoplasmic reticulum <br>\n> **`7`** - Golgi apparatus <br>\n> **`8`** - Intermediate filaments <br>\n> **`9`** - Actin filaments <br>\n> **`10`** - Microtubules <br>\n> **`11`** - Mitotic spindle <br>\n> **`12`** - Centrosome <br>\n> **`13`** - Plasma membrane <br>\n> **`14`** - Mitochondria <br>\n> **`15`** - Aggresome <br>\n> **`16`** - Cytosol <br>\n> **`17`** - Vesicles and punctate cytosolic patterns <br>\n> **`18`** -  Negative <br>\n\n\n<b style=\"text-decoration: underline; font-family: Verdana;\">DATA FILES</b>\n> **`train.csv`** - the training data information where respective IDs are mapped to labels<br>\n**`sample_submission.csv`** - a sample submission file in the correct format"},{"metadata":{"papermill":{"duration":0.042063,"end_time":"2021-01-13T22:39:39.507915","exception":false,"start_time":"2021-01-13T22:39:39.465852","status":"completed"},"tags":[]},"cell_type":"markdown","source":"<h3 style=\"text-align: font-family: Verdana; font-size: 20px; font-style: normal; font-weight: normal; text-decoration: none; text-transform: none; letter-spacing: 2px; color: navy; background-color: #ffffff;\">1.2  THE GOAL</h3>\n\n---\n\nIn this competition, we will utilize the weakly labelled dataset to create a model that can segment and conduct multi-label classification on all individual cells located within a microscopy image. A give cell can have multiple labels, and the segmentation maps and ground-truth individual cell annotations are not provided."},{"metadata":{},"cell_type":"markdown","source":"<h3 style=\"text-align: font-family: Verdana; font-size: 20px; font-style: normal; font-weight: normal; text-decoration: none; text-transform: none; letter-spacing: 2px; color: navy; background-color: #ffffff;\">1.3  ADDITIONAL INFORMATION ON CELL MORPHOLOGY</h3>\n\n<p style=\"font-size: 10px; color: red; font-weight: bold; font-family: Verdana; text-transform: uppercase;\">Most Of The Content For This Markdown Cell Comes From <a href=\"https://www.kaggle.com/c/hpa-single-cell-image-classification/discussion/214827\">This Notebook</a> Written By The Talented <a href=\"https://www.kaggle.com/saurabhshahane\">User: Saurabh Shahane</a></p>\n\n---\n\n<br><b style=\"text-decoration: underline; font-family: Verdana; text-transform: uppercase;\">Nucleoplasm - (0)</b>\n* The fluid inside the nucleus surrounded by nuclear membrane is called nucleoplasm\n* It controls the cell's growth and reproduction because it contains cell's hereditary information\n* Protein is localized to the Nucleoplasm\n\n<br><b style=\"text-decoration: underline; font-family: Verdana; text-transform: uppercase;\">Nuclear Membrane - (1)</b>\n* A nuclear membrane is a double membrane that encloses the cell nucleus\n* It serves to separate the chromosomes from the rest of the cell\n* The nuclear membrane includes an array of small holes or pores that permit the passage of certain materials, such as nucleic acids and proteins, between the nucleus and cytoplasm\n\n<br><b style=\"text-decoration: underline; font-family: Verdana; text-transform: uppercase;\">Nucleoli - (2)</b>\n* Nucleoli are small basophilic spherical bodies located in the nucleus\n* Usually they can be found in the central nuclear region but may also be close to the nuclear membrane.\n\n<br><b style=\"text-decoration: underline; font-family: Verdana; text-transform: uppercase;\">Nucleoli Fibrillar Center - (3)</b>\n* The Nucleolus Fibrillar Center (FC) is a sub-compartment of most metazoan nucleoli.\n\n<br><b style=\"text-decoration: underline; font-family: Verdana; text-transform: uppercase;\">Nuclear Speckles - (4)</b>\n* The nuclear speckles are small subnuclear membrane less organelles or structures, also called the splicing factor (SF) compartments that correspond to nuclear domains located in interchromatin regions of the nucleoplasm\n\n<br><b style=\"text-decoration: underline; font-family: Verdana; text-transform: uppercase;\">Nuclear Bodies - (5)</b>\n* Nuclear bodies are membraneless structures found in the cell nuclei of eukaryotic cells\n* Nuclear bodies include Cajal bodies, the nucleolus, and promyelocytic leukemia protein nuclear bodies\n\n<br><b style=\"text-decoration: underline; font-family: Verdana; text-transform: uppercase;\">Endoplasmic Reticulum - (6)</b>\n* Endoplasmic reticulum (ER) is a continuous membrane system that forms a series of flattened sacs within the cytoplasm of eukaryotic cells. \n* ER serves multiple functions, being important particularly in the synthesis, folding, modification, and transport of proteins.\n\n<br><b style=\"text-decoration: underline; font-family: Verdana; text-transform: uppercase;\">Golgi Apparatus - (7)</b>\n* The Golgi apparatus is an organelle in eukaryotic organisms that moves molecules from the endoplasmic reticulum to their destination.\n* The organelle also modifies products of the endoplasmic reticulum to their final form.\n* The Golgi apparatus is comprised of a series of flattened sacs that extend from the endoplasmic reticulum. The main function of the Golgi apparatus is the ability to deliver vesicles, or packets of various cell products, to different locations throughout the cell.\n* The Golgi also has important functions in tagging vesicles with proteins and sugar molecules, which serve as identifiers for the vesicles so they can be delivered to the proper target.\n* The organelle is also called the Golgi complex or Golgi body.\n\n<br><b style=\"text-decoration: underline; font-family: Verdana; text-transform: uppercase;\">Intermediate Filaments - (8)</b>\n* Intermediate filaments are important components of the cell's cytoskeletal system.\n* They may stabilize organelles, like the nucleus, or they may be involved in specialized junctions.\n* They are distinguished from \"thin filaments\" by their size (8-10 nm) and the fact that thin filaments are obviously motile.\n\n<br><b style=\"text-decoration: underline; font-family: Verdana; text-transform: uppercase;\">Actin Filaments - (9)</b>\n* Actin Filaments, also called Microfilaments, are protein filaments in the cytoplasm of eukaryotic cells that form part of the cytoskeleton.\n* They are primarily composed of polymers of actin, but are modified by and interact with numerous other proteins in the cell.\n\n<br><b style=\"text-decoration: underline; font-family: Verdana; text-transform: uppercase;\">Microtubules - (10)</b>\n* Microtubules are microscopic hollow tubes made of the proteins alpha and beta tubulin that are part of a cell's cytoskeleton, a network of protein filaments that extends throughout the cell, gives the cell shape, and keeps its organelles in place.\n\n<br><b style=\"text-decoration: underline; font-family: Verdana; text-transform: uppercase;\">Mitotic Spindle - (11)</b>\n* The mitotic spindle is the macromolecular machine that segregates chromosomes to two daughter cells during mitosis.\n* The major structural elements of the spindle are microtubule polymers, whose intrinsic polarity and dynamic properties are critical for bipolar spindle organization and function.\n\n<br><b style=\"text-decoration: underline; font-family: Verdana; text-transform: uppercase;\">Centrosome - (12)</b>\n* Centrosomes are organelles which serve as the main microtubule organizing centers for animal cells.\n* Centrosomes are made of from arrangement of two barrel-shaped clusters of microtubules, called “centrioles,” and a complex of proteins that help additional microtubules to form.\n\n<br><b style=\"text-decoration: underline; font-family: Verdana; text-transform: uppercase;\">Plasma Membrane - (13)</b>\n* The plasma membrane, also called the cell membrane, is the membrane found in all cells that separates the interior of the cell from the outside environment.\n* In bacterial and plant cells, a cell wall is attached to the plasma membrane on its outside surface.\n\n<br><b style=\"text-decoration: underline; font-family: Verdana; text-transform: uppercase;\">Mitochondria - (14)</b>\n* Mitochondria are membrane-bound cell organelles (mitochondrion, singular) that generate most of the chemical energy needed to power the cell's biochemical reactions.\n* Chemical energy *(powerhouse of the cell)* produced by the mitochondria is stored in a small molecule called adenosine triphosphate (ATP).\n\n<br><b style=\"text-decoration: underline; font-family: Verdana; text-transform: uppercase;\">Aggresome - (15)</b>\n* An aggresome refers to an aggregation of misfolded proteins in the cell, formed when the protein degradation system of the cell is overwhelmed.\n* Aggresome formation is a highly regulated process that possibly serves to organize misfolded proteins into a single location.\n\n<br><b style=\"text-decoration: underline; font-family: Verdana; text-transform: uppercase;\">Cytosol - (16)</b>\n* Cytosol is the liquid found inside of cells.\n* It is the water-based solution in which organelles, proteins, and other cell structures float.\n* The cytosol of any cell is a complex solution, whose properties allow the functions of life to take place.\n\n<br><b style=\"text-decoration: underline; font-family: Verdana; text-transform: uppercase;\">Vesicles - (17)</b>\n* A vesicle is a structure within or outside a cell, consisting of liquid or cytoplasm enclosed by a lipid bilayer.\n* Vesicles form naturally during the processes of secretion (exocytosis), uptake (endocytosis) and transport of materials within the plasma membrane."},{"metadata":{"papermill":{"duration":0.042459,"end_time":"2021-01-13T22:39:39.678624","exception":false,"start_time":"2021-01-13T22:39:39.636165","status":"completed"},"tags":[]},"cell_type":"markdown","source":"<h1 style=\"font-family: Verdana; font-size: 24px; font-style: normal; font-weight: bold; text-decoration: none; text-transform: none; letter-spacing: 3px; color: navy; background-color: #ffffff;\" id=\"setup\">2&nbsp;&nbsp;NOTEBOOK SETUP</h1>"},{"metadata":{"execution":{"iopub.execute_input":"2021-01-13T22:39:39.779248Z","iopub.status.busy":"2021-01-13T22:39:39.778548Z","iopub.status.idle":"2021-01-13T22:39:40.76653Z","shell.execute_reply":"2021-01-13T22:39:40.767021Z"},"papermill":{"duration":1.046203,"end_time":"2021-01-13T22:39:40.767178","exception":false,"start_time":"2021-01-13T22:39:39.720975","status":"completed"},"tags":[],"trusted":true},"cell_type":"code","source":"# Define the root data directory\nROOT_DIR = \"/kaggle/input\"\nDATA_DIR = \"/kaggle/input/hpa-single-cell-image-classification\"\nMASK_DIR = \"/kaggle/input/hpa-mask\"\n\n# Define segmentation mask directory\nCELL_MASK_DIR = os.path.join(MASK_DIR, \"hpa_cell_mask\")\nCELL_MASK_NPZ_PATHS = sorted([os.path.join(CELL_MASK_DIR, f_name) for f_name in os.listdir(CELL_MASK_DIR)])\nNUC_MASK_DIR = os.path.join(MASK_DIR, \"hpa_nuclei_mask\")\nNUC_MASK_NPZ_PATHS = sorted([os.path.join(NUC_MASK_DIR, f_name) for f_name in os.listdir(NUC_MASK_DIR)])\n\n# Define the paths to the training tiles for the cell-wise classification dataset\nRED_TILE_DIR = os.path.join(ROOT_DIR, \"human-protein-atlas-red-cell-tile-dataset\")\nGREEN_TILE_DIR = os.path.join(ROOT_DIR, \"human-protein-atlas-green-cell-tile-dataset\")\nBLUE_TILE_DIR = os.path.join(ROOT_DIR, \"human-protein-atlas-blue-cell-tile-dataset\")\nYELLOW_TILE_DIR = os.path.join(ROOT_DIR, \"human-protein-atlas-yellow-cell-tile-dataset\")\n\n# Define the paths to the training and testing tfrecord and image folders respectively\nTRAIN_IMG_DIR = os.path.join(DATA_DIR, \"train\")\nTRAIN_TFREC_DIR = os.path.join(DATA_DIR, \"train_tfrecords\")\nTEST_IMG_DIR = os.path.join(DATA_DIR, \"test\")\nTEST_TFREC_DIR = os.path.join(DATA_DIR, \"test_tfrecords\")\n\n# Capture all the relevant full image paths\nTRAIN_IMG_PATHS = sorted([os.path.join(TRAIN_IMG_DIR, f_name) for f_name in os.listdir(TRAIN_IMG_DIR)])\nTEST_IMG_PATHS = sorted([os.path.join(TEST_IMG_DIR, f_name) for f_name in os.listdir(TEST_IMG_DIR)])\nprint(f\"\\n... Recall that 4 training images compose one example (R,G,B,Y) ...\")\nprint(f\"... \\t– i.e. The first 4 training files are:\")\nfor path in [x.rsplit('/',1)[1] for x in TRAIN_IMG_PATHS[:4]]: print(f\"... \\t\\t– {path}\")\nprint(f\"\\n... The number of training images is {len(TRAIN_IMG_PATHS)} i.e. {len(TRAIN_IMG_PATHS)//4} 4-channel images ...\")\nprint(f\"... The number of testing images is {len(TEST_IMG_PATHS)} i.e. {len(TEST_IMG_PATHS)//4} 4-channel images ...\")\n\n# Capture all the relevant full tfrec paths\nTRAIN_TFREC_PATHS = sorted([os.path.join(TRAIN_TFREC_DIR, f_name) for f_name in os.listdir(TRAIN_TFREC_DIR)])\nTEST_TFREC_PATHS = sorted([os.path.join(TEST_TFREC_DIR, f_name) for f_name in os.listdir(TEST_TFREC_DIR)])\nprint(f\"\\n... The number of training tfrecord files is {len(TRAIN_TFREC_PATHS)} ...\")\nprint(f\"... The number of testing tfrecord files is {len(TEST_TFREC_PATHS)} ...\\n\")\n\n# Define paths to the relevant csv files\nPUBLIC_DATA_CSV = \"/kaggle/input/publichpa/kaggle_2021.tsv\"\nTRAIN_CSV = os.path.join(DATA_DIR, \"train.csv\")\nSS_CSV = os.path.join(DATA_DIR, \"sample_submission.csv\")\nUPDATED_TRAIN_CSV = os.path.join(ROOT_DIR, \"hpa-processed-train-dataframe-with-cellwise-rle/train_df_w_masks_shape_bboxes.csv\")\n\n# Create the relevant dataframe objects\nbbox_train_df = pd.read_csv(UPDATED_TRAIN_CSV)\nbbox_train_df.cell_masks = bbox_train_df.cell_masks.apply(lambda x: ast.literal_eval(x))\nbbox_train_df.bboxes = bbox_train_df.bboxes.apply(lambda x: ast.literal_eval(x))\ntrain_df = pd.read_csv(TRAIN_CSV)\nss_df = pd.read_csv(SS_CSV)\n\nprint(\"\\n\\nTRAIN DATAFRAME\\n\\n\")\ndisplay(train_df.head(3))\n\nprint(\"\\n\\nUPDATED TRAIN DATAFRAME\\n\\n\")\ndisplay(bbox_train_df.head(3))\n\nprint(\"\\n\\nSAMPLE SUBMISSION DATAFRAME\\n\\n\")\ndisplay(ss_df.head(3))","execution_count":null,"outputs":[]},{"metadata":{"papermill":{"duration":0.043842,"end_time":"2021-01-13T22:39:40.85577","exception":false,"start_time":"2021-01-13T22:39:40.811928","status":"completed"},"tags":[]},"cell_type":"markdown","source":"<h1 style=\"text-align: font-family: Verdana; font-size: 24px; font-style: normal; font-weight: bold; text-decoration: none; text-transform: none; letter-spacing: 3px; color: navy; background-color: #ffffff;\" id=\"helper_functions\">3&nbsp;&nbsp;HELPER FUNCTIONS</h1>"},{"metadata":{"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","execution":{"iopub.execute_input":"2021-01-13T22:39:40.980411Z","iopub.status.busy":"2021-01-13T22:39:40.979421Z","iopub.status.idle":"2021-01-13T22:39:40.982404Z","shell.execute_reply":"2021-01-13T22:39:40.981881Z"},"papermill":{"duration":0.080032,"end_time":"2021-01-13T22:39:40.982523","exception":false,"start_time":"2021-01-13T22:39:40.902491","status":"completed"},"tags":[],"trusted":true},"cell_type":"code","source":"def load_image(img_id, img_dir):\n    \"\"\" Load An Image Using ID and Directory Path - Composes 4 Individual Images \"\"\"\n    rgby = [\n        np.asarray(Image.open(os.path.join(img_dir, img_id+f\"_{c}.png\")), np.uint8) \\\n        for c in [\"red\", \"green\", \"blue\", \"yellow\"]\n    ]\n    return np.stack(rgby, axis=-1)\n\n\ndef plot_rgb(arr, figsize=(12,12)):\n    \"\"\" Plot 3 Channel Microscopy Image \"\"\"\n    plt.figure(figsize=figsize)\n    plt.title(f\"RGB Composite Image\", fontweight=\"bold\")\n    plt.imshow(arr)\n    plt.axis(False)\n    plt.show()\n    \n    \ndef get_color_path_map(color_dir):\n    c_p_map = {}\n    for c in tqdm(os.listdir(color_dir), total=len(os.listdir(color_dir))):\n        if c.endswith(\"256\"):\n            cls = c.split(\"_\", 1)[1].rsplit(\"_\",1)[0]\n            c_dir = os.path.join(color_dir, c, \"data\", \"train_tiles\", cls)\n            c_p_map[STR_2_INT_LOWER[cls]] = sorted([\n                os.path.join(c_dir, f_name) \\\n                for f_name in  os.listdir(c_dir) \\\n                if f_name.endswith(\".png\")\n            ])\n    return c_p_map\n\n\ndef plot_ex(arr, figsize=(20,6), title=None, plot_merged=True, rgb_only=False):\n    \"\"\" Plot 4 Channels Side by Side \"\"\"\n    if plot_merged and not rgb_only:\n        n_images=5 \n    elif plot_merged and rgb_only:\n        n_images=4\n    elif not plot_merged and rgb_only:\n        n_images=4\n    else:\n        n_images=3\n    plt.figure(figsize=figsize)\n    if type(title) == str:\n        plt.suptitle(title, fontsize=20, fontweight=\"bold\")\n\n    for i, c in enumerate([\"Red Channel – Microtubles\", \"Green Channel – Protein of Interest\", \"Blue - Nucleus\", \"Yellow – Endoplasmic Reticulum\"]):\n        if not rgb_only:\n            ch_arr = np.zeros_like(arr[..., :-1])        \n        else:\n            ch_arr = np.zeros_like(arr)\n        if c in [\"Red Channel – Microtubles\", \"Green Channel – Protein of Interest\", \"Blue - Nucleus\"]:\n            ch_arr[..., i] = arr[..., i]\n        else:\n            if rgb_only:\n                continue\n            ch_arr[..., 0] = arr[..., i]\n            ch_arr[..., 1] = arr[..., i]\n        plt.subplot(1,n_images,i+1)\n        plt.title(f\"{c.title()}\", fontweight=\"bold\")\n        plt.imshow(ch_arr)\n        plt.axis(False)\n        \n    if plot_merged:\n        plt.subplot(1,n_images,n_images)\n        \n        if rgb_only:\n            plt.title(f\"Merged RGB\", fontweight=\"bold\")\n            plt.imshow(arr)\n        else:\n            plt.title(f\"Merged RGBY into RGB\", fontweight=\"bold\")\n            plt.imshow(convert_rgby_to_rgb(arr))\n        plt.axis(False)\n        \n    plt.tight_layout(rect=[0, 0.2, 1, 0.97])\n    plt.show()\n    \n    \ndef flatten_list_of_lists(l_o_l, to_string=False):\n    if not to_string:\n        return [item for sublist in l_o_l for item in sublist]\n    else:\n        return [str(item) for sublist in l_o_l for item in sublist]\n\n    \n    \ndef grab_contours(cnts):\n    # if the length the contours tuple returned by cv2.findContours\n    # is '2' then we are using either OpenCV v2.4, v4-beta, or\n    # v4-official\n    if len(cnts) == 2:\n        cnts = cnts[0]\n\n    # if the length of the contours tuple is '3' then we are using\n    # either OpenCV v3, v4-pre, or v4-alpha\n    elif len(cnts) == 3:\n        cnts = cnts[1]\n\n    # otherwise OpenCV has changed their cv2.findContours return\n    # signature yet again and I have no idea WTH is going on\n    else:\n        raise Exception((\"Contours tuple must have length 2 or 3, \"\n            \"otherwise OpenCV changed their cv2.findContours return \"\n            \"signature yet again. Refer to OpenCV's documentation \"\n            \"in that case\"))\n\n    # return the actual contours array\n    return cnts\n\n    \ndef convert_rgby_to_rgb(arr):\n    \"\"\" Convert a 4 channel (RGBY) image to a 3 channel RGB image.\n    \n    Advice From Competition Host/User: lnhtrang\n\n    For annotation (by experts) and for the model, I guess we agree that individual \n    channels with full range px values are better. \n    In annotation, we toggled the channels. \n    For visualization purpose only, you can try blending the channels. \n    For example, \n        - red = red + yellow\n        - green = green + yellow/2\n        - blue=blue.\n        \n    Args:\n        arr (numpy array): The RGBY, 4 channel numpy array for a given image\n    \n    Returns:\n        RGB Image\n    \"\"\"\n    \n    rgb_arr = np.zeros_like(arr[..., :-1])\n    rgb_arr[..., 0] = arr[..., 0]\n    rgb_arr[..., 1] = arr[..., 1]+arr[..., 3]/2\n    rgb_arr[..., 2] = arr[..., 2]\n    \n    return rgb_arr\n\ndef tif_gzip_to_png(tif_path):\n    \"\"\"Function to convert .tif.gz to .png and put it in the same folder\n    \n    Eg. for working in local work station\n    \n    Args:\n        tif_path (str): Path to the tif zip file to be converted to png\n        \n    Returns:\n        None; Zip will be unzipped to same directory as .tif zip exists    \n    \"\"\"\n    \n    png_path = pathlib.Path(tif_path.replace('.tif.gz','.png'))\n    tf = gzip.open(tif_path).read()\n    img = imageio.imread(tf, 'tiff')\n    imageio.imwrite(png_path, img)\n    \n    \ndef download_and_convert_tifgzip_to_png(url, target_path):    \n    \"\"\"Function to convert .tif.gz to .png and put it in the same folder\n    \n    Args:\n        url (str): Path to the url containing the tifgzip file\n        target_path (str): Path to directory to unzip to\n        \n    Returns:\n        None; Images are downloaded and unzipped    \n    \"\"\"\n    \n    r = requests.get(url)\n    f = io.BytesIO(r.content)\n    tf = gzip.open(f).read()\n    img = imageio.imread(tf, 'tiff')\n    imageio.imwrite(target_path, img)\n\n    \ndef get_new_data():\n    public_hpa_df = pd.read_csv('../input/publichpa/kaggle_2021.tsv',sep='\\t',header=None)\n    public_hpa_df.columns = ['Image', 'Label']\n    colors = ['blue', 'red', 'green', 'yellow']\n    save_dir = os.path.join(os.getcwd(),'publichpa')\n    if not os.path.exists(save_dir):\n        os.makedirs(save_dir)\n\n    for i, row in public_hpa_df.iterrows():\n        try:\n            img = row.Image\n            for color in colors:\n                img_url = f'{img}_{color}.tif.gz'\n                save_path = os.path.join(save_dir,  f'{os.path.basename(img)}_{color}.png')\n                download_and_convert_tifgzip_to_png(img_url, save_path)\n                print(f'Downloaded {img_url} as {save_path}')    \n        except:\n            print(f'failed to download: {img}')\n    return save_dir\n\ndef seg_demo_plot(mt, er, nu):\n    fig, ax = plt.subplots(3,3, figsize=(20,18))\n    for i in range(3):\n        microtubule = plt.imread(mt[i])    \n        endoplasmicrec = plt.imread(er[i])    \n        nuclei = plt.imread(nu[i])\n        mask = plt.imread(mt[i].replace('red','predictedmask'))\n        img = np.dstack((microtubule, endoplasmicrec, nuclei))\n        ax[0][i].imshow(img)\n        ax[1][i].imshow(mask)\n        ax[2][i].imshow(img)\n        ax[2][i].imshow(mask, alpha=0.5)\n\n        ax[0][i].axis('off')\n        ax[1][i].axis('off')\n        ax[2][i].axis('off')\n    plt.tight_layout()\n    plt.show()\n    \n    \ndef multihot_melt(row):\n    \"\"\" Melt current column into 19 binary-1-hot columns \"\"\"\n    return [1 if L in row[\"target_list\"] else 0 for L in LBL_NAMES]\n\nprint(\"\\n... DEMO IMAGE BROKEN INTO THE RESPECTIVE CHANNELS ...\\n\")\nrgby_demo = load_image(train_df.iloc[0].ID, img_dir=TRAIN_IMG_DIR)\nplot_ex(rgby_demo, title=\"Demo Image\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Define the paths to the training files for the tile dataset as a map from class index to list of paths\nRED_FILE_MAP = get_color_path_map(RED_TILE_DIR)\nGREEN_FILE_MAP = get_color_path_map(GREEN_TILE_DIR)\nBLUE_FILE_MAP = get_color_path_map(BLUE_TILE_DIR)\nYELLOW_FILE_MAP = get_color_path_map(YELLOW_TILE_DIR)","execution_count":null,"outputs":[]},{"metadata":{"papermill":{"duration":0.143736,"end_time":"2021-01-13T22:50:29.721487","exception":false,"start_time":"2021-01-13T22:50:29.577751","status":"completed"},"tags":[]},"cell_type":"markdown","source":"<h1 style=\"text-align: font-family: Verdana; font-size: 24px; font-style: normal; font-weight: bold; text-decoration: none; text-transform: none; letter-spacing: 3px; color: navy; background-color: #ffffff;\" id=\"label_ex\">4&nbsp;&nbsp;LABEL EXPLORATION</h1>\n\nThis section will explore the distribution and occurences of the 19 different labels that exist in this dataset"},{"metadata":{"papermill":{"duration":0.144463,"end_time":"2021-01-13T22:50:30.008724","exception":false,"start_time":"2021-01-13T22:50:29.864261","status":"completed"},"tags":[]},"cell_type":"markdown","source":"<h3 style=\"text-align: font-family: Verdana; font-size: 20px; font-style: normal; font-weight: normal; text-decoration: none; text-transform: none; letter-spacing: 2px; color: navy; background-color: #ffffff;\">4.1 INVESTIGATE TRAIN CSV BASICS</h3>\n\n---\n\nLet's investigate the **`train.csv`** file at a high-level first.\n\n<br><b style=\"text-decoration: underline; font-family: Verdana; text-transform: uppercase;\">We Deduce From The Output Below...</b>\n\n* There are 2 columns<br>\n&nbsp;&nbsp; - **`ID`** &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;--> Unique Identifier Which Identifies Which Respective Image Is Being Referenced<br>\n&nbsp;&nbsp; - **`Label`** --> A **`|`** delimited list of labels that exist within a given image\n\n\n* The **`Label`** column has only **`432`** unique values\n\n* The maximum number of unique labels in an image is **`5`**\n\n* The minimum number of unique labels in an image is **`1`**\n\n---\n\n**NOTE: We will also add a column, titled `string_label` which will be the `Label` column with the full names of the classes/labels replacing the integer representations. This will help us with visualizations and plotting later.**"},{"metadata":{"trusted":true},"cell_type":"code","source":"display(train_df.describe().T)\n\nMAX_LABELS_PER_IMAGE = max([x.count(\"|\")+1 for x in train_df.Label.unique()])\nprint(f\"\\n... The Maximum Number of Labels Per Image is : >>{MAX_LABELS_PER_IMAGE}<< ...\\n\\n\")\n\ntrain_df.info()\n\ntrain_df[\"string_label\"] = train_df.Label.apply(lambda x: \"|\".join([INT_2_STR[int(i)] for i in x.split(\"|\")]))\ntrain_df.sample(10)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<h3 style=\"text-align: font-family: Verdana; font-size: 20px; font-style: normal; font-weight: normal; text-decoration: none; text-transform: none; letter-spacing: 2px; color: navy; background-color: #ffffff;\">4.2 WHICH PROTEINS OCCUR MOST OFTEN</h3>\n\n---\n\n<br><b style=\"text-decoration: underline; font-family: Verdana; text-transform: uppercase;\">We Deduce From The Graph Output Below...</b>\n\n* We can see that most common protein structures belong to coarse grained cellular components like the plasma membrane, the cytosol and the nucleus (Nucleoplasm). \n* In contrast small or thin components like the mitotic spindle, microtubles, and vesicles are very seldom in our train data. In addition, rare organelles like Aggresome's and No-Label (Negative) also have very little representation in the dataset. For these classes the prediction will be very difficult as we have only a few examples that may not cover all variation normally present within these biological structures will be captured. From this we can expect that we will struggle and will be forced to make less accurate predictions on the minor classes."},{"metadata":{"trusted":true},"cell_type":"code","source":"label_counts = Counter([c for sublist in train_df.string_label.str.split(\"|\").to_list() for c in sublist])\nfig = px.bar(x=label_counts.keys(), y=label_counts.values(), opacity=0.85, \n             color=label_counts.keys(),\n             color_discrete_sequence=[LABEL_COL_MAP[str(STR_2_INT[x])] for x in label_counts.keys()],\n             labels={\n                 \"y\":\"<b>Number of Occurences Within The Dataset</b>\", \n                 \"x\":\"<b>Label Name</b>\", \n                 \"color\":\"<b>Label Name</b>\"\n             },\n             title=\"<b>Number of Occurences For Each Label Within The Dataset</b>\")\nfig.update_layout(legend_title=None,\n                  font=FIG_FONT,\n                  xaxis_title=\"<b>Label Names</b>\",\n                  yaxis_title=\"<b>Number of Occurences Within The Dataset</b>\")\nfig.update_xaxes(categoryorder=\"total descending\")\nfig.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<h3 style=\"text-align: font-family: Verdana; font-size: 20px; font-style: normal; font-weight: normal; text-decoration: none; text-transform: uppercase; letter-spacing: 2px; color: navy; background-color: #ffffff;\">4.3 Distribution of Multiple Labels Per Image in the Training Data</h3>\n\n---\n\nThis will help us identify the trivial case (one label present) as well as the more difficult cases. NOTE: We already know that the maximum number of present labels for one image is **`5`**.\n\n<br><b style=\"text-decoration: underline; font-family: Verdana; text-transform: uppercase;\">We Deduce From The Graph Output Below...</b>\n\n* Most train images only have 1 or 2 labels.\n* Images containing 3 or more labels are uncommon\n* Images containing 5 labels only occur **3** times"},{"metadata":{"trusted":true},"cell_type":"code","source":"label_distributions = [{k:v for k,v in train_df.string_label.value_counts().items() if k.count(\"|\")+1==i} for i in range(1,6)]\nlabel_distribution_sums = [sum([v for v in d.values()]) for d in label_distributions]\n\nfig = px.bar(x=[str(x) for x in np.arange(1,6)], y=label_distribution_sums, opacity=0.85, log_y=True,\n             color=[str(x) for x in np.arange(1,6)], color_discrete_sequence=LABEL_COLORS[19:0:-4],\n             labels={\n                 \"y\":\"<b>Number of Occurences Within The Dataset</b>\", \n                 \"x\":\"<b>Number of Labels Present In An Image</b>\", \n                 \"color\":\"<b>Number of Labels Present In An Image</b>\",\n             },\n             title=\"<b>Number of Occurences Of Images With Multiple Labels In The Dataset   \" \\\n                   \"<i><sub>(Log Scale for Y-Axis)</sub></i></b>\",)\nfig.update_layout(legend_title=None,\n                  font=FIG_FONT,\n                  xaxis_title=\"<b>Number of Labels Present In An Image</b>\",\n                  yaxis_title=\"<b>Number of Occurences Within The Dataset</b>\")\nfig.update_xaxes(categoryorder=\"total descending\")\nfig.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<h3 style=\"text-align: font-family: Verdana; font-size: 20px; font-style: normal; font-weight: normal; text-decoration: none; text-transform: none; letter-spacing: 2px; color: navy; background-color: #ffffff;\">4.4 DISTRIBUTION OF SINGLE-LABEL IMAGES VS. MULTI-LABEL IMAGES WITHIN THE TRAINING DATA</h3>\n\n---\n\nThe single-label case is the trivial case and will allow us to deduce cell-level classification for classes with sufficient data. \nThe multi-label case is where the weak-supervised training becomes more difficult. We will identify differences in the distributions of labels so we can see which organelles are more commonly found alone vs. with other organelles as labels.\n\n<br><b style=\"text-decoration: underline; font-family: Verdana; text-transform: uppercase;\">We Deduce From The Graph Output Below...</b>\n\n* The most common organelle (Nucleoplasm) is also the one most commonly used to individually identify an image. This will be incredibly useful, as it will allow us to identify/classify Nucleoplasm's with a high-degree of confidence in images containing Nucleoplasm's and other types of organelles.\n* Both Mitochondria and Nuclear Speckles are much more commonly found as individual image labels when compared to the gross distribution across all images."},{"metadata":{"trusted":true},"cell_type":"code","source":"single_label_images = label_distributions[0]\nmulti_label_images = {k:v-single_label_images[k] for k,v in label_counts.items()}\n\nfig = px.bar(x=single_label_images.keys(), y=single_label_images.values(), opacity=0.85, \n             color=single_label_images.keys(),\n             color_discrete_sequence=[LABEL_COL_MAP[str(STR_2_INT[x])] for x in single_label_images.keys()],\n             labels={\n                 \"y\":\"<b>Count Of Single-Label Images</b>\", \n                 \"x\":\"<b>Label Name</b>\", \n                 \"color\":\"<b>Label Name</b>\"\n             },\n             title=\"<b>Number of Occurences Of Single-Label Images</b>\")\nfig.update_layout(legend_title=None,\n                  font=FIG_FONT,\n                  xaxis_title=\"<b>Label Names</b>\",\n                  yaxis_title=\"<b>Count Of Single-Label Images</b>\")\nfig.update_xaxes(categoryorder=\"total descending\")\nfig.show()\n\nfig = px.bar(x=multi_label_images.keys(), y=multi_label_images.values(), opacity=0.85, \n             color=multi_label_images.keys(),\n             color_discrete_sequence=[LABEL_COL_MAP[str(STR_2_INT[x])] for x in multi_label_images.keys()],\n             labels={\n                 \"y\":\"<b>Count Of Multi-Label Images</b>\", \n                 \"x\":\"<b>Label Name</b>\", \n                 \"color\":\"<b>Label Name</b>\"\n             },\n             title=\"<b>Number of Occurences Of Multi-Label Images</b>\")\nfig.update_layout(legend_title=None,\n                  font=FIG_FONT,\n                  xaxis_title=\"<b>Label Names</b>\",\n                  yaxis_title=\"<b>Counts Of Multi-Label Images</b>\")\nfig.update_xaxes(categoryorder=\"total descending\")\nfig.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<h3 style=\"text-align: font-family: Verdana; font-size: 20px; font-style: normal; font-weight: normal; text-decoration: none; text-transform: none; letter-spacing: 2px; color: navy; background-color: #ffffff;\">4.5 IDENTIFY CORRELATION IN IMAGES WITH MULTIPLE LABELS</h3>\n\n---\n\nIdentifying correlations between organelles found in multi-label objects may help us deduce which organelles often pair together.\n\n<br><b style=\"text-decoration: underline; font-family: Verdana; text-transform: uppercase;\">We Deduce From The Graph Output Below...</b>\n\n* Almost all of the organelles only exhibit a slight correlation to other organelles.\n* One slight exception might be the co-occurence of **microtubles** and **mitotic-spindles** within an image (~ +20%)\n\n* Another slight exception is **nucleoplasm** negatively correlation (~ -30% to -10%) with the following in descending order of intensity:<br>\n&nbsp;&nbsp; - **nuclear speckles**<br>\n&nbsp;&nbsp; - **cytoplasm**<br>\n&nbsp;&nbsp; - **plasma membrane**<br>\n&nbsp;&nbsp; - **endoplasmic reticulum**<br>"},{"metadata":{"trusted":true},"cell_type":"code","source":"# Update training dataframe\ntrain_df[\"target_list\"] = train_df.string_label.str.split(\"|\")\ntrain_df[\"n_targets\"] = train_df.target_list.apply(len)\ntrain_df = pd.concat([train_df, train_df.apply(multihot_melt, axis=1, result_type='expand')], axis=\"columns\")\ntrain_df.rename(columns = {i:LBL_NAMES[i].lower().replace(\" \", \"_\") for i in np.arange(19)}, inplace = True)\n\n# Split The Dataframe Into the Single-Label and Multi-Label Portions\nsingle_label_df = train_df[train_df.n_targets==1]\nmulti_label_df = train_df[train_df.n_targets>1]\nplt.figure(figsize=(16,16))\nplt.title(\"Heatmap Showing Correlation Between Organelles Co-Occuring Within A Single Image\", fontweight=\"bold\")\nsns.heatmap(multi_label_df[multi_label_df.n_targets>1][[x.lower().replace(\" \", \"_\") for x in LBL_NAMES]].corr(), cmap=\"Spectral\", vmin=-1, vmax=1)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"papermill":{"duration":0.980498,"end_time":"2021-01-13T22:54:16.184969","exception":false,"start_time":"2021-01-13T22:54:15.204471","status":"completed"},"tags":[]},"cell_type":"markdown","source":"<h1 style=\"text-align: font-family: Verdana; font-size: 24px; font-style: normal; font-weight: bold; text-decoration: none; text-transform: none; letter-spacing: 3px; color: navy; background-color: #ffffff;\" id=\"image_ex\">5&nbsp;&nbsp;IMAGE EXPLORATION</h1>\n\nThis section will explore the images themselves and show examples of each class as well as certain common combinations."},{"metadata":{},"cell_type":"markdown","source":"<h3 style=\"text-align: font-family: Verdana; font-size: 20px; font-style: normal; font-weight: normal; text-decoration: none; text-transform: none; letter-spacing: 2px; color: navy; background-color: #ffffff;\">5.1 VISUALIZE EACH LABEL USING IMAGES WHERE THAT IS THE ONLY LABEL PRESENT</h3>\n\n---\n\nThe trivial class of images are those where only one label is present. In these instances, we know that every cell can be labelled accurately based on the image-level label. Let's see an example of each label and see what we can find out."},{"metadata":{"trusted":true},"cell_type":"code","source":"# SETUP FOR FUTURE AND SORT\nunique_class_map = {\n    row[\"string_label\"]:row[\"ID\"] \\\n    for i, row in single_label_df.drop_duplicates(subset=\"Label\")[[\"ID\", \"string_label\"]].iterrows()\n}\nindex_map = {v: i for i, v in enumerate(LBL_NAMES)}\nsorted_unique_class_map={k:v for k,v in sorted(unique_class_map.items(), key=lambda pair: index_map[pair[0]])}\n\nfor cls, ID, in sorted_unique_class_map.items():\n    rgby_img = load_image(ID, img_dir=TRAIN_IMG_DIR)\n    plot_ex(rgby_img, title=f\"Example Of {cls} Class\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<h3 style=\"text-align: font-family: Verdana; font-size: 20px; font-style: normal; font-weight: normal; text-decoration: none; text-transform: none; letter-spacing: 2px; color: navy; background-color: #ffffff;\">5.2 IDENTIFY DUPLICATES WITHIN THE TRAINING DATA</h3>\n\n---\n\nInspired by [**this notebook**](https://www.kaggle.com/rai555/hpa-duplicate-images-in-train) I wanted to highlight similar images that can be found within the training dataset. This might also give insight into how to detect leakage between the train and test datasets."},{"metadata":{"trusted":true},"cell_type":"code","source":"# def get_phash(_id):\n#     return str(imagehash.average_hash(Image.fromarray(convert_rgby_to_rgb(load_image(_id, TRAIN_IMG_DIR)))))\n\n# tqdm.pandas()\n# train_df[\"rgb_phash\"] = train_df.ID.progress_apply(lambda x: get_phash(x))\n\n# def tf_load_image(path, img_size=(256,256)):\n#     return decode_img(tf.io.read_file(path), img_size, n_channels=3)\n\n# def decode_img(img, img_size=(256,256), n_channels=3):\n#     \"\"\"TBD\"\"\"\n    \n#     # convert the compressed string to a 3D uint8 tensor\n#     img = tf.image.decode_png(img, channels=n_channels)\n\n#     # resize the image to the desired size\n#     return tf.cast(tf.image.resize(img, img_size), tf.uint8)\n    \n\n# BATCH_SIZE = 64\n\n# train_ds = tf.data.Dataset.from_tensor_slices([\n#     os.path.join(\"/kaggle/input/shopee-product-matching/train_images\", f_name) \\\n#     for f_name in train_gf.image.to_array()\n# ])\n# train_ds = train_ds.map(lambda x: tf_load_image(x))\n# train_ds = train_ds.batch(BATCH_SIZE).prefetch(tf.data.AUTOTUNE)\n# train_ds\n\n###TBD###","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<h1 style=\"text-align: font-family: Verdana; font-size: 24px; font-style: normal; font-weight: bold; text-decoration: none; text-transform: none; letter-spacing: 3px; color: navy; background-color: #ffffff;\" id=\"segmentation_ex\">6&nbsp;&nbsp;SEGMENTATION EXPLORATION</h1>\n\nWe will use the CellSegmentator tool. This is the tool that the competition host team used in annotating the ground-truth dataset. "},{"metadata":{},"cell_type":"markdown","source":"<h3 style=\"text-align: font-family: Verdana; font-size: 20px; font-style: normal; font-weight: normal; text-decoration: none; text-transform: uppercase; letter-spacing: 2px; color: navy; background-color: #ffffff;\">6.0 Segmentation Utility Functions</h3>\n\n---"},{"metadata":{"trusted":true},"cell_type":"code","source":"def encode_binary_mask(mask, mask_val=1):\n    \"\"\"Converts a binary mask into OID challenge encoding ascii text.\"\"\"\n    mask = np.where(mask==mask_val, 1, 0)\n    \n    # check input mask --\n    if mask.dtype != np.bool:\n        raise ValueError(f\"encode_binary_mask expects a binary mask, received dtype == {mask.dtype}\")\n\n    mask = np.squeeze(mask)\n    if len(mask.shape) != 2:\n        raise ValueError(f\"encode_binary_mask expects a 2d mask, received shape == {mask.shape}\")\n\n    # convert input mask to expected COCO API input --\n    mask_to_encode = mask.reshape(mask.shape[0], mask.shape[1], 1)\n    mask_to_encode = mask_to_encode.astype(np.uint8)\n    mask_to_encode = np.asfortranarray(mask_to_encode)\n\n    # RLE encode mask --\n    encoded_mask = coco_mask.encode(mask_to_encode)[0][\"counts\"]\n\n    # compress and base64 encoding --\n    binary_str = zlib.compress(encoded_mask, zlib.Z_BEST_COMPRESSION)\n    base64_str = base64.b64encode(binary_str)\n    return base64_str\n\n\ndef rle_encoding(img, mask_val=1):\n    \"\"\"\n    Turns our masks into RLE encoding to easily store them\n    and feed them into models later on\n    https://en.wikipedia.org/wiki/Run-length_encoding\n    \n    Args:\n        img (np.array): Segmentation array\n        mask_val (int): Which value to use to create the RLE\n        \n    Returns:\n        RLE string\n    \n    \"\"\"\n    dots = np.where(img.T.flatten() == mask_val)[0]\n    run_lengths = []\n    prev = -2\n    for b in dots:\n        if (b>prev+1): run_lengths.extend((b + 1, 0))\n        run_lengths[-1] += 1\n        prev = b\n        \n    return ' '.join([str(x) for x in run_lengths])\n\n\ndef rle_to_mask(rle_string, height, width):\n    \"\"\" Convert RLE sttring into a binary mask \n    \n    Args:\n        rle_string (rle_string): Run length encoding containing \n            segmentation mask information\n        height (int): Height of the original image the map comes from\n        width (int): Width of the original image the map comes from\n    \n    Returns:\n        Numpy array of the binary segmentation mask for a given cell\n    \"\"\"\n    rows,cols = height,width\n    rle_numbers = [int(num_string) for num_string in rle_string.split(' ')]\n    rle_pairs = np.array(rle_numbers).reshape(-1,2)\n    img = np.zeros(rows*cols,dtype=np.uint8)\n    for index,length in rle_pairs:\n        index -= 1\n        img[index:index+length] = 255\n    img = img.reshape(cols,rows)\n    img = img.T\n    return img\n\n\ndef create_segmentation_maps(list_of_image_lists, batch_size=4):\n    \"\"\" Function to generate segmentation maps using CellSegmentator tool \n    \n    Args:\n        list_of_image_lists (list of lists):\n            - [[micro-tubules(red)], [endoplasmic-reticulum(yellow)], [nucleus(blue)]]\n        batch_size (int): Batch size to use in generating the segmentation masks\n        \n    Returns:\n        List of lists containing RLEs for all the cells in all images\n    \"\"\"\n    \n    all_mask_rles = {}\n    t1 = time.time()\n    segmentator = cellsegmentator.CellSegmentator()\n    print(f\"CELL SEGMENTOR CLASS   {time.time()-t1:.2f}\")\n    print()\n    \n    for i in tqdm(range(0, len(list_of_image_lists[0]), batch_size), total=len(list_of_image_lists[0])//batch_size):\n        \n        # Get batch of images\n        sub_images = [img_channel_list[i:i+batch_size] for img_channel_list in list_of_image_lists]\n        # Do segmentation\n        \n        t1 = time.time()\n        cell_segmentations = segmentator.pred_cells(sub_images)\n        nuc_segmentations = segmentator.pred_nuclei(sub_images[2])\n        print(f\"MODEL PREDICT BATCH {i//batch_size+1}    {time.time()-t1:.2f}\")\n\n        # post-processing\n        for j, path in enumerate(sub_images[0]):\n            t1 = time.time()\n            img_id = path.replace(\"_red.png\", \"\").rsplit(\"/\", 1)[1]\n            nuc_mask, cell_mask = label_cell(nuc_segmentations[j], cell_segmentations[j])\n            new_name = os.path.basename(path).replace('red','mask')\n            all_mask_rles[img_id] = [rle_encoding(cell_mask, mask_val=k) for k in range(1, np.max(cell_mask)+1)]\n            print(f\"LABEL CELL BATCH {i+1}-{j+1}    {time.time()-t1:.2f}\", )\n        print()\n    return all_mask_rles\n    \n\ndef get_img_list(img_dir, return_ids=False, sub_n=1):\n    \"\"\" Get image list in the format expected by the CellSegmentator tool \"\"\"\n    if sub_n is None:\n        sub_n=len(glob(img_dir + '/' + f'*_red.png'))\n    if return_ids:\n        images = [sorted(glob(img_dir + '/' + f'*_{c}.png'))[:sub_n] for c in [\"red\", \"yellow\", \"blue\"]]\n        return [x.replace(\"_red.png\", \"\").rsplit(\"/\", 1)[1] for x in images[0]], images\n    else:\n        return [sorted(glob(img_dir + '/' + f'*_{c}.png'))[:sub_n] for c in [\"red\", \"yellow\", \"blue\"]]\n    \n    \n    \ndef get_contour_bbox(rle, width, height):\n    \"\"\" Get bbox of contour as `xmin ymin xmax ymax`\n    \n    Args:\n        rle (rle_string): Run length encoding containing \n            segmentation mask information\n        height (int): Height of the original image the map comes from\n        width (int): Width of the original image the map comes from\n    \n    Returns:\n        Numpy array for a cell bounding box coordinates\n    \"\"\"\n    \n    cnts = grab_contours(\n        cv2.findContours(\n            rle_to_mask(rle, height, width).copy(), \n            cv2.RETR_EXTERNAL, \n            cv2.CHAIN_APPROX_SIMPLE\n        ))\n    x,y,w,h = cv2.boundingRect(cnts[0])\n    return x,y,x+w,y+h\n\n\ndef pad_to_square(a):\n    \"\"\" Pad an array `a` evenly until it is a square \"\"\"\n    if a.shape[1]>a.shape[0]: # pad height\n        n_to_add = a.shape[1]-a.shape[0]\n        top_pad = n_to_add//2\n        bottom_pad = n_to_add-top_pad\n        a = np.pad(a, [(top_pad, bottom_pad), (0, 0), (0, 0)], mode='constant')\n\n    elif a.shape[0]>a.shape[1]: # pad width\n        n_to_add = a.shape[0]-a.shape[1]\n        left_pad = n_to_add//2\n        right_pad = n_to_add-left_pad\n        a = np.pad(a, [(0, 0), (left_pad, right_pad), (0, 0)], mode='constant')\n    else:\n        pass\n    return a\n\n\ndef cut_out_cells(rgby, rles, resize_to=(256,256), square_off=True):\n    \"\"\" Cut out the cells as padded square images \n    \n    Args:\n        rgby (np.array): 4 Channel image to be cut into tiles\n        rles (list of RLE strings): List of run length encoding containing \n            segmentation mask information\n        resize_to (tuple of ints, optional): The square dimension to resize the image to\n        square_off (bool, optional): Whether to pad the image to a square or not\n        \n    Returns:\n        list of square arrays representing squared off cell images\n    \"\"\"\n    w,h = rgby.shape[:2]\n    contour_bboxes = [get_contour_bbox(rle, w, h) for rle in rles]\n    arrs = [rgby[bbox[1]:bbox[3], bbox[0]:bbox[2], :] for bbox in contour_bboxes]\n    if square_off:\n        arrs = [pad_to_square(arr) for arr in arrs]\n        \n    if resize_to is not None:\n        arrs = [cv2.resize(pad_to_square(arr), resize_to, interpolation=cv2.INTER_CUBIC) for arr in arrs]\n    \n    return arrs","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<h3 style=\"text-align: font-family: Verdana; font-size: 20px; font-style: normal; font-weight: normal; text-decoration: none; text-transform: uppercase; letter-spacing: 2px; color: navy; background-color: #ffffff;\">6.1 Explore the CellSegmentator Tool</h3>\n\n---\n\nLet's use an example image to show how we will be using the CellSegmentator tool and how we will be post-processing the generated segmentation maps"},{"metadata":{"trusted":true},"cell_type":"code","source":"######################################\n######### METHOD I WOULD USE #########\n######################################\ntesting_ids, testing_images = get_img_list(TRAIN_IMG_DIR, return_ids=True, sub_n=2)\ntesting_masks = create_segmentation_maps(testing_images, 2)\n\ntesting_id_1 = testing_ids[1]\ntesting_img_1 = load_image(testing_id_1, TRAIN_IMG_DIR)\ntesting_mask_1 = testing_masks[testing_id_1]\ncell_boxes = cut_out_cells(testing_img_1, testing_mask_1)\n######################################\n\n########################################\n######### METHOD USING TITO DS #########\n########################################\nnpz_load_path = [x for x in CELL_MASK_NPZ_PATHS if testing_ids[1] in x][0]\na = np.load(load_path)[\"arr_0\"]\nt1 = time.time()\ncell_boxes_2 = cut_out_cells(testing_img_1, [rle_encoding(a, mask_val=k) for k in range(1, np.max(a)+1)])\nprint(time.time()-t1)\n########################################\n\n\nprint(\"\\n... OVERALL IMAGE ...\\n\")\nplot_ex(testing_img_1)\n\nprint(\"\\n... FIRST 5 SINGLE CELL IMAGES SQUARED TO 256x256 ...\\n\")\nfor b, b2 in zip(cell_boxes, cell_boxes_2):\n    plot_rgby(b, title=\"MY METHOD\")\n    plot_rgby(b2, title=\"FROM TITO DS\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<h3 style=\"text-align: font-family: Verdana; font-size: 20px; font-style: normal; font-weight: normal; text-decoration: none; text-transform: uppercase; letter-spacing: 2px; color: navy; background-color: #ffffff;\">6.2 Create Cell Classification Dataset</h3>\n\n---\n\nThe brilliant Kaggle user [tito](https://www.kaggle.com/its7171) created a dataset using this tool. His dataset completes the CellSegmentator portion of the above process for us. \n\nTherefore we will iterate over his dataset and we will use it to create the square-subset dataset"},{"metadata":{"trusted":true},"cell_type":"code","source":"def generate_rle_lists(path):\n    cell_mask = np.load(path)[\"arr_0\"]\n    return [rle_encoding(cell_mask, mask_val=k) for k in range(1, np.max(cell_mask)+1)]\n    \npath_df = train_df[[\"ID\", \"Label\"]].sort_values(by=\"ID\")\npath_df[\"cell_mask_path\"] = CELL_MASK_NPZ_PATHS\npath_df[\"nuclei_mask_path\"] = NUC_MASK_NPZ_PATHS\npath_df = path_df[path_df.Label.str.count(\"\\|\")==0].reset_index(drop=True)\n\nnpz_list = path_df.cell_mask_path.to_list()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"n_proc = 8\nnpz_list_subsets = [npz_list[i::n_proc] for i in range(n_proc)]\n\ndef subprocess_rle_encode(subset):\n    rles = [generate_rle_lists(path) for path in subset]\n    with open(f'./outfile_pid__{str(os.getpid())}.pkl', 'wb') as f:\n        pickle.dump(rles, f, pickle.HIGHEST_PROTOCOL) \n\nprocesses = [multiprocessing.Process(target=subprocess_rle_encode, args=(npz_list_subsets[n],)) for n in range(n_proc)]\nfor p in processes:\n    p.start()\nfor p in processes:\n    p.join()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<h1 style=\"text-align: font-family: Verdana; font-size: 24px; font-style: normal; font-weight: bold; text-decoration: none; text-transform: none; letter-spacing: 3px; color: navy; background-color: #ffffff;\" id=\"swlice\">7&nbsp;&nbsp;SINGLE WEAK-LABEL INDIVIDUAL CELL EXPLORATION</h1>\n\nIn this section we will explore what the different classes look like at a cell-level. The steps are as follows:\n* Find IDs with only one image-level class label\n* Leverage my previously created 256x256 single channel datasets and pull all the cell-wise images for all of those single image-level class label images.\n* Identify patterns, color histograms, shapes, etc. that are consistently found in cells presenting with the protein of interest\n* Use this to show that many cells in the images may be **`Negative`**, even though the image-level class label is something else.\n"},{"metadata":{"trusted":true},"cell_type":"code","source":"def create_input_list(crp, cgp, cbp, cyp, shuffle=True, as_numpy=False):\n    lbl_arr = flatten_list_of_lists([[k,]*len(v) for k, v in sorted(crp.items())])\n    cr_arr = flatten_list_of_lists([v for k,v in sorted(crp.items())])\n    cg_arr = flatten_list_of_lists([v for k,v in sorted(cgp.items())])\n    cb_arr = flatten_list_of_lists([v for k,v in sorted(cbp.items())])\n    cy_arr = flatten_list_of_lists([v for k,v in sorted(cyp.items())])\n    if shuffle:\n        to_shuffle = list(zip(cr_arr, cg_arr, cb_arr, cy_arr, lbl_arr))\n        random.shuffle(to_shuffle)\n        cr_arr, cg_arr, cb_arr, cy_arr, lbl_arr = zip(*to_shuffle)\n    if as_numpy:\n        return np.array(cr_arr), np.array(cg_arr), np.array(cb_arr), np.array(cy_arr), np.array(lbl_arr, np.int32)\n    else:\n        return list(cr_arr), list(cg_arr), list(cb_arr), list(cy_arr), list(lbl_arr)\n\ndef filter_by_class(rp, gp, bp, yp, lbls, cls_filter):\n    cls_indices = np.where(lbls==cls_filter)\n    return rp[cls_indices], gp[cls_indices], bp[cls_indices], yp[cls_indices], lbls[cls_indices]\n\ndef load_from_tile_paths(rp, gp, bp, yp):\n    rgby = [\n        np.asarray(Image.open(cp), np.uint8) \\\n        for cp in [rp, gp, bp, yp]\n    ]\n    return np.stack(rgby, axis=-1)\n\nri, gi, bi, yi, lbls = create_input_list(\n    RED_FILE_MAP, \n    GREEN_FILE_MAP, \n    BLUE_FILE_MAP, \n    YELLOW_FILE_MAP, \n    shuffle=False,\n    as_numpy=True,\n)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**We can see below that just because a cell exists in a single-label image, there is no guarantee that it presents the required green-staining to indicate that the protein is localized in the indicated organelle**"},{"metadata":{"trusted":true},"cell_type":"code","source":"N_EX = 5\nfor k in LBL_NAMES:\n    print(f\"\\n... {N_EX} {k.upper()} CELL TILE EXAMPLES ...\\n\")\n    ri_f, gi_f, bi_f, yi_f, lbls_f = filter_by_class(ri, gi, bi, yi, lbls, cls_filter=STR_2_INT[k])\n    for i in range(N_EX):\n        img = load_from_tile_paths(ri_f[i], gi_f[i], bi_f[i], yi_f[i])\n        plot_ex(img)","execution_count":null,"outputs":[]}],"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}