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

Set theme_gray() as default theme. It improves plots readability.

In [3]:
LetsPlot.set_theme(theme_grey())

Data

In [4]:
labels_df = {
    'x': [0, 1, 2, 3, 4, 5, 6, 7, 8],
    'y': [0, 45, 90, 135, 180, 225, 270, 315, 360],
    'r_y': [360, 315, 270, 225, 180, 135, 90, 45, 0],
    'l': ['l0', 'l45', 'l90', 'l135', 'l180', 'l225', 'l270', 'l315', 'l360'],
    'g': ['g1', 'g1', 'g1', 'g2', 'g2', 'g2', 'g3', 'g3', 'g3']
}

lollipop_df = {
    'c': ['a', 'b', 'c', 'd', 'e', 'f'],
    'x': [1, 2, 3, 4, 5, 6],
    'y': [1, 2, 3, 4, 5, 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]
}
Util for PlotSpecDebugger.kt
In [5]:
def dump_plot(plot, display=False):
    import json

    try:
        import clipboard
    except:
        clipboard = None
        
    from lets_plot._type_utils import standardize_dict
    
    plot_dict = standardize_dict(plot.as_dict())
    plot_json = json.dumps(plot_dict, indent=2)
    
    if clipboard:
        clipboard.copy('')
        clipboard.copy(str(plot_json))
    else:
        if display is None:
            display = True

    return plot

Geoms

geom_area()

Line get transformed into a circle:

In [6]:
p = ggplot() + geom_area(aes(x=[0, 1], y=[1, 1]))
gggrid([
    p,
    p + coord_polar()
])
Out[6]:

flat=True

The plot can be transformed into a radar plot by using flat=True and a discrete x-scale.

In [7]:
p = ggplot(student_df) \
    + geom_area(aes(x='subj_id', y='score'), flat=True) \
    + geom_point(aes(x='subj_id', y='score')) \

labels = { 1: 'progr', 2: 'math', 3: 'physic', 4: 'chemistry', 5: 'biology' }

continuous = scale_x_continuous(labels=labels)
discrete = scale_x_discrete(labels=labels)

gggrid([
    p + continuous,
    p + continuous + coord_polar() + ggtitle('scale_x_continuous'),
    p + discrete + coord_polar() + ggtitle('scale_x_discrete'),
]) 
Out[7]:

geom_segment()

In [8]:
p = ggplot() \
    + geom_segment(x=0, y=0, xend=4, yend=4, arrow=arrow(), size=1) \
    + geom_segment(x=8, y=0, xend=4, yend=4, arrow=arrow(), size=1) \

gggrid([
    p,
    p + coord_polar()
])
Out[8]:

size_end/stroke_end precision length adjustment parameters:

In [9]:
# known problem - zero-length segment because of second datapoint.
# this is a temp workaround to sync stroke/stroke_end and size/size_ens domains
d= { 
    'x': [0,1], 
    'y':[0,0], 
    'size': [8,10], 
    'stroke':[1,2],
    'size_end':[10,0], 
    'stroke_end':[2,0]
}

p = ggplot(d, aes('x','y')) \
    + geom_point(aes(size='size', stroke='stroke'), shape=21, alpha=0.5, color="red", show_legend=False) \
    + geom_segment(
        aes(size_start='size', stroke_start='stroke',size_end='size_end', stroke_end='stroke_end'),
        xend=1, 
        yend=0,
        size=2,
        arrow=arrow(ends='both', type='open', length=22, angle=30), 
    ) \
    + scale_identity(['size','size_start','size_end']) 

gggrid([
    p,
    p + coord_polar(xlim=[-0.35, 1.35], ylim=[-2, 2]) # lims are only to make the figure smiling
])
Out[9]:

geom_label()

Regular scatter plot.

In [10]:
p = ggplot(labels_df, aes(x='x', y='y', label='l')) + geom_label()

