MDX: Calculating MONTH COVER (of Stock) in a performant way - filter

this is my dataset:
I want to calculate the "Cover Month". Therefore I have to look for Stock(in this example in january 2016 = 5,000), then have a look for each future month if current stock(january 2016) is bigger than "cum. Sales" of following month. If yes, then remember value = 1. This should be done for each future month. After this step all remembered values should be added, so result is 4 (Cover Month). Stock will be enough for 4 following months.
Next step system should do this for next month - dynamically for each month...
How can I do this in a performant way?
Is this the right way:
Filter([TIME].[Year to Month].currentmember : NULL,
[Measures].[cum Sales] < [Measures].[Stock])
?
Maybe anybody can give me a hint? Or maybe I need another alternative formula to get a subtotal and then do another calculation?
Thanks in advance, Andy

If you just require a 1 or 0 then can things not be simplified:
IIF(
SUM(
{[TIME].[Year to Month].currentmember : NULL},
[Measures].[cum Sales]
)
< ([Measures].[Stock],[TIME].[Year to Month].&[Jan-2016]) //<<amend to the date format used in your cube
,1
,NULL
)

Related

Years of Service Anniversary formula - Cognos Analytics - Costpoint 8.0.0

this one has been bugging me for a few weeks... I'm trying to write a formula in Cognos Analytics (costpoint) that returns if someone is hitting a new years of service milestone in the actual month.
returning a simple "true/false" or "yes/no" is perfect
essentially it's just if their years of service fall between multiple date ranges (ex: i want a return value of "yes" for someone currently at 4.95 years of service since they would hit their 5 years within the coming month)
years of service are in number format in column "A" in excel and in column [years of service] in costpoint (cognos) (ex: 9.154, 4.982, 24.995 ...)
i got an Excel version to work seen below:
=IF(OR(AND(A1>4.91,A1<=5),(AND(A1>9.91,A1<=10)),(AND(A1>14.91,A1<=15)),(AND(A1>19.91,A1<=20)),(AND(A1>24.91,A1<=25)),(AND(A1>29.91,A1<=30))),"yes","no")
i'm still just getting familiar with Cognos(costpoint) syntax, so i tried to write it as seen below:
if(or(and([Years of Service]>4.91,[Years of Service]<5),(and([Years of Service]>14.91,[Years of Service]<15)))then ('yes') else ('null')
without any luck...
anyone want to take a crack at it?? :)
In the absence of start dates, which would be easier, and handling the more general case (What if they are approaching 45 years of service?):
case
when MOD(MOD([Years Of Service], 5) + 1, 5) > 0.91
and MOD(MOD([Years Of Service], 5) + 1, 5) <= 1
then 'yes'
else 'no'
end
To see who reaches a "years of service" value that is a 5-year milestone next month, create a filter:
mod(
_months_between (
_first_of_month (
_add_months (current_date, 1)
),
_first_of_month ([StartDate])
),
60
) = 0
But if you have service start dates, you can use dates calculations to see who reaches a 5-year milestone next month:
mod(
_months_between (
_first_of_month (
_add_months (current_date, 1)
),
_first_of_month ([StartDate])
),
60
)
Look at the various case functions and try them for your expression.
I don't think you need a start date, you could just focus on the decimal portion of the Years of service field. You know that if the decimal portion is equal to or above ).9166 then the years of service is in the final month.
if ([YOS]-cast([YOS],integer)>=0.9166)
then
('Yes')
else
('No')
If you want to figure a major milestone year such as 5,10,15 then you can take the expression further.
if mod(
if ([YOS]-cast([YOS],integer)>=0.9166)
then
(cast([YOS],integer)+1)
else
(cast([YOS],integer)),5=0)
then ('Milestone Year')
else ('Keep on Working!')
Hopefully the above helps!

Power Query (M language) 50 day moving Average

