Minimal-code to get axes limits of log-log plots, but using gscatter

6 views (last 30 days)
I used to specify scatter plots using loglog(X,Y,'o') to plot little circles at the data points. Now, gscatter(X,Y,GroupVector,'br','o') allows me to designate different colours for different data points. In my case, I only had two groups, and with logical GroupVector above designating which points belongs to which group. The 'br' says to use blue and red for the two groups.
Unfortunately, gscatter doesn't have a log scale option. I can set(gca,'xscale','log','yscale','log'), but I get complaints that negative axis limits are ignored. Sure enough, that is because the axis limits do include negative numbers, even if the data does not. As a result of this, the log scale scatter plot does not *automatically* have the nice axis limits that loglog(X,Y,'o') has.
My nonideal solution has been to create both plots, then copy the axes limits from the loglog plot to the gscatter plot. It seems heavily redundant. It sure would be nice to have a log scale option for gscatter. In the meantime, what is the most minimal code alternative of replicating loglog axes limits for gscatter? My method *might* be "minimal code", but I mean without duplicating plots.

Answers (3)

Fabio Freschi
Fabio Freschi on 2 Jun 2022
You can remove the negative part of the axis before setting the log scale
set(gca,'XLim',[0 max(xlim)],'YLim',[0 max(ylim)]);
set(gca,'XScale','log','YScale','log')
  3 Comments
Fabio Freschi
Fabio Freschi on 2 Jun 2022
Edited: Fabio Freschi on 2 Jun 2022
Is this working for you
set(gca,'XLim',[10^floor(log10(max([min(xlim) 0]))) 10^ceil(log10(max(xlim)))],'YLim',[10^floor(log10(max([min(ylim) 0]))) 10^ceil(log10(max(ylim)))]);
set(gca,'XScale','log','YScale','log')

Sign in to comment.


