Create numeric range slicer - powerbi

I have a table containing :
VideoID Views DeviceType
1 12 Desktop
1 30 Mobile
1 95 Tablette
...
I want to create a slicer names Views Ranges with ranges like below to filter videos by the sum of their views :
From 100 to 1000
From 1001 to 10000
...
VideoID 1 has a total 137 of views, so when I choose From 100 to 1000 in the filter it will be show.
This is what I tried :
Range =
VAR ranges =[Total Views]
RETURN
SWITCH(TRUE(),
ranges <= 100, "From 0 to 100",
ranges <= 10000, "From 100 to 10,000",
ranges <= 20000, "From 10,000 to 20,000",
....
)
But the filter is empty.

This appears to fit the Basic Category of the Static Segmentation pattern. It's explained in full detail here:
https://www.daxpatterns.com/static-segmentation/
To summarize: I would add a DAX Calculated Column to your first table ("Views"?) to return the Key from a table of ranges. You can then use that Key to establish a relationship to the table of ranges. With the relationship in place, you can use the table of ranges to filter in a Slicer, or categorize in any other visual.

Related

Power BI - Filtering model on latest version of all attributes of all dimensions through DAX

I have a model that's comprised of multiple tables containing, for every ID, multiple rows with a valid_from and valid_to dates.
This model has one table in that is linked to every other table (a table working as both a fact and a dimension).
This fact has bi-directional cross filtering with the other tables.
I also have a date dimension that is not linked to any other table.
I want to be able to calculate the sum of a column in this table in the following way:
If a date range is selected, I want to get the sum of the latest value per ID from the fact able that is before the max selected date from the date dimension.
If no date is selected, I want to get the sum of the current version of the value per ID.
This comes down to selecting the latest value per ID filtered on the dates.
Because of the nature of the model (bi-directional with the fact/dimension table), I want to have the latest version of any attribute from any dimension selected in the visual.
Here's an data example and the desired outcome:
fact/dimension table:
ID
Valid_from
Valid_to
Amount
SK_DIM1
SK_DIM2
1
01-01-2020
05-12-2021
50
1234
6787
1
05-13-2021
07-31-2021
100
1235
6787
1
08-01-2021
12-25-2021
100
1236
6787
1
12-26-2021
12-31-2021
200
1236
6787
1
01-01-2022
12-31-9999
200
1236
6788
Dimension 1:
ID
SK
Valid_from
Valid_to
Name
1
1234
10-20-2019
06-01-2021
Name 1
1
1235
06-02-2021
07-31-2021
Name 2
1
1236
08-01-2021
12-31-9999
Name 3
Dimension 2:
ID
SK
Valid_from
Valid_to
Name
1
6787
10-20-2019
12-31-2021
Name 1
1
6788
01-01-2022
12-31-9999
Name 2
My measure is supposed to do the following:
If no date is selected than the result will be a matrix like the following:
Dim 1 Name
Dim 2 Name
Amount Measure
Name 3
Name 2
200
If July 2021 is selected than the result will be a matrix like the following:
Dim 1 Name
Dim 2 Name
Amount Measure
Name 2
Name 1
100
So the idea here is that the measure would filter the fact table on the latest valid value in the selected date range, and then the bi-directional relationships will filter the dimensions to get the corresponding version to that row with the max validity (last valid row) in the selected range date.
I have tried to do the following two DAX codes but it's not working:
Solution 1: With this solution, filtering on other dimensions work and I get the last version in the selected date range for all attributes of all used dimensions. But the problem here is that the max valid from is not calculated per ID, so I only get the max valid from overall.
Amount Measure=
VAR _maxSelectedDate = MAX(Dates[Dates])
VAR _minSelectedDate = MIN(Dates[Dates])
VAR _maxValidFrom =
CALCULATE(
MAX(fact[valid_from]),
DATESBETWEEN(fact[valid_from], _minSelectedDate, _maxSelectedDate)
|| DATESBETWEEN(fact[valid_to], _minSelectedDate, _maxSelectedDate)
)
RETURN
CALCULATE(
SUM(fact[Amount]),
fact[valid_from] = _maxValidFrom
)
Solution 2: With this solution, I do get the right max valid from per ID and the resulting number is correct, but for some reason, when I use other attributes from the dimensions, it duplicates the amount for every version of that attribute. The bi-directional filtering does not work anymore with Solution 2.
Amount Measure=
VAR _maxSelectedDate = MAX(Dates[Dates])
VAR _minSelectedDate = MIN(Dates[Dates])
VAR _maxValidFromPerID =
SUMMARIZE(
FILTER(
fact,
DATESBETWEEN(fact[valid_from], _minSelectedDate, _maxSelectedDate)
|| DATESBETWEEN(fact[valid_to], _minSelectedDate, _maxSelectedDate)
),
fact[ID],
"maxValidFrom",
MAX(fact[valid_from])
)
RETURN
CALCULATE(
SUM(fact[Amount]),
TREATAS(
_maxValidFromPerID,
fact[ID],
fact[valid_from]
)
)
So if somebody can explain why the bi-directional filtering doesn't work anymore that will be great, and also, more importantly, if you have any solution to have both the latest value per ID and still keep filtering on other attributes, that would be great!
Sorry for the long post, but I thought it's best to give all the details for a complete understanding of my issue, this has been picking my brain since few days now and I'm sure I'm missing something stupid but I turned to this community for help because I cannot seem to be able to find a solution!
Thank you very much in advance for any help!
Seems to be workable with a dummy model. I didn't got the point how filter ID, so if it creates a problem let me know how you handle ID. Then I changed fact to facts as fact is a function. Also, I'm not sure about the workability of the measure at your real model. Hope you will give some feedback.
Amount Measure =
VAR ValidDate=
calculate(
max(facts[Valid_to])
,ALLEXCEPT(facts,facts[ID])
,facts[Valid_to]<=MAX(Dates[Date])
)
Return
CALCULATE(
SUM(facts[Amount])
,TREATAS({ValidDate},facts[Valid_to])
)

