.ipynb

Visualize Morphospace#

Plot specimens in reduced space with optional shape overlays.

Setup#

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.decomposition import PCA

from ktch.datasets import load_outline_mosquito_wings
from ktch.harmonic import EllipticFourierAnalysis
from ktch.plot import explained_variance_ratio_plot, morphospace_plot

data = load_outline_mosquito_wings(as_frame=True)
coords = data.coords.to_numpy().reshape(-1, 100, 2)

efa = EllipticFourierAnalysis(n_harmonics=20)
coef = efa.fit_transform(coords)

pca = PCA(n_components=5)
scores = pca.fit_transform(coef)

df_pca = pd.DataFrame(scores, columns=[f"PC{i + 1}" for i in range(5)])
df_pca.index = data.meta.index
df_pca = df_pca.join(data.meta)

Basic scatter plot#

fig, ax = plt.subplots()
sns.scatterplot(data=df_pca, x="PC1", y="PC2", hue="genus", palette="Paired", ax=ax)
ax.set_aspect("equal")
../../../_images/513f9a7e81301878fd3c2ebd0d1e0e0633a654c1df9735229c7d12a73713a7fd.png

Morphospace with shape overlays#

ax = morphospace_plot(
    data=df_pca,
    x="PC1", y="PC2", hue="genus",
    reducer=pca,
    descriptor=efa,
    palette="Paired",
    n_shapes=5,
    shape_scale=0.5,
)
../../../_images/631b90d7dceace0a6c864dc9667993c5824a28974e544ad5b2e2820db53c62f5.png

Multiple component pairs#

fig, axes = plt.subplots(2, 2, figsize=(16, 16), dpi=200)

for ax, (i, j) in zip(axes.flat[:3], [(0, 1), (1, 2), (2, 0)]):
    morphospace_plot(
        data=df_pca,
        x=f"PC{i + 1}", y=f"PC{j + 1}", hue="genus",
        reducer=pca,
        descriptor=efa,
        components=(i, j),
        palette="Paired",
        n_shapes=5,
        shape_color="gray",
        shape_scale=0.8,
        shape_alpha=0.8,
        ax=ax,
    )

explained_variance_ratio_plot(pca, ax=axes[1, 1])
<Axes: >
../../../_images/23c04515f38e6bb1c46d6e9b42c9190601f8c85c80333cdfbef8ee70ebb162d7.png

Compose with existing axes#

When called without data/x/y, morphospace_plot skips the scatter step and adds only shape overlays, using the current axis limits to position them. Pass the pre-populated axes via ax.

fig, ax = plt.subplots(figsize=(10, 10))
sns.scatterplot(
    data=df_pca, x="PC1", y="PC2", hue="genus", palette="Paired", ax=ax, s=80,
)
morphospace_plot(
    reducer=pca,
    descriptor=efa,
    components=(0, 1),
    ax=ax,
)
<Axes: xlabel='PC1', ylabel='PC2'>
../../../_images/a3954c0ac4ddb30a5d1648a4aeda5e07e31371e84129a3d54ad822c38cb9134c.png

Plot explained variance#

fig, ax = plt.subplots(figsize=(8, 4))
explained_variance_ratio_plot(pca, ax=ax)
<Axes: >
../../../_images/e3ffc5918d99c42e9fe2f9960700545b3c81ac707f7388f37f99cdfeb5046833.png

See also