If-Else-Then with today() - date

i am currently trying to write some code that goes through my data and marks a number 0-12 based off the date in the "Week" column. this number appears in a new column called group which is created by the code you see below. The problem is that this column is periods all the way down and not numbers. There are no errors messages in the log so i dont know where i went wrong (im fairly new to sas). PS. the dates range from 6/17 to 9/9
data have;
set have;
if today()+84 = Week > today()+79 then group=12;
else if today()+77 = Week > today()+72 then group=11;
else if today()+70 = Week > today()+65 then group=10;
else if today()+63 = Week > today()+58 then group=9;
else if today()+56 = Week > today()+51 then group=8;
else if today()+49 = Week > today()+45 then group=7;
else if today()+42 = Week > today()+37 then group=6;
else if today()+35 = Week > today()+30 then group=5;
else if today()+28 = Week > today()+23 then group=4;
else if today()+21 = Week > today()+16 then group=3;
else if today()+14 = Week > today()+11 then group=2;
else if today()+7 = Week > today()+2 then group=1;
else if today() = Week > today()-5 then group=0;
run;
update:
the first column is called week and is a monday date that goes 12 weeks into the future. the rest of the columns are variables that i will end up summing based on the group that row is in.
ex:
week ID var2 ... var18
17jun2019 1 x x
24jun2019 1 x x
and it continues until 09sept2019.. it does this for each ID (roughly 10,000 of them) but not every id goes 12 weeks out thats why i am using the else if
i would like it to look like
week ID var2 ... var18 group
17jun2019 1 x x 0
24jun2019 1 x x 1
01july2019 1 x x 2

A full reference to SAS operators can be found in SAS help by searching SAS Operators in Expression. SAS expressions can use some operators that are relatively unique across the spectrum of coding languages. Here are some that are not typically found in newly coded SAS (at time of this post)
<> MAX operator
>< MIN operator
implied AND operator
Two comparisons with a common variable linked by AND can be condensed with an implied AND.
So the uninitiated readers of the question may misunderstand
…
if today()+35 = Week > today()+30 then group=5;
…
as incorrect, instead of recognizing it as an implied AND
…
if today()+35 = Week AND Week > today()+30 then group=5;
…
When syntactically correct, the = in the implied AND causes the expression to be true only on equality. The week value in open interval ( today()+35, today()+34 ) will never evaluate as true in the above expression. This is the likely cause of the missing values (.) you are seeing.
Why does the code exhibit non-static delta of 7 in the sequence 30,23,16,11,2,-5 ?
Should it be 30,23,16,9,2,-5.
In other words why is group 1 apparently shooting for a 5 day range [+7, +2) when all the others are 3, such as [+14, +11) ?
Why are there 2-days domains, presumed weekends, in which group is not assigned, and would thus be missing (.) ?
This type of wallpaper code is often better represented by a an arithmetic expression.
For example, presuming integer SAS date values:
group = ifn ( MOD (week-today(), 7) in (1,2)
, .
, CEIL (week-today() / 7 )
);
if not ( 0 <= group <= 12 ) then group = .; * probably dont want this but makes it compliant with OP;
Tomorrow the group value could 'wrong' because it is today() based. Consider coding a view instead of creating a permanent data set -- OR -- place meta information in the variable name group_on_20190622 = …
If you insist on wallpaper, consider using a select statement which is less prone to typing errors that can happen with errant semi-colons or missing elses.

It is not at all clear what you are trying to do. It sounds a little like you want to group observations based on how many weeks the date variable (called WEEK) is away from today's date. It might be easiest to just use the INTCK() function. That will count how many week boundary's are crossed between the two dates.
data have ;
input id week date9.;
format week date9.;
cards;
1 17jun2019
1 24jun2019
1 01jul2019
2 24jun2019
2 01jul2019
2 08jul2019
;
data want ;
set have;
group = intck('week',today(),week);
run;
You can then summarize the number of ID's per group.
proc freq data=want;
tables group;
run;
Results:
The FREQ Procedure
Cumulative Cumulative
group Frequency Percent Frequency Percent
----------------------------------------------------------
-1 1 16.67 1 16.67
0 2 33.33 3 50.00
1 2 33.33 5 83.33
2 1 16.67 6 100.00

