Percentile 25 as a DAX Measure on a combination weekDay & Hour - dax

I have a table in Power BI called Registration. The table has the following columns: timestamp, weekDay, Hour, condition, events. timestamp is the timestamp column, weekDay is the name of the day in the week like "Sunday", "Monday", "Tuesday", etc. Hour has the hour of the events and Hour goes from 0 to 23. The 'condition' column has the following values: "Success", "Failed", and "Partial". 'events' column is of data type integer and hold the number of events at that Hour. The condition I am interested in is "Failed". But here is the tricky part: I want to calculate percentile 25 of every group combination of weekDay and Hour. Example: percentile 25 for all 'Tuesday' at Hour 16, and same way for all combinations weekDay & Hour. In summary, I want to write a Measure that calculate the percentile 25 of every combination of all groups of weekDay and Hour. Here below something I thought was easy but is not working:
Percentile 25 =
VAR failedEvents =
FILTER(Registration, Registration[condition] = "Failed")
RETURN
CALCULATE(
PERCENTILEX.INC(failedEvents, Registration[Events], 0.25),
GROUPBY(failedEvents, Registration[weekDay], Registration[Hour])
)
Here below a sample in Kusto language.
let EventsData = datatable(Date:datetime, Event:long, weekDay: string, Hour: int ) [
datetime(2022-11-29 00:00:00.0000000), 6, "Tuesday", 16,
datetime(2022-12-06 00:00:00.0000000), 9, "Tuesday", 16,
datetime(2022-12-13 00:00:00.0000000), 12, "Tuesday", 16,
datetime(2022-12-20 00:00:00.0000000), 6, "Tuesday", 16,
datetime(2022-12-27 00:00:00.0000000), 11, "Tuesday", 16,
datetime(2023-01-03 00:00:00.0000000), 7, "Tuesday", 16
];
EventsData
| summarize percentiles(Event, 25, 50, 75)
| extend IQR = percentile_Event_75 - percentile_Event_25
| extend LT = percentile_Event_25 - 2 * IQR
| extend UT = percentile_Event_75 + 2 * IQR
The result should be:
percentile_Event_25 percentile_Event_50 percentile_Event_75 IQR LT UT
6 7 11 5 -4 21
Note: Added sample data and answer as per #DavidBacci request:
timestamp weekDay Hour condition events
2022-09-09T11:00:00Z Friday 7 Failed 8
2022-10-07T11:00:00Z Friday 7 Failed 15
2022-10-14T11:00:00Z Friday 7 Failed 16
2022-10-21T11:00:00Z Friday 7 Failed 16
2022-10-28T11:00:00Z Friday 7 Failed 16
2022-11-04T11:00:00Z Friday 7 Failed 17
2022-11-11T12:00:00Z Friday 7 Failed 25
2022-11-18T12:00:00Z Friday 7 Failed 11
2022-12-02T12:00:00Z Friday 7 Failed 9
2022-12-09T12:00:00Z Friday 7 Failed 9
result must be:
weekDay Hour percentile_25 percentile_75
Friday 7 9 16
I greatly appreciate your help. Thanks.

I think you just want PERCENTILE.EXC.
e.g.
Measure 1:
percentile_25 = PERCENTILE.EXC('Table'[events],0.25)
Measure 2:
percentile_75 = PERCENTILE.EXC('Table'[events],0.75)

I've resolved this using Kusto. No need DAX for it. Here is how I resolved it.
Registration_Events
| summarize reg_failed_summ = sum(reg_failed) by bin(timestamp, 1d), weekDay, Hour
| summarize pcntls = percentiles_array(reg_failed_summ, 50, 25, 75) by weekDay, Hour
| extend p50 = todouble(pcntls[0]), p25 = todouble(pcntls[1]), p75 = todouble(pcntls[2])
| extend IQR = p75 - p25
| extend LT = p25 - 2 * IQR, UT = p75 + 2 * IQR
| extend LT = iif(LT <= 0, real(null), LT)
| project weekDay, Hour, LT, p25, IQR, p75, UT

Related

DAX - How to sum a measure over the last N weeks, this year and last year

