Polar Coordinate System

The polar coordinate system is used for pie charts and polar plots.

Note that the polar coordinate system may change the default expansion for position scales:

  • Discrete x-scale is expanded so that the first and last values don't overlap.
  • Continuous position scales have their expands set to zero so that the pie chart doesn't get an inner hole, and the first and last sectors don't get a gap.
In [1]:
import numpy as np
import pandas as pd

from lets_plot import *
from lets_plot.mapping import as_discrete
In [2]:
LetsPlot.setup_html()
LetsPlot.set_theme(theme_grey())
In [3]:
mpg = pd.read_csv('https://raw.githubusercontent.com/JetBrains/lets-plot-docs/master/data/mpg.csv')
mpg.head()
Out[3]:
Unnamed: 0 manufacturer model displ year cyl trans drv cty hwy fl class
0 1 audi a4 1.8 1999 4 auto(l5) f 18 29 p compact
1 2 audi a4 1.8 1999 4 manual(m5) f 21 29 p compact
2 3 audi a4 2.0 2008 4 manual(m6) f 20 31 p compact
3 4 audi a4 2.0 2008 4 auto(av) f 21 30 p compact
4 5 audi a4 2.8 1999 6 auto(l5) f 16 26 p compact

Pie Chart

geom_bar() with coord_polar() get transformed into a pie chart. Note the theta='y' parameter.

In [4]:
bars = ggplot(mpg) + geom_bar(aes(fill=as_discrete('cyl')), size=0)
gggrid([
    bars + ggtitle('From Bar Chart'),
    bars + coord_polar(theta='y') + ggtitle('To Pie Chart')
])
Out[4]:

Various Bar Charts in the Polar Coordinate System

In [5]:
bull_eye = ggplot(mpg) \
        + geom_bar(aes(fill=as_discrete('cyl')), size=0, show_legend=False)
    
radial_bar = ggplot(mpg) \
        + geom_bar(aes(fill=as_discrete('cyl')), size=0, position='dodge', show_legend=False)
    
coxcomb = ggplot(mpg) \
        + geom_bar(aes(fill=as_discrete('cyl')), size=0, position='dodge', show_legend=False)


gggrid([
    bull_eye + coord_polar() + ggtitle('Bulls eye'),
    radial_bar + coord_polar(theta='y') + ggtitle('Radial bar chart'),
    coxcomb + coord_polar() + ggtitle('Coxcomb plot'),

    bull_eye + ggtitle('was:'),
    radial_bar + ggtitle('was:'),
    coxcomb + ggtitle('was:'),
], ncol=3)
Out[5]:

Radar Plot

Use the flat parameter in path-based geoms to construct a radar plot.

In [6]:
student_df = {
    'subj': ['progr', 'math', 'physic', 'chemistry', 'biology'],
    'subj_id': [1, 2, 3, 4, 5],
    'student': ['John'] * 5,
    'score': [19, 15, 18, 12, 9]
}
labels = { 1: 'progr', 2: 'math', 3: 'physic', 4: 'chemistry', 5: 'biology' }

p = (ggplot(student_df) 
    + geom_area(aes(x='subj_id', y='score'), 
                flat=True)                      # <-- Flat. I.e., do not transform segments to curves.
    + geom_point(aes(x='subj_id', y='score')) 
    + scale_x_discrete(labels=labels) 
    + coord_polar())
p
Out[6]:

Heatmap Plot

In [7]:
from scipy.interpolate import griddata

max_r = 100
max_theta = 2.0 * np.pi
number_points = 5_000
np.random.seed = 42

grid_r, grid_theta = np.meshgrid(
    np.linspace(0, max_r, 200), # r
    np.linspace(0.0, max_theta, 100) # theta
)

points = np.random.rand(number_points, 2) * [max_r, max_theta]
values = points[:,0] * np.sin(points[:,1])**2 * np.cos(points[:,1])**2
data = griddata(points, values, (grid_r, grid_theta), method='cubic',fill_value=0)

df = {
    'x': grid_theta.flatten(), 
    'y': grid_r.flatten(), 
    'z': data.flatten()
}

p = ggplot(df) \
    + geom_tile(aes('x', 'y', color='z', fill='z'), size=1, tooltips='none') \
    + scale_brewer(['color', 'fill'], palette='Spectral', direction=-1) \
    + theme(axis_title=element_blank())

gggrid([
    p + coord_cartesian(),
    p + coord_polar() + theme(legend_position='none'),
])
Out[7]:

Theming

The polar coordinate system supports axis theme options, incorporates specialized logic for the theme's panel_inset parameter, and offers its own transform_bkgr option to manage plot panel transformation:

In [8]:
ggplot(mpg) \
    + geom_bar(aes(x='model', y='cty', fill='cty'), stat='identity', position='dodge') \
    + scale_fill_gradient(low='red', high='white', limits=(5,40)) \
    + theme(
        axis_line_y=element_line(color='red', size=2),
        axis_line_x=element_line(color='blue', size=2),
        axis_ticks_length_y=5,
        axis_ticks_length_x=10,
        axis_ticks_y=element_line(size=5, color='red'), 
        axis_ticks_x=element_line(size=3, color='blue'),
        axis_text_x=element_text(color='blue', angle=10),
        axis_text_y=element_text(color='red'),
        panel_inset=[20, 140, 30, 135]                      # New! Expand the panel to fit axis labels.
    ) \
    + ggsize(900, 500) \
    + coord_polar(transform_bkgr=False)                     # Keep the old school rectangular background.
Out[8]: