DAX Cumulative Sum, reset by x period - powerbi

I have a function for a cumulative sum as follows:
Actions closed cumulative =
IF(MAX(Dates[Date])<CALCULATE(MIN(Dates[Date]),
FILTER(all(Dates),Dates[PeriodFiscalYear]=[CurrentPeriod1])),
CALCULATE(Actions[Actions closed on time],
FILTER(ALL(Dates),
Dates[Date]<=max(Dates[Date])
)
))
Where CurrentPeriod1 is the period we're in, which returns something like this:
PeriodFiscalYear | Actions | Actions Closed Cumulative
P01-2018/19 | 4 | 608
P02-2018/19 | 19 | 627
P03-2018/19 | 17 | 644
P04-2018/19 | 6 | 650
P05-2018/19 | 7 | 657
So it's basically counting all the actions closed in the table at the moment but I'd like to reset on a certain number of periods, for example 3 periods would be:
PeriodFiscalYear | Actions | Actions Closed Cumulative
P12-2017/18 | 10 |
P13-2017/18 | 10 |
P01-2018/19 | 4 | 24
P02-2018/19 | 19 | 33
P03-2018/19 | 17 | 40
P04-2018/19 | 6 | 42
P05-2018/19 | 7 | 30
I'm struggling to understand how to do it, despite quite a lot of reading. I have a calendar table with dates by 13 periods per year and also pretty much every measure you could think of, month, monthyear, monthperiod etc etc. Any help would be appreciated. Ultimate goal is a moving average over a set number of periods.
Thanks

Assuming your Actions table uses date values and the periods are stored in the Dates table, I suggest first creating an index column for the periods to that they are much easier to work with:
PeriodIndex = 100 * VALUE(MID(Dates[PeriodFiscalYear], 5, 4)) +
VALUE(MID(Dates[PeriodFiscalYear], 2, 2))
These index values should be integers that look like 201804 instead of P04-2018/19, for example.
Now that you have the index column to work with, you can write a rolling cumulative sum like this:
Trailing3Periods =
VAR CurrentPeriod = MAX(Dates[PeriodIndex])
RETURN CALCULATE(SUM(Actions[Actions closed on time]),
FILTER(ALL(Dates),
Dates[PeriodIndex] <= CurrentPeriod &&
Dates[PeriodIndex] > CurrentPeriod - 3))

Related

Find ID's not present in a date represented by a yyyyweek_number

