graficos graficas graficar font coordenadas python matplotlib plot hierarchical-clustering dendrogram

python - graficas - ¿Cómo ajustar las longitudes de rama del dendrograma en matplotlib(como en astrodendro)?



plot title font size matplotlib (1)

No estoy seguro de que esto realmente constituya una respuesta práctica, pero le permite generar dendrogramas con líneas colgantes truncadas. El truco consiste en generar la trama de manera normal, luego manipular la trama matplotlib resultante para recrear las líneas.

No pude hacer que tu ejemplo funcione localmente, por lo que acabo de crear un conjunto de datos ficticios.

from matplotlib import pyplot as plt from scipy.cluster.hierarchy import dendrogram, linkage import numpy as np a = np.random.multivariate_normal([0, 10], [[3, 1], [1, 4]], size=[5,]) b = np.random.multivariate_normal([0, 10], [[3, 1], [1, 4]], size=[5,]) X = np.concatenate((a, b),) Z = linkage(X, ''ward'') fig = plt.figure() ax = fig.add_subplot(1,1,1) dendrogram(Z, ax=ax)

El gráfico resultante es el dendrograma de brazo largo habitual.

Ahora lo más interesante. Un dendrograma se compone de una serie de objetos LineCollection (uno para cada color). Para actualizar las líneas, iteramos a través de estas, extrayendo los detalles sobre sus rutas constituyentes, modificándolas para eliminar cualquier línea que llegue a una y de cero, y luego LineCollection una LineCollection para estas rutas modificadas.

La ruta actualizada se agrega a los ejes y el original se elimina.

La única parte difícil es determinar qué altura dibujar en lugar de cero. Ya que estamos iterando sobre cada ruta de dendrogramas, no sabemos qué punto vino antes, básicamente no tenemos idea de dónde estamos. Sin embargo, podemos explotar el hecho de que las líneas colgantes cuelgan verticalmente. Suponiendo que no haya líneas en la misma x , podemos buscar los otros valores de y conocidos para una x dada y usarlos como la base de nuestra nueva y al calcular. El inconveniente es que para asegurarnos de que tengamos este número, debemos escanear previamente los datos.

Nota: Si puede obtener líneas colgantes de dendrogramas en la misma x , deberá incluir la y y buscar la y más cercana sobre esta x para hacer esto.

import numpy as np from matplotlib.path import Path from matplotlib.collections import LineCollection fig = plt.figure() ax = fig.add_subplot(1,1,1) dendrogram(Z, ax=ax); for c in ax.collections[:]: # use [:] to get a copy, since we''re adding to the same list paths = [] for path in c.get_paths(): segments = [] y_at_x = {} # Pre-pass over all elements, to find the lowest y value at each x value. # we can use this to caculate where to cut our lines. for n, seg in enumerate(path.iter_segments()): x, y = seg[0] # Don''t store if the y is zero, or if it''s higher than the current low. if y > 0 and y < y_at_x.get(x, np.inf): y_at_x[x] = y for n, seg in enumerate(path.iter_segments()): x, y = seg[0] if y == 0: # If we know the last y at this x, use it - 0.5, limit > 0 y = max(0, y_at_x.get(x, 0) - 0.5) segments.append([x,y]) paths.append(segments) lc = LineCollection(paths, colors=c.get_colors()) # Recreate a LineCollection with the same params ax.add_collection(lc) ax.collections.remove(c) # Remove the original LineCollection

El dendrograma resultante se ve así:

Aquí está mi gráfico resultante a continuación, pero me gustaría que se pareciera a los dendrogramas truncados en astrodendro como this :

También hay un dendrograma de aspecto realmente genial de este documento que me gustaría recrear en matplotlib .

A continuación se muestra el código para generar un conjunto de datos de iris con variables de ruido y trazar el dendrograma en matplotlib .

¿Alguien sabe cómo: (1) truncar las ramas como en las figuras de ejemplo; y / o (2) ¿usar astrodendro con una matriz de enlaces personalizados y etiquetas?

import pandas as pd import numpy as np from sklearn.datasets import load_iris import astrodendro from scipy.cluster.hierarchy import dendrogram, linkage from scipy.spatial import distance def iris_data(noise=None, palette="hls", desat=1): # Iris dataset X = pd.DataFrame(load_iris().data, index = [*map(lambda x:f"iris_{x}", range(150))], columns = [*map(lambda x: x.split(" (cm)")[0].replace(" ","_"), load_iris().feature_names)]) y = pd.Series(load_iris().target, index = X.index, name = "Species") c = map_colors(y, mode=1, palette=palette, desat=desat)#y.map(lambda x:{0:"red",1:"green",2:"blue"}[x]) if noise is not None: X_noise = pd.DataFrame( np.random.RandomState(0).normal(size=(X.shape[0], noise)), index=X_iris.index, columns=[*map(lambda x:f"noise_{x}", range(noise))] ) X = pd.concat([X, X_noise], axis=1) return (X, y, c) def dism2linkage(DF_dism, method="ward"): """ Input: A (m x m) dissimalrity Pandas DataFrame object where the diagonal is 0 Output: Hierarchical clustering encoded as a linkage matrix Further reading: http://docs.scipy.org/doc/scipy-0.14.0/reference/generated/scipy.cluster.hierarchy.linkage.html https://pypi.python.org/pypi/fastcluster """ #Linkage Matrix Ar_dist = distance.squareform(DF_dism.as_matrix()) return linkage(Ar_dist,method=method) # Get data X_iris_with_noise, y_iris, c_iris = iris_data(50) # Get distance matrix df_dism = 1- X_iris_with_noise.corr().abs() # Get linkage matrix Z = dism2linkage(df_dism) #Create dendrogram with plt.style.context("seaborn-white"): fig, ax = plt.subplots(figsize=(13,3)) D_dendro = dendrogram( Z, labels=df_dism.index, color_threshold=3.5, count_sort = "ascending", #link_color_func=lambda k: colors[k] ax=ax ) ax.set_ylabel("Distance")