I am trying to create a rolling 4 week average conversion rate. The column LTA is the conversion rate and equals (Appts/Leads). Right now, LTA is week by week. I need to create a new column that is a 4 rolling conversion rate.
Here is the data.
Week Leads Appts LTA
4/17/2022 205 83 40.49%
4/24/2022 126 68 53.97%
5/1/2022 117 40 34.19%
5/8/2022 82 38 46.34%
5/15/2022 60 32 53.33%
5/22/2022 45 19 42.22%
5/29/2022 25 19 76.00%
So if we started at the bottom, the RollingAvg for May 29 would be (19+19+32+38)/(25+45+60+82) = 50.943 %
For the week may 22, the numbers would roll back one week, so it'd be (19+32+38+0)/(45+60+82+117) = 29.276 %
Help would be appreciated.
transform(data.frame(lapply(df, zoo::rollsum, k=4)), roll = Appts/Leeds * 100)
Leeds Appts roll
1 530 38 7.169811
2 385 70 18.181818
3 304 89 29.276316
4 212 108 50.943396
Simple solution for a calculated column in DAX:
RollingAvg =
VAR _currentDate = [Week]
VAR _minDate = _currentDate - 4*7
RETURN
CALCULATE (
DIVIDE (
SUM ( 'Table'[Appts] ) ,
SUM ( 'Table'[Leads] )
),
// Lift filters on table to have all rows visible
ALL ( 'Table' ) ,
// Add constraints to dates for a 4-week average
'Table'[Week] <= _currentDate ,
'Table'[Week] > _minDate
)
Or better yet, a measure that doesn't take up space in the data model:
RollingAvgMeasure =
/*
Calculates the 4-week rolling average if used with the Week dimension.
Else calculates the total rolling average.
*/
VAR _currentDate = MAX ( 'Table'[Week] )
VAR _minDate = _currentDate - 4*7
VAR _movingAvg =
CALCULATE (
DIVIDE (
SUM ( 'Table'[Appts] ) ,
SUM ( 'Table'[Leads] )
),
ALL ( 'Table' ) ,
'Table'[Week] <= _currentDate ,
'Table'[Week] > _minDate
)
VAR _total = DIVIDE ( SUM ( 'Table'[Appts] ) , SUM ( 'Table'[Leads] ) )
RETURN
// Replace if-statement with only return of _movingAvg to display the latest 4-week value.
IF (
ISFILTERED ( 'Table'[Week] ),
_movingAvg ,
_total
)
Related
I'm strugging to build two calculated columns (named balance and avg). My original SQLite base is:
name seq side price qnt
groupA 1 B 30 100
groupA 2 B 36 200
groupA 3 S 23 300
groupA 4 B 30 100
groupA 5 B 54 400
groupB 1 B 70 300
groupB 2 B 84 300
groupB 3 B 74 600
groupB 4 S 90 100
Rational for the 2 calculated new columns:
balance: the first line of each group (seq = 1), must have the same value of qnt. The next records follow the below formula (Excel-based scheme):
if(side="B"; `previous balance record` + `qnt`; `previous balance record` - `qnt`)
avg: the first line of each group (seq = 1), must have the same value of price. The next records follow the below formula (Excel-based scheme):
if(side="B"; ((`price` \* `qnt`) + (`previous balance record` \* `previous avg record`)) / (`qnt` + `previous balance record`); `previous avg record`)
Example with numbers (the second row of groupA is calculated below):
--> balance: 100 + 200 = 300
--> avg: ((36 * 200) + (100 * 30)) / (200 + 100) = 34
I think this problem must be solved with CTE because I need the previous record, which is in being calculated every time.
I wouldn't like to aggregate groups - my goal is to display every record.
Finally, this is what I expect as the output:
name seq side price qnt balance avg
groupA 1 B 30 100 100 30
groupA 2 B 36 200 300 34
groupA 3 S 23 300 0 34
groupA 4 B 30 100 100 30
groupA 5 B 54 400 500 49,2
groupB 1 B 70 300 300 70
groupB 2 B 84 300 600 77
groupB 3 B 74 600 1200 75,5
groupB 4 S 90 100 1100 75,5
Thank you in advance!
Here is my dbfiddle test: https://dbfiddle.uk/TSarc3Nl
I tried to explain part of the coding (commented) to make things easier.
The balance can be derived from a cumulative sum (using a case expression for when to deduct instead of add).
Then the recursive part just needs a case expression of its own.
WITH
adjust_table AS
(
SELECT
*,
SUM(
CASE WHEN side='B'
THEN qnt
ELSE -qnt
END
)
OVER (
PARTITION BY name
ORDER BY seq
)
AS balance
FROM
mytable
),
recurse AS
(
SELECT adjust_table.*, price AS avg FROM adjust_table WHERE seq = 1
UNION ALL
SELECT
n.*,
CASE WHEN n.side='B'
THEN ((n.price * n.qnt * 1.0) + (s.balance * s.avg)) / (n.qnt + s.balance)
ELSE s.avg
END
AS avg
FROM
adjust_table n
INNER JOIN
recurse s
ON n.seq = s.seq + 1
AND n.name = s.name
)
SELECT
*
FROM
recurse
ORDER BY
name,
seq
https://dbfiddle.uk/mWz945pG
Though I'm not sure what the avg is meant to be doing, so it's possible I got that wrong and/or it could possibly be simplified to not need recursion.
NOTE: Never use , to join tables.
EDIT: Recursion-less version
Use window functions to accumulate the balance, and also the total spent.
Then, use that to a enable the use of another window function to accumulate how much 'spend' is being 'recouped' by sales.
Your avg is then the adjusted spend divided by the current balance.
WITH
accumulate AS
(
SELECT
*,
SUM(
CASE WHEN side='B' THEN qnt ELSE -qnt END
)
OVER (
PARTITION BY name
ORDER BY seq
)
AS balance,
1.0
*
SUM(
CASE WHEN side='B' THEN price * qnt END
)
OVER (
PARTITION BY name
ORDER BY seq
)
AS total_spend
FROM
mytable
)
SELECT
*,
(
total_spend
-
SUM(
CASE WHEN side='S'
THEN qnt * total_spend / (balance+qnt)
ELSE 0
END
)
OVER (
PARTITION BY name
ORDER BY seq
)
-- cumulative sum for amount 'recouped' by sales
)
/ NULLIF(balance, 0)
AS avg
FROM
accumulate
https://dbfiddle.uk/O0HEr556
Note: I still don't understand why you're calculating the avg price this way, but it matched your desired results/formulae, without recursion.
I have a PowerBI measure to calculate sick leave days between 2 dates.
CALCULATE(
COUNTROWS(PERIOD),
DatesBetween(
'Period'[Datekey],
min('Leave Management'[DateFrom]),
max('Leave Management'[DateTo])),
Filter(
Period,
'Period'[ISWorkDay] = 1 &&
'Period'[IsPublicHoliday] = FALSE
),
ALLSELECTED('Leave Management')
)```
a second measure(may not be essential) to categorize the leave days as
```DateDiff =
SWITCH(
True(),
'Staff Management'[workdays without holidays] <= 1 , 1,
'Staff Management'[workdays without holidays] = 2 , 2 ,
'Staff Management'[workdays without holidays] >= 3 , 3
)
table reference...[Table/Matrix][1]
I would like to count the number of Days per category, I have tried measures...
1DayLeave Distinct Count =
COUNTROWS(FILTER('Leave Management',[DateDiff]=1))
3DayCount =
COUNTX(Summarize(FILTER('Staff Management','Leave Management'[DateDiff] = 3
),"Leave 3 Day Catergory",'Leave Management'[DateDiff]),[DateDiff])
```[enter image description here][1]
[1]: https://i.stack.imgur.com/Wq9DV.png
Thanks for reading,
I have a DAX measure that finds the slope of some data over time. To do this it needs to find the SUMX of a date field from a date table.
When I try to display only this sum, it comes back with an invalid OADate value error message:
Couldn't load the data for this visual
Invalid OADate value '7126785'. Accepted values are between -657435.0 and 2958465.99999999
Here is the query I'm using:
measure =
VAR Known =
FILTER (
SELECTCOLUMNS (
ALLSELECTED ( 'Dates'[Date] ),
"Known[X]", 'Dates'[Date],
"Known[Y]", sum('table'[peoplecount])
),
AND (
NOT or(( ISBLANK ( Known[X] ) ),Known[X] = 0),
NOT or(( ISBLANK ( Known[Y] ) ),Known[Y] = 0)
)
)
VAR Count_Items =
COUNTROWS ( Known )
VAR Sum_X =
SUMX(Known,Known[X])
RETURN
Sum_X
My date table looks like this:
Date (Date)
3/22/2020
3/23/2020
etc
My table containing data to be counted looks like this :
(This comes from direct query)
Date (Datetime),peoplecount (int)
3/21/2020 12:00:00 AM, 3
3/22/2020 12:00:00 AM, 9
etc
So I created the following Measures:
First, I created this because I couldn't get VAR in the 2nd measure to pick it up from Table1.
Meetings in the Past 90 Days =
CALCULATE ( SUM ( table1[MEETING_HOSTED_PAST_90DAYS] ) )
Second, i created:
mtgs Category =
VAR mtgs = [Meetings in the Past 90 Days]
RETURN
SWITCH (
TRUE (),
mtgs = BLANK (), "0 Mtgs Past 90 Days",
mtgs >= 1 && mtgs <= 100, "1-100 Mtgs Past 90 Days",
mtgs >= 101 && mtgs <= 500, "101-500 Mtgs Past 90 Days",
"500+ Mtgs Past 90 Days"
)
but I also have another column called Account Manager. and when I filter on account manager it will not show me the just the filter but all the sites in the table below.
Data is as
index id
1 112
1 112
2 109
2 109
3 125
3 125
4 199
4 199
5 100
5 100
The id are not incremental but are sequential in nature take it as a GUID that's why i have assigned index for Range query
The user will give #startid #endid and i will get the rows for this range
Now first i get the index corresponding these id like
#indexes = SELECT DISTINCT index
FROM #table
WHERE id IN (#startid, endid);
as a result i get (let's say for example if #startid = 2 and #endid = 4)
2
4
Now i know the Range will be BETWEEN 2 and 4 i.e. i want rows corresponding 2,3 and 4
#result= SELECT index AS index,
id AS id
FROM #data
WHERE
index BETWEEN (THE TWO ENTRIES FROM #indexes)
would have done this using Nested SELECT but USQL doesn't support it.
now is there a way to treat #indexes as a list and specify range or something???
BETWEEN is supported in U-SQL, it's just case-sensitive, eg
DECLARE CONST #startId int = 2;
DECLARE CONST #endId int = 4;
#input = SELECT *
FROM (
VALUES
( 1, 112 ),
( 1, 112 ),
( 2, 109 ),
( 2, 109 ),
( 3, 125 ),
( 3, 125 ),
( 4, 199 ),
( 4, 199 ),
( 5, 100 ),
( 5, 100 )
) AS x ( index, id );
#output =
SELECT *
FROM #input
WHERE index BETWEEN #startId AND #endId;
OUTPUT #output TO "/output/output.csv"
USING Outputters.Csv(quoting:false);
My results:
Alternative approach:
DECLARE CONST #startId int = 109;
DECLARE CONST #endId int = 199;
#input = SELECT *
FROM (
VALUES
( 1, 112 ),
( 1, 112 ),
( 2, 109 ),
( 2, 109 ),
( 3, 125 ),
( 3, 125 ),
( 4, 199 ),
( 4, 199 ),
( 5, 100 ),
( 5, 100 )
) AS x ( index, id );
#output =
SELECT i. *
FROM #input AS i
CROSS JOIN
(
SELECT MIN(index) AS minIndex,
MAX(index) AS maxIndex
FROM #input AS i
WHERE id IN ( #startId, #endId )
) AS x
WHERE i.index BETWEEN x.minIndex AND x.maxIndex;
OUTPUT #output TO "/output/output.csv"
USING Outputters.Csv(quoting:false);