dax subtracting value from another column - dax

I need your help on this below scenarios, We have data in column Date ,A and B.
I need the output like other column "Result"
steps :
A- B = Result
then take value from Result - next row value of B
until the result value get zero.
once you get zero then need to divide the previous value of result and corresponding vale of B . which i am highlight as red color.
this should be in measure. not by calculated column.

I will say that your desired result is a little confusing. You first say that you want output like the "Result" column in your screenshot. But then you later say that you want the division of the red numbers. Luckily, to do the division, the "Result" column needs to calculated (at least in a way), so here are both pieces.
First is the "Result" column (which is actually a measure).
The key to that is boiling down the problem to the simplest math terms. The number in the results column for any given row is the value from column A for the max date, minus the sum of column B for that date through the date of the given row (until we hit or go below 0).
First we will need to know what the date is for the given row
VAR SelectedDate = SELECTEDVALUE(Data[Date])
Then we will need to know the max date.
VAR MaxDate = MAXX(ALLSELECTED(Data), [Date])
Now that we know the max date, we can get the value from column A for that date.
VAR MaxDateA = SUMX(FILTER(ALL(Data), [Date] = MaxDate), [A])
With those three pieces, we can create our running total. Take the value of column A for the max date and subtract the sum of column B for all dates between the max date and the selected date (inclusive).
MaxDateA - SUMX(FILTER(ALL(Data), [Date] >= SelectedDate && [Date] <= MaxDate), [B])
Now we just need to handle the case for when the result hits, or goes below, 0. We can write a simple if statement that looks very similar to the running total above, with one simple change; exclude the selected date. It is basically checking if the prior date's running total is still greater than 0, calculate the running total for the given date.
IF(
MaxDateA - SUMX(FILTER(ALL(Data), [Date] > SelectedDate && [Date] <= MaxDate), [B]) > 0,
MaxDateA - SUMX(FILTER(ALL(Data), [Date] >= SelectedDate && [Date] <= MaxDate), [B])
BLANK()
)
Here is the formula in one piece.
Result =
VAR SelectedDate = SELECTEDVALUE(Data[Date])
VAR MaxDate = MAXX(ALLSELECTED(Data), [Date])
VAR MaxDateA = SUMX(FILTER(ALL(Data), [Date] = MaxDate), [A])
RETURN
IF(
MaxDateA - SUMX(FILTER(ALL(Data), [Date] > SelectedDate && [Date] <= MaxDate), [B]) > 0,
MaxDateA - SUMX(FILTER(ALL(Data), [Date] >= SelectedDate && [Date] <= MaxDate), [B]),
BLANK()
)
Next is the division measure.
We'll use some of the same logic as the first part. In this, "Selected" is for the given row context, so it will replace the function of the "Max" in the above.
VAR SelectedDate = SELECTEDVALUE(Data[Date])
VAR SelectedA = SUM(Data[A])
Once we have the selected date, we can generate a temporary table that will hold our running total. Basically, we are going to take a distinct list of dates from the data table and add a column (called "Result") that is defined very similarly to the formula for the first part.
VAR OlderDatesList = SUMMARIZE(FILTER(ALL(Data), [Date] <= SelectedDate), [Date], "Result", SelectedA - SUMX(FILTER(ALL(Data), [Date] <= SelectedDate && [Date] >= EARLIER([Date])), [B]))
Having this list lets us find the threshold when the running total hits (or goes below) 0. From that point, we want to know the date when the threshold was met, the running total right before that date, and the value of column B for that date.
VAR LastSelectedDate = MAXX(FILTER(OlderDatesList, [Result] <= 0), [Date])
VAR PriorSelectedResult = SUMX(FILTER(OlderDatesList, [Date] = MINX(FILTER(OlderDatesList, [Date] > LastSelectedDate), [Date])), [Result])
VAR LastSelectedB = SUMX(FILTER(ALL(Data), [Date] = LastSelectedDate), [B])
We now have both pieces to do the division.
DIVIDE(PriorSelectedResult, LastSelectedB, BLANK())
Again, here is the formula in one piece.
Division =
VAR SelectedDate = SELECTEDVALUE(Data[Date])
VAR SelectedA = SUM(Data[A])
VAR OlderDatesList = SUMMARIZE(FILTER(ALL(Data), [Date] <= SelectedDate), [Date], "Result", SelectedA - SUMX(FILTER(ALL(Data), [Date] <= SelectedDate && [Date] >= EARLIER([Date])), [B]))
VAR LastSelectedDate = MAXX(FILTER(OlderDatesList, [Result] <= 0), [Date])
VAR PriorSelectedResult = SUMX(FILTER(OlderDatesList, [Date] = MINX(FILTER(OlderDatesList, [Date] > LastSelectedDate), [Date])), [Result])
VAR LastSelectedB = SUMX(FILTER(ALL(Data), [Date] = LastSelectedDate), [B])
RETURN
DIVIDE(PriorSelectedResult, LastSelectedB, BLANK())
EDIT: Based on comment, here is the final piece to get the number of rows before the "Result" value hits (or goes below) 0.
The logic for this piece starts the same as the "Division" measure, since we need that OlderDatesList for this calculation as well. Once we have that list, we just need to check if the "Result" column ever hits (or goes below) 0, and if it does, return the number of rows before that point.
Count before Zero =
VAR SelectedDate = SELECTEDVALUE(Data[Date])
VAR SelectedA = SUM(Data[A])
VAR OlderDatesList = SUMMARIZE(FILTER(ALL(Data), [Date] <= SelectedDate), [Date], "Result", SelectedA - SUMX(FILTER(ALL(Data), [Date] <= SelectedDate && [Date] >= EARLIER([Date])), [B]))
RETURN
IF(
COUNTAX(FILTER(OlderDatesList, [Result] <= 0), [Date]) >= 1,
COUNTAX(FILTER(OlderDatesList, [Result] > 0), [Date]),
BLANK()
)

