LenaH
LenaH

Reputation: 323

How to plot very low negative values in Matlab

I want to plot the following - see below.

The reason that I want to use semilogy - or something else, maybe you have suggestions? - is that the data goes so low that the scale is such that all the positive data appears to be zero.

Of course semilogy doesn't work with negative data. But what can I do? The goal is that positive and negative data are somehow visible in the plot as different from zero.

I saw this question (Positive & Negitive Log10 Scale Y axis in Matlab), but is there a simpler way?

Another issue I have with the semilogy command is that the data are plotted as if they go from November to April, whereas they really go from January to June!

%% Date vector
Y = [];
for year = 2008:2016
    Y = vertcat(Y,[year;year]);
end
M = repmat([01;07],9,1);
D = [01];
vector = datetime(Y,M,D);

%% Data
operatingValue=...
   1.0e+05 *...
   [0.020080000000000,   0.000010000000000,   0.000430446606112,   0.000286376498540,   0.000013493575572,   0.000008797774209;...
    0.020080000000000,   0.000020000000000,   0.000586846360023,   0.000445575962649,   0.000118642085670,   0.000105982759202;...
    0.020090000000000,   0.000010000000000,   0.000304503221392,   0.000168068072591,  -0.000004277640797,   0.000006977580173;...
    0.020090000000000,   0.000020000000000,   0.000471819542315,   0.000318827321824,   0.000165018495621,   0.000188500216550;...
    0.020100000000000,   0.000010000000000,   0.000366527395452,   0.000218539902929,   0.000032265798656,   0.000038839492621;...
    0.020100000000000,   0.000020000000000,   0.000318807172600,   0.000170892065948,  -0.000093830970932,  -0.000096575559444;...
    0.020110000000000,   0.000010000000000,   0.000341114962826,   0.000187311222835,  -0.000118595282218,  -0.000135188693035;...
    0.020110000000000,   0.000020000000000,   0.000266317725166,   0.000128625220303,  -0.000314547081599,  -0.000392868178754;...
    0.020120000000000,   0.000010000000000,   0.000104302824558,  -0.000000079359646,  -0.001817533087893,  -0.002027417507676;...
    0.020120000000000,   0.000020000000000,   0.000093484465168,  -0.000019260661622,  -0.002180826237198,  -0.001955577709102;...
    0.020130000000000,   0.000010000000000,   0.000052921606827,  -0.000175185193313,  -4.034665389612666,  -4.573270848282296;...
    0.020130000000000,   0.000020000000000,   0.000027218083520,  -0.000167098897097,                   0,                   0;...
    0.020140000000000,   0.000010000000000,   0.000044907412504,  -0.000106127286095,  -0.012248660549809,  -0.010693498138601;...
    0.020140000000000,   0.000020000000000,   0.000061663936450,  -0.000070280400096,  -0.015180683545658,  -0.008942771925367;...
    0.020150000000000,   0.000010000000000,   0.000029214681162,  -0.000190870890021,                   0,                   0;...
    0.020150000000000,   0.000020000000000,   0.000082672707169,  -0.000031566292849,  -0.003226048850797,  -0.003527284081616;...
    0.020160000000000,   0.000010000000000,   0.000084562787728,  -0.000024916156477,  -0.001438488940835,  -0.000954872893879;...
    0.020160000000000,   0.000020000000000,   0.000178181932848,   0.000054988621755,  -0.000172520970578,  -0.000139835312255]

figure;
semilogy( datenum(vector), operatingValue(:,3), '-+', datenum(vector), operatingValue(:,4), '-o',...
    datenum(vector), operatingValue(:,5), '-*', datenum(vector), operatingValue(:,6), '-x',...
    'LineWidth',1.2 ), grid on;
dateaxis('x', 12);

Upvotes: 0

Views: 1132

Answers (1)

hello123
hello123

Reputation: 313

