DATA proj4.gasQTR;
SET proj4.gasQTR;
INPUT Q1 Q2 Q3 Q4;
IF MONTH = 1 or 2 or 3 THEN Q1 = 1;
ELSE IF MONTH = 4 or 5 or 6 THEN Q2 = 2;
ELSE IF MONTH = 7 or 8 or 9 THEN Q3 = 3;
ELSE IF MONTH = 10 or 11 or 12 THEN Q4 = 4;
quarter = MONTH; FORMAT Quarter qtrw.;
RUN;
I am trying to get a 1-4 value for each qtr of each year, my error comes from Quarter qtrw. 'ERROR 388-185 Expecting an arithmetic operator'
*Data is already in 1-4 format for the month variable
What am I doing wrong?
Any help would be appreciated!
Thank you!
You normally do not use both a SET statement to retrieve data from an existing dataset and an INPUT statement to read values from a text file in the same data step. And if you do want to INPUT values from a text file you must tell SAS where to find the text by including either an INFILE statement or add the text in-line with the code by using a DATALINES (or CARDS) statement.
SAS will consider any number that is not zero or missing as TRUE. So the condition 2 or 3 or 4 is always TRUE. So Q1 will always be set to 1 and Q2, Q3 and Q4 will always be missing (or if they existed already unchanged). If you want to test if a variables has any of a number of values use the IN operator instead of the equality operator. month in (1 2 3 4)
You also should not be reading and writing the same dataset. If there are logic issues in your coding you might destroy the original dataset. So hopefully you have backup copy of proj4.gasQTR, or a program that can recreate it.
What is the format QTRW ? Is that something you created? Show its definition.
Assuming you have a variable named MONTH with integer values in the range 1 to 12 you can calculate QUARTER with integer values in the range 1 to 4 with a simple arithmetic function instead of coding a series of IF conditions.
data want;
set have;
quarter = ceil(month/3) ;
run;
If you actually have a DATE variable then perhaps all you were supposed to do was use the MONTH or QTR format to display the dates as the month number or quarter number that they fall into.
Try this program to see the impact of applying different formats to the same values.
data test;
do month=1 to 12;
date1=mdy(month,1,2022);
date2=date1;
date3=date1;
output;
end;
format date1 date9. date2 month. date3 qtr.;
run;
proc print;
run;
Use the in operator or repeat the equality for every case.
Example from the doc:
You can use the IN operator with character strings to determine whether a variable's value is among a list of character values. The following statements produce the same results:
if state in ('NY','NJ','PA') then region+1;
if state='NY' or state='NJ' or state='PA' then region+1;
Therefore
DATA proj4.gasQTR;
SET proj4.gasQTR;
IF MONTH = 1 or MONTH = 2 or MONTH = 3 THEN Q1 = 1;
ELSE IF MONTH = 4 or MONTH = 5 or MONTH = 6 THEN Q2 = 2;
ELSE IF MONTH = 7 or MONTH = 8 or MONTH = 9 THEN Q3 = 3;
ELSE IF MONTH = 10 or MONTH = 11 or MONTH = 12 THEN Q4 = 4;
quarter = MONTH; FORMAT Quarter qtrw.;
RUN;
is equivalent to
DATA proj4.gasQTR;
SET proj4.gasQTR;
IF MONTH in (1,2,3) THEN Q1 = 1;
ELSE IF MONTH in (4,5,6) THEN Q2 = 2;
ELSE IF MONTH in (7,8,9) THEN Q3 = 3;
ELSE IF MONTH in (10,11,12) THEN Q4 = 4;
quarter = MONTH; FORMAT Quarter qtrw.;
RUN;
I am very new to SAS and want to create a simple dummy variable (MALE) that equals 1 if SEX = 1, and equals 0 if SEX = 2. However, I get error messages: ERROR: The decimal specification of 2 must be less than the width specification of 1.
How do I solve this? This is the code I use:
DATA WORK.BMI_D ;
SET WORK.BMI ;
IF SEX = 1 THEN MALE = 1;
ELSE MALE = 0;
RUN;
The variable SEX has length 8, type Numeric and format F8.2. What am I doing wrong?
You have not showed the code that is generating that error message but why not just remove the illogical format that you have attached to the variable SEX. Perhaps the error is from later step that is trying to display SEX with a width of only 1 byte and is having trouble display the strings like 1.00 or 2.00 that the F8.2 format would generate.
Since there is no need to use a special display format for numeric values of 1 and 2 just remove the format from SEX and see if that solves the issue.
DATA WORK.BMI_D ;
SET WORK.BMI ;
IF SEX = 1 THEN MALE = 1;
ELSE MALE = 0;
format sex ;
RUN;
Three columns are depicted in the picture - abc, def, and xyz. The next two columns are dervied from these 3 columns. min column calculates the min value out of these 3 columns. mincol column should give the column name which belongs to the min value.
You can
Use the VNAME function to retrieve the variable name of an element in a variable array.
Use the WHICHN function to find the index of the first occurring value in an array
Use the MIN function to find the minimum value in an array
Put them all together, such as:
data have;
input abc def xzy;
length min 8 mincol $32;
datalines;
1 2 3
2 0 3
-1 0 -3
;
data want;
set have;
array values abc def xzy;
min = min(of values(*));
mincol = vname(values(whichn(min,of values(*))));
run;
I have a table with list of account numbers bucket n monthperiod. I need to make a bucket string like below..please help (base SAS)
ACC Bucket Month bucketstring
123 0 jan18 0
123 1 feb18 10
123 2 mar18 210
345 0 feb18 0
345 1 mar18 10
The retain statement is used to maintain the value of a non-set variable over the iterations of the implicit loop that happens during the DATA step.
This example will work with ACC groups having upto 15 months (0..15). ACCs with more months will see a message put in the log.
data want;
set have;
by ACC;
length bucketstring $20; * bucketstring might have to be made longer;
retain buckstring;
if length (bucketstring) = 20 and not first.ACC then
put 'ERROR: bucketstring has to be longer for the case of ' ACC= month=;
if first.ACC
then bucketstring = cats(month);
else bucketstring = cats(bucketstring,month);
run;
The cats function concatenates items. The items are automatically stripped of leading and trailing spaces, as well as automatically converting a number-item to a character value if necessary.
I'm trying to convert a character string to a numeric variable and then sum the values of each character to use as a unique identifier for that field.
So for example, I would like A=1, B=2, C=3.....X=24 Y=25 Z=26.
Say my string is "CAB" so after running the code I would like the result to be an intermidiary column of numbers, where the value for CAB IS 3 1 2 and the result column would be derived by summing the string 3+1+2= 6 and show the value of the intermideate column, so the final value woud be 6.
Here is the sas code I used to convert the characters to numbers, but I need help with the result column.
DATA CHAR_VALUE;
SET WORK.XYZ;
CHAR_2_NUM=TRANSLATE(MY_VAR_CHAR, '1 2 3 ...24 25 26', 'A B C ...X Y Z');
NUM_CHAR=INPUT(CHAR_2_NUM,32.);
RUN;
Thanks in advance...I appreciate any help or suggestions.
-rachel
RANK will give the ASCII numeric value underlying a character; so A=65, B=66, Z=90, a=97, z=122.
So this should work (if you want only the uppercase values - not a different value for a than A):
data test;
charval='CAB';
do _t=1 to length(Charval);
numval=sum(numval,rank(char(upcase(charval),_t))-64);
end;
put _all_;
run;
Another option (Based on the comments below), is to build an informat with the relationships between letter and value. My loop iterates over each character A to Z, you can then put whatever value you want for each letter as label (I just put 1,2,3,4... but label= will change that).
data fmts;
retain fmtname 'CHARNUM' type 'i';
do _t=65 to 90;
start=byte(_t); *the character, so byte(65)='A';
label=_t-64; *the resulting number;
output;
end;
run;
proc format cntlin=fmts;
quit;
data test;
charval='CAB';
do _t=1 to length(Charval);
numval=sum(numval,input(char(upcase(charval),_t),CHARNUM.));
end;
put _all_;
run;
Finally, if you want to be able to construct this in the same datastep, you could construct the relationships in a hash table and look up the result. I can explain that if desired, though I'd like to see a more detailed example of what you want to do in terms of defining the relationship between a letter and its code.
If you need to see the intermediate values, you can do that by inserting a CAT function in the loop- I recommend CATX:
data test;
charval='CAB';
format intermed $100.;
do _t=1 to length(Charval);
numval=sum(numval,input(char(upcase(charval),_t),CHARNUM.));
intermed=catx('|',intermed,input(char(upcase(charval),_t),CHARNUM.)); *or the RANK portion from earlier;
end;
put _all_;
run;
That would give you 3|1|2, which you could then do math on via SCAN:
do _t = 1 to countc(intermed,'|')+1;
numval2 = sum(numval2,scan(intermed,_t,'|'));
end;
Your method to try and translate is a good attempt, but it will not really work. Here is a simple solution:
DATA CHAR_VALUE;
retain all_chars 'ABCDEFGHIJKLMMOPQRSTUVXXYZ';
set XYZ;
length CHAR_2_NUM $200;
CHAR_2_NUM = ' ';
NUM_CHAR = 0;
do i=1 to length(MY_VAR_CHAR);
if i=1 then CHAR_2_NUM = substr(MY_VAR_CHAR,i,1);
else CHAR_2_NUM = trim(CHAR_2_NUM) || ' ' || substr(MY_VAR_CHAR,i,1);
NUM_CHAR + index(all_chars,substr(MY_VAR_CHAR,i,1));
end;
drop i all_chars;
RUN;
This takes advantage of the fact that the indexed position of each character of your source variable in the all_chars variable corresponds to the mapping you desired.
UPDATED to also create your CHAR_2_NUM variable, which I overlooked in the original question.
Another simple solution is based on the collate function:
To convert a variable called MyNumbers (in the range of 1 to 26) to English upper-case characters, one can use:
collate(64 + MyNumbers, 64 + MyNumbers)
To obtain lower-case characters, one can use:
collate(96 + MyNumbers, 96 + MyNumbers)
Here's a quick example:
data _null_;
do MyNumbers = 1 to 26;
MyLettersUpper = collate(64 + MyNumbers, 64 + MyNumbers);
MyLettersLower = collate(96 + MyNumbers, 96 + MyNumbers);
put MyNumbers MyLettersUpper MyLettersLower;
end;
run;
1 A a
2 B b
3 C c
4 D d
5 E e
6 F f
7 G g
8 H h
9 I i
10 J j
11 K k
12 L l
13 M m
14 N n
15 O o
16 P p
17 Q q
18 R r
19 S s
20 T t
21 U u
22 V v
23 W w
24 X x
25 Y y
26 Z z
NOTE: DATA statement used (Total process time):
real time 0.03 seconds
cpu time 0.03 seconds