I have a measure [Total] reported by a custom week-ending date field 'Date'[CustWEndingDate] from Date table, based on Sat - Fri weeks (so each week ends on a Fri), plus an associated 'Date'[WeekNum] and 'Date'[Year] to that. Data looks like this:
[CustWEndingDate], [Year], [WeekNum], [Total]
3/29/2019, 2019, 13, 400
4/5/2019, 2019, 14, 350
4/12/2019, 2019, 15, 420
4/19/2019, 2019, 16, 390
...
3/27/2020, 2020, 13, 315
4/3/2020, 2020, 14, 325
4/10/2020, 2020, 15, 405
4/17/2020, 2020, 16, 375
My question is this: How do I create DAX measure to calculate last 3 weeks this year OVER same last 3 weeks last year? For example, week 14, 15 and 16 this year (325+405+375) vs same week 14, 15 and 16 last year (350+420+390)?
Thank you in advance for any help you can provide!
I would use the following approach:
Calculate scalar with today's date
Calculate scalar with year of today's date
Calculate scalar with WeekNum associated to today's date
Calculate scalar with CY value for last 3 weeks this year
Calculate scalar with PY value for (same) last 3 weeks previous year
Calculate ratio
Here is the technical implementation:
Joel's Measure:=
VAR _TODAY = TODAY()
VAR _YEAR = YEAR(_TODAY)
VAR _WEEKNUM = CALCULATE(MIN('Date'[WeekNum]), 'Date'[Date] = _TODAY)
VAR _CY = CALCULATE([MEASURE], 'Date'[Year] = _YEAR, 'Date'[WeekNum] IN {_WEEKNUM, _WEEKNUM - 1, _WEEKNUM - 2})
VAR _PY = CALCULATE([MEASURE], 'Date'[Year] = _YEAR - 1, 'Date'[WeekNum] IN {_WEEKNUM, _WEEKNUM - 1, _WEEKNUM - 2})
RETURN
DIVIDE(_CY - _PY, ABS(_PY))
This should solve your problem. If not, please share a feedback with the result.

Sum values by date range in multiple columns

I need to sum value by date range in multiple columns. Every date range is one week of a month. It can be shorter than 7 days if it is the start of the month or the end of the month.
For example, I have dates for February:
my_user my_date my_value
A 01.02.2019 100
A 02.02.2019 150
B 01.02.2019 90
Z 28.02.2019 120
How can I have in date range format such as below?
my_user 01/02-03/02 04/02-10/02 11/02-17/02 18/02-24/02 25/02-28/02
A 250 0 0 0 0
B 90 0 0 0 0
Z 0 0 0 0 120
Any suggestions? Thanks!
You can do this:
select *
from (
select to_char(dt, 'iw') - to_char(trunc(dt, 'month'), 'iw') + 1 wk, usr, val from t)
pivot (sum(val) for wk in (1, 2, 3, 4, 5, 6))
demo
USR 1 2 3 4 5 6
--- ---------- ---------- ---------- ---------- ---------- ----------
A 250
B 90
Z 120
Header numbers are the weeks of month. Maximum may be 6 if month starts at the end of the week and is longer than 28 days.
Similiar way you can find first and last day of each week if needed, but you can't put them as headers, or at least not easily.
Edit:
it is possible to define certain date range with pivot, simple as two
dates? For example, I need to sum values from 5 December 2018 to 4
January 2019, 5 January 2019 to 4 February 2019, 5 March 2019 to 4
April 2019
Yes. Everything depends on how we count first and next weeks. Here:
to_char(dt, 'iw') - to_char(trunc(dt, 'month'), 'iw') + 1
i am subtracting week in year for given date and week in year of first day in month for this date. You can simply replace this second value with your starting date, either by hardcoding it in your query or by sending parameter to query or finding minimum date at first in a subquery:
(to_char(dt, 'iw') - to_char(date '2019-03-05', 'iw')) + 1
or
(to_char(dt, 'iw') - to_char((select min(dt) from data), 'iw')) + 1
Edit 2:
There is one problem however. When user defined period contains two or more years. to_date(..., 'iw') works fine for one year, but for two we get values 51, 52, 01, 02... We have to deal with this somehow, for instance like here:
with t(dt1, dt2) as (select date '2018-12-16', date '2019-01-15' from dual)
select min(dt) mnd, max(dt) mxd, iw, row_number() over (order by min(dt)) rn
from (select dt1 + level - 1 dt, to_char(dt1 + level - 1, 'iw') iw
from t connect by level -1 <= dt2 - dt1)
group by iw
which gives us:
MND MXD IW RN
----------- ----------- -- ----------
2018-12-16 2018-12-16 50 1
2018-12-17 2018-12-23 51 2
2018-12-24 2018-12-30 52 3
2018-12-31 2019-01-06 01 4
2019-01-07 2019-01-13 02 5
2019-01-14 2019-01-15 03 6
In first line we have user defined date ranges. Then I did hierarchical query looping through all dates in range assigning week, then grouped by this week, found start and end dates for each week and assigned row number rn which can be further used by pivot.
You can now simply join your input data with this query, let's name it weeks:
from data join weeks on dt between mnd and mxd
and make pivot. But for longer periods you have to find how many weeks there can be and specify them in pivot clause in (1, 2, 3, 4...). You can also add aliases if you need:
pivot ... for rn in (1 week01, 2 week02... 12 week12)
There is no simply way to avoid listing them manually. If you need it please look for oracle dynamic pivot in SO, there where hundreds similiar questions already. ;-)

