# Chart Type - Radial Bar Chart in Matplotlib (Python)

Here's a script that takes a data frame with two values, the current and benchmark, and returns radial bar charts to plot progress toward a goal. You can also choose a color using the color_theme parameter that takes values 'Grey', 'Purple, 'Blue', 'Green', 'Orange', or 'Red'.

Reason to use it:

One of the most common questions an analyst is asked is how close am I to a goal? We had a great thread about doing this in a variety of ways here. The most common tool that people default to on these is to use a gauge chart, which albeit feeling familiar with driving a car, is not necessarily appropriate for visually encoding in an elegant way.

With Python in Periscope now, we can start to create custom chart types that are awesome and fit use-cases perfectly. The biggest issue with gauge charts is when you go beyond 100%, do you loop back to the beginning of the gauge or just adjust where 100% is? If we take a step back and look at how the visual could work, we can assemble a few radial components into a full 360° = 100%. That way, when we want a 200%, we can just have multiple 360° rings.

```#######################################################################
###                                                        LIBRARIES                                                            ###
#######################################################################
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np

#######################################################################
###                                                    HELPER FUNCTIONS                                                ###
#######################################################################

#USE: Create an array structure for rings.
#INPUT: a df of row length 1 with the first column as the current metric value and the second colum is the target metric value
#OUTPUT: an aray of arrays representing each ring
def calculate_rings(df):
if df.iloc[0,0] < df.iloc[0,1]:
rings=[[df.iloc[0,0],df.iloc[0,1]-df.iloc[0,0]],[0,0]]
elif df.iloc[0,0] / df.iloc[0,1] < 2:
rings=[[df.iloc[0,0],0],[df.iloc[0,0] % df.iloc[0,1], df.iloc[0,1]-df.iloc[0,0] % df.iloc[0,1]]]
else:
rings = [[0,0],[0,0]]
return rings

#USE: Determine if the label for the rotating number label should be left/center/right
#INPUT: a df of row length 1 with the first column as the current metric value and the second colum is the target metric value
#OUTPUT: the proper text alignment
def horizontal_aligner(df):
metric = 1.0 * df.iloc[0,0] % df.iloc[0,1] / df.iloc[0,1]
if metric in (0, 0.5):
align = 'center'
elif metric < 0.5:
align = 'left'
else:
align = 'right'
return align

def vertical_aligner(df):
metric = 1.0 * df.iloc[0,0] % df.iloc[0,1] / df.iloc[0,1]
if metric < 0.25:
align = 'bottom'
elif metric < 0.75:
align = 'top'
elif metric > 0.75:
align = 'bottom'
else:
align = 'center'
return align

#USE: Create a center label in the middle of the radial chart.
#INPUT: a df of row length 1 with the first column as the current metric value and the second column is the target metric value
#OUTPUT: the proper text label
def add_center_label(df):
percent = str(round(1.0*df.iloc[0, 0]/df.iloc[0, 1]*100,1)) + '%'
return plt.text(0,
0.2,
percent,
horizontalalignment='center',
verticalalignment='center',
fontsize = 40,
family = 'sans-serif')

#USE: Formats a number with the apropiate currency tags.
#INPUT: a currency number
#OUTPUT: the properly formmated currency string
def get_currency_label(num):
currency = ''
if num < 10**3:
currency = '\$' + str(num)
elif num < 10**6:
currency = '\$' + str(round(1.0*num/10**3,1)) + 'K'
elif df.iloc[0,0] < 10**9:
currency = '\$' + str(round(num/10**6,1)) + 'M'
else:
currency = '\$' + str(round(num/10**9,1)) + 'B'

return currency

#USE: Create a dynamic outer label that servers a pointer on the ring.
#INPUT: a df of row length 1 with the first column as the current metric value and the second column is the target metric value
#OUTPUT: the proper text label at the apropiate position
def add_current_label(df):
currency = get_currency_label(df.iloc[0,0])
print('vertical: ' + vertical_aligner(df))
print('horizontal: ' + horizontal_aligner(df))
return plt.text(1.5 * np.cos(0.5 *np.pi - 2 * np.pi * (float(df.iloc[0,0]) % df.iloc[0,1] /df.iloc[0,1])),
1.5 * np.sin(0.5 *np.pi - 2 * np.pi * (float(df.iloc[0,0]) % df.iloc[0,1] / df.iloc[0,1])),
currency,
horizontalalignment=horizontal_aligner(df),
verticalalignment=vertical_aligner(df),
fontsize = 20,
family = 'sans-serif')

def add_sub_center_label(df):
amount = 'Goal: ' + get_currency_label(df.iloc[0,1])
return plt.text(0,
-.1,
amount,
horizontalalignment='center',
verticalalignment='top',
fontsize = 22,family = 'sans-serif')

#######################################################################
###                                                    MAIN FUNCTION                                                        ###
#######################################################################
def create_radial_chart(df, color_theme = 'Purple'):

# base styling logic
color = plt.get_cmap(color_theme + 's')
ring_width = 0.3
outer_radius = 1.5
inner_radius = outer_radius - ring_width

# set up plot
ring_arrays = calculate_rings(df)
fig, ax = plt.subplots()

if df.iloc[0, 0] > df.iloc[0, 1]:
ring_to_label = 0
outer_edge_color = None
inner_edge_color = 'white'
else:
ring_to_label = 1
outer_edge_color, inner_edge_color = ['white', None]

# plot logic
outer_ring, _ = ax.pie(ring_arrays[0],radius=1.5,
colors=[color(0.9), color(0.15)],
startangle = 90,
counterclock = False)
plt.setp( outer_ring, width=ring_width, edgecolor=outer_edge_color)

inner_ring, _ = ax.pie(ring_arrays[1],
radius=inner_radius,
colors=[color(0.55), color(0.05)],
startangle = 90,
counterclock = False)
plt.setp(inner_ring, width=ring_width, edgecolor=inner_edge_color)

# add labels and format plots
add_center_label(df)
add_current_label(df)
add_sub_center_label(df)
ax.axis('equal')
plt.margins(0,0)
plt.autoscale('enable')

return plt

# call the chart maker function and display the chart
periscope.output(create_radial_chart(df, color_theme='Purple'))
# Currently supported color themes: Grey, Purple, Blue, Green, Orange, Red

```
1reply Oldest first
• Oldest first
• Newest first
• Active threads
• Popular
• An additional layer of information can be introduced using a subtitle below the percentage. Thanks to  Kyle Dempsey for developing a simple function to do this. Here's the code with an adjusted position of the percent and subtitle.

```def add_center_label(df):
percent = str(round(1.0*df.iloc[0, 0]/df.iloc[0, 1]*100)) + '%'
return plt.text(0,
0.2,
percent,
horizontalalignment='center',
verticalalignment='center',
fontsize = 40,
family = 'sans-serif')

def add_sub_center_label(df):
amount = 'Goal: ' + get_currency_label(df.iloc[0,1])
return plt.text(0,
-0.1,
amount,
horizontalalignment='center',
verticalalignment='top',
fontsize = 22,family = 'sans-serif')
```
Reply Like 1
Like6 Follow
• 6 Likes
• 1 yr agoLast active
• 1Replies
• 2115Views
• 1 Following