簡體   English   中英

我如何使用 plotly 截斷樹狀圖 plot plot?

[英]How can i plot a truncated dendrogram plot using plotly?

I want to plot a dendrogram plot for hierarchical clustering using plotly and show a small subset of the plot as with the large number of samples the plot can be very dense at the bottom.

我使用 plotly 包裝器 function create_dendrogram 繪制了 plot 和以下代碼:

from scipy.cluster.hierarchy import linkage
import plotly.figure_factory as ff
fig = ff.create_dendrogram(test_df, linkagefun=lambda x: linkage(test_df, 'average', metric='euclidean'))
fig.update_layout(autosize=True, hovermode='closest')
fig.update_xaxes(mirror=False, showgrid=True, showline=False, showticklabels=False)
fig.update_yaxes(mirror=False, showgrid=True, showline=True)
fig.show()

在此處輸入圖像描述

下面是使用 matplotlib 的 plot,scipy 庫默認使用 plot,以便於解釋:

from scipy.cluster.hierarchy import dendrogram,linkage
x = linkage(test_df,method='average')
dendrogram(x,truncate_mode='level',p=4)
plt.show()

在此處輸入圖像描述

如您所見,截斷對於解釋大量樣本非常有用,我如何在 plotly 中實現這一點?

使用ff.create_dendrogram()似乎沒有直接的方法來做到這一點。 但這並不意味着它是不可能的。 但我至少會考慮Dash Clustergram提供的出色功能。 如果你堅持堅持使用ff.create_dendrogram() ,這將比 Plotly 用戶已經習慣的更混亂。 您還沒有提供數據樣本,所以讓我們改用 Plotly Basic Dendrogram示例:

Plot 1

在此處輸入圖像描述

代碼 1

import plotly.figure_factory as ff
import numpy as np
np.random.seed(1)

X = np.random.rand(15, 12) # 15 samples, with 12 dimensions each
fig = ff.create_dendrogram(X)
fig.update_layout(width=800, height=500)
f = fig.full_figure_for_development(warn=False)
fig.show()

好消息是,在我們采取了一些我將在下面詳細解釋的步驟之后,完全相同的代碼段將生成以下截斷的 plot。

Plot 2

在此處輸入圖像描述

細節

如果在我的回答中得到這么多的人知道執行以下操作的更好方法,分享。

1. ff.create_dendrogram()scipy.cluster.hierarchy.dendrogram的包裝器

您可以致電help(ff.create_dendrogram)並了解:

[...]這是圍繞 scipy.cluster.hierarchy.dendrogram 的薄包裝。

從可用的 arguments 中,您還可以看到似乎沒有人處理與截斷相關的任何事情:

create_dendrogram(X,orientation='bottom',labels=None,colorscale=None,distfun=None,linkagefun=<function at 0x0000016F09D4CEE0>,hovertext=None,color_threshold=None)

2.仔細看scipy.cluster.hierarchy.dendrogram

在這里,當我們將其與源代碼進行比較時,我們可以看到在ff.create_dendrogram(X)中實現 function 后,一些中心元素被遺漏了:

scipy.cluster.hierarchy.dendrogram(Z, p=30, truncate_mode=None, color_threshold=None, get_leaves=True, orientation='top', labels=None, count_sort=False, distance_sort=False, show_leaf_counts=True, no_plot=False, no_labels=False, leaf_font_size=None, leaf_rotation=None, leaf_label_func=None, show_contracted=False, link_color_func=None, ax=None, above_threshold_color='C0')

truncate_mode應該正是我們正在尋找的。 所以,現在我們知道scipy可能擁有我們為截斷樹狀圖構建基礎所需的一切,但下一步是什么?

3.在ff.create_dendrogram(X)中找到scipy.cluster.hierarchy.dendrogram隱藏在哪里

ff.create_dendrogram.__code__將顯示源代碼在系統中的位置。 就我而言,這是:

"C:\Users\vestland\Miniconda3\envs\dashy\lib\site-packages\plotly\figure_factory\_dendrogram.py"

因此,如果您願意,可以仔細查看相應文件夾中的完整源代碼。 如果你這樣做,你會看到一個特別有趣的部分,我們上面列出的一些屬性得到了處理:

