matplotlib 在刻度分布不均匀的极坐标图中垂直旋转所有刻度标签

lnlaulya  于 12个月前  发布在  其他
关注(0)|答案(1)|浏览(106)

我正在制作一个极坐标图,其中的刻度不是均匀分布在圆上。有几个非常好的Q&A对处理均匀分布的答案,它们都使用 divide-up-the-circle 方法。E.g. this
我想知道是否可以使用烘焙到标签中的转换来旋转文本,使其按照我想要的方式放置。
我可以做到这一点,但我不知道如何正确地锚它。代码是这样的:

for tick in plt.xticks()[1]:
    tick._transform = tick._transform + mpl.transforms.Affine2D().rotate_deg_around(0, 0, 10)

它给出如下输出:

而我想要这样的输出:

(from上述相关问题)
显然,我需要90°旋转,而不是10°,但90°旋转它离开画布。
这种方法可行吗?或者我需要重新评估我的策略吗?
完整的代码块在这里:

import random

import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np

one_person = {
    "Human": {
        "Collaboration": 4,
        "Growth Mindset": 3,
        "Inclusion": 5,
        "Project and Studio Life": 2,
    },
    "Tectonics": {
        "Office Manual and Procedures": 3,
        "Documentation Standards": 3,
        "Site Stage Services": 2,
        "External and Public Domain Works": 2,
        "Structure": 3,
        "Enclosure": 2,
        "Waterproofing (int. and ext.)": 3,
        "Interiors": 1,
        "Structure and Services": 2,
    },
    "Technology": {
        "Bluebeam": 2,
        "Confluence": 3,
        "Drawing on screens": 0,
        "dRofus": 0,
        "Excel": 2,
        "Grasshopper": 1,
        "InDesign": 2,
        "Outlook": 2,
        "Python": 5,
        "Rhino": 1,
        "Teams": 2,
        "Timesheets and expenses": 3,
    },
    "Regenerative": {
        "REgenerative Design": 3,
        "Materials and Embodied Carbon practice": 1,
        "Materials and Embodied Carbon analysis": 2,
        "Energy": 3,
        "Resilience": 1,
        "Rating Systems": 2,
    },
    "Design": {
        "Predesign - Briefing, Stakeholder Engagement & Establishing Project Values": 2,
        "Predesign - Feasibility Studies And Strategic Organisational Planning": 3,
        "Initiating Design": 2,
        "Conserving Design": 3,
        "Design Communication - Written": 2,
        "Design Communication - Visual": 4,
        "Design Communication - Verbal": 3,
    },
    "Connecting with country": {"Connecting with Country": 2},
}
colours = [
    "b",  # blue.
    "g",  # green.
    "r",  # red.
    "c",  # cyan.
    "m",  # magenta.
    "y",  # yellow.
    "k",  # black.
    # "w",  # white.
]

def draw_radar(data, colour_letters, person_name=""):
    """Draw the graph.

    Based substantially on this SO thread:
    https://stackoverflow.com/questions/60563106/complex-polar-plot-in-matplotlib
    """
    # not really sure why -1, but if you don't you get an empty segment
    num_areas = len(data) - 1
    running_total = 0
    thetas = {}
    for key, value in data.items():
        this_area_num_points = len(value)
        this_area_theta = ((2 * np.pi) / num_areas) / (this_area_num_points)
        thetas[key] = []
        for i in range(len(value)):
            thetas[key].append((i * this_area_theta) + running_total)
        running_total += (2 * np.pi) / num_areas

    labels = []
    for key, value in data.items():
        for area, score in value.items():
            labels.append(f"{score} {key}: {area}")

    for name, theta_list in thetas.items():
        individual_scores = list(data[name].values())
        colour = random.choice(colour_letters)
        if len(theta_list) > 1:
            plt.polar(theta_list, individual_scores, c=colour, label=name)
        elif len(theta_list) == 1:
            plt.scatter(theta_list, individual_scores, c=colour, label=name)
    plt.yticks(np.arange(-5, 5), [""] * 5 + list(range(5)))
    plt.xticks(
        np.concatenate(tuple(list(thetas.values()))),
        labels,
        transform_rotates_text=True,
    )
    for tick in plt.xticks()[1]:
        tick._transform = tick._transform + mpl.transforms.Affine2D().rotate_deg_around(
            0, 0, 10
        )
    if person_name:
        plt.title = f"Competency for {person_name}"
    plt.savefig("radar.png")

draw_radar(one_person, colours)
svmlkihl

svmlkihl1#

不使用Matplotlib转换,您可以使用ax.get_xticklabels(),然后使用label.set_rotation(θ¹)迭代地为每个标签设置特定于刻度的旋转

**<$**其中,Angular θ源自与每个标签相关的极坐标数据;实际上等效于极坐标图中的x维度(即圆形网格线)。

例如,修改您提供的代码,如下所示:

import random

import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np

