BookmarkSubscribeRSS Feed
MARY9999
Calcite | Level 5

%MACRO FIND_CLOSEST_VALUE(limOrDedOption,limorDedValue);

     %LET numOptions = %Sysfunc(Countw(&limOrDedOption.));

     limOrDedSetFlag = 'N';

     %DO curLimOrDed = 1 %TO &numOptions.;

          IF limOrDedSetFlag = 'N" AND &curlimOrDed > 1 THEN DO;

               lwrVal = %Scan(&limOrDedOption., &curLimOrDed. - 1);

               uprVal = %Scan(&limOrDedOption., &curLimOrDed.);

               IF &limOrDedValue. < uprVal THEN DO;

                    IF (&limOrDedValue. - lwrVal)/(uprVal - lwrVal) < 0.5 THEN &limOrDedValue = lwrVal;

                    ELSE &limOrDedValue = uprVal;

               END;

          END;

     %END;

%MEND;    

Receiving an error:

  ERROR: A character operand was found in the %EVAL function or %IF condition where a numeric operand is required. The condition was:

prevLimOrDed

ERROR: Argument 2 to macro function %SCAN is not a number.

 

Error occurs on the lwrVal. uprVal is fine. It's almost as if the if condition is not met on the &curLirOrDed > 1

Any ideas?

   

 

16 REPLIES 16
PaigeMiller
Diamond | Level 26

Can we see the relevant portions of the code that you used to call this macro?

--
Paige Miller
MARY9999
Calcite | Level 5

%LET numArray = 200000 300000 500000 1000000 2000000;

DATA TEST;

     SET inforce;

     %FIND_CLOSEST_VALUE(&numArray, testValue);

RUN;

ballardw
Super User

This does not pass the VALUE of testValue variable to the macro, it passes the TEXT value "testValue";

MARY9999
Calcite | Level 5

testValue is actually a dataset column. The macro should check what the value in the testValue column is.

For example, say the value is 60, but acceptable values are 50 and 100.

The macro calculates if 60 is closer to 50 or 100, and based on that, resets the testValue to 50.

jh_ti_bw
Obsidian | Level 7

there are different Errors.

  1. the macro does not do, what is intended. The limOrDedSetFlag was not changed, in the loop, if the Operation was succesfull. So always writes the next loop over the result of the previous loop.
  2. the Expression "&curLimOrDed. - 1" in the %scan-function must be "%eval(&curLimOrDed. - 1)"
  3. The test is very complicated. Why do you not simply compare the differences from  limorDedValue to lower bound and upper bound.

%MACRO FIND_CLOSEST_VALUE(limOrDedOption,limorDedValue);

%LET numOptions = %Sysfunc(Countw(&limOrDedOption.,%str( )));

limOrDedSetFlag = 'N';

  /* probe of less or equal as the smallest value */

if &limOrDedValue. <= %Scan(&limOrDedOption., 1,%str( )) then do;

      &limOrDedValue. = %Scan(&limOrDedOption., 1,%str( ));

           limOrDedSetFlag = 'Y';

end;

    /* probe of equal or greater as the highest value */

else if &limOrDedValue. >= %Scan(&limOrDedOption., -1,%str( )) then do;

   &limOrDedValue. = %Scan(&limOrDedOption., -1,%str( ));

   limOrDedSetFlag = 'Y';

end;

else do;

/* the value must be between 2 values or equal a value of the list*/

      %DO curLimOrDed = 2 %TO &numOptions.;

      IF limOrDedSetFlag = 'N' THEN DO;

        lwrVal =%Scan(&limOrDedOption., %eval(&curLimOrDed. - 1));

      uprVal =%Scan(&limOrDedOption., &curLimOrDed.);

      IF (lwrval <= &limOrDedValue.) and (&limOrDedValue.) <= uprVal THEN DO;

        IF (&limOrDedValue. - lwrVal) < (uprVal - &limOrDedValue.)

          THEN &limOrDedValue. = lwrVal;

                   ELSE  &limOrDedValue. = uprVal;

        limOrDedSetFlag = 'Y';

      END;

    END;

  %END;

end;

%MEND;   

/* usage */

%LET
numArray = 200000 300000 500000 1000000 2000000;

optionsmprint;

DATA TEST;

   testvalue1=  34000;

   testvalue2= 340000;

   testvalue3=3400000;

%FIND_CLOSEST_VALUE(&numArray., testValue1);

%FIND_CLOSEST_VALUE(&numArray., testValue2);

%FIND_CLOSEST_VALUE(&numArray., testValue3);

RUN;

RW9
Diamond | Level 26 RW9
Diamond | Level 26

Hi,

Have you tried:

               lwrVal = %Scan(&limOrDedOption., %eval(&curLimOrDed. - 1));

If so maybe put options mlogic mprint symbolgen; on and see what the macro variables resolve to.

MARY9999
Calcite | Level 5

yes, I tried %eval and i tried put(&curLimOrDed,1.) - 1. I am receiving the exact same error =(

RW9
Diamond | Level 26 RW9
Diamond | Level 26

Well, couple of things:

- You have a typo in the macro, IF limOrDedSetFlag = 'N" AND - double quotes after the N.

- Secondly you only want to do the %do loop from 2 to numoptions, other 1 with -1 = 0 which is an invalid scan array.

Note, can't actually test this as no data provided.

So change: %DO curLimOrDed = 2 %TO &numOptions.;

MARY9999
Calcite | Level 5

For some reason cannot copy paste in this editor. I typed 'N" up, it is actually a 'N'.

As for the second comment, I have a condition to execule when &curLimOrDed = 1 and when &curLimOrDed > 1. The

%Scan(&limOrDedOption, &curLimOrDed. -1 ) executes only when :

  IF limOrDedSetFlag = 'N" AND &curlimOrDed > 1 THEN DO;

RW9
Diamond | Level 26 RW9
Diamond | Level 26

Ok, another thought, what is the prevLimOrDed, mentioned in the error message, I don't see this in the macro or code.  Also, have you tried putting options mlogic mprint symbolgen; on and seeing what is being generated?

MARY9999
Calcite | Level 5

The prevLimOrDed was me testing. sorry, removed it, still getting an error:

           

               

RW9
Diamond | Level 26 RW9
Diamond | Level 26

Perhaps start again, post the actual code (including call), the actual log text with the error, and if possible some test data which triggers the error.  Will have a look tomorrow.

art297
Opal | Level 21

MARY9999: Do you have to use a macro for this? It can be done easily within a datastep. E.g.:

DATA TEST;

     SET sashelp.class (rename=(age=testValue));

     array limOrDedOption(8) _temporary_ (10.4,11.6,12.4,13.6,14.4,15.6,16.4,17.6);

     do _n_=2 to dim(limOrDedOption);

       if limOrDedOption(_n_) ge testValue then do;

         if testValue+(limOrDedOption(_n_)-limOrDedOption(_n_-1))/2 gt limOrDedOption(_n_)

          then closest=limOrDedOption(_n_);

         else closest=limOrDedOption(_n_-1);

         leave;

       end;

       else if _n_ eq dim(limOrDedOption) then closest=limOrDedOption(_n_);

     end;

RUN;

MARY9999
Calcite | Level 5

Ideally yes, i have t odo this for 20 different variables.

sas-innovate-2024.png

Join us for SAS Innovate April 16-19 at the Aria in Las Vegas. Bring the team and save big with our group pricing for a limited time only.

Pre-conference courses and tutorials are filling up fast and are always a sellout. Register today to reserve your seat.

 

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
  • 16 replies
  • 1478 views
  • 0 likes
  • 6 in conversation