使用 pandas 获取 matplotlib 雷达图

getting matplotlib radar plot with pandas

我正在尝试更进一步,创建一个类似于上一个问题所使用的 this question states. I using the same source code 的雷达图,但我正在尝试使用 pandas 数据框和数据透视表来实现它。

import numpy as np
import pandas as pd
from StringIO import StringIO

import matplotlib.pyplot as plt
from matplotlib.projections.polar import PolarAxes
from matplotlib.projections import register_projection


def radar_factory(num_vars, frame='circle'):
    """Create a radar chart with `num_vars` axes."""
    # calculate evenly-spaced axis angles
    theta = 2 * np.pi * np.linspace(0, 1 - 1. / num_vars, num_vars)
    # rotate theta such that the first axis is at the top
    theta += np.pi / 2

    def draw_poly_frame(self, x0, y0, r):
        # TODO: use transforms to convert (x, y) to (r, theta)
        verts = [(r * np.cos(t) + x0, r * np.sin(t) + y0) for t in theta]
        return plt.Polygon(verts, closed=True, edgecolor='k')

    def draw_circle_frame(self, x0, y0, r):
        return plt.Circle((x0, y0), r)

    frame_dict = {'polygon': draw_poly_frame, 'circle': draw_circle_frame}
    if frame not in frame_dict:
        raise ValueError, 'unknown value for `frame`: %s' % frame

    class RadarAxes(PolarAxes):
        """Class for creating a radar chart (a.k.a. a spider or star chart)
        http://en.wikipedia.org/wiki/Radar_chart
        """
        name = 'radar'
        # use 1 line segment to connect specified points
        RESOLUTION = 1
        # define draw_frame method
        draw_frame = frame_dict[frame]

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

        def plot(self, *args, **kwargs):
            """Override plot so that line is closed by default"""
            lines = super(RadarAxes, self).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(theta * 180 / np.pi, labels)

        def _gen_axes_patch(self):
            x0, y0 = (0.5, 0.5)
            r = 0.5
            return self.draw_frame(x0, y0, r)

    register_projection(RadarAxes)
    return theta

def day_radar_plot(df):
    fig = plt.figure(figsize=(6,6))
    #adjust spacing around the subplots
    fig.subplots_adjust(wspace=0.25,hspace=0.20,top=0.85,bottom=0.05)

    ldo,rup = 0.1,0.8         #leftdown and right up normalized

    ax = fig.add_axes([ldo,ldo,rup,rup],polar=True)

    N = len(df['Group1'].unique())
    theta = radar_factory(N)

    polar_df = pd.DataFrame(df.groupby([df['Group1'],df['Type'],df['Vote']]).size())
    polar_df.columns = ['Count']

    radii = polar_df['Count'].get_values()
    names = polar_df.index.get_values()

    #get the number of unique colors needed
    num_colors_needed = len(names)

    #Create the list of unique colors needed for red and blue shades
    Rcolors = []
    Gcolors = []

    for i in range(num_colors_needed):
        ri=1-(float(i)/float(num_colors_needed))
        gi=0.
        bi=0.

        Rcolors.append((ri,gi,bi))

    for i in range(num_colors_needed):
        ri=0.
        gi=1-(float(i)/float(num_colors_needed))
        bi=0.

        Gcolors.append((ri,gi,bi))

    from_x  = np.linspace(0,0.95,num_colors_needed)
    to_x = from_x + 0.05

    i = 0

    for d,f,R,G in zip(radii,polar_df.index,Rcolors,Gcolors):
        i = i+1
        if f[2].lower() == 'no':
            ax.plot(theta,d,color=R)
            ax.fill(theta,d,facecolor=R,alpha=0.25)

            #this is where I think i have the issue
            ax.axvspan(from_x[i],to_x[i],color=R)

        elif f[2].lower() == 'yes':
            ax.plot(theta,d,color=G)
            ax.fill(theta,d,facecolor=G,alpha=0.25)

            #this is where I think i have the issue
            ax.axvspan(from_x[i],to_x[i],color=G)



    plt.show()

所以,假设我有一个 StringIO,它有一个 Group1 投票赞成或反对的列表,它们来自编号类型。这些数字在标记中是任意的,但仅作为示例..

fakefile = StringIO("""\
Group1,Type,Vote
James,7,YES\nRachael,7,YES\nChris,2,YES\nRachael,9,NO
Chris,2,YES\nChris,7,NO\nRachael,9,NO\nJames,2,NO
James,7,NO\nJames,9,YES\nRachael,9,NO
Chris,2,YES\nChris,2,YES\nRachael,7,NO
Rachael,7,YES\nJames,9,YES\nJames,9,NO
Rachael,2,NO\nChris,2,YES\nRachael,7,YES
Rachael,9,NO\nChris,9,NO\nJames,7,NO
James,2,YES\nChris,2,NO\nRachael,9,YES
Rachael,9,YES\nRachael,2,NO\nChris,7,YES
James,7,YES\nChris,9,NO\nRachael,9,NO\n
Chris,9,YES
""")

record = pd.read_csv(fakefile, header=0)
day_radar_plot(record)

我得到的错误是 Value Error: x and y must have same first dimension。 正如我在脚本中指出的那样,我认为我有一个解决方案,但显然我走错了路。有人有什么建议或指导吗?

由于我完全迷失在你正在尝试做的事情中,我将简单地提供一个关于如何从给定数据绘制雷达图的解决方案。

它将回答人们投票赞成或反对的频率这个问题。

import pandas as pd
import numpy as np
from StringIO import StringIO
import matplotlib.pyplot as plt


fakefile = StringIO("""\
Group1,Type,Vote
James,7,YES\nRachael,7,YES\nChris,2,YES\nRachael,9,NO
Chris,2,YES\nChris,7,NO\nRachael,9,NO\nJames,2,NO
James,7,NO\nJames,9,YES\nRachael,9,NO
Chris,2,YES\nChris,2,YES\nRachael,7,NO
Rachael,7,YES\nJames,9,YES\nJames,9,NO
Rachael,2,NO\nChris,2,YES\nRachael,7,YES
Rachael,9,NO\nChris,9,NO\nJames,7,NO
James,2,YES\nChris,2,NO\nRachael,9,YES
Rachael,9,YES\nRachael,2,NO\nChris,7,YES
James,7,YES\nChris,9,NO\nRachael,9,NO\n
Chris,9,YES""")

df = pd.read_csv(fakefile, header=0)
df["cnt"] = np.ones(len(df))

pt = pd.pivot_table(df, values='cnt', index=['Group1'],
                  columns=['Vote'], aggfunc=np.sum)

fig = plt.figure()
ax = fig.add_subplot(111, projection="polar")

theta = np.arange(len(pt))/float(len(pt))*2.*np.pi
l1, = ax.plot(theta, pt["YES"], color="C2", marker="o", label="YES")
l2, = ax.plot(theta, pt["NO"], color="C3", marker="o", label="NO")

def _closeline(line):
    x, y = line.get_data()
    x = np.concatenate((x, [x[0]]))
    y = np.concatenate((y, [y[0]]))
    line.set_data(x, y)
[_closeline(l) for l in [l1,l2]]

ax.set_xticks(theta)
ax.set_xticklabels(pt.index)
plt.legend()
plt.title("How often have people votes Yes or No?")
plt.show()