Assuming week is date and not datetime.
data test;
do i = 1 to 30;
dt = intnx('day',today(),1*i);
output;
end;
format dt date9.;
run;
data test2;
set test;
if dt ge today() and dt le today()+7 then dt2 = 1;
else if dt ge today()+8 and dt le today()+14 then dt2 = 2;
else if dt ge today()+15 and dt le today()+21 then dt2 = 3;
else if dt ge today()+22 and dt le today()+28 then dt2 = 4;
else if dt ge today()+29 and dt le today()+35 then dt2 = 5;
/* another way */
dt3 = ceil(intck('day',today(),dt)/7);
run;
removed wrong answer.

Related

Manipulating last two rows if there's data based on a Cut date

This question is a slightly varied version of this one...
Now I'm using Measures instead of Calculated columns and the date is static instead of having it based on a dropdown list.
Here's the Power BI test .pbix file:
https://drive.google.com/open?id=1OG7keqhdvDUDYkFQFMHyxcpi9Zi6Pn3d
This printscreen describes what I'm trying to accomplish:
Basically the date in P6 Update table is used as a cut date and will be fixed\static. It's imported from an Excel sheet where the user can customize it however they want.
Here's what should happen when a matching row in Test data table is found for P6 Update date:
column Earned Daily - must have its value summed with the next row if there's one;
column Earned Cum - must grab the next row's value;
all the previous rows should remain intact, that is, their values won't change;
all subsequent rows must have their values assigned 0.
So for example:
If P6 Update is 1-May-2018, this is the expected result:
1-May 7,498 52,106
2-May 0 0
If P6 Update is 30-Apr-2018, this is the expected result:
30-Apr 13,173 50,699
1-May 0 0
2-May 0 0
If P6 Update is 29-Apr-2018, this is the expected result:
29-Apr 11,906 44,608
30-Apr 0 0
1-May 0 0
2-May 0 0
and so on...
Hope this makes sense.
This is easier in Excel, but trying to do this in Power BI is making me go nuts.
I will ignore previously asked related questions and start from scratch.
First, create a measure:
Current Earn =
CALCULATE (
SUM( 'Test data'[Value]),
'Test data'[Act Rem] = "Actual Units",
'Test data'[Type] = "Current"
)
This measure will be used in other measures, to save you from typing all these conditions ("Actual Units" and "Current") again and again. It's a great practice to re-use measures in other measures - saves work, makes code cleaner and easier to refactor.
Create another measure:
Cut Date = SELECTEDVALUE('P6 Update'[Date])
We will use this measure whenever we need a cut off date. Please note that it does not have to be hard-coded - if P6 table contains a list of dates, you can create a pull-down slicer from the dates, and can choose the cut-off date dynamically. The formula will work properly.
Create third measure:
Next Earn =
VAR Cut_Date = [Cut Date]
VAR Current_Date = MAX ( 'Test data'[Date] )
VAR Next_Date = Current_Date + 1
VAR Current_Earn = [Current Earn]
VAR Next_Earn = CALCULATE ( [Current Earn], 'Test data'[Date] = Next_Date )
RETURN
SWITCH (
TRUE,
Current_Date < Cut_Date, Current_Earn,
Current_Date = Cut_Date, Current_Earn + Next_Earn,
BLANK ()
)
I am not sure if "Next Earn" is a good name for it, hopefully you will find a more intuitive name. The way it works: we save all necessary inputs into variables, and then use SWITCH function to define the results. Hopefully it's self-explanatory. (Note: if you need 0 above Cut Date, replace BLANK() with 0).
Finally, we define a measure for cumulative earn. It does not require any special logic, because previous measure takes care of it properly:
Cum Earn =
VAR Current_Date = MAX('Test data'[Date])
RETURN
CALCULATE(
[Next Earn],
FILTER(ALL('Test data'[Date]), 'Test data'[Date] <= Current_Date))
Result:

How to reformat dates?