def get_dendrogram_traces(
    self, X, colorscale, distfun, linkagefun, hovertext, color_threshold
):
    """
    Calculates all the elements needed for plotting a dendrogram.
.
.
.
P = sch.dendrogram(
        Z,
        orientation=self.orientation,
        labels=self.labels,
        no_plot=True,
        color_threshold=color_threshold,
    )

在這里,我們處於問題的核心。 完整回答您的問題的第一步就是在P中包含truncate_modep ,如下所示:

P = sch.dendrogram(
    Z,
    orientation=self.orientation,
    labels=self.labels,
    no_plot=True,
    color_threshold=color_threshold,
    truncate_mode = 'level',
    p = 2
)

這是你如何做到的:

4. 猴子補丁

在 Python 中,monkey patch 一詞僅指在運行時對 class 或模塊的動態修改,這意味着猴子補丁是一段 Python 代碼在運行時擴展或修改其他代碼。 這是在我們的案例中如何做到這一點的精髓:

import plotly.figure_factory._dendrogram as original_dendrogram
original_dendrogram._Dendrogram.get_dendrogram_traces = modified_dendrogram_traces

其中modified_dendrogram_tracesmodified_dendrogram_traces()完整 function 定義以及我已經提到的修改。 以及一些將丟失的導入,否則會在您調用import plotly.figure_factory as ff時運行

現在足夠詳細了。 下面是全部內容。 如果這是您可以使用的東西,我們也許可以使整個事情比硬編碼truncate_mode = 'level'p = 2更具動態性。

完整代碼:

from scipy.cluster.hierarchy import linkage
import plotly.figure_factory as ff
import plotly.figure_factory._dendrogram as original_dendrogram
import numpy as np

def modified_dendrogram_traces(
    self, X, colorscale, distfun, linkagefun, hovertext, color_threshold
):
    """
    Calculates all the elements needed for plotting a dendrogram.

    :param (ndarray) X: Matrix of observations as array of arrays
    :param (list) colorscale: Color scale for dendrogram tree clusters
    :param (function) distfun: Function to compute the pairwise distance
                               from the observations
    :param (function) linkagefun: Function to compute the linkage matrix
                                  from the pairwise distances
    :param (list) hovertext: List of hovertext for constituent traces of dendrogram
    :rtype (tuple): Contains all the traces in the following order:
        (a) trace_list: List of Plotly trace objects for dendrogram tree
        (b) icoord: All X points of the dendrogram tree as array of arrays
            with length 4
        (c) dcoord: All Y points of the dendrogram tree as array of arrays
            with length 4
        (d) ordered_labels: leaf labels in the order they are going to
            appear on the plot
        (e) P['leaves']: left-to-right traversal of the leaves

    """
    import plotly
    from plotly import exceptions, optional_imports
    np = optional_imports.get_module("numpy")
    scp = optional_imports.get_module("scipy")
    sch = optional_imports.get_module("scipy.cluster.hierarchy")
    scs = optional_imports.get_module("scipy.spatial")
    sch = optional_imports.get_module("scipy.cluster.hierarchy")
    d = distfun(X)
    Z = linkagefun(d)
    P = sch.dendrogram(
        Z,
        orientation=self.orientation,
        labels=self.labels,
        no_plot=True,
        color_threshold=color_threshold,
        truncate_mode = 'level',
        p = 2
    )

    icoord = scp.array(P["icoord"])
    dcoord = scp.array(P["dcoord"])
    ordered_labels = scp.array(P["ivl"])
    color_list = scp.array(P["color_list"])
    colors = self.get_color_dict(colorscale)

    trace_list = []

    for i in range(len(icoord)):
        # xs and ys are arrays of 4 points that make up the '∩' shapes
        # of the dendrogram tree
        if self.orientation in ["top", "bottom"]:
            xs = icoord[i]
        else:
            xs = dcoord[i]

        if self.orientation in ["top", "bottom"]:
            ys = dcoord[i]
        else:
            ys = icoord[i]
        color_key = color_list[i]
        hovertext_label = None
        if hovertext:
            hovertext_label = hovertext[i]
        trace = dict(
            type="scatter",
            x=np.multiply(self.sign[self.xaxis], xs),
            y=np.multiply(self.sign[self.yaxis], ys),
            mode="lines",
            marker=dict(color=colors[color_key]),
            text=hovertext_label,
            hoverinfo="text",
        )

        try:
            x_index = int(self.xaxis[-1])
        except ValueError:
            x_index = ""

        try:
            y_index = int(self.yaxis[-1])
        except ValueError:
            y_index = ""

        trace["xaxis"] = "x" + x_index
        trace["yaxis"] = "y" + y_index

        trace_list.append(trace)

    return trace_list, icoord, dcoord, ordered_labels, P["leaves"]

original_dendrogram._Dendrogram.get_dendrogram_traces = modified_dendrogram_traces
X = np.random.rand(15, 12) # 15 samples, with 12 dimensions each
fig = ff.create_dendrogram(X)
fig.update_layout(width=800, height=500)
f = fig.full_figure_for_development(warn=False)
fig.show()

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM