0

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?

Community
  • 1
  • 1
Daniel
  • 77
  • 3
  • 20
  • Please read and understand [mcve]. Also, when reporting about an error, include full traceback, indicating the line where the error occurs. Those Value errors are usually easy to find, once a minimal example exists, by inspecting the shape of the arrays that are plotted. – ImportanceOfBeingErnest Mar 18 '17 at 20:23
  • What am i missing? I followed the guidelines. – Daniel Mar 19 '17 at 12:23
  • The code you have is not runnable (there are missing variables and syntax errors) and it's not minimal (e.g. setting a color is not necessary to produce the error, I would guess.). Without being able to run the code I cannot reproduce the error. But you do not even tell where the error occurs (which line) and what it really is (traceback missing). At the end it's your own choice, if you want to have someone help you or not. – ImportanceOfBeingErnest Mar 19 '17 at 12:29
  • The color is the issue. The syntax errors and missing variables...I thought i had the minimal since i have to print my code out and type it by hand to get it here. Im not perfect. Thats why this is on here. This is the sixth time you gave me negative points with just the generic claimer without any real comments about what is wrong. I am actually genuinely trying to get help. – Daniel Mar 19 '17 at 12:33
  • I missed importing numpy as np. – Daniel Mar 19 '17 at 12:33
  • I'm not mean or pendantic. I'm really trying to help. I did try to copy your code and run it. The problem is not that some numpy import is missing, and I can easily fix some syntax errors, but if then there are still missing variables around, I can't do anything for you. The code is then also much too long to find out where the error would occur without being able to run it. How can it be that you need to type up your code? Just copy and paste it. What hinders you to include the error traceback? – ImportanceOfBeingErnest Mar 19 '17 at 12:39
  • I'm using an embedded system. I'm not able to transfer anything from it. I suppose for you to understand that this is the minimal code...the radar axes class is what makes this polar/radar plot look exactly how i need it to look like. The radar factory function interprets to the radar axes. I need to figure out how to be able to make my own colormaps depending on how many unique entities come into play. So the Rcolor and Gcolor gets produced for this example. The issue I have is that 'ax.axvspan' is supposed to allow the x and y coordinates to line up..i think. At least thats how it should b – Daniel Mar 19 '17 at 12:47
  • The error that showed up was what i started below. I thought that you or anyone else might have already tried zipping colors with unique entities on a polar plot. – Daniel Mar 19 '17 at 12:50
  • Started =stated – Daniel Mar 19 '17 at 12:50
  • The missing variables that prevent me from running the code are **(1)** `which` in the line `N = len(df[which].unique())`, **(2)** `final_df` in the line `for d,f,R,G in zip(radii,final_df.index,Rcolors,Gcolors):` and **(3)** `loc` in the legend. – ImportanceOfBeingErnest Mar 19 '17 at 13:02
  • Ok. I fixed them. Sorry, i do this everytime on my phone. I miss a lot of the changes i make to simplify the problem. Yes, i dont have a pc that connects to the net. – Daniel Mar 19 '17 at 13:11
  • Revenge downvoting? I did no such thing. – Daniel Mar 20 '17 at 09:48
  • It's still completely unclear what you want to plot. You have 3 names and thus a list `theta`with 3 items. In order to plot a graph, you need to select 3 other datapoints to plot against those. – ImportanceOfBeingErnest Mar 20 '17 at 12:59

1 Answers1

6

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()
ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712
  • Thank you! I know I was going by it the wrong way. I explained what I wanted to do. I wanted to know how to plot exactly this with pandas. This was just an example and you showed me exactly what aspect i was missing. So, lesson learned: I would still need to make a pivot table and concatenate the x and y coordinates in numpy for all individual plots in order to have the first dimension of x and y coordinates to be the same. Right? – Daniel Mar 20 '17 at 18:38
  • I don't know if you have to do it, but if the plot is what you're after, it's at least one possible solution that - as shown here - works. – ImportanceOfBeingErnest Mar 20 '17 at 18:41
  • How come pandas does not have a function to do this automatically? – Daniel Mar 20 '17 at 18:42
  • I can't follow you. `pd.pivot_table()` is a single command, that **is** pretty automatic, right? – ImportanceOfBeingErnest Mar 20 '17 at 18:46
  • I meant the polar/radar plot. It just seems that pandas should have more automation to build those plots...like pivot.plot(type='polar'...) where the x and y is formatted. – Daniel Mar 20 '17 at 18:50
  • Personally I don't agree. The more specialized cases you introduce into a plotting library to harder it becomes to manage and there will always be cases, where the automated solution fails. However, if you are convinced that this kind of plot needs to be inside pandas, you may very well file a feature request at their site. – ImportanceOfBeingErnest Mar 20 '17 at 19:09
  • I've been looking into this and the existing Pandas plot types all use the cartesian plane and axes (even the pie chart). To do a polar plot, Pandas would have to add a polar axis, potentially replacing an existing axis if one has already been created by the user (e.g. when using `plt.subplots`). – Bill Sep 26 '21 at 16:17