{"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":"code","source":"!pip install nb-black > /dev/null\n%load_ext lab_black","metadata":{"_kg_hide-input":true,"_kg_hide-output":true,"execution":{"iopub.execute_input":"2021-12-08T11:16:22.592723Z","iopub.status.busy":"2021-12-08T11:16:22.591551Z","iopub.status.idle":"2021-12-08T11:16:34.995948Z","shell.execute_reply":"2021-12-08T11:16:34.995096Z"},"papermill":{"duration":12.426351,"end_time":"2021-12-08T11:16:34.996139","exception":false,"start_time":"2021-12-08T11:16:22.569788","status":"completed"},"tags":[]},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nfrom itertools import cycle\nimport matplotlib.pylab as plt\nfrom matplotlib.patches import Rectangle\n\nplt.style.use(\"ggplot\")\ncolor_pal = plt.rcParams[\"axes.prop_cycle\"].by_key()[\"color\"]\ncolor_cycle = cycle(plt.rcParams[\"axes.prop_cycle\"].by_key()[\"color\"])","metadata":{"_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","execution":{"iopub.execute_input":"2021-12-08T11:16:35.042554Z","iopub.status.busy":"2021-12-08T11:16:35.041908Z","iopub.status.idle":"2021-12-08T11:16:35.056953Z","shell.execute_reply":"2021-12-08T11:16:35.056439Z","shell.execute_reply.started":"2021-12-08T10:59:56.470636Z"},"papermill":{"duration":0.0409,"end_time":"2021-12-08T11:16:35.057109","exception":false,"start_time":"2021-12-08T11:16:35.016209","status":"completed"},"tags":[]},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train = pd.read_csv(\"../input/tensorflow-great-barrier-reef/train.csv\")\ntest = pd.read_csv(\"../input/tensorflow-great-barrier-reef/test.csv\")\nss = pd.read_csv(\"../input/tensorflow-great-barrier-reef/example_sample_submission.csv\")\n\ntrain.shape, test.shape","metadata":{"execution":{"iopub.execute_input":"2021-12-08T11:16:35.098604Z","iopub.status.busy":"2021-12-08T11:16:35.09799Z","iopub.status.idle":"2021-12-08T11:16:35.170783Z","shell.execute_reply":"2021-12-08T11:16:35.171258Z","shell.execute_reply.started":"2021-12-08T10:59:56.505916Z"},"papermill":{"duration":0.094877,"end_time":"2021-12-08T11:16:35.171466","exception":false,"start_time":"2021-12-08T11:16:35.076589","status":"completed"},"tags":[]},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Add Additional Columns\n\n* `n_annotaions`: Number of annotations in the frame\n* `video_sequence`: `video_id` + '_' + `sequence`","metadata":{"papermill":{"duration":0.020094,"end_time":"2021-12-08T11:16:35.211686","exception":false,"start_time":"2021-12-08T11:16:35.191592","status":"completed"},"tags":[]}},{"cell_type":"code","source":"train[\"sum_cots\"] = train[\"annotations\"].apply(lambda x: len(eval(x)))\ntrain[\"video_sequence\"] = (\n    train[\"video_id\"].astype(\"str\") + \"_\" + train[\"sequence\"].astype(\"str\")\n)","metadata":{"execution":{"iopub.execute_input":"2021-12-08T11:16:35.254652Z","iopub.status.busy":"2021-12-08T11:16:35.253652Z","iopub.status.idle":"2021-12-08T11:16:35.585448Z","shell.execute_reply":"2021-12-08T11:16:35.584878Z","shell.execute_reply.started":"2021-12-08T10:59:56.595312Z"},"papermill":{"duration":0.354367,"end_time":"2021-12-08T11:16:35.585642","exception":false,"start_time":"2021-12-08T11:16:35.231275","status":"completed"},"tags":[]},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# How Naive GroupKFold Creates Unbalanced Folds","metadata":{"papermill":{"duration":0.019971,"end_time":"2021-12-08T11:16:35.626325","exception":false,"start_time":"2021-12-08T11:16:35.606354","status":"completed"},"tags":[]}},{"cell_type":"code","source":"def plot_folds(df):\n    df = df.copy()\n    plt.style.use('ggplot')\n    df = df.groupby('fold_id').agg(\n        sum_cots=('sum_cots', 'sum'), duration=('fold_id', 'count'))\n    df['mean_cots'] = df.sum_cots / df.duration\n    fig, axs = plt.subplots(1, 3, figsize=(15, 4))\n    df.sum_cots.plot(kind='bar', ax=axs[0])\n    df.duration.plot(kind='bar', ax=axs[1])\n    df.mean_cots.plot(kind='bar', ax=axs[2])\n    axs[0].set_title('#COTS')\n    axs[1].set_title('#Frames')\n    axs[2].set_title('#COTS/frame')\n    return df","metadata":{"execution":{"iopub.execute_input":"2021-12-08T11:16:35.676548Z","iopub.status.busy":"2021-12-08T11:16:35.675567Z","iopub.status.idle":"2021-12-08T11:16:35.694422Z","shell.execute_reply":"2021-12-08T11:16:35.693862Z","shell.execute_reply.started":"2021-12-08T11:03:44.107375Z"},"papermill":{"duration":0.048395,"end_time":"2021-12-08T11:16:35.69457","exception":false,"start_time":"2021-12-08T11:16:35.646175","status":"completed"},"tags":[]},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from sklearn.model_selection import GroupKFold\n\n\ndef allocate_group_k_fold(df, n_split):\n    df = df.copy()\n    kf = GroupKFold(n_splits=n_split)\n    df['fold_id'] = -1\n    for fold, (train_idx, val_idx) in enumerate(kf.split(df, groups=df.video_sequence)):\n        df.loc[val_idx, 'fold_id'] = fold\n    return df","metadata":{"execution":{"iopub.execute_input":"2021-12-08T11:16:35.741278Z","iopub.status.busy":"2021-12-08T11:16:35.740564Z","iopub.status.idle":"2021-12-08T11:16:36.72363Z","shell.execute_reply":"2021-12-08T11:16:36.724382Z","shell.execute_reply.started":"2021-12-08T11:11:16.924881Z"},"papermill":{"duration":1.010299,"end_time":"2021-12-08T11:16:36.724627","exception":false,"start_time":"2021-12-08T11:16:35.714328","status":"completed"},"tags":[]},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"n_split = 5\ndf = train.copy()\ndf = df.query('sum_cots > 0') # select only annotated frames\ndf.reset_index(inplace=True)\ngroup_k_alloc_df = allocate_group_k_fold(df, n_split)\ndf = plot_folds(group_k_alloc_df)\nplt.suptitle('Visualization of Statistics of each Folds - GroupKFold', fontsize=16)\ndf, df.std()","metadata":{"execution":{"iopub.execute_input":"2021-12-08T11:16:36.780505Z","iopub.status.busy":"2021-12-08T11:16:36.779804Z","iopub.status.idle":"2021-12-08T11:16:37.409611Z","shell.execute_reply":"2021-12-08T11:16:37.410186Z","shell.execute_reply.started":"2021-12-08T11:12:44.322896Z"},"papermill":{"duration":0.657696,"end_time":"2021-12-08T11:16:37.410396","exception":false,"start_time":"2021-12-08T11:16:36.7527","status":"completed"},"tags":[]},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"In the avobe graph, frame number are almost perfectly balanced, but **the number of COTSs are poorly balanced**.","metadata":{"papermill":{"duration":0.020454,"end_time":"2021-12-08T11:16:37.451954","exception":false,"start_time":"2021-12-08T11:16:37.4315","status":"completed"},"tags":[]}},{"cell_type":"markdown","source":"# Spliting Sequence into Tiny Sub-Sequences\n\nHypothesis:\n\n* two consecutive annotated frames may include the same individual COTS.\n* two frames in the same sequence don't include the same individual of COTS when these are sepalated by non-annotated frames. That means all the individuals of COTS are traced (annotated) consecutively.\n\nSo I introduced a simple sequence-spliting algorithm:\n1. for each sequence, add the label 'annotated' to each frames which means more than one annotation(s) are inclued.\n2. split the sequence into parts which have consecutive 'annotated' frames","metadata":{"papermill":{"duration":0.020818,"end_time":"2021-12-08T11:16:37.49355","exception":false,"start_time":"2021-12-08T11:16:37.472732","status":"completed"},"tags":[]}},{"cell_type":"code","source":"df = train.copy()\n\n# make annotated flag\ndf[\"annotated\"] = df[\"sum_cots\"].apply(lambda x: min(x, 1))\n\ndfs = []\n\n# calculate non-annotated frame sub_sequence\nfor i, d in df.groupby(\"video_id\"):\n    ad = d.groupby((d[\"annotated\"] != d[\"annotated\"].shift()).cumsum(), as_index=False)[\n        [\"video_frame\", \"annotated\", \"sum_cots\"]\n    ].agg(\n        annotated=(\"annotated\", \"first\"),\n        start_frame=(\"video_frame\", 'first'),\n        end_frame=(\"video_frame\", \"last\"),\n        sum_cots=(\"sum_cots\", \"sum\"),\n        mean_cots=(\"sum_cots\", \"mean\"),\n    )\n    ad[\"video_id\"] = i\n    dfs.append(ad)\n\ndf_annot = pd.concat(dfs)\ndf_annot[\"duration\"] = df_annot[\"end_frame\"] - df_annot[\"start_frame\"] + 1\nsub_sequence = df_annot.query(\"annotated == 1\")\n\nsub_sequence.reset_index(drop=True)\n\nlast_sub_sequence_end = -1\nsub_sequence_id = 0\nsub_sequence_ids = []\ncontinuous = False\nprev_video_id = 0\nfor idx, (\n    annotated,\n    start_frame,\n    end_frame,\n    sum_cots,\n    mean_cots,\n    video_id,\n    duration,\n) in sub_sequence.iterrows():\n    sub_sequence_ids.append(sub_sequence_id)\n    last_sub_sequence_end = end_frame\n    prev_video_id = video_id\n    if not (prev_video_id == video_id and last_sub_sequence_end + 1 == start_frame):\n        sub_sequence_id += 1\n\nsub_sequence.loc[:, \"sub_sequence_id\"] = sub_sequence_ids\nsub_sequence.drop('annotated', axis=1, inplace=True)\nsub_sequence.reset_index(drop=True, inplace=True)\nsub_sequence","metadata":{"execution":{"iopub.execute_input":"2021-12-08T11:16:37.539136Z","iopub.status.busy":"2021-12-08T11:16:37.538467Z","iopub.status.idle":"2021-12-08T11:16:37.676793Z","shell.execute_reply":"2021-12-08T11:16:37.676211Z","shell.execute_reply.started":"2021-12-08T11:12:56.384603Z"},"papermill":{"duration":0.162162,"end_time":"2021-12-08T11:16:37.676931","exception":false,"start_time":"2021-12-08T11:16:37.514769","status":"completed"},"tags":[]},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Visualize Sub-Sequences","metadata":{"papermill":{"duration":0.021133,"end_time":"2021-12-08T11:16:37.719743","exception":false,"start_time":"2021-12-08T11:16:37.69861","status":"completed"},"tags":[]}},{"cell_type":"code","source":"import matplotlib.patches as mpatches\n\n\nfig, axes = plt.subplots(3, 1, figsize=(15, 8), sharex=True, sharey=True)\naxes = axes.ravel()\nmax_annotation = df[\"sum_cots\"].max()\nfor i, d in df.groupby([\"video_id\", \"sequence\"]):\n    video_id = d[\"video_id\"].values[0]\n    ax = axes[video_id]\n    d.set_index(\"video_frame\")[\"sum_cots\"].apply(\n        lambda x: x / max_annotation\n    ).plot(ax=ax, c=\"black\", linewidth=0.5)\n\n    ax.set_title(f\"Video ID: {video_id}\")\n\n\n# visualize clippable interval\nfor (\n    annotated,\n    start_frame,\n    end_frame,\n    sum_cots,\n    mean_cots,\n    video_id,\n    duration,\n    sub_sequence_id,\n) in sub_sequence.itertuples():\n    ax = axes[int(video_id)]\n    rect = mpatches.Rectangle(\n        (start_frame, 0), duration, 1, alpha=0.3, facecolor='red'\n    )\n    ax.add_patch(rect)\n\nfig.suptitle(\"Sub-Sequences Visualized\", fontsize=15)\nplt.tight_layout()\nplt.show()","metadata":{"execution":{"iopub.execute_input":"2021-12-08T11:16:37.766752Z","iopub.status.busy":"2021-12-08T11:16:37.766113Z","iopub.status.idle":"2021-12-08T11:16:38.814177Z","shell.execute_reply":"2021-12-08T11:16:38.813605Z","shell.execute_reply.started":"2021-12-08T11:12:57.786392Z"},"papermill":{"duration":1.072578,"end_time":"2021-12-08T11:16:38.814311","exception":false,"start_time":"2021-12-08T11:16:37.741733","status":"completed"},"tags":[]},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Fold Split Algorithm (Sub-Sequence Allocation)\n\nTo balance the statistics (like number of COTS, frame length etc.) of folds, I used simle round-robin algorithm to allocate sub-sequences to each folds:\n\n1. sort sub_sequence in descending order for total COTS in the sub_sequences\n2. allocate sub_sequences for each folds in order","metadata":{"papermill":{"duration":0.023434,"end_time":"2021-12-08T11:16:38.862073","exception":false,"start_time":"2021-12-08T11:16:38.838639","status":"completed"},"tags":[]}},{"cell_type":"code","source":"def allocate_fold(df, n_split, key=\"sum_cots\"):\n    df = df.copy()\n    assert key in df.columns\n    df.sort_values(key, ascending=False, inplace=True)\n    df[\"fold_id\"] = -1\n    for fold_id in range(n_split):\n        index = df.iloc[fold_id::n_split].index\n        df.loc[index, \"fold_id\"] = fold_id\n\n    return df","metadata":{"execution":{"iopub.execute_input":"2021-12-08T11:16:38.919265Z","iopub.status.busy":"2021-12-08T11:16:38.918264Z","iopub.status.idle":"2021-12-08T11:16:38.92832Z","shell.execute_reply":"2021-12-08T11:16:38.928788Z","shell.execute_reply.started":"2021-12-08T11:12:58.868918Z"},"papermill":{"duration":0.042149,"end_time":"2021-12-08T11:16:38.928958","exception":false,"start_time":"2021-12-08T11:16:38.886809","status":"completed"},"tags":[]},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def plot_folds_sub_sequence(df):\n    df = df.copy()\n    plt.style.use('ggplot')\n    df = df.groupby('fold_id').agg(\n        sum_cots=('sum_cots', 'sum'), duration=('duration', 'sum'))\n    df['mean_cots'] = df.sum_cots / df.duration\n    fig, axs = plt.subplots(1, 3, figsize=(15, 5))\n    df.sum_cots.plot(kind='bar', ax=axs[0])\n    df.duration.plot(kind='bar', ax=axs[1])\n    df.mean_cots.plot(kind='bar', ax=axs[2])\n    axs[0].set_title('#COTS')\n    axs[1].set_title('#Frames')\n    axs[2].set_title('#COTS/frame')\n    \n    return df","metadata":{"execution":{"iopub.execute_input":"2021-12-08T11:16:38.986641Z","iopub.status.busy":"2021-12-08T11:16:38.983933Z","iopub.status.idle":"2021-12-08T11:16:39.002783Z","shell.execute_reply":"2021-12-08T11:16:39.003264Z","shell.execute_reply.started":"2021-12-08T11:14:33.485688Z"},"papermill":{"duration":0.050764,"end_time":"2021-12-08T11:16:39.003466","exception":false,"start_time":"2021-12-08T11:16:38.952702","status":"completed"},"tags":[]},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df = allocate_fold(sub_sequence, n_split=5)\ndf = plot_folds_sub_sequence(df)\nplt.suptitle('Statistics of Folds by Round-Robin Algorithm', fontsize=16)\nplt.tight_layout()\ndf, df.agg('std')","metadata":{"execution":{"iopub.execute_input":"2021-12-08T11:16:39.054394Z","iopub.status.busy":"2021-12-08T11:16:39.053783Z","iopub.status.idle":"2021-12-08T11:16:39.651632Z","shell.execute_reply":"2021-12-08T11:16:39.652105Z","shell.execute_reply.started":"2021-12-08T11:14:34.219719Z"},"papermill":{"duration":0.624556,"end_time":"2021-12-08T11:16:39.65231","exception":false,"start_time":"2021-12-08T11:16:39.027754","status":"completed"},"tags":[]},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"By splitting into subsequences and applying subsequence-allocation algorighm, the standard deviations of #COTS and #COTS/frame are decreased, which means the unbalance is mitigated.\n\n* std(#COTS): 955 -> 787\n* std(#COTS/frame): 0.89 -> 0.42","metadata":{"papermill":{"duration":0.02525,"end_time":"2021-12-08T11:16:39.703103","exception":false,"start_time":"2021-12-08T11:16:39.677853","status":"completed"},"tags":[]}},{"cell_type":"markdown","source":"## Find Optimal Number of Split\n\nSince the algorithm introduced before is dependent on the number of splits, I searched the most balanced split number.","metadata":{"papermill":{"duration":0.024858,"end_time":"2021-12-08T11:16:39.753495","exception":false,"start_time":"2021-12-08T11:16:39.728637","status":"completed"},"tags":[]}},{"cell_type":"code","source":"def calc_split_statistics(sub_sequence, n_split):\n    df = allocate_fold(sub_sequence, n_split)\n    df = df.groupby('fold_id').agg(\n    sum_cots=('sum_cots', 'sum'), duration=('duration', 'sum'))\n    df['mean_cots'] = df.sum_cots / df.duration\n    return df\n\n\ndeviations = {\"sum_cots\": [], \"duration\": [], \"mean_cots\": []}\nn_splits = np.arange(3, 11)\nfor i in n_splits:\n    data = calc_split_statistics(sub_sequence, i).std()\n    for key in data.keys():\n        deviations[key].append(data[key])\n\nfig, ax = plt.subplots(1, 3, figsize=(15, 4))\nfor i, key in enumerate(deviations.keys()):\n    ax[i].plot(n_splits, deviations[key], label=key)\n    ax[i].set_ylim(bottom=0)\n    ax[i].set_title(key)\n    ax[i].set_xlabel(\"n_splits\")\nplt.suptitle(\"Standard Deviation vs. #Splits\", fontsize=15)","metadata":{"execution":{"iopub.execute_input":"2021-12-08T11:16:39.806921Z","iopub.status.busy":"2021-12-08T11:16:39.806256Z","iopub.status.idle":"2021-12-08T11:16:40.441136Z","shell.execute_reply":"2021-12-08T11:16:40.440442Z","shell.execute_reply.started":"2021-12-08T11:13:01.534225Z"},"papermill":{"duration":0.66269,"end_time":"2021-12-08T11:16:40.441276","exception":false,"start_time":"2021-12-08T11:16:39.778586","status":"completed"},"tags":[]},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"In terms of minimizing standard deviation of #COTS, the best balanced setting is `n_split == 4`.","metadata":{"papermill":{"duration":0.026522,"end_time":"2021-12-08T11:16:40.496741","exception":false,"start_time":"2021-12-08T11:16:40.470219","status":"completed"},"tags":[]}},{"cell_type":"markdown","source":"# Visualization of Folds in Video Frames","metadata":{"papermill":{"duration":0.026055,"end_time":"2021-12-08T11:16:40.549633","exception":false,"start_time":"2021-12-08T11:16:40.523578","status":"completed"},"tags":[]}},{"cell_type":"code","source":"import matplotlib.patches as mpatches\n\n\nfig, axes = plt.subplots(3, 1, figsize=(15, 8), sharex=True, sharey=True)\naxes = axes.ravel()\n\ndf = train.copy()\nmax_annotation = df[\"sum_cots\"].max()\nfor i, d in df.groupby([\"video_id\", \"sequence\"]):\n    video_id = d[\"video_id\"].values[0]\n    ax = axes[video_id]\n    d.set_index(\"video_frame\")[\"sum_cots\"].apply(\n        lambda x: x / max_annotation\n    ).plot(ax=ax, c=\"black\", linewidth=0.5)\n\n    ax.set_title(f\"Video ID: {video_id}\")\n\n\nn_split = 4\ndf = allocate_fold(sub_sequence, n_split)\noof_colors = [\"red\", \"blue\", \"green\", \"yellow\"]\n# visualize clippable interval\nfor (\n    annotated,\n    start_frame,\n    end_frame,\n    sum_cots,\n    mean_cots,\n    video_id,\n    duration,\n    sub_sequence_id,\n    fold_id,\n) in df.itertuples():\n    ax = axes[int(video_id)]\n    rect = mpatches.Rectangle(\n        (start_frame, 0), duration, 1, alpha=0.3, facecolor=oof_colors[int(fold_id)]\n    )\n    ax.add_patch(rect)\n\nfig.suptitle(\"Fold-Splitted Sub-Sequences Visualized\", fontsize=15)\nplt.tight_layout()\nplt.show()","metadata":{"execution":{"iopub.execute_input":"2021-12-08T11:16:40.611983Z","iopub.status.busy":"2021-12-08T11:16:40.611328Z","iopub.status.idle":"2021-12-08T11:16:41.814816Z","shell.execute_reply":"2021-12-08T11:16:41.815411Z","shell.execute_reply.started":"2021-12-08T11:14:14.516498Z"},"papermill":{"duration":1.238915,"end_time":"2021-12-08T11:16:41.815593","exception":false,"start_time":"2021-12-08T11:16:40.576678","status":"completed"},"tags":[]},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Merge Fold Column to the Train Metadata ","metadata":{"papermill":{"duration":0.029433,"end_time":"2021-12-08T11:16:41.874333","exception":false,"start_time":"2021-12-08T11:16:41.8449","status":"completed"},"tags":[]}},{"cell_type":"code","source":"# concatenate sub_sequence table and train table\n\nn_split = 4\ndf = train.copy()\ndfs = []\nalloc_df = allocate_fold(sub_sequence, n_split)\nbb = alloc_df.copy()\nfor video_id, d in df.groupby(\"video_id\"):\n    a = d[\"video_frame\"].values\n    b = bb.query(\"video_id == @video_id\").drop(\"video_id\", axis=1)\n    sub_sequence_low = b[\"start_frame\"].values\n    sub_sequence_high = b[\"end_frame\"].values\n\n    i, j = np.where((a[:, None] >= sub_sequence_low) & (a[:, None] <= sub_sequence_high))\n    dfs.append(\n        pd.DataFrame(\n            np.column_stack([d.values[i], b.values[j]]),\n            columns=d.columns.append(b.columns),\n        )\n    )\n\ndf = pd.concat(dfs)\ndf = df.loc[:, ~df.columns.duplicated()] # remove duplicated columns\n\nfor column in alloc_df.columns:\n    if column != \"mean_cots\":\n        df[column] = df[column].astype(int)\n        \ndf.to_csv(\"train_metadata_ext.csv\", index=False)\ndf[:3]","metadata":{"execution":{"iopub.execute_input":"2021-12-08T11:16:41.938971Z","iopub.status.busy":"2021-12-08T11:16:41.937965Z","iopub.status.idle":"2021-12-08T11:16:42.090084Z","shell.execute_reply":"2021-12-08T11:16:42.089503Z","shell.execute_reply.started":"2021-12-08T11:13:04.330774Z"},"papermill":{"duration":0.185346,"end_time":"2021-12-08T11:16:42.090225","exception":false,"start_time":"2021-12-08T11:16:41.904879","status":"completed"},"tags":[]},"execution_count":null,"outputs":[]}]}