Save the function symlog in your directory.

 function symlog(varargin)
    % SYMLOG bi-symmetric logarithmic axes scaling
    %   SYMLOG applies a modified logarithm scale to the specified or current
    %   axes that handles negative values while maintaining continuity across
    %   zero. The transformation is defined in an article from the journal
    %   Measurement Science and Technology (Webber, 2012):
    %
    %     y = sign(x)*(log10(1+abs(x)/(10^C)))
    %
    %   where the scaling constant C determines the resolution of the data
    %   around zero. The smallest order of magnitude shown on either side of
    %   zero will be 10^ceil(C).
    %
    %   SYMLOG(ax=gca, var='xyz', C=0) applies this scaling to the axes named
    %   by letter in the specified axes using the default C of zero. Any of the
    %   inputs can be ommitted in which case the default values will be used.
    %
    %   SYMLOG uses the UserData attribute of the specified axes to record the
    %   current transformation applied so that subsequent calls to symlog
    %   operate on the original data rather than the newly transformed data.
    %
    % Example:
    %   x = linspace(-50,50,1e4+1);
    %   y1 = x;
    %   y2 = sin(x);
    %
    %   subplot(2,4,1)
    %   plot(x,y1,x,y2)
    %
    %   subplot(2,4,2)
    %   plot(x,y1,x,y2)
    %   set(gca,'XScale','log') % throws warning
    %
    %   subplot(2,4,3)
    %   plot(x,y1,x,y2)
    %   set(gca,'YScale','log') % throws warning
    %
    %   subplot(2,4,4)
    %   plot(x,y1,x,y2)
    %   set(gca,'XScale','log','YScale','log') % throws warning
    %
    %   subplot(2,4,6)
    %   plot(x,y1,x,y2)
    %   symlog('x')
    %
    %   s = subplot(2,4,7);
    %   plot(x,y1,x,y2)
    %   symlog(s,'y') % can but don't have to provide s.
    %
    %   subplot(2,4,8)
    %   plot(x,y1,x,y2)
    %   symlog() % no harm in letting symlog operate in z axis, too.
    %
    % Created by:
    %   Robert Perrotta
    %
    % Referencing:
    %   Webber, J. Beau W. "A Bi-Symmetric Log Transformation for Wide-Range
    %   Data." Measurement Science and Technology 24.2 (2012): 027001.
    %   Retrieved 6/28/2016 from
    %   https://kar.kent.ac.uk/32810/2/2012_Bi-symmetric-log-transformation_v5.pdf

    % default values
    ax = []; % don't call gca unless needed
    var = 'xyz';
    C = 0;

    % user-specified values
    for ii = 1:length(varargin)
        switch class(varargin{ii})
            case 'matlab.graphics.axis.Axes'
                ax = varargin{ii};
            case 'char'
                var = varargin{ii};
            case {'double','single'}
                C = varargin{ii};
            otherwise
                error('Don''t know what to do with input %d (type %s)!',ii,class(varargin{ii}))
        end
    end

    if isempty(ax) % user did not specify a value
        ax = gca;
    end

    % execute once per axis
    if length(var) > 1
        for ii = 1:length(var)
            symlog(ax,var(ii),C);
        end
        return
    end

    % From here on we redefine C to be 10^C
    C = 10^C;

    % Axes must be in linear scaling
    set(ax,[var,'Scale'],'linear')

    % Check for existing transformation
    userdata = get(ax,'UserData');
    if isfield(userdata,'symlog') && isfield(userdata.symlog,lower(var))
        lastC = userdata.symlog.(lower(var));
    else
        lastC = [];
    end
    userdata.symlog.(lower(var)) = C; % update with new value
    set(ax,'UserData',userdata)


    if strcmpi(get(ax,[var,'LimMode']),'manual')
        lim = get(ax,[var,'Lim']);
        lim = sign(lim).*log10(1+abs(lim)/C);
        set(ax,[var,'Lim'],lim)
    end

    % transform all objects in this plot into logarithmic coordiates
    transform_graph_objects(ax, var, C, lastC);

    % transform axes labels to match
    t0 = max(abs(get(ax,[var,'Lim']))); % MATLAB's automatically-chosen limits
    t0 = sign(t0)*C*(10.^(abs(t0))-1);
    t0 = sign(t0).*log10(abs(t0));
    t0 = ceil(log10(C)):ceil(t0); % use C to determine lowest resolution
    t1 = 10.^t0;

    mt1 = nan(1,8*(length(t1))); % 8 minor ticks between each tick
    for ii = 1:length(t0)
        scale = t1(ii)/10;
        mt1(8*(ii-1)+(1:8)) = t1(ii) - (8:-1:1)*scale;
    end

    % mirror over zero to get the negative ticks
    t0 = [fliplr(t0),-inf,t0];
    t1 = [-fliplr(t1),0,t1];
    mt1 = [-fliplr(mt1),mt1];

    % the location of our ticks in the transformed space
    t1 = sign(t1).*log10(1+abs(t1)/C);
    mt1 = sign(mt1).*log10(1+abs(mt1)/C);
    lbl = cell(size(t0));
    for ii = 1:length(t0)
        if t1(ii) == 0
            lbl{ii} = '0';
    % uncomment to display +/- 10^0 as +/- 1
    %     elseif t0(ii) == 0
    %         if t1(ii) < 0
    %             lbl{ii} = '-1';
    %         else
    %             lbl{ii} = '1';
    %         end
        elseif t1(ii) < 0
            lbl{ii} = ['-10^{',num2str(t0(ii)),'}'];
        elseif t1(ii) > 0
            lbl{ii} = ['10^{',num2str(t0(ii)),'}'];
        else
            lbl{ii} = '0';
        end
    end
    set(ax,[var,'Tick'],t1,[var,'TickLabel'],lbl)
    set(ax,[var,'MinorTick'],'on',[var,'MinorGrid'],'on')
    rl = get(ax,[var,'Ruler']);
    try
        set(rl,'MinorTick',mt1)
    catch err
        if strcmp(err.identifier,'MATLAB:datatypes:onoffboolean:IncorrectValue')
            set(rl,'MinorTickValues',mt1)
        else
            rethrow(err)
        end
    end



    function transform_graph_objects(ax, var, C, lastC)
    % transform all lines in this plot
    lines = findobj(ax,'Type','line');
    for ii = 1:length(lines)
        x = get(lines(ii),[var,'Data']);
        if ~isempty(lastC) % undo previous transformation
            x = sign(x).*lastC.*(10.^abs(x)-1);
        end
        x = sign(x).*log10(1+abs(x)/C);
        set(lines(ii),[var,'Data'],x)
    end

    % transform all Patches in this plot
    patches = findobj(ax,'Type','Patch');
    for ii = 1:length(patches)
        x = get(patches(ii),[var,'Data']);
        if ~isempty(lastC) % undo previous transformation
            x = sign(x).*lastC.*(10.^abs(x)-1);
        end
        x = sign(x).*log10(1+abs(x)/C);
        set(patches(ii),[var,'Data'],x)
    end

    % transform all Retangles in this plot
    rectangles = findobj(ax,'Type','Rectangle');
    for ii = 1:length(rectangles)
        q = get(rectangles(ii),'Position'); % [x y w h]
        switch var
            case 'x'
                x = [q(1) q(1)+q(3)]; % [x x+w]
            case 'y'
                x = [q(2) q(2)+q(4)]; % [y y+h]
        end
        if ~isempty(lastC) % undo previous transformation
            x = sign(x).*lastC.*(10.^abs(x)-1);
        end
        x = sign(x).*log10(1+abs(x)/C);

        switch var
            case 'x'
                q(1) = x(1);
                q(3) = x(2)-x(1);
            case 'y'
                q(2) = x(1);
                q(4) = x(2)-x(1);
        end

        set(rectangles(ii),'Position',q)
    end

Plot your functions including symlog(gca,'y',-1.7) in the end:

plot( datenum(vector), operatingValue(:,3), '-+', datenum(vector), operatingValue(:,4), '-o',...
    datenum(vector), operatingValue(:,5), '-*', datenum(vector), operatingValue(:,6), '-x',...
    'LineWidth',1.2 ), grid on;
symlog(gca,'y',-1.7)

Here is your plot with positive and negative values: Final plot

Hope this solves your problem.

Upvotes: 2

Related Questions