Make first few rows unresponsive to slicer Power BI

I have a data which looks like below:
Brands Sales Category Index
Brand1 588 A 1
Brand2 846 A 2
Brand3 827 A 3
Brand4 951 A 4
Brand5 673 B 5
Brand6 637 B 6
Brand7 575 B 7
Brand8 995 B 8
Btand9 737 C 9
Brand10 661 C 10
Brand11 729 C 11
Brand12 789 C 12
Brand13 836 C 13
Problem statement :
I am trying to put Category as a slicer. However I want the rows for Category A to be present in the table view irrespective of the slicer which is selected.
Example: Lets say if Category B is selected in slicer , in this case the table should return all rows until Rank 8.
Below is an example of the desired output when category C is selected:
As you can see, the visual table has both Category A and Category C.
Similarly when both B and C are selected, I should be able to display all the categories (A,B and C).
What tried:
I was thinking if we can use a conditional DAX which return 1 for selected values in slicers and mark rest as 0, I could use that as a visual filter and filter out 0. I tried various combinations of Filter with in Filters and SELECTCOLUMN but it did not work. Even the below measure returns all the rows instead of Selected values|| category="A"
test1 = CALCULATE(MIN('Table'[Index]),FILTER(ALLEXCEPT('Table','Table'[Brands]),'Table'[Category]=SELECTEDVALUE('Table'[Category]) || 'Table'[Category]="A"))
I also tried something like:
test = var cat = min('Table'[Category]) return IF(cat = SELECTEDVALUE('Table'[Category])||cat="A",1,0)
But this gives all as 1 , doesnot give 0 for rows which does not match the condition (note I have blocked the slicer interaction here)
Any help would be highly appreciated.
First, you need to separate your slicer table as keeping the value in the same table you can not achieve the requirement. You can create custom table with this below code-
considering your base table name sales
Lets create the custom table category list
No relation can be there between table sales and category list
category list =
SELECTCOLUMNS(
sales,
"Category",sales[Category]
)
Now, create the slicer using new custom table category list and create this below Measure-
is filter =
if(
MIN(sales[Category]) = "A",
1,
if (
MIN(sales[Category]) IN VALUES('category list'[Category]),
1,
0
)
)
Here below is a sample output when C selected-

