Decimal to Hex to Decimal - sas

im reading in some data where numbers that were originally hex have been read in as decimal.
As these hex examples are all also valid decimal numbers, when I have read them in from a text file SAS has read then as decimal.
Examples are 53 and 68, which are the hex numbers for 83 and 104 decimals respectively.
Using the hex2. format converts 53 and 68 into hex numbers (68 becomes 44 for example), which is not what I want.
Is there away to get SAS to recognise 53 and 68 as hex without trying to convert them to hex?

Use the informat hexw.. Recall that informats and formats are different.
Informat
An informat is a type of SAS language element that applies a pattern to or executes instructions for a data value to be read as input.
Format
A format is a type of SAS language element that applies a pattern to
or executes instructions for a data value to be displayed or written
as output.
A good way to remember it:
A format is how you see it
An informat is how SAS internally sees it
Explanation
Informats are used to convert input values to something that SAS can appropriately perform mathematical operations on. For example,
01JAN2015
By itself, it's just a character string. SAS doesn't know how to perform math on 01JAN2015, and it will read the value in as a character string automatically. But, SAS does know how to interpret what that string represents. You just need to tell SAS to interpret it in a certain way.
For SAS to do math on dates, it uses a reference date of 1/1/1960. By applying the informat date9., SAS is able to understand that the string represents the 20,089th day since 1/1/1960. The output will then look as such:
20089
That's hard for people to read. You can apply the mmddyy10. format to this so that you can read it, but SAS can also understand it. Format will not change what SAS sees. The value is still stored as a decimal, but the way it's displayed to you is more readable.
**To SAS** **To you**
date date
20089 01/01/2015
In your case, SAS knows the value you're reading is numeric. It will automatically assume it's a decimal, but we know for a fact that it is not.
Following the above logic, we want to apply an informat to the numeric values so SAS will interpret the values as hex, and not decimals.
data want;
informat hex_value hex2.;
input hex_value;
datalines;
53
68
;
run;
Your table will look as such:
hex_value
83
104
If you apply the format hex2., you'll see the output table as hex again. Since we've already interpreted the value, we're just changing how it looks to you, but not how it looks to SAS:
**To SAS** **To you**
hex_value hex_value
83 53
104 68

Related

Wrong date output

I am trying to convert simple date to date9. format.
%let snapshot_date=201806;
%let dt0=%sysfunc(intnx(month,%sysfunc(inputn(&snapshot_date.,yymmn6.)),0,b),yymmn6.);
data new;
set sample;
format cutoff_date date9.;
cutoff_date=input(&dt0.,anydtdte11.);
run;
I am getting cutof_date as 28jun2020 instead of 30jun2018. Is iam doing anything wrong here.
So the macro statements start with a YYYMM string. Convert it to the first day of the month using INPUTN() function. Then convert it from that date back to exact same date using INTNX() function with an interval of zero. (Perhaps in your real problem the interval is not zero?). Then convert it back to a new YYYYMM string.
The SAS code you are generating is :
cutoff_date=input(201806,anydtdte11.);
That is trying to convert the number 201,806 into a date using the ANYDTDTE11. informat. Since the INPUT() function needs a string and not a number as its input SAS will convert the number 201,806 into a string using the BEST12. format. So it runs this code:
cutoff_date=input(" 201806",anydtdte11.);
The ANYDTDTE informat has to decides to map those 6 characters into month, day and year so it splits into three parts 20 18 06. Since the first two are larger than 12 one must be day and the other year. It decides it is Y/D/M order. Not sure why as I have never seen that order used in real life.
Instead use the same informat in the SAS code that was used in the macro code. So to convert the string 201806 in SAS code you would use either of these statements:
cutoff_date=input("201806",yymmn6.);
cutoff_date=inputn("201806","yymmn6.");
To generate that from your macro variable you need to add the quotes. So use:
cutoff_date=input("&dt0.",yymmn6.);
SAS interprets dates as the number of days since Jan 1st 1960, and you are supplying a number to the input function which is designed to convert characters to numbers. anydtdte. is interpreting it incorrectly as a result. Put quotes around &dt0 and use the yymmn6. informat instead so that SAS converts it to a date correctly.
data new;
format cutoff_date date9.;
cutoff_date=input("&dt0.",yymmn6.);
run;
Output:
cutoff_date
01JUN2018
anydtdte. will not work here since yymmn6. is not in the list of formats it tries to read. A list of the date types it will read is located here:
https://go.documentation.sas.com/doc/en/pgmsascdc/9.4_3.5/leforinforref/n04jh1fkv5c8zan14fhqcby7jsu4.htm?homeOnFail

SAS Numeric Informat vs Length