Related

How do I code IF statement in Dax for power BI

How do I code this in dax with IF statement.
Numbers 1-5 = Inadmissible
Numbers 5-25,27,28 = Family reasons
Number 26 = Medical reasons
Do exactly like you would do in Excel. Note that you would need a row context, so put the formula into a calculated column.
Result =
IF( Sewa[Number] <= 5, "Inadmissible",
IF( Sewa[Number] <= 25, "Family reasons",
IF( Sewa[Number] = 26, "Medical reasons",
IF( Sewa[Number] > 26, "Family reasons"
))))
Use Switch():
SWITCH(
TRUE()
,AND(value<29,value>26),”Family reason”
,value=26,”Medical reasons”
,value>4,”Family reasons”
,value>0,”inadmissible”
,”undefined”
)

Time based Calculation groups - apply year to date to a yearly comparison

The workfile is available here
I wish to conduct time-based analysis on my datas.
For this purpose, I have created 2 calculation groups.
Year comparison that enables me to deal with relative yearly data :
N :=
VAR lv_MAX_YEAR =
[RELATIVE_MAX_YEAR] - 0
RETURN
CALCULATE (
SELECTEDMEASURE() ,
dim_CALENDAR[Year] = lv_MAX_YEAR
)
N-1 :=
VAR lv_MAX_YEAR =
[RELATIVE_MAX_YEAR] - 1
RETURN
CALCULATE (
SELECTEDMEASURE() ,
dim_CALENDAR[Year] = lv_MAX_YEAR
)
N/N-1 Evolution :=
VAR N = CALCULATE( SELECTEDMEASURE() , 'Year comparison'[Year comparison] = "N" )
VAR N_1 = CALCULATE( SELECTEDMEASURE() , 'Year comparison'[Year comparison] = "N-1" )
RETURN
N - N_1
Works fine for me with the expeced behaviour : N is by default the current year (max year in my calendar table), but if I filter on a given year, N becomes that one.
and Relative Period allows me to compare Sales by the number of passed day from the beginning of the year :
[MAX_YEAR_DAY] :=
VAR lv_MAX_YEAR =
CALCULATE (
MAX ( dim_CALENDAR[Year] ) ,
ALL ( dim_CALENDAR[Year] )
)
RETURN
CALCULATE (
MAX ( dim_CALENDAR[Year_day] ) ,
dim_CALENDAR[Year] = lv_MAX_YEAR
)
Year To Max Year_day :=
VAR lv_MAX_YEAR_DAY =
[MAX_YEAR_DAY]
RETURN
CALCULATE (
SELECTEDMEASURE () ,
dim_CALENDAR[YEAR_DAY] <= lv_MAX_YEAR_DAY
)
So far it's a partial success :
my grand totals seem correct ;
but my monthly details don't ;
and my yearly comparison gets busted when it's activated.
How can I use both calculation groups simultaneously?

