5.3 Adjacency spectral embedding

5.3 Adjacency spectral embedding#

mode = "svg"

import matplotlib

font = {'family' : 'Dejavu Sans',
        'weight' : 'normal',
        'size'   : 20}

matplotlib.rc('font', **font)

import matplotlib
from matplotlib import pyplot as plt
from graspologic.simulations import sbm
from graphbook_code import generate_sbm_pmtx, lpm_from_sbm
import numpy as np

n = 100
# construct the block matrix B as described above
B = np.array([[0.6, 0.1], 
              [0.1, 0.4]])

# sample a graph from SBM_{100}(tau, B)
np.random.seed(0)
A, zs = sbm(n=[n//2, n//2], p=B, return_labels=True)
zs = zs + 1 # shift to 1-index

X = lpm_from_sbm(zs, B)
P = generate_sbm_pmtx(zs, B)
from graphbook_code import lpm_heatmap, heatmap
import os

fig, axs = plt.subplots(1, 3, figsize=(14, 5), gridspec_kw={"width_ratios": [.5, 1.27, 1.27]})

lpm_heatmap(X, xtitle="Latent Dim.", xticks=[0.5, 1.5], xticklabels=[1, 2],
            yticks=[0.5, 49.5, 99.5], yticklabels=[1, 50, 100], ytitle="Node",
            ax=axs[0], title="(A) Latent positions")
heatmap(P, legend_title="Edge probability", ax=axs[1],
        xticks=[0.5, 49.5, 99.5], xticklabels=[1, 50, 100], xtitle="Node",
        yticks=[0.5, 49.5, 99.5], yticklabels=[1, 50, 100], ytitle="Node",
        title="(B) Probability matrix", vmin=0, vmax=1)
heatmap(A.astype(int), legend_title="Edge?", ax=axs[2],
        xticks=[0.5, 49.5, 99.5], xticklabels=[1, 50, 100], xtitle="Node",
        yticks=[0.5, 49.5, 99.5], yticklabels=[1, 50, 100], ytitle="Node",
        title="(C) Sampled adj. matrix")
fig.tight_layout()

os.makedirs("Figures", exist_ok=True)
fname = "ase_sbm_ex"
if mode != "png":
    os.makedirs(f"Figures/{mode:s}", exist_ok=True)
    fig.savefig(f"Figures/{mode:s}/{fname:s}.{mode:s}")

os.makedirs("Figures/png", exist_ok=True)
fig.savefig(f"Figures/png/{fname:s}.png")
../../_images/9b9e26bcab8fac9783895b00daadb2f152b552a91136c7853707c039beaeadaf.png
from graspologic.embed import AdjacencySpectralEmbed as ase

d = 2  # the latent dimensionality
# estimate the latent position matrix with ase
Xhat = ase(n_components=d, svd_seed=0).fit_transform(A)
Phat = Xhat @ Xhat.transpose()
fig, axs = plt.subplots(1, 4, figsize=(15, 5), gridspec_kw={"width_ratios": [.4, .5, 1, 1.27]})


lpm_heatmap(X, xtitle="Latent Dim.", xticks=[0.5, 1.5], xticklabels=[1, 2],
            yticks=[0.5, 49.5, 99.5], cbar=False, yticklabels=[1, 50, 100], ytitle="Node",
            ax=axs[0], title="(A) $X$", vmin=-.5, vmax=1)
lpm_heatmap(Xhat, xtitle="Latent Dim.", xticks=[0.5, 1.5], xticklabels=[1, 2],
            yticks=[0.5, 49.5, 99.5], yticklabels=[1, 50, 100], ytitle="Node",
            ax=axs[1], title="(B) $\hat X$", shrink=0.7, vmin=-.5, vmax=1)

heatmap(P, cbar=False, ax=axs[2],
        xticks=[0.5, 49.5, 99.5], xticklabels=[1, 50, 100], xtitle="Node",
        inner_hier_labels=zs, title="(C) $P = XX^\\top$", vmin=0, vmax=1)
heatmap(Phat, legend_title="Edge probability", ax=axs[3],
        xticks=[0.5, 49.5, 99.5], xticklabels=[1, 50, 100], xtitle="Node",
        inner_hier_labels=zs, title="(D) $\hat P = \\hat X \\hat X^\\top$", vmin=0, vmax=1)

fig.tight_layout()

fname = "ase_result"
if mode != "png":
    fig.savefig(f"Figures/{mode:s}/{fname:s}.{mode:s}")

fig.savefig(f"Figures/png/{fname:s}.png")
<>:9: SyntaxWarning: invalid escape sequence '\h'
<>:16: SyntaxWarning: invalid escape sequence '\h'
<>:9: SyntaxWarning: invalid escape sequence '\h'
<>:16: SyntaxWarning: invalid escape sequence '\h'
/tmp/ipykernel_4201/638704847.py:9: SyntaxWarning: invalid escape sequence '\h'
  ax=axs[1], title="(B) $\hat X$", shrink=0.7, vmin=-.5, vmax=1)
/tmp/ipykernel_4201/638704847.py:16: SyntaxWarning: invalid escape sequence '\h'
  inner_hier_labels=zs, title="(D) $\hat P = \\hat X \\hat X^\\top$", vmin=0, vmax=1)
../../_images/d18021934626eb46de716b9eb69ae88a1b62a1dc9c42a183d31fe83ba094a709.png
vtx_perm = np.random.choice(n, size=n, replace=False)

# reorder the adjacency matrix
Aperm = A[tuple([vtx_perm])] [:,vtx_perm]
# reorder the community assignment vector
zperm = np.array(zs)[vtx_perm]

# compute the estimated latent positions using the
# permuted adjacency matrix
Xhat_perm = ase(n_components=2, svd_seed=0).fit_transform(Aperm)
import matplotlib.gridspec as gridspec
import matplotlib.image as mpimg

fig, axs = plt.subplots(1, 2, figsize=(10, 5), gridspec_kw={"width_ratios": [1, .2]})

heatmap(Aperm.astype(int), legend_title="Edge?", ax=axs[0],
        xticks=[0.5, 49.5, 99.5], xticklabels=[1, 50, 100], xtitle="Node (Random Order)",
        yticks=[0.5, 49.5, 99.5], yticklabels=[1, 50, 100], ytitle="Node (Random Order)",
        title="(A) Permuted adj. matrix")
lpm_heatmap(Xhat_perm, xtitle="Latent Dim.", xticks=[0.5, 1.5], xticklabels=[1, 2],
            yticks=[0.5, 49.5, 99.5], yticklabels=[1, 50, 100], ytitle="Node (Random Order)",
            ax=axs[1], title="(B) $\hat X$ for permuted $A$", shrink=0.7, vmin=-.5, vmax=1)
fig.tight_layout()

fname = "ase_permutedab"
if mode != "png":
    fig.savefig(f"Figures/{mode:s}/{fname:s}.{mode:s}")

fig.savefig(f"Figures/png/{fname:s}.png")
<>:12: SyntaxWarning: invalid escape sequence '\h'
<>:12: SyntaxWarning: invalid escape sequence '\h'
/tmp/ipykernel_4201/2433709469.py:12: SyntaxWarning: invalid escape sequence '\h'
  ax=axs[1], title="(B) $\hat X$ for permuted $A$", shrink=0.7, vmin=-.5, vmax=1)
../../_images/8b83825fd9816a510d267a960f90f73cd2c5b3f4abf95c677b1f987f6db5d7d9.png
import pandas as pd

def tidy_lpm(X, labels=None, label_name="Community"):
    """
    A function which takes a latent position matrix X,
    and tidyfies it for seaborn. Optionally, adds a column
    for the community of the nodes.
    """
    X_tidy = pd.DataFrame(X)
    X_tidy.columns = ["Dimension {:d}".format(d + 1) for d in range(0, X.shape[1])]
    if labels is not None:
        X_tidy[label_name] = labels
    return X_tidy

Xhat_perm_tidy = tidy_lpm(Xhat_perm, labels=zperm)
import seaborn as sns

def pairplot(X, labels=None, label_name="Community", title=None, **kwargs):
    """
    A function which takes a latent position matrix X,
    and plots it as a pairplot with seaborn.
    """
    X_tidy = tidy_lpm(X, labels=labels, label_name=label_name)
    cols_to_pairplot = [col for col in X_tidy.columns.tolist() if "Dimension" in col]
    fig = sns.pairplot(Xhat_perm_tidy, vars=["Dimension 1", "Dimension 2"], **kwargs)
    # delete useless axis values
    for ax in fig.axes.flatten():
        ax.set_yticks([])
        ax.set_xticks([])
    if title is not None:
        fig.fig.suptitle(title, y=1.05)
    
    if labels is not None and "hue" in kwargs.keys():
        for lh in fig._legend.legend_handles:
            lh.set_alpha(1)
            lh._sizes = [100] 
    return fig

fig = pairplot(Xhat_perm, labels=zperm, diag_kind="hist", title="(C) Pairs plot of $\\hat X$ for permuted $A$", height=2.5,
               plot_kws={"color": "#000000"}, diag_kws={"color": "#000000"})
fig.tight_layout()

fname = "ase_permutedc"
if mode != "png":
    fig.savefig(f"Figures/{mode:s}/{fname:s}.{mode:s}")

fig.savefig(f"Figures/png/{fname:s}.png")
../../_images/739b1606c42b19199d4f90cff7cb55ae3c7d3732d37e34f8daebbe534b884b85.png
fig = pairplot(Xhat_perm, labels=zperm, diag_kind="hist", title="(D) Pairs plot of $\\hat X$ with community annotation", height=2.5,
              hue="Community", palette={1: "#000000", 2: "#999999"}, markers={1: "o", 2: "s"})
fig.tight_layout()

fname = "ase_permutedd"
if mode != "png":
    fig.savefig(f"Figures/{mode:s}/{fname:s}.{mode:s}")

fig.savefig(f"Figures/png/{fname:s}.png")
../../_images/9f1392f24db00df819d02ac617d6ab645f30c5a8c317b3c3467065c1831c0d36.png
from scipy.spatial import distance_matrix

D = distance_matrix(Xhat, Xhat)
import math
from matplotlib import patches

fig, axs = plt.subplots(1, 1, figsize=(5, 5))

heatmap(D, title="Pairwise dist. matrix", ax=axs,
        xticks=[0.5, 49.5, 99.5], xticklabels=[1, 50, 100], xtitle="Node",
        inner_hier_labels=zs, legend_title="Eucl. dist.")
fig.tight_layout()

fname = "dist"
if mode != "png":
    fig.savefig(f"Figures/{mode:s}/{fname:s}.{mode:s}")

fig.savefig(f"Figures/png/{fname:s}.png")
../../_images/82f8dad1b3ca8727a85be97ba51e47d5292330f3af630ef551e026b5f7645a67.png