{"cells":[{"metadata":{},"cell_type":"markdown","source":"### Welcome to RANZCR CLiP - Catheter and Line Position Challenge\n### In short, this notebook contains the following information:\n* How many times each of the classes apear in training set?\n* Distribution of each of the 4 class categories (CVC, NGT, EET and SGC) in the training set\n* How many tubes of each category a train image can contain? And their correlation\n* Proper 5 fold CV split and looking at number of classes in each of the folds"},{"metadata":{"trusted":true},"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport seaborn as sns\nimport numpy as np\nimport pandas as pd\nimport cv2\nimport matplotlib.image as mpimg\nfrom sklearn.model_selection import GroupKFold","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"#This function is taken from here: https://www.kaggle.com/c/ranzcr-clip-catheter-line-classification/discussion/204638 Thanks to @gunesevitan\ndef visualize_annotations(filename):\n    image = cv2.imread(f'../input/ranzcr-clip-catheter-line-classification/train/{filename}')\n    image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n    study_instance_uid = filename.split('.jpg')[0]\n    if study_instance_uid in df_train_annotations['StudyInstanceUID'].values:\n        labels = df_train_annotations.loc[df_train_annotations['StudyInstanceUID'] == study_instance_uid]['label'].values.tolist()\n        lines = df_train_annotations.loc[df_train_annotations['StudyInstanceUID'] == study_instance_uid]['data'].apply(lambda x: eval(x)).values.tolist()\n        #print(f'Sample {study_instance_uid}\\n{\"-\" * (7 + len(study_instance_uid))}\\n')\n        fig = plt.figure(figsize=(image.shape[0] // 300, image.shape[1] // 300))\n        ax = plt.imshow(image)\n        for line, label in zip(lines, labels):\n            #print(f'{label}\\n{\"-\" * len(label)}\\n{line}\\n')            \n            xs = []\n            ys = []\n            for point in line:\n                xs.append(point[0])\n                ys.append(point[-1])\n            plt.scatter(xs, ys, s=40, label=label)\n        plt.tick_params(axis='x', labelsize=10)\n        plt.tick_params(axis='y', labelsize=10)\n        plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0, prop={'size': 10})\n        #plt.title(f'{study_instance_uid} Annotations', size=10, pad=10)\n        plt.show()\n    else:\n        return None","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"X_train = pd.read_csv(\"../input/ranzcr-clip-catheter-line-classification/train.csv\")\ndf_train_annotations = pd.read_csv(\"../input/ranzcr-clip-catheter-line-classification/train_annotations.csv\")\nsample_sub = pd.read_csv(\"../input/ranzcr-clip-catheter-line-classification/sample_submission.csv\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Number of images in the training set"},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"X_train.shape[0]","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Target columns"},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"target_cols = X_train.columns[1:12]\ntarget_cols","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Class distribution in the Training Set"},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"target_cols = X_train.columns[1:12]\ntarget_counts = X_train[target_cols].sum(axis = 0).sort_values(ascending = False)\nplt.figure(figsize = (15, 5))\nsns.barplot(y = target_counts.index.values, x = target_counts.values)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Now lets take a look at how many images contain at least one type of CVC, NGT, ETT and Swan Ganz Catheter Present"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"cvc_target = [\"CVC - Normal\", \"CVC - Borderline\", \"CVC - Abnormal\"]\nngt_target = [\"NGT - Normal\", \"NGT - Incompletely Imaged\", \"NGT - Borderline\", \"NGT - Abnormal\"]\nett_target = [\"ETT - Normal\", \"ETT - Borderline\", \"ETT - Abnormal\"]\nsgcp_target = [\"Swan Ganz Catheter Present\"]\nshorten_tar = [\"CVC\", \"NGT\", \"ETT\", \"Swan Ganz Catheter Present\"]\n\ncvc_counts = X_train[X_train.apply(lambda x: x[\"CVC - Normal\"] == 1 or x[\"CVC - Borderline\"] == 1 or x[\"CVC - Abnormal\"] == 1, axis = 1)]\nngt_counts = X_train[X_train.apply(lambda x: x[\"NGT - Normal\"] == 1 or x[\"NGT - Borderline\"] == 1 or x[\"NGT - Abnormal\"] == 1 or x[\"NGT - Incompletely Imaged\"] == 1, axis = 1)]\nett_counts = X_train[X_train.apply(lambda x: x[\"ETT - Normal\"] == 1 or x[\"ETT - Borderline\"] == 1 or x[\"ETT - Abnormal\"] == 1, axis = 1)]\nsgcp_counts = X_train[X_train.apply(lambda x: x[\"Swan Ganz Catheter Present\"] == 1, axis = 1)]\n\nplt.figure(figsize = (10,7))\nsns.barplot(y = [cvc_counts.shape[0] * 100 / 30083, ngt_counts.shape[0] * 100 / 30083, ett_counts.shape[0] * 100 / 30083, sgcp_counts.shape[0] * 100 / 30083], x = shorten_tar, palette=\"ch:.25\")\nplt.xlabel(\"Category\", fontsize = 20)\nplt.ylabel(\"% of training set\", fontsize = 20)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## How many images contain more than one tube of each categories (\"CVC\", \"NGT\", \"EET\" and \"Swan Ganz Catheter Present\")"},{"metadata":{},"cell_type":"markdown","source":"# CVC (Central Venous Catheter)"},{"metadata":{},"cell_type":"markdown","source":"### Number of images containing 0, 1, 2 and 3 CVC tubes"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"vals = pd.DataFrame(X_train[\"CVC - Normal\"] + X_train[\"CVC - Abnormal\"] + X_train[\"CVC - Borderline\"]).value_counts().sort_index(ascending = True)\nvals = pd.Series(vals.values).sort_values(ascending = False)\nplt.figure(figsize = (15, 5))\nsns.barplot(x = vals.index.values, y = vals.values, palette = \"Reds\")\nplt.ylabel(\"# of images\", fontsize = 15)\nplt.xlabel(\"# of CVC tubes\", fontsize = 15)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### An image with all the three CVC categories True"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"a = X_train[X_train.apply(lambda x: x[\"CVC - Normal\"] == 1 and x[\"CVC - Borderline\"] == 1 and x[\"CVC - Abnormal\"] == 1, axis = 1)]\ncvc3_ex = a.iloc[0][\"StudyInstanceUID\"]\na.head(1)","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"visualize_annotations(f'{cvc3_ex}.jpg')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# NGT (Nasogastric tube)"},{"metadata":{},"cell_type":"markdown","source":"### Number of images containing different number of NGT tubes"},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"vals = pd.DataFrame(X_train[\"NGT - Normal\"] + X_train[\"NGT - Abnormal\"] + X_train[\"NGT - Borderline\"] + X_train[\"NGT - Incompletely Imaged\"]).value_counts().sort_index(ascending = True)\nvals = pd.Series(vals.values).sort_values(ascending = False)\nplt.figure(figsize = (10, 5))\nsns.barplot(x = vals.index.values, y = vals.values, palette = \"Greens\")\nplt.ylabel(\"# of images\", fontsize = 15)\nplt.xlabel(\"# of NGT tubes\", fontsize = 15)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Interesting, there are 4 columns corresponding to NGT tube placement, but there is no image with more than 2 NGT tubes. And most of the images (21775) don't have NGT tube at all. What NGT tubes appear together?"},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"X_train[X_train[\"NGT - Normal\"] + X_train[\"NGT - Abnormal\"] + X_train[\"NGT - Borderline\"] + X_train[\"NGT - Incompletely Imaged\"] == 2].head()[ngt_target]","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Ah, it seems that there is no correlation between NGT tubes"},{"metadata":{},"cell_type":"markdown","source":"# ETT (Endotracheal tube)"},{"metadata":{},"cell_type":"markdown","source":"## How many ETT tube can train image contain?"},{"metadata":{"trusted":true},"cell_type":"code","source":"vals = pd.DataFrame(X_train[\"ETT - Normal\"] + X_train[\"ETT - Abnormal\"] + X_train[\"ETT - Borderline\"]).value_counts().sort_index(ascending = True)\nvals = pd.Series(vals.values).sort_values(ascending = False)\nplt.figure(figsize = (10, 5))\nsns.barplot(x = vals.index.values, y = vals.values, palette = \"Blues\")\nplt.ylabel(\"# of images\", fontsize = 15)\nplt.xlabel(\"# of ETT tubes\", fontsize = 15)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Majority of the images (21626) does not contain ETT tube. Remaining, contain only one ETT tube. Indeed, only one ETT tube can be placed in patient."},{"metadata":{},"cell_type":"markdown","source":"## How to split training data into k fold CV?"},{"metadata":{"trusted":true,"_kg_hide-input":false,"_kg_hide-output":true},"cell_type":"code","source":"folds = []\ngroup_kfold = GroupKFold(n_splits=5)\n\nfor train_index, test_index in group_kfold.split(X_train, X_train[X_train.columns[1:12]], X_train[\"PatientID\"]):\n    folds.append(X_train.iloc[test_index])","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-output":true,"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"for i, fold in enumerate(folds):\n    fold['fold'] = f'fold{i + 1}'","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### How many images are there in each of the folds?"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"plt.figure(figsize = (10, 5))\nsns.barplot(y = [\"fold1\", \"fold2\", \"fold3\", \"fold4\", \"fold5\"], x = [folds[i].shape[0] for i in [0, 1, 2, 3, 4]], palette = \"Purples\")\nplt.xlabel(\"# of images\" ,fontsize = 15)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Number of positive target classes in each of the folds"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"df = pd.concat([folds[0], folds[1], folds[2], folds[3], folds[4]])\n\nd = pd.DataFrame(columns=[\"placement\", \"fold\"])\nfor col in target_cols:\n    for fold in folds:\n        a = fold[fold[col] == 1][[col, \"fold\"]]\n        a = a.rename(columns = {col:\"placement\"})\n        a[\"placement\"] = col\n        d = pd.concat([d, a])\n\nplt.figure(figsize = (15,5))\nax = sns.countplot(x = \"placement\", hue = \"fold\", data = d)\nax.set_xticklabels(ax.get_xticklabels(), rotation = 40, ha = \"right\")\nplt.tight_layout()\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Future work\n* To look at correlation of classes in each of the folds. I think, this can lead to overfitting issue if the correlations are different."},{"metadata":{},"cell_type":"markdown","source":"## Don't forget to upvote if you find this notebook helpful :)"}],"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}