{"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":"# CLAM\n\nNOTE: Some of the descriptions or images are cited from: https://github.com/mahmoodlab/CLAM\n\n![img](https://github.com/mahmoodlab/CLAM/raw/master/docs/CLAM2.jpg)\n\n## TL;DR:\n\n+ CLAM is a high-throughput and interpretable method for data efficient whole slide image (WSI) classification using slide-level labels without any ROI extraction or patch-level annotations, and is capable of handling multi-class subtyping problems. Tested on three different WSI datasets, trained models adapt to independent test cohorts of WSI resections and biopsies as well as smartphone microscopy images (photomicrographs).\n+ paper: https://arxiv.org/abs/2004.09666\n\n## How to apply CLAM on the STRIP AI dataset ?\n\n+ I prepared four notebooks for pre-process, train and inference:\n\n### pre-process\n\n+ (1) image generation: https://www.kaggle.com/code/fx6300/clam-strip-ai-image-generation\n+ (2) feature extraction: https://www.kaggle.com/code/fx6300/clam-strip-ai-feature-extraction\n\n### train\n\n+ (3) train: https://www.kaggle.com/code/fx6300/clam-strip-ai-train\n\n### inference\n\n+ <b>&gt; THIS NOTEBOOK &lt;</b> (4) inference: https://www.kaggle.com/code/fx6300/clam-strip-ai-inference\n\n## How to visualize the attention generated by CLAM ?\n\n+ I prepared an example:\n  + https://www.kaggle.com/fx6300/clam-strip-ai-attention-heatmap\n\n## NOTE\n\n+ The source code from CLAM (https://github.com/mahmoodlab/CLAM) is licensed under GPLv3 and available for non-commercial academic purposes.","metadata":{}},{"cell_type":"code","source":"!conda install ../input/how-to-use-pyvips-offline/*.tar.bz2","metadata":{"execution":{"iopub.status.busy":"2022-09-26T16:09:37.168268Z","iopub.execute_input":"2022-09-26T16:09:37.169307Z","iopub.status.idle":"2022-09-26T16:10:15.657905Z","shell.execute_reply.started":"2022-09-26T16:09:37.169194Z","shell.execute_reply":"2022-09-26T16:10:15.656767Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!yes | pip uninstall opencv-python\n!pip install ../input/opencv-contrib/opencv_contrib_python-4.6.0.66-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl\n!pip install ../input/tiling030/tiling-0.3.0-py2.py3-none-any.whl\n\n!pip install --no-deps ../input/my-rembg/asyncer-*.whl\n!pip install --no-deps ../input/my-rembg/filetype-*.whl\n!pip install --no-deps ../input/my-rembg/onnxruntime-*.whl\n!pip install --no-deps ../input/my-rembg/PyMatting-*.whl\n!pip install --no-deps ../input/my-rembg/watchdog-*.whl\n!pip install --no-deps ../input/my-rembg/click-*.whl\n!pip install --no-deps ../input/my-rembg/fastapi-*.whl\n!pip install --no-deps ../input/my-rembg/opencv_python_headless-*.whl\n!pip install --no-deps ../input/my-rembg/uvicorn-*.whl\n\n#!pip install --no-deps ../input/my-rembg/Pillow-*.whl #skip\n#!pip install --no-deps ../input/my-rembg/scikit_image-*.whl #skip\n\n!cp -rp /kaggle/input/my-rembg/gdown-4.5.1/gdown-4.5.1/ /kaggle/working\n!cd /kaggle/working/gdown-4.5.1 && python3 setup.py install\n#!python3 -m pip install -e file:///kaggle/working/gdown-4.5.1 --no-index -f file:///kaggle/input/my-rembg/ --prefix /opt/conda/lib/python3.7/site-packages\n!rm -r /kaggle/working/gdown-4.5.1\n\n!cp -rp /kaggle/input/my-rembg/python-multipart-0.0.5/python-multipart-0.0.5/ /kaggle/working\n!cd /kaggle/working/python-multipart-0.0.5 && python3 setup.py install\n#!python3 -m pip install -e file:///kaggle/working/python-multipart-0.0.5 --no-index -f file:///kaggle/input/my-rembg/ --prefix /opt/conda/lib/python3.7/site-packages\n!rm -r /kaggle/working/python-multipart-0.0.5\n\n!pip install --no-deps ../input/my-rembg/rembg-2.0.25-py3-none-any.whl","metadata":{"execution":{"iopub.status.busy":"2022-09-26T16:10:15.661061Z","iopub.execute_input":"2022-09-26T16:10:15.661453Z","iopub.status.idle":"2022-09-26T16:15:16.232801Z","shell.execute_reply.started":"2022-09-26T16:10:15.661418Z","shell.execute_reply":"2022-09-26T16:15:16.231606Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!pip show gdown\n!pip show python-multipart","metadata":{"execution":{"iopub.status.busy":"2022-09-26T16:15:16.235332Z","iopub.execute_input":"2022-09-26T16:15:16.236046Z","iopub.status.idle":"2022-09-26T16:15:33.712649Z","shell.execute_reply.started":"2022-09-26T16:15:16.236005Z","shell.execute_reply":"2022-09-26T16:15:33.71147Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!test -d ~/.u2net || mkdir ~/.u2net\n!cp -f ../input/u2net-onnx/u2net.onnx ~/.u2net/","metadata":{"execution":{"iopub.status.busy":"2022-09-26T16:15:33.71735Z","iopub.execute_input":"2022-09-26T16:15:33.71765Z","iopub.status.idle":"2022-09-26T16:15:39.980295Z","shell.execute_reply.started":"2022-09-26T16:15:33.717621Z","shell.execute_reply":"2022-09-26T16:15:39.979012Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import sys\nsys.path.append(\"/opt/conda/lib/python3.7/site-packages/gdown-4.5.1-py3.7.egg\")\nsys.path.append(\"/opt/conda/lib/python3.7/site-packages/python_multipart-0.0.5-py3.7.egg\")\nimport os\nimport gc\nimport cv2\nimport copy\nimport time\nimport random\nimport string\nimport joblib\nimport tifffile\nimport numpy as np \nimport pandas as pd \nimport torch\nfrom torch import nn\nimport seaborn as sns\nfrom torchvision import models\nimport matplotlib.pyplot as plt\nfrom torch.utils.data import Dataset, DataLoader\nfrom sklearn.model_selection import train_test_split\nfrom tqdm.notebook import tqdm\nfrom torch.optim import lr_scheduler\nimport warnings\nimport openslide\nfrom openslide.deepzoom import DeepZoomGenerator\nimport tempfile\nfrom PIL import Image\nimport pyvips\nfrom scipy.special import expit\nfrom tiling import ConstStrideTiles\nimport h5py\nfrom rembg import remove\nwarnings.filterwarnings(\"ignore\")\ngc.enable()","metadata":{"_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","papermill":{"duration":3.063995,"end_time":"2022-07-08T14:24:41.045696","exception":false,"start_time":"2022-07-08T14:24:37.981701","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-09-26T16:15:39.982222Z","iopub.execute_input":"2022-09-26T16:15:39.982609Z","iopub.status.idle":"2022-09-26T16:16:06.728506Z","shell.execute_reply.started":"2022-09-26T16:15:39.982577Z","shell.execute_reply":"2022-09-26T16:16:06.727525Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"debug = False\ngenerate_new = True\ntest_df = pd.read_csv(\"../input/mayo-clinic-strip-ai/test.csv\")\ndirs = [\"../input/mayo-clinic-strip-ai/train/\", \"../input/mayo-clinic-strip-ai/test/\"]\ntest_df","metadata":{"papermill":{"duration":0.02504,"end_time":"2022-07-08T14:24:41.073811","exception":false,"start_time":"2022-07-08T14:24:41.048771","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-09-26T16:16:06.729802Z","iopub.execute_input":"2022-09-26T16:16:06.730514Z","iopub.status.idle":"2022-09-26T16:16:06.762248Z","shell.execute_reply.started":"2022-09-26T16:16:06.730464Z","shell.execute_reply":"2022-09-26T16:16:06.761339Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def tiling(img):\n    h, w = img.height, img.width\n    tiles = ConstStrideTiles(image_size=(h, w), tile_size=(512, 512), stride=(512, 512), \n                             origin=(0, 0),\n                             scale=1.0,\n                             include_nodata=True)\n    print(\"Number of tiles: %i\" % len(tiles))\n    imgs = []\n    for extent, out_size in tiles:\n        x, y, width, height = extent\n        data = img.crop(x, y, width, height)\n        imgs.append(data)\n    return imgs","metadata":{"execution":{"iopub.status.busy":"2022-09-26T16:16:06.763697Z","iopub.execute_input":"2022-09-26T16:16:06.764039Z","iopub.status.idle":"2022-09-26T16:16:06.771698Z","shell.execute_reply.started":"2022-09-26T16:16:06.764005Z","shell.execute_reply":"2022-09-26T16:16:06.769846Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def resize_image(img: Image, width) -> Image:\n    return pyvips2pil(img.thumbnail_image(width))","metadata":{"execution":{"iopub.status.busy":"2022-09-26T16:16:06.773392Z","iopub.execute_input":"2022-09-26T16:16:06.773661Z","iopub.status.idle":"2022-09-26T16:16:06.781206Z","shell.execute_reply.started":"2022-09-26T16:16:06.773636Z","shell.execute_reply":"2022-09-26T16:16:06.780088Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def resize_image_with_canvas(img: Image, width) -> Image:\n    canvas = Image.new(\"RGB\", (width, width), \"white\")\n    img2 = pyvips2pil(img.thumbnail_image(width))\n    canvas.paste(img2, (0, 0))\n    return canvas","metadata":{"execution":{"iopub.status.busy":"2022-09-26T16:16:06.782894Z","iopub.execute_input":"2022-09-26T16:16:06.783294Z","iopub.status.idle":"2022-09-26T16:16:06.791348Z","shell.execute_reply.started":"2022-09-26T16:16:06.78326Z","shell.execute_reply":"2022-09-26T16:16:06.790459Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def bg_trim(_img: Image) -> Image:\n    img = np.array(_img)\n    yidx = []\n    xidx = []\n    for y in range(img.shape[0]):\n        if sum(sum(255 - img[y,:])) < 10:\n            yidx += [y]\n    for x in range(img.shape[1]):\n        if sum(sum(255 - img[:,x])) < 10:\n            xidx += [x]\n    img = np.delete(img, xidx, axis=1)\n    img = np.delete(img, yidx, axis=0)\n    return Image.fromarray(img)","metadata":{"execution":{"iopub.status.busy":"2022-09-26T16:16:06.795684Z","iopub.execute_input":"2022-09-26T16:16:06.795952Z","iopub.status.idle":"2022-09-26T16:16:06.804556Z","shell.execute_reply.started":"2022-09-26T16:16:06.795915Z","shell.execute_reply":"2022-09-26T16:16:06.802462Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def pyvips2pil(img: pyvips.Image) -> Image:\n    # RGB -> BGR\n    imgBGR = cv2.cvtColor(img.numpy(), cv2.COLOR_RGB2BGR)\n    return Image.fromarray(imgBGR)","metadata":{"execution":{"iopub.status.busy":"2022-09-26T16:16:06.805843Z","iopub.execute_input":"2022-09-26T16:16:06.80612Z","iopub.status.idle":"2022-09-26T16:16:06.81658Z","shell.execute_reply.started":"2022-09-26T16:16:06.806078Z","shell.execute_reply":"2022-09-26T16:16:06.815672Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def pil2pyvips(img: Image) -> pyvips.Image:\n    # BGR -> RGB\n    imgRGB = cv2.cvtColor(np.array(img), cv2.COLOR_BGR2RGB)\n    return pyvips.Image.new_from_array(imgRGB)","metadata":{"execution":{"iopub.status.busy":"2022-09-26T16:16:06.817807Z","iopub.execute_input":"2022-09-26T16:16:06.81868Z","iopub.status.idle":"2022-09-26T16:16:06.826564Z","shell.execute_reply.started":"2022-09-26T16:16:06.818646Z","shell.execute_reply":"2022-09-26T16:16:06.825806Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def my_remove(img: Image) -> Image:\n    img = remove(img) # PIL -> PIL\n    B, G, R, A = img.split()\n    alpha = np.array(A) / 255\n    R = (255 * (1 - alpha) + np.array(R) * alpha).astype(np.uint8)\n    G = (255 * (1 - alpha) + np.array(G) * alpha).astype(np.uint8)\n    B = (255 * (1 - alpha) + np.array(B) * alpha).astype(np.uint8)\n    return cv2.merge((B, G, R))","metadata":{"execution":{"iopub.status.busy":"2022-09-26T16:16:06.827831Z","iopub.execute_input":"2022-09-26T16:16:06.828415Z","iopub.status.idle":"2022-09-26T16:16:06.837376Z","shell.execute_reply.started":"2022-09-26T16:16:06.828381Z","shell.execute_reply":"2022-09-26T16:16:06.83652Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class ImgDataset(Dataset):\n    def __init__(self, df):\n        self.df = df \n        self.train = 'label' in df.columns\n    def __len__(self):\n        return len(self.df) * 64\n    \n    def __getitem__(self, _index):\n        paths = [\"./test/4096-tiles-v4/\", \"./train/4096-tiles-v4/\"]\n        index = _index // 64\n        pos = _index % 64\n        image_id = self.df.iloc[index].image_id\n        image = cv2.imread(paths[self.train] + image_id + f\"_{pos}\" + \".jpg\").transpose(2, 0, 1)\n        return image, image_id, pos","metadata":{"execution":{"iopub.status.busy":"2022-09-26T16:16:06.838786Z","iopub.execute_input":"2022-09-26T16:16:06.839453Z","iopub.status.idle":"2022-09-26T16:16:06.847246Z","shell.execute_reply.started":"2022-09-26T16:16:06.839416Z","shell.execute_reply":"2022-09-26T16:16:06.846408Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class FeatureDataset(Dataset):\n    def __init__(self, df, data_dir, **kwargs):\n        self.df = df\n        self.data_dir = data_dir\n    def __len__(self):\n        return len(self.df)\n        \n    def __getitem__(self, index):\n        image_id = self.df.iloc[index].image_id\n        patient_id = self.df.iloc[index].patient_id\n        full_path = f\"{self.data_dir}/{self.df.iloc[index].image_id}.h5\"\n        with h5py.File(full_path,'r') as hdf5_file:\n            features = torch.stack([torch.tensor(hdf5_file[str(i)]) for i in range(64)]).view(64, 1024)\n            coords = torch.tensor([i for i in range(64)]).view(64)\n        return features, coords, patient_id","metadata":{"execution":{"iopub.status.busy":"2022-09-26T16:16:06.849117Z","iopub.execute_input":"2022-09-26T16:16:06.850564Z","iopub.status.idle":"2022-09-26T16:16:06.859021Z","shell.execute_reply.started":"2022-09-26T16:16:06.850527Z","shell.execute_reply":"2022-09-26T16:16:06.858099Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def apply_model(model, test_loader, output_dir):\n    s = nn.Softmax(dim=1)\n    model.cuda()\n    model.eval()\n    for item in tqdm(test_loader, leave=False):\n        images = item[0].cuda().float()\n        image_ids = item[1]\n        image_poses = item[2]\n        with torch.no_grad():\n            output = model(images)\n        for i in range(output.shape[0]):\n            f = h5py.File(f\"{output_dir}/{image_ids[i]}.h5\", 'a')\n            f.create_dataset(f\"{image_poses[i]}\", data=output[i].cpu(), dtype=np.float32)\n            f.close()\n        del images, image_ids, image_poses\n        gc.collect()\n        torch.cuda.empty_cache()","metadata":{"execution":{"iopub.status.busy":"2022-09-26T16:16:06.860313Z","iopub.execute_input":"2022-09-26T16:16:06.861223Z","iopub.status.idle":"2022-09-26T16:16:06.871728Z","shell.execute_reply.started":"2022-09-26T16:16:06.861188Z","shell.execute_reply":"2022-09-26T16:16:06.871057Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def predict(model, dataloader):\n    model.cuda()\n    model.eval()\n    outputs = []\n    attentions = []\n    s = nn.Softmax(dim=1)\n    ids = []\n    for item in tqdm(dataloader, leave=False):\n        patient_id = item[2][0]\n        ids.append(patient_id)\n        try:\n            images = item[0][0].cuda().float()  \n            _, output, _, _, attention = model(images)\n            outputs.append(s(output.cpu()[:,:2])[0].detach().numpy())\n            attentions.append(attention[0].view(8, 8).cpu().detach().numpy())\n            del output, images\n        except Exception as e:\n            print(e)\n            outputs.append(s(torch.tensor([[1, 1]]).float())[0].detach().numpy())\n            attentions.append(torch.ones(8, 8).detach().cpu().numpy())\n        gc.collect()\n        torch.cuda.empty_cache()\n    return np.array(outputs), ids, attentions","metadata":{"papermill":{"duration":null,"end_time":null,"exception":null,"start_time":null,"status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-09-26T16:16:06.87285Z","iopub.execute_input":"2022-09-26T16:16:06.873794Z","iopub.status.idle":"2022-09-26T16:16:06.883581Z","shell.execute_reply.started":"2022-09-26T16:16:06.873759Z","shell.execute_reply":"2022-09-26T16:16:06.882894Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# image generation\nif not os.path.exists(\"./test/\"):\n    os.mkdir(\"./test/\")\nif not os.path.exists(\"./test/4096-tiles-v4\"):\n    os.mkdir(\"./test/4096-tiles-v4\")\nfor i in tqdm(range(test_df.shape[0])):\n    img_id = test_df.iloc[i].image_id\n    img = pyvips.Image.new_from_file(dirs[1] + img_id + \".tif\", access='sequential')\n    img = resize_image(img, 4096) # pyvips -> PIL\n    img = my_remove(img) # PIL -> PIL\n    img = bg_trim(img) # PIL -> PIL\n    img = resize_image_with_canvas(pil2pyvips(img), 4096) # PIL -> pyvips -> PIL\n    cv2.imwrite(f\"./test/4096-tiles-v4/{img_id}.jpg\", np.array(img))\n    imgs = tiling(pyvips.Image.new_from_array(img)) # retain BGR order\n    for i, _img in enumerate(imgs):\n        cv2.imwrite(f\"./test/4096-tiles-v4/{img_id}_{i}.jpg\", _img.numpy()) # BGR\n    del imgs\n    gc.collect()","metadata":{"execution":{"iopub.status.busy":"2022-09-26T16:16:06.884655Z","iopub.execute_input":"2022-09-26T16:16:06.885601Z","iopub.status.idle":"2022-09-26T16:20:08.19372Z","shell.execute_reply.started":"2022-09-26T16:16:06.885567Z","shell.execute_reply":"2022-09-26T16:20:08.189326Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# feature extraction\nfor test_idx in test_df.index:\n    resnet50_baseline = torch.jit.load(\"../input/resnet50-baselinepth/resnet50_baseline.pth\")\n    test = test_df.iloc[[test_idx]]\n    batch_size = 32\n    test_loader = DataLoader(\n        ImgDataset(test), \n        batch_size=batch_size, \n        shuffle=False, \n        num_workers=1\n    )\n    output_dir = \"./my_features\"\n    if not os.path.exists(output_dir):\n        os.mkdir(output_dir)\n    apply_model(resnet50_baseline, test_loader, output_dir)\n    del resnet50_baseline, test, test_loader\n    gc.collect()\n    torch.cuda.empty_cache()","metadata":{"execution":{"iopub.status.busy":"2022-09-26T16:20:08.195052Z","iopub.execute_input":"2022-09-26T16:20:08.198153Z","iopub.status.idle":"2022-09-26T16:20:25.639952Z","shell.execute_reply.started":"2022-09-26T16:20:08.198097Z","shell.execute_reply":"2022-09-26T16:20:25.638898Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model_paths = [\n    '../input/clambaseline/model_fold0.pth',\n]\n\nprob = pd.DataFrame()\ndf_list = []\nfor model_id, model_path in enumerate(model_paths):\n    torch.cuda.empty_cache()\n    model = torch.jit.load(model_path)\n    torch.cuda.empty_cache()\n    \n    batch_size = 1\n    test_loader = DataLoader(\n        FeatureDataset(test_df, \"./my_features\"), \n        batch_size=batch_size, \n        shuffle=False, \n        num_workers=1\n    )\n    anss, ids, attentions = predict(model, test_loader)\n    del model, test_loader\n    gc.collect()\n    torch.cuda.empty_cache()\n    mydf = pd.DataFrame({\"CE\" : anss[:,0], \"LAA\" : anss[:,1], \"id\" : ids}).groupby(\"id\").mean()\n    df_list += [mydf]","metadata":{"papermill":{"duration":null,"end_time":null,"exception":null,"start_time":null,"status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-09-26T16:20:25.64225Z","iopub.execute_input":"2022-09-26T16:20:25.643225Z","iopub.status.idle":"2022-09-26T16:20:58.245532Z","shell.execute_reply.started":"2022-09-26T16:20:25.643181Z","shell.execute_reply":"2022-09-26T16:20:58.244461Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"laa_probs = 1.0 - sum([1.0/len(df_list) * df_list[i].CE for i in range(len(df_list))])\nsubmission = pd.DataFrame({\"patient_id\": df_list[0].index, \"CE\": 1.0 - np.asarray(laa_probs.to_list()), \"LAA\": laa_probs.to_list()})\nsubmission","metadata":{"execution":{"iopub.status.busy":"2022-09-26T16:20:58.246921Z","iopub.execute_input":"2022-09-26T16:20:58.247968Z","iopub.status.idle":"2022-09-26T16:20:58.261957Z","shell.execute_reply.started":"2022-09-26T16:20:58.247924Z","shell.execute_reply":"2022-09-26T16:20:58.26099Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission.to_csv(\"submission.csv\", index = False)","metadata":{"execution":{"iopub.status.busy":"2022-09-26T16:20:58.263548Z","iopub.execute_input":"2022-09-26T16:20:58.264265Z","iopub.status.idle":"2022-09-26T16:20:58.274426Z","shell.execute_reply.started":"2022-09-26T16:20:58.264218Z","shell.execute_reply":"2022-09-26T16:20:58.273048Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}