{"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.7.12"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":71549,"databundleVersionId":8561470,"sourceType":"competition"}],"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import pandas as pd\nimport matplotlib.pyplot as plt\nimport pydicom\nimport numpy as np\nimport os\nimport glob\nfrom tqdm import tqdm\nimport gc\nimport sklearn\n\nimport torchvision\nimport torch\nimport torch.nn as nn\nfrom torch.utils.data import Dataset\nfrom fastai.vision.all import *\nimport segmentation_models_pytorch as smp\n\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")","metadata":{"_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","execution":{"iopub.execute_input":"2024-07-22T15:36:47.391937Z","iopub.status.busy":"2024-07-22T15:36:47.389385Z","iopub.status.idle":"2024-07-22T15:37:02.967144Z","shell.execute_reply":"2024-07-22T15:37:02.965578Z","shell.execute_reply.started":"2024-07-22T15:36:47.391871Z"},"collapsed":true,"jupyter":{"outputs_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"SEED = 1337\nFOLDS = [4,5]\nPATH = 'C:/Users/Angel/kaggle/'# Main path\nTRAIN_PATH = 'C:/Users/Angel/kaggle/train/'# Training images folder\nENCODER_NAME = \"resnet18\"\nPATCH_H = 512\nPATCH_W = 512\nANGLE = 30\npatch_size = 64\nLmax = 15\nLR_MAX = 5e-6#1e-5\nBS = 32\nEPOCHS = 16","metadata":{"execution":{"iopub.execute_input":"2024-07-22T15:37:25.201958Z","iopub.status.busy":"2024-07-22T15:37:25.201502Z","iopub.status.idle":"2024-07-22T15:37:25.208848Z","shell.execute_reply":"2024-07-22T15:37:25.207312Z","shell.execute_reply.started":"2024-07-22T15:37:25.201926Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"spinal = [\n    'spinal_canal_stenosis_l1_l2',\n    'spinal_canal_stenosis_l2_l3',\n    'spinal_canal_stenosis_l3_l4',\n    'spinal_canal_stenosis_l4_l5',\n    'spinal_canal_stenosis_l5_s1'\n]\ncoor = [\n    'x_L1L2',\n    'y_L1L2',\n    'x_L2L3',\n    'y_L2L3',\n    'x_L3L4',\n    'y_L3L4',\n    'x_L4L5',\n    'y_L4L5',\n    'x_L5S1',\n    'y_L5S1'\n]\nlabels = {\n    'Normal/Mild':0,\n    'Moderate':1,\n    'Severe':2,\n    'UNK':-100\n}","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def seed_everything(seed):\n    random.seed(seed)\n    np.random.seed(seed)\n    os.environ[\"PYTHONHASHSEED\"] = str(seed)\n    torch.manual_seed(seed)\n    torch.cuda.manual_seed(seed)\n    torch.backends.cudnn.deterministic = True\n    torch.backends.cudnn.benchmark = False\n    \n# CosineAnnealingAlpha\ndef nt(nmin,nmax,tcur,tmax):\n    return (nmax - .5*(nmax-nmin)*(1+np.cos(tcur*np.pi/tmax))).astype(np.float32)\n\nplt.plot(nt(.25,1,np.arange(EPOCHS),EPOCHS))\nplt.show()\n\n# callback to update alpha during training\ndef cb(self):\n    alpha = torch.as_tensor(nt(.25,1,learn.train_iter,EPOCHS*n_iter))\n    learn.dls.train_ds.alpha = alpha\nalpha_cb = Callback(before_batch=cb)\n\ndef augment_image(image):\n#   Randomly rotate the image.\n    angle = torch.as_tensor(random.uniform(-ANGLE, ANGLE))\n    image = torchvision.transforms.functional.rotate(\n        image,angle.item(),\n#       interpolation=torchvision.transforms.InterpolationMode.BILINEAR\n    )\n    return image","metadata":{"execution":{"iopub.execute_input":"2024-07-22T15:37:02.982642Z","iopub.status.busy":"2024-07-22T15:37:02.982119Z","iopub.status.idle":"2024-07-22T15:37:03.364342Z","shell.execute_reply":"2024-07-22T15:37:03.363214Z","shell.execute_reply.started":"2024-07-22T15:37:02.982516Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train = pd.read_csv(PATH + 'train_split.csv')\ntrain = train[['study_id','fold']+spinal][train[spinal].isna().sum(1) < len(spinal)].reset_index(drop=True)\ntrain.tail()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train = train.fillna('UNK')\ntrain[(train[spinal] == 'UNK').sum(1)>0].reset_index(drop=True).tail()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_coor = pd.read_csv(PATH + 'train_label_coordinates.csv')\ndf_coor.tail()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"S = df_coor[\n    df_coor['condition'] == 'Spinal Canal Stenosis'\n].sort_values([\n    'study_id',\n    'series_id',\n    'level'\n]).reset_index(drop=True)\nS.tail()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"S['x_mean_fraction'] = S['x']/S.groupby(['study_id','series_id'])['x'].mean().loc[[(study_id,series_id) for study_id,series_id in S[['study_id','series_id']].values]].values\nS.tail()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"S = S[S['x_mean_fraction'] > .8]\nS.tail()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"S['instance_number'] = S['instance_number'] - 1\nS.tail()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"coordinates = {}\nfor study_id,df in S.groupby('study_id'):\n    coordinates[study_id] = {}\nfor (study_id,series_id),df in tqdm(S.groupby(['study_id','series_id'])):\n    coordinates[study_id][series_id] = {\n                'L1/L2':{\n                    'x':torch.nan,\n                    'y':torch.nan,\n                    'instance_number':torch.nan\n                },\n                    'L2/L3':{\n                    'x':torch.nan,\n                    'y':torch.nan,\n                    'instance_number':torch.nan\n                },\n                'L3/L4':{\n                    'x':torch.nan,\n                    'y':torch.nan,\n                    'instance_number':torch.nan\n                },\n                'L4/L5':{\n                    'x':torch.nan,\n                    'y':torch.nan,\n                    'instance_number':torch.nan\n                },\n                'L5/S1':{\n                    'x':torch.nan,\n                    'y':torch.nan,\n                    'instance_number':torch.nan\n                }\n    }\n    \n    for i in range(len(df)):\n        row = df.iloc[i]\n        coordinates[row['study_id']][row['series_id']][row['level']]['x'] = row['x']\n        coordinates[row['study_id']][row['series_id']][row['level']]['y'] = row['y']\n        coordinates[row['study_id']][row['series_id']][row['level']]['instance_number'] = row['instance_number']","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"S =  S[[\n    'study_id',\n    'series_id'\n]].groupby([\n    'study_id',\n    'series_id'\n]).count().reset_index()\nS.tail()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"v = np.zeros((len(S),15))\nfor i in tqdm(range(len(S))):\n    row = S.iloc[i]\n    k = 0\n    for level in coordinates[row['study_id']][row['series_id']]:\n        v[i,k:k+3] = list(coordinates[row['study_id']][row['series_id']][level].values())\n        k += 3","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"S[[\n    'x_L1L2',\n    'y_L1L2',\n    'i_L1L2',\n    'x_L2L3',\n    'y_L2L3',\n    'i_L2L3',\n    'x_L3L4',\n    'y_L3L4',\n    'i_L3L4',\n    'x_L4L5',\n    'y_L4L5',\n    'i_L4L5',\n    'x_L5S1',\n    'y_L5S1',\n    'i_L5S1'\n]] = v\nS.tail()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"S = S.merge(train,left_on='study_id',right_on='study_id')\nS.tail()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"mask = S[[\n    'x_L1L2',\n    'x_L2L3',\n    'x_L3L4',\n    'x_L4L5',\n    'x_L5S1'\n]].isna().values\nmask += S[[\n    'y_L1L2',\n    'y_L2L3',\n    'y_L3L4',\n    'y_L4L5',\n    'y_L5S1'\n]].isna().values\nmask += S[[\n    'i_L1L2',\n    'i_L2L3',\n    'i_L3L4',\n    'i_L4L5',\n    'i_L5S1'\n]].isna().values\nmask = mask > 0\nmask[-5:]\nv = S[spinal].values\nv[mask] = 'UNK'\nS[spinal] = v","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"S['flip'] = False\nfS = S.copy()\nfS['flip'] = True\nS = pd.concat([S,fS]).reset_index(drop=True)\nS.tail()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"S.groupby('fold').count()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class Sagittal_T2_Spinal_Dataset(Dataset):\n    def __init__(self, df, VALID=False, P=patch_size):\n        self.data = df\n        self.VALID = VALID\n        self.P = P\n        self.resize = torchvision.transforms.Resize((PATCH_H,PATCH_W),antialias=True)\n\n    def __len__(self):\n        return len(self.data)\n\n    def __getitem__(self, index):\n        row = self.data.iloc[index]\n        \n        \n        sample = TRAIN_PATH + str(row['study_id']) + '/'+str(row['series_id'])\n\n        images = [x.replace('\\\\','/') for x in glob.glob(sample+'/*.dcm')]\n        images.sort(reverse=False, key=lambda x: int(x.split('/')[-1].replace('.dcm', '')))\n\n        instance_numbers = row[[\n            'i_L1L2',\n            'i_L2L3',\n            'i_L3L4',\n            'i_L4L5',\n            'i_L5S1'\n        ]].values\n\n        label = torch.as_tensor([labels[x] for x in row[spinal]]).to(device)\n\n        image = torch.stack([\n            torch.as_tensor(pydicom.dcmread(x).pixel_array.astype(np.float32)) for x in images\n        ]).float().to(device)\n        image = image/image.max()\n        D,H,W = image.shape\n\n        c = torch.as_tensor([x for x in row[coor]]).view(5,2).float()\n        missing = c.isnan().sum(1) > 0\n        c[missing] = torch.as_tensor([H/2,W/2])\n\n        label[missing] = - 100\n\n        if H > W:\n            d = W\n            h = (H - d)//2\n            image = image[:,h:h+d]\n            c[:,1] -= h\n            H = W\n        elif H < W:\n            d = H\n            w = (W - d)//2\n            image = image[:,:,w:w+d]\n            c[:,0] -= w\n            W = H\n\n        image = self.resize(image)\n        image = nn.functional.pad(image,[self.P]*4,'reflect')\n        c[:,1] = c[:,1]*PATCH_H/H + self.P\n        c[:,0] = c[:,0]*PATCH_W/W + self.P\n        c = c.long()\n        if not self.VALID: c += torch.normal(0,5,(5,2)).long()\n\n        crops = torch.stack([\n            image[\n                :,\n                xy[1]-self.P:xy[1]+self.P,\n                xy[0]-self.P:xy[0]+self.P\n            ] for xy in c\n        ])\n\n        image = torch.zeros(5,Lmax,2*self.P,2*self.P).to(device)\n        slices_mask = torch.ones(5,Lmax).bool().to(device)\n        for i in range(5):\n            if ~missing[i]:\n                instance_number = instance_numbers[i].astype(int)\n                start = max([0,instance_number - Lmax//2])\n                end = min([D,start + Lmax])\n                crop = crops[i,start:end]\n\n                if row.flip:\n                    image[i,:len(crop)] = crop.flip(0)\n                else:\n                    image[i,:len(crop)] = crop\n                slices_mask[i,:len(crop)] = False\n\n                if not self.VALID:\n                    image[i] = augment_image(image[i].reshape(-1,2*self.P,2*self.P)).reshape(Lmax,2*self.P,2*self.P)\n            \n        image = image[:,:,self.P//2:self.P//2+self.P,self.P//2:self.P//2+self.P]\n\n        return [image,slices_mask],label","metadata":{"execution":{"iopub.execute_input":"2024-07-22T15:37:03.377957Z","iopub.status.busy":"2024-07-22T15:37:03.376752Z","iopub.status.idle":"2024-07-22T15:37:03.413904Z","shell.execute_reply":"2024-07-22T15:37:03.412746Z","shell.execute_reply.started":"2024-07-22T15:37:03.377915Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"ds = Sagittal_T2_Spinal_Dataset(S)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"[sample,mask],label = ds.__getitem__(np.random.randint(len(ds)))\nprint(label)\nfor k in range(5):\n    fig, axes = plt.subplots(1,Lmax, figsize=(10,2))\n    for i in range(2):\n        for j in range(Lmax):\n            axes[j].imshow(sample.cpu()[k,j])\n    plt.show()\n\nplt.imshow(mask.cpu().view(-1,Lmax))","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"del ds\ngc.collect()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class Sagittal_T2_spine_Discriminator(nn.Module):\n    def __init__(self, dim=512):\n        super().__init__()\n        CNN = torchvision.models.resnet18(weights='DEFAULT')\n        W = nn.Parameter(CNN.conv1.weight.sum(1, keepdim=True))\n        CNN.conv1 = nn.Conv2d(1, patch_size, kernel_size=(7, 7), stride=(2, 2), padding=(3, 3), bias=False)\n        CNN.conv1.weight = W\n        CNN.fc = nn.Identity()\n        self.emb = CNN.to(device)\n        self.proj_out = nn.Linear(dim,2).to(device)\n    \n    def forward(self, x):        \n        x = self.emb(x.view(-1,1,patch_size,patch_size))\n        x = self.proj_out(x.view(-1,512))\n        return x","metadata":{"execution":{"iopub.execute_input":"2024-07-22T15:37:03.416625Z","iopub.status.busy":"2024-07-22T15:37:03.415818Z","iopub.status.idle":"2024-07-22T15:37:03.443753Z","shell.execute_reply":"2024-07-22T15:37:03.442621Z","shell.execute_reply.started":"2024-07-22T15:37:03.416584Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class SinusoidalPosEmb(nn.Module):\n    def __init__(self, dim=16, M=10000):\n        super().__init__()\n        self.dim = dim\n        self.M = M\n\n    def forward(self, x):\n        device = x.device\n        half_dim = self.dim // 2\n        emb = math.log(self.M) / half_dim\n        emb = torch.exp(torch.arange(half_dim, device=device) * (-emb))\n        emb = x[...,None] * emb[None,...]\n        emb = torch.cat((emb.sin(), emb.cos()), dim=-1)\n        return emb\n\nclass Sagittal_T2_Spinal_ViT(nn.Module):\n    def __init__(\n            self,\n            ENCODER,\n            dim=512,\n            depth=24,\n            head_size=64\n        ):\n        super().__init__()\n        self.ENCODER = ENCODER\n        self.slices_enc = SinusoidalPosEmb(dim)(torch.arange(Lmax, device=device).unsqueeze(0))\n        self.slices_enc = nn.Parameter(self.slices_enc)\n        pos_enc = SinusoidalPosEmb(dim)(torch.arange(5, device=device).unsqueeze(0))\n        self.pos_enc = nn.Parameter(pos_enc)\n        self.slices_transformer = nn.TransformerEncoder(\n                nn.TransformerEncoderLayer(d_model=dim, nhead=dim//head_size, dim_feedforward=4*dim,\n                dropout=0.1, activation=nn.GELU(), batch_first=True, norm_first=True, device=device), depth)\n        self.transformer = nn.TransformerEncoder(\n                nn.TransformerEncoderLayer(d_model=dim, nhead=dim//head_size, dim_feedforward=4*dim,\n                dropout=0.1, activation=nn.GELU(), batch_first=True, norm_first=True, device=device), depth)\n        self.proj_out = nn.Linear(dim,3).to(device)\n    \n    def forward(self, x):\n        x,slices_mask = x\n        slices_mask = slices_mask.view(-1,Lmax)\n        mask = slices_mask.sum(-1) < Lmax\n        \n        x = self.ENCODER(x.view(-1,1,patch_size,patch_size))\n\n        x = x.view(-1,Lmax,512)\n        x = x + self.slices_enc\n        x[mask] = self.slices_transformer(x[mask],src_key_padding_mask=slices_mask[mask])\n\n        x[slices_mask] = 0\n        d = (~slices_mask).sum(1).unsqueeze(-1).tile(1,512)\n        x = x.sum(1)\n        x[d > 0] = x[d > 0]/d[d > 0]\n\n        level_mask = (slices_mask.sum(1) == Lmax).view(-1,5)\n        x = x.view(-1,5,512) + self.pos_enc\n        x = self.transformer(x,src_key_padding_mask=level_mask)\n        x = self.proj_out(x.view(-1,512)).view(-1,5,3)\n        return x","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def myLoss(preds,target):\n    target = target.view(-1)\n    preds = preds.view(-1,3)\n    \n    return nn.CrossEntropyLoss(weight=torch.as_tensor([1.,2.,4.]).to(device))(preds,target)","metadata":{"execution":{"iopub.execute_input":"2024-07-22T15:37:03.446270Z","iopub.status.busy":"2024-07-22T15:37:03.445500Z","iopub.status.idle":"2024-07-22T15:37:03.459352Z","shell.execute_reply":"2024-07-22T15:37:03.457940Z","shell.execute_reply.started":"2024-07-22T15:37:03.446231Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"for f in FOLDS:\n    seed_everything(SEED)\n    model = Sagittal_T2_Spinal_ViT(\n        torch.load(PATH + 'Sagittal_T2/discriminator/Sagittal_T2_spine_discriminator_'+str(f)).emb\n    )\n    df = S\n    tdf = df[df.fold != f]\n    vdf = df[df.fold == f]\n    tds = Sagittal_T2_Spinal_Dataset(tdf)\n    vds = Sagittal_T2_Spinal_Dataset(vdf,VALID=True)\n    tdl = torch.utils.data.DataLoader(tds, batch_size=BS, shuffle=True, drop_last=True)\n    vdl = torch.utils.data.DataLoader(vds, batch_size=BS, shuffle=False)\n    dls = DataLoaders(tdl,vdl)\n\n    n_iter = len(tds)//BS\n\n    learn = Learner(\n        dls,\n        model,\n        loss_func=myLoss,\n        cbs=[\n            ShowGraphCallback(),\n            GradientClip(3.0),\n            alpha_cb\n        ]\n    )\n    learn.fit_one_cycle(EPOCHS, lr_max=LR_MAX, wd=0.05, pct_start=0.02)\n    torch.save(model,'Sagittal_T2_Spinal_ViT_'+str(f))\n    del model,df,tdf,vdf,tds,vds,tdl,vdl,dls,learn\n    gc.collect()","metadata":{"execution":{"iopub.execute_input":"2024-07-22T15:39:58.974867Z","iopub.status.busy":"2024-07-22T15:39:58.974392Z","iopub.status.idle":"2024-07-22T15:40:10.837835Z","shell.execute_reply":"2024-07-22T15:40:10.835528Z","shell.execute_reply.started":"2024-07-22T15:39:58.974828Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"FOLDS = [\n    '''0\t0.908724\t0.633825\t12:41\n1\t0.542367\t0.627722\t12:53\n2\t0.481850\t0.481803\t11:25\n3\t0.423859\t0.318504\t11:13\n4\t0.389764\t0.335094\t11:18\n5\t0.381459\t0.319343\t11:20\n6\t0.369244\t0.373869\t11:17\n7\t0.338741\t0.319158\t11:17\n8\t0.327234\t0.322730\t11:07\n9\t0.325960\t0.336394\t11:17\n10\t0.319744\t0.302709\t11:15\n11\t0.321403\t0.320284\t11:24\n12\t0.299962\t0.302910\t11:15\n13\t0.303122\t0.318872\t11:26\n14\t0.292123\t0.305572\t11:25\n15\t0.295646\t0.306218\t11:27''',\n    '''0\t0.834889\t0.626539\t11:56\n1\t0.559498\t0.518421\t11:19\n2\t0.462401\t0.395586\t11:30\n3\t0.431726\t0.364882\t11:20\n4\t0.390481\t0.343762\t11:18\n5\t0.381275\t0.321904\t11:17\n6\t0.365477\t0.336417\t11:33\n7\t0.372566\t0.325591\t11:34\n8\t0.326688\t0.302057\t12:02\n9\t0.318840\t0.314251\t11:17\n10\t0.311919\t0.297093\t13:36\n11\t0.320316\t0.300823\t11:32\n12\t0.307305\t0.298338\t11:06\n13\t0.308204\t0.298642\t11:12\n14\t0.300773\t0.294550\t11:30\n15\t0.302716\t0.295606\t11:12''',\n    '''0\t0.849269\t0.930417\t11:11\n1\t0.541680\t0.391493\t11:23\n2\t0.458766\t0.356661\t11:29\n3\t0.425333\t0.330684\t11:15\n4\t0.400492\t0.343798\t11:12\n5\t0.381700\t0.317394\t11:36\n6\t0.374329\t0.338669\t12:36\n7\t0.346137\t0.305142\t12:16\n8\t0.329711\t0.314581\t11:47\n9\t0.321498\t0.315245\t12:45\n10\t0.333052\t0.302263\t12:39\n11\t0.306373\t0.313393\t11:55\n12\t0.302495\t0.309797\t11:59\n13\t0.299533\t0.303616\t11:43\n14\t0.295559\t0.304309\t12:23\n15\t0.299785\t0.301494\t11:47''',\n    '''0\t0.732079\t0.575000\t12:39\n1\t0.574035\t0.345816\t12:40\n2\t0.478511\t0.316075\t12:02\n3\t0.430179\t0.303258\t11:30\n4\t0.436835\t0.418426\t11:26\n5\t0.394055\t0.315322\t11:13\n6\t0.379945\t0.312369\t11:10\n7\t0.380446\t0.333714\t11:10\n8\t0.352238\t0.282738\t11:11\n9\t0.334740\t0.291416\t11:12\n10\t0.338533\t0.284008\t11:09\n11\t0.330591\t0.281311\t11:06\n12\t0.315402\t0.281208\t11:09\n13\t0.305365\t0.279470\t11:10\n14\t0.302912\t0.279434\t12:34\n15\t0.305688\t0.277819\t12:40''',\n    '''0\t0.769392\t0.645293\t11:11\n1\t0.571276\t0.423692\t11:40\n2\t0.454518\t0.342082\t12:07\n3\t0.421902\t0.332166\t12:18\n4\t0.393885\t0.317729\t11:20\n5\t0.367514\t0.334296\t11:25\n6\t0.355679\t0.319901\t11:48\n7\t0.360160\t0.300120\t11:10\n8\t0.334430\t0.304180\t11:12\n9\t0.320053\t0.285834\t11:14\n10\t0.328051\t0.289729\t11:10\n11\t0.308249\t0.292546\t11:10\n12\t0.306878\t0.286116\t11:09\n13\t0.300543\t0.283985\t11:06\n14\t0.297565\t0.282372\t11:07\n15\t0.294830\t0.282471\t11:11'''\n]","metadata":{},"execution_count":null,"outputs":[]}]}