Simplifying SAS code (if statement) using macro - sas

I have a variable x with values from 1 to 390. I need to group it in intervals of 5,
in other words
if x<6 then interval=1;
if x>5 and X<11then interval=2;
if x>10 and x<16 then interval=3; etc.
What is a shorter way of writing this code without having to repeat this if statement all the way to x=390?

This is really a bit too basic, but ok.
interval = ceil(x/5);

Related

Use of paratheses symbols in django-viewflow flow

I have been at a loss understanding the use of parentheses in django-viewflow flow code.
For example in the code below
start = (
flow.Start(views.StartView)
.Permission('shipment.can_start_request')
.Next(this.split_clerk_warehouse)
)
# clerk
split_clerk_warehouse = (
flow.Split()
.Next(this.shipment_type)
.Next(this.package_goods)
)
from here
It seems as though, a tuple containing functions is assigned to start and to split_clerk_warehouse e.t.c. What does it mean. From my best guess it would seem that the .Next functions accept a tuple as input.
NOTE I do understand the method chaining used here. I am just at a loss to understand the use of braces.
Thanks.
If I understand correctly, you wonder what the use is of the outer brackets.
Let us first write the (first, but applicable to the second) statement without outer brackets:
start = flow.Start(views.StartView).Permission('shipment.can_start_request').Next(this.split_clerk_warehouse)
This is exactly equivalent to code in your sample. But you probably agree that this is quite unreadable. It requires a user to scroll over the code, and furthermore it is a long chain of characters, without any structure. A programmer would have a hard time understanding it, especially if - later - we would also use brackets inside the parameters of calls.
So perhaps it would make sense to write it like:
start = flow.Start(views.StartView).
Permission('shipment.can_start_request').
Next(this.split_clerk_warehouse)
But this will not work: Python is a language that uses spacing as a way to attach semantics on code. As a result it will break: Python will try to parse the separate linkes as separate statements. But then what to do with the tailing dot? As a result the parser would error.
Now Python has some ways to write statements in a multi-line fashion. For example with backslashes:
start = flow.Start(views.StartView). \
Permission('shipment.can_start_request'). \
Next(this.split_clerk_warehouse)
with the backslash we specify that the next line actually belongs to the current one, and thus it is parsed like we wrote this all on a single line.
The disadvantage is that we easily can forget a backslash here, and this would again let the parser error. Furthermore this requires linear work: for every line we have to add one element.
But programming languages actually typically have a feature that programmers constantly use to group (sub)expressions together: brackets. We use it to give precedence (for example 3 * (2 + 5)), but we can use it to simply group one expression over multiple lines as well, like:
start = (
flow.Start(views.StartView)
.Permission('shipment.can_start_request')
.Next(this.split_clerk_warehouse)
)
Everything that is within the brackets belongs to the same expression, so Python will ignore the new lines.
Note that tuple literals also use brackets. For example:
() # empty tuple
(1, ) # singleton tuple (one element)
(1, 'a', 2, 5) # 4-tuple
But here we need to write a comma at the end for a singleton tuple, or multiple elements separated by comma's , (except for the empty tuple).

Nesting AND NOT ISBLANK with MULTIPLE IFs