Filter with Linq comma separated field

I have a table with a field called COMMA_SEPARATED_VALUES. How can I filter with a single! (I have to integrate it into a larger query) LINQ query
all rows, where one of the entries is in a range of integer.
Table TEST
ID COMMA_SEPARATED_VALUES
-----------------------------------
1 '1,2,3,4'
2 '1,5,100,4,33'
3 '666,999'
4 '5,55,5'
Filter for Range "10 - 99" would result in
ID
------------------------
2 (because of 33)
4 (because of 55)
If you are aware of the performance side effect of calling AsEnumerable() method and it doesn't harm:
int lowerBound = 10; // lower bound of your range
int upperBound = 99; // upper bound of your range
var d = from row in context.Test.AsEnumerable()
let integers = row.COMMA_SEPERATED_VALUES
.Split(new char[] { ',' })
.Select(p => int.Parse(p))
where integers.Any(p => p < upperBound && p > lowerBound)
select row;

Linq - Grouping by range results in "sub-query returns more than one row"

I am using the following LINQ to group by a variable range (as per question here)
var ranges = new List<decimal> { 5m, 10m, 20m };
var grouped = entities.PointTransaction.Where( x => x.UserInfo.College == collegeID
&& x.Amount < 0)
.GroupBy( x=> ranges.FirstOrDefault( r => r >= Math.Abs( (decimal) x.Amount) )
).ToList();
However, I will get the error:
single-row subquery returns more than one row
When none of the rows match any of the ranges ( say, the values are all less than 5), the query will work.
I am using DevArt Dotconnect for Oracle, Entity Framework 4

Algorithm to determine if a given date/time is between two date/time pairs