FM
FM on 2 Jun 2022
Edited: FM on 6 Jun 2022
Hi, Fabio,
Here is the set of data that seems to reveal differences between "loglog" layout and your two suggestions for "gscattter", totalling three scatter plots.
At the end of each of the three set of plotting instructions are 2 lines that make the "x" and "y" axes symmetrical. That hides the above discrepancies. I put them there because that's eventually what I want. But I'm still interested in how the "loglog" layout decisions can be emulated. I thought it might be easy, but it looks like it involves guessing about the process that it uses.
Thanks.
% Close figure windows and set up data
%-------------------------------------
close all;
x=[
0.3531 0.3531 12.9305 12.9305 0.4744 0.4744 0.3649 0.3649 0.3770 ...
0.3770 0.4318 0.4318 0.2568 0.2568 0.3288 0.3288 0.4668 0.4668 0.3228 ...
0.3228 0.2664 0.2664 1.4536 1.4536 0.2041 0.2934 0.3591 0.3591 0.8932 ...
0.8932 0.3095 0.3095 0.3800 0.3800 0.5411 0.5411 0.3390 0.3390 1.0329 ...
1.0329 0.4423 0.4423 0.5537 0.5537 0.5048 0.5048 0.5338 0.5338 0.8937 ...
0.8937 0.1257 0.1257 0.7695 0.7695 10.5885 10.5885 0.2753 0.2753 ...
0.7879 0.7879 0.2630 0.2630 0.5923 0.5923 0.6481 0.6481 0.3127 0.3127 ...
0.3159 0.3159 10.9593 10.9593 0.4369 0.4369 1.1650 1.1650 0.3901 ...
0.3901 0.2631 0.2631 0.1317 0.1317 13.5365 13.5365 0.3627 0.3627 ...
12.5942 12.5942 0.9374 0.9374 0.3617 0.3617 0.5765 0.5765 0.5280 ...
0.5280 0.4550 0.4550 0.4141 0.4141 0.4400 0.4400 0.5764 0.5764 0.3103 ...
0.3103 0.5172 0.2633 1.5205 1.1162 0.5018 0.5567 0.2402 0.2581 0.5132 ...
1.4910 0.4151 0.7040 12.2202 10.2918 0.3412 0.9795 0.3006 0.4063 ...
0.4293 0.3683 0.4776 10.6593 0.3107 12.9954 0.4495 0.4852 0.4539 ...
0.5873 9.7542 11.7193 0.5353 0.4711 0.3711 0.6086 0.8484 0.3802 0.2874 ...
0.2883 10.3625 0.1769 0.6156 0.2677 12.0048 0.3398 0.7914 13.2012 ...
0.4201 0.3387 ];
y=[
0.3893 0.3695 0.3893 0.3695 0.5019 0.4650 0.5019 0.4650 0.4889 0.5750 ...
0.4889 0.5750 0.2664 0.5354 0.2664 0.5354 0.4596 0.3731 0.4596 0.3731 ...
0.3385 0.3823 0.3385 0.3823 0.3239 0.3239 0.5162 0.7474 0.5162 0.7474 ...
0.4392 0.4705 0.4392 0.4705 0.6075 0.2430 0.6075 0.2430 0.5813 0.4279 ...
0.5813 0.4279 2.0488 0.4991 2.0488 0.4991 0.8421 0.6363 0.8421 0.6363 ...
0.8016 0.5145 0.8016 0.5145 0.4891 0.3124 0.4891 0.3124 0.4811 0.3389 ...
0.4811 0.3389 0.4555 0.3411 0.4555 0.3411 0.8957 0.5301 0.8957 0.5301 ...
0.3736 0.3827 0.3736 0.3827 0.3790 0.5523 0.3790 0.5523 0.6001 0.2787 ...
0.6001 0.2787 0.3654 0.4716 0.3654 0.4716 0.7382 0.3084 0.7382 0.3084 ...
0.6224 0.5768 0.6224 0.5768 0.5205 0.8075 0.5205 0.8075 0.3892 0.7064 ...
0.3892 0.7064 1.0712 0.4087 1.0712 0.4087 0.3074 0.6415 1.0017 0.7258 ...
0.3847 0.9258 0.3310 0.2532 0.9183 0.3233 0.2444 0.6016 0.7064 0.4829 ...
0.6532 0.5670 1.0520 0.8499 0.4388 0.7136 0.3934 0.4591 1.5558 0.9160 ...
0.4748 0.4851 0.4481 0.6678 0.6283 0.6660 0.7489 0.6644 0.3003 0.6246 ...
0.4611 0.7549 0.5237 0.3981 0.3973 0.5200 0.6287 0.3837 0.9782 0.3594 ...
0.8034 0.3673 1.5584 0.3934 ];
% "loglog" layout
%----------------
loglog(x,y,'o');
ax=gca;
% xlim([ min( [ ax.XLim(1) ax.YLim(1) ] ) max( [ ax.XLim(2) ax.YLim(2) ] ) ]);
% ylim(ax.XLim);
% Fabio's first suggestion
%-------------------------
figure
gscatter(x,y)
set(gca,'XLim',[0 max(xlim)],'YLim',[0 max(ylim)]); % <------------
set(gca,'XScale','log','YScale','log')
% xlim([ min( [ ax.XLim(1) ax.YLim(1) ] ) max( [ ax.XLim(2) ax.YLim(2) ] ) ]);
% ylim(ax.XLim);
% Fabio's second suggestion
%--------------------------
figure
gscatter(x,y)
set(gca,'XLim',[10^floor(log10(max([min(xlim) 0]))) 10^ceil(log10(max(xlim)))],'YLim',[10^floor(log10(max([min(ylim) 0]))) 10^ceil(log10(max(ylim)))]);
set(gca,'XScale','log','YScale','log')
% xlim([ min( [ ax.XLim(1) ax.YLim(1) ] ) max( [ ax.XLim(2) ax.YLim(2) ] ) ]);
% ylim(ax.XLim);
P.S. This was meant to be a comment in response to Fabio (under the originally posted question). I apologize for having posted it as an answer, as it certainly is not. I don't want to delete it because it contains Fabio's comments and insights, which I found to be very helpful in formulating my own solution.
  4 Comments
