Story:
Basically I'm trying to showcase the month to date compared to the parallel month along with the delta.
Usually this will only be used for the current month. I was able to add future dates but the values for month to date is repeated for all the days in the future
Objective:
I would like the value to be 0 instead of the last value being repeated for the entire future dates
Essentially I would like the following code for my measure but its not working:
_Join MTD =
IF(
TODAY()<Current Row date,
0,
TOTALMTD(SUM('Fact Table'[Join Count]),'Date Table'[Date])
)
Measures have no concept of a "current row". Measures are only affected by filter context. This can be confusing at first, because a table visual or a matrix visual has row labels, but these things are contributing filter context. The only place you will find row context is when adding a calculated column to a data table or within an iterator function.
You can probably do something like what I show below, but if you need any more help you'll need to share a sample of your data model (tables, columns, a few sample rows from each, relationships among tables) as well as the visual you're building.
_Join MTD =
IF(
TODAY () < MIN ( 'Date Table'[Date] ), // here we find the smallest day in filter
// context with MIN. If there is only one day
// in context, well then that is the min
0,
TOTALMTD ( SUM ( 'Fact Table'[Join Count] ), 'Date Table'[Date] )
)
It's a common idiom in DAX to take the MAX or MIN of a column when you want a "currently selected" value, though there are also more selective functions, such as SELECTEDVALUE if you want to guarantee that exactly one value is in filter context.
Edit: Per comments, the goal is to compare to yesterday, not today. We can simply take the measure above and modify its predicate as below:
( TODAY () - 1 ) < MIN ( 'Date Table'[Date] )
Arithmetic operators do reasonable things with date and time types in DAX. Also sometimes useful is that the DATE function deals gracefully with overflow and underflow, per the examples below:
DATE ( 2018, 1, 366 ) // => 2019-01-01
DATE ( 2018, 0, 1 ) // => 2017-12-01
DATE ( 2019, 2, 31 ) // => 2019-03-03
DATE ( 2019, 3, 0 ) // => 2019-02-28
So, if you must, you can abuse this function for date shifting as well, though it's usually a last resort.
Related
My intention is to populate days of the month to simulate a data warehouse periodic snapshot table using DAX measures. My goal is to show non-additive values for the quantity.
Consider the following transactions:
The granularity of my snapshot table is day. So it should show the following:
Take note that a day may have multiple entries but I am only interested in the latest entry for the day. If I am looking at the figures using a week period it should show the latest entry for the week. It all depends on the context fixter.
However after applying the measure I end up with:
There are three transactions. Two on day 2 and the other on day 4. Instead of calculating a running total I want to show the latest Qty for the days which have no transactions without running accumulating totals. So, day 4 should show 4 instead of summing up day 3 and day 4 which gives me 10. I've been experimenting with LASTNONBLANK without much success.
This is the measure I'm using:
Snapshot =
CALCULATE(
SUM('Inventory'[Quantity]),
FILTER(
ALL ( 'Date'[Date] ),
'Date'[Date] <= MAX( 'Date'[Date] )
)
)
There are two tables involved:
Table # 1: Inventory table containing the transactions. It includes the product id, the date/time the transaction was recorded and the quantity.
Table # 2: A date table 'Date' which has been marked as a date table in Power BI. There is a relationship between the Inventory and the Date table based on a date key. So, in the measure, 'Date'[Date] refers to the Date column in the Date table.
You can use the LASTNONBLANKVALUE function, that returns the last value of the expression specified as second parameter sorted by the column specified as first parameter.
Since LASTNONBLANKVALUE implicitly wraps the second parameter into a CALCULATE, a context transition happens and therefore the row context is transformed into the corresponding filter context. So we also need to use VALUES to apply the filter context to the T[Qty] column. The returned table is a single row column and DAX can automatically convert a single column, single row table to a scalar value.
Then, since we don't have a dimension table we have to get rid of cross-filtering, therefore we must use REMOVEFILTERS over the whole table.
the filter expression T[Day] < MaxDay is needed because LASTNONBLANKVALUE must be called in a filter context containing all the rows preceding and including the current one.
So, assuming that the table name is T with fields Day and Qty like in your sample data, this code should work
Edit: changed in order to support multiple rows with same day, assuming the desired result is the sum of the last day quantities
Measure =
VAR MaxDay =
MAX ( T[Day] )
RETURN
CALCULATE (
LASTNONBLANKVALUE (
T[Day],
SUM ( T[Qty] )
),
T[Day] <= MaxDay,
REMOVEFILTERS ( T )
) + 0
Edit: after reading the comments, this might work on your model (untested)
Measure =
VAR MaxDay =
MAX ( 'Date'[Date] )
RETURN
CALCULATE (
LASTNONBLANKVALUE (
Inventory[RecordedDate],
SUM ( Inventory[Quantity] )
),
'Date'[Date] <= MaxDay
) + 0
I have created a date table in a Power BI data model that includes Date, Week, Month, Year, Fiscal Month, etc.
If I am sliced by week (the week appearing in the "Columns" section of a Power BI Matrix Visual), then I am including 7 rows of data (one for each day) from the date table. If by month, then it could be anywhere from 28 to 31 days.
I'm creating a measure that depends on calculating values for the previous slice's date range. For instance, if I am sliced to 6/15/2020 then I need to make a calculation for 6/14/2020 and if I'm sliced by Month then I need the previous month's date range, etc.
Is there a way in DAX to slice by the previous slice or to detect which field you are currently sliced by?
I would like to avoid writing a giant if statement to detect the different possibilities. I am hoping there is a generic way to retrieve all the dates in the previous slice no matter how we are sliced.
I have made use of the typical parrallelperiod, datesinperiod, datesmtd, etc. but that is not what I'm looking for.
Date tables typically have several hierarchies that could allow for use of stuff like isinscope, but since I don't want to have a giant block of code to run through the various things that could potentially be in scope, I don't think that approach will work either.
My goal is to build a single measure that would work for users building various reports no matter which date field they slice by.
Since Week isn't even a period that Time Intelligence works for (as fas as I understand), I don't think there's much hope for a clean general solution but here are a couple of imperfect approaches sketched:
1. SWITCH
PriorSlice =
VAR PriorDates =
SWITCH (
TRUE (),
ISFILTERED ( DateTable[DateKey] ), DATEADD ( DateTime[DateKey], -1, DAY ),
ISFILTERED ( DateTable[Week] ), DATEADD ( DateTime[DateKey], -7, DAY ),
ISFILTERED ( DateTable[Month] ), PARALLELPERIOD ( DateTime[DateKey], -1, MONTH ),
ISFILTERED ( DateTable[Year] ), PARALLELPERIOD ( DateTime[DateKey], -1, YEAR )
)
RETURN
CALCULATE ( [Measure], PriorDates )
(I'm not sure if ISINSCOPE would be better than ISFILTERED here or not.)
2. Shift by days in scope
PriorSlice =
VAR Shift = 1 + MAX ( DateTable[DateKey] ) - MIN ( DateTable[DateKey] )
RETURN
CALCULATE ( [Measure], DATEADD ( DateTable[DateKey], -Shift, DAY ) )
This won't really work for uneven periods like months. For example, you have February selected and want to get all of January (instead of the last 28/29 days).
Ultimately, you're going to need some kind of SWITCH either in your measure or in a helper measure if you want to work at multiple time granularities simultaneously.
I trying to work with COVID data and find the day on day increase of cases. Essentially, take today's value and minus yesterday's value to get the increase figure. My data also starts on April 10th so if the data is this, I will return a 0.
Given the below formula, the 0 is correctly returned for April 10th but all other values return 17908. All column types are 'Whole number'. Can anybody give me some information on this? Apologies if this is an obvious issue, I am used to working with Python and R and have been thrust into Power BI.
My data is very simple. It just continues like this:
ID Date No of cases
1) 10 April 3
1) 11 April 6
1) 12 April 15
Diff_Daily =
VAR blankValue = 0
VAR difference =
SUM ( Table[No of cases] )
- CALCULATE ( SUM ( Table[No of cases] ), PREVIOUSDAY ( Table[Date] ) )
RETURN
IF ( Table[Date].[Date] = DATE ( 2020, 04, 10 ), blankValue, difference )
In order to solve that I made the following measures. I made more than one just because to me it usually makes more sense (you can re-use them in a KPI or other charts) but you can merge them together if you don't need them all.
Cases = SUM(MyTable[No Of Cases])
Cases (prev Day) =
CALCULATE(
[Cases]
,PREVIOUSDAY(MyTable[Date])
)
Daily Delta =
IF(ISBLANK([Cases (prev Day)])
,0
,[Cases] - [Cases (prev Day)]
)
Let me know if this helps.
About your formula, It looks nice to me, I suggest you check the data type of your columns, especially the date one. other than that the only error I see is the use of a field inside the IF statement, you may want to use ISBLANK([MyMeasure]) (at least in this case)
I have a metric I need to replicate in DAX for PowerBI and I am not quite sure how to do it.
The basics are this:
Fact Table: Opportunity
Dimensions: Created Date, Closed Date
The metric goes like this, I will just give an example, because I really dont know how to explain it.
SUM OF:
Created in FEB and Closed Date in FEB, MAR, APR
Created in MAR and Closed Date in MAR, APR
Created in APR and Closed in APR
This would happen for each month in the table/matrix.
Seems like I would need some variables something like
Measure =
VAR Month1 = SUM(ClosedOpps) where ClosedDate between CurrentMonth and CurrentMonth + 2
VAR Month2 = SUM(CLosedOpps) where ClosedDate betwwen CurrentMonth + 1 and CurrentMonth + 2
VAR Month3 = SUM(ClosedOpps) where ClosedDate = CurrentMonth + 2
Return Month1 + Month2 + Month3
My understanding is, the Closed Date filter would be the Table/Matrix Visual when I drag the column MonthYear into the visual
EDIT:
Here is a simplified replica of what they are doing in Excel
So The data on the left is the fact table. You can see when the Opps are created, when they are closed. I added in the Created MonthYear and the Closed MonthYear. The Pivot is what they have now in Excel. Dates across the top (Columns) are Created YearMonth, Dates for the Rows are Closed YearMonth.
I need to be able to SUM the numbers inside the of I3:K5 which total 5500 in the example.
UPDATE:
So I have added in a suggested Date Dimension table, Duplicated it (One for Open Date, one for Closed Date) I added a column DateDIM_KEY to each which is just a numerical index. The fact table has these keys, and they are loaded off of the same date range (2013 to 2030). The column ActualValue in the fact table is the column we would SUM.
Here is the updated Fact table sample. I pulled the DateDIM_KEY values directly from the date dimension for those dates.
You need a good date dimension. And you need to have it roleplaying for OpenDate and CloseDate. There are many good date dimensions out there. I like mine.
Assuming that you're putting 'OpenDate'[Month] on an axis label.
Opportunity Value = SUM ( 'Opportunity'[Value] )
MyMeasure iterator =
// start of the month on the current row of a pivot/axis label of a chart
VAR CurrentMonthStart = MIN ( 'OpenDate'[Date] )
// End of the month 2 months out
VAR ThreeMonthsOutEnd = EOMONTH ( CurrentMonthStart, 2 )
// This represents one row per month. You could also use a MonthAndYear type field.
// We will walk through the three open months we care about, and in each will sum
// the value for the opportunities opened in that month, with additional filters.
VAR NextThreeOpenMonths =
CALCULATETABLE (
VALUES ( 'OpenDate'[MonthIndex] ),
ALL ( 'OpenDate' ),
DATESBETWEEN ( 'OpenDate'[Date], CurrentMonthStart, ThreeMonthsOutEnd )
)
RETURN
// Iterate each OpenMonth
SUMX (
NextThreeOpenMonths,
// On each step of the iteration, grab the start of the currently iterated month
VAR IterMonthStart = CALCULATE ( MIN ( 'OpenDate'[Date] ) )
RETURN
CALCULATE (
[Opportunity Value],
// There is date context from visuals we want to ignore:
ALLEXCEPT ( 'OpenDate', 'OpenDate'[MonthIndex] ),
// filter CloseDate to be between the start of the currently iterated
// open month and the originally calculated ThreeMonthsOutEnd. The latter
// is static within the scope of the iteration.
DATESBETWEEN ( 'CloseDate'[Date], IterMonthStart, ThreeMonthsOutEnd )
)
)
Also, while writing the previous iterative approach, I realized we could do the work in a single setfilter:
MyMeasure set =
// MonthIndex is in my date dimension - super useful for arithmetic on dates.
// Read the readme.
VAR C = SELECTEDVALUE ( 'OpenDate'[MonthIndex] ) // want a short name below
// Table literal syntax - two column table, where each parenthesized expression
// forms a row. If it were much more, I'd do something clever with generate, but
// six cases are easy to write by hand.
VAR MonthFilters = {
(C, C),
(C, C+1),
(C, C+2),
(C+1, C+1),
(C+1, C+2),
(C+2, C+2)
}
RETURN
CALCULATE (
[Opportunity Value],
TREATAS ( MonthFilters, 'OpenDate'[MonthIndex], 'CloseDate'[MonthIndex] )
)
I like the latter a lot better, but didn't think of it until after writing the iterative version, so I'm leaving both. Set-based should be better performing.
Edit: some screengrabs I forgot:
Here's the relationship diagram for roleplaying date dim:
And here's the visual in action with both measures:
Best thing to do here is to add a custom column (under edit querys) with the date diff per month. Now you can filter after the column LeadTimeInMonth for you scenarios. If you drag and drop your fields into the visual you can filter by this column.
Date.Month([ClosedDAte])-Date.Month([OpenDate])
I am not sure what you really want to evaluate but if you need need exactly ClosedDate between CurrentMonth and CurrentMonth + 2 you can first evaluate the month from the ClosedDate then the month of today and filter after the result.
I have following scenario which has been simplified a little:
Costs fact table:
date, project_key, costs €
Project dimension:
project_key, name, starting date, ending date
Date dimension:
date, years, months, weeks, etc
I would need to create a measure which would tell project duration of days using starting and ending dates from project dimension. The first challenge is that there isn't transactions for all days in the fact table. Project starting date might be 1st of January but first cost transaction is on fact table like 15th on January. So we still need to calculate the days between starting and ending date if on filter context.
So the second challenge is the filter context. User might want to view only February. So it project starting date is 1.6.2016 and ending date is 1.11.2016 and user wants to view only September it should display only 30 days.
The third challenge is to view days for multiple projects. So if user selects only single day it should view count for all of the projects in progress.
I'm thankful for any help which could lead towards the solution. So don't hesitate to ask more details if needed.
edit: Here is a picture to explain this better:
Update 7.2.2017
Still trying to create a single measure for this solution. Measure which user could use with only dates, projects or as it is. Separate calculated column for ongoing project counts per day would be easy solution but it would only filter by date table.
Update 9.2.2017
Thank you all for your efforts. As an end result I'm confident that calculations not based on fact table are quite tricky. For this specific case I ended up doing new table with CROSS JOIN on dates and project ids to fulfill all requirements. One option also was to add starting and ending dates as own lines to fact table with zero costs. The real solution also have more dimensions we need to take into consideration.
To get the expected result you have to create a calculated column and a measure, the calculated column lets count the number of projects in dates where projects were executed and the measure to count the number of days elapsed from [starting_date] and [ending_date] in each project taking in account filters.
The calculated column have to be created in the dim_date table using this expression:
Count of Projects =
SUMX (
FILTER (
project_dim,
[starting_date] <= EARLIER ( date_dim[date] )
&& [ending_date] >= EARLIER ( date_dim[date] )
),
1
)
The measure should be created in the project_dim table using this expression:
Duration (Days) =
DATEDIFF (
MAX ( MIN ( [starting_date] ), MIN ( date_dim[date] ) ),
MIN ( MAX ( [ending_date] ), MAX ( date_dim[date] ) ),
DAY
)
+ 1
The result you will get is something like this:
And this if you filter the week using an slicer or a filter on dim_date table
Update
Support for SSAS 2014 - DATEDIFF() is available in SSAS 2016.
First of all, it is important you realize you are measuring two different things but you want only one measure visible to your users. In the first Expected result you want to get the number of projects running in each date while in the Expected results 2 and 3 (in the OP) you want the days elapsed in each project taking in account filters on date_dim.
You can create a measure to wrap both measures in one and use HASONEFILTER to determine the context where each measure should run. Before continue with the wrapping measure check the below measure that replaces the measure posted above using DATEDIFF function which doesn't work in your environment.
After creating the previous calculated column that is required to determine the number of projects in each date, create a measure called Duration Measure, this measure won't be used by your users but lets us calculate the final measure.
Duration Measure = SUMX(FILTER (
date_dim,
date_dim[date] >= MIN ( project_dim[starting_date] )
&& date_dim[date] <= MAX ( project_dim[ending_date] )
),1
)
Now the final measure which your users should interact can be written like this:
Duration (Days) =
IF (
HASONEFILTER ( date_dim[date] ),
SUM ( date_dim[Count of Projects] ),
[Duration Measure]
)
This measure will determine the context and will return the right measure for the given context. So you can add the same measure for both tables and it will return the desired result.
Despite this solution is demonstrated in Power BI it works in Power Pivot too.
First I would create 2 relationships:
project_dim[project_key] => costs_fact[project_key]
date_dim[date] => costs_fact[date]
The Costs measure would be just: SUM ( costs_fact[costs] )
The Duration (days) measure needs a CALCULATE to change the filter context on the Date dimension. This is effectively calculating a relationship between project_dim and date_dim on the fly, based on the selected rows from both tables.
Duration (days) =
CALCULATE (
COUNTROWS ( date_dim ),
FILTER (
date_dim,
date_dim[date] >= MIN ( project_dim[starting_date] )
&& date_dim[date] <= MAX ( project_dim[ending_date] )
)
)
I suggest you to separate the measure Duration (days) into different calculated column/measure as they don't actually have the same meaning under different contexts.
First of all, create a one-to-many relationship between dates/costs and projects/costs. (Note the single cross filter direction or the filter context will be wrongly applied during calculation)
For the Expected result 1, I've created a calculated column in the date dimension called Project (days). It counts how many projects are in progress for a given day.
Project (days) =
COUNTROWS(
FILTER(
projects,
dates[date] >= projects[starting_date] &&
dates[date] <= projects[ending_date]
)
)
P.S. If you want to have aggregated results on weekly/monthly basis, you can further create a measure and aggregate Project (days).
For Expected result 2 and 3, the measure Duration (days) is as follows:
Duration (days) =
COUNTROWS(
FILTER(
dates,
dates[date] >= FIRSTDATE(projects[starting_date]) &&
dates[date] <= FIRSTDATE(projects[ending_date])
)
)
The result will be as expected: