Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

In some settings it may be necessary to recode a categorical variable with character values into a variable with numeric values. For example, the matching macro we discussed in example 7.35 will only match on numeric variables. One way to convert character variables to numeric values is to determine which values exist, then write a possibly long series of conditional tests to assign numbers to the values. Surely there’s a better way?

SAS
In SAS, Rick Wicklin offers an IML solution and links to a macro with the same function. But if you’re not an IML coder, and you don’t want to investigate a macro solution, it’s simple enough to do with data steps. We’ll begin by making some fake data.
data test;
do i = 1 to 100;
cat = "meow";
if i gt 30 then cat = "Purr";
if i gt 70 then cat = "Hiss";
output;
end;
run;

To make the new variable, we’ll just sort (section 1.5.6) the data on the categorical variable we want to convert, then use the set ds; by x; syntax to keep track of when a new value is encountered in the data. It’s hard to believe that we’ve never demonstrated this useful syntax before– perhaps we just can’t find it today. The set ds; by x; syntax makes new temporary variables first.x and last.x that are equal to 1 for the first and last observations of each new level of x, respectively, and 0 otherwise. When we find a new value, we’ll increase a counter by 1; the counter is our new numeric-valued variable.
proc sort data = test; by cat; run;

data catize;
set test;
by cat;
retain catnum 0;
if first.cat then catnum = catnum + 1;
run;

/* check the result */
proc freq data = catize;
tables cat * catnum;
run;

The table also shows the recoding values.
                             Table of cat by catnum

cat       catnum

Frequency|
Percent  |
Row Pct  |
Col Pct  |       1|       2|       3|  Total
---------+--------+--------+--------+
Hiss     |     30 |      0 |      0 |     30
|  30.00 |   0.00 |   0.00 |  30.00
| 100.00 |   0.00 |   0.00 |
| 100.00 |   0.00 |   0.00 |
---------+--------+--------+--------+
Purr     |      0 |     40 |      0 |     40
|   0.00 |  40.00 |   0.00 |  40.00
|   0.00 | 100.00 |   0.00 |
|   0.00 | 100.00 |   0.00 |
---------+--------+--------+--------+
meow     |      0 |      0 |     30 |     30
|   0.00 |   0.00 |  30.00 |  30.00
|   0.00 |   0.00 | 100.00 |
|   0.00 |   0.00 | 100.00 |
---------+--------+--------+--------+
Total          30       40       30      100
30.00    40.00    30.00   100.00



R
We begin by making the data. To convert to numbers, we use the labels option to the factor() function, feeding it the sequences of numbers between 1 and however many different values there are. Note that we find this using the factor() function again. There’s probably a better way of doing this, but it’s a little bit amusing to code it this way. Then we have numbers, but they’re store as a factor. We can get them out with a call to as.numeric().
cat = c(rep("meow",30),rep("Hiss",30), rep("Purr", 40))
catn1 = factor(cat, labels=(1:length(levels(factor(cat)))))
catn = as.numeric(catn1)
table(catn,cat)

cat
catn Hiss meow Purr
1   30    0    0
2    0   30    0
3    0    0   40


There’s a warning in the documentation for factor() that the values are assigned in location-specific fashion, so the table should be used to establish how the codes were assigned.  For the record, the use cases for this kind of recoding in R may be more strained than the SAS example given above.