Daniel
Daniel

Reputation: 101

getting matplotlib radar plot with pandas

I am trying to go a step further by creating a radar plot like this question states. I using the same source code that the previous question was using, except I'm trying to implement this using pandas dataframe and pivot tables.

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()

So, let's say I have this StringIO that has a list of Group1 voting either yes or no and they are from a numbered type..these numbers are arbitrary in labeling but just as an example..

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)

The error I get is Value Error: x and y must have same first dimension. As I indicated in my script, I thought I had a solution for it but apparently I'm going by it the wrong way. Does anyone have any advice or guidance?

Upvotes: 0

Views: 7381

Answers (1)

ImportanceOfBeingErnest
ImportanceOfBeingErnest

Reputation: 339440

Since I'm completely lost in what you are trying to do, I will simply provide a solution on how to draw a radar chart from the given data.

It will answer the question how often have people voted Yes or No.

enter image description here

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()

Upvotes: 6

Related Questions