DAX selecting and displaying the max value of all selected records

Problem
I'm trying to calculate and display the maximum value of all selected rows alongside their actual values in a table in Power BI. When I try to do this with the measure MaxSelectedSales = MAXX(ALLSELECTED(FactSales), FactSales[Value]), the maximum value ends up being repeated, like this:
If I add additional dimensions to the output, even more rows appear.
What I want to see is just the selected rows in the fact table, without the blank values. (i.e., only four rows would be displayed for SaleId 1 through 4).
Does anyone know how I can achieve my goal with the data model shown below?
Details
I've configured the following model.
The DimMarket and DimSubMarket tables have two rows each, you can see their names above. The FactSales table looks like this:
SaleId
MarketId
SubMarketId
Value
IsCurrent
1
1
1
100
true
2
2
1
50
true
3
1
2
60
true
4
2
2
140
true
5
1
1
30
false
6
2
2
20
false
7
1
1
90
false
8
2
2
200
false
In the table output, I've filtered FactSales to only include rows where IsCurrent = true by setting a visual level filter.
Your max value (the measure) is a scalar value (a single value only). If you put a scalar value in a table with the other records, the value just get repeated. In general mixing scalar values and records (tables) does not really bring any benefit.
Measures like yours can be better displayed in a KPI or Multi KPI visual (normally with the year, that you get the max value per year).
If you just want to display the max value of selected rows (for example a filter in your table), use this measure:
Max Value = MAX(FactSales[Value])
This way all filter which are applied are considered in the measures calculation.
Here is a sample:
I've found a solution to my problem, but I'm slightly concerned with query performance. Although, on my current dataset, things seem to perform fairly well.
MaxSelectedSales =
MAXX(
FILTER(
SELECTCOLUMNS(
ALLSELECTED(FactSales),
"id", FactSales[SaleId],
"max", MAXX(ALLSELECTED(FactSales), FactSales[Value])
),
[id] = MAX(FactSales[SaleId])
),
[max]
)
If I understand this correctly, for every row in the output, this measure will calculate the maximum value across all selected FactSales rows, set it to a column named max and then filter the table so that only the current FactSales[SaleId] is selected. The performance hit comes from the fact that MAX needs to be executed for every row in the output and a full table scan would be done when that occurs.
Posted on behalf of the question asker

Many to Many Relationship BY DATE in PowerBI