FM
FM on 3 Jun 2022
Edited: FM on 3 Jun 2022
Thanks, Fabio. I used your logic as a starting point and cobbled together an alternative process for choosing axis limits, in the form of function "Dat2LogLim.m":
% Dat2LogLim.m
%--------------
% Calculates loose axis limits if input data is
% plotted in log10 scale
function Lim2 = Dat2LogLim( Dat )
DatMin = min(Dat);
FloorLog10min = floor(log10(DatMin));
if DatMin >= 2*10^FloorLog10min
Lim2(1) = DatMin - 10^FloorLog10min;
else
Lim2(1) = 0.9*10^FloorLog10min;
end %
DatMax = max(Dat);
FloorLog10max = floor(log10(DatMax));
Lim2(2) = DatMax + 10^FloorLog10max;
return
Of course, I don't need to repeatedly raise quantities to the 10th power, but I want the code to be clear. Computation here is not high volume, so this doesn't act as a bottleneck.
As an example, assume that the data minimum is between 10 and just under 100. If the minimum is between 20 and 100, I simply subtract 10 to get the lower axis limit. If it is between 10 and 20, choose the lower axis limit to be 9, since that is where I expect the next minor tick to be. Of course, it assumes a common pattern of axis ticks. If the lower limit is between 100 and just under 1000, All the figures above get increased 10x. Similar scaling for when the lower limit is between 1 and just under 10, etc.
The upper axis limit is easier to calculate. If the maximum is between 10 and 100, I just add 10. Even if the maximum is 95, that puts the upper axis limit at 105, which then encompasses 100, which is what I want.
Thanks for setting me on this path. Given the above data for variables "x" and "y", here is the code to utilize "Dat2LogLim" and the resulting graph.
gscatter(x,y)
set(gca,'XLim',Dat2LogLim(x));
set(gca,'YLim',Dat2LogLim(y));
set(gca,'XScale','log','YScale','log')
grid on

Sign in to comment.


FM
FM on 3 Jun 2022
Edited: FM on 3 Jun 2022
Based on Fabio's approach, here is my final solution, using a utility function "Dat2LogLim.m":
% Dat2LogLim.m
%--------------
% Calculates loose axis limits if input data is
% plotted in log10 scale
function Lim2 = Dat2LogLim( Dat )
Dat = Dat( ~isnan(Dat) & isreal(Dat) & isfinite(Dat) & Dat>0 );
DatMin = min(Dat);
TickStep = 10^floor(log10(DatMin));
if DatMin >= 2*TickStep
Lim2(1) = floor(DatMin/TickStep)*TickStep - 0.1*TickStep;
else
Lim2(1) = 0.9*TickStep;
end % if DatMin
DatMax = max(Dat);
TickStep = 10^floor(log10(DatMax));
if DatMax <= 9*TickStep
Lim2(2) = ceil(DatMax/TickStep)*TickStep + 0.2*TickStep;
else
Lim2(2) = 11*TickStep;
end % if DatMax
return
This assumes that the minor ticks progress as (say) [...0.8 0.9 1.0 1.1 1.2...]. The idea is to choose loose enough exis limits so that the next minor tick is included. If there are too few orders of magnitude, or too many, then the minor ticks will not progress as above, and the axis limits will be too loose or too tight.
Here is the test driver script "Test.m":
clear
close all;
Xs={[0.15 0.95] [0.25 0.85] [0.45 0.65]}; % 3 test plots
for ix=1:length(Xs)
SymmetricScatter(Xs{ix});
exportgraphics(gcf,"LogLog"+ix+".png");
end % for x
return
function SymmetricScatter(x)
figure
gscatter(x,x)
set(gca,'XLim',Dat2LogLim(x));
set(gca,'YLim',Dat2LogLim(x));
set(gca,'XScale','log','YScale','log')
grid on
return
end % function SymmetricScatter
Here are the resulting plots.

Categories

Find more on Creating and Concatenating Matrices in Help Center and File Exchange

Products


Release

R2022a

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!