Hello currently my dates are stored as numeric in the form of 40547. How can I convert these to MMDDYY10.?
data SevenSec11;
set Seven11;
DateRecieved = input(put(DateRecieved, 8.), MMDDYY10.);
format DateRecieved MMDDYY10.;
run;
How to convert it depends on what the value represents. If it is dates as stored by Excel then change the offset value. If it is supposed to represent MMDDYY values then use the Z6. format in your PUT() function call.
data test;
input num ;
sasdate1 = num + '30DEC1899'd ;
sasdate2 = input(put(num ,z6.),mmddyy10.);
format num comma7. sasdate: yymmdd10. ;
cards;
40547
;
Result:
Obs num sasdate1 sasdate2
1 40,547 2011-01-04 1947-04-05
Note that using Y-M-D order for dates will eliminate confusion that truncated leading zeros can cause. It will also prevent half of your audience from confusing April 5th with May 4th.

Convert unix time to month number?

Using os.time how can I get how many months have passed since the unix epoch (Unix Timestamp)
I just need it for a month ID, so any kind of number would be fine, as long as it changes every month, and it can be reversed to get the actual month.
local function GetMonth(seconds)
local dayduration,year = 3600*24
local days={31,0,31,30,31,30,31,31,30,31,30,31}
for i=1970,10000 do -- For some reason too lazy to use while
local yeardays = i%4 == 0 and i%100 ~= 0 and 366 or 365
local yearduration = dayduration * yeardays
if yearduration < seconds then
seconds = seconds - yearduration
else
year = i break
end
end
days[2]=(year%4==0) and 29 or 28
seconds = seconds%(365*24*3600)
for i=1,12 do
if seconds>days[i]*dayduration then
seconds=seconds-days[i]*dayduration
else
return --i + year*12 <-- If you want a unique ID
end
end
end
Currently, it'll give the number 2, since it's February. If you uncomment the code at the end for the unique ID, you'll get 554 instead, meaning we're currently at the 554th month since the epoch.
As Jean-Baptiste Yunès said in his answer's comments, I'm not sure if your sentence:
NOTE: This is for Lua, but I'm unable to use os.date
meant you have no os.date, or that you don't know how to use it. You have an answer for both cases, you can use the one you need.
This may do the trick:
print (os.date("*t",os.time())["month"])
os.time() gives you the current date as a number. os.date("*t",...) converts it into a table in which the month equals to the number of the month corresponding to the date.

How to loop through a date range in which dates are stored as Integers?

I have to develop a system, where user will specify a starting range and an ending range (By Range, I mean to say a particular period, where PERIOD is given as a concatenation of YEAR AND MONTH). For example, PERIOD = 201304, where 2013 is the user entered Year and 04 is the MONTH. The user can specify a maximum range of upto 2 years only.
Data needs to be selected on the basis of the user entered range. The problem is whenever I try to loop through the period, the PERIOD changes after 201312 to 201313. I have separate variables for user selected year and month (start_year, start_month, end_year, end_month)
I did a IF loop there in which I tried to do the following
FOR p_tmpyear = p_tempfrom TO p_tempto
IF (p_monthfrm < 12) THEN
LET p_yearfrm = p_yearfrm + 1
LET p_monthfrm = 01
LET p_fromperiod = p_yearfrm + 1,p_monthfrm >p_fromperiod is an integer storing concatenated Month and Year, to achieve the desired PERIOD format as mentioned above.
LET p_tempfrom = p_fromperiod
END IF
DISPLAY p_tmpyear
END FOR
I even tried thsi one :
IF (p_fromperiod MOD p_yearfrm = 13) THEN
LET p_yearfrm = p_yearfrm + 1
LET p_monthfrm = 01
LET p_fromperiod = p_yearfrm + 1,p_monthfrm
Still the period changes after reaching 201212 to 201213. I want this to be 201301. Please help.
As you have probably guessed, you have some flaws in your logic. The first example is too hard to decipher, whilst the second shows you don't understand what the MOD operator does. If used correctly, in this context, it would be of the form IF variable MOD 12 = 0 i.e. every 12th value do something.
I think where you are going wrong is trying to do it all in one loop. I would keep it simple and break the problem into two nested loops, one for year, one for month
DEFINE start_year, end_year, start_month, end_month INTEGER -- sample values 2012, 2013,7,6
DEFINE loop_year, loop_month INTEGER -- values used to loop through year and month respectively
DEFINE loop_first_month, loop_last_month INTEGER -- for each year, calc first month and last month
DEFINE period CHAR(6)
-- First loop over year
FOR loop_year = start_year TO end_year
-- Calculate first and last month for the given year
-- If first year, use passed in start month else use 1
IF loop_year = start_year THEN
LET loop_first_month = start_month
ELSE
LET loop_first_month = 1
END IF
-- If last year, use passed in end month else use 12
IF loop_year = end_year THEN
LET loop_last_month = end_month
ELSE
LET loop_last_month = 12
END IF
-- Second loop over month
FOR loop_month = loop_first_month TO loop_last_month
LET period = loop_year USING "&&&&",loop_month USING "&&"
DISPLAY period
END FOR
END FOR
I would also encourage you to also post questions like this in the dedicated 4Js Genero developers forum at the 4Js website http://www.4js.com/fjs_forum/ as well as here. Most Genero developers should be aware of that forum and have access.

