如何使用极坐标绘制scipy.hierarchy.dendrogram?

7
我正在尝试将以下资源适应到这个问题中:

Python坐标转换

https://matplotlib.org/gallery/pie_and_polar_charts/polar_scatter.html

我似乎无法将树状图的坐标转换为极坐标。

有人知道如何做到这一点吗?我知道在networkx中有一个实现,但那需要构建图形,然后使用pygraphviz后端获取位置。

是否有一种方法可以使用matplotlibnumpy将树状图笛卡尔坐标转换为极坐标?

import requests
from ast import literal_eval
import matplotlib.pyplot as plt
import numpy as np 

def read_url(url):
    r = requests.get(url)
    return r.text

def cartesian_to_polar(x, y):
    rho = np.sqrt(x**2 + y**2)
    phi = np.arctan2(y, x)
    return(rho, phi)

def plot_dendrogram(icoord,dcoord,figsize, polar=False):
    if polar:
        icoord, dcoord = cartesian_to_polar(icoord, dcoord)
    with plt.style.context("seaborn-white"):
        fig = plt.figure(figsize=figsize)
        ax = fig.add_subplot(111, polar=polar)
        for xs, ys in zip(icoord, dcoord):
            ax.plot(xs,ys, color="black")
        ax.set_title(f"Polar= {polar}", fontsize=15)

# Load the dendrogram data
string_data = read_url("https://pastebin.com/raw/f953qgdr").replace("\r","").replace("\n","").replace("\u200b\u200b","")

# Convert it to a dictionary (a subset of the output from scipy.hierarchy.dendrogram)
dendrogram_data = literal_eval(string_data)
icoord = np.asarray(dendrogram_data["icoord"], dtype=float)
dcoord = np.asarray(dendrogram_data["dcoord"], dtype=float)

# Plot the cartesian version
plot_dendrogram(icoord,dcoord, figsize=(8,3), polar=False)

# Plot the polar version
plot_dendrogram(icoord,dcoord, figsize=(5,5), polar=True)

enter image description here

我刚刚尝试了这个方法,虽然更接近正确答案但还不完全正确:

import matplotlib.transforms as mtransforms
with plt.style.context("seaborn-white"):
    fig, ax = plt.subplots(figsize=(5,5))
    for xs, ys in zip(icoord, dcoord):
        ax.plot(xs,ys, color="black",transform=trans_offset)

    ax_polar = plt.subplot(111, projection='polar')
    trans_offset = mtransforms.offset_copy(ax_polar.transData, fig=fig)
    for xs, ys in zip(icoord, dcoord):
        ax_polar.plot(xs,ys, color="black",transform=trans_offset)

enter image description here

2个回答

10

你可以让树的“根”从中间开始,并将叶子放在外面。同时,你还需要在“条”部分添加更多的点,使其看起来更加美观和圆润。

我们注意到,icoord和dcoord中的每个元素(我将其称为seg)都有四个点:

seg[1]        seg[2]
+-------------+
|             |
+ seg[0]      + seg[3]

垂直条可以作为两个点之间的直线,但是在 seg[1]seg[2] 之间我们需要更多的点(水平条将需要成为一个弧形)。

这个函数将在这些位置添加更多的点,并且可以在绘图函数中分别对xs和ys进行调用:

def smoothsegment(seg, Nsmooth=100):
    return np.concatenate([[seg[0]], np.linspace(seg[1], seg[2], Nsmooth), [seg[3]]])

现在我们必须修改绘图函数以计算径向坐标。一些实验已经导致我使用的对数公式,基于另一个也使用对数刻度的答案。我在右侧留下了空白来放置径向标签,并且进行了非常简单的映射,将“icoord”坐标映射到径向坐标,以便标签与矩形图中的标签相对应。我不确定如何处理径向维度。对于对数而言,数字是正确的,但我们可能也想要映射它们。

def plot_dendrogram(icoord,dcoord,figsize, polar=False):
    if polar:
        dcoord = -np.log(dcoord+1)
        # avoid a wedge over the radial labels
        gap = 0.1
        imax = icoord.max()
        imin = icoord.min()
        icoord = ((icoord - imin)/(imax - imin)*(1-gap) + gap/2)*2*numpy.pi
    with plt.style.context("seaborn-white"):
        fig = plt.figure(figsize=figsize)
        ax = fig.add_subplot(111, polar=polar)
        for xs, ys in zip(icoord, dcoord):
            if polar:
                xs = smoothsegment(xs)
                ys = smoothsegment(ys)
            ax.plot(xs,ys, color="black")
        ax.set_title(f"Polar= {polar}", fontsize=15)
        if polar:
            ax.spines['polar'].set_visible(False)
            ax.set_rlabel_position(0)
            Nxticks = 10
            xticks = np.linspace(gap/2, 1-gap/2, Nxticks)
            ax.set_xticks(xticks*np.pi*2)
            ax.set_xticklabels(np.round(np.linspace(imin, imax, Nxticks)).astype(int))

其结果如下图所示:

径向树状图


3

首先,我认为你可能会从这个问题中受益。

然后,让我们分解下目标:对我来说不是很清楚您想要做什么,但我假设您想要得到类似于polar dendogram这样的东西。

来源,第14页

要呈现这样的东西,您需要能够在极坐标下渲染出作为半圆的水平线。然后,就是将您的水平线映射到极坐标图中的问题了。

首先,请注意,此行中的半径没有被归一化:

if polar:
    icoord, dcoord = cartesian_to_polar(icoord, dcoord)

您可以通过重新映射icoord为[0;2pi)来对其进行归一化。

现在,让我们试着绘制一些更简单的东西,而不是您复杂的绘图:

icoord, dcoord = np.meshgrid(np.r_[1:10], np.r_[1:4])

# Plot the cartesian version
plot_dendrogram(icoord, dcoord, figsize=(8, 3), polar=False)

# Plot the polar version
plot_dendrogram(icoord, dcoord, figsize=(5, 5), polar=True)

结果如下所示:

常规绘图 极坐标绘图

如你所见,极坐标代码不会将水平线映射为半圆,因此这种方法行不通。让我们尝试使用plt.polar代替:
plt.polar(icoord.T, dcoord.T)

生成

plt.polar

这更符合我们的需要。我们需要先修正角度,然后考虑Y坐标向内(而您可能希望它从中心到边缘)。它归结为以下内容

nic = (icoord.T - icoord.min()) / (icoord.max() - icoord.min())
plt.polar(2 * np.pi * nic, -dcoord.T)

该代码会生成以下结果:

极地树状图

这与您所需要的类似。请注意,直线保持直线不变,不会被弧替代,因此您可能需要在for循环中重新采样它们。

另外,您可能会从单一颜色和对数刻度中获益,以使阅读更加容易。

更好的极地树状图

plt.subplots(figsize=(10, 10))
ico = (icoord.T - icoord.min()) / (icoord.max() - icoord.min())
plt.polar(2 * np.pi * ico, -np.log(dcoord.T), 'b')

1
那个对数刻度确实让它看起来很棒。 - chthonicdaemon
1
我已经找到了更好的第一张图片来源,即生成它的Matlab包(https://www.mathworks.com/matlabcentral/fileexchange/21983-draw-a-polar-dendrogram)。 - chthonicdaemon
1
对数刻度有相当的帮助,但它需要一些调整。很高兴看到你在回答中解决了这个问题!赞,你的图表很棒! - The Data Scientician

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接