{"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":"# 利用 3D语义分割 模型处理训练图片（训练图片是dcm格式）\n\n大神“Qishen Hai”的主页地址(https://www.kaggle.com/haqishen)\n大神没有提供语义分割模型（stage1得到的模型）处理训练数据的代码，直接提供了处理得到的结果，如链接所示(https://www.kaggle.com/datasets/haqishen/rsna-cropped-2d-224-0920-2m)，利用该数据训练下一阶段的分类模型（stage2）\n\n**第一步，加载3D语义分割模型**\n\n* stage1的3D语义分割模型: https://www.kaggle.com/code/haqishen/rsna-2022-1st-place-solution-train-stage1\n* stage1输出的模型，在这儿https://www.kaggle.com/code/haqishen/rsna-2022-1st-place-solution-train-stage2-type2\n* 相对比较常规的方式加载模型，5折交叉验证，5个模型\n\n**进行语义分割，得到MASK**\n* 输入：dcm是128✖128，128张dcm组成128✖128✖128的3D输入图片\n* 输出：7✖(128✖128✖128)，7对应C1-C7类别\n\n**数据后处理，并保存为npy**\n* 对于每个类别，如C1，从输出的MASK(128✖128✖128)中crop某个通道的某一部分（xx,yy,1）,从该MASK对应的dcm中选择5张，将上述两部分resize到224✖244，再在通道维度进行合并，得到（224✖224✖6）的矩阵。相应的C2也会得到一个（224✖224✖6）矩阵，...，C7也会得到一个（224✖224✖6）矩阵.\n* 最终将这些（224✖224✖6）的这些矩阵作为下一阶段的网络的输入\n\n**具体执行逻辑，通过执行代码进行理解**\n**该代码主要是通过大神的inference推理notebook修改未来，对于同一个UID得到的（224✖224✖6）和(https://www.kaggle.com/datasets/haqishen/rsna-cropped-2d-224-0920-2m里面的npy不一致，不知是否正常（后续再继续研究）**","metadata":{"execution":{"iopub.status.busy":"2023-11-02T06:21:25.243867Z","iopub.execute_input":"2023-11-02T06:21:25.244905Z","iopub.status.idle":"2023-11-02T06:21:52.671065Z","shell.execute_reply.started":"2023-11-02T06:21:25.244864Z","shell.execute_reply":"2023-11-02T06:21:52.670009Z"}}},{"cell_type":"code","source":"import os\nimport sys\nsys.path = [\n    '../input/covn3d-same',\n    '../input/timm20221011/pytorch-image-models-master',\n    '../input/smp20210127/segmentation_models.pytorch-master/segmentation_models.pytorch-master',\n    '../input/smp20210127/pretrained-models.pytorch-master/pretrained-models.pytorch-master',\n    '../input/smp20210127/EfficientNet-PyTorch-master/EfficientNet-PyTorch-master',\n] + sys.path\n\n!pip install pylibjpeg\n!pip install python_gdcm\n!cp -r ../input/timm-20220211/pytorch-image-models-master/timm ./timm4smp","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import gc\nimport ast\nimport cv2\nimport time\nimport timm\nimport timm4smp\nimport pickle\nimport random\nimport pydicom\nimport argparse\nimport warnings\nimport threading\nimport numpy as np\nimport pandas as pd\nfrom PIL import Image\nfrom tqdm import tqdm\nfrom glob import glob\nimport albumentations\nimport matplotlib.pyplot as plt\nimport segmentation_models_pytorch as smp\n\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nimport torch.cuda.amp as amp\nimport torch.nn.functional as F\nfrom torch.utils.data import DataLoader, Dataset\nfrom pylab import rcParams\n\n%matplotlib inline\ndevice = torch.device('cuda')\ntorch.backends.cudnn.benchmark = True\n\ntimm.__version__, timm4smp.__version__","metadata":{"execution":{"iopub.status.busy":"2023-11-02T06:21:52.67338Z","iopub.execute_input":"2023-11-02T06:21:52.674173Z","iopub.status.idle":"2023-11-02T06:22:03.424092Z","shell.execute_reply.started":"2023-11-02T06:21:52.674134Z","shell.execute_reply":"2023-11-02T06:22:03.423032Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"data_dir = '../input/rsna-2022-cervical-spine-fracture-detection/'\nimage_size_seg = (128, 128, 128)\nmsk_size = image_size_seg[0]\nimage_size_cls = 224\nn_slice_per_c = 15\nn_ch = 5\n\nbatch_size_seg = 1\nnum_workers = 2","metadata":{"execution":{"iopub.status.busy":"2023-11-02T06:22:03.426756Z","iopub.execute_input":"2023-11-02T06:22:03.427198Z","iopub.status.idle":"2023-11-02T06:22:03.432103Z","shell.execute_reply.started":"2023-11-02T06:22:03.427174Z","shell.execute_reply":"2023-11-02T06:22:03.431145Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from timm4smp.models.layers.conv2d_same import Conv2dSame\nfrom conv3d_same import Conv3dSame\n\ndef convert_3d(module):\n\n    module_output = module\n    if isinstance(module, torch.nn.BatchNorm2d):\n        module_output = torch.nn.BatchNorm3d(\n            module.num_features,\n            module.eps,\n            module.momentum,\n            module.affine,\n            module.track_running_stats,\n        )\n        if module.affine:\n            with torch.no_grad():\n                module_output.weight = module.weight\n                module_output.bias = module.bias\n        module_output.running_mean = module.running_mean\n        module_output.running_var = module.running_var\n        module_output.num_batches_tracked = module.num_batches_tracked\n        if hasattr(module, \"qconfig\"):\n            module_output.qconfig = module.qconfig\n            \n    elif isinstance(module, Conv2dSame):\n        module_output = Conv3dSame(\n            in_channels=module.in_channels,\n            out_channels=module.out_channels,\n            kernel_size=module.kernel_size[0],\n            stride=module.stride[0],\n            padding=module.padding[0],\n            dilation=module.dilation[0],\n            groups=module.groups,\n            bias=module.bias is not None,\n        )\n        module_output.weight = torch.nn.Parameter(module.weight.unsqueeze(-1).repeat(1,1,1,1,module.kernel_size[0]))\n\n    elif isinstance(module, torch.nn.Conv2d):\n        module_output = torch.nn.Conv3d(\n            in_channels=module.in_channels,\n            out_channels=module.out_channels,\n            kernel_size=module.kernel_size[0],\n            stride=module.stride[0],\n            padding=module.padding[0],\n            dilation=module.dilation[0],\n            groups=module.groups,\n            bias=module.bias is not None,\n            padding_mode=module.padding_mode\n        )\n        module_output.weight = torch.nn.Parameter(module.weight.unsqueeze(-1).repeat(1,1,1,1,module.kernel_size[0]))\n\n    elif isinstance(module, torch.nn.MaxPool2d):\n        module_output = torch.nn.MaxPool3d(\n            kernel_size=module.kernel_size,\n            stride=module.stride,\n            padding=module.padding,\n            dilation=module.dilation,\n            ceil_mode=module.ceil_mode,\n        )\n    elif isinstance(module, torch.nn.AvgPool2d):\n        module_output = torch.nn.AvgPool3d(\n            kernel_size=module.kernel_size,\n            stride=module.stride,\n            padding=module.padding,\n            ceil_mode=module.ceil_mode,\n        )\n\n    for name, child in module.named_children():\n        module_output.add_module(\n            name, convert_3d(child)\n        )\n    del module\n\n    return module_output\n\n\n\nclass TimmSegModel(nn.Module):\n    def __init__(self, backbone, segtype='unet', pretrained=False):\n        super(TimmSegModel, self).__init__()\n\n        self.encoder = timm4smp.create_model(\n            backbone,\n            in_chans=3,\n            features_only=True,\n            pretrained=pretrained\n        )\n        g = self.encoder(torch.rand(1, 3, 64, 64))\n        encoder_channels = [1] + [_.shape[1] for _ in g]\n        decoder_channels = [256, 128, 64, 32, 16]\n        if segtype == 'unet':\n            self.decoder = smp.unet.decoder.UnetDecoder(\n                encoder_channels=encoder_channels[:n_blocks+1],\n                decoder_channels=decoder_channels[:n_blocks],\n                n_blocks=n_blocks,\n            )\n        self.segmentation_head = nn.Conv2d(decoder_channels[n_blocks-1], 7, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n\n    def forward(self,x):\n        global_features = [0] + self.encoder(x)[:n_blocks]\n        seg_features = self.decoder(*global_features)\n        seg_features = self.segmentation_head(seg_features)\n        return seg_features\n    \n    \nclass TimmModel(nn.Module):\n    def __init__(self, backbone, image_size, pretrained=False):\n        super(TimmModel, self).__init__()\n        self.image_size = image_size\n        self.encoder = timm.create_model(\n            backbone,\n            in_chans=in_chans,\n            num_classes=1,\n            features_only=False,\n            drop_rate=0,\n            drop_path_rate=0,\n            pretrained=pretrained\n        )\n\n        if 'efficient' in backbone:\n            hdim = self.encoder.conv_head.out_channels\n            self.encoder.classifier = nn.Identity()\n        elif 'convnext' in backbone or 'nfnet' in backbone:\n            hdim = self.encoder.head.fc.in_features\n            self.encoder.head.fc = nn.Identity()\n\n        self.lstm = nn.LSTM(hdim, 256, num_layers=2, dropout=0, bidirectional=True, batch_first=True)\n        self.head = nn.Sequential(\n            nn.Linear(512, 256),\n            nn.BatchNorm1d(256),\n            nn.Dropout(0),\n            nn.LeakyReLU(0.1),\n            nn.Linear(256, 1),\n        )\n        self.lstm2 = nn.LSTM(hdim, 256, num_layers=2, dropout=0, bidirectional=True, batch_first=True)\n        self.head2 = nn.Sequential(\n            nn.Linear(512, 256),\n            nn.BatchNorm1d(256),\n            nn.Dropout(0),\n            nn.LeakyReLU(0.1),\n            nn.Linear(256, 1),\n        )\n\n\n    def forward(self, x):  # (bs, nc*7, ch, sz, sz)\n        bs = x.shape[0]\n        x = x.view(bs * n_slice_per_c * 7, in_chans, self.image_size, self.image_size)\n        feat = self.encoder(x)\n        feat = feat.view(bs, n_slice_per_c * 7, -1)\n        feat1, _ = self.lstm(feat)\n        feat1 = feat1.contiguous().view(bs * n_slice_per_c * 7, 512)\n        feat2, _ = self.lstm2(feat)\n\n        return self.head(feat1), self.head2(feat2[:, 0])\n    \n\n","metadata":{"execution":{"iopub.status.busy":"2023-11-02T06:22:03.435143Z","iopub.execute_input":"2023-11-02T06:22:03.43568Z","iopub.status.idle":"2023-11-02T06:22:03.47543Z","shell.execute_reply.started":"2023-11-02T06:22:03.435648Z","shell.execute_reply":"2023-11-02T06:22:03.47452Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#加载语义分割模型 5 折\nmodels_seg = []\n\nkernel_type = 'timm3d_v2s_unet4b_128_128_128_dsv2_flip12_shift333p7_gd1p5_mixup1_lr1e3_20x50ep'\nbackbone = 'tf_efficientnetv2_s_in21ft1k'\nmodel_dir_seg = '../input/seg-v2s-0911/'\nn_blocks = 4\nfor fold in range(5):\n    model = TimmSegModel(backbone, pretrained=False)\n    model = convert_3d(model)\n    model = model.to(device)\n    load_model_file = os.path.join(model_dir_seg, f'{kernel_type}_fold{fold}_best.pth')\n    sd = torch.load(load_model_file)\n    if 'model_state_dict' in sd.keys():\n        sd = sd['model_state_dict']\n    sd = {k[7:] if k.startswith('module.') else k: sd[k] for k in sd.keys()}\n    model.load_state_dict(sd, strict=True)\n    model.eval()\n    models_seg.append(model)\n\nlen(models_seg)","metadata":{"execution":{"iopub.status.busy":"2023-11-02T06:22:03.476474Z","iopub.execute_input":"2023-11-02T06:22:03.476763Z","iopub.status.idle":"2023-11-02T06:22:19.452146Z","shell.execute_reply.started":"2023-11-02T06:22:03.476732Z","shell.execute_reply":"2023-11-02T06:22:19.451258Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def load_dicom(path):\n    dicom = pydicom.read_file(path)\n    data = dicom.pixel_array\n    data = cv2.resize(data, (image_size_seg[0], image_size_seg[1]), interpolation = cv2.INTER_AREA)\n    return data\n\n\ndef load_dicom_line_par(path):\n\n    t_paths = sorted(glob(os.path.join(path, \"*\")), key=lambda x: int(x.split('/')[-1].split(\".\")[0]))\n\n    n_scans = len(t_paths)\n#     print(n_scans)\n    indices = np.quantile(list(range(n_scans)), np.linspace(0., 1., image_size_seg[2])).round().astype(int)\n    t_paths = [t_paths[i] for i in indices]\n\n    images = []\n    for filename in t_paths:\n        images.append(load_dicom(filename))\n    images = np.stack(images, -1)\n    \n    images = images - np.min(images)\n    images = images / (np.max(images) + 1e-4)\n    images = (images * 255).astype(np.uint8)\n\n    return images\n\n\nclass SegTestDataset(Dataset):\n\n    def __init__(self, df):\n        self.df = df.reset_index()\n\n    def __len__(self):\n        return self.df.shape[0]\n\n    def __getitem__(self, index):\n        row = self.df.iloc[index]\n\n        image = load_dicom_line_par(row.image_folder)#读取UID下的128个dcm，拼接为128*128*128的image\n        if image.ndim < 4:\n            image = np.expand_dims(image, 0)\n        image = image.astype(np.float32).repeat(3, 0)  # to 3ch\n        #原始image，为128*128*128==》3*（128*128*128）复制3次。简单理解，RGB图片3个通道，这次构造一个128通道的图片。\n        image = image / 255.\n        return torch.tensor(image).float()","metadata":{"execution":{"iopub.status.busy":"2023-11-02T06:22:19.453812Z","iopub.execute_input":"2023-11-02T06:22:19.454204Z","iopub.status.idle":"2023-11-02T06:22:19.465903Z","shell.execute_reply.started":"2023-11-02T06:22:19.454168Z","shell.execute_reply":"2023-11-02T06:22:19.465015Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#获取train的内容，并添加不同UID对应dcm的地址，此处只处理20个StudyInstanceUID\ndf = pd.read_csv(os.path.join(data_dir, 'train.csv')).head(20)\ndf = pd.DataFrame({\n    'StudyInstanceUID': df['StudyInstanceUID'].unique().tolist()\n})\ndf['image_folder'] = df['StudyInstanceUID'].apply(lambda x: os.path.join(data_dir, 'train_images', x))","metadata":{"execution":{"iopub.status.busy":"2023-11-02T06:22:19.46701Z","iopub.execute_input":"2023-11-02T06:22:19.467288Z","iopub.status.idle":"2023-11-02T06:22:19.517131Z","shell.execute_reply.started":"2023-11-02T06:22:19.467265Z","shell.execute_reply":"2023-11-02T06:22:19.516238Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df.head()","metadata":{"execution":{"iopub.status.busy":"2023-11-02T06:22:19.518234Z","iopub.execute_input":"2023-11-02T06:22:19.518496Z","iopub.status.idle":"2023-11-02T06:22:19.529883Z","shell.execute_reply.started":"2023-11-02T06:22:19.518474Z","shell.execute_reply":"2023-11-02T06:22:19.528907Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df.info()","metadata":{"execution":{"iopub.status.busy":"2023-11-02T06:22:19.530852Z","iopub.execute_input":"2023-11-02T06:22:19.531109Z","iopub.status.idle":"2023-11-02T06:22:19.547556Z","shell.execute_reply.started":"2023-11-02T06:22:19.531087Z","shell.execute_reply":"2023-11-02T06:22:19.546604Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def load_bone(msk, cid, t_paths, cropped_images, nameUID,save_path):\n    n_scans = len(t_paths)\n    bone = []\n    try:\n        msk_b = msk[0] > 0.2\n        msk_c = msk[cid] > 0.05\n\n        x = np.where(msk_b.sum(1).sum(1) > 0)[0]\n        y = np.where(msk_b.sum(0).sum(1) > 0)[0]\n        z = np.where(msk_b.sum(0).sum(0) > 0)[0]\n#         print(x)\n#         print(y)\n#         print(z)\n\n        if len(x) == 0 or len(y) == 0 or len(z) == 0:\n            x = np.where(msk_c.sum(1).sum(1) > 0)[0]\n            y = np.where(msk_c.sum(0).sum(1) > 0)[0]\n            z = np.where(msk_c.sum(0).sum(0) > 0)[0]\n\n        x1, x2 = max(0, x[0] - 1), min(msk.shape[1], x[-1] + 1)\n        y1, y2 = max(0, y[0] - 1), min(msk.shape[2], y[-1] + 1)\n        z1, z2 = max(0, z[0] - 1), min(msk.shape[3], z[-1] + 1)\n        zz1, zz2 = int(z1 / msk_size * n_scans), int(z2 / msk_size * n_scans)\n\n        inds = np.linspace(zz1 ,zz2-1 ,n_slice_per_c).astype(int)#dcm的索引范围\n        inds_ = np.linspace(z1 ,z2-1 ,n_slice_per_c).astype(int)#mask立方体的通道范围\n#         print(x1,x2,y1,y2,z1,z2,zz1,zz2)\n#         print(inds,inds_)\n        for sid, (ind, ind_) in enumerate(zip(inds, inds_)):\n\n            msk_this = msk[cid, :, :, ind_]\n#             print(msk_this.shape) (128, 128)\n\n            images = []\n            for i in range(-n_ch//2+1, n_ch//2+1):\n                #5张dcm图片\n                try:\n                    dicom = pydicom.read_file(t_paths[ind+i])\n                    images.append(dicom.pixel_array)\n                except:\n                    images.append(np.zeros((512, 512)))\n\n            data = np.stack(images, -1)\n            data = data - np.min(data)\n            data = data / (np.max(data) + 1e-4)\n            data = (data * 255).astype(np.uint8)\n#             print(data.shape) (512, 512, 5)\n            msk_this = msk_this[x1:x2, y1:y2]\n#             print(msk_this.shape) (33, 54)\n            xx1 = int(x1 / msk_size * data.shape[0])\n            xx2 = int(x2 / msk_size * data.shape[0])\n            yy1 = int(y1 / msk_size * data.shape[1])\n            yy2 = int(y2 / msk_size * data.shape[1])\n#             print(xx1,xx2,yy1,yy2) 84 216 144 360\n            data = data[xx1:xx2, yy1:yy2]\n#             print(data.shape) (132, 216, 5)\n            data = np.stack([cv2.resize(data[:, :, i], (image_size_cls, image_size_cls), interpolation = cv2.INTER_LINEAR) for i in range(n_ch)], -1)\n#             print(data.shape) (224, 224, 5) 一个通道一个通道地进行resize\n            msk_this = (msk_this * 255).astype(np.uint8)\n            msk_this = cv2.resize(msk_this, (image_size_cls, image_size_cls), interpolation = cv2.INTER_LINEAR)\n#             print(msk_this.shape) (224, 224)\n\n            data = np.concatenate([data, msk_this[:, :, np.newaxis]], -1)\n#             print(data.shape)#(224, 224, 6)\n            npy_name = nameUID+'_'+str(cid)+'_'+str(sid)+'.npy'\n            np.save(save_path+npy_name,data)\n\n            bone.append(torch.tensor(data))\n\n\n    except:\n        for sid in range(n_slice_per_c):\n            bone.append(torch.ones((image_size_cls, image_size_cls, n_ch+1)).int())\n\n\n    cropped_images[cid] = torch.stack(bone, 0)\n#     print(torch.stack(bone, 0).shape) #torch.Size([15, 224, 224, 6])\n\n\ndef load_cropped_images(msk, image_folder, n_ch=n_ch):\n\n    t_paths = sorted(glob(os.path.join(image_folder, \"*\")), key=lambda x: int(x.split('/')[-1].split(\".\")[0]))\n    nameUID = image_folder.split('/')[-1]\n    save_path = '/kaggle/working/train_seg_244*244*6_output/'\n    if not os.path.exists(save_path):\n        os.mkdir(save_path)\n    for cid in range(7):\n        threads[cid] = threading.Thread(target=load_bone, args=(msk, cid, t_paths, cropped_images,nameUID,save_path))\n        threads[cid].start()\n    for cid in range(7):\n        threads[cid].join()\n\n#     for cid in range(7):\n#         load_bone(msk, cid, t_paths, cropped_images,nameUID)\n\n    return torch.cat(cropped_images, 0)\n","metadata":{"execution":{"iopub.status.busy":"2023-11-02T06:22:19.551029Z","iopub.execute_input":"2023-11-02T06:22:19.551335Z","iopub.status.idle":"2023-11-02T06:22:19.574649Z","shell.execute_reply.started":"2023-11-02T06:22:19.551271Z","shell.execute_reply":"2023-11-02T06:22:19.573703Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#构建数据集\ndataset_seg = SegTestDataset(df)\ntrain_seg = torch.utils.data.DataLoader(dataset_seg, batch_size=batch_size_seg, shuffle=False, num_workers=num_workers)","metadata":{"execution":{"iopub.status.busy":"2023-11-02T06:22:19.57565Z","iopub.execute_input":"2023-11-02T06:22:19.575909Z","iopub.status.idle":"2023-11-02T06:22:19.59312Z","shell.execute_reply.started":"2023-11-02T06:22:19.575887Z","shell.execute_reply":"2023-11-02T06:22:19.592238Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"bar = tqdm(train_seg)\nwith torch.no_grad():\n    for batch_id, (images) in enumerate(bar):\n        images = images.cuda()\n\n        # SEG\n        pred_masks = []\n        for model in models_seg:\n            pmask = model(images).sigmoid()\n            pred_masks.append(pmask)\n        pred_masks = torch.stack(pred_masks, 0).mean(0).cpu().numpy()#cpu().numpy()转为np，版本不同可能会报错\n        \n        threads = [None] * 7\n        cropped_images = [None] * 7\n\n        for i in range(pred_masks.shape[0]):\n            row = df.iloc[batch_id*batch_size_seg+i]#一行一行）（UID）处理3D分割的结果\n            cropped_images = load_cropped_images(pred_masks[i], row.image_folder)\n","metadata":{"execution":{"iopub.status.busy":"2023-11-02T06:22:19.594389Z","iopub.execute_input":"2023-11-02T06:22:19.594731Z","iopub.status.idle":"2023-11-02T06:23:57.877087Z","shell.execute_reply.started":"2023-11-02T06:22:19.594701Z","shell.execute_reply":"2023-11-02T06:23:57.876067Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"r = np.load('/kaggle/working/train_seg_244*244*6_output/1.2.826.0.1.3680043.27262_0_3.npy')","metadata":{"execution":{"iopub.status.busy":"2023-11-02T06:52:29.947742Z","iopub.execute_input":"2023-11-02T06:52:29.948398Z","iopub.status.idle":"2023-11-02T06:52:29.953548Z","shell.execute_reply.started":"2023-11-02T06:52:29.948366Z","shell.execute_reply":"2023-11-02T06:52:29.952494Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"r","metadata":{"execution":{"iopub.status.busy":"2023-11-02T06:52:31.10183Z","iopub.execute_input":"2023-11-02T06:52:31.102435Z","iopub.status.idle":"2023-11-02T06:52:31.109461Z","shell.execute_reply.started":"2023-11-02T06:52:31.102403Z","shell.execute_reply":"2023-11-02T06:52:31.108452Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}