BookmarkSubscribeRSS Feed
🔒 This topic is solved and locked. Need further help from the community? Please sign in and ask a new question.
bigbigben
Obsidian | Level 7

I tried using in function to find observation values matching any value of a given series; however, SAS does not always give me the correct answer and the mistakes seem to happen when the give numbers are in decimals.

 

Here is a piece of sample code:

 

%let nodes=-0.1 0 0.1 0.2;

%let x_min=-0.2;
%let x_max=0.2;
%let x_step=0.01;

data xxx(keep=x nodes_flag);
    nstep=round((&x_max-&x_min)/&x_step);
	do iter=0 to nstep;
	nodes_flag=0;
	x=&x_min+&x_step*iter;
	if x in (&nodes) then nodes_flag=1;
	output;
	end;
run;

data nodes;
  set xxx;
  if nodes_flag=1;
  run;
  proc print data=nodes;
  run;

The dataset "nodes" is suppose to capture the x values if it is any of (-0.1 0 0.1 0.2); however, below is what SAS gives me:

nodes_flag x

1    -0.1
1    0
1    0.2

 

It missed 0.1.

 

If I change the set values to -0.1 0 0.1, SAS gives me:

 

1    -0.1
1    0

 

It still missed 0.1.

 

When I change the set values to be 0.1 0.05 0, SAS can only select 0.

 

Can some one explain how the function works and what I did wrong?

 

Many thanks.

1 ACCEPTED SOLUTION

Accepted Solutions
Astounding
PROC Star

The safest way to eliminate the issue is to deal with integers the whole time.  For example:

 

%let x = -10 0 10 20;

 

%let x_min = -20;

%let x_max = 20;

%let x_step = 1;

 

Then just before the OUTPUT statement:

 

x = x / 100;

View solution in original post

7 REPLIES 7
Kurt_Bremser
Super User

You're doing math with fractions, which can (and will) cause artifacts because of the limitations of the 8-byte real format SAS uses for numbers. Apply the round() function before comparing.

Astounding
PROC Star

The safest way to eliminate the issue is to deal with integers the whole time.  For example:

 

%let x = -10 0 10 20;

 

%let x_min = -20;

%let x_max = 20;

%let x_step = 1;

 

Then just before the OUTPUT statement:

 

x = x / 100;

SASJedi
SAS Super FREQ

SAS numeric values are all double-precision floating point data types, which guarantee only an approximate representation of decimal data. The issues with representing decimal values in floating point are common to all computing platforms.  For a deep dive, check Numerical Accuracy in SAS Software. So, to resolve your problem keeping the values as-is, you need a different way of representing numeric values.

 

Fortunately for you, Base SAS comes with the DS2 language, which is capable of handling DECIMAL fixed-point numerics values. Decimal values provide exact representation of the decimal values.  This DS2 code will provide the results you seek:

%let nodes=-0.1 0 0.1 0.2;
%let x_min=-0.2;
%let x_max=0.2;
%let x_step=0.01;

proc ds2;
data xxx/overwrite=yes;
   dcl decimal(5,2) x;
   dcl int nodes_flag; 
   method run();
      dcl int nstep iter;
      nstep=round((&x_max-&x_min)/&x_step);
   	do iter=0 to nstep;
            nodes_flag=0;
            x=&x_min+&x_step*iter;
            if x in (&nodes) then nodes_flag=1;
            output;
   	end;
   end;
enddata;
run;
quit;

data nodes;
   set xxx;
   if nodes_flag=1;
run;
proc print data=nodes;
run;
Check out my Jedi SAS Tricks for SAS Users
PGStats
Opal | Level 21

Keep everything well rounded with

 

x=round(&x_min+&x_step*iter, &x_step);

 

PG
Kurt_Bremser
Super User

Here a slightly optimized and visually structured version of your code, and expanded with the round() function:

%let nodes=-0.1 0 0.1 0.2;

%let x_min=-0.2;
%let x_max=0.2;
%let x_step=0.01;

data nodes (
  keep=x nodes_flag
  where=(nodes_flag = 1)
);
  nstep = round((&x_max. - &x_min.) / &x_step.);
	do iter = 0 to nstep;
	  nodes_flag = 0;
	  x = round(&x_min. + &x_step. * iter, .01);
	  if x in (&nodes.) then nodes_flag = 1;
	  output;
	end;
run;

proc print data=nodes noobs;
run;

Result:

nodes_
 flag        x

   1      -0.1
   1       0.0
   1       0.1
   1       0.2
ChrisHemedinger
Community Manager

In addition to the good practice info from @Kurt_Bremser (about rounding) and DECIMAL precision tidbits by @SASJedi, I'll offer this.  The IN function is one of those lightly documented functions that many users try to squeeze more uses from than maybe they should.  It's a natural temptation for those familiar with the IN clause in SQL -- but that works differently.

 

Instead, you might be able to get what you want, precision aside, by comparing the formatted values you seek.  Here's your original code adjusted to use of the WHICHN function (which is documented thoroughly).  Note that I added commas to your delimited list of nodes to generate the proper syntax.

 

%let nodes=-0.1, 0, 0.1, 0.2;

%let x_min=-0.2;
%let x_max=0.2;
%let x_step=0.01;

data xxx(keep=x nodes_flag);
    nstep=round((&x_max-&x_min)/&x_step);
	do iter=0 to nstep;
	nodes_flag=0;
	x=&x_min+&x_step*iter;
	if whichn(put(x,best12.), &nodes) then nodes_flag=1;
	output;
	end;
run;

data nodes;
  set xxx;
  if nodes_flag=1;
  run;
  proc print data=nodes;
  run;
It's time to register for SAS Innovate! Join your SAS user peers in Las Vegas on April 16-19 2024.
bigbigben
Obsidian | Level 7

Thank you all. All these solutions taught me something about SAS. I really appreciate all the help.

sas-innovate-2024.png

Don't miss out on SAS Innovate - Register now for the FREE Livestream!

Can't make it to Vegas? No problem! Watch our general sessions LIVE or on-demand starting April 17th. Hear from SAS execs, best-selling author Adam Grant, Hot Ones host Sean Evans, top tech journalist Kara Swisher, AI expert Cassie Kozyrkov, and the mind-blowing dance crew iLuminate! Plus, get access to over 20 breakout sessions.

 

Register now!

How to Concatenate Values

Learn how use the CAT functions in SAS to join values from multiple variables into a single value.

Find more tutorials on the SAS Users YouTube channel.

Click image to register for webinarClick image to register for webinar

Classroom Training Available!

Select SAS Training centers are offering in-person courses. View upcoming courses for:

View all other training opportunities.

Discussion stats
  • 7 replies
  • 1284 views
  • 8 likes
  • 6 in conversation