Today’s theme is Weather and I while searching for inspiration I came across these beautiful wind rose charts from the Lets-Plot gallery.opted for a simple interpretation of showing the changing monthly temperature distribution over a year. Data is taken from a Python version of Forecasting: Principles and Practise.
As always, I like to adapt the examples to my home country of Singapore and data for Singapore’s wind readings was provided by Iowa State University’s Iowa Environmental Mesonet site.
One thing that really fascinated me was how easily different component geoms came together to create the final chart. A true testament to the power of The Grammer of Graphics.
We start first with a simple stacked bar chart of wind readings for every direction (0 to 360 degrees).
Then we shift the chart up by inserting a blank rectangle underneath the chart. At this point, a seemingly strange decision.
Code
(ggplot(df)+ geom_bar( aes('Direction', 'value', fill=as_discrete('Speed Group')), size=0, width=.8, stat='identity', tooltips=layer_tooltips().format('^y', '{.2g}%').format('^x', '{}°') ) + geom_rect(# Visually align the width of the rectangle with the bars - widen it by 5 (half a bar width) xmin=5, xmax=365, ymin=-1, ymax=0, fill='white', size=0 ))
But it all comes together when we convert the y axis to polar coordinates. Our bar chart is now a compass. And that blank rectangle is wrapped into a perfect blank circle, for us to add annotations!
Code
(ggplot(df)+ geom_bar( aes('Direction', 'value', fill=as_discrete('Speed Group')), size=0, width=.8, stat='identity', tooltips=layer_tooltips().format('^y', '{.2g}%').format('^x', '{}°') ) + geom_rect(# Visually align the width of the rectangle with the bars - widen it by 5 (half a bar width) xmin=5, xmax=365, ymin=-1, ymax=0, fill='white', size=0 )+ coord_polar( ylim=[-1, None], # -1 is to make inner circle start=(3.14*2) /36/2# Divide by 2 (i.e. rotate by half a bar width) to make the N-S axis perpendicular ))
Final Chart
And with some finishing touches, we get a very professional looking final product.
Code
(ggplot(df)+ geom_bar( aes('Direction', 'value', fill=as_discrete('Speed Group')), size=0, width=.8, stat='identity', tooltips=layer_tooltips().format('^y', '{.2g}%').format('^x', '{}°') ) + geom_rect(# Visually align the width of the rectangle with the bars - widen it by 5 (half a bar width) xmin=5, xmax=365, ymin=-1, ymax=0, fill='white', size=0 )+ coord_polar( ylim=[-1, None], # -1 is to make inner circle start=(3.14*2) /36/2# Divide by 2 (i.e. rotate by half a bar width) to make the N-S axis perpendicular )+ geom_hline(yintercept=0, size=2) + geom_text(x=180, y=-1, label=f'Calm\n{CALM:.1f}%', hjust='middle', vjust='center', size=6)+ scale_fill_manual( name='Wind Speed (mph):', values=['#002bff', '#03d3f8', '#7afe81', '#fde609', '#ff4404', '#780200'], labels={0: '2 - 4.9', 1: '5 - 6.9', 2: '7 - 9.9', 3: '10 - 14.9', 4: '15 - 19.9', 5: '20+' } )+ scale_y_continuous( breaks=[0, 1, 2, 3, 4, 5], # To not add automatically generated ticks for values outside of the data rangeformat='{}%' )+ scale_x_continuous( labels={45: 'NE', 90: 'E', 135: 'SE', 180: 'S', 225: 'SW',270: 'W',315: 'NW',360: 'N', }, ) + labs( title ='Wind Rose for Singapore', subtitle ='Observations from 2001 to 2023', caption ='#30DayChartChallenge #Day16 Weather\nData: Iowa Environmental Mesonet\nMade by: www.ddanieltan.com' )+ ggsize(800,800)+ theme_minimal2()+ theme( plot_title=element_text(size=24, face='bold'), plot_subtitle=element_text(size=18), plot_caption=element_text(size=12, color='grey'), panel_grid_minor_x=element_line(), panel_grid=element_line(color='#A0A0A0'), axis_ticks_y=element_line(), axis_text_x=element_text(size=18), axis_title=element_blank(), ))
TIL
To add a list as a column to a Polars DataFrame use `df.with_columns(pl.Series(…)). Akin to df[] = list(…) in Pandas.
There’s a treasure chest of learnings to be gathered from the reference Lets-Plot notebook. So many little tweaks to add more polish to the final chart.