如何在python中制作多边形雷达(蜘蛛)图 [英] How to make a polygon radar (spider) chart in python

查看:79
本文介绍了如何在python中制作多边形雷达(蜘蛛)图的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

将 matplotlib.pyplot 导入为 plt将 numpy 导入为 np标签=['围攻','启动','Crowd_control','Wave_clear','Objective_damage']标记 = [0, 1, 2, 3, 4, 5]str_markers = [0"、1"、2"、3"、4"、5"]def make_radar_chart(name, stats, attribute_labels=labels,plot_markers=markers,plot_str_markers=str_markers):标签 = np.array(attribute_labels)角度 = np.linspace(0, 2*np.pi, len(labels), endpoint=False)stats = np.concatenate((stats,[stats[0]]))角度 = np.concatenate((angles,[angles[0]]))fig = plt.figure()ax = fig.add_subplot(111, polar=True)ax.plot(角度,统计数据,'o-',线宽=2)ax.fill(角度,统计数据,alpha=0.25)ax.set_thetagrids(角度 * 180/np.pi,标签)plt.yticks(标记)ax.set_title(name)ax.grid(真)fig.savefig("static/images/%s.png" % name)返回 plt.show()make_radar_chart("烈火", [2,3,4,4,5]) # 示例

基本上我希望图表是五边形而不是圆形.有人能帮忙吗.我正在使用 python matplotlib 来保存稍后将存储和显示的图像.我希望我的图表具有第二张图片的形式

 gridlines = ax.yaxis.get_gridlines()对于网格线中的 gl:gl.get_path()._interpolation_steps = 5

从下面的答案中添加这部分代码有很大帮助.我得到这个图表.仍然需要弄清楚如何摆脱最外环:

解决方案

在这里,外部脊椎根据需要是多边形的.然而,内部网格线是圆形的.所以悬而未决的问题是如何使网格线与脊椎形状相同.

这可以通过覆盖draw 方法并将网格线的路径插值步骤变量设置为RadarAxes 类的变量数来完成.

gridlines = self.yaxis.get_gridlines()对于网格线中的 gl:gl.get_path()._interpolation_steps = num_vars

完整示例:

将 numpy 导入为 np导入 matplotlib.pyplot 作为 plt从 matplotlib.patches 导入 Circle,RegularPolygon从 matplotlib.path 导入路径从 matplotlib.projections.polar 导入 PolarAxes从 matplotlib.projections 导入 register_projection从 matplotlib.spines 导入 Spine从 matplotlib.transforms 导入 Affine2Ddefradar_factory(num_vars, frame='circle'):"""创建一个带有 `num_vars` 轴的雷达图.此函数创建一个 RadarAxes 投影并注册它.参数----------num_vars : 整数雷达图的变量数.框架:{'圆圈'|'多边形'}围绕轴的框架形状."""# 计算均匀分布的轴角theta = np.linspace(0, 2*np.pi, num_vars, endpoint=False)类雷达轴(PolarAxes):名称 = '雷达'def __init__(self, *args, **kwargs):super().__init__(*args, **kwargs)# 旋转绘图,使第一个轴位于顶部self.set_theta_zero_location('N')def 填充(self, *args, closed=True, **kwargs):"""覆盖填充,使该行默认关闭"""返回 super().fill(closed=closed, *args, **kwargs)def plot(self, *args, **kwargs):"""覆盖绘图以便该线默认关闭"""行 = super().plot(*args, **kwargs)对于线中线:self._close_line(line)def _close_line(self, line):x, y = line.get_data()# FIXME: x[0], y[0] 处的标记加倍如果 x[0] != x[-1]:x = np.concatenate((x, [x[0]]))y = np.concatenate((y, [y[0]]))line.set_data(x, y)def set_varlabels(自我,标签):self.set_thetagrids(np.degrees(theta), 标签)def _gen_axes_patch(self):# Axes 补丁必须以 (0.5, 0.5) 为中心,半径为 0.5# 在轴坐标中.如果框架=='圆圈':返回圆((0.5, 0.5), 0.5)elif 框架 == '多边形':返回 RegularPolygon((0.5, 0.5), num_vars,半径=.5,边缘颜色=k")别的:raise ValueError("'frame' 的未知值:%s" % frame)def draw(self, renderer):""" 绘制.如果框架是多边形,则使网格线为多边形"""如果框架 == '多边形':网格线 = self.yaxis.get_gridlines()对于网格线中的 gl:gl.get_path()._interpolation_steps = num_vars超级().绘制(渲染器)def _gen_axes_spines(self):如果框架=='圆圈':返回 super()._gen_axes_spines()elif 框架 == '多边形':#spine_type 必须是 'left'/'right'/'top'/'bottom'/'circle'.脊椎=脊椎(轴=自我,脊柱类型='圆圈',路径=Path.unit_regular_polygon(num_vars))# unit_regular_polygon 给出一个半径为 1 的多边形,以# (0, 0) 但我们想要一个半径为 0.5 的多边形,以 (0.5,# 0.5) 在轴坐标中.spine.set_transform(Affine2D().scale(.5).translate(.5, .5)+ self.transAxes)返回 {'polar': 脊椎}别的:raise ValueError("'frame' 的未知值:%s" % frame)register_projection(雷达轴)返回θ数据 = [['硫酸盐','硝酸盐','EC','OC1','OC2','OC3','OP','CO','O3'],('基本情况', [[0.88, 0.01, 0.03, 0.03, 0.00, 0.06, 0.01, 0.00, 0.00],[0.07, 0.95, 0.04, 0.05, 0.00, 0.02, 0.01, 0.00, 0.00],[0.01, 0.02, 0.85, 0.19, 0.05, 0.10, 0.00, 0.00, 0.00],[0.02, 0.01, 0.07, 0.01, 0.21, 0.12, 0.98, 0.00, 0.00],[0.01, 0.01, 0.02, 0.71, 0.74, 0.70, 0.00, 0.00, 0.00]])]N = len(数据[0])theta =radar_factory(N, frame='polygon')辐条标签 = data.pop(0)标题,case_data = 数据[0]图, ax = plt.subplots(figsize=(6, 6), subplot_kw=dict(projection='radar'))fig.subplots_adjust(顶部=0.85,底部=0.05)ax.set_rgrids([0.2, 0.4, 0.6, 0.8])ax.set_title(title, position=(0.5, 1.1), ha='center')对于 d in case_data:线 = ax.plot(theta, d)ax.fill(theta, d, alpha=0.25)ax.set_varlabels(spoke_labels)plt.show()

import matplotlib.pyplot as plt
import numpy as np

labels=['Siege', 'Initiation', 'Crowd_control', 'Wave_clear', 'Objective_damage']
markers = [0, 1, 2, 3, 4, 5]
str_markers = ["0", "1", "2", "3", "4", "5"]

def make_radar_chart(name, stats, attribute_labels=labels,
                     plot_markers=markers, plot_str_markers=str_markers):

    labels = np.array(attribute_labels)

    angles = np.linspace(0, 2*np.pi, len(labels), endpoint=False)
    stats = np.concatenate((stats,[stats[0]]))
    angles = np.concatenate((angles,[angles[0]]))

    fig = plt.figure()
    ax = fig.add_subplot(111, polar=True)
    ax.plot(angles, stats, 'o-', linewidth=2)
    ax.fill(angles, stats, alpha=0.25)
    ax.set_thetagrids(angles * 180/np.pi, labels)
    plt.yticks(markers)
    ax.set_title(name)
    ax.grid(True)

    fig.savefig("static/images/%s.png" % name)

    return plt.show()

make_radar_chart("Agni", [2,3,4,4,5]) # example

Basically I want the chart to be a pentagon instead of circle. Can anyone help with this. I am using python matplotlib to save an image which will stored and displayed later. I want my chart to have the form of the second picture

EDIT:

    gridlines = ax.yaxis.get_gridlines()
    for gl in gridlines:
        gl.get_path()._interpolation_steps = 5

adding this section of code from answer below helped a lot. I am getting this chart. Still need to figure out how to get rid of the outer most ring:

解决方案

The radar chart demo shows how to make the a radar chart. The result looks like this:

Here, the outer spine is polygon shaped as desired. However the inner grid lines are circular. So the open question is how to make the gridlines the same shape as the spines.

This can be done by overriding the draw method and setting the gridlines' path interpolation step variable to the number of variables of the RadarAxes class.

gridlines = self.yaxis.get_gridlines()
for gl in gridlines:
    gl.get_path()._interpolation_steps = num_vars

Complete example:

import numpy as np

import matplotlib.pyplot as plt
from matplotlib.patches import Circle, RegularPolygon
from matplotlib.path import Path
from matplotlib.projections.polar import PolarAxes
from matplotlib.projections import register_projection
from matplotlib.spines import Spine
from matplotlib.transforms import Affine2D


