{ "cells": [ { "cell_type": "markdown", "id": "a669fa8e", "metadata": {}, "source": [ "# Elliptic Fourier Analysis" ] }, { "cell_type": "code", "execution_count": null, "id": "54596e87", "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "import pandas as pd\n", "\n", "import matplotlib.pyplot as plt\n", "import seaborn as sns\n", "\n", "from sklearn.decomposition import PCA\n", "\n", "from ktch.datasets import load_outline_mosquito_wings\n", "from ktch.outline import EllipticFourierAnalysis" ] }, { "cell_type": "markdown", "id": "b230a097", "metadata": {}, "source": [ "## Load mosquito wing outline dataset\n", "from Rohlf and Archie 1984 _Syst. Zool._" ] }, { "cell_type": "code", "execution_count": null, "id": "aae20408", "metadata": {}, "outputs": [], "source": [ "data_outline_mosquito_wings = load_outline_mosquito_wings(as_frame=True)\n", "data_outline_mosquito_wings.coords" ] }, { "cell_type": "code", "execution_count": null, "id": "13805be1", "metadata": {}, "outputs": [], "source": [ "coords = data_outline_mosquito_wings.coords.to_numpy().reshape(-1,100,2)" ] }, { "cell_type": "code", "execution_count": null, "id": "d90a7ac3", "metadata": {}, "outputs": [], "source": [ "fig, ax = plt.subplots()\n", "sns.lineplot(x=coords[0][:,0], y=coords[0][:,1], sort= False,estimator=None,ax=ax)\n", "ax.set_aspect('equal')" ] }, { "cell_type": "code", "execution_count": null, "id": "25460180", "metadata": {}, "outputs": [], "source": [ "fig, ax = plt.subplots(figsize=(10,10))\n", "sns.lineplot(\n", " data= data_outline_mosquito_wings.coords,x=\"x\", y=\"y\",\n", " hue=\"specimen_id\",\n", " sort= False,estimator=None,ax=ax)\n", "ax.set_aspect('equal')" ] }, { "cell_type": "markdown", "id": "d115ff1c", "metadata": {}, "source": [ "## EFA" ] }, { "cell_type": "code", "execution_count": null, "id": "3d6fa361", "metadata": {}, "outputs": [], "source": [ "efa = EllipticFourierAnalysis(n_harmonics=20)" ] }, { "cell_type": "code", "execution_count": null, "id": "4255a1c8", "metadata": {}, "outputs": [], "source": [ "coef = efa.fit_transform(coords)" ] }, { "cell_type": "markdown", "id": "0d43a830", "metadata": {}, "source": [ "## PCA" ] }, { "cell_type": "code", "execution_count": null, "id": "5742c421", "metadata": {}, "outputs": [], "source": [ "pca = PCA(n_components=12)\n", "pcscores = pca.fit_transform(coef)" ] }, { "cell_type": "code", "execution_count": null, "id": "7cca3580", "metadata": {}, "outputs": [], "source": [ "df_pca = pd.DataFrame(pcscores)\n", "df_pca[\"specimen_id\"] = [i for i in range(1,len(pcscores)+1)]\n", "df_pca = df_pca.set_index(\"specimen_id\")\n", "df_pca = df_pca.join(data_outline_mosquito_wings.meta)\n", "df_pca = df_pca.rename(columns={i:(\"PC\"+str(i+1)) for i in range(12)})\n", "df_pca" ] }, { "cell_type": "code", "execution_count": null, "id": "eb3bdc02", "metadata": {}, "outputs": [], "source": [ "fig, ax = plt.subplots()\n", "sns.scatterplot(data=df_pca, x=\"PC1\", y = \"PC2\", hue=\"genus\", ax=ax, palette=\"Paired\")" ] }, { "cell_type": "markdown", "id": "b176777b", "metadata": {}, "source": [ "## Morphospace" ] }, { "cell_type": "code", "execution_count": null, "id": "1e4c6c7b", "metadata": {}, "outputs": [], "source": [ "def get_pc_scores_for_morphospace(ax, num = 5):\n", " xrange = np.linspace(ax.get_xlim()[0], ax.get_xlim()[1],num)\n", " yrange = np.linspace(ax.get_ylim()[0], ax.get_ylim()[1],num)\n", " return xrange, yrange\n", "\n", "def plot_recon_morphs(pca, efa, hue, hue_order, fig, ax, \n", " n_PCs_xy = [1, 2], lmax = 20, morph_num = 3, morph_scale = 1.0, morph_color = \"lightgray\", morph_alpha = 0.7, \n", " standardized_by_1st_ellipsoid = False):\n", " \n", " pc_scores_h, pc_scores_v = get_pc_scores_for_morphospace(ax, morph_num)\n", " print(\"PC_h: \", pc_scores_h, \", PC_v: \", pc_scores_v)\n", " for pc_score_h in pc_scores_h:\n", " for pc_score_v in pc_scores_v:\n", " pc_score = np.zeros(pca.n_components_)\n", " n_PC_h, n_PC_v = n_PCs_xy\n", " pc_score[n_PC_h-1] = pc_score_h\n", " pc_score[n_PC_v-1] = pc_score_v\n", "\n", " arr_coef = pca.inverse_transform([pc_score])\n", "\n", " \n", " ax_width = ax.get_window_extent().width\n", " fig_width = fig.get_window_extent().width\n", " fig_height = fig.get_window_extent().height\n", " morph_size = morph_scale*ax_width/(fig_width*morph_num)\n", " loc = ax.transData.transform((pc_score_h, pc_score_v))\n", " axins = fig.add_axes([loc[0]/fig_width-morph_size/2, loc[1]/fig_height-morph_size/2,\n", " morph_size, morph_size], anchor='C')\n", " \n", " coords = efa.inverse_transform(arr_coef)\n", " x = coords[0][:,0]\n", " y = coords[0][:,1]\n", " \n", " axins.plot(x.astype(float),y.astype(float),color=morph_color, alpha = morph_alpha)\n", " axins.axis('equal')\n", " axins.axis('off') " ] }, { "cell_type": "code", "execution_count": null, "id": "714ff8c0", "metadata": {}, "outputs": [], "source": [ "fig = plt.figure(figsize=(16,16),dpi=200)\n", "\n", "ax = fig.add_subplot(2,2,1)\n", "sns.scatterplot(data=df_pca, x=\"PC1\", y=\"PC2\", hue=\"genus\", hue_order=None, \n", " palette=\"Paired\", ax = ax, legend = True)\n", "\n", "plot_recon_morphs(pca,efa,morph_num=5,morph_scale=0.5,\n", " hue=None,hue_order=None, \n", " fig = fig, ax = ax)" ] }, { "cell_type": "code", "execution_count": null, "id": "cf6b6ced", "metadata": {}, "outputs": [], "source": [ "morph_num = 5\n", "morph_scale = 0.8\n", "morph_color = \"gray\"\n", "morph_alpha = 0.8\n", "\n", "fig = plt.figure(figsize=(16,16),dpi=200)\n", "\n", "#########\n", "# PC1\n", "#########\n", "ax = fig.add_subplot(2,2,1)\n", "sns.scatterplot(data=df_pca, x=\"PC1\", y=\"PC2\", hue=\"genus\", hue_order=None, \n", " palette=\"Paired\", ax = ax, legend = True)\n", "\n", "plot_recon_morphs(pca,efa,morph_num=5,morph_scale=morph_scale,\n", " hue=None,hue_order=None, \n", " morph_color=morph_color, morph_alpha=morph_alpha,\n", " fig = fig, ax = ax)\n", "\n", "sns.scatterplot(data=df_pca, x=\"PC1\", y=\"PC2\", hue=\"genus\", hue_order=None, \n", " palette=\"Paired\", ax = ax)\n", "\n", "ax.patch.set_alpha(0)\n", "ax.set(xlabel='PC1', ylabel='PC2')\n", "\n", "print(\"PC1-PC2 done\")\n", "\n", "#########\n", "# PC2\n", "#########\n", "ax = fig.add_subplot(2,2,2)\n", "sns.scatterplot(data=df_pca, x=\"PC2\", y=\"PC3\", hue=\"genus\", hue_order=None, \n", " palette=\"Paired\", ax = ax, legend = True)\n", "\n", "plot_recon_morphs(pca,efa,morph_num=5,morph_scale=morph_scale,\n", " hue=None,hue_order=None, \n", " morph_color=morph_color, morph_alpha=morph_alpha,\n", " fig = fig, ax = ax, n_PCs_xy = [2, 3])\n", "\n", "sns.scatterplot(data=df_pca, x=\"PC2\", y=\"PC3\", hue=\"genus\", hue_order=None, \n", " palette=\"Paired\", ax = ax)\n", "\n", "ax.patch.set_alpha(0)\n", "ax.set(xlabel='PC2', ylabel='PC3')\n", "\n", "print(\"PC2-PC3 done\")\n", "\n", "#########\n", "# PC3\n", "#########\n", "ax=fig.add_subplot(2,2,3)\n", "sns.scatterplot(data=df_pca, x=\"PC3\", y=\"PC1\", hue=\"genus\", hue_order=None, \n", " palette=\"Paired\", ax = ax, legend = True)\n", "\n", "plot_recon_morphs(pca,efa,morph_num=5,morph_scale=morph_scale,\n", " hue=None,hue_order=None, \n", " morph_color=morph_color, morph_alpha=morph_alpha,\n", " fig = fig, ax = ax, n_PCs_xy = [3, 1])\n", "\n", "sns.scatterplot(data=df_pca, x=\"PC3\", y=\"PC1\", hue=\"genus\", hue_order=None, \n", " palette=\"Paired\", ax = ax)\n", "\n", "ax.patch.set_alpha(0)\n", "ax.set(xlabel='PC3', ylabel='PC1')\n", "\n", "print(\"PC3-PC1 done\")\n", "\n", "#########\n", "# CCR\n", "#########\n", "\n", "fig.add_subplot(2,2,4)\n", "sns.barplot(x=[\"PC\"+str(i+1) for i in range(12)], y=pca.explained_variance_ratio_[0:16], color=\"gray\")" ] }, { "cell_type": "code", "execution_count": null, "id": "75599d43", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" } }, "nbformat": 4, "nbformat_minor": 5 }