I'm trying to determine how SAS is reading the length statement and then the informat statement. I could be misunderstanding, but I'm under the impression that the informat statement for numeric variables worked like this:
informat number 5.;
This would give the variable number the informat 5, allowing 5 numbers to fill it. E.G. 12345
However, when I run the below program, I have a number that has 9 digits, 987654321, with the appropriate length to fit the digits, 6, which will represent all numbers up to 137,438,953,472
Q: is length statement 'overriding' the informat statement and allowing all 9 digits to fill the variable number? How are all 9 digits able to fit in the variable number with an informat of 5.?
data tst;
input number;
length number 6;
informat number 5.;
datalines;
987654321
;
run;
proc print data=tst;
run;
Based on this SAS documentation:
http://support.sas.com/documentation/cdl/en/lrdict/64316/HTML/default/viewer.htm#a000199348.htm
w specifies the width of the input field. Range: 1-32
It would seem that the informat w.d would work as I first described and not allow all 9 digits to fill number
Because you are using list mode input. In that situation SAS reads the next word, however long it is. Essentially in list mode input (including when using the : modifier before an informat specified in the input statement) the width on a informat is ignored.
Other than for creating metadata in the SAS dataset there is not much value in attaching informats like 5. or $10. to variables.
SAS does not need them to understand how to convert text into values, unlike informats like date..
In list mode it ignores the width part.
And in formatted input, where the width matters, you have to specify the informat in the INPUT statement itself.
First off: length is not overriding, or having any impact on, the informat or the read-in. length solely describes how many bytes are used to store the number, nothing more.
For numeric variables, informats don't work quite the intuitive way. I'm not sure why - but they don't.
See this quotation from the list input documentation:
For a character variable, this format modifier reads the value from the next non-blank column until the pointer reaches the next blank column, the defined length of the variable, or the end of the data line, whichever comes first. For a numeric variable, this format modifier reads the value from the next non-blank column until the pointer reaches the next blank column or the end of the data line, whichever comes first.
They do listen to the informat to some extent - add a .2 there and you'll get a forced decimal - but they don't listen to it as to how long of a value to read in. I'm not sure why; it seems intuitive that they should, but they don't.
Here's it with character variables - they respect the length but also ignore the informat:
data tst;
length number $9;
informat number $5.;
input number;
datalines;
987654321
;
run;
proc print data=tst;
run;
Though you do need to put the informat before the input statement (and the length for numeric variables).
More detail is available on the documentation page for INFORMAT:
How SAS Treats Variables When You Assign Informats with the INFORMAT Statement
Informats that are associated with variables by using the INFORMAT statement behave like informats that are used with modified list input. SAS reads the variables by using the scanning feature of list input, but applies the informat.
In modified list input, SAS
does not use the value of w in an informat to specify column positions or input field widths in an external file
uses the value of w in an informat to specify the length of previously undefined character variables
ignores the value of w in numeric informats
uses the value of d in an informat in the same way it usually does for numeric informats
treats blanks that are embedded as input data as delimiters unless you change their status with a DLM= or DLMSTR= option specification in an INFILE statement.
That is much more explicit about the fact that SAS ignores the value of w.
The length of a variable defines the amount of space the value occupies when stored to disk. NOTE: During a running DATA step all numerics are double precision, the truncation to a length < 8 only occurs during output media.
The informat is a separate concept from the length. Informat defines how incoming value representations are to be interpreted for storage as a SAS numeric value. Incoming value representations would be what ever text has to be processed; be it a INPUT statement reading a file, a VIEWTABLE field edit processing a typed in value, an EG grid cell edit, etc...
The format is similarly separate concept that defines how SAS renders a numeric value for output; be it a PUT statement, a VIEWTABLE row render, a placement in a PROCs output, an EG grid cell, etc...
Explanation
Now that that is out of the way, The informat is honored when explicitly stated in an INPUT statement:
data _null_;
attrib number length=6 informat=5.;
input number 5.;
put 'NOTE: ' number=;
datalines;
987654321
run;
===== LOG =====
NOTE: number=98765
And, as you question, the variables associated informat is not applied an explicit numeric informat is not stated
data _null_;
attrib number length=6 informat=5.;
input number;
put 'NOTE: ' number=;
datalines;
987654321
run;
===== LOG =====
NOTE: number=987654321
So the first is LIST input with format specified and the second is a simple LIST input (because no format is specified).
Simple list input will accept some absurdly large data, and the resultant value, while not tail-end precise, will be at the correct exponential level.
data _null_;
attrib number length=6 informat=5.;
input number;
put 'NOTE: ' number= ;
datalines;
123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
run;
===== LOG =====
NOTE: number=1.2345679E89
What do the docs for INPUT Statement, List say ? Certainly nothing about using the variables declared informat when none indicated
Simple List Input
Simple list input places several restrictions on the type of data that
the INPUT statement can read:
• By default, at least one blank must separate the input values. Use
the DLM= or DLMSTR= option or the DSD option in the INFILE statement
to specify a delimiter other than a blank.
• Represent each missing value with a period, not a blank, or two
adjacent delimiters.
• Character input values cannot be longer than 8 bytes unless the
variable is given a longer length in an earlier LENGTH, ATTRIB, or
INFORMAT statement.
• Character values cannot contain embedded blanks unless you change
the delimiter.
• Data must be in standard numeric or character format. (footnote 1)
FOOTNOTE 1: See SAS Language Reference: Concepts for the information about standard and nonstandard data values. (my LOL)
The concepts for "SAS Variable Attributes" states
informat
refers to the instructions that SAS uses when reading data values. If
no informat is specified, the default informat is w.d for a numeric
variable, and $w. for a character variable. You can assign SAS
informats to a variable in the INFORMAT or ATTRIB statement. You can
use the FORMAT procedure to create your own informat for a variable.
(my bold)
Apparently there is no explicit default such as 32. or best32. because values with more than 32 digits will be inputted without error.
So does the documentation explain things ? Yea, well, sorta. What are the take aways:
The human intuition of a numeric variable inheriting its informat during simple list input does not align with the actual implemented behavior.
Tectonic amounts of existing SAS code means a change to implement this intuition is highly unlikely
Simple statements can involve a lot of concepts with wide ranging documentation
Possible change is that the documentation will be updated to be more explicit about the simple list input caveats