gggrid([
    p, 
    p + coord_polar() + ggtitle('coord_polar()'),
    p + coord_polar(theta='y') + ggtitle('theta=y'),
])
Out[10]:

geom_path()

The transform resamples path data by converting straight segments into curves. The flat parameter controls this behaviour.

In [11]:
p = ggplot(labels_df, aes(x='x', y='y', color='y')) + scale_color_brewer(palette='GnBu')

gggrid([
    p + geom_path(size=3) + coord_polar() + ggtitle('coord_polar()'),
    p + geom_path(size=3, flat=True) + coord_polar(theta="x") + ggtitle('coord_polar(), flat=True'),
], ncol=2)
Out[11]:

Autoclose on a discrete x-scale

In [12]:
ggplot(student_df) + geom_path(aes(x='subj', y='score'), flat=True) + coord_polar(ylim=[0, 20])
Out[12]:

geom_lollipop()

See the Params section for details on using the ylim parameters.

In [13]:
p = ggplot(lollipop_df, aes('c', 'y')) + geom_lollipop()

gggrid([
    p, 
    p + coord_polar(),
])
Out[13]:

geom_bar()

This works similarly to rects, but with the addition of tooltips.

position='stack'

In [14]:
from lets_plot.mapping import as_discrete
bar_df = { 'foo': [1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3] }
p = ggplot(bar_df) + geom_bar(aes(fill=as_discrete('foo', order=1)), size=0)

gggrid([
    p,
    p + coord_polar(theta='y') + ggtitle('position=stack, coord_polar(theta=y)'),
    p + coord_polar(theta='x') + ggtitle('position=stack, coord_polar(theta=x)'),
])
Out[14]:

position='dodge'

In [15]:
p = ggplot(bar_df) + geom_bar(aes(fill=as_discrete('foo', order=1)), size=0, position='dodge')

gggrid([
    p,
    p + coord_polar(theta='y') + ggtitle('position=dodge, coord_polar(theta=y)'),
    p + coord_polar(theta='x') + ggtitle('position=dodge, coord_polar(theta=x)'),
])
Out[15]:

stat='identity'

On a continuous x-scale the first and last bars stuck. The expand parameter can be used to fix this.

In [16]:
data = {
    'x': [1, 2, 3],
    'y': [5, 3, 4],
}

bar_id = ggplot(data) + geom_bar(aes('x', 'y'), stat='identity', width=0.8) + coord_polar()

gggrid([
    bar_id + ggtitle('Continuous x'),
    bar_id + scale_x_continuous(expand=[0, 0.1]) + ggtitle('scale_x_continuous(expand=[0, 0.1))')
])
Out[16]:
In [17]:
bar_id + scale_x_discrete() + ggtitle('Discrete x')
Out[17]:
In [18]:
wind_df = pd.read_csv('coord_polar_wind.csv')

def is_float(x):
    try:
        float(x)
    except ValueError:
        return False
    return True


wind_df = wind_df[wind_df.sped.apply(lambda x: is_float(x))]
wind_df = wind_df[wind_df.drct.apply(lambda x: is_float(x))]

wind_df['sped'] = wind_df['sped'].astype(float)
wind_df['drct'] = wind_df['drct'].astype(float)

wind_df = wind_df[wind_df.sped.apply(lambda x: x >= 2.0)]

wind_df.head()
Out[18]:
station valid drct sped
0 PEA 2002-05-17 00:14 330.0 8.05
1 PEA 2002-05-17 00:20 330.0 8.05
2 PEA 2002-05-17 00:34 340.0 10.35
3 PEA 2002-05-17 00:40 340.0 10.35
4 PEA 2002-05-17 00:54 340.0 14.95
In [19]:
# Define the speed bins
bins = [2, 5, 7, 10, 15, 20, float('inf')]
bin_ids = list(range(6))

wind_df['speed_group'] = pd.cut(wind_df['sped'], bins=bins, labels=bin_ids, right=False)

