1

In my research area (meteorology) graphs within graphs are commonly produced.

False Alarm Ratio Graph

more information about it can be found here.

Each of those lines joints up data points that have:

  1. An x-value, between 0 and 1 (values greater than 1 should not be represented in the graph).
  2. A y-value, between 0 and 1.
  3. A PSS value, between 1 and -1.
  4. A Frequency Bias value, ranging from 0 to +∞, but values higher than 4 are not displayed.
  5. A False Alarm Ratio (FAR) value, ranging from 0.0 to 0.9. The False Alarm Ratio value is held constant at a particular value for each data point on any given line.

EDIT: To make things really concrete, I've drawn a pink dot on the graph. That dot represents a data point for which x=0.81, y=0.61, PSS=-0.2, B=3.05, FAR=0.8.

I am trying to reproduce something similar in MATLAB. Googling turned up a lot of answers like this, which feature inset figures rather than what I'm looking for.

I have the data organized in a 3D array, where each page refers to a different level of False Alarm Ratio. The page with a FAR of 0.8 (data here) starts out like this

First four lines of data matrix

Then there are other pages on the 3D array devoted to FARs of 0.7, 0.6, and so on.

Questions
1. Is it even possible to create such an graph in MATLAB?
2. If so, what function should I use, and what approach should I take? EDIT: I have working code (below) that creates a somewhat similar figure using the linear plot function, but the documentation for this function does not indicate any way to insert a graph inside another graph. I am not sure how helpful this code is, but have inserted it in response to the downvoter.

H = [0:0.01:1];    
figure; hold on
fill([0 1 1],[0 0 1],[0 0.2 0.4]) % Deep blue
fill([0 1 0],[0 1 1],[0.4 0 0]) % Purple    
low_colours = {[0 0.501 1],[0 0.8 0.4], [0.4 0.8 0], [0.8 0.8 0]};
high_colours = {[0.6 0 0],[0.8 0 0], [1 0.5019 0], [0.988 0.827 0.196]};    
colour_counter = 0;

for ii = -0.8:0.2:0
    colour_counter = colour_counter + 1;
    if colour_counter < 5
        colour_now = low_colours{colour_counter};
    end
    ORSS = ones(1,size(H,2))*ii;
    F = (H .* (1-ORSS)) ./ ((1-2.*H) .* ORSS + 1);    
    plot(F,H)
    fill(F,H,colour_now);        
end

colour_counter = 0;

for ii = 0.8:-0.2:0
    colour_counter = colour_counter + 1;
    if colour_counter < 5
        colour_now = high_colours{colour_counter};
    end
    ORSS = ones(1,size(H,2))*ii;
    F = (H .* (1-ORSS)) ./ ((1-2.*H) .* ORSS + 1);
    plot(F,H)
    fill(F,H,colour_now);    
end
EBH
  • 10,350
  • 3
  • 34
  • 59
  • You should really post some more data, enough to make all parts of the graph. – EBH Sep 28 '16 at 21:54
  • 1
    @EBH I've now fixed the original google sheet so that it contains all the FARs. I also post the generating code [here](https://docs.google.com/document/d/1cZk007a3sp7MDpJYS3zQ5SAnjro9LvPM3s_v5Ax4BeM/edit?usp=sharing), along with [a supporting function](https://docs.google.com/document/d/1QW2TNbdg7jmY1Wk2EPlo5JhNBGv4-E5RXkSxc-nHRpg/edit?usp=sharing) and [another supporting function](https://docs.google.com/document/d/1sGZTdUQdrT-zcdXsR5ExjloydMO-SFHe6bxS8tv6-5M/edit?usp=sharing). – user1205901 - Слава Україні Sep 29 '16 at 01:15

1 Answers1

3

I think I got what you want, but before you go to the code below, notice the following:

  1. I didn't need any of your functions in the link (and I have no idea what they do).
  2. I also don't really use the x and y columns in the data, they are redundant coordinates to the PSS and B.
  3. I concat all the 'pages' in your data to one long table (FAR below) with 5 columns (FAR,x,y,PSS,FB).

If you take closer look at the data you see that some areas that supposed to be colored in the graph has no representation in it (i.e. no values). So in order to interpolate the color to there we need to add the corners:

FAR{end+1,:} = [0.8 0 0 0 4];
FAR{end+1,:} = [0.9 0 0 -0.66 3.33];
FAR{end+1,:} = [1 0 0 0 0];
FAR{end+1,:} = [1 0 0 -1 3];