w.d format incorrectly handling integers

I have a column which range from 0 to 1. Most of the values are decimals but some values are exactly 1. When I format the column to 10.4 for example, the 1s get converted to 0.0001 rather than 1.0000! Why is this happening???
INFORMAT
probability_of_default 7.4
....
;
FORMAT
probability_of_default 7.4
....
;
INPUT
probability_of_default
;
Sounds like you what you did was read the data using an INFORMAT of 10.4, instead of displaying the data using a FORMAT of 10.4. If you specify the decimal part of an informat then you are telling SAS that when there is no decimal point in the text that it is reading to assume there is one at d characters before the end. So you told SAS to divide the integers by 10**4. Instead just use an informat without any decimal part, like 10., instead of 10.4.

Why does comma9.2 not work?

Can anyone tell me why comma9.2 is not working in my sas codes?
data have;
input x $16.;
y = input(x, comma9.2);
z = input(x, comma9.);
put x= y= z= ;
cards;
1,740.32
5200
520
52
7,425
9,000.00
36,000.00
;
run;
To expand on Reeza's answer:
Informat decimal places do not quite work the way Format decimal places do. In almost all cases, you will not want to or need to specify the d in the informat. Comma9. is almost always correct, no matter how many decimal places you expect - even if you expect always two.
The only use informat decimal places serve is when you have a number like 12345600, which has no decimal in it, but it ought to (the last two zeros are after the decimal).
data _null_;
input numval 8.2;
put numval=;
datalines;
12345600
12345605
99999989
1857.145
;;;;
run;
This was something that was common once upon a time in the age of punch cards, particularly for accounting; since everything was in dollars and cents, you could save a column by leaving out the decimal, and just read everything in with two decimals. It is no longer common in most fields (at least in my experience), but SAS is always backwards compatible.
SAS will ignore the .d specification if it encounters a decimal point in the data (and will then use the location of that decimal to read in the value correctly), but if there are no decimal points in the data it may read it in incorrectly if you specify the .d. Notice in my example the final row has a decimal point followed by three decimal places, and is read in correctly.
You can read SAS Documentation for more information.
Comma9.2 assumes that values will always have 2 decimal places.

long digit reading in sas

I have a long ID number (say, 12184447992012111111). BY using proc import from csv file that number shortens itself with a addition of 'E' in between the digits (1.2184448E19, with format best12. and informat best32.). Browsing here I got to know the csv format itself shortens it previously so it is nothing to do with SAS. So I tried to copy say about 5 numbers and use datalines statement then also it results same.... It wil be helpful if anyone can suggest which format I need to use. Using best32. format I donot get the original number since most probably it modifies that altered number, which infact gives me 12184447992012111872 which is not my desired number.
Because your ID variable is really an identifier rather than a "real" number, you need to read it in as a character string. The value you show as an example is too large to be represented as an integer, so since SAS stores all numerics as floating point, you are losing "precision".
Since you mention using PROC IMPORT, copy the SAS program it generates and change the FORMAT and INFORMAT specifications from "21." and "best32." to "$32." (or whatever value matched your data.
Best of course would be if you had SAS Access to PC File formats, in which case you cound format the column as "text" in Excel and let SAS read it directly.
I'm not sure about the csv changing the value (they are just plain text files) - unless you are saving an excel spreadsheet as a csv file. If you are using excel just set the column to number format, no decimal places.
It might be easier to treat the column as text when importing it to SAS - unless you need to perform mathematical operations on it! If you really need to keep it as a number the format 32. should force it to be a 32 digit number - best is fairly sensibly changing it into scientific notation (though I suspect the data is there in the background and just displayed unhelpfully).
There is a SAS informat for reading exponential notation - Ew.d where w is the width and d the number of decimal places. In your case, it probably won't help because you will "lose" the complete number - and the value stored in case you read with this informat will be 1.2184448 * (10^19). The only way in your case is to ensure that the program which produces the CSV file outputs it in the right way. If you are creating the data from an Excel worksheet, then format the number in the Excel worksheet to display all the digits correctly.