I can't find an example close enough to this one on StackOverflow so here goes:
I want to return a message "Type?" if cell X is blank and cell Y has any text. But I'm trying to nestle it into an existing set of IFs.
Existing :
=IF($G241="Evo";M241*L241;IF($G241="Free";M241*L241;IF($G241="GN";M241*L241))))
Nestling this into the above:
=IF(AND(NOT(ISBLANK($J234));ISBLANK(G234));"Type?";"OK")
I tried this but it returns FALSE, maybe due to the AND I'm using, which I need since I'm creating a return based on two cells two cells.
=IF($G240="Evo";M240*L240;IF(AND(NOT(ISBLANK($J240));ISBLANK(G240);"Type?";"OK");IF($G240="Free";M240*L240;IF($G240="GN";M240*L240))))
getting Error:
AND expects boolean values. But 'Type?' is a text and cannot be coerced to a boolean.
IF(and(isblank(cell x),iferror(isstring(cell y),false)),"Type?","OK")
That should do it for you I think. you will need to replace cell x and cell y with the appropriate references. The iferror statement is there to catch what happens when evaluating a blank cell y.
The problem with this formula
=IF($G240="Evo";M240*L240;IF(AND(NOT(ISBLANK($J240));ISBLANK(G240);"Type?";"OK");IF($G240="Free";M240*L240;IF($G240="GN";M240*L240))))
is you are trying to check G240 for different values when it cant. Lets simplify your formula. We will replace your empty cell check with FORMULA 1
=If($G240="EVO", Do True Condition, Do Formula 1, IF(G$240=Free, Do Free True Condition, Do Free False Condition)
The problem is since you already did something (Formula 1) when G240 = "EVO", you cant start another check on what G240 after the fact with the way you have embedded your formula. a batter way of thinking of it is how to do a second check when G240="EVO" is false. Remember the general format of an if statement is:
IF(CONDITION,True Result, False Result)
There are only 3 things that go into an if statement. you tried putting in 3.
Try rearranging to this:
=If($G240="EVO", Do True Condition, IF(SOME CHECK to determine DO FOMULA 1 or CHECK for G240 = FREE, Do Formula 1, IF(G$240=Free, Do Free True Condition, Do Free False Condition)))
Basically break down what you want to check for in G240 and do it in sequence with your IF statement. Right now with what you have written, I cant tell how you want to determine if you want to run your formula 1 or if you want to check if G240="free" since you have two different outcomes if G240="Free"/
OK I think i found the issue. The IF(AND(NOT(ISBLANK works on it's own since there are no other IFs in the formula. I do want to test two different cells for text(letters) in order to show a warning if one cell was blank while the other not. But as soon as you insert the (AND into a string of multiple IFs it doesn't work.
Simply removing the (AND was all I needed to do. Another way to achieve a test for more than one blank cell was to simply add multiple IF(ISBLANKs.
EG: =IF(ISBLANK(A1)+IF(ISBLANK(A2)>2;condition true;condition false)
ForwardEd thanks very much for your help!
Regards

Using variables in SAS User defined function

I am a beginner SAS user with much experience in VBA and am having a hard time figuring out User defined functions in SAS.
I am having several problems using variables in User defined functions, but I think the two listed below are probably a related issue and would likely solve the rest of them.
I) How do you use a variable in a macro function from within a user defined function?
proc fcmp outlib = sasuser.funcs.trial;
function testNumbers(testvar $) $;
length testing $ 100;
lencheck = %length(testvar);
return (lencheck);
endsub;
run;
options cmplib = sasuser.funcs;
%put %sysfunc(testnumbers(short));
No matter what the input to the function is, the result is always 7, which matches the length of the input variable name "testvar" If I change the variable name, it changes the result. I've tried putting an ampersand in front of the variable name, but this doesn't work (it just makes the result 8...). I can get the function to return the input by putting in "return (testvar)" but can't figure out how to get the length function to work.
II) How do you define a variable as numeric in the context of the user defined function?
proc fcmp outlib=sasuser.funcs.trial;
function testNumbers(testvar $) $;
length testvar $ 100;
myNumber = 5
testNum = put(myNumber, 2.);
tempPath = %substr(1234567890, 3, 2)
tempPath1 = %substr(1234567890, 3, myNumber)
tempPath2 = %substr(1234567890, 3, testNum)
tempPath3 = %substr(1234567890, 3, put(myNumber, 2.))
return (tempPath);
endsub;
run;
The first tempPath works and returns "34" as expected. But tempPath1, tempPath2 and tempPath3 all return errors. The error is that Argument 3 to macro function %substr is not a number. For tempPath3 there is an additional error that a required operateor not found in the expression.
Note: I am aware that these functions do not do anything worthwhile. These are simplified as I am trying to learn the language and the possibilities. There may be other problems even with the simple code provided, and any advice on that would be appreciated.
What I was actually trying to code was a function that will allow for dynamically changing the library being used (so if a temp flag is set, everything will go into the Work directory, but if not, it will go to the final production location). If there is a better solution than a UDF for this, I'd love to hear this too.
The macro processor evaluates before the results are passed onto base SAS for processing.
Since your program uses this macro logic.
lencheck = %length(testvar);
The macro processor will calculate %length(testvar) which is 7 since that is how many characters are in the string testvar. It is the same as if wrote this statement:
lencheck = 7 ;
If you want the function to find the length of the variable TESTVAR then you need to use the LENGTH() function and not the macro function %LENGTH().
You have a similar issue with your use of the %SUBSTR() macro function instead of the function SUBSTR() in your second example.

Extract left part of the string in SAS?

Is there a function SAS proc SQL which i can use to extract left part of the string.it is something similar to LEFT function sql server. in SQL I have left(11111111, 4) * 9 = 9999, I would like to something similar in SAS proc SQL. Any help will be appreciated.
Had an impression you want to repeat the substring instead of multiply, so I'm adding REPEAT function just for the curiosity.
proc sql;
select
INPUT(SUBSTR('11111111', 1, 4), 4.) * 9 /* if source is char */
, INPUT(SUBSTR(PUT(11111111, 16. -L), 1, 4), 4.) * 9 /* if source is number */
, REPEAT(SUBSTR(PUT(11111111, 16. -L), 1, 4), 9) /* repeat instead of multiply */
FROM SASHELP.CLASS (obs=1)
;
quit;
substr("some text",1,4) will give you "some". This function works the same way in a lot of SQL implementations.
Also, note that this is a string function, but in your example you're applying it to a number. SAS will let you do this, but in general it's wise to control you conversion between strings and numbers with put() and input() functions to keep your log clean and be sure that you're only converting where you actually intend to.
You might be looking for SUBSTRN function..
SUBSTRN(string, position <, length>)
Arguments
string specifies a character or numeric constant, variable,
or expression.
If string is numeric, then it is converted to a character value that
uses the BEST32. format. Leading and trailing blanks are removed, and
no message is sent to the SAS log.
position is an integer that specifies the position of the first
character in the substring.
length is an integer that specifies the length of the substring. If
you do not specify length, the SUBSTRN function returns the substring
that extends from the position that you specify to the end of the
string.
As others have pointed out, substr() is the function you are looking for, although I feel that a more useful answer would also 'teach you how to fish'.
A great way to find out about SAS functions is to google sas functions by category which at the time of writing this post will direct you here:
SAS Functions and CALL Routines by Category
It's worth scanning through this list at least once just to get an idea of all of the functions available.
If you're after a specific version, you may want to include the SAS version number in your search. Note that the link above is for 9.2.
If you have scanned through all the functions, and still can't find what you are looking for, then your next option may be to write your own SAS function using proc fcmp. If you ever need assistance with doing this than I suggest posting a new question.

Avoid converting numbers to characters in Erlang

I am having trouble with Erlang converting listed numbers to characters whenever none of the listed items could not also be representing a character.
I am writing a function to separate all the numbers in a positive integer and put them in a list, for example: digitize(123) should return [1,2,3] and so on.
The following code works fine, except when the list only consist of 8's and/or 9's:
digitize(_N) when _N =:= 0 -> [];
digitize(_N) when _N > 0 -> _H = [_N rem 10], _T = digitize(_N div 10), _T ++ _H.
For example: Instead of digitize(8) returning [8], it gives me the nongraphic character "\b" and digitize(89) returns "\b\t". This is only for numbers 8 and 9 and when they're put alone inside the list. digitize(891) will correctly return [8,9,1] for example.
I am aware of the reason for this but how can I solve it without altering my result? (ex: to contain empty lists inside the result like [[],[8]] for digitize(8)).
If you look at comments you will see that it is more problem of the way shell prints your data, than the data itself. You logic is wright and I would not change it. You could introduce some use of io:format/2 into you code, but I guess that it would make it harder to use this function in other parts of code.
Other way around it is changing the shell settings itself. There is shell:strings/1 functions that disables printing lists as stings, and it should do exactly what you want. Just remember to change it back when you finish with your shell stuff, since it could introduce some confusion when you will start using some "string" returning functions.