.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/cdd39af69e63d8dbf7911e152b68d28f957bdafc73548bb04013c7a4170309ad.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/17cee5f023e61f53d835d69c9f16826616376f18e2b36ca4dcfcbf73d1750a4e.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/c9a42fbbfaab792d74b34411dcaf15808e8146c969c1691cae66590caf214d11.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/cfdd7fac58fffbac6c800ef5fae142255be6664f98b3e678fcfc35b2b3c49ec9.png

Plot explained variance#

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

See also