I've 2 data sets, one which represts a list of all of the customers and other with their order dates
The order date are in a yyyyweek_number format, so for instance as today (2020-09-29) is week 40, the order date would be represented as 202040
I want to get a list of dealers who haven't placed orders in 4 day ranges viz. 30 days or less
60 days or less
90 days or less and
90+ days
To illustrate lets say the customer dataset is as under:
+----+
| ID |
+----+
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
| 6 |
| 7 |
| 8 |
| 9 |
| 10 |
| 11 |
| 12 |
| 13 |
| 14 |
| 15 |
+----+
and the Order table is as under:
+----+-----------------+
| ID | Order_YYYY_WEEK |
+----+-----------------+
| 1 | 202001 |
| 2 | 202003 |
| 3 | 202004 |
| 5 | 202006 |
| 2 | 202008 |
| 3 | 202010 |
| 6 | 202012 |
| 8 | 202009 |
| 1 | 202005 |
| 10 | 202015 |
| 11 | 202018 |
| 13 | 202038 |
| 15 | 202039 |
| 12 | 202040 |
+----+-----------------+
The slicer format that I've looks like this
Now say for instance the 30 days or less button is selected,
the resulting table should represent a table as under, with all the ID's from the Customer table that aren't present in the ORDER table where ORDER_YYYY_WEEK is 30 days from todays week
+----+
| ID |
+----+
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
| 6 |
| 7 |
| 8 |
| 9 |
| 10 |
| 11 |
| 14 |
+----+
Steps:
Create relationship between Customer id's in Customer table and Order table (if not already there)
Create a Date table
Convert Weeks to dates in a new calculated column in the Order table
Create relationship between Customer id's in Customer table and Order table
Create relationship between Dates in Date table and Order table
Create calculated column in Date Table with Day ranges ("30 days or less" etc)
Create measure to identify if an order was placed
Add slicer with date range from Date table and table visual with Customer id.
Add measure to table visual on filter pane and set to "No"
Some of these steps have additional detail below.
2. Create a Date table
We can do this is PowerQuery or in DAX. Here's the DAX version:
Calendar =
VAR
Days = CALENDAR ( DATE ( 2020, 1, 1 ), DATE ( 2020, 12, 31 ) )
RETURN
ADDCOLUMNS (
Days,
"Year Week", YEAR ( [Date] ) & WEEKNUM([Date])
)
Now mark this table as a date table in the "Table Tools" ribbon with the button "Mark as date table"
3. Convert Weeks to dates
For this to work, I have had to create a calculated column in the Order table with the first day of the year first. This can probably be improved upon.
StartYear = DATE(Left(Orders[Year week], 4), 01, 01)
Next the calculated column that we need in the Order table, that identifies the first day of the week. The Variable "DayNoInYear" takes the week number times 7 and substracting 7 to arrive at the first day of the week, returning the nth day of the year. This is then converted to a date with the variable "DateWeek":
Date =
VAR DayNoInYear = RIGHT(Orders[Year week], 2) * 7 - 7
VAR DateWeek = DATEADD(Orders[StartYear].[Date], DayNoInYear, DAY)
RETURN
DateWeek
6. Create calculated column in Date Table with Day ranges
Day ranges =
VAR Today = TODAY()
VAR CheckDate = 'Calendar'[Date] RETURN
SWITCH(TRUE(),
CheckDate - Today <= -90, "90+ days",
CheckDate - Today <= -60 && CheckDate - Today > -90 , "90 days or less",
CheckDate - Today <= -30 && CheckDate - Today > -60 , "60 days or less",
CheckDate - Today <= 0 && CheckDate - Today > -30 , "30 days or less",
"In the future"
)
7. Create measure to identify if an order was placed
Yes - No order =
VAR Yes_No =
IF(
ISBLANK(FIRSTNONBLANK(Orders[Customer id], Orders[Customer id])),
"No",
"Yes"
)
VAR ThirtyDays = SELECTEDVALUE('Calendar'[Day ranges]) = "30 days or less"
VAR SixtyDays = SELECTEDVALUE('Calendar'[Day ranges]) = "30 days or less" || SELECTEDVALUE('Calendar'[Day ranges]) = "60 days or less"
VAR NinetyDays = SELECTEDVALUE('Calendar'[Day ranges]) = "30 days or less" || SELECTEDVALUE('Calendar'[Day ranges]) = "60 days or less" || SELECTEDVALUE('Calendar'[Day ranges]) = "90 days or less"
RETURN
SWITCH(TRUE(),
AND(ThirtyDays = TRUE(), Yes_No = "No"), "No",
AND(SixtyDays = TRUE(), Yes_No = "No"), "No",
AND(NinetyDays = TRUE(), Yes_No = "No"), "No",
Yes_No = "No",
"Yes"
)
Steps 8 and 9
Create slicer with the newly created "Day range" column in the Date table and create a table visual with the "Yes - No order" measure as visual-level filter set at "No" as in screenshot attached below

How to sum up a measure based on different levels in Power BI using DAX