Unix gettimeofday() - compatible algorithm for determining week within month?

If I've got a time_t value from gettimeofday() or compatible in a Unix environment (e.g., Linux, BSD), is there a compact algorithm available that would be able to tell me the corresponding week number within the month?
Ideally the return value would work in similar to the way %W behaves in strftime() , except giving the week within the month rather than the week within the year.
I think Java has a W formatting token that does something more or less like what I'm asking.
[Everything below written after answers were posted by David Nehme, Branan, and Sparr.]
I realized that to return this result in a similar way to %W, we want to count the number of Mondays that have occurred in the month so far. If that number is zero, then 0 should be returned.
Thanks to David Nehme and Branan in particular for their solutions which started things on the right track. The bit of code returning [using Branan's variable names] ((ts->mday - 1) / 7) tells the number of complete weeks that have occurred before the current day.
However, if we're counting the number of Mondays that have occurred so far, then we want to count the number of integral weeks, including today, then consider if the fractional week left over also contains any Mondays.
To figure out whether the fractional week left after taking out the whole weeks contains a Monday, we need to consider ts->mday % 7 and compare it to the day of the week, ts->wday. This is easy to see if you write out the combinations, but if we insure the day is not Sunday (wday > 0), then anytime ts->wday <= (ts->mday % 7) we need to increment the count of Mondays by 1. This comes from considering the number of days since the start of the month, and whether, based on the current day of the week within the the first fractional week, the fractional week contains a Monday.
So I would rewrite Branan's return statement as follows:
return (ts->tm_mday / 7) + ((ts->tm_wday > 0) && (ts->tm_wday <= (ts->tm_mday % 7)));
If you define the first week to be days 1-7 of the month, the second week days 8-14, ... then the following code will work.
int week_of_month( const time_t *my_time)
{
struct tm *timeinfo;
timeinfo =localtime(my_time);
return 1 + (timeinfo->tm_mday-1) / 7;
}
Assuming your first week is week 1:
int getWeekOfMonth()
{
time_t my_time;
struct tm *ts;
my_time = time(NULL);
ts = localtime(&my_time);
return ((ts->tm_mday -1) / 7) + 1;
}
For 0-index, drop the +1 in the return statement.
Consider this pseudo-code, since I am writing it in mostly C syntax but pretending I can borrow functionality from other languages (string->int assignment, string->time conversion). Adapt or expand for your language of choice.
int week_num_in_month(time_t timestamp) {
int first_weekday_of_month, day_of_month;
day_of_month = strftime(timestamp,"%d");
first_weekday_of_month = strftime(timefstr(strftime(timestamp,"%d/%m/01")),"%w");
return (day_of_month + first_weekday_of_month - 1 ) / 7 + 1;
}
Obviously I am assuming that you want to handle weeks of the month the way the standard time functions handle weeks of the year, as opposed to just days 1-7, 8-13, etc.