How to convert a positive duration into a negative in Google Spreadsheet

I have to deal with time and duration in Google Spreadsheet App and I have to calculate with negative duration.
Problem:
--------------------------------------------------------
Begin | End | Duration | calculated in negative (for some reasons)
--------------------------------------------------------
08:00 | 14:00 | 06:00 | no
10:00 | 15:00 | 05:00 | yes
If column 'Begin' and 'End' were formatted as "Time", the difference can be easily calculated in the duration column. However converting the duration value into a negative one with a simple solution like(end-begin)*(-1)seems not to be supported.
First solution:
With the following formula I achieved one goal:
[duration = end - begin]
(HOUR(duration)*60) + MINUTE(duration))(-1)
I had to convert the duration into minutes, multiply with -1 to convert the number into negative. But this leads to a strange behavior:
--------------------------------------------------------
Begin | End | Duration | calculated in negative (for some reasons)
--------------------------------------------------------
08:00 | 14:00 | 06:00 | no
10:00 | 15:00 | -7200:00:00 | yes
So I tried to divide it with 24, 60, 3600, but nothing seems to fit. Until I used the magic number 1440.
This number is a multiple of 60, exactly 24 times.
Final solution:
[duration = end - begin]
((HOUR(duration)*60) + MINUTE(duration))(-1))/1440
My questions are:
Does anyone know why to use the number 1440?
Is there another way to solve this problem?
Google Sheets treat dates and time like serial numbers (same as Excel does):
today() is 42 458;
tommorow = today() + 1 = 42 459;
each day counts one.
time is the number between 0 and 1. So we have 24 hours in 1, and 60 minutes in 1 hour. Therefore to get duration
in minutes: = 24 * 60 = 1440;
in seconds = 24 * 60 * 60 = 86 400;

DateTime subtraction in ruby 2?

I need to subtract two DateTime objects in order to find out the difference in hours between them.
I try to do the following:
a = DateTime.new(2015, 6, 20, 16)
b = DateTime.new(2015, 6, 21, 16)
puts a - b
I get (-1/1), the object of class Rational.
So, the question is, how do I find out what the difference betweent the two dates is? In hours or days, or whatever.
And what does this Rational mean/represent when I subtract DateTimes just like that?
BTW:
When I try to subtract DateTime's with the difference of 1 year, I get (366/1), so when I do (366/1).to_i, I get the number of days. But when I tried subtracting two DateTime's with the difference of 1 hour, it gave me -1, the number of hours. So, how do I also find out the meaning of the returned value (hours, days, years, seconds)?
When you substract two datetimes, you'll get the difference in days, not hours.
You get a Rational type for the precision (some float numbers cannot be expressed exactly with computers)
To get a number of hours, multiply the result by 24, for minutes multiply by 24*60 etc...
a = DateTime.new(2015, 6, 20, 16)
b = DateTime.new(2015, 6, 21, 16)
(a - b).to_i
# days
# => -1
((a - b)* 24).to_i
# hours
# => -24
# ...
Here's a link to the official doc
If you do subtraction on them as a Time object it will return the result in seconds and then you can multiply accordingly to get minutes/hours/days/whatever.
a = DateTime.new(2015, 6, 20, 16)
b = DateTime.new(2015, 6, 21, 16)
diff = b.to_time - a.to_time # 86400
hours = diff / 60 / 60 # 24

Algorithm to find nth working day [closed]

