Download notebook (.ipynb)

Coordinate Systems#

The coordinate system determines how the x and y aesthetics combine to position elements in the plot.

import pandas as pd

from lets_plot import *
from lets_plot.geo_data import *
The geodata is provided by © OpenStreetMap contributors and is made available here under the Open Database License (ODbL).
LetsPlot.setup_html()
df = pd.read_csv('https://raw.githubusercontent.com/JetBrains/lets-plot-docs/master/data/mpg.csv')
print(df.shape)
df.head()
(234, 12)
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
states_gdf = geocode_states().scope('UK').get_boundaries(6)
states_gdf.head()
state found name geometry
0 Wales Wales MULTIPOLYGON (((-5.35304 51.86591, -5.33064 51...
1 Scotland Scotland MULTIPOLYGON (((-8.61762 57.82752, -8.56173 57...
2 England England MULTIPOLYGON (((-6.33758 49.94591, -6.34859 49...
3 Northern Ireland Northern Ireland MULTIPOLYGON (((-8.00601 54.54594, -7.85066 54...

1. Cartesian Coordinates#

The Cartesian coordinate system is the most familiar and common type of coordinate system. Setting limits on the coordinate system will zoom the plot like you’re looking at it with a magnifying glass. It does not change the underlying data as setting limits on a scale does.

Typical use case: changing limits of axes.

gggrid([
    ggplot(df, aes('cty', 'hwy')) + geom_point() + ggtitle('Default'),
    ggplot(df, aes('cty', 'hwy')) + geom_point() + \
        ggtitle('With coord_cartesian()') + \
        coord_cartesian(xlim=[10, 25], ylim=[10, 35]) 
])

2. Fixed Coordinates#

A fixed scale coordinate system forces a specified ratio between the physical representations of data units on the axes.

Typical use case: stretching a plot along one of the axes.

gggrid([
    ggplot(df, aes('cty', 'hwy')) + geom_point() + ggtitle('Default'),
    ggplot(df, aes('cty', 'hwy')) + geom_point() + \
        ggtitle('With coord_fixed()') + \
        coord_fixed(ratio=1)
])

Fixing coordinates could be combined with changing limits as for coord_cartesian().

gggrid([
    ggplot(df, aes('cty', 'hwy')) + geom_point() + \
        coord_fixed(ratio=1) + ggtitle('Stretching only'),
    ggplot(df, aes('cty', 'hwy')) + geom_point() + \
        ggtitle('With changing limits') + \
        coord_fixed(ratio=1, xlim=[10, 30], ylim=[10, 40])
])

3. Flipping Coordinates#

You can flip axis of default coordinate system so that horizontal axis becomes vertical and vice versa.

gggrid([
    ggplot(df, aes(x='fl')) + geom_bar() + ggtitle('Default'),
    ggplot(df, aes(x='fl')) + geom_bar() + \
        ggtitle('With coord_flip()') + coord_flip()
])

Flipping could also be combined with changing limits.

gggrid([
    ggplot(df, aes(x='fl')) + geom_bar() + \
        coord_flip() + ggtitle('Flipping only'),
    ggplot(df, aes(x='fl')) + geom_bar() + \
        coord_flip(ylim=[0, 300]) + ggtitle('With changing limits') + \
        theme(plot_margin=[0, 5])
])

Also, you can add flip as an option for coordinate system.

gggrid([
    ggplot(df, aes(x='fl')) + geom_bar() + \
        coord_flip() + ggtitle('Flipping via coord_flip()'),
    ggplot(df, aes(x='fl')) + geom_bar() + \
        coord_cartesian(flip=True) + ggtitle('Flipping via flip=True')
])

Tooltips after coord_flip() are flipped too.

gggrid([
    ggplot(df) + geom_boxplot(aes(as_discrete('class',order=1, order_by='..middle..'), 'hwy')) + \
        theme(axis_title_x='blank') + ggtitle('Default'),
    ggplot(df) + geom_boxplot(aes(as_discrete('class',order=1, order_by='..middle..'), 'hwy')) + \
        theme(axis_title_y='blank') + coord_flip() + ggtitle('Flipped')
])

4. Map Coordinates#

coord_map() projects a portion of the earth, which is approximately spherical, onto a flat 2D plane. Map projections generally do not preserve straight lines, so this requires considerable computation.

Typical use case: geospatial data.

gggrid([
    ggplot() + geom_polygon(aes(fill='state'), data=states_gdf) + \
        ggtitle('Default'),
    ggplot() + geom_polygon(aes(fill='state'), data=states_gdf, tooltips='none') + \
        coord_map() + ggtitle('With coord_map()')
])

Parameters xlim, ylim, flip are also available for coord_map().

5. Polar Coordinates#

Typical use case: pie charts and polar plots.

Let’s set theme_grey() to improve plots readability.

LetsPlot.set_theme(theme_grey())

5.1. Pie Chart#

geom_bar() with coord_polar() is transformed to a pie chart.

gggrid([
    ggplot(df) + geom_bar(aes(fill=as_discrete('cyl')), size=0) + ggtitle('From Bar Chart'),
    ggplot(df) + geom_bar(aes(fill=as_discrete('cyl')), size=0) + \
        coord_polar(theta='y') + ggtitle('To Pie Chart')
])

5.2. Various Bar Charts#

bull_eye = ggplot(df) + \
    geom_bar(aes(fill=as_discrete('cyl')), size=0, show_legend=False)
    
radial_bar = ggplot(df) + \
    geom_bar(aes(fill=as_discrete('cyl')), size=0, position='dodge', show_legend=False)
    
coxcomb = ggplot(df) + \
    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)

5.3. Parameters#

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']
}
p1 = ggplot(labels_df, aes(x='x', y='y', color='y')) + \
    geom_path(size=3, show_legend=False) + scale_color_brewer(palette='GnBu')

p1 + coord_polar() + ggtitle('Default plot with coord_polar()')

transform_bkgr

When using the transform_bkgr parameter, the panel is not transformed into a circle but remains a rectangle.

p1 + coord_polar(transform_bkgr=False) + ggtitle('coord_polar(transform_bkgr=False)') 

direction

You can specify angle direction: 1 = clockwise, -1 = counter-clockwise.

p1 + coord_polar(direction=-1) + ggtitle('coord_polar(direction=-1)')

start

This parameter specifies the offset relative to the starting angle (12 o’clock) in radians.

gggrid([
    p1 + coord_polar(start=3.14 / 2) + ggtitle('start=PI/2'),
    p1 + coord_polar(start=-3.14 / 2) + ggtitle('start=-PI/2')
])

theta

The theta parameter allows to select aesthetic used to map angle.

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

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

p2 = ggplot(data1) + \
    geom_path(aes(x='subj', y='score', color='student'), flat=True) + \
    geom_point(aes(x='subj', y='score', color='student')) 

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

xlim and ylim

The xlim parameter can be used to fix overlapping between first and last value.
The ylim parameter can be used to move data out of the center.

To prevent biology and progr overlapping let’s change the xlim to [None, 5] (None is to keep the default minimum limit as this value is not important to us).
To show the full score range we change the ylim to [1, 20].

gggrid([
    p2 + coord_polar(),
    p2 + coord_polar(xlim=[None, 5], ylim=[1, 20])
])  

5.4. 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:

ggplot(df) + \
    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, vjust=.7, hjust=.4),
          axis_text_y=element_text(color='red'),
          panel_inset=[20, 140, 30, 135]) + \
    ggsize(900, 500) + \
    coord_polar(transform_bkgr=False)