How to use IsLeaf in mondrian mdx formula? - function

Trying to recreate this formula in Pentaho Mondrian Cube.
iif(ISLEAF([Time].[Month].CurrentMember)),[Measures].m1,0)
This formula is being used in SSAS cube already. Need to recreate similar formula in Pentaho Mondrian Cube.
Can IsLeaf be used in mondrian or is there any alternative for this?

Something like the following should work at any arbitrary level within the default hierarchy.
It will return [Mesures].m1 for the last year, which (has non-empty m1 measure and satisfies your filter conditions) in case you select [Time].[Year] members.
Or it will return the measure for the last month of the last year, which has non-empty m1 measure in case you select [Time].[Month] members.
Though I don't think it will work if you mix members for different levels (e.g. WITH SET time_members AS {[Time].[2017], [Time].[2017].[1]. [Time].[2017].[1].[31]})
If you don't need such a generic approach, then this solution may be simplified and the measure calculation speed may be improved.
Warning: the query may cause 100% CPU utilization (or may not, I am not sure), thus making your server unresponsive. So, choose your testing environment carefully.
Disclaimer: I don't have mondrian to test at the moment, so the following example is very likely to have errors.
Iif(
// Check if current [Time] member is the last member:
[Time].CurrentMember
IS
// Take the 0th member from the end of the set (AFAIK, mondrian sets are guaranteed to be ordered):
Tail(
// AFAIK, Level.Members returns the full set of members, disregarding query filters.
// So I use Filter function to filter members, which don't exist in context of current cell.
// It should leave only members, which (are related to current cell and satisfy filter conditions of the query).
Filter(
[Time].CurrentMember.Level.members
// If your measure is nullable, then you might want to use count measure in this condition instead of m1:
, NOT IsEmpty([Measures].m1)
)
// Number of members to get by the Tail() function:
, 1
// Return the only member of the set as a Member (not as a Set):
).Item(0)
// return if true
, [Measures].m1
// else:
, 0
)
Some points that may be problematic and need to be tested:
How the measure is calculated if the last [Time] member has empty m1
measure (if this is a valid case for your measure)
How the measure is calculated at different levels of [Time]
hierarchy.
How the measure is calculated if you don't use [Time] dimension in
report explicitly.
How the measure is calculated if you use [Time] dimension on slicer
axis only (in WHERE condition)
How the measure is calculated if you use restricted set of [Time]
members, e.g. explicitly enumerating members in set literal (e.g.
{[Time].[2006].[01], [Time].[2006].[02]}) or via using Filter()
function on the dimension.
How the measure is calculated if you use fiters on other
dimensions/measures.
How the measure is calculated at calculated members of [Time]
dimension (including totals and sub-totals generated by Analyzer).
How the measure is calculated if you select members from different
levels on the same axis.

Related

BO Webi - Need Variable to Remove Nulls From Results

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.

Tableau's functions - how to find an equivalent to IF EXISTS

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

How to Sum the aggregates of a child group

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.

query of calculate the annual profit growth in qlikview by using text object

I want to calculate the annual average profit growth rate for 5 years by using a text object. Qlikview does not seem to support nested aggregation... I am new to QlikView, can anyone can help me?
Tried query:
='Annual Growth Rate (2003-2007) :' & avg((sum(Profit_per_Product)/sum(Total_Retail_Price))*100)
Thanks in advance!
QlikView does support nested aggregation and can fulfil your requirements if you utilise the AGGR function. This function allows you to perform aggregation over a set of dimensions that you specify and in its simplest form, can be use as follows:
=aggr(expression,dimension)
This means that expression is evaluated for each value of dimension. For example, in your case if you used sum(Profit_per_Product)/sum(Total_Retail_Price) as your expression, and used your dimension that contained your data's year, then aggr would result in five different values (one for each change in your year). As you wish to take the average, then the AVG function happily takes these five values and provides you with your desired result.
Therefore, all you need to do is to slightly change your expression to the following (here I assumed that your dimension that contains the year is named Year):
='Annual Growth Rate (2003-2007):' & num(avg(aggr(sum(Profit_per_Product)/sum(Total_Retail_Price),Year)),'0.00%')
I also added the NUM function to format your result as a percentage.
More information on Advanced Aggregation (i.e. AGGR) may be found in section 25.2 of the QlikView Reference Manual (in my copy it's on page 291).

SSAS :: MDX :: Scope function :: Exchange Rates

Hey all,
My problem:
I'm trying to create a scope function that calculates exchange rates based on a date and currency AND a specified rate.
I have this working fine, but within my scope function I want to say - if Dimension.Attribute.member = "Latest" then use FXRate 1 otherwise use FXRate 2.
Now I even have that "working"... BUT that only works if the member is in my dataset... i.e. I can't use it as a parameter\filter.
I don't want to have to tell the users "you always have to have LatestFlag in every report... just hide the column"
I want to give the user the ability to set the report parameters before he starts analysing the data.
So here's a snippet of my code so far:
Scope ( { Measures.[Amount]} );
Scope( Leaves([ExchangeDate]), [Reporting Currency].[USD],Leaves([Currency]));
Scope( { Measures.[Amount]});
This = iif(
[Latest Flag].[Flag].CURRENTMEMBER.name = "Yes",
[Reporting Currency].[Local] / Measures.[Rate2],
[Reporting Currency].[Local] / Measures.[Rate]
);
End Scope;
End Scope;
End Scope;
I suspect I need to use another Scope instead of the iif - but I'm not sure how to implement.
Any ideas?
Oh it's probably important to note.
The FXRate table has two rates.
Rate is updated daily.
Rate2 is repeated for every currency everyday.
So irrevelant of the date, Rate2 will always be the latest rate for that currency.
The LatestFlag dimension is merely a table with yes and no and doens't have any relationship to any other table.
I'm just using it as a filter.
There is a flag on the FX table too - but I'm not using this as I need the date to be considered if it's not the latest rate.
My solution was solved in the following link.
http://social.msdn.microsoft.com/Forums/en/sqlanalysisservices/thread/cb5ec22b-65c0-4b86-9879-40e42c039808