I need to be able to calculate a measure in a table of many to many relationship. Here are my two tables:
Contracts Table
Serial# ContractTyp StartDate EndDate
A MP 1/1/2017 1/6/2018
B ML 10/24/2017 6/30/2020
A ML 1/6/2018 12/30/2019
C MU 5/15/2018 1/1/2021
Performance Table
Serial# Diff Good Bad Date
A 15 1 0 1/30/2017
B -24 1 0 12/17/2017
A 57 0 1 4/22/2017
A 18 1 0 2/1/2018
C 123 0 1 9/12/2018
So, my measure is simple. It just calculates the percentage of Good by Serial#.
NUM_GOOD = CALCULATE(COUNTA('Performance'[Good]),'Performance[Good] IN {"1"})
NUM_BAD = CALCULATE(COUNTA('Performance'[Bad]),'Performance[Bad] IN {"1"})
PERFORMANCE_METRIC = NUM_GOOD/(NUM_BAD + NUM_GOOD)
I need to be able to run that performance metric for every Serial# but only for when the machines were under a MP or MU ContractTyp. So I need it to be able to look at the Date in the Performance Table and make sure that date falls in the range between StartDate and EndDate on the Contracts Table where Serial# is MP or MU.
So I for example if I want to look at the Performance of all Serial#'s with a ContractTyp of MP or MU in the last 2 years I would want the result to look as follows:
Serial# PERFORMANCE_METRIC
A 50%
B 100%
C 0%
Thanks in advance!
Below is a formula for a Calculated Column that you could add to the Performance table to return the True / False values (ref your comments).
To explain the logic:
Store the Serial and Date values (from the current record) in variables.
If we find at least 1 matching Contract with the required ContractTyp and Date range, return True, otherwise False.
Has Contract =
VAR vSerial = [Serial#]
VAR vDate = [Date]
RETURN
IF (
CALCULATE (
COUNTROWS ( Contracts ),
Contracts[Serial#] = vSerial,
Contracts[ContractTyp] IN { "MP", "MU" },
Contracts[StartDate] <= vDate,
Contracts[EndDate] >= vDate
) > 0,
TRUE (),
FALSE ()
)
One way to do it would be to create a new table in Power BI by converting the Contracts table which is at a Serial #, Start date and End date level to a Serial # and Date level. For example if the data has the following records:
ID Type Start End
1 MP 1/1/2019 1/2/2019
You should convert it into something like this:
ID Type Date
1 MP 1/1/2019
1 MP 1/2/2019
The following link should help you out regarding this:
https://natechamberlain.com/2018/08/08/how-to-add-rows-for-dates-between-start-and-end-dates-in-power-bi-date-range-data/
Once the table is converted, you should be able to do a simple join based on Serial# and date and it will be a one to one relationship. Then you can apply whatever filter you are looking for.
One caveat here is that, the size of the data would matter a lot. If the Contracts table has a reasonable number of records, this would work, otherwise the data might blow up.
Adding another possible answer using merge queries. Instead of creating a relationship you can directly merge the two tables. If you have not done this before, you can access this is in the edit query window. Once you merge both the tables using serial ID as the key, you can use a formula like below to use as a filter:
Filter Field = IF(Date2[Date]>=Date2[Date1.Start]&&Date2[Date]<=Date2[Date1.End],1,0)
Then you can simply set Filter Field =1 in the filter pane and you should be good to go. Hope this helps.
Here is a tutorial on merge queries. The link is specifically for excel, but it should work in power bi as well:
https://www.powerquery.training/merge-tables/

DAX query for counting values based on another cumulative DAX measure

I have this table:
Id Length(m) Defect Site Date
1 10 1 y 10/1/19
2 60 0 x 09/1/19
3 30 1 y 08/1/19
4 80 1 x 07/1/19
5 20 1 x 06/1/19
I want to count the amount of defects and ids that are in the last 100m of length(sorted by date DESC), whilst maintaining the ability for this to change with additional filters. For example, what are the amount of defects for site x in the last 100m, or what are the amount of defects in the last 100m that have an ID bigger than 1.
For the question 'What are the amount of defects for site x in the last 100m', I would like the result to be 2, as the table should look like this:
Id Length(m) Length Cum. Defect Site Date
4 80 80 1 x 07/1/19
5 20 100 1 x 06/1/19
I believe the issue in creating this query so far has been that I need to create a cumulative DAX query first and then base the counting query off of that DAX query.
Also important to note that the filtering will be undertaken in PowerBI. I don't want to hardcode filters in the DAX query.
Any help is welcome.
Allwright!
I have taken a crack at this. I did assume that the id of the items(?) increments through time, so the oldest item has the lowest id.
You were correct that we need to filter the table based on the cumulative sum of the meters. So I first add a virtual column to the table (CumulativeMeters) which I can then use to filter the table on. I need to break the filter context of the ADDCOLUMNS function to sum up the hours of multiple rows.
Important is to use ALLSELECTED to keep any external filters in place. After this it is pretty straightforward to filter the table on a maximum CumulativeMeters of <= 100 meters and where the row is a defect. Counting the rows in the resulting table gives you the result you are looking for:
# Defects last 100m =
CALCULATE (
COUNTROWS ( Items ),
FILTER (
ADDCOLUMNS (
Items,
"CumulativeMeters", CALCULATE (
SUM ( Items[Length(m)] ),
FILTER (
ALLSELECTED( Items ),
Items[Date] <= EARLIER ( Items[Date] )
&& Items[Id] <= EARLIER ( Items[Id] )
)
)
),
[CumulativeMeters] <= 100
&& Items[Defect] = 1
)
)
Hope that helps,
Jan