MathWorks turned 40 years old earlier this year, marking a significant milestone in our journey of accelerating the pace of engineering and science! Coincidentally, I am also turning 40 this October, and to age as gracefully as possible I am trying to embrace change and, you know, resist the temptation to hold on too tightly to outdated ideas. In the spirit of evolution, today I am introducing
compassplot, the latest grown-up addition to MATLAB's suite of
PolarAxes-based plotting functions introduced in R2024b.
As we celebrate this new chapter, it's also time to say goodbye to the trusty compass function. It served us well in a MATLAB landscape before the introduction of object handles, App Designer, and PolarAxes, but hasn't been able to keep up. The new compassplot represents a more mature approach, offering streamlined workflows and enhanced customization capabilities. Join me today to explore the possibilities of this new visualization.
theta = linspace(0,3*pi/4,10);
compassplot(theta, rho, DisplayName="simulation 1")
compassplot(theta, -rho, DisplayName="simulation 2")
title("compassplot, new in R2024b")
By comparison, the old compass function only understands Cartesian coordinates so we have to take an extra step to convert the polar inputs to Cartesian using pol2cart.
[u,v] = pol2cart(theta,rho);
% And old compass is not embarrassed to fill up your legend like this ...
legend(Location="northeastoutside")
Customization with compassplot and PolarAxes
Besides doing something sensible with legend, the real strength of the new compassplot comes from the PolarAxes it is drawn in. The older compass function draws both the compass arrows as well as the concentric circles, radii and labels needed for the polar coordinate system - all in an actually Cartesian Axes! This means there is no (easy) way to customize how that coordinate system appears.
title("old compass is really a cartesian axes")
Both PolarAxes and the new PolarCompassPlot object created by compassplot, however, have polar-specific properties you can use to customize a visualization to your needs, as you can start to see when we display the handles of the two objects.
pax = polaraxes
pax =
PolarAxes with properties:
ThetaLim: [0 360]
RLim: [0 1]
ThetaAxisUnits: 'degrees'
ThetaDir: 'counterclockwise'
ThetaZeroLocation: 'right'
Show all properties
% Create sample data & compassplot
values = [91, 45, 80, 77, 34];
theta = linspace(0, (n-1)*2*pi/n, n);
c = compassplot(theta,values)
c =
PolarCompassPlot with properties:
Color: [0 0.447 0.741]
LineStyle: '-'
LineWidth: 0.5
ThetaData: [0 1.2566 2.5133 3.7699 5.0265]
RData: [91 45 80 77 34]
Show all properties
Using these properties, and some nice convenience functions, we can customize the objects to create a spiderplot-like visualization of some made-up portfolio metrics.
% Customize PolarAxes by setting properties.
pax.ThetaAxisUnits = "radians";
pax.RAxisLocation = 3*pi/5;
% Simple functions are also available to customize PolarAxes
metrics = ["ROI","EBRG/yr","Liquidity","Diversification","INVPE"];
thetaticklabels(metrics);
title("Portfolio Performance")
% Customize PolarCompassPlot properties.
PolarAxes also supports combining multiple objects, for instance adding text labels and regions to help the audience understand the data, which was difficult or impossible to do with the older "fake" polar Axes.
% Add text objects to indicate the R value at at each compass vertex
n = diff(rlim)*.08; % 8 percent of rlim for padding
txt = text(theta, values+n, string(values), FontWeight="bold",HorizontalAlignment="center",VerticalAlignment="middle");
r(1) = radiusregion([0 50], FaceColor="#C8969F", DisplayName="High risk");
r(2) = radiusregion([50 75], FaceColor="#CBE5A8", DisplayName="Moderate risk");
r(3) = radiusregion([75 100], FaceColor="#9DAD55", DisplayName="Low risk");
Which way the wind blows ...
The example above uses the
compassplot vectors to illustrate the magnitude of several distinct metrics. Compass vectors are also useful for illustrating more classic vector-type data, for instance wind measurements. My colleague Ned Gulley
highlighted a community File Exchange submission several years ago using a NOAA dataset with wind speed and direction measurements at a specific buoy in Boston Harbor, not far from MathWorks' headquarters.
We can import some more recent data from this same dataset using readtable to really see compassplot at work. After a little bit of data cleanup, we have a table with daily wind speed and direction measurements at this buoy from September 2023.
url = "https://www.ndbc.noaa.gov/view_text_file.php?filename=44013h2023.txt.gz&dir=data/historical/stdmet/";
tbl = readtable(url, VariableNamingRule="preserve");
tbl.WDIR(tbl.WDIR == 999) = NaN; % Clean up missing values
tbl.WDIR = deg2rad(tbl.WDIR); % Convert to radians
sept = tbl(tbl.MM == 9, :); % Filter down to September
% The data set includes observations every 10 minutes. For simplicity, we'll use the
% mean value for each day.
sept = groupsummary(sept, ["#YY","MM","DD"], "mean", ["WDIR","WSPD"]);
sept(1:8,:)
ans = 8×6 table
|
#YY |
MM |
DD |
GroupCount |
mean_WDIR |
mean_WSPD |
1 |
2023 |
9 |
1 |
144 |
2.6411 |
4.3458 |
2 |
2023 |
9 |
2 |
144 |
3.675 |
4.6618 |
3 |
2023 |
9 |
3 |
144 |
3.6539 |
4.2896 |
4 |
2023 |
9 |
4 |
144 |
3.3423 |
1.9854 |
5 |
2023 |
9 |
5 |
144 |
3.4703 |
1.9278 |
6 |
2023 |
9 |
6 |
144 |
4.061 |
3 |
7 |
2023 |
9 |
7 |
144 |
3.6439 |
3.2201 |
8 |
2023 |
9 |
8 |
144 |
3.198 |
3.6403 |
Plot the September data. Notice that we're passing the table directly into the compassplot function and specifying which table variables to use. We can also configure the PolarAxes with 0° oriented at the "top" of the display and with direction values arranged clockwise from there.
compassplot(sept, "mean_WDIR", "mean_WSPD");
% Configure polaraxes to account for the fact that WDIR measurements are in
% degrees clockwise from true north.
pax.ThetaDir = "clockwise";
pax.ThetaZeroLocation = "top";
title("Mean Daily Wind Observations", "September 2023")
Adding Compass Labels
As we saw in the previous example, the PolarAxes is can be easily customized. Here, we're really making compassplot feel at home by adding familiar compass labels.
thetaticklabels(["N" "NE" "E" "SE" "S" "SW" "W" "NW"]);
With these labels, we can start to see that some of the strongest mean windspeeds are observed coming from the north-easterly direction, which is characteristic of a type of storm New Englanders call a "nor'easter." We also see one day of strong winds from the west-southwest. A quick Google search helped me find that this was one of the outer arms of Hurricane Lee spinning out clockwise from the storm center on September 15, 2023.
How does September compare to the annual data?
So far, we've only visualized the observations from September, 2023. We can once again combine compassplot with another visualization - polarhistogram in this case - to help us understand how the September observations compare to observations from the entire year. This shows us that the nor'easterly observations from September are actually relatively rare.
ph = polarhistogram(tbl.WDIR,...
SeriesIndex="none", FaceAlpha=0.25, EdgeColor="none",...
DisplayName="2023 Wind Dir Observations");
% The polarhistogram will show the number of observations at different bins around the polar axes.
% To show these counts on the same plot as the wind speed data, we will normalize the bin counts
% so they are in the same range as the September wind speeds.
ph.BinCounts = normalize(ph.BinCounts, "range" ) * max(sept.mean_WSPD);
c = compassplot(sept,"mean_WDIR","mean_WSPD","DisplayName","September");
% Once again customize the PolarAxes to show the compass directions.
pax.ThetaDir="clockwise";
pax.ThetaZeroLocation ="top";
pax.ThetaTick = 0:45:360;
pax.ThetaTickLabel = ["N" "NE" "E" "SE" "S" "SW" "W" "NW"];
title("Mean Daily Wind Speed & Direction")
Animate it!
Part of the beauty of MATLAB's modern plotting functions, like polarcompass, is that the data on these objects can be updated in a loop (or in an app callback!) to visualize a different data set or slice of the data. For instance, here we're looping through months 6 through 12 (June through December) and updating the PolarCompassPlot object, stored in the variable c, to visualize the daily wind observations month-by-month.
thisMonth = tbl(tbl.MM == i, :);
thisMonth = groupsummary(thisMonth, ["#YY","MM","DD"], "mean", ["WDIR","WSPD"]);
c.SourceTable = thisMonth;
monthName = month(datetime(1,i,1), "name");
c.DisplayName = monthName{:};
Conclusion
As I mentioned at the top, compassplot is just the latest in our set of full-featured polar plotting functions. I hope the examples I've shared can inspire you with ideas for your next visualization! And maybe they're leaving you itching for more. Please leave a comment and let me know how you will be using compassplot and what additional polar-plotting features you're interested in having us work on in the future! I'll leave you with this little reminder of the functions already on offer.
figure(Position=[1 1 900 500]);
t = tiledlayout(2, 5, TileIndexing="columnmajor", Padding="tight");
polarplot(linspace(0,6*pi), linspace(0,6*pi))
polarscatter(linspace(0,2*pi,20), 1:20)
polarhistogram(atan2(rand(1000,1)-0.5,2*(rand(1000,1)-0.5)))
polarbubblechart(linspace(0,2*pi,15),sin(1:15),logspace(8,10,15));
title("polarbubblechart")
compassplot(linspace(0,1.5*pi,10), -15:-6)
thetaregion(p1, 2*pi/3, 4*pi/3, FaceColor="red")
radiusregion(p2, 0.5, 0.8, FaceColor="blue")
polarregion(p3, [pi 7*pi/4], [0.2 0.4], FaceColor="green")
Comments
To leave a comment, please click here to sign in to your MathWorks Account or create a new one.