Next, the process has 2 parts. First we make a matrix for each variable, that ordered in columns by the corresponding FAR value, so for instance, in the PSS matrix the first column is all PSS values where FAR is 0, the second column is all PSS values where FAR is 0.1, and so on. We make such matrices for FAR(F), PSS and FreqBias(B), and we initialize them with NaNs so we can have columns with different number of values:

F = nan(max(histcounts(FAR.FAR,10)),10);
PSS = F;
B = F;
c = 1;
f = unique(FAR.FAR).';
for k = f
    valid = FAR.FAR==k & FAR.x<=1;
    B(1:sum(valid),c) = FAR.FB(valid);
    B(sum(valid):end,c) = B(sum(valid),c);
    PSS(1:sum(valid),c) = FAR.PSS(valid);
    PSS(sum(valid):end,c) = PSS(sum(valid),c);
    F(:,c) = k;
    c = c+1;
end

Then we set the colors for the colormap (which I partially took from you), and set the labels position:

colors = [0 0.2 0.4
    0 0.501 1;
    0 0.8 0.4;
    0.4 0.8 0;
    0.8 0.8 0;
    0.988 0.827 0.196;
    1 0.5019 0;
    0.8 0 0;
    0.6 0 0.2;
    0.4 0.1 0.5];

label_pos =[0.89 0.77
    1.01         0.74
    1.14         0.69
    1.37         0.64
    1.7          0.57
    2.03         0.41
    2.65         0.18
    2.925       -0.195
    2.75        -0.55];

And we use contourf to plot everything together, and set all kind of properties to make it look good:

[C,h] = contourf(B,PSS,F);
xlim([0 4])
ylim([-1 1])
colormap(colors)
caxis([0 1])
xlabel('Frequency Bias B')
ylabel('Pierce Skill Score PSS')
title('False Alarm Ratio')
ax = h.Parent;
ax.XTick = 0:4;
ax.YTick = -1:0.5:1;
ax.FontSize = 20;
for k = 1:numel(f)-2
    text(label_pos(k,1),label_pos(k,2),num2str(f(k+1)),...
        'FontSize',12+k)
end

And here is the result:

FAR


Getting the labels position:

If you wonder what is a fast way to obtain the variable label_pos, then here is how I made it...

You run the code above without the last for loop. Then you run the following code:

clabel(C,'manual')
f = gcf;
label_pos = zeros(numel(f.Children.Children)-1,2);
for k = 1:2:size(label_pos,1)
    label_pos(k,:) = f.Children.Children(k).Position(1:2);
end
label_pos(2:2:size(label_pos,1),:) = [];

After the first line the script will pause and you will see this message in the command window:

Carefully select contours for labeling. When done, press RETURN while the Graph window is the active window.

Click on the figure where you want to have a label, and press Enter.
That's it! Now the variable label_pos has the positions of the labels, just as I used it above.

EBH
  • 10,350
  • 3
  • 34
  • 59
  • This almost does it! I posted [an imgur album](http://imgur.com/a/dgRaB) to show the difference between your plot and a traditional meteorological plot. The key difference is that yours continues even when x > 0, whereas a traditional plot wouldn't. Another difference is that you don't have the bounded purple region representing regions that are x<1 and y>0 but not already shaded. I also updated and commented my make_skill_graphs.m file, which recreates your plot exactly, with the minor difference that yours lacks a 0.9 caption, whereas for some unknown reason mine lacks a 0.8 caption. – user1205901 - Слава Україні Sep 30 '16 at 01:58
  • @user1205197 I have done some editing and I think it's OK now, what do you say? It can be improved a little, but I think you can continue from here, am I right? – EBH Sep 30 '16 at 07:41
  • Yes, it looks brilliant, and with a little effort can also be reconfigured to work with other kinds of input. – user1205901 - Слава Україні Sep 30 '16 at 08:10
  • @user1205197, is this type of graph has a name? I tried to look for it in the web and didn't find it. – EBH Oct 01 '16 at 19:49
  • In their book _Forecast Verification: A Practitioner's Guide in Atmospheric Science_, Jolliffe & Stephenson call them "skill-bias diagrams". However, I believe that is a neologism particular to the context in which they are writing. I am not aware of diagrams like this having any general name. – user1205901 - Слава Україні Oct 02 '16 at 08:28
  • Thanks. Just wanted to make your question more clear and general, for future users that might encounter the same problem (I also add a little edit to the answer). – EBH Oct 02 '16 at 09:02