.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/0b54ad39bd2ddd8cea84b8d796974b1ffb7f0a7d8efc05a6e1854392ffb4e60f.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/52d0df41c104258e788dea28be7023c255d245353b457d8b6b75be52593c6457.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/1e917bf86373399f588b645cffa7ee20168f33b68ddaf0d9b897c850d0de2996.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/50e876e8e5a7d73fda77ac93abe80f1c583b865db78ae2de7bdfc5337cf59944.png

Plot explained variance#

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

See also