CASE WHEN statements not returning a false - sas

I am trying to create a flag that shows a 1, when a variable match_flg = total_match_flg, otherwise return a 0.
When i run the following code
proc sql;
create table xxxxxxx as
select*,
CASE
when match_flg = total_match_flg then 1 else 0
end as keep_flg
quit;
it returns all 1 and am sure in the dataset that statement should false and return some 0
What am i doing wrong ?

Is it because you're not reading any data in with a from statement?
I ran similar code (added a from) and it ran fine.
Edit: Including my test data;
data test;
do i = 1 to 10;
match_flag = i;
total_match_flag = 10-i;
output;
end;
drop i;
run;
proc sql;
create table x as
select *,
case
when match_flag = total_match_flag then 1 else 0
end as keep_flg
from test;
quit;
As a sidenote, case can be clumsy to use. Have a look at the IFC/IFN functions instead.
http://www.lexjansen.com/wuss/2012/28.pdf

Related

Populate SAS macro-variable using a SQL statement within another SQL statement?

I stumbled upon the following code snippet in which the variable top3 has to be filled from a table have rather than from an array of numbers.
%let top3 = 14 15 42; /* This should be made obsolete.. */
%let no = 3;
proc sql;
create table want as
select *
from (select x, y from foo) a
%do i = 1 %to &no.;
%let current = %scan(&top3.,&i.); /* What do I need to put here? */
left join (select x, y from bar where z=&current.) row_&current.
on a.x = row_&current..x
%end;
;
quit;
The table have contains the xs from the string and looks as follows:
i x
1 14
2 15
3 42
I am now wondering how I should modify the %let current = ... line such that current is populated from the table have. I know how to populate a macro variable using proc sql with select .. into, but I am afraid that the way I am going right now is fully against SAS philosophy.
It looks like you're more or less transposing something. If that's the case, this is doable in macro/sql pretty easily.
First, here's the simple version - no macro.
proc sql;
create table class_t as
select * from (
select name from sashelp.class ) class
left join (
select name, age as age_Alfred
from sashelp.class
where name='Alfred') Alfred
on class.name = Alfred.name
;
quit;
We grab the value of age from the Alfred row and put it on the main join. This isn't exactly what you're doing, but it seems similar. (I'm just using one table, but you can of course use two here.)
Now, how do we extend this to be table-driven and not handwritten? Macros!
First, here's the macro - just taking the Alfred bit and making it generic.
%macro joiner(name=);
left join (
select name, age as age_&name.
from sashelp.class
where name="&name.") &name.
on class.name = &name..name
%mend joiner;
Second, we look at this and see two things we need to put into macro lists: the SELECT variable list (we'll get one new variable for each call), and the JOIN list.
proc sql;
select cats('%joiner(name=',name,')')
into :joinlist separated by ' '
from sashelp.class;
select cats(name,'.age_',name)
into :selectlist separated by ','
from sashelp.class;
quit;
And then, we just call it!
proc sql;
create table class_t as
select class.name,&selectlist. from (
select name from sashelp.class) class
&joinlist.
;
quit;
Now, your dataset you call the macro lists from is perhaps the dataset with the 3 rows in it you have above ("have"). The dataset you actually get the appending data from is some other dataset ("bar"), right? And then the ones you join to is perhaps a third dataset ("foo"). Here I just use the one, for simplicity, but the concept is the same, just different sources.
When the lookup data is in a table you can perform a three way join without any need for SAS Macro. You don't provide any data so the example will mock some.
Example:
Suppose a master record has several associated detail records, and the detail records contain a z value used for selection into a result set per a wanted z lookup table.
data masters;
call streaminit(2020);
do id = 1 to 100;
do x = 1 to 100;
m_rownum + 1;
code = rand('integer', 10,45);
output;
end;
end;
run;
data details;
call streaminit(2020);
do date = 1 to 20;
do x = 1 to 100;
do rep = 1 to 5;
d_rownum + 1;
amount = rand('integer', 100,200);
z = rand('integer', 10,45);
output;
end;
end;
end;
run;
data zs;
input z ##; datalines;
14 15 42
;
proc sql;
create table want as
select
m_rownum
, d_rownum
, masters.id
, masters.x
, masters.code
, details.z
, details.date
, details.amount
from
masters
left join
details
on
details.x = masters.x
inner join
zs
on
zs.z = details.z
order by
masters.id, masters.x, details.z, details.date
;
quit;

How can I update a series of tables with values from one table in SAS?

I'd like to update a series of tables with values from just one table. I'm trying something like this:
%let list_counter = 3;
%macro update_list_view;
proc sql;
%do i=1 %to &list_counter;
update temp.rule_list_value_&i a
set attribute_code = (select attribute_code from
temp.country b
where a.primary_entity_key = b.primary_entity_key);
%end;
quit;
%mend update_list_view;
Thanks in advance!
You will want to add an existential qualifier for the rows that are to be updated.
...
where exists ( <sub-select> )
In this case, the sub-select can be the same as the set assignment select. The update requires the table temp.country be unique in primary_entity_key.
Example:
data have_1 have_2 have_3;
do key = 1 to 10;
code = key*2; output have_1;
code = key*10; output have_2;
code = 100+key; output have_3;
end;
run;
* updated code for key to be applied;
data new_codes;
key = 5; code = 555; output;
key = 9; code = 999; output;
run;
%macro update_code(base=, n=, data=);
%local i;
proc sql;
%do i = 1 %to &n;
update &&base.&i as outer
set code = ( select code from &data as inner
where inner.key = outer.key
)
where exists
( select code from &data as inner
where inner.key = outer.key
)
;
%end;
quit;
%mend;
%update_code(base=have_, n=3, data=new_codes);
%let syslast=have_1;
Will Log
NOTE: 2 rows were updated in WORK.HAVE_1.
NOTE: 2 rows were updated in WORK.HAVE_2.
NOTE: 2 rows were updated in WORK.HAVE_3.

How can I add id-s for variables with a specific conditions?

In SAS I'd like to add id values to the variables with a specific conditions. I have the following code:
DATA market_new;
SET sashelp.cars;
if Make = 'Audi' then id = 0;
else id = _N_;
RUN;
proc print data=market_new;
run;
Output:
The problem is that the id continues with 27, 28 etc. after the make isn't equal to Audi. My goal is to have 8, 9 instead.
Use a SUM (+) statement to track the Audis.
if make='Audi' then do;
audi_seq + 1; drop audi_seq;
audi_id = audi_seq;
end;
else
audi_id = 0;

check all entries in a column sas and return a dummy variable sas

I am new in SAS and want to check if all entries in a variable in a data set satisfy a condition (namely =1) and return just one dummy variable 0 pr one depending whether all entries in the variable are 1 or at least one is not 1.
Any idea how to do it?
IF colvar = 1 THEN dummy_variable = 1
creates another variable dummy_variable of the same size as the original variable.
Thank you
* Generate test data;
data have;
colvar=0;
colvar2=0;
do i=1 to 20;
colvar=round(ranuni(0));
output;
end;
drop i;
run;
* Read the input dataset twice, first counting the number
* of observations and setting the dummy variables to 1 if
* the corresponding variable has the value 1 in any obser-
* vation, second outputting the result. The dummy variables
* remain unchanged during the second loop.;
data want;
_n_=0;
d_colvar=0;
d_colvar2=0;
do until (eof);
set have end=eof;
if colvar = 1
then d_colvar=1;
if colvar2 = 1
then d_colvar2=1;
* etc.... *;
_n_=_n_+1;
end;
do _n_=1 to _n_;
set have;
output;
end;
run;
PROC SQL is a good tool for quickly generating a summary of an arbitrarily defined condition. What exactly your condition is is not clear. I think you want the ALL_ONE value in the table the code below generates. That will be 1 when every observation has COLVAR=1. Any value that is NOT a one will cause the condition to be false (0) and so ALL_ONE will then have a value of 0 instead of 1.
You could store the result into a small table.
proc sql ;
create table check_if_one as
select min( colvar=1 ) as all_one
, max( colvar=1 ) as any_one
, max( colvar ne 1 ) as any_not_one
, min( colvar ne 1 ) as all_not_one
from my_table
;
quit;
But you could also just store the value into a macro variable that you could easily use later for some purpose.
proc sql noprint ;
select min( colvar=1 ) into :all_one trimmed from my_table ;
quit;

How to calculate a mean for the non zero values using proc means or proc summary

I want to have a mean which is based in non zero values for given variables using proc means only.
I know we do can calculate using proc sql, but I want to get it done through proc means or proc summary.
In my study I have 8 variables, so how can I calculate mean based on non zero values where in I am using all of those in the var statement as below:
proc means = xyz;
var var1 var2 var3 var4 var5 var6 var7 var8;
run;
If we take one variable at a time in the var statement and use a where condition for non zero variables , it works but can we have something which would work for all the variables of interest mentioned in the var statement?
Your suggestions would be highly appreciated.
Thank you !
One method is to change all of your zero values to missing, and then use PROC MEANS.
data zeromiss /view=zeromiss ;
set xyz ;
array n{*} var1-var8 ;
do i = 1 to dim(n) ;
if n{i} = 0 then call missing(n{i}) ;
end ;
drop i ;
run ;
proc means data=zeromiss ;
var var1-var8 ;
run ;
Create a view of your input dataset. In the view, define a weight variable for each variable you want to summarise. Set the weight to 0 if the corresponding variable is 0 and 1 otherwise. Then do a weighted summary via proc means / proc summary. E.g.
data xyz_v /view = xyz_v;
set xyz;
array weights {*} weight_var1-weight_var8;
array vars {*} var1-var8;
do i = 1 to dim(vars);
weights[i] = (vars[i] ne 0);
end;
run;
%macro weighted_var(n);
%do i = 1 to &n;
var var&i /weight = weight_var&i;
%end;
%mend weighted_var;
proc means data = xyz_v;
%weighted_var(8);
run;
This is less elegant than Chris J's solution for this specific problem, but it generalises slightly better to other situations where you want to apply different weightings to different variables in the same summary.
Can't you use a data statement?
data lala;
set xyz;
drop qty;
mean = 0;
qty = 0;
if(not missing(var1) and var1 ^= 0) then do;
mean + var1;
qty + 1;
end;
if(not missing(var2) and var2 ^= 0) then do;
mean + var2;
qty + 1;
end;
/* ... repeat to all variables ... */
if(not missing(var8) and var8 ^= 0) then do;
mean + var8;
qty + 1;
end;
mean = mean/qty;
run;
If you want to keep the mean in the same xyz dataset, just replace lala with xyz.