.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/b51f6b55f5db318243678fd2bb265fe742e18a0d484709e02b9b81166654cd60.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/ce524bd2769a00a725b5c78cca187406cd7d3f93b721546e58dbd8ee9fe93b06.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/b83a9af5528604d2ceb36fe86e342ce53a4cf0787a89e7a921e3a7edd0513afe.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/bc0cd7eab50be5394d3e535f5975ca950a2a1c2378755a3a00c7651a90a4f73f.png

Plot explained variance#

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

See also