# Group by 'drct' and 'speed_group', and count the occurrences
grouped_counts = wind_df.groupby(['drct', 'speed_group']).size().reset_index(name='count')

# Calculate the total number of observations in the dataset
total_observations = wind_df.shape[0]

# Calculate the percentage of each speed group within each direction relative to the total number of observations
grouped_counts['percentage_of_total'] = (grouped_counts['count'] / total_observations) * 100
In [20]:
from lets_plot.mapping import as_discrete

ggplot(grouped_counts) \
    + geom_bar(aes('drct', 'percentage_of_total', fill=as_discrete('speed_group', order=1)), stat='identity', size=0, width=0.8) \
    + scale_fill_manual(
        name='Wind Speed:', 
        breaks={'2 - 4.9': 0, '5 - 6.9': 1, '7 - 9.9': 2, '10 - 14.9': 3, '15 - 19.9': 4, '20+': 5},
        values=['#002bff', '#03d3f8', '#7afe81', '#fde609', '#ff4404', '#780200'], 
    ) \
    + scale_x_continuous(
        expand=[0, 1],
        breaks={'N': 360, 'NE': 45, 'E': 90, 'SE': 135, 'S': 180, 'SW': 225, 'W': 270, 'NW': 315},
        
    ) \
    + ggsize(800, 800) \
    + coord_polar(ylim=[-1, None], start=(3.14 * 2) / 36 / 2) \
    + theme_minimal2() \
    + theme(
        panel_grid_ontop=True, 
        axis_ontop=True, 
        panel_grid=element_line(color='#A0A0A0')
    )
Out[20]:

geom_hline()/geom_vline()

In [21]:
p = ggplot() \
    + geom_hline(yintercept=5, color='red') \
    + geom_hline(yintercept=10, color='green') \
    + geom_hline(yintercept=15, color='blue') \
    + geom_hline(yintercept=20, color='orange') \
    + geom_vline(xintercept=10, color='pink') \
    + geom_vline(xintercept=20, color='magenta') \
    + geom_vline(xintercept=30, color='dark_green') \
    + xlim(0, 30)\
    + ylim(0, 20)
gggrid([p, p + coord_polar()])
Out[21]:

geom_tile()

In [22]:
x = list(range(24))
d1 = list(np.interp(x, [0, 8, 14, 18, 23], [3, 12, 18, 8, 2]))
d2 = list(np.interp(x, [0, 8, 14, 18, 23], [2, 7, 11, 5, 0]))
d3 = list(np.interp(x, [0, 8, 14, 18, 23], [0, 11, 15, 13, 8]))
d4 = list(np.interp(x, [0, 8, 14, 18, 23], [8, 7, 11, 5, 2]))
d5 = list(np.interp(x, [0, 8, 14, 18, 23], [2, 12, 20, 15, 12]))
d6 = list(np.interp(x, [0, 8, 14, 18, 23], [12, 14, 22, 19, 15]))
d7 = list(np.interp(x, [0, 8, 14, 18, 23], [15, 13, 26, 22, 11]))
temp = d1 + d2 + d3 + d4 + d5 + d6 + d7
day = ([1] * 24) + ([2] * 24) + ([3] * 24) + ([4] * 24) + ([5] * 24) + ([6] * 24) + ([7] * 24)

df = pd.DataFrame({
    "time": [i for i in range(24)] * 7,
    "day": day,
    "temp": temp
})

p = ggplot(df) \
    + geom_tile(aes(x='time', y='day', fill='temp'), tooltips=layer_tooltips().format('^x', '{.1d}:00')) \
    + scale_fill_viridis() \
    + scale_x_continuous(format='{.1d}:00') \
    + scale_y_discrete(breaks={ 'Mon': 1, 'Tue': 2, 'Wen': 3, 'Thu': 4, 'Fri': 5, 'Sat': 6, 'Sun': 7 })