def radar_factory(num_vars, frame='circle'):
    """Create a radar chart with `num_vars` axes.

    This function creates a RadarAxes projection and registers it.

    Parameters
    ----------
    num_vars : int
        Number of variables for radar chart.
    frame : {'circle' | 'polygon'}
        Shape of frame surrounding axes.

    """
    # calculate evenly-spaced axis angles
    theta = np.linspace(0, 2*np.pi, num_vars, endpoint=False)

    class RadarAxes(PolarAxes):

        name = 'radar'

        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            # rotate plot such that the first axis is at the top
            self.set_theta_zero_location('N')

        def fill(self, *args, closed=True, **kwargs):
            """Override fill so that line is closed by default"""
            return super().fill(closed=closed, *args, **kwargs)

        def plot(self, *args, **kwargs):
            """Override plot so that line is closed by default"""
            lines = super().plot(*args, **kwargs)
            for line in lines:
                self._close_line(line)

        def _close_line(self, line):
            x, y = line.get_data()
            # FIXME: markers at x[0], y[0] get doubled-up
            if x[0] != x[-1]:
                x = np.concatenate((x, [x[0]]))
                y = np.concatenate((y, [y[0]]))
                line.set_data(x, y)

        def set_varlabels(self, labels):
            self.set_thetagrids(np.degrees(theta), labels)

        def _gen_axes_patch(self):
            # The Axes patch must be centered at (0.5, 0.5) and of radius 0.5
            # in axes coordinates.
            if frame == 'circle':
                return Circle((0.5, 0.5), 0.5)
            elif frame == 'polygon':
                return RegularPolygon((0.5, 0.5), num_vars,
                                      radius=.5, edgecolor="k")
            else:
                raise ValueError("unknown value for 'frame': %s" % frame)

        def draw(self, renderer):
            """ Draw. If frame is polygon, make gridlines polygon-shaped """
            if frame == 'polygon':
                gridlines = self.yaxis.get_gridlines()
                for gl in gridlines:
                    gl.get_path()._interpolation_steps = num_vars
            super().draw(renderer)


        def _gen_axes_spines(self):
            if frame == 'circle':
                return super()._gen_axes_spines()
            elif frame == 'polygon':
                # spine_type must be 'left'/'right'/'top'/'bottom'/'circle'.
                spine = Spine(axes=self,
                              spine_type='circle',
                              path=Path.unit_regular_polygon(num_vars))
                # unit_regular_polygon gives a polygon of radius 1 centered at
                # (0, 0) but we want a polygon of radius 0.5 centered at (0.5,
                # 0.5) in axes coordinates.
                spine.set_transform(Affine2D().scale(.5).translate(.5, .5)
                                    + self.transAxes)


                return {'polar': spine}
            else:
                raise ValueError("unknown value for 'frame': %s" % frame)

    register_projection(RadarAxes)
    return theta


data = [['Sulfate', 'Nitrate', 'EC', 'OC1', 'OC2', 'OC3', 'OP', 'CO', 'O3'],
        ('Basecase', [
            [0.88, 0.01, 0.03, 0.03, 0.00, 0.06, 0.01, 0.00, 0.00],
            [0.07, 0.95, 0.04, 0.05, 0.00, 0.02, 0.01, 0.00, 0.00],
            [0.01, 0.02, 0.85, 0.19, 0.05, 0.10, 0.00, 0.00, 0.00],
            [0.02, 0.01, 0.07, 0.01, 0.21, 0.12, 0.98, 0.00, 0.00],
            [0.01, 0.01, 0.02, 0.71, 0.74, 0.70, 0.00, 0.00, 0.00]])]

N = len(data[0])
theta = radar_factory(N, frame='polygon')

spoke_labels = data.pop(0)
title, case_data = data[0]

fig, ax = plt.subplots(figsize=(6, 6), subplot_kw=dict(projection='radar'))
fig.subplots_adjust(top=0.85, bottom=0.05)

ax.set_rgrids([0.2, 0.4, 0.6, 0.8])
ax.set_title(title,  position=(0.5, 1.1), ha='center')

for d in case_data:
    line = ax.plot(theta, d)
    ax.fill(theta, d,  alpha=0.25)
ax.set_varlabels(spoke_labels)

plt.show()

这篇关于如何在python中制作多边形雷达(蜘蛛)图的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