I'm looking to create a variable that will take the take the numeric value for a dimension. I tried removing Nulls in my query details, but that won't work because some results only have a Null value (see screenshot) and I was losing results that way.
I also need the variable for use in a cross tab table so I can do a count of each acuity level. I tried creating a Max variable on the acuity field =Max([Acuity Level]). That works for the main tab, but it doesn't work in a cross tab table. Please see attached screenshots for more details.
Acuity Crosstab
Column: Acuity Level
Row: Tracking Date
=FormatDate([Start Tracking Date & Time];"MM/dd/yyyy")
Body: # of Patients
=Count([Financial Number])
First off I created a query with your test data so I could drop into a free-hand SQL query so I have an example with which I can work. I added Row Number to maintain the row order of your data.
My approach requires three variables. A different approach may be possible requiring less variables or the formulas in the variables could be consolidated. However, I like to keep them separated for better understanding of the logical progression and better maintainability.
Var Acuity Level Adjusted gets set to -1 if the Acuity Level is Null and otherwise leave it as is just to make it easier to deal with...
=If(IsNull([Acuity Level]); -1; [Acuity Level])
Var Max Acuity Level is the greatest value of Var Acuity Level Adjusted within each combination of Patient Name and Encounter Type. This is called a calculation context. I do not understand the nuances of this topic well enough to explain why what I have below works, but it does. I refer to that previous link a lot. Also, this is why it was important that I picked -1 to replace Null.
=Max([Var Acuity Level Adjusted]) In ([Patient Name]; [Encounter Type])
Var Max Filter flags the row where the first two variables are equal. This variable is necessary because you cannot filter based on one object relative to another object.
=If([Var Acuity Level Adjusted] = [Var Max Acuity Level]; 1; 0)
Now if I add those variables it looks like this...
Then we can add a filter to only show the records where Var Max Filter = 1. You can hide the extra columns or even delete them from the table.
Hope you can apply this to your situation.
Related
I have a cross table in business objects web intelligence which has customer IDs in the rows, items in the columns and quantity in the body. I am trying to create a variable (dimension?) that will flag whether a customer has bought one particular item plus any other item. So, if for example the set item is 'item1' then the flag would show if the customer bought that and also if they bought at least one other item which could be any from another 100 or so. I am trying to write something that is akin to looking at two Booleans returning opposite results in the same statement.
if([item]="item1" And [quantity]>0;"Y";"")
and combine it with something like:
if([item]<>"item1" And [quantity]>0;"Y";"")
At the moment I can only get one or the other working and I can't then seem to create any further variable that would combine them because I end up with "Y" for all rows which is not correct. This is how the table might look after (mock-up in Excel):
I want to then be able to filter based on the flag results variable. Can anyone advise how I might address this problem?
You are really close. I defined count (or quantity) variables with where clauses. I created Var Select Item Count as...
=[quantity] Where ([item] = [your selected item])
And Var Other Items Count as...
=[quantity] Where ([item] <> [your selected item])
Finally, define your FLAG variable as...
=If([Var Select Item Count] > 0 And [Var Other Items Count] > 0; "Y")
Then you can filter on your FLAG variable.
Certainly, you could embed the code from the first two variables into the last one. However, I like to create my variables such that they build upon one other for easier understanding and maintenance.
Here is my example with "173" being the selected item.
I'm creating a Tableau Dashboard with 'buttons' which are coloured red or green based on certain criteria and what is selected in the filters. The filters are just a way to select different offices in different regions and when selecting an office the buttons should change colour depending on whether the targets for the different metrics have been hit for that office or not.
The navigation buttons on Tableau won't accommodate this so I've made a work around. For each 'button' I've created a worksheet with just the text of the metric name on the Label mark and a calculated field on the colour mark. I've then added the worksheet to the Dashboard and added an action to go to the corresponding metric dashboard when the 'button' is clicked on.
The issue I'm having is the conditional colouring of one of these metrics. This metric is based on stock levels. For each office there are multiple categories of stock types, each with a corresponding target, with multiple 'bins' in each category. I want the button to turn red if ANY of the combined total of stock in the bins for one category is over the target for that category for that office.
To try and type it logically-
For the currently filtered data: IF EXISTS(FOR EACH OFFICE( FOR EACH CATEGORY: [SUM(BinValue)< CategoryTarget])) THEN 'Green' ELSE 'Red'
I've tried to translate that logic into Tableau's functions in a calculated field and have the following:
SUM(INT({INCLUDE [Category]:Min([CategoryTarget])} > {INCLUDE [Category]:SUM(BinValue)}))
This colouring is correct when I add the Office Name and Category pills to the worksheet to test my logic however when I remove the pills the colouring isn't correct. Something seems to be going wrong when I try to sum the number of categories that are within target levels over all offices and targets.
I've tried so many iterations of the following functions and have been going around in circles for days now:
INCLUDE, EXCLUDE, FIXED, IF, SUM, INT
If anyone knows how to do this properly or even just a different way of being able to conditionally colour buttons on a dashboard I would be incredibly grateful.
The structure of my data is as follows with some dummy data as an example:
Region
SubRegion
Office
Category
Bin
BinValue
CategoryTarget
North
NorthWest
Manchester
Toys
B123
30
50
North
NorthWest
Manchester
Toys
B456
40
50
So for a Stock Level metric selecting any of ALL/North/NorthWest/Manchester filter options should flag as red due to the total of the bins in one category in an office being higher than the target amount for that category for that office.
I've updated my calculated field however I'm still having issues with the grouping showing as true/false correctly.
This is what it is now:
MAX( {INCLUDE Category, Office:Sum(BinValue)} > {INCLUDE Category, Office:MIN(CategoryTarget)} )
With True showing as Red and False Green (we want to be below target hence the green).
When working on the example to showcase the issue I managed to get it working.
I ended up using the following logic:
max({EXCLUDE [Bin]:SUM([Bin Value])} > [Category Target])
This meant that even if most of the Offices in the filter were within their stock level targets, if there was one with stock levels over target the 'button' showed as red.
I published the example I've used anyway in case it helps others in the future.
Link to the Tableau Public dashboard:
https://public.tableau.com/views/ConditionalColouring/Dashboard1?:language=en-GB&:useGuest=true&:display_count=y&:origin=viz_share_link
Thank you very much for the help!
To work with logical conditions, such as testing whether a condition holds for any (or every) record in a group of data rows, it helps to understand that Tableau treats the boolean value "True" as greater than the boolean value "False".
Once you get comfortable with that idea, you can use the functions MAX() (or MIN()) to test whether a condition holds for any record (or for every record, respectively). So MAX(False, False, True, False) is True.
So to tell if any records have an actual value below their target, test MAX([Actual Value] < [Target Value])
You can then combine this idea with dimensions on the viz (or LOD calcs if necessary) to group the data records appropriately before testing your conditions. If you work with the same conditions repeatedly, this type of calculation can be very useful for defining sets that get used in multiple places.
One technical caveat, if your condition test ever evaluates to NULL, then those null values are ignored by MIN() and MAX() - just like other aggregation functions do. So for example, you could test whether every record satisfies a condition using MIN() and get a possibly misleading result if all the non-null values are True (so MIN() reports True). MIN(TRUE, TRUE, NULL, TRUE) = TRUE. If your condition can evaluate to NULL, and you don't want to ignore nulls, but instead treat it as, say, the same as False, then you can use the IFNULL() function to provide a default value for your condition.
As an example, MIN(IFNULL([Actual Value] > [Target Value], FALSE)) returns True only if every record has a value above its target, treating any records with missing values or targets as failing the condition - i.e. not exceeding the target. The choice of whether to have a default value for a condition, and what it should be, are problem dependent of course. If your data does not have null values, you don't have this complication to consider.
Though the data you have given is very less, yet I think this calculation field you require
IF { FIXED [Region], [Sub-Region], [Office], [Category] : SUM([Bin Value])}
> {FIXED [Region], [Sub-Region], [Office], [Category] : MIN([Category Target])} THEN 'RED' ELSE 'GREEN' END
This is based on assumption that for every group of region/sub-region/office/category target value will be same in each row within the group. Therefore MAX/AVG etc. will all work in place of MIN used in the calculation.
See I added two rows in your data
and result
I have a big report with 100+ columns named 'Column001', 'Column002' etc etc.
These columns hide depending on whether there is any data or not, and I'd like to have the last column have a right side border.
So I could do something like (in pseudocode)
= IIF(COUNT(ReportFields!Columns.Values) = CINT(RIGHT([CurrentReportItem].Name,3)), RIGHT_BORDER, NO_BORDER)
Is there any way to get the current item (ReportItem!) so that I can get it's name?
If you have control over the query then you can modify it to return the number of visible rows as a non-displayed new field in the result set and then compare that value at each column to decide if the border should be displayed.
If you do not have control over the query then I think you are limited to a painful series of nested IIf() statements.
This should be easy, but I am stuck.
I have a table listing some figures about Qualifications - to achieve which a dataset that is essentially a row per Student is being grouped on Qualification with a Parent Grouping on "Measure" (which is just a bucket of qualifications).
One of the columns is trying to work out the number of students (well, more properly the number of students with a value in a particular field, weighted by another field) in each Measure/Qualification. In the screenshot below, it's the "Pred. Avg" column on the right hand side.
So for the Qualification Row Grouping, that column is calculated by:
=CountDistinct(Iif(IsNothing(Fields!AVG_PTS.Value) = False, Fields!Learner_ID.Value, Nothing), "Qual") * Lookup(Fields!Qual_Code.Value, Fields!Qual_Code.Value, Fields!size.Value, "DS_KS5Nationals_LKP")
This works fine - the values of 35 and 11.5 in that rightmost column are correct for those rows. What the top row should be doing is simply adding up the values in the other rows to give me the number of students in this Measure, in this case to give 46.5. To do that the expression I am using is:
=Sum(CountDistinct(Iif(IsNothing(Fields!AVG_PTS.Value) = False, Fields!Learner_ID.Value, Nothing), "Qual") * Lookup(Fields!Qual_Code.Value, Fields!Qual_Code.Value, Fields!size.Value, "DS_KS5Nationals_LKP"), "Measure")
However as you can see in the screenshot, this returns 2917 instead.
So my question is; Why doesn't that work, and given that it doesn't work how can I, within a parent group, aggregate the results of aggregates inside a child group?
EDIT:
OK so, I have determined that the following works correctly:
=Sum(CountDistinct(Iif(IsNothing(Fields!AVG_PTS.Value) = False, Fields!Learner_ID.Value, Nothing), "Qual"), "Measure")
The problem there is that the Qual row that returns 11.5 is weighted to 0.5. I.E. it actually returns 23, and the Lookup(Fields!Qual_Code.Value, Fields!Qual_Code.Value, Fields!size.Value, "DS_KS5Nationals_LKP") is for that row returning 0.5 and altering it to 11.5...so the question becomes; "how do I force that ...*Lookup(Fields!Qual_Code.Value, Fields!Qual_Code.Value, Fields!size.Value, "DS_KS5Nationals_LKP") into the "Qual" scope, like the CountDistinct() is already in?
The issue here is that you're trying to aggregate values using that Lookup function which only returns one value. There are a couple ways you could go about doing this. One option would be to use the LookupSet function to get the applicable weightings. An even better option is to combine the data in your dataset so that the weighting is available without using a lookup. That way the function can recalculate an any grouping level without you having to force a scope on it. Also, CountDistinct ignores "Nothing" so you can do without the extra IIf statement. Hope that helps.
I am trying to dynamically format the labels on my SSRS charts based on the underlying value. I'm trying to do this in two scenarios, one to format dates as ordinals and another to choose the appropriate number of decimal places based on actual values present. However, when I use the expression editor with an expression something like this...
=IIF(MAX(ABS(Fields![axisfield].Value))<2, "0.0%","0%")
...the Fields![axisfield].Value is always returning the first value from the dataset, meaning, in this example, if the first value is less than two, the labels will be formatted with one decimal place, even if it is the only one less than two. (So the 'MAX' function is essentially irrelevant.)
That example is attempting to set the overall formatting based on the largest data point in the series, in this next one I'm trying to format each label separately to get Ordinal dates (i.e. 1st, 2nd, etc, and yes, this formula is incomplete: it doesn't need to be to illustrate the problem):
="dd"+IIF(DatePart("d", Fields!date.Value)=1,"\s\t"
,IIF(DatePart("d", Fields!date.Value)=2,"\n\d"
,IIF(DatePart("d", Fields!date.Value)=3,"\r\d"
,"\t\h")))
This will give 1st, 2st, 3st and so on, as the first row in the dataset is for the first.
So, my question is, how do I get round this and, in the first example get the true maximum, and in the second reference the actual value being formatted?
Thanks!
I've had the same issues with using custom functions for setting label visibility. (see my entry for this: How to Display Only 1 Value Label in SSRS 2012 Calculated/Derived Series? )
I believe the issue is that data and fields are bound to the underlying data series but are not bound and accessible within the label itself.
You should be able to set the formatting in the function for the series data itself (as in the 2nd example) and then just set data labels, which will use the underlying series field value. An example with your data might be something like the following, which returns the values with the format:
="dd"+IIF(DatePart("d", Fields!date.Value)=1,Format(Fields!date.Value, "\s\t")
,IIF(DatePart("d", Fields!date.Value)=2,Format(Fields!date.Value,"\n\d")
,IIF(DatePart("d", Fields!date.Value)=3,Format(Fields!date.Value, "\r\d")
,Format(Fields!date.Value,"\t\h"))))
In the first example, you can get the max value to referring to the Dataset, as opposed to the field. Your code would then be:
=IIF(ABS(MAX(Fields![axisfield].Value, "YourDatasetName"))<2, "0.0%","0%")
(I changed the order of operations for Abs and Max because you have to use an aggregate function when referring to the whole dataset. Only then can you refer to the specific value.)