gggrid([
    p,
    p \
        + theme(panel_inset=[0, 20, 0, 20])
        + coord_polar(ylim=[-2, None], start=(-3.14 * 2) / 24 / 2) # ylim=-2 to make the hole, start to align ticks vertically
]) + ggsize(1200, 600)
Out[22]:

geom_rect()

Stacked bars

are transformed into a pie chart

In [23]:
c1 = '#66c2a5'
c2 = '#fc8d62'
c3 = '#8da0cb'
p = ggplot() \
    + geom_rect(xmin=0, xmax=5, ymin=0, ymax=7, fill=c1, size=0) \
    + geom_rect(xmin=0, xmax=5, ymin=7, ymax=11, fill=c2, size=0) \
    + geom_rect(xmin=0, xmax=5, ymin=11, ymax=14, fill=c3, size=0) 

gggrid([
    p,
    p + coord_polar() + ggtitle('coord_polar()'),
    p + coord_polar(theta='y') + ggtitle('coord_polar(theta=y)'),
]).show()

gggrid([
    p + coord_polar(theta='y', direction=-1) + ggtitle('coord_polar(theta=y, dir=-1)'),
    p + coord_polar(theta='y', direction=-1, start=3.14/2) + ggtitle('coord_polar(theta=y, dir=-1, start=PI/2)'),
]).show()

Dodged bars

In [24]:
p = ggplot() \
    + geom_rect(xmin=0, xmax=1, ymin=0, ymax=7, fill=c1, size=0) \
    + geom_rect(xmin=1, xmax=2, ymin=0, ymax=4, fill=c2, size=0) \
    + geom_rect(xmin=2, xmax=3, ymin=0, ymax=3, fill=c3, size=0) \

gggrid([
    p, 
    p + coord_polar(theta='y') + ggtitle('coord_polar(theta=y)'),
    p + coord_polar(theta='x') + ggtitle('coord_polar(theta=x)'),
])
Out[24]:

Horizontal bars

In [25]:
p = ggplot() \
    + geom_rect(ymin=0, ymax=1, xmin=0, xmax=7, fill=c1, size=0) \
    + geom_rect(ymin=1, ymax=2, xmin=0, xmax=4, fill=c2, size=0) \
    + geom_rect(ymin=2, ymax=3, xmin=0, xmax=3, fill=c3, size=0) \

gggrid([
    p, 
    p + coord_polar(theta='y') + ggtitle('coord_polar(theta=y)'),
    p + coord_polar(theta='x') + ggtitle('coord_polar(theta=x)'),
])
Out[25]:

Params

In [26]:
p = ggplot(labels_df, aes(x='x', y='y', color='y')) + geom_path(size=3, show_legend=False) + scale_color_brewer(palette='GnBu')


p + coord_polar() + ggtitle('Default plot with coord_polar()')
Out[26]:

transform_bkgr

When using the transform_bkgr parameter, the panel is not transformed into a circle, but remains a rectangle. This behaviour is similar to ggplot2.

In [27]:
p + coord_polar(transform_bkgr=False) + ggtitle('coord_polar(transform_bkgr=False)')
Out[27]:

direction

In [28]:
p + coord_polar(direction=-1) + ggtitle('coord_polar(direction=-1)')
Out[28]:

start

In [29]:
gggrid([
    p + coord_polar(start=3.14 / 2) + ggtitle('start=PI/2'),
    p + coord_polar(start=-3.14 / 2) + ggtitle('start=-PI/2'),
])
Out[29]:

direction + start

In [30]:
gggrid([
    p + coord_polar(start=3.14 / 2, direction=-1) + ggtitle('dir=-1, start=PI/2'),
    p + coord_polar(start=-3.14 / 2, direction=-1) + ggtitle('dir=-1, start=-PI/2'),
])
Out[30]:

xlim and ylim

