{"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":"## Abstract\n\nIn this notebook, I tried basic EDA.\nIn the latter part, I made animated GIF of sampled landmarks.","metadata":{}},{"cell_type":"code","source":"!pip install nb-black","metadata":{"_kg_hide-output":true,"execution":{"iopub.status.busy":"2023-03-16T09:59:27.967917Z","iopub.execute_input":"2023-03-16T09:59:27.968302Z","iopub.status.idle":"2023-03-16T09:59:43.116697Z","shell.execute_reply.started":"2023-03-16T09:59:27.968268Z","shell.execute_reply":"2023-03-16T09:59:43.115764Z"},"collapsed":true,"jupyter":{"outputs_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from typing import Dict\nfrom pathlib import Path\nfrom types import SimpleNamespace\nimport polars as pl\nimport matplotlib.pyplot as plt\nimport matplotlib.animation as animation\nfrom IPython.core.display import Image\nfrom tqdm import tqdm\n\nplt.style.use(\"ggplot\")\n\ncfg = SimpleNamespace()\ncfg.INPUT = Path(\"/kaggle/input/asl-signs\")\ncfg.OUTPUT = Path(\"/kaggle/working/animation\")\n\nif not cfg.OUTPUT.exists():\n    cfg.OUTPUT.mkdir()\n\n# %load_ext lab_black\n%load_ext autoreload\n%autoreload 2","metadata":{"_kg_hide-input":false,"execution":{"iopub.status.busy":"2023-03-16T13:04:15.320097Z","iopub.execute_input":"2023-03-16T13:04:15.320666Z","iopub.status.idle":"2023-03-16T13:04:15.382718Z","shell.execute_reply.started":"2023-03-16T13:04:15.320616Z","shell.execute_reply":"2023-03-16T13:04:15.381381Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train = pl.read_csv(cfg.INPUT / \"train.csv\")","metadata":{"_kg_hide-input":false,"execution":{"iopub.status.busy":"2023-03-16T13:04:19.344829Z","iopub.execute_input":"2023-03-16T13:04:19.34565Z","iopub.status.idle":"2023-03-16T13:04:19.533436Z","shell.execute_reply.started":"2023-03-16T13:04:19.345599Z","shell.execute_reply":"2023-03-16T13:04:19.532356Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(f\"#unique participants: {len(train['participant_id'].unique())}\")\nprint(f\"#unique sequence: {len(train['sequence_id'].unique()):,}\")\nprint(f\"#unique signs: {len(train['sign'].unique()):,}\")","metadata":{"_kg_hide-input":false,"execution":{"iopub.status.busy":"2023-03-16T13:09:27.996131Z","iopub.execute_input":"2023-03-16T13:09:27.996821Z","iopub.status.idle":"2023-03-16T13:09:28.059712Z","shell.execute_reply.started":"2023-03-16T13:09:27.996739Z","shell.execute_reply":"2023-03-16T13:09:28.057901Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sequence_per_participant = (\n    train.groupby(\"participant_id\")\n    .agg(pl.col(\"sequence_id\").unique().count())\n    .with_columns(pl.col(\"participant_id\").cast(str))\n)\n\n_, ax = plt.subplots()\nax.barh(\n    y=sequence_per_participant[\"participant_id\"],\n    width=sequence_per_participant[\"sequence_id\"],\n)\nax.set(xlabel=\"#sequences\", ylabel=\"participant_id\", title=\"#sequences per participant\")\nplt.show()","metadata":{"_kg_hide-input":false,"execution":{"iopub.status.busy":"2023-03-16T13:29:43.508734Z","iopub.execute_input":"2023-03-16T13:29:43.509319Z","iopub.status.idle":"2023-03-16T13:29:43.929915Z","shell.execute_reply.started":"2023-03-16T13:29:43.509244Z","shell.execute_reply":"2023-03-16T13:29:43.928549Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sequence_per_sign = (\n    train.groupby(\"sign\").agg(pl.col(\"sequence_id\").count()).sort(\"sequence_id\")\n)\n_, ax = plt.subplots()\nax.hist(sequence_per_sign[\"sequence_id\"], bins=20)\nax.set(xlabel=\"#sequences/sign\", ylabel=\"count\", title=\"#sequences per sign\")\nplt.show()","metadata":{"_kg_hide-input":false,"execution":{"iopub.status.busy":"2023-03-15T18:08:39.990285Z","iopub.execute_input":"2023-03-15T18:08:39.990655Z","iopub.status.idle":"2023-03-15T18:08:40.275941Z","shell.execute_reply.started":"2023-03-15T18:08:39.990618Z","shell.execute_reply":"2023-03-15T18:08:40.274945Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Skeletical data","metadata":{}},{"cell_type":"code","source":"edges = {\n    \"left_hand\": [\n        (0, 1),\n        (1, 2),\n        (2, 3),\n        (3, 4),\n        (0, 5),\n        (0, 17),\n        (5, 6),\n        (6, 7),\n        (7, 8),\n        (5, 9),\n        (9, 10),\n        (10, 11),\n        (11, 12),\n        (9, 13),\n        (13, 14),\n        (14, 15),\n        (15, 16),\n        (13, 17),\n        (17, 18),\n        (18, 19),\n        (19, 20),\n    ],\n    \"right_hand\": [\n        (0, 1),\n        (1, 2),\n        (2, 3),\n        (3, 4),\n        (0, 5),\n        (0, 17),\n        (5, 6),\n        (6, 7),\n        (7, 8),\n        (5, 9),\n        (9, 10),\n        (10, 11),\n        (11, 12),\n        (9, 13),\n        (13, 14),\n        (14, 15),\n        (15, 16),\n        (13, 17),\n        (17, 18),\n        (18, 19),\n        (19, 20),\n    ],\n    \"pose\": [\n        (8, 6),\n        (6, 5),\n        (6, 4),\n        (4, 0),\n        (0, 1),\n        (1, 2),\n        (2, 3),\n        (3, 7),\n        (10, 9),\n        #\n        (11, 12),\n        (11, 13),\n        (11, 23),\n        (13, 15),\n        (15, 21),\n        (15, 17),\n        (15, 19),\n        (17, 19),\n        (12, 14),\n        (12, 24),\n        (14, 16),\n        (16, 22),\n        (16, 20),\n        (16, 18),\n        (18, 20),\n        (23, 24),\n        (24, 26),\n        (26, 28),\n        (28, 30),\n        (28, 32),\n        (30, 32),\n        (23, 25),\n        (25, 27),\n        (27, 29),\n        (27, 31),\n        (29, 31),\n    ],\n}","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2023-03-15T18:08:40.277317Z","iopub.execute_input":"2023-03-15T18:08:40.277724Z","iopub.status.idle":"2023-03-15T18:08:40.339896Z","shell.execute_reply.started":"2023-03-15T18:08:40.277687Z","shell.execute_reply":"2023-03-15T18:08:40.338793Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"lm_data = {k: v for k, v in zip(train.columns, train.row(0))}\n\ndf_landmark = pl.read_parquet(cfg.INPUT / lm_data[\"path\"])\nlm_first_frame = df_landmark.partition_by(\"frame\")[0]\nlms = lm_first_frame.partition_by(\"type\")\n\n_, axes = plt.subplots(2, 2, figsize=(8, 8))\naxes = axes.ravel()\n\nfor lm, ax in zip(lms, axes):\n    lm_type = lm.row(0)[2]\n\n    ax.scatter(lm[\"x\"], 1 - lm[\"y\"])\n    if lm_type != \"face\":\n        for row in lm.iter_rows():\n            dt = {k: v for k, v in zip(lm.columns, row)}\n            x, y, idx = dt[\"x\"], dt[\"y\"], dt[\"landmark_index\"]\n\n            if (x is not None) & (y is not None):\n                ax.text(x, 1 - y, idx)\n    if lm_type in [\"left_hand\", \"right_hand\", \"pose\"]:\n        for edge in edges[lm_type]:\n            i, j = edge\n            x1, x2, y1, y2 = lm[\"x\"][i], lm[\"x\"][j], lm[\"y\"][i], lm[\"y\"][j]\n            if not ((x1 is None) | (x2 is None) | (y1 is None) | (y2 is None)):\n                ax.plot((x1, x2), (1 - y1, 1 - y2), color=\"gray\")\n    ax.set(title=f\"landmark: {lm_type}\")","metadata":{"_kg_hide-input":false,"execution":{"iopub.status.busy":"2023-03-15T18:08:40.341791Z","iopub.execute_input":"2023-03-15T18:08:40.342529Z","iopub.status.idle":"2023-03-15T18:08:41.502537Z","shell.execute_reply.started":"2023-03-15T18:08:40.342489Z","shell.execute_reply":"2023-03-15T18:08:41.501258Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"_, axes = plt.subplots(1, 3, figsize=(12, 4))\n\nfor ax, col in zip(axes, [\"x\", \"y\", \"z\"]):\n    ax.hist(df_landmark[col], bins=20, alpha=0.5, label=col)\n    ax.set(xlabel=col)\nplt.suptitle(\"Distribution of axes of normalized coordinate\")\nplt.show()","metadata":{"_kg_hide-input":false,"execution":{"iopub.status.busy":"2023-03-15T18:08:41.504114Z","iopub.execute_input":"2023-03-15T18:08:41.505221Z","iopub.status.idle":"2023-03-15T18:08:42.06225Z","shell.execute_reply.started":"2023-03-15T18:08:41.505156Z","shell.execute_reply":"2023-03-15T18:08:42.060811Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Making Amination","metadata":{}},{"cell_type":"code","source":"def make_animation(data: Dict, fps: int = 60):\n    sign, participant_id, sequence_id = (\n        data[\"sign\"],\n        data[\"participant_id\"],\n        data[\"sequence_id\"],\n    )\n\n    df_landmark = pl.read_parquet(cfg.INPUT / data[\"path\"])\n    df_landmark = df_landmark.sort([\"frame\", \"type\", \"landmark_index\"])\n\n    fig, axes = plt.subplots(2, 2, figsize=(8, 8))\n    axes = axes.ravel()\n\n    lms_all = df_landmark.partition_by(\"frame\")\n\n    def draw_frame(frame):\n        lms = lms_all[frame].partition_by(\"type\")\n\n        for lm, ax in zip(lms, axes):\n            ax.cla()\n            lm_type = lm.row(0)[2]\n\n            ax.scatter(lm[\"x\"], 1 - lm[\"y\"])\n            if lm_type != \"face\":\n                for row in lm.iter_rows():\n                    dt = {k: v for k, v in zip(lm.columns, row)}\n                    if (dt[\"x\"] is not None) & (dt[\"y\"] is not None):\n                        ax.text(dt[\"x\"], 1 - dt[\"y\"], dt[\"landmark_index\"])\n            if lm_type in [\"left_hand\", \"right_hand\", \"pose\"]:\n                for edge in edges[lm_type]:\n                    i, j = edge\n                    x1, x2, y1, y2 = lm[\"x\"][i], lm[\"x\"][j], lm[\"y\"][i], lm[\"y\"][j]\n                    if not ((x1 is None) | (x2 is None) | (y1 is None) | (y2 is None)):\n                        ax.plot((x1, x2), (1 - y1, 1 - y2), color=\"gray\")\n            ax.set(title=f\"{lm_type}\")\n        plt.suptitle(f'sign: \"{sign}\"')\n\n    ani = animation.FuncAnimation(\n        fig, draw_frame, frames=range(len(lms_all)), interval=1000 / fps\n    )\n\n    if not (cfg.OUTPUT / sign).exists():\n        (cfg.OUTPUT / sign).mkdir()\n    ani.save(\n        cfg.OUTPUT / sign / f\"{participant_id}_{sequence_id}.gif\",\n        writer=\"pillow\",\n        fps=fps,\n    )\n    plt.close(fig)","metadata":{"_kg_hide-input":false,"execution":{"iopub.status.busy":"2023-03-15T18:41:57.448929Z","iopub.execute_input":"2023-03-15T18:41:57.449453Z","iopub.status.idle":"2023-03-15T18:41:57.533114Z","shell.execute_reply.started":"2023-03-15T18:41:57.449411Z","shell.execute_reply":"2023-03-15T18:41:57.531884Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Choose 5 samples per each group","metadata":{}},{"cell_type":"code","source":"train_unique_signs = train.filter(\n    (pl.arange(0, pl.count())).shuffle(seed=42).over(\"sign\") < 5\n)","metadata":{"execution":{"iopub.status.busy":"2023-03-15T18:41:58.377032Z","iopub.execute_input":"2023-03-15T18:41:58.377515Z","iopub.status.idle":"2023-03-15T18:41:58.426945Z","shell.execute_reply.started":"2023-03-15T18:41:58.377472Z","shell.execute_reply":"2023-03-15T18:41:58.425643Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_unique_signs.groupby(\"sign\").agg(pl.count()).head()","metadata":{"execution":{"iopub.status.busy":"2023-03-15T18:41:58.854428Z","iopub.execute_input":"2023-03-15T18:41:58.855582Z","iopub.status.idle":"2023-03-15T18:41:58.895955Z","shell.execute_reply.started":"2023-03-15T18:41:58.855523Z","shell.execute_reply":"2023-03-15T18:41:58.894389Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"for row in tqdm(train_unique_signs.iter_rows(), total=len(train_unique_signs)):\n    columns = train_unique_signs.columns\n    data = {k: v for k, v in zip(columns, row)}\n    make_animation(data)","metadata":{"execution":{"iopub.status.busy":"2023-03-15T18:41:59.974044Z","iopub.execute_input":"2023-03-15T18:41:59.975174Z","iopub.status.idle":"2023-03-15T18:44:12.240473Z","shell.execute_reply.started":"2023-03-15T18:41:59.975121Z","shell.execute_reply":"2023-03-15T18:44:12.238124Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Generated Animations\n\nYou can access to the full 250 class of signs in the Data tab.\n\n<img src=\"./animation/TV/28656_125816896.gif\" width=500>\n<img src=\"./animation/brown/49445_1280132603.gif\" width=500>\n<img src=\"./animation/all/26734_1247514751.gif\" width=500>\n<img src=\"./animation/bug/61333_1268993802.gif\" width=500>\n<img src=\"./animation/brother/30680_1279123159.gif\" width=500>\n<img src=\"./animation/lion/29302_1271911796.gif\" width=500>","metadata":{}}]}