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

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

问题描述

  import matplotlib.pyplot as plt 
import numpy as np

labels = ['Siege','Initiation','Crowd_control',' Wave_clear','Objective_damage']
标记= [0、1、2、3、4、5]
str_markers = [ 0, 1, 2, 3, 4, 5]

def make_radar_chart(名称,统计信息,attribute_labels =标签,plot_markers =标记,plot_str_markers = str_markers):

标签= 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(角度* 180 / np.pi,标签)
plt.yticks(标记)
ax.set_title(名称)
ax.grid(真)

图savefig( static / images /%s.png%名称)

return plt.s how()

make_radar_chart( Agni,[2,3,4,4,5])#示例





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



编辑:

 网格线= ax.yaxis.get_gridlines()
用于网格线中的gl:
gl.get_path()._ interpolation_steps = 5

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

解决方案



在这里,外脊柱是所需的多边形。但是,内部网格线是圆形的。
因此,悬而未决的问题是如何使网格线与脊椎相同。



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

 网格线= self.yaxis.get_gridlines()
for gl in gridlines:
gl.get_path()._ interpolation_steps = num_vars

完整示例:

 将numpy导入为np 

从matplotlib.patches导入为plt
的matplotlib.pyplot从matplotlib.pathches导入Circle,RegularPolygon
从matplotlib.projections导入路径
。 polar从matplotlib.projections导入register_projection
从matplotlib.spines导入Spine
从matplotlib.transforms导入Affine2D


defradar_factory(num_vars, frame ='circle'):
使用`num_vars`轴创建雷达图。

此函数创建RadarAxes投影并进行注册。

参数
----------
num_vars:int
雷达图变量的数量。
框:{’圆圈 | 'polygon'}
围绕轴的框架形状。


#计算均匀间隔的轴角
theta = np.linspace(0,2 * np.pi,num_vars,endpoint = False)

类RadarAxes(PolarAxes):

name ='radar'

def __init __(self,* args,** kwargs):
super( ).__ init __(* args,** kwargs)
#旋转绘图,使第一个轴位于
self的顶部。set_theta_zero_location('N')

def fill(自我,* 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]!= x [-1],则x [0],y [0]处的标记会加倍

x = np.concatenate((x,[x [ 0]]]))
y = np.concatenate((y,[y [0]]))
line.set_data(x,y)

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

def _gen_axes_patch(self):
#轴补丁必须以(0.5,0.5)为中心),并且在轴坐标中的半径为0.5
#。
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)
否则:
提高ValueError('frame'的未知值:%s%frame)

def draw(self,renderer):
绘制。如果框架是多边形,则使framelines为多边形
如果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):
如果frame =='circle':
return super()._ gen_axes_spines()
elif frame =='polygon':
#spine_type必须为'left'/'right'/'top' /底部 /圆圈。
spine = Spine(轴=自我,
spine_type ='circle',
path = Path.unit_regular_polygon(num_vars))
#unit_regular_polygon给出一个以$为中心的半径为1的多边形b $ b#(0,0),但我们希望在轴坐标中以(0.5,
#0.5)为中心的半径为0.5的多边形。
spine.set_transform(Affine2D()。scale(.5).translate(.5,.5)
+ self.transAxes)


return {' Polar':spine}
else:
提高ValueError('frame'的未知值:%s%frame)

register_projection(RadarAxes)
返回theta


数据= [['硫酸盐','硝酸盐','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(data [0 ])
theta =雷达工厂(N,框架=多边形)

条幅标签= data.pop(0)
标题,case_data = data [0]

无花果,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')$ b $在case_data中d的b

line = 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天全站免登陆