The xlim parameter can be used to prevent overlap between the first and last values.
The ylim parameter can be used to shift data away from the centre or the outer circle.

To prevent overlap between 6 and 1, we adjust the xlim to [None, 7] while keeping the default minimum limit as it is not relevant.
In addition, we change ylim to [None, 6.5] to prevent the lollipop's top from overlapping with the outer circle.

In [31]:
p = ggplot(lollipop_df) \
    + geom_lollipop(aes(x='x', y='y'))

gggrid([
    p + coord_polar(),
    p + coord_polar(xlim=[None, 7], ylim=[None, 6.5])
])                
Out[31]:

Scales

Interaction between scales and polar coordinate system.

In [32]:
pie = ggplot() \
    + geom_rect(xmin=0, xmax=1, ymin=0, ymax=7, fill='red', size=0) \
    + geom_rect(xmin=1, xmax=2, ymin=0, ymax=4, fill='blue', size=0) \
    + geom_rect(xmin=2, xmax=3, ymin=0, ymax=3, fill='green', size=0) \
    + coord_polar()

sticks_df = {
    'x': [0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5],
    'y': [0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6],
    'g': [5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0]
}

sticks = ggplot(sticks_df) + geom_path(aes(x='x', y='y', group='g', size='g')) + coord_polar()

Limit

The limits parameter sets the lower and upper limits individually, but expects absolute values.
x=[None, 6] limit to make the first and last elements not overlap.
y=[-2, 7] limit to make a medium sized centre hole and a small outer buffer (with a length of 1).

In [33]:
gggrid([
    sticks + ggtitle('No limits'),
    sticks + lims(x=[None, 6], y=[-2, 7]) + ggtitle('lims(x=[None, 6], y=[-2, 7])'),
])
Out[33]:

Discrete x-scale

If the x-scale is discrete, the coordinate system will automatically adjust domain so that the first and last values don't overlap.

In [34]:
gggrid([
    sticks + ggtitle('Continuous'), 
    sticks + scale_x_discrete() + ggtitle('scale_x_discrete()')
])
Out[34]:

clip-path

Segments should not get rendered outside the panel boundaries.

In [35]:
gggrid([
    sticks,
    sticks + coord_polar(ylim=[0, 1.5])
])
Out[35]:

Expand

By default coord_polar() resets the expansion to zero, but it can still be set explicitly.
Horizontal non-zero expand will produce a gap between first and last sectors, so the plot will never become a circle.
Vertical non-zero expand creates a central hole (expand for the bottom of the domain) and a buffer between the plot and the axis (expand for the top of the domain).

expand is symmetric, so it can't be used to adjust only the bottom or only the top.

In [36]:
gggrid([
    sticks + ggtitle('No expand'),
    sticks \
        + scale_x_continuous(expand=[0, 2.5]) \
        + scale_y_continuous(expand=[0, 2]) \
        + ggtitle('scale_XY_continuous(expand=...)'),
])
Out[36]:

scale_y_log10()

Log-scale works fine.

In [37]:
d = {
    'x': [1, 2, 3, 4, 5, 6, 7, 8],
    'y': [1, 10, 100, 1_000, 10_000, 100_000, 1_000_000, 10_000_000],
}

p = ggplot(d) + geom_path(aes(x='x', y='y'), flat=True)
p

gggrid([
    p,
    p + coord_polar(),
    p + scale_y_log10(),
    p + scale_y_log10(format='.1~e') + coord_polar(),
], ncol=2)
Out[37]:

Theme

In [38]:
p = ggplot(labels_df, aes(x='x', y='y', color='y')) + scale_color_brewer(palette='GnBu') + geom_path(size=3)
polar_p = p + coord_polar()

Themes list