one_person = {
    "Human": {
        "Collaboration": 4,
        "Growth Mindset": 3,
        "Inclusion": 5,
        "Project and Studio Life": 2,
    },
    "Tectonics": {
        "Office Manual and Procedures": 3,
        "Documentation Standards": 3,
        "Site Stage Services": 2,
        "External and Public Domain Works": 2,
        "Structure": 3,
        "Enclosure": 2,
        "Waterproofing (int. and ext.)": 3,
        "Interiors": 1,
        "Structure and Services": 2,
    },
    "Technology": {
        "Bluebeam": 2,
        "Confluence": 3,
        "Drawing on screens": 0,
        "dRofus": 0,
        "Excel": 2,
        "Grasshopper": 1,
        "InDesign": 2,
        "Outlook": 2,
        "Python": 5,
        "Rhino": 1,
        "Teams": 2,
        "Timesheets and expenses": 3,
    },
    "Regenerative": {
        "REgenerative Design": 3,
        "Materials and Embodied Carbon practice": 1,
        "Materials and Embodied Carbon analysis": 2,
        "Energy": 3,
        "Resilience": 1,
        "Rating Systems": 2,
    },
    "Design": {
        "Predesign - Briefing, Stakeholder Engagement & Establishing Project Values": 2,
        "Predesign - Feasibility Studies And Strategic Organisational Planning": 3,
        "Initiating Design": 2,
        "Conserving Design": 3,
        "Design Communication - Written": 2,
        "Design Communication - Visual": 4,
        "Design Communication - Verbal": 3,
    },
    "Connecting with country": {"Connecting with Country": 2},
}
colours = [
    "b",  # blue.
    "g",  # green.
    "r",  # red.
    "c",  # cyan.
    "m",  # magenta.
    "y",  # yellow.
    "k",  # black.
]

def draw_radar(data, colour_letters, person_name=""):
    """Draw the graph.

    Based substantially on this SO thread:
    https://stackoverflow.com/questions/60563106/complex-polar-plot-in-matplotlib
    """

    fig, ax = plt.subplots(
        subplot_kw={"projection": "polar"}, figsize=(10, 10)
    )
    num_areas = len(data) - 1
    running_total = 0
    thetas = {}
    for key, value in data.items():
        this_area_num_points = len(value)
        this_area_theta = ((2 * np.pi) / num_areas) / (this_area_num_points)
        thetas[key] = []
        for i in range(len(value)):
            thetas[key].append((i * this_area_theta) + running_total)
        running_total += (2 * np.pi) / num_areas

    labels = []
    for key, value in data.items():
        for area, score in value.items():
            labels.append(f"{score} {key}: {area}")

    for name, theta_list in thetas.items():
        individual_scores = list(data[name].values())
        colour = colour_letters.pop()  # random.choice(colour_letters)
        if len(theta_list) > 1:
            ax.plot(theta_list, individual_scores, c=colour, label=name)
        elif len(theta_list) == 1:
            ax.scatter(theta_list, individual_scores, c=colour, label=name)
    ax.set_yticks(np.arange(-5, 5), [""] * 5 + list(range(5)))
    ax.set_xticks(
        np.concatenate(tuple(list(thetas.values()))), labels,
    )

    plt.gcf().canvas.draw()
    max_label_len = max(list(map(len, labels)))
    t_labels = []
    for label in ax.get_xticklabels():
        x, y = label.get_position()
        text = label.get_text()
        angle = x
        y_adjust = (len(text) / max_label_len) * 0.8
        if text.endswith("Country"):
            x_adjust = 0.05
            angle += 0.05
        else:
            x_adjust = 0
        lab = ax.text(
            x + x_adjust,
            y - y_adjust,
            label.get_text(),
            transform=label.get_transform(),
            ha=label.get_ha(),
            va=label.get_va(),
        )
        if np.cos(angle) < 0:
            angle = angle + np.pi
        angle = np.rad2deg(angle)
        lab.set_rotation(angle)
        t_labels.append(lab)
    ax.set_xticklabels([])
    plt.show()

draw_radar(one_person, colours)

结果:x1c 0d1x
您可能需要调整以下设置:

  1. figsize(在figax图形和轴对象的定义中),相应地,
  2. y_adjust因子(设置为0.8以上)。
    一般逻辑是为每个极坐标数据值找到绘制的thetaAngular ,然后将该Angular 值本身用作标注文本的刻度特定旋转。np.cos代码将检查并适当旋转倒置标注。每个标注将向外移动(即,在y-维度上为负[即,* 径向 *;或者换句话说,在圆的外部&在任何可能的半径的方向上])增加一个基于其标准化标签文本长度的量(相对于最大长度标签),导致所有标签或多或少精确地位于圆外部(即,文本标签越长,它必须移动和移动得越多)。
    此外,还有一个标签(以“Country”结尾),与另一个标签重叠-对于那个标签,我自定义将其向上移动,并稍微弯曲其刻度标签旋转的Angular ,以反映它实际上属于/应该指向极坐标图上与其下方刻度标签数据具有相同θ角的数据。

相关问题