{"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_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"## Few important points about the problem.\n- This is a weakly supervised multi-label classification problem. (Weakly supervised learning is a machine learning framework where the model is trained using examples that are only partially annotated or labeled. Here, we are only provided with image level labels.)\n\n- Each sample consists of four files. Each file represents a different filter on the subcellular protein patterns represented by the sample. \n  - The format should be [filename]_[filter color].png for the PNG files. Colors are <span style=\"background-color: #FF0000\">red for microtubule channels</span>, <span style=\"background-color: #0000FF\">blue for nuclei channels</span>, <span style=\"background-color: #FFFF00\">yellow for Endoplasmic Reticulum (ER) channels</span>, and <span style=\"background-color: #00FF00\">green for the protein of interest.</span>\n  - The green filter should hence be used to predict the label, and the other filters are used as references.\n- Since this is a multi-label problem, each image is given a set of labels. Following are the index to label mappings used for this problem.\n\n|                     Labels                   |\n|----------------------------------------------|\n| 0. Nucleoplasm                               |\n| 1. Nuclear membrane                          |\n| 2. Nucleoli                                  |\n| 3. Nucleoli fibrillar center                 |\n| 4. Nuclear speckles                          |\n| 5. Nuclear bodies                            |\n| 6. Endoplasmic reticulum                     |\n| 7. Golgi apparatus                           |\n| 8. Intermediate filaments                    |\n| 9. Actin filaments 10. Microtubules          |\n| 11. Mitotic spindle                          |\n| 12. Centrosome                               |\n| 13. Plasma membrane                          |\n| 14. Mitochondria                             |\n| 15. Aggresome                                |\n| 16. Cytosol                                  |\n| 17. Vesicles and punctate cytosolic patterns |\n| 18. Negative                                 |\n","metadata":{}},{"cell_type":"markdown","source":"### Import Libraries","metadata":{"_uuid":"1e27c63e-ef9f-4726-b0df-2bbdda74f37d","_cell_guid":"f9972e9a-011d-43cc-b4d2-ec78331d55a9","trusted":true}},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport cv2\n\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nimport plotly as py\nimport plotly.express as px\npy.offline.init_notebook_mode (connected = True)\n\nimport tqdm\n%matplotlib inline","metadata":{"_uuid":"0df26687-48dd-4e9b-8819-c5fc6e09cb88","_cell_guid":"c823328a-0d03-4d9c-b269-e9b9c94e8aa9","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"path = '../input/hpa-single-cell-image-classification/'","metadata":{"_uuid":"0cc65f01-0b49-4488-b030-11927d6eb898","_cell_guid":"b2f8037c-5bac-440c-b10e-04ba34de810f","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_df = pd.read_csv(path + 'train.csv')","metadata":{"_uuid":"0e2a8f8d-6bb7-4176-ae16-a53981e1e395","_cell_guid":"3c2611f1-5569-410f-869a-c20cc07445b7","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Converting multi labels in one hot encodings","metadata":{"_uuid":"f01fd7dd-b887-4c0c-a0c1-3aab061e6e85","_cell_guid":"1bcaabfa-31f0-4929-bbcf-053d45456f5e","trusted":true}},{"cell_type":"code","source":"label_to_name = {\n    '0': \"Nucleoplasm\",\n    '1': \"Nuclear membrane\",\n    '2': \"Nucleoli\",\n    '3': \"Nucleoli fibrillar center\",\n    '4': \"Nuclear speckles\",\n    '5': \"Nuclear bodies\",\n    '6': \"Endoplasmic reticulum\",\n    '7': \"Golgi apparatus\",\n    '8': \"Intermediate filaments\",\n    '9': \"Actin filaments\",\n    '10': \"Microtubules\",\n    '11': \"Mitotic spindle\",\n    '12': \"Centrosome\",\n    '13': \"Plasma membrane\",\n    '14': \"Mitochondria\",\n    '15': \"Aggresome\",\n    '16': \"Cytosol\",\n    '17': \"Vesicles and punctate cytosolic patterns\",\n    '18': \"Negative\",\n}","metadata":{"_uuid":"51441416-bc23-47de-bdcf-4f68e1e38d01","_cell_guid":"616f9016-7cdd-4a0b-962c-bd38cd62d549","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def view_df(df, idx=0, full=False):\n    '''\n    Helper function to view dataframe in a row or to view just a particular row in the dataframe\n    \n    '''\n    with pd.option_context('display.max_rows', None, 'display.max_columns', None):\n        if full:\n            display(df.head())\n        else:\n            display(pd.DataFrame(df.iloc[idx]).T)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train = train_df.copy()\nlab_idx = label_to_name.keys()\n\ntrain['Label'] = train['Label'].map(lambda x: x.split('|'))\n\nfor label in lab_idx:\n    train[label_to_name[label]] = train['Label'].map(lambda result: 1 if label in result else 0)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"view_df(train, 4)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"view_df(train, full=True)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import plotly.graph_objects as go\nimport plotly.figure_factory as ff\n\ndef barplot(x, y, c, x_title, y_title, title):\n    fig = px.bar(x=x, y=y, opacity=0.90, color=c, \\\n            labels={'x': x_title, 'y': y_title})\n    fig.update_layout(title_text=title, title_x=0.5)\n    fig.show()\n\ndef piechart(x, y, c, title):\n    fig = px.pie(names=x, values=y, color=c)\n    fig.update_layout(title_text=title, title_x=0.5)\n    fig.show()\n    \ndef heatmap(x):\n    z = train.drop(['ID', 'Label'], axis=1).corr()\n    z_text = np.around(z, decimals=2)\n    fig = go.Figure(data = go.Heatmap(x = x, y = x, z = z, zmin=-1, zmax=1, colorscale = 'rainbow')) \n  \n    fig.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Number of Images per Label","metadata":{}},{"cell_type":"code","source":"values = [train[col].value_counts()[1] for col in train.columns[2:]]\nnames = list(label_to_name.values())\n\nbarplot(values, names, names, 'Number of images', 'Name of labels', 'Number of images per label')\npiechart(names, values, names, 'Number of images per label')","metadata":{"_uuid":"90fa079d-695b-434d-8568-152ff7f5b8df","_cell_guid":"367d1517-7d30-4d1f-a07e-5e09aa8e4045","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### Observation from above:\n- Images with Nucleoplasm label are highest in number (8497)\n- Negative labelled images are quite low (34)\n- About 50% of the images consists of 3 labels (Nucleoplasm, Cytosol, Plasma membrane)","metadata":{}},{"cell_type":"markdown","source":"### Number of labels per image","metadata":{}},{"cell_type":"code","source":"from collections import Counter\nnum_labels = Counter([len(n) for n in train['Label']])\nprint(num_labels.keys())\nprint(num_labels.values())\n\nbarplot(num_labels.keys(), num_labels.values(), num_labels.keys(), 'Number of labels', 'Number of images', 'Number of labels per image')\npiechart(num_labels.keys(), num_labels.values(), num_labels.keys(), 'Number of labels per image')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### Observation from above\n- About 10412 images have only one label.\n- About 99% of images have at most 3 labels and 88.7% of images have at most 2 labels.","metadata":{}},{"cell_type":"markdown","source":"### Correlation between labels ","metadata":{}},{"cell_type":"code","source":"values = [train[col].value_counts()[1] for col in train.columns[2:]]\nnames = list(label_to_name.values())\n\nheatmap(names)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### Observations from above\n- Nucleoplasm and Nuclear speckles are higly correlated.\n- Cytosol and mitochondria are also correlated to some extent.","metadata":{}},{"cell_type":"markdown","source":"### Train Image dataset\n","metadata":{}},{"cell_type":"code","source":"def read_image(img_name):\n    \n    green = cv2.imread('../input/hpa-single-cell-image-classification/train/{}_green.png'.format(img_name), cv2.IMREAD_GRAYSCALE )\n    red = cv2.imread('../input/hpa-single-cell-image-classification/train/{}_red.png'.format(img_name), cv2.IMREAD_GRAYSCALE )\n    blue = cv2.imread('../input/hpa-single-cell-image-classification/train/{}_blue.png'.format(img_name), cv2.IMREAD_GRAYSCALE )\n    yellow = cv2.imread('../input/hpa-single-cell-image-classification/train/{}_yellow.png'.format(img_name), cv2.IMREAD_GRAYSCALE )\n    \n    return green, red, blue, yellow\n\n\ndef remove_ticks(ax):\n    ax.set_xticklabels([])\n    ax.set_yticklabels([])\n    ax.tick_params(left=False, bottom=False)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Below images are images related to specific channels","metadata":{}},{"cell_type":"code","source":"from matplotlib.colors import LinearSegmentedColormap\nimg_name = '0060269e-bbbc-11e8-b2ba-ac1f6b6435d0'\nprint(f\"Image name is: {img_name}\")\ngreen, red, blue, yellow = read_image(img_name)\n\n#reset seaborn style\nsns.reset_orig()\n\n# creating custom color map\nc1 = {'red':   ((0.0,  0.0, 0.0),\n                   (1.0,  0.0, 0.0)),\n\n         'green': ((0.0,  0.0, 0.0),\n                   (0.75, 1.0, 1.0),\n                   (1.0,  1.0, 1.0)),\n\n         'blue':  ((0.0,  0.0, 0.0),\n                   (1.0,  0.0, 0.0))}\n\nc2 = {'red':   ((0.0,  0.0, 0.0),\n                   (0.75, 1.0, 1.0),\n                   (1.0,  1.0, 1.0)),\n\n         'green': ((0.0,  0.0, 0.0),\n                   (1.0,  0.0, 0.0)),\n\n         'blue':  ((0.0,  0.0, 0.0),\n                   (1.0,  0.0, 0.0))}\n\nc3 = {'red':   ((0.0,  0.0, 0.0),\n                   (1.0,  0.0, 0.0)),\n\n         'green': ((0.0,  0.0, 0.0),\n                   (1.0,  0.0, 0.0)),\n\n         'blue':  ((0.0,  0.0, 0.0),\n                   (0.75, 1.0, 1.0),\n                   (1.0,  1.0, 1.0))}\n\nc4 = {'red': ((0.0,  0.0, 0.0),\n                   (0.75, 1.0, 1.0),\n                   (1.0,  1.0, 1.0)),\n\n         'green': ((0.0,  0.0, 0.0),\n                   (0.75, 1.0, 1.0),\n                   (1.0,  1.0, 1.0)),\n\n         'blue':  ((0.0,  0.0, 0.0),\n                   (1.0,  0.0, 0.0))}\n\ngreen_map = LinearSegmentedColormap('Green', c1)\nred_map = LinearSegmentedColormap('Red', c2)\nblue_map = LinearSegmentedColormap('Blue', c3)\nyellow_map = LinearSegmentedColormap('Yellow', c4)\n\nf, axarr = plt.subplots(nrows=2, ncols=2, figsize=(14, 14))\naxarr[0,0].imshow(green, cmap=green_map)\naxarr[0,0].set_title('Green: Protein of interest')\nremove_ticks(axarr[0,0])\naxarr[0,1].imshow(red, cmap=red_map)\naxarr[0,1].set_title('Red: Microtubule')\nremove_ticks(axarr[0,1])\naxarr[1,0].imshow(blue, cmap=red_map)\naxarr[1,0].set_title('Blue: Nuclei')\nremove_ticks(axarr[1,0])\naxarr[1,1].imshow(yellow, cmap=yellow_map)\naxarr[1,1].set_title('Yellow: Endoplasmic Reticulum (ER)')\nremove_ticks(axarr[1,1])\nplt.plot()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"The reference for the above is taken from [this](https://www.kaggle.com/jschnab/exploring-the-human-protein-atlas-images) notebook","metadata":{}},{"cell_type":"code","source":"def threshold(img, img_name, cmap='Greys'):\n    ret,thresh1 = cv2.threshold(img,40, 255, cv2.THRESH_BINARY)\n    ret,thresh2 = cv2.threshold(img,40, 255, cv2.THRESH_TRUNC)\n    thresh3 = cv2.adaptiveThreshold(img,255,cv2.ADAPTIVE_THRESH_MEAN_C,\\\n            cv2.THRESH_BINARY,21,4)\n    thresh4 = cv2.adaptiveThreshold(img,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,\\\n            cv2.THRESH_BINARY,21,4)\n    \n    \n    f, ax = plt.subplots(nrows=1, ncols=4, figsize=(23, 23)) \n    ax[0].imshow(thresh1, cmap=cmap)\n    ax[0].set_title(f'Binary Threshold: {img_name}')\n    remove_ticks(ax[0])\n    ax[1].imshow(thresh2, cmap=cmap)\n    ax[1].set_title(f'Trunc Threshold: {img_name}')\n    remove_ticks(ax[1])\n    ax[2].imshow(thresh3, cmap=cmap)\n    ax[2].set_title(f'Adaptive Mean Thresholding: {img_name}')\n    remove_ticks(ax[2])\n    ax[3].imshow(thresh4, cmap=cmap)\n    ax[3].set_title(f'Adaptive Gaussian Thresholding: {img_name}')\n    remove_ticks(ax[3])\n    plt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### Thresholded images across all the 4 channels","metadata":{}},{"cell_type":"code","source":"print(\"Thresholding with channels\")\nthreshold(green, 'Protein', green_map)\nthreshold(red, 'Microtubules', red_map)\nthreshold(blue, 'Nuclei', blue_map)\nthreshold(yellow, 'Endoplasmic Reticulum', yellow_map)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### Thresholding images across all 4 channels with Grey color map","metadata":{}},{"cell_type":"code","source":"print(\"Thresholding with B/W\")\nthreshold(green, 'Protein')\nthreshold(red, 'Microtubules')\nthreshold(blue, 'Nuclei')\nthreshold(yellow, 'Endoplasmic Reticulum')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Otsu’s Binarization\n\n- In the above thresholding method, a specific threshold was choosen arbitrary. Otsu's binarization automatically calculates a threshold value from image histogram for a bimodal image. \n","metadata":{}},{"cell_type":"code","source":"img_name = '0060269e-bbbc-11e8-b2ba-ac1f6b6435d0'\nprint(f\"Image name is: {img_name}\")\ngreen, red, blue, yellow = read_image(img_name)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# taking the picture for endoplasmic reticulum\nimg = yellow\n\n# global thresholding\nret1,th1 = cv2.threshold(img,127,255,cv2.THRESH_BINARY)\n\n# Otsu's thresholding\nret2,th2 = cv2.threshold(img,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)\n\n# Otsu's thresholding after Gaussian filtering\nblur = cv2.GaussianBlur(img,(5,5),0)\nret3,th3 = cv2.threshold(blur,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)\n\n# plot all the images and their histograms\nimages = [img, 0, th1,\n          img, 0, th2,\n          blur, 0, th3]\n\ntitles = ['Original Noisy Image','Histogram','Global Thresholding (v=127)',\n          'Original Noisy Image','Histogram',\"Otsu's Thresholding\",\n          'Gaussian filtered Image','Histogram',\"Otsu's Thresholding\"]\n\nf, ax = plt.subplots(nrows=3, ncols=3, figsize=(20, 20))\n\nfor i in range(3):\n    ax[i, 0].imshow(images[i*3], cmap='Greys')\n    ax[i, 0].set_title(titles[i*3])\n    remove_ticks(ax[i, 0])\n    ax[i, 1].hist(images[i*3].ravel(), 256)\n    ax[i, 1].set_title(titles[i*3+1])\n    remove_ticks(ax[i, 1])\n    ax[i, 2].imshow(images[i*3+2], cmap='Greys')\n    ax[i, 2].set_title(titles[i*3+2])\n    remove_ticks(ax[i, 2])\nplt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"To study more about Otsu's Binarization, go to [this link](https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_imgproc/py_thresholding/py_thresholding.html#otsus-binarization). ","metadata":{}},{"cell_type":"markdown","source":"### Vizualize RGB images for each class of the label","metadata":{}},{"cell_type":"code","source":"for i, label in enumerate(train.columns[2:]): \n    f, ax = plt.subplots(nrows=1, ncols=1, figsize=(5, 5))\n    img_name = train[train[label]==1].iloc[0]['ID']\n    green, red, blue, yellow = read_image(img_name)\n    img = np.dstack((green,red, blue))\n    \n    ax.imshow(img)\n    ax.set_title(label+\" : Image name> \"+img_name)\n    remove_ticks(ax)\n    plt.show()","metadata":{"_kg_hide-input":false,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### U-Net (from scratch)\n\n- The u-net is convolutional network architecture for fast and precise segmentation of images. \n- U-net was used for segmenting biomedical images in the original paper.","metadata":{}},{"cell_type":"markdown","source":"Following are the steps that can be followed here:\n* conv 3x3 applied 2 times, followed by RelU.\n* do a max pooling with filter 2x2 stride of 2 for downsampling\n* Repeat it for 5 steps\n* Then perform up conv\n","metadata":{}},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nim = '../input/hpa-single-cell-image-classification/train/000a6c98-bb9b-11e8-b2b9-ac1f6b6435d0_red.png'\n","metadata":{"_uuid":"d2fe9bfc-bc2f-4cf8-b935-b7080cceeaa0","_cell_guid":"01d739f9-e929-44b6-a696-abd23445c5e4","_kg_hide-input":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def double_conv(in_channel, out_channel):\n    conv = nn.Sequential(\n        nn.Conv2d(in_channel, out_channel, kernel_size=3),\n        nn.ReLU(inplace=True),\n        nn.Conv2d(out_channel, out_channel, kernel_size=3),\n        nn.ReLU(inplace=True),\n    )\n    \n    return conv\n    \n    \ndef crop_tensor(original, target):\n    target_size = target.size()[2]\n    original_size = original.size()[2]\n    delta = original_size - target_size\n    \n    delta = delta // 2\n    return original[:, :, delta: original_size-delta, delta: original_size-delta]\n\n\n\nclass UNet(nn.Module):\n    def __init__(self):\n        super(UNet, self).__init__()\n        \n        self.max_pool_2x2 = nn.MaxPool2d(\n            kernel_size=2, \n            stride=2\n        )\n        self.down_conv_1 = double_conv(1, 64)\n        self.down_conv_2 = double_conv(64, 128)\n        self.down_conv_3 = double_conv(128, 256)\n        self.down_conv_4 = double_conv(256, 512)\n        self.down_conv_5 = double_conv(512, 1024)\n        \n        \n        self.conv_trans_1 = nn.ConvTranspose2d(\n            1024, \n            512, \n            kernel_size=2, \n            stride=2\n        )\n        \n        self.up_conv_1 = double_conv(1024, 512)\n        \n        self.conv_trans_2 = nn.ConvTranspose2d(\n            512, \n            256, \n            kernel_size=2, \n            stride=2\n        )\n        self.up_conv_2 = double_conv(512, 256)\n        \n        self.conv_trans_3 = nn.ConvTranspose2d(\n            256, \n            128, \n            kernel_size=2, \n            stride=2\n        )\n        self.up_conv_3 = double_conv(256, 128)\n        \n        self.conv_trans_4 = nn.ConvTranspose2d(\n            128, \n            64, \n            kernel_size=2, \n            stride=2\n        )\n        self.up_conv_4 = double_conv(128, 64)\n        \n        self.out = nn.Conv2d(\n            64, \n            2, \n            kernel_size=1\n        )\n        \n    \n    def forward(self, image):\n        # encoder\n        \n        x1 = self.down_conv_1(image)\n        m1 = self.max_pool_2x2(x1)\n        \n        x2 = self.down_conv_2(m1)\n        m2 = self.max_pool_2x2(x2)\n        \n        x3 = self.down_conv_3(m2)\n        m3 = self.max_pool_2x2(x3)\n        \n        x4 = self.down_conv_4(m3)\n        m4 = self.max_pool_2x2(x4)\n        \n        x5 = self.down_conv_5(m4)        \n        \n        # decoder\n        # all the x's are passed in the decoder part too\n        \n        # x4 is 64x64|x is 56x56| we need to crop x4 to same size as x\n        x = self.conv_trans_1(x5)\n        x4 = crop_tensor(x4, x)\n\n        x = torch.cat([x, x4], dim=1)\n        x = self.up_conv_1(x)\n        \n        x = self.conv_trans_2(x)\n        x3 = crop_tensor(x3, x)\n        \n        x = torch.cat([x, x3], dim=1)\n        x = self.up_conv_2(x)\n        \n        x = self.conv_trans_3(x)\n        x2 = crop_tensor(x2, x)\n\n        x = torch.cat([x, x2], dim=1)\n        x = self.up_conv_3(x)\n        \n        x = self.conv_trans_4(x)\n        x1 = crop_tensor(x1, x)\n\n        x = torch.cat([x, x1], dim=1)\n        x = self.up_conv_4(x)\n        \n        out = self.out(x)\n        \n        return out","metadata":{"_uuid":"c0e7aaa7-2a25-4875-bdaa-281888271719","_cell_guid":"668ddce3-d276-4820-9458-cc95578321e8","_kg_hide-input":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from torchvision import transforms\n\nwidth = 572\nheight = 572\n\nimage = cv2.imread(im, 0)\nimage = cv2.resize(image, (width, height))\nplt.imshow(image, cmap='magma')\ntran = transforms.ToTensor()\n\nimg_tensor = tran(image).unsqueeze(0)\nprint(img_tensor.shape)\n\nmodel = UNet()\nmodel(img_tensor)","metadata":{"_uuid":"ed31c238-f697-496f-9a9e-99d735564762","_cell_guid":"5aab2449-9476-4783-8f06-28d9ccd84399","_kg_hide-input":true,"_kg_hide-output":false,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"[Cell Segmenation](https://github.com/CellProfiling/HPA-Cell-Segmentation) can be used to extract segment masks for each cell.","metadata":{"_uuid":"3ca9c322-aa50-43c2-a952-bb346f5d111f","_cell_guid":"79a19562-7de0-47cd-8c67-8821a78849e0","trusted":true}},{"cell_type":"markdown","source":"To be continued .   .  . .. ","metadata":{}},{"cell_type":"code","source":"","metadata":{"trusted":true},"execution_count":null,"outputs":[]}]}