I am looking for a solution to sum previous rows in a calculated column.
Sample data:
product | sales | cumulative
000001 | 2000 | 2000
000001 | 2000 | 4000
000002 | 1500 | 1500
000001 | 2000 | 6000
000002 | 1500 | 3000
Could anyone help me with the DAX please.
This is what I would do
step 1)
I would create a new ordinal column with a sequential number for each product group
index =
RANKX (
FILTER (
Sales,
EARLIER ( Sales[Product] ) = Sales[Product]
),
Sales[Sales],
,
ASC
)
Step 2)
I create the Running Totals column that shows the running totals by group
Running Totals =
CALCULATE (
SUM (Sales[Sales]),
FILTER(
Sales,
Sales[Product] = EARLIER ( Sales[Product])
&& Sales[index] <= EARLIER ( Sales[index])
)
)
this is the result
Related
Suppose we have a table
weeknum | revenue
------------------
12 | 10000
12 | 10000
12 | 10000
13 | 10000
13 | 10000
13 | 10000
14 | 10000
14 | 10000
I tried to calculate the sum of the revenue for the previous weeknum:
Previous Revenue =
CALCULATE(
SUM(Table1[revenue]),
FILTER(
ALL(Table1[weeknum]),
Table1[weeknum] = Table1[weeknum]-1
)
)
But, it is failed. Any Idea on this one
The statement you are passing to FILTER's FilterExpression needs to refer to the entry from Table1[weeknum] within the current row context.
This can be achieved by replacing
Table1[weeknum] = Table1[weeknum]-1
with
Table1[weeknum] = MIN(Table1[weeknum])-1
though it is perhaps better practice to create a variable, viz:
Previous Revenue :=
VAR ThisWeekNum =
MIN( Table1[weeknum] )
RETURN
CALCULATE(
SUM( Table1[revenue] ),
FILTER(
ALL( Table1[weeknum] ),
Table1[weeknum] = ThisWeekNum - 1
)
)
I have this table:
Order | Total | FirstPayment | Months
1 | 1000 | 2021-01-01 | 2
2 | 600 | 2021-02-01 | 3
And I need to create a another table with the installments, like this:
Month | Order | Value
2021-01-01 | 1 | 500
2021-02-01 | 1 | 500
2021-02-01 | 2 | 200
2021-03-01 | 2 | 200
2021-04-01 | 2 | 200
So, I want to create a child table with one row for each month of payment.
Please, can you help?
As per my comment, I would actually do it like this:
Create dates table that spans all dates between two ranges. You could actually filter it to contain only relevant dates for better performance, but I didn't bother (this is a table formula):
Payments = CALENDAR(MIN(Orders[FirstPayment]), MAXX(Orders, EDATE(Orders[FirstPayment], Orders[Months])))
Create a measure that would show appropriate values for relevant dates:
Payment amount =
SUMX (
Payments,
VAR d =
DAY ( Payments[Date] )
RETURN
SUMX (
FILTER (
Orders,
DAY ( Orders[FirstPayment] )
== d
&& Payments[Date] <= EDATE ( Orders[FirstPayment], Orders[Months] -1 )
&& Payments[Date] >= Orders[FirstPayment]
),
[Total] / [Months]
)
)
The result - based on Order from Orders table and Date from Payments table:
EDIT
Of course, it is also possible to do what you asked. You have to combine the two formulas to create a calculated table like this (below is a table formula that you apply when you select New table):
Installments =
SELECTCOLUMNS (
FILTER (
CROSSJOIN (
CALENDAR (
MIN ( Orders[FirstPayment] ),
MAXX ( Orders, EDATE ( Orders[FirstPayment], Orders[Months] ) )
),
Orders
),
[Date] >= [FirstPayment]
&& DAY ( [Date] ) = DAY ( [FirstPayment] )
&& [Date]
<= EDATE ( [FirstPayment], [Months] - 1 )
),
"Date", [Date],
"Order", [Order],
"Value", [Total] / [Months]
)
I'm trying to calculate a rolling average for each row of a table based on values present in this table based on a sliding time window looking ahead and back a certain amount of days.
Given the following table:
myTable
+------------+-------+
| Date | Value |
+------------+-------+
| 31/05/2020 | 5 |
+------------+-------+
| 31/05/2020 | 10 |
+------------+-------+
| 01/06/2020 | 50 |
+------------+-------+
| 01/08/2020 | 50 |
+------------+-------+
and the measure
myMeasure =
VAR LookAheadAndBehindInDays = 28
RETURN
AVERAGEX (
DATESINPERIOD (
myTable[Date],
DATEADD ( LASTDATE ( myTable[Date] ), LookAheadAndBehindInDays, DAY ),
-2 * LookAheadAndBehindInDays,
DAY
),
myTable[Value]
)
I checked that the DATESINPERIOD returns effectively the right dates. My problem lies in the calculation of the average.
Instead of calculating the average of all values directly (expected result)
+------------+-------+---------------------------+
| Date | Value | myMeasure |
+------------+-------+---------------------------+
| 31/05/2020 | 5 | (5 + 10 + 50) / 3 = 21.66 |
+------------+-------+---------------------------+
| 31/05/2020 | 10 | (5 + 10 + 50) / 3 = 21.66 |
+------------+-------+---------------------------+
| 01/06/2020 | 50 | (5 + 10 + 50) / 3 = 21.66 |
+------------+-------+---------------------------+
| 01/08/2020 | 27 | 27 / 1 = 27 |
+------------+-------+---------------------------+
It first calculates the average of each date, and then the average of those values:
+------------+-------+--------------------+------------------------+
| Date | Value | Avg. by Date | myMeasure |
+------------+-------+--------------------+------------------------+
| 31/05/2020 | 5 | (5 + 10) / 2 = 7.5 | (7.5 + 50) / 3 = 28.75 |
+------------+-------+--------------------+------------------------+
| 31/05/2020 | 10 | (5 + 10) / 2 = 7.5 | (7.5 + 50) / 3 = 28.75 |
+------------+-------+--------------------+------------------------+
| 01/06/2020 | 50 | 50 / 1 = 50 | (7.5 + 50) / 3 = 28.75 |
+------------+-------+--------------------+------------------------+
| 01/08/2020 | 27 | 27 / 1 = 27 | 27 / 1 = 27 |
+------------+-------+--------------------+------------------------+
I found out about this behavior by using this measure:
myMeasure DEBUG =
VAR LookAheadAndBehindInDays = 28
VAR vTable =
DATESINPERIOD (
myTable[Date],
DATEADD ( LASTDATE ( myTable[Date] ), LookAheadAndBehindInDays , DAY ),
-2 * LookAheadAndBehindInDays,
DAY
)
RETURN
FIRSTDATE ( vTable ) & " - " & LASTDATE ( vTable ) & UNICHAR(10)
& " - Row Count: " & COUNTROWS ( vTable ) & UNICHAR(10)
& " - Avg: " & AVERAGEX(vTable, myTable[Value]) & UNICHAR(10)
& " - Dates: " & CONCATENATEX ( vTable, myTable[Date], "," ) & UNICHAR(10)
& " - Values: " & CONCATENATEX ( vTable, myTable[Value], "," )
This returns for rows with the date '31/05/2020' and '31/05/2020' the following value:
31/05/2020 - 01/06/2020
Row Count: 2
Avg: 28.75
Dates: 31/05/2020,01/06/2020
Values: 7.5,50
Most notable are the Row Count 2, which I would expect to be 3 and the values 5,10 and 50 (as reflected above in the tables)
So my question is, how can in calculate the rolling average over time by weighting each value equally, instead of weighting each day equally.
I'm not sure I completely understood the problem, but to me you just need a standard AVERAGE and not the AVERAGEX iterator.
I've changed the formula a bit and didn't use DATESINPERIOD, this one achieves the same result and (to me) is more clear and readable
Avg =
VAR DaysInterval = 28
RETURN
CALCULATE (
AVERAGE ( myTable[Value] ),
DATESBETWEEN (
myTable[Date],
MAX ( myTable[Date] ) - DaysInterval, --from
MAX ( myTable[Date] ) + DaysInterval --to
)
)
here is the result (based on the sample dataset)
What you are looking for is the calculated average from the days -/+28:
myMeasure =
VAR LookAheadAndBehindInDays = 28
var curDAte = rolling[ Date]
return CALCULATE(AVERAGE(rolling[Value]),
FILTER(rolling,
rolling[ Date] +LookAheadAndBehindInDays >= curDAte &&
rolling[ Date] -LookAheadAndBehindInDays <= curDAte))
as you can see I am using the filter to get the rows falling in the date range and calculate the average over those.
Objective:
Get the previous value based on a criteria.
Situation:
I have a table with groups numbered 1,2. I would like to look at the previous value (referring to the previous date) but for each group.
Desired Output:
My output should look like this
+------------+-------+-------+----------------+
| date | group | value | previous value |
+------------+-------+-------+----------------+
| 2019-02-02 | 2 | 50 | 45 |
| 2019-02-02 | 1 | 60 | 80 |
| 2019-01-18 | 2 | 45 | |
| 2019-01-18 | 1 | 80 | |
+------------+-------+-------+----------------+
What I tried:
previous value =
LOOKUPVALUE(
Table[value],
Table[date],
CALCULATE(
MAX(Table[date]),
FILTER(
Table,
Table[group]=EARLIER(Table[group]) && Table[date]<EARLIER(Table[date])
)
)
)
As I understand, you want this as a calculated column, not a measure. Try:
Previous Value =
VAR Current_Date = Table[date]
VAR Previous_Date =
CALCULATE (
MAX ( Table[date] ),
Table[date] < Current_Date,
ALLEXCEPT ( Table, Table[group] )
)
RETURN
CALCULATE (
MAX ( Table[value] ),
Table[Date] = Previous_Date,
ALLEXCEPT ( Table, Table[group] )
)
How it works:
We iterate each record of the Table and store its date in "Current_Date" variable.
For each record, find previous date, which is the max date that is smaller than the date of the record we are iterating. To do that, we need to have access to all dates, not only the date of the current record, so we need to use ALL function. However, since we need to do it by group, we use ALLEXCEPT, which preserves filter for the current group.
Once previous date is found, you can use exactly the same pattern to find previous value - find MAX value where record's date equals previous date, while preserving group filter.
I have a table with inventory movements. Each inventory item has a unique ID and they change status overtime (let's say status A, B, C and D, but not always in this order). Each status change of an ID is a new record in the table with the timestamp of the status change. My goal is to calculate with Power BI DAX the number of inventory at a certain day in status 'B'. The logic is to count the number of distinct IDs, which breached status 'B' before the certain day but doesn't have any newer status before that day.
Example of the source table:
ID | TimeStamp | Status
1 | 8/20/2018 | A
1 | 8/21/2018 | B
1 | 8/24/2018 | C
2 | 8/19/2018 | A
2 | 8/20/2018 | B
2 | 8/22/2018 | C
2 | 8/24/2018 | D
3 | 8/18/2018 | A
3 | 8/21/2018 | B
4 | 8/15/2018 | A
4 | 8/17/2018 | B
4 | 8/24/2018 | D
Example of the output table:
Date | Count of Items in Status B on this Day
8/17/2018 | 3
8/18/2018 | 2
8/19/2018 | 0
8/20/2018 | 8
8/21/2018 | 10
8/22/2018 | 5
8/23/2018 | 3
I was thinking of creating a table for the latest timestamp with status 'B' for each ID and then look for the next timestamp, after the timestamp of status 'B', if applicable:
ID (primary key) | TimeStamp of 'B' breached | TimeStamp of next status breach
1 | 8/20/2018 | 8/21/2018
2 | 8/18/2018 | 8/22/2018
3 | 8/21/2018 |
4 | 8/15/2018 | 8/20/2018
Then I would plug the above data into the Date context and count the number of IDs from the above table, where the "TimeStamp of 'B' breached" value is smaller AND the "TimeStamp of next status breach" value is greater than the certain date.
Unfortunately I am not sure how to plug this logic into DAX syntax, hence any recommendations would be appreciated.
Thanks a lot!
Gergő
This is a bit tricky, but we can do it with the use of a temporary calculated summary table within a measure:
CountStatusB =
SUMX(
ADDCOLUMNS(
SUMMARIZE(
FILTER(
ALL(Inventory),
Inventory[TimeStamp] <= MAX(Inventory[TimeStamp])
),
Inventory[ID],
"LastTimeStamp",
MAX(Inventory[TimeStamp])
),
"Status",
LOOKUPVALUE(Inventory[Status],
Inventory[ID], Inventory[ID],
Inventory[TimeStamp], [LastTimeStamp])
),
IF([Status] = "B",
1,
0
)
)
First, we create a summary table which calculates the last TimeStamp for each ID value. To do this, we use the SUMMARIZE function on a filtered table where we only consider dates from the current day or earlier, group by ID, and calculated the max TimeStamp.
Once we have the maximum TimeStamp per ID for the current day, we can look up what the Status is on that day and add that as a column to the summary table.
Once we know the most recent Status for each ID for the current day, we just need to sum up the ones where that Status is "B" and ignore the other ones.
It may be easier to read the measure if we break it up into steps. Here's the same logic as before, but using variables for more clarity.
CountB =
VAR CurrDay = MAX(Inventory[TimeStamp])
VAR Summary = SUMMARIZE(
FILTER(
ALL(Inventory),
Inventory[TimeStamp] <= CurrDay
),
Inventory[ID],
"LastTimeStamp",
MAX(Inventory[TimeStamp])
)
VAR LookupStatus = ADDCOLUMNS(
Summary,
"Status",
LOOKUPVALUE(Inventory[Status],
Inventory[ID], Inventory[ID],
Inventory[TimeStamp], [LastTimeStamp]
)
)
RETURN SUMX(LookupStatus, IF([Status] = "B", 1, 0))