Could someone help me to color these measures on 3 variations - less than 0, equal to 0 and greater than 0. I tried with a calculation
IF [Margin 1] > 0 THEN 1 ELSEIF [Margin 1] < 0 THEN 2 ELSEIF [Margin 1] = 0 THEN 3 END
The reason I have assigned numbers instead of string in the above calculation is, the data is from a cube which will not allow string to be mixed with IF conditon
This is only for Margin 1 while a similar one is also required for Margin 2 and Margin 3 as well. Once I put this calculation to color shelf it colored first value of Margin 1 correctly but at the same time even colors Margin 2 and Margin 3 as well with same shade.
Could someone help to nest color logic calculation for all three Margins so I can have control to choose less than, equal to and greater than 0 in different shades.
Attached is the image of data and tableau sheet of what I have arrived as of now, do let me know for any other details
Not sure how the fact the data is from cube makes much difference, inside of tableau you're looking at a integer and returning a string shouldn't matter.
If you want give this a try
create a calculated field like this:
ZN([Margin 1]) + ZN([Margin 2]) + ZN([Margin 3])
Then create your if statement based on the new calculated field returning string
If [NewCalculated field] <0 then
"Red"
If [NewCalculated field] 0 then
"Amber"
Else
"Green"
End
Have a go
Related
I want to color format the values (.csv file format) based on the below conditions in tableau desktop.
If consecutive increase of the values or same values with increase values from start Date then values should be Red color .
If Consecutive decrease of the values from startDate then values color should be Green Color
If the values are Increase and then decrease from start date then Yellow.
If the values are decrease then increase then yellow.
For the above data the expected color for the student values should be below..
Student:Mon,Pat,Henry,Kim Yellow,
Jack,stanley Red
Kevin,Lendl -Green
I have tried below code but not working as expected..
Please correct if i missing any thing thing.
//Green - consecutive downward trend
if
window_sum(if FIRST() = 0 then 0
elseif sum([Value]) < lookup(sum([Value]),-1) then 1
end) = MAX([Number of Days]) then "Green"
//Yellow - downward and upward trend but not consecutive
elseif window_sum(if FIRST() = 0 then 0
elseif sum([Value]) < lookup(sum([Value]),-1) then 1
end) > 0 and
window_sum(if FIRST() = 0 then 0
elseif sum([Value]) < lookup(sum([Value]),-1) then 1
end) < MAX([Number of Days]) then "Yellow"
//Red - consecutive upward trend
elseif
window_sum(if FIRST() = 0 then 0
elseif sum([Value]) > lookup(sum([Value]),-1) then 1
end) = MAX([Number of Days]) then "Red"
end
Thanks for your help in advance !!
Without knowing your data structure you can look into table calculations. This may not be straightforward, especially to get the "Compute Using" settings right, but some logic as follows may work.
Calculate the day on day differences (a table calculation will do that).
If the day on day diff is positive then give it a value 1, is negative set it to -1. This would be an IF statement around your table calculation.
Next SUM the result.
Calculate how many dates there are, which will provide the maximum possible number if all are increasing or decreasing every day.
Finally compare the result of the summing if up or down with the maximum possible. This field can sit on the colour shelf.
I haven't tested any of this but, from a logic perspective, you may be able to make this work.
Edit based on comments:
Build the following calculated fields, it should work. You'll be able to troubleshoot calc by calc if having "compute using" challenges. Put the [Colour] field on the colour shelf:
[DailyDiff]: ZN(SUM([Value])) - LOOKUP(ZN(SUM([Value])), -1)
[isUpOrDown]: IF [DailyDiff] >0 THEN 1 ELSEIF [DailyDiff] <0 THEN -1 ELSE 0 END
[TotalUpOrDown]: WINDOW_SUM([isUpOrDown])
[NumDays]: {COUNTD([Date])}-1 //1 less to account for the first day
[Colour]: IF [TotalUpOrDown] = MIN([NumDays]) THEN "Increasing" ELSEIF [TotalUpOrDown]=-MIN([NumDays]) THEN "Decreasing" ELSE "Other" END
I am calculating ENSO indices using Matlab and one condition is that I have to find anomalous sea surface temperatures. The condition is that an El NiƱo event is characterised by sea surface temperatures that are 0.5 degrees above the normalised "0-value" for 5 months. I have gotten as far as to make my monthly time series data logical (i.e. "1" is a monthly data value above 0.5 and "0" is a monthly data value below 0.5), but I wanted to know if there was a command in Matlab that allows me to identify when this value repeats 5 times or more.
As an example code:
Monthly_data=[0 0 1 1 1 1 1 0 0 0 1 1 0 0 0 0 1 0 1 1 1 1 1 1 1 0]
I would ideally need a command that finds when a minimum of five "1"s occur after each other. Does this exist?
If more info is needed please let me know, I am new to matlab so I am not yet sure of the structure and syntax that is valued for asking questions on here.
Thank you!
not sure this is what you need but perhaps gives you some direction.
> x = diff(Monthly_data);
> find(x==-1)-find(x==1)
ans =
5 2 1 7
these are the lengths of the 1 sequences. You may need to pad front and end of the array with 0 to eliminate sequences missing one boundary.
To find the start index of the sequence longer than 5:
> s=find(x==1);
> s(find(x==-1)-s>5)
ans = 18
or
> s(find(x==-1)-s>=5)
ans =
2 18
note that because of the diff lag, these are one more than the array index, or consider it as position for zero based indexing.
I create a figure using a barplot that represents means of groups.
I have a matrix that tells me whether the means are statistically different.
sign_diff =
0 0 0 1
0 0 0 1
0 0 0 0
1 1 0 0
In this case the means of the first group and second group are significantly different from the mean of the 4th group.
How to read the matrix:
first rows: there is a 1 in the last column -> first bar is different from bar 4 so bar 1 and bar 4 get a star.
second row: there is a 1 in the last column -> second bar is different from bar 4 so bar 2 and bar 4 get a star. In more since bar 1 and bar 2 are not different between them the stars in bar 1 and bar 2 should be at the same high
How can I add a marker on top of the bars that are different?
I would like to have something like this:
Please note that the first two stars should be at the same levels indicating that bar1 and bar2 do not differ, but they both differ from bar4 (then the star on top of bar 4 should be higher)
Hope you can help me
I'm still not sure I quite grasp the height logic (and we don't have a functioning example) but in the meantime there's a simple answer the superimposition question. You can use line to superimpose the stars onto your plot.
For example:
y = [1 2 3 4];
bar(y);
ylim([0 6]);
sign_diff = [0 0 0 1; 0 0 0 1; 0 0 0 0; 1 1 0 0];
needs_star = (sum(sign_diff) ~= 0); % See which bars need a star
star_heights = sum(sign_diff).*0.75;
star_x = 1:length(y);
star_y = max(y) + star_heights;
star_x = star_x(needs_star);
star_y = star_y(needs_star);
line(star_x, star_y, ...
'linestyle', 'none', ...
'marker', 'p', ...
'markersize', 15 ...
);
Produces the following:
line accepts XY inputs, so if you can create coordinates for your stars based on your sign_diff matrix you can use them in the line call.
Edit: I have updated with my stab at figuring out the logic. Some tweaks will be needed based on your data. The ylim and max calls will need to be adjusted based on the maximum height of the data in your graph in order to fit everything into the axes and to make sure there is no overlap. You can tweak the 0.75 value to whatever you would like in order to show the differences adequately. This is probably not the most efficient method but the behavior is at least explicit.
I have a vector that could look like this:
v = [1 1 2 2 2 3 3 3 3 2 2 1 1 1];
that is, the number of equal elements can vary, but they always increase and decrease stepwise by 1.
What I want is an easy way to be left with a new vector looking like this:
v2 = [ 1 2 3 2 1];
holding all the different elements (in the right order as they appear in v), but only one of each. Preferably without looping, since generally my vectors are about 10 000 elements long, and already inside a loop that's taking for ever to run.
Thank you so much for any answers!
You can use diff for this. All you're really asking for is: Delete any element that's equal to the one in front of it.
diff return the difference between all adjacent elements in a vector. If there is no difference, it will return 0. v(ind~=0) will give you all elements that have a value different than zero. The 1 in the beginning is to make sure the first element is counted. As diff returns the difference between elements, numel(diff(v)) = numel(v)-1.
v = [1 1 2 2 2 3 3 3 3 2 2 1 1 1];
ind = [1 diff(v)];
v(ind~=0)
ans =
1 2 3 2 1
This can of course be done in a single line if you want:
v([1, diff(v)]~=0)
You could try using diff which, for a vector X, returns [X(2)-X(1) X(3)-X(2) ... X(n)-X(n-1)] (type help diff for details on this function). Since the elements in your vector always increase or decrease by 1, then
diff(v)
will be a vector (of size one less than v) with zeros and ones where a one indicates a step up or down. We can ignore all the zeros as they imply repeated numbers. We can convert this to a logical array as
logical(diff(v))
so that we can index into v and access its elements as
v(logical(diff(v)))
which returns
1 2 3 2
This is almost what you want, just without the final number which can be added as
[v(logical(diff(v))) v(end)]
Try the above and see what happens!
This is a bit more of a general question, but, no matter how many times I read the description of MATLAB's im2col function, I cannot fully understand it. I need it for the computational efficiency because MATLAB is awful with nested for loops. Here's what I'm attempting to do, but using nested for loops:
[TRIMMED]=TM_FILTER(IMAGE, FILTER_SIZE, PERCENT)
Takes a 2-D array and returns the array, filtered with a
square trimed mean filter with length/width equal to FILTER_SIZE and percent equal to PERCENT.
%}
function [trimmed]=tm_filter(image, filter_size, percent)
if rem(filter_size, 2)==0 %make sure filter has a center pixel
error('filter size must be odd numbered'); %error and return if number is odd
return
end
if percent > 100 || percent < 0
error('Percentage must be ? [0, 100]');
return
end
[rows, columns]=size(image); %figure out pixels needed
n=(filter_size-1)/2; %n is pixel distance from center pixel to boundaries
padded=(padarray(image, [n,n],128)); %padding on boundaries so center pixel always has neighborhood
for i=1+n:rows %rows from first non-padded entry to last nonpadded entry
for j=1+n:columns %colums from first non-padded entry to last nonpadded entry
subimage=padded(i-n:i+n,j-n:j+n); %neighborhood same size as filter
average=trimmean(trimmean(subimage, percent), percent); %computes trimmed mean of neighborhood as trimmed mean of vector of trimmed means
trimmed(i-n, j-n)=average; %stores averaged pixel in new array
end
end
trimmed=uint8(trimmed); %converts image to gray levels from 0-255
Here is the code you want: note the entire nested loop was replaced with a single statement.
[TRIMMED]=TM_FILTER(IMAGE, FILTER_SIZE, PERCENT)
Takes a 2-D array and returns the array, filtered with a
square trimed mean filter with length/width equal to FILTER_SIZE and percent equal to PERCENT.
%}
function [trimmed]=tm_filter(image, filter_size, percent)
if rem(filter_size, 2)==0 %make sure filter has a center pixel
error('filter size must be odd numbered'); %error and return if number is odd
return
end
if percent > 100 || percent < 0
error('Percentage must be ? [0, 100]');
return
end
trimmed = (uint8)trimmean(im2col(image, filter_size), percent);
Explanation:
the im2col function turns each region of filter_size into a column. Your trimmean function can then operate on each of the regions (columns) in a single operation - much more efficient than extracting each shape in turn. Also note this requires only a single application of trimmean - in your original you first do it on the columns, then again on the rows, which will actually cause a more severe trim than I think you intended (exclude 50% first time, then 50% again - feels like excluding 75%. Not exactly true but you get my point). Also you will find that changing the order of operations (row, then column vs column, then row) would change the result because the filter is nonlinear.
For example
im = reshape(1:9, [3 3]);
disp(im2col(im,[2 2])
results in
1 2 4 5
2 3 5 6
4 5 7 8
5 6 8 9
since you took each of the 4 possible blocks of 2x2 from this matrix:
1 4 7
2 5 8
3 6 9
and turned them into columns
Note - with this technique (applied to the unpadded image) you do lose some pixels on the edge; your method added some padding so that every pixel (even ones on the edge) has a complete neighborhood, and as such the filter returns an image that is the same size as the original (but it's not clear what the effect of padding/filtering will be near the margin, and especially the corner: you have almost 75% percent of pixels fixed at 128 and that is likely to dominate the behavior in the corner).
why im2col? why not nlfilter?
>> trimmed = nlfilter( image, [filter_size filter_size],...
#(x) treimmean( trimmean(x, percent), percent ) );
Are you sure you process the entire image?
i and j only goes up to rows and columns respectively. However, when you update trimmed you access i-n and j-n. What about the last n rows and columns?
Why do you apply trimmean twice for each block? Isn't it more appropriate to process the block at once, as in trimmean( x(:), percent)?
I believe the results of trimmean( trimmean(x, percent), percent) will be different than those of trimmean( x(:), percent). Have you give it a thought?
A small remark, it is best not to use i and j as variable names in matlab.