It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 10 years ago.
I have deviced a procedure to find nth working day without using loops.
Please bring around your suggesstions over this -
Algorithm to manipulate working days -
Problem: Find the date of nth working day from any particular day.
Solution:
Normalize to closest Monday -
If today(or the initial day) happens to be something other than monday, bring the day to the closest monday by simple addition or subtraction.
eg: Initial Day - 17, Oct. This happens to be wednesday. So normalize this no monday by going 2 dates down.
Now name this 2 dates, the initial normalization factor.
Add the number of working days + week ends that fall in these weeks.
eg: to add 10 working days, we need to add 12 days. Since 10 days has 1 week that includes only 1 saturday and 1 sunday.
this is because, we are normalizing to nearest monday.
Amortizing back -
Now from the end date add the initial normalization factor (for negative initial normalization) and another constant factor (say, k).
Or add 1 if the initial normalization is obtained from a Friday, which happens to be +3.
If start date falls on Saturday and sunday , treat as monday. so no amortization required at this step.
eg: Say if initial normalization is from wednesday, the intial normalization factor is -2. Hence add 2 to the end date and a constant k.
The constant k is either 2 or 0.
Constant definition -
If initial normalization factor is -3, then add 2 to the resulting date if the day before amortization is (wed,thu,fri)
If initial normalization factor is -2, then add 2 to the resulting date if the day before amortization is (thu,fri)
If initial normalization factor is -1, then add 2 to the resulting date if the day before amortization is (fri)
Example -
Find the 15th working day from Oct,17 (wednesday).
Step 1 -
initial normalization = -2
now start date is Oct,15 (monday).
Step 2 -
add 15 working days -
15 days => 2 weeks
weekends = 2 (2 sat, 2 sun)
so add 15 + 4 = 19 days to Oct, 15 monday.
end_date = 2, nov, Friday
Step 3a -
end_date = end_date + initial normalization = 4, nov sunday
Step 3b -
end_date = end_date + constant_factor = 4, nov, sunday + 2 = 6, nov (Tuesday)
Cross Verfication -
Add 15th working day to Oct, 17 wednesday
Oct,17 + 3 (Oct 17,18,19) + 5 (Oct 22-26) + 5 (Oct 29 - Nov 2) + 2 (Nov 5, Nov 6)
Now the answer is 6, Nov, Tuesday.
I have verified with a few cases. Please share your suggesstions.
Larsen.
To start with, its a nice algorithm, i have doubts about boundary conditions though: for example, what if i need to find the 0th working day from today's date:
Step 1 -
initial normalization = -2 now start date is Oct,15 (monday).
Step 2 -
add 0 working days -
0 days => 0 weeks
weekends = 0
so add 0 + 0 = 0 days to Oct, 15 monday.
end_date = 15, oct, monday
Step 3a -
end_date = end_date + initial normalization = 17, oct wednesday
Step 3b -
end_date = end_date + constant_factor = 17, Oct wednesday or 19,oct friday based on whether constant factor is 0 or 2 as it be only one of these values.
Now lets repeat the steps for finding the 1st working day from today:
Step 1 -
initial normalization = -2 now start date is Oct,15 (monday).
Step 2 -
add 1 working days -
1 days => 0 weeks
weekends = 0
so add 1 + 0 = 1 days to Oct, 15 monday.
end_date = 15, oct, monday
Step 3a -
end_date = end_date + initial normalization = 17, oct wednesday
Step 3b -
end_date = end_date + constant_factor = 17, Oct wednesday or 19,oct friday based on whether constant factor is 0 or 2 as it be only one of these values.
Did you notice, algorithm gives the same end result for 0 and 1. May be thats not an issue if t defined beforehand that 0 working days and 1 working days are considered as same scenario, but ideally they should be giving different results.
I would also suggest you to consider the negative test cases, like what if i need to find -6th working day from today, will your alforithm give me a date in past rightfully?
Lets consider 0th working day from today (17/10, wed).
Step 1 -
start_date = 17/10 wed
normalized date = 15/10 mon
Step 2 -
end_date = normalized date + working days
= 15/10 mon + 0 = 15/10 mon
Step 3 -
amortized_back = end_date_before_amortization + normalization factor
= 15/10 + (+2) = 17/10 wed
since the end_date_before_amortization falls on monday and initial normalization is 2, constant factor = 0.
hence, end_date = 17/10 wed.
now case 2, 1st working day from today.
Step 1 -
start_date = 17/10 wed
normalized date = 15/10 mon
Step 2 -
end_date = normalized date + working days
= 15/10 mon + 1 = 16/10 tue
Step 3 -
amortized_back = end_date_before_amortization + normalization factor
= 16/10 + (+2) = 18/10 thu.
since the end_date_before_amortization falls on tuesday and initial normalization is 2, constant factor = 0.
hence, end_date = 18/10 thu.
Looks to be working for 0th and 1st WD.

Resources