I have the following table structure:
| Name 1 | Name 2 | Month | Count 1 | Count 2 | SumCount |
|--------|--------|--------|---------|---------|----------|
| A | E | 1 | 5 | 3 | 8 |
| A | E | 2 | 1 | 6 | 7 |
| A | F | 3 | 3 | 4 | 7 |
Now I calculate the following with a DAX measure.
Measure = (sum(Table[Count 2] - sum(Table[Count 1])) * sum(Table[SumCount])
I can't use a column because then the formula is applied before excluding a layer (eg. month). Added to my table structure and excluded month it would look like that:
| Name 1 | Name 2 | Count 1 | Count 2 | SumCount | Measure |
|--------|--------|---------|---------|----------|---------|
| A | E | 6 | 9 | 15 | 45 |
| A | F | 3 | 4 | 7 | 7 |
I added a table to the view which only displays Name 1in which case the measure of course will sum up Count 1, Count 2 and SumCount and applies the measure which leads to the following result:
| Name 1 | Measure |
|--------|---------|
| A | 88 |
But the desired result should be
| Name 1 | Measure |
|--------|---------|
| A | 52 |
which is the sum of Measure.
So basically I want to have the calculation on my base level Measure = (sum(Table[Count 1] - sum(Table[Count 2])) * sum(Table[SumCount]) but when drilling up and grouping those names it should only perform a sum.
An iterator function like SUMX is what you want here since you are trying to sum row by row rather than aggregating first.
Measure = SUMX ( Table, ( Table[Count 2] - Table[Count 1] ) * Table[SumCount] )
Any filters you have will be applied to the first argument, Table, and it will only sum the corresponding rows.
Edit:
If I'm understanding correctly, you want to aggregate over Month before taking the difference and product. One way to do this is by summarizing (excluding Month) before using SUMX like this:
Measure =
VAR Summary =
SUMMARIZE (
Table,
Table[Name 1],
Table[Name 2],
"Count1Sum", SUM ( Table[Count 1] ),
"Count2Sum", SUM ( Table[Count 2] ),
"SumCountSum", SUM ( Table[SumCount] )
)
RETURN
SUMX ( Summary, ( [Count2Sum] - [Count1Sum] ) * [SumCountSum] )
You don't want measure in this case, rather you need new column,
Same formula but new column will give your desired result.
Column = ('Table (2)'[Count1]-'Table (2)'[Count2])*'Table (2)'[SumCount]

PowerBI - Comparing two similar sets of data (Many to Many)

I am trying to compare to sets of data that are very similar. I have done a bridge relation and used M:M relationship on PowerBI but I am still not getting the result I want.
Here is an example of the data:
Dataset 1
Name | Service | Usage
A | 1 | 10
A | 2 | 20
B | 1 | 10
B | 2 | 10
C | 1 | 20
C | 2 | 10
Dataset 2
Name | Service | Usage
A | 1 | 40
A | 2 | 20
B | 1 | 40
B | 2 | 10
C | 1 | 40
C | 2 | 10
Desired output
Name | Service | Usage 1 | Usage 2
A | 1 | 10 | 40
A | 2 | 20 | 20
B | 1 | 10 | 40
B | 2 | 10 | 10
C | 1 | 20 | 40
C | 2 | 10 | 10
Is this possible in PowerBI?
One approach (as suggested in comments), is to separate the distinct Name and Service values into separate dimension tables, in the query editor:
Names:
= Table.FromList(List.Distinct(List.Combine({#"Dataset 1"[Name], #"Dataset 2"[Name]})),Splitter.SplitByNothing(),{"Name"})
Services:
= Table.FromList(List.Distinct(List.Combine({#"Dataset 1"[Service], #"Dataset 2"[Service]})),Splitter.SplitByNothing(),{"Service"})
Create the DAX measures you want:
Usage 1 = SUM ( 'Dataset 1'[Usage] )
Usage 2 = SUM ( 'Dataset 2'[Usage] )
Now create relationships between the fact tables (Dataset 1, Dataset 2) and the dimension tables (Names, Services):
Then simply layout the visual as required:
Another approach may be to combine your dataset fact tables into one table, with an added "dataset" column:
Create your "combined" table in the query editor.
Combined Table:
= Table.Combine({Table.AddColumn(#"Dataset 1", "Dataset", each "Dataset 1", type text), Table.AddColumn(#"Dataset 2", "Dataset", each "Dataset 2", type text)})
Now use this table as your single source - either with a crosstab visual:
Or by adding separate measure for each dataset:
Usage 1 = CALCULATE ( SUM('Combined Data'[Usage]), 'Combined Data'[Dataset] = "Dataset 1" )
Usage 2 = CALCULATE ( SUM('Combined Data'[Usage]), 'Combined Data'[Dataset] = "Dataset 2" )

How to create a table (or list) with the order codes of orders with both products

I have a Transactions table with the following structure:
ID | Product | OrderCode | Value
1 | 8 | ABC | 100
2 | 5 | ABC | 150
3 | 4 | ABC | 80
4 | 5 | XPT | 100
5 | 6 | XPT | 100
6 | 8 | XPT | 100
7 | 5 | XYZ | 100
8 | 8 | UYI | 90
How do I create a table (or list) with the order codes of orders with both products 5 and 8?
In the example above it should be the orders ABC and XPT.
There are probably many ways to do this, but here's a fairly general solution what I came up with:
FilteredList =
VAR ProductList = {5, 8}
VAR SummaryTable = SUMMARIZE(Transactions,
Transactions[OrderCode],
"Test",
COUNTROWS(INTERSECT(ProductList, VALUES(Transactions[Product])))
= COUNTROWS(ProductList))
RETURN SELECTCOLUMNS(FILTER(SummaryTable, [Test]), "OrderCode", Transactions[OrderCode])
The key here is if the set of products for a particular order code contains both 5 and 8, then the intersection of VALUES(Transations[Product]) with the set {5,8} is exactly that set and has a count of 2. If it doesn't have both, the count will be 1 or 0 and the test fails.
Please elaborate more on your question, From your above post I understood is you want to filter the list, For that, you can use below code
List<Transactions> listTransactions = listTransactions.FindAll(x=>x.Product == 5 || x.Product == 8)

Compute year on year growth of GDP for each quarter

I want to compute in Stata year on year growth rate of the GDP for each quarter. Basically, I want to compute : (gdp_q1y1980-gdp_q1y1979)/gdp_q1y1979.
// create some silly example data
clear
set obs 10
gen time = _n
format time %tq
gen gdp = _n^2*100
// do the computation
tsset time
gen growth = S4.gdp / L4.gdp
// admire the result
list
For more information see here.
I prefer #Maarten Buis' solution but know also that you could use subscripting:
sort time
gen growth = (gdp / gdp[_n-4]) - 1
Run help subscripting for details (or http://www.stata.com/help.cgi?subscripting).
Note that extra care must be taken if, for example, there is a gap in your time series:
. clear all
. set more off
.
. // create some silly example data
. set obs 15
obs was 0, now 15
. gen time = _n
. format time %tq
. gen gdp = _n^2*100
.
. // create a gap deleting 1962q4
. drop in 11
(1 observation deleted)
.
. // using -tsset-
. tsset time
time variable: time, 1960q2 to 1963q4, but with a gap
delta: 1 quarter
. gen growth = S4.gdp / L4.gdp
(5 missing values generated)
.
. // subscripting
. sort time
. gen growth2 = (gdp / gdp[_n-4]) - 1
(4 missing values generated)
.
. list, separator(0)
+--------------------------------------+
| time gdp growth growth2 |
|--------------------------------------|
1. | 1960q2 100 . . |
2. | 1960q3 400 . . |
3. | 1960q4 900 . . |
4. | 1961q1 1600 . . |
5. | 1961q2 2500 24 24 |
6. | 1961q3 3600 8 8 |
7. | 1961q4 4900 4.444445 4.444445 |
8. | 1962q1 6400 3 3 |
9. | 1962q2 8100 2.24 2.24 |
10. | 1962q3 10000 1.777778 1.777778 |
11. | 1963q1 14400 1.25 1.938776 |
12. | 1963q2 16900 1.08642 1.640625 |
13. | 1963q3 19600 .96 1.419753 |
14. | 1963q4 22500 . 1.25 |
+--------------------------------------+
The results for the solution with subscripts (variable growth2) are messed up once the gap begins (1963q1). A good reason, I think, to prefer tsset.