In [39]:
gggrid([
    p,
    polar_p + theme_minimal2() + ggtitle('theme_minimal2()'),
    polar_p + theme_bw() + ggtitle('theme_bw()'),
    polar_p + theme_classic() + ggtitle('theme_classic()'),
    polar_p + theme_grey() + ggtitle('theme_grey()'),
    polar_p + theme_light() + ggtitle('theme_light()'),
    polar_p + theme_minimal() + ggtitle('theme_minimal()'),
    polar_p + theme_none() + ggtitle('theme_none()'),
    polar_p + theme_void() + ggtitle('theme_void()'),
], ncol=3)
Out[39]:

Axis configuration

In [40]:
p_tmp = p + 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'),
    axis_text_y=element_text(color='red'),
)

gggrid([
    p_tmp,
    p_tmp + coord_polar()
])
Out[40]:

panel_inset

The panel_inset parameter can be used to create space between the axis and the panel content:

In [41]:
p_themed = p_tmp
p_themed += theme(
            plot_background=element_rect(fill='pink'),
            panel_background=element_rect(fill='grey'),
            panel_border=element_rect(color='green', size=1),
            panel_inset=[10, 10, 10, 10],
        )

gggrid([
    p_themed,
    p_themed + coord_polar()
])
Out[41]:

Issues

1. Ticks overlapping

In [42]:
data = {
    'x': ['txt 1', 'txt 2', 'txt 3', 'txt 4'],
    'y': [1, 2, 3, 4],
}
ggplot(data, aes('x', 'y')) + geom_point() + coord_polar()
Out[42]:

2. Marginal plot (what to do with them at all)

In [43]:
ggplot(labels_df, aes(x='x', y='y', color='y')) + scale_color_brewer(palette='GnBu') \
    + geom_path(size=3, show_legend=False) \
    + ggmarginal('trlb', layer=geom_line(color='black', show_legend=False)) \
    + coord_polar() \
    + 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'),
        axis_text_y=element_text(color='red'),
        panel_border=element_rect(color='magenta', size=1),
        panel_background=element_rect(fill='orange'),
        panel_inset=[10, 10, 10, 10],
        plot_margin=10,
        plot_background=element_rect(fill='cyan'),
    )
Out[43]:
In [44]:
bar_df = { 'foo': [1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3] }
p = ggplot(bar_df) + geom_bar(aes(fill=as_discrete('foo', order=1)), size=0)

p = ggplot(bar_df) + geom_bar(aes(fill=as_discrete('foo', order=1)), size=0, position='dodge')

gggrid([
    p,
    p + coord_polar(theta='y') + ggtitle('position=dodge, coord_polar(theta=y)'),
    p + coord_polar(theta='x') + ggtitle('position=dodge, coord_polar(theta=x)'),
])
Out[44]:

Regressions

Axis and grid alignment still works in all cases:

  • coord_flip
  • title
  • top/bottom/left/right
  • marginal
  • facet
In [45]:
p = ggplot(labels_df, aes(x='x', y='y', color='y')) + geom_path(size=3) + scale_color_brewer(palette='GnBu') + theme_light()
default = p + ggtitle('Default')
both = p + scale_x_continuous(position='both') + scale_y_continuous(position='both') + ggtitle('Both')
flip = p + coord_flip() + ggtitle('coord_flip()')
flip_both = p + scale_x_continuous(position='both') + scale_y_continuous(position='both') + coord_flip() + ggtitle('both + coord_flip()')
facet = p + facet_grid(x='g') + ggtitle('Facet')
facet_both = both + facet_grid(x='g') + ggtitle('Facet Both')
facet_flip = flip + facet_grid(x='g') + ggtitle('Facet Flip')
facet_flip_both = flip_both + facet_grid(x='g') + ggtitle('Facet Flip Both')

g = gggrid([
    default,
    both,
    flip,
    flip_both,
    facet, 
    facet_both,
    facet_flip,
    facet_flip_both
], ncol=1)

g
Out[45]:
In [46]:
both + coord_polar()
Out[46]:

Single plot review:

In [47]:
p = ggplot(labels_df, aes(x='x', y='y', label='l')) + geom_label()

p_rect_stack = ggplot() \
    + geom_rect(xmin=0, xmax=5, ymin=0, ymax=7, fill=c1, size=0) \
    + geom_rect(xmin=0, xmax=5, ymin=7, ymax=11, fill=c2, size=0) \
    + geom_rect(xmin=0, xmax=5, ymin=11, ymax=14, fill=c3, size=0) \


p_rect_dodge = ggplot() \
    + geom_rect(xmin=0, xmax=1, ymin=0, ymax=7, fill=c1, size=0) \
    + geom_rect(xmin=1, xmax=2, ymin=0, ymax=4, fill=c2, size=0) \
    + geom_rect(xmin=2, xmax=3, ymin=0, ymax=3, fill=c3, size=0) \

gggrid([
    p_rect_dodge, 
    p_rect_dodge + coord_polar(theta='y') + ggtitle('coord_polar(theta=y)'),
    p_rect_dodge + coord_polar(theta='x') + ggtitle('coord_polar(theta=x)'),
])

from lets_plot.mapping import as_discrete
bar_df = { 'foo': [1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3] }
p_bar_stack = ggplot(bar_df) + geom_bar(aes(fill=as_discrete('foo', order=1)), size=0)
p_bar_dodge = ggplot(bar_df) + geom_bar(aes(fill=as_discrete('foo', order=1)), size=0, position='dodge')

# lollipop
p_lollipop = ggplot(lollipop_df, aes('x', 'y')) + geom_lollipop() + coord_polar(xlim=[1, 7])

# radar

p_radar = ggplot(student_df) \
    + geom_path(aes(x='subj', y='score', color='student'), flat=True) \
    + geom_point(aes(x='subj', y='score', color='student'))\
    + coord_polar(ylim=[0, 20])

gggrid([
    p + coord_polar() + ggtitle('coord_polar()'),
    p + coord_polar(theta='y') + ggtitle('theta=y'),
    None,
    p_rect_stack + coord_polar(theta='y') + ggtitle('coord_polar(theta=y)'),
    p_rect_stack + coord_polar(theta='y', direction=-1) + ggtitle('coord_polar(theta=y, dir=-1)'),
    p_rect_stack + coord_polar(theta='x') + ggtitle('coord_polar(theta=x)'),
    p_rect_dodge, 
    p_rect_dodge + coord_polar(theta='y') + ggtitle('coord_polar(theta=y)'),
    p_rect_dodge + coord_polar(theta='x') + ggtitle('coord_polar(theta=x)'),
    p_bar_stack,
    p_bar_stack + coord_polar(theta='y') + ggtitle('position=stack, coord_polar(theta=y)'),
    p_bar_stack + coord_polar(theta='x') + ggtitle('position=stack, coord_polar(theta=x)'),
    p_bar_dodge,
    p_bar_dodge + coord_polar(theta='y') + ggtitle('position=dodge, coord_polar(theta=y)'),
    p_bar_dodge + coord_polar(theta='x') + ggtitle('position=dodge, coord_polar(theta=x)'),
    p_lollipop,
    p_radar
], ncol=3)
Out[47]:

Sandbox

In [48]:
mpg = pd.read_csv('https://raw.githubusercontent.com/JetBrains/lets-plot-docs/master/data/mpg.csv')
mpg
Out[48]:
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
... ... ... ... ... ... ... ... ... ... ... ... ...
229 230 volkswagen passat 2.0 2008 4 auto(s6) f 19 28 p midsize
230 231 volkswagen passat 2.0 2008 4 manual(m6) f 21 29 p midsize
231 232 volkswagen passat 2.8 1999 6 auto(l5) f 16 26 p midsize
232 233 volkswagen passat 2.8 1999 6 manual(m5) f 18 26 p midsize
233 234 volkswagen passat 3.6 2008 6 auto(s6) f 17 26 p midsize