I have an array of dates in a one week range stored in an unusual way.
The Dates are stored in this numeric format: 12150
From left to right:
1st digit represents day: 1 = sunday, 2 = monday, 3 = tuesday, ...., 7 = saturday
next two digits represent hour in a 24 hour system: 00 = midnight, 23 = 11pm
next two digits represent minutes: 00-59
Given an input date and a start date and end date I need to know if the input date is between the start and end date.
I have an algorithm right now that I think works 100% of the time, but I am not sure.
In any case, I think there is probably a better and simpler way to do this and I was wondering if anybody knew what that algorithm was.
If not it would be cool if someone could double check my work and verify that it does actually work for 100% of valid cases.
What I have right now is:
if (startDate < inputDate &&
endDate > inputDate) {
inRange = yes;
}
else if (endDate < startDate) {
if((inputDate + 72359) > startDate &&
(inputDate + 72359) < endDate) {
inRange = yes;
}
else if((inputDate + 72359) > startDate &&
(inputDate + 72359) < (endDate + 72359)) {
inRange = yes;
}
}
How about
const int MAX = 72460; // Or anything more than the highest legal value
inRange = (MAX + inputDate - startDate) % MAX <
(MAX + endDate - startDate) % MAX;
This assumes of course that all the dates are well formed (according to your specs).
This addresses the case where the start is "after" the end. (e.g. Friday is in range if start is Wednesday and end is Monday)
It may take a second to see (which probably isn't good, because readability is usually the most important) but I think it does work.
Here's the basic trick:
Legend:
0: Minimum time
M: Maximum time
S: Start time
1,2,3: Input Time test points
E: End Time
The S E => Not in range
2 In range
3 > E => Not in range
The S > E case
0 M
Original -1--E----2---S--3--
Add Max -------------------1--E----2---S--3--
Subtract StartDate ------1--E----2---S--3--
% Max S--3--1--E----2----
1 In range
2 > E => Not in range
3 In range
If you really want to go nuts (and be even more difficult to decipher)
const int MAX = 0x20000;
const int MASK = 0x1FFFF;
int maxMinusStart = MAX - startDate;
inRange = (maxMinusStart + inputDate) & MASK <
(maxMinusStart + endDate) & MASK;
which ought to be slightly faster (trading modulus for a bitwise and) which we can do since the value of MAX doesn't really matter (as long as it exceeds the maximum well-formed value) and we're free to choose one that makes our computations easy.
(And of course you can replace the < with a <= if that's what you really need)
There is some logic error with dates in that format. Since the month and year information is missing, you cannot know what calendar day is missing. e.g. 50755 might be Thursday March 12 2009, but it might just as well be exactly a week ago, or 18 weeks ahead. That for you could never be 100% sure if any date in that format is between any other 2 dates.
Here the condition of the inner if can never be true, since endDate < startDate:
if (endDate < startDate) {
if((inputDate + 72359) > startDate &&
(inputDate + 72359) < endDate) {
// never reached
inRange = yes;
}
The following if also can't be optimal, since the first part is always true and the second part is just identical to inputDate < endDate:
if((inputDate + 72359) > startDate &&
(inputDate + 72359) < (endDate + 72359))
I think you want something like this:
if (startDate < endDate)
inRange = (startDate < inputDate) && (inputDate < endDate);
else
inRange = (startDate < inputDate) || (inputDate < endDate);
you should use >= and <= if you really want it in range
say i pick this date 10000 or 72359, how you would handle this? it is in range or not?
also i didn't know value for startDate and endDate since you didn't initialize it, correct me if i were wrong, variable that didn't initialized will start with 0 or null or ''
so i assume the startDate = 10000 and endDate 72359
btw why you pick this kind of array (as int or string value?) why first value was day? not date example:
010000 -> date 1st of the month 00:00
312359 -> date 31th of the month 23:59
but it's up to you :D
so sorry if i were wrong i took algorithm class only on university and it was 5 years ago :D
A better approach might be to normalize your data converting all the day of the week values to be relative to the start date. Something like this:
const int dayScale = 10000; // scale factor for the day of the week
int NormalizeDate(int date, int startDay)
{
int day = (date / dayScale) - 1; // this would be a lot easier if Sunday was 0
int sday = startDay - 1;
if (day < sday)
day = (day + 7 - sday) % 7;
return ((day+1) * dayScale) + (date % dayScale);
}
int startDay = startDate / dayScale; // isolate the day of the week
int normalizedStartDate = NormalizeDate(startDate, startDay);
int normalizedEndDate = NormalizeDate(endDate, startDay);
int normalizedInputDate = NormalizeDate(inputDate, startDay);
inRange = normalizedInputDate >= normalizedStartDate &&
normalizedInputDate <= normalizedEndDate;
I am pretty sure this will work as written. In any case, the concept is cleaner that multiple comparisons.
The simplest solution i found is this:
said x your generic time and S, E the start and end time respectively (with 0 < S,E < T):
f(x) = [(x-S) * (x-E) * (E-S) < 0]
This function returns TRUE if x is in between the start and end time, and FALSE otherwise.
It will also take care of start time bigger than end time (i.e. you start working at 20:00 and finish at 04:00, 23:13 will return TRUE)
i must say, considering the multiplications, it could not be the most efficient in terms of speed, but it is definitely the most compact (and pretty IMHO)
EDIT:
i found a much more elegant and efficient solution:
f(x) = (x<S) XOR (x<E) XOR (E<S)
you can substitute XOR with the "different" operator ( != )
I explain it:
The first formula comes from the considering the relation inequality study:
if S < E:
...............S.....E..........
(x-S)----------+++++++++++++++++
(x-E)----------------+++++++++++
(E-S)+++++++++++++++++++++++++++
total++++++++++------+++++++++++
so, the total is negative if x is in between S and E
if S > E:
...............E.....S..........
(x-S)----------------+++++++++++
(x-E)----------+++++++++++++++++
(E-S)---------------------------
total----------++++++-----------
so, the total is negative if x is bigger than S or smaller than E
To reach the final equation, you decompose the first formula in 3 terms:
(x-S)<0 => x<S
(x-E)<0 => x<E
(E-S)<0 => E<S
the product of these terms is negative only if they are all negative (true, true, true) or only one is negative and the other are positive (true, false, false, but the order does not matter)
Therefore the problem can be solved via
f(x) = (x<S) != (x<E) != (E<S)
These solution can be applied to any similar problem with periodic system, such as checking if the angle x is inside the arc formed by the two angles S and E.
Just make sure that all the variable are between 0 and the period of your system (2PI for arcs in a circle, 24h for hours, 24*60*60 for the seconds count of a day.....and so on)

Resources