I have a list of products and would like to get a 50 day simple moving average of its volume using Power Query (M).
The table is sorted by product name and date. I add a custom column and applied the code below.
if [date] >= #date(2018,1,29)
then List.Average(List.Range(Source[Volume],[Volume]-1,-50))
else ""
Since it is already sorted by date and name, an if statement was applied with a date as criteria/filter. However, an error occurs that says
'Volume' column not found in the table.
I expect to have an added column in the power query with volume 50 day moving average per product. the calculation to be done if date is greater than or equal Jan 29, 2018.
We don't know what your columns are, but assuming you have [product], [date] and [volume] in Source, this would average the last 50 days of [volume] for the identical [product] based on each [date], and place in a new column
AvgAmountAdded = Table.AddColumn(Source, "AverageAmount", (i) => List.Average(Table.SelectRows(Source, each ([product] = i[product] and [date]<=i[date] and [date]>=Date.AddDays(i[date],-50)))[volume]), type number)
Finally! found a solution.
First, apply Index by product see this post for further details
Then index again without criteria (index all rows)
Then, apply below code
= Table.AddColumn(#"Previous Step", "Volume SMA(50)", each if [Index_byProduct] >= 50 then List.Average(List.Range(#"Previous Step"[Volume], ([Index_All]-50),50)) else 0),
For large dataset, Table.Buffer function is recommended after index-expand step to improve PQ calculation speed

Find min value of member, MDX

Just started to explore MDX. Can anyone help me to get below result.
Looking to build a MDX query which gives same out as below SQL query
select max(date),min(date) from Fiscal_calendar
Where :
1. Fiscal_Calendar is dimension in cube
2. date is the attribute
Min Date will be the First Child of the [All] member while Max Date will be the Last Child.
SELECT {Fiscal_Calendar.Date.[All].FirstChild, Fiscal_Calendar.Date.[All].LastChild} ON 1,
{} ON 0
FROM [YourCube]
Second last child:
Fiscal_Calendar.Date.[All].LastChild.Lag(1)
SELECT {
[Dimension - Fiscal Calendar].[Fiscal Quarter].members.item([Dimension - Fiscal Calendar].[Fiscal Quarter].members.count-2)
,[Dimension - Fiscal Calendar].[Fiscal Quarter].[All].firstChild
} ON 1,{} ON 0 FROM [MyCube]
Thanks #SourabA. Above code worked out to me. Do let me know if any better approach

Time aligned variable in stata

I have a panel data set for multiple waves (13) for roughly 10,000 individuals each year, with people entering and exiting at various time points. I am interested in what happens as people become diagnosed with a disease over time. Therefore I need to recode the time variable so that it becomes t=0 the first wave when diagnosed, then t=1 is the next year and so on, so that all of my individuals are comparable (and I guess -1 for t-1 etc). However I am unsure about how to go about this in stata. Would anyone be able to advise? Many thanks
The case of one diagnosis per person
clear all
set more off
*----- example data -----
set obs 100
set seed 2357
generate id = _n
generate year = floor(10 * runiform()) + 1990
expand 5
bysort id: replace year = year + _n
bysort id (year): generate diag = cond(_n == 3, 1, 0)
list in 1/20, sepby(id)
*----- what you seek -----
bysort id (diag): gen time = year - year[_N]
sort id year
list in 1/20
I assume the same data structure as #RichardHerron and use his example. diag is an indicator variable that takes on the value of 1 at the time of diagnosis and 0 otherwise (only one diagnosis per person is considered).
The sorting done by bysort is critical. The observation holding the time of diagnosis is pushed to the end of the database (by id groups) and then all that's left to do is compare (subtract) all years with that reference year. See help _variables for details on system variables like _N.
The case of multiple diagnoses per person
If several diagnoses are made per person, but we care only for the first occurence (according to year), we could do:
gsort id diag -year
by id: gen time = year - year[_N]
Simple but not optimal solution
Suppose diagnosis is 1 when diagnosed (at most once per person) and 0 otherwise.
Then the time at diagnosis is at its simplest
egen time_diagnosis = total(diagnosis * year), by(id)
but you need to ignore any zeros. To spell that out,
replace time_diagnosis = . if time_diagnosis == 0
Better alternative
A more complicated but preferable alternative can handle multiple diagnoses if they occur:
egen time_diagnosis = min(year / diagnosis), by(id)
as year / diagnosis is year when diagnosis is 1 and missing otherwise. This yields missing values if there is no diagnosis, which is as it should be.
Then you subtract that to get a new time variable.
gen time2 = time - time_diagnosis
In short, I think you can get this done in two statements, handling panel structure too.
Update
#Richard Herron asks why use egen with by(), and not just
gen time_diagnosis = time * diagnosis
A limitation of that is that the "correct" value is contained only in those observations for which diagnosis is 1; that value still has to be "spread" to other values for the same id. But that is precisely what egen does here. In the simplest situation, with one diagnosis the total of time * diagnosis is just time * 1 or time, as any zeros make no difference to the sum.
It is usually helpful to provide test data, but here they are easy enough to generate. The trick is to find the first year for each individual (my fyear), which I'll do with min() from egen. Then I'll subtract this first year fyear from the actual year to find the year relative to diagnosis ryear.
/* generate panel */
clear
set obs 10000
generate id = _n
generate year = floor(10 * runiform()) + 1990
expand 10
bysort id: replace year = year + _n
sort id year
list in 1/20
/* generate relative year */
bysort id: egen fyear = min(year)
generate ryear = year - fyear
list in 1/20
If the first year in the panel is not diagnosis, then just construct fyear based on diagnosis criteria.
Edit: Thinking more on this, maybe it's the last part that you're having a hard time with (i.e., identifying the diagnosis year to subtract from the calendar year). Here's what I would do.
bysort id (year): generate diagnosis = cond(_n == 5, 1, 0)
preserve
tempfile diagnosis
keep if (diagnosis == 1)
rename year dyear
keep id dyear
save `diagnosis'
restore
merge m:1 id using `diagnosis', nogenerate
generate ryear2 = year - dyear

Qlikview Rolling 12 Month Fiscal

This forum seems to be more geared toward more programming issues but I would be interested if someone would look at the logic issue in the link below.
My issue is about how to calculate the previous 12 months' total using the calendar already defined. I have found very few examples of this on the QV community. Please see the link below for more details. I would be willing to look at an SQL or a QV script solution.
Our fiscal year runs Nov to Oct. I would like to have the end user select the Year and a chart to display the last rolling 12 months' margin. I have had issues getting my total to accumulate for previous months.
My goal would be for it look similar to the Rolling 12 Month Total - GP column in the manually calculated Excel image 'Goal' (look at QV link for screenshot).
Rolling Margin equation: my attempt to use Set Analysis to make a rolling avg equation.
=Sum({<master_date={'>=$(=MonthStart(Max(master_date), -12))<=$(=MonthEnd(Max(master_date)))'}>}
MasterCalendar:
TempCalendar:
LOAD
$(vDateMin) + RowNo() - 1 AS DateNumber,
Date($(vDateMin) + RowNo() - 1) AS TempDate
AUTOGENERATE 1
WHILE $(vDateMin)+IterNo()-1<= $(vDateMax);
MasterCalendar: this uses master_date to connect items together. This an fiscal calendar are hard to put together with rolling avg
LOAD
TempDate AS master_date,
Day(TempDate) AS CalendarDay,
WeekDay(TempDate) AS CalendarWeekDay,
Week(TempDate) AS CalendarWeek,
Month(TempDate) AS CalendarMonth,
Year(TempDate) AS CalendarYear,
'Q' & Ceil(Month(TempDate)/3) AS CalendarQuarter,
WeekDay(TempDate) & '-' & Year(TempDate) AS CalendarWeekAndYear,
Month(TempDate) & '-' & Year(TempDate) AS CalendarMonthAndYear,
If(Num(TempDate) >= $(vYearStart) AND Num(TempDate) < $(vMonthNow), -1, 0) AS YTD,
If(Num(TempDate) >= $(vYearStartLY) AND Num(TempDate) < $(vMonthNowLY), -1, 0) AS LY_YTD,
Year2Date(TempDate) * -1 AS YTD_Flag,
Year2Date(TempDate,-1, 1, $(vToday))*-1 AS LY_YTD_Flag
RESIDENT TempCalendar ORDER BY TempDate ASC;
DROP TABLE TempCalendar;
FiscalCalendar: This defines our fiscal year
FiscalCalendar:
LOAD date(date#(20011101,'YYYYMMDD')+recno(),'MM/DD/YY') AS "master_date"
AUTOGENERATE today()-date#(20011101,'YYYYMMDD');
LEFT JOIN (FiscalCalendar)
LOAD
"master_date",
date(monthstart(master_date),'MMM YY') AS "MonthFisical",
date(monthstart(master_date),'MMM') AS "MonthFisical_MonthTitle",
date(yearstart(master_date,1,11),'YYYY') AS "YearFiscal",
month(master_date)-month(num(today(1))) AS FiscalMonthsElapsed,
YearToDate(master_date, 0,11)*-1 AS YTD_FLAG_Fiscal,
YearToDate(master_date,-1,11)*-1 AS LY_YTD_FLAG_Fiscal
RESIDENT FiscalCalendar;
To see screenshots:
http://community.qlikview.com/message/219912#219912
Thank you for taking the time to look at this issue.
The solution is not in the calendar : you have to create a pivot table between your calendar and the fact table.
In this pivot table you have 2 type : DIRECT and CROSSING.
For type DIRECT, a row in fact table is linked to the date in calendar
For type CROSSING, a row in fact table is linked to all the dates of the 12 future months in calendar.
So in Qlikview, you use the type DIRECT all the time, except when you want to present for each month the total of the past 12 months. In this case you use CROSSING because all rows are linked to the dates of the 12 future months, so it means (reversed point of view) that a month is linked to all data of the past 12 months.
Examples:
xlsx
QVW

Resources