234 rows × 12 columns

In [49]:
mpg2 = mpg.copy()
mpg2['name'] = mpg2.apply(lambda x: f"{x['model']}-{x['displ']}-{x['year']}-{x['trans']}-{x['drv']}", axis=1)
mpg2
Out[49]:
Unnamed: 0 manufacturer model displ year cyl trans drv cty hwy fl class name
0 1 audi a4 1.8 1999 4 auto(l5) f 18 29 p compact a4-1.8-1999-auto(l5)-f
1 2 audi a4 1.8 1999 4 manual(m5) f 21 29 p compact a4-1.8-1999-manual(m5)-f
2 3 audi a4 2.0 2008 4 manual(m6) f 20 31 p compact a4-2.0-2008-manual(m6)-f
3 4 audi a4 2.0 2008 4 auto(av) f 21 30 p compact a4-2.0-2008-auto(av)-f
4 5 audi a4 2.8 1999 6 auto(l5) f 16 26 p compact a4-2.8-1999-auto(l5)-f
... ... ... ... ... ... ... ... ... ... ... ... ... ...
229 230 volkswagen passat 2.0 2008 4 auto(s6) f 19 28 p midsize passat-2.0-2008-auto(s6)-f
230 231 volkswagen passat 2.0 2008 4 manual(m6) f 21 29 p midsize passat-2.0-2008-manual(m6)-f
231 232 volkswagen passat 2.8 1999 6 auto(l5) f 16 26 p midsize passat-2.8-1999-auto(l5)-f
232 233 volkswagen passat 2.8 1999 6 manual(m5) f 18 26 p midsize passat-2.8-1999-manual(m5)-f
233 234 volkswagen passat 3.6 2008 6 auto(s6) f 17 26 p midsize passat-3.6-2008-auto(s6)-f

234 rows × 13 columns

In [50]:
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_void()
Out[50]:
In [51]:
p_default = 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)) \
    + ggsize(800, 400) \
    + ggtitle('default') \
    + coord_polar() 
p_default.show()

inset = [20, 140, 30, 120]
p_adjusted = 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_text_x=element_text(angle=10), 
        panel_inset=inset
    ) \
    + ggsize(900, 500) \
    + ggtitle('panel_inset=' + str(inset)) \
    + coord_polar() 
p_adjusted.show()
In [52]:
p = ggplot(labels_df, aes(x='x', y='y', label='l')) + geom_point(alpha=0.3, color='red') + geom_text()
gggrid([
    p + coord_polar(theta='x'),
    p + coord_polar(theta='y'),
])
Out[52]:
In [53]:
p = ggplot() + geom_density2df(aes(x=[0, 1], y=[0, 1], fill='..level..'), bins=6, show_legend=False) 

gggrid([
    p,
    p + coord_polar()
])
Out[53]:
In [54]:
p = ggplot() + geom_density2df(aes(x=[0, 1], y=[0, 1], fill='..level..'), bins=2, show_legend=False) 

gggrid([
    p,
    p + coord_polar()
])
Out[54]:
In [55]:
p = ggplot() + geom_density2df(aes(x=[1, 0, 0, 1], y=[1, 0, 1, 0], fill='..level..'), bins=2, show_legend=False) 

gggrid([
    p,
    p + coord_polar()
])
Out[55]:
In [56]:
import numpy as np
from lets_plot import *
LetsPlot.setup_html()
n = 1000
np.random.seed(42)
x = np.random.normal(size=n)
y = np.random.normal(size=n)

p = ggplot({'x': x, 'y': y}, aes(x='x', y='y')) + \
    geom_density2df(aes(fill='..group..'), show_legend=False) + \
    scale_fill_brewer(type='seq', palette='GnBu', direction=-1)

gggrid([
    p,
    p + coord_polar()
])
Out[56]:
In [ ]: