snputils.visualization.admixture

 1import numpy as np
 2import matplotlib.pyplot as plt
 3import matplotlib
 4
 5def reorder_admixture(Q_mat):
 6    """
 7    Reorder Q_mat rows so that rows are grouped by each sample's dominant ancestry,
 8    and columns are sorted by descending average ancestry proportion.
 9    """
10    n_samples, K = Q_mat.shape
11    
12    # Reorder columns by descending average proportion
13    col_means = Q_mat.mean(axis=0)
14    col_order = np.argsort(col_means)[::-1]   # largest first
15    Q_cols_sorted = Q_mat[:, col_order]
16    
17    # Group samples by whichever column is argmax
18    row_groups = []
19    boundary_list = [0]
20    for k in range(K):
21        rows_k = np.where(np.argmax(Q_cols_sorted, axis=1) == k)[0]
22        # Sort these rows by their proportion in col k
23        rows_k_sorted = rows_k[np.argsort(Q_cols_sorted[rows_k, k])[::-1]]
24        row_groups.append(rows_k_sorted)
25        boundary_list.append(boundary_list[-1] + len(rows_k_sorted))
26    
27    # Combine them into one final row order
28    row_order = np.concatenate(row_groups)
29    Q_mat_sorted = Q_cols_sorted[row_order, :]
30    
31    return Q_mat_sorted, row_order, boundary_list, col_order
32
33def plot_admixture(ax, Q_mat_sorted, boundary_list, col_order=None, colors=None):
34    """
35    Plot a structure-style bar chart of Q_mat_sorted in the given Axes ax.
36    If colors is not None, it should be a list or array of length K.
37    If col_order is not None, colors are reordered according to col_order.
38    """
39    n_samples, K = Q_mat_sorted.shape
40    
41    # If we have a specific color list and a col_order, reorder the colors to match the columns
42    if (colors is not None) and (col_order is not None):
43        # re-map the user-provided colors to match the new column order
44        colors = [colors[idx] for idx in col_order]
45    
46    # cumulative sum across columns for stacked fill
47    Q_cum = np.cumsum(Q_mat_sorted, axis=1)
48    x_vals = np.arange(n_samples)
49    
50    step_mode = "post"
51    ax.step(x_vals, Q_cum, linewidth=0.0, where=step_mode)
52    
53    # fill-between for a stacked bar effect
54    for j in range(K):
55        c = colors[j] if (colors is not None) else None
56        if j == 0:
57            ax.fill_between(x_vals, 0, Q_cum[:, j], step=step_mode, color=c)
58        else:
59            ax.fill_between(x_vals, Q_cum[:, j - 1], Q_cum[:, j], step=step_mode, color=c)
60    
61    # Vertical lines for group boundaries
62    for boundary in boundary_list:
63        ax.axvline(boundary, color='black', ls='--', lw=1.0)
64    
65    ax.set_xlim(0, n_samples - 1)
66    ax.set_ylim(0, 1)
67    ax.set_xlabel("Samples")
68    ax.set_ylabel("Ancestry Proportion")
def reorder_admixture(Q_mat):
 6def reorder_admixture(Q_mat):
 7    """
 8    Reorder Q_mat rows so that rows are grouped by each sample's dominant ancestry,
 9    and columns are sorted by descending average ancestry proportion.
10    """
11    n_samples, K = Q_mat.shape
12    
13    # Reorder columns by descending average proportion
14    col_means = Q_mat.mean(axis=0)
15    col_order = np.argsort(col_means)[::-1]   # largest first
16    Q_cols_sorted = Q_mat[:, col_order]
17    
18    # Group samples by whichever column is argmax
19    row_groups = []
20    boundary_list = [0]
21    for k in range(K):
22        rows_k = np.where(np.argmax(Q_cols_sorted, axis=1) == k)[0]
23        # Sort these rows by their proportion in col k
24        rows_k_sorted = rows_k[np.argsort(Q_cols_sorted[rows_k, k])[::-1]]
25        row_groups.append(rows_k_sorted)
26        boundary_list.append(boundary_list[-1] + len(rows_k_sorted))
27    
28    # Combine them into one final row order
29    row_order = np.concatenate(row_groups)
30    Q_mat_sorted = Q_cols_sorted[row_order, :]
31    
32    return Q_mat_sorted, row_order, boundary_list, col_order

Reorder Q_mat rows so that rows are grouped by each sample's dominant ancestry, and columns are sorted by descending average ancestry proportion.

def plot_admixture(ax, Q_mat_sorted, boundary_list, col_order=None, colors=None):
34def plot_admixture(ax, Q_mat_sorted, boundary_list, col_order=None, colors=None):
35    """
36    Plot a structure-style bar chart of Q_mat_sorted in the given Axes ax.
37    If colors is not None, it should be a list or array of length K.
38    If col_order is not None, colors are reordered according to col_order.
39    """
40    n_samples, K = Q_mat_sorted.shape
41    
42    # If we have a specific color list and a col_order, reorder the colors to match the columns
43    if (colors is not None) and (col_order is not None):
44        # re-map the user-provided colors to match the new column order
45        colors = [colors[idx] for idx in col_order]
46    
47    # cumulative sum across columns for stacked fill
48    Q_cum = np.cumsum(Q_mat_sorted, axis=1)
49    x_vals = np.arange(n_samples)
50    
51    step_mode = "post"
52    ax.step(x_vals, Q_cum, linewidth=0.0, where=step_mode)
53    
54    # fill-between for a stacked bar effect
55    for j in range(K):
56        c = colors[j] if (colors is not None) else None
57        if j == 0:
58            ax.fill_between(x_vals, 0, Q_cum[:, j], step=step_mode, color=c)
59        else:
60            ax.fill_between(x_vals, Q_cum[:, j - 1], Q_cum[:, j], step=step_mode, color=c)
61    
62    # Vertical lines for group boundaries
63    for boundary in boundary_list:
64        ax.axvline(boundary, color='black', ls='--', lw=1.0)
65    
66    ax.set_xlim(0, n_samples - 1)
67    ax.set_ylim(0, 1)
68    ax.set_xlabel("Samples")
69    ax.set_ylabel("Ancestry Proportion")

Plot a structure-style bar chart of Q_mat_sorted in the given Axes ax. If colors is not None, it should be a list or array of length K. If